simplify store, add more feats

This commit is contained in:
wryk 2020-01-16 20:10:40 +01:00
parent cd10193d3e
commit ae187a96ef
10 changed files with 174 additions and 275 deletions

5
package-lock.json generated
View File

@ -3018,11 +3018,6 @@
"locate-path": "^3.0.0"
}
},
"folktale": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/folktale/-/folktale-2.3.2.tgz",
"integrity": "sha512-+8GbtQBwEqutP0v3uajDDoN64K2ehmHd0cjlghhxh0WpcfPzAIjPA03e1VvHlxL02FVGR0A6lwXsNQKn3H1RNQ=="
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",

View File

@ -21,7 +21,6 @@
"dependencies": {
"@babel/runtime-corejs3": "^7.7.7",
"date-fns": "^2.9.0",
"folktale": "^2.3.2",
"get-urls": "^9.2.0",
"iter-tools": "^7.0.0-rc.0",
"yt-player": "^3.4.3"

View File

@ -4,7 +4,9 @@
</header>
<section class="viewer">
<Viewer></Viewer>
{#if $current}
<Viewer></Viewer>
{/if}
</section>
<section class="queue">
@ -23,6 +25,8 @@
import Controls from '/components/Controls.svelte'
import Queue from '/components/Queue.svelte'
import Viewer from '/components/Viewer.svelte'
import { current } from '/store.js'
</script>
<style>

View File

@ -3,14 +3,12 @@
<button on:click={() => $muted = !$muted}>
{#if $muted}
🔇
{:else}
{#if $volume < 20}
🔈
{:else if $volume < 80}
🔉
{:else}
🔊
{/if}
{:else if $volume < 20}
🔈
{:else if $volume < 80}
🔉
{:else }
🔊
{/if}
</button>
@ -18,9 +16,21 @@
</div>
<div class="controls-group">
<button on:click={() => entry.previous()}>⏮️</button>
<button on:click={() => $paused = !$paused}>{#if $paused}▶️{:else}⏸️{/if}</button>
<button on:click={() => entry.next()}>⏭️</button>
<button class:cant={!$canPrevious} on:click={() => selectPrevious()}>⏮️</button>
<button on:click={() => $paused = !$paused}>
{#if $index === null}
▶️
{:else if $loading}
🕒
{:else if $paused}
▶️
{:else }
⏸️
{/if}
</button>
<button class:cant={!$canNext} on:click={() => selectNext()}>⏭️</button>
</div>
<div class="controls-group">
@ -30,7 +40,18 @@
</div>
<script>
import { paused, volume, muted, entry } from '/stores.js'
import {
paused,
muted,
volume,
index,
queue,
canPrevious,
canNext,
selectPrevious,
selectNext,
loading
} from '/store.js'
</script>
<style>
@ -42,4 +63,8 @@
.controls-group {
margin: 0 1rem;
}
.cant {
background-color: red;
}
</style>

View File

@ -1,53 +1,58 @@
<div>
{#each $entries as entry}
<div class="entry" class:active={entry === $currentEntry}>
{#each $queue as track, i}
<div class="entry" class:active={i === $index}>
<div>
<button on:click={() => toggleEntry(entry)}>
{#if entry === $currentEntry && !$paused}
⏸️
{:else}
<button on:click={() => toggle(i)}>
{#if i != $index}
▶️
{:else if $loading}
🕒
{:else if $paused}
▶️
{:else}
⏸️
{/if}
</button>
</div>
<div>
<div>{entry.metadata.title}</div>
<div>{track.metadata.title}</div>
<div>
<b>{entry.status.account.username} <small style="color: dimgray">{entry.status.account.acct}</small></b>
{entry.data.url}
<b>{track.status.account.username} <small style="color: dimgray">{track.status.account.acct}</small></b>
{track.data.url}
</div>
</div>
</div>
{/each}
<button on:click={() => entries.load(5)}>LOAD 5 MOAR</button>
{#if $enqueueing}
LOADING ...
{/if}
</div>
<script>
import { onMount } from 'svelte'
import { paused, entry as currentEntry, entries } from '/stores.js'
import { index, queue, paused, enqueueing, enqueue, loading } from '/store.js'
const toggleEntry = (entry) => {
if (entry !== $currentEntry) {
$currentEntry = entry
} else {
const toggle = i => {
if (i === $index) {
$paused = !$paused
} else {
$index = i
$paused = false
}
}
onMount(() => {
const unsubscribe = entries.subscribe(async (xs) => {
if (xs.length) {
const [firstEntry] = xs
currentEntry.set(firstEntry)
unsubscribe()
}
enqueue().then(() => {
$index = 0
})
entries.load(1)
})
$: if ($index !== null && $index === $queue.length - 1) {
enqueue()
}
</script>
<style>

View File

@ -1,12 +1,22 @@
<div>
<div class:hidden={!duration}>
<div class="embed-container" class:hidden={!duration}>
<div bind:this={element}></div>
<div class="embed-overlay" on:click={() => $paused = !$paused}></div>
</div>
{#if duration}
{currentTimeText}
<input type="range" min="0" max={duration} value={currentTime} disabled>
<input
type="range"
min="0"
max={duration}
value={currentTime}
on:input={ (e) => updatePlayerCurrentTime(e.target.value) }
on:mousedown={() => { if (player && !$paused) player.pause() }}
on:mouseup={() => { if (player && !$paused) player.play() }}>
{durationText}
{:else}
LOADING TRACK
{/if}
</div>
@ -14,8 +24,8 @@
import { onMount, onDestroy } from 'svelte'
import { get } from 'svelte/store'
import YoutubePlayer from 'yt-player'
import { entry, paused, muted, volume } from '/stores.js'
import { secondsToElapsedTime } from '/util.js'
import { paused, muted, volume, current, selectNext, loading } from '/store.js'
let element
let player
@ -29,40 +39,45 @@
$: currentTimeText = currentTime !== null ? secondsToElapsedTime(currentTime) : null
$: durationText = duration !== null ? secondsToElapsedTime(duration) : null
$: updateEntry($entry)
$: updatePaused($paused)
$: updateMuted($muted)
$: updateVolume($volume)
$: updatePlayerVideoId($current)
$: updatePlayerPaused($paused)
$: updatePlayerMuted($muted)
$: updatePlayerVolume($volume)
const updateViewerDurationCallback = () => {
if (player) {
duration = player.getDuration()
currentTime = player.getCurrentTime()
$loading = false
}
}
const updateEntry = (entry) => {
if (player && entry) {
const updatePlayerVideoId = ($current) => {
if (player && $current) {
duration = null
currentTime = null
$loading = true
player.off('playing', updateViewerDurationCallback)
player.load(entry.data.id, !$paused)
player.load($current.data.id, !$paused)
}
}
const updatePaused = (paused) => {
const updatePlayerPaused = (paused) => {
if (player) paused ? player.pause() : player.play()
}
const updateMuted = (muted) => {
const updatePlayerMuted = (muted) => {
if (player) muted ? player.mute() : player.unMute()
}
const updateVolume = (volume) => {
const updatePlayerVolume = (volume) => {
if (player) player.setVolume(volume)
}
const updatePlayerCurrentTime = (seconds) => {
if (player) player.seek(seconds)
}
onMount(() => {
@ -77,17 +92,9 @@
related: false
})
updatePaused($paused)
updateMuted($muted)
updateVolume($volume)
// player.on('playing', () => {
// $paused = false
// })
// player.on('paused', () => {
// $paused = true
// })
updatePlayerPaused($paused)
updatePlayerMuted($muted)
updatePlayerVolume($volume)
player.on('unstarted', () => {
player.once('playing', updateViewerDurationCallback)
@ -97,16 +104,18 @@
currentTime = time
})
player.on('ended', () => entry.next())
player.on('ended', () => {
selectNext()
})
player.on('unplayable', (...args) => {
console.log('unplayable', ...args)
entry.next()
selectNext()
})
player.on('error', (...args) => {
console.log('error', ...args)
entry.next()
selectNext()
})
})
@ -121,4 +130,16 @@
.hidden {
display: none;
}
.embed-container {
position: relative;
}
.embed-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
</style>

View File

@ -1,122 +0,0 @@
import { writable, get } from 'svelte/store'
import { mkTracksIterator } from '/util.js'
export const writableLocalStorage = (key, value) => {
const item = JSON.parse(localStorage.getItem(key))
const store = writable(item === null ? value : item)
store.subscribe(x => localStorage.setItem(key, JSON.stringify(x)))
return store
}
export const stackStore = (domain, hashtags) => {
const tracksIterator = mkTracksIterator(domain, hashtags)
const store = writable([])
const { update, subscribe } = store
let promise = Promise.resolve()
const buffer = []
const load = async () => {
const n = 5 - buffer.length
for (let i = 0; i < n; i++) {
const iteratorResult = await tracksIterator.next()
if (iteratorResult.value) {
update(entries => [...entries, iteratorResult.value])
} else {
// iterator don't have new entries for now
break
}
}
}
const unshift = async () => {
let promise = promise.then(() => {
})
}
promise = load()
return { subscribe, unshift }
}
export const entryStore = (entriesStore) => {
const store = writable(null)
const { set, update, subscribe } = store
const select = (entry) => {
update(() => entry)
const entriesList = get(entriesStore)
const index = entriesList.indexOf(entry)
if (index === entriesList.length - 1) {
entriesStore.load(1)
}
}
const previous = () => {
const entriesList = get(entriesStore)
update(currentEntry => {
const index = entriesList.indexOf(currentEntry)
return index > 0 ? entriesList[index - 1] : null
})
}
const next = () => {
const entriesList = get(entriesStore)
update(oldEntry => {
if (entriesList.length === 0) {
return null
}
const index = entriesList.indexOf(oldEntry)
if (index === -1) {
return null
}
const nextIndex = index + 1
if (nextIndex === entriesList.length - 1) {
entries.load(1)
}
return entriesList[nextIndex]
})
}
return { subscribe, set: select, previous, next }
}
export const entriesStore = (domain, hashtags) => {
const tracksIterator = mkTracksIterator(domain, hashtags)
const store = writable([])
const { update, subscribe } = store
const load = async (number) => {
for (let i = 0; i < number; i++) {
const iteratorResult = await tracksIterator.next()
if (iteratorResult.value) {
update(entries => [...entries, iteratorResult.value])
} else {
// iterator don't have new entries for now
break
}
}
}
return { subscribe, load }
}

46
src/store.js Normal file
View File

@ -0,0 +1,46 @@
import { writable, derived, get } from 'svelte/store'
import { writableLocalStorage, mkTracksIterator } from '/util.js'
export const domain = writableLocalStorage('domain', 'eldritch.cafe')
export const hashtags = writableLocalStorage('hashtags', [
'np',
'nowplaying',
'tootradio',
'pouetradio'
])
export const paused = writable(false)
export const muted = writableLocalStorage('muted', false)
export const volume = writableLocalStorage('volume', 100)
export const index = writable(null)
export const queue = writable([])
export const enqueueing = writable(false)
export const current = derived([queue, index], ([$queue, $index]) => $queue[$index])
export const canPrevious = derived([index], ([$index]) => $index !== null && $index > 0)
export const canNext = derived([index, queue], ([$index, $queue]) => $index !== null && $index < $queue.length - 1)
export const loading = writable(false)
const tracksIterator = mkTracksIterator(get(domain), get(hashtags))
export const selectPrevious = () => { if (get(canPrevious)) index.update($index => $index - 1) }
export const selectNext = () => { if (get(canNext)) index.update($index => $index + 1) }
export const enqueue = async () => {
if (!get(enqueueing)) {
enqueueing.set(true)
const { value: newTrack } = await tracksIterator.next()
if (newTrack) {
queue.update($queue => [...$queue, newTrack])
}
enqueueing.set(false)
}
}

View File

@ -1,84 +0,0 @@
import { writable, get } from 'svelte/store'
import { writableLocalStorage, entriesStore, entryStore } from '/services/store.js'
export const domain = writableLocalStorage('domain', 'eldritch.cafe')
export const hashtags = writableLocalStorage('hashtags', [
'np',
'nowplaying',
'tootradio',
'pouetradio'
])
export const paused = writable(false)
export const muted = writableLocalStorage('muted', false)
export const volume = writableLocalStorage('volume', 100)
export const entries = entriesStore(get(domain), get(hashtags))
export const entry = entryStore(entries)
const tracksIterator = mkTracksIterator(get(domain), get(hashtags))
export const track = writable(null)
export const queue = writable([])
export const stack = writable([])
export const state = writable({
current: null,
queue: []
})
export const enqueue = () => {
const { value: newTrack } = await tracksIterator.next()
if (!newTrack) {
state.update(s => ({ ...s, queue: [...s.queue, newTrack] }))
}
}
export const select = (track) => {
state.update(s => ({ ...s, current: track }))
}
export const selectPrevious = () => {
state.update(s => {
if (s.current === null) return s
const
return
})
const tracks = get(queue)
track.update(oldTrack => {
const index = tracks.indexOf(oldTrack)
return index > 0 ? tracks[index - 1] : null
})
}
export const selectNext = () => {
const tracks = get(queue)
const oldTrack = get(track)
track.update(oldTrack => {
const index = tracks.indexOf(oldTrack)
if (index !== -1 && ) {
return null
}
return index !== -1 && index + 1 < tracks.length
? tracks[index + 1]
: null
})
enqueueIfTrack(track)
}

View File

@ -1,6 +1,16 @@
import { writable } from 'svelte/store'
import getUrls from 'get-urls'
import { execPipe, asyncFilter, asyncMap } from 'iter-tools'
export const writableLocalStorage = (key, value) => {
const item = JSON.parse(localStorage.getItem(key))
const store = writable(item === null ? value : item)
store.subscribe(x => localStorage.setItem(key, JSON.stringify(x)))
return store
}
const millisecond = 1
const second = 1000 * millisecond
const minute = 60 * second