update youtube player bindings

This commit is contained in:
wryk 2020-02-24 23:18:51 +01:00
parent 4263abe3e4
commit 656d250852
4 changed files with 134 additions and 98 deletions

View File

@ -2,16 +2,18 @@
<div class="playerBig__player" class:placeholder={!ready} class:hidden={!large}> <div class="playerBig__player" class:placeholder={!ready} class:hidden={!large}>
{#if $current} {#if $current}
<YoutubePlayer <YoutubePlayer
id={$current ? $current.media.credentials.id : null} bind:this={player}
id={$current.media.credentials.id}
class="playerBig__iframe" class="playerBig__iframe"
paused={$paused} paused={$paused}
volume={$volume} volume={$volume}
bind:ready on:canplay={onCanPlay}
bind:ended on:play={onPlay}
bind:error on:pause={onPause}
bind:currentTime on:timeupdate={onTimeUpdate}
bind:duration on:durationchange={onDurationChange}
bind:seek={seek} on:ended={onEnded}
on:error={onError}
></YoutubePlayer> ></YoutubePlayer>
<div class="playerBig__overlay" on:click={() => $paused = !$paused}></div> <div class="playerBig__overlay" on:click={() => $paused = !$paused}></div>
<button <button
@ -28,7 +30,7 @@
{#if $current} {#if $current}
<img <img
class="playerCover__img" class="playerCover__img"
src={'https://img.youtube.com/vi/' + $current.media.credentials.id + '/mqdefault.jpg'} src={$current.media.cover}
alt="cover" alt="cover"
> >
<button <button
@ -42,11 +44,11 @@
</div> </div>
<Progress <Progress
duration={duration}
currentTime={currentTime} currentTime={currentTime}
duration={duration}
ready={ready} ready={ready}
on:input={event => updateCurrentTime(event.target.value, false)} on:input={event => seek(event.target.value, true)}
on:change={event => updateCurrentTime(event.target.value, true)} on:change={event => seek(event.target.value, true)}
></Progress> ></Progress>
<div class="playerTrack"> <div class="playerTrack">
@ -81,8 +83,8 @@
duration={duration} duration={duration}
currentTime={currentTime} currentTime={currentTime}
ready={ready} ready={ready}
on:input={event => updateCurrentTime(event.target.value, false)} on:input={event => seek(event.target.value, true)}
on:change={event => updateCurrentTime(event.target.value, true)} on:change={event => seek(event.target.value, true)}
></Progress> ></Progress>
<div class="playerSticky__referer">shared by <span class="playerTrack__username">{$current.referer.username}</div> <div class="playerSticky__referer">shared by <span class="playerTrack__username">{$current.referer.username}</div>
</div> </div>
@ -103,6 +105,8 @@
export let large export let large
export let sticky export let sticky
let player
const paused = getContext('paused') const paused = getContext('paused')
const volume = getContext('volume') const volume = getContext('volume')
const current = getContext('current') const current = getContext('current')
@ -110,23 +114,43 @@
const selectNext = getContext('selectNext') const selectNext = getContext('selectNext')
let ready = null let ready = null
let ended = null
let error = null
let currentTime = null let currentTime = null
let duration = null let duration = null
let seek = null
$: if (ended || error) { const onCanPlay = () => {
ready = true
}
const onPlay = () => {
$paused = false
}
const onPause = () => {
$paused = true
}
const onTimeUpdate = event => {
currentTime = event.detail
}
const onDurationChange = event => {
duration = event.detail
}
const onEnded = () => {
selectNext() selectNext()
} }
const updateCurrentTime = (seconds, seekAhead) => { const onError = event => {
seek(seconds, seekAhead) console.error(event.detail)
currentTime = seconds selectNext()
}
const seek = (seconds, seekAhead) => {
player.seek(seconds, seekAhead)
} }
const switchBigPlayer = () => { const switchBigPlayer = () => {
large = !large large = !large
} }
</script> </script>

View File

@ -1,7 +1,7 @@
<div bind:this={element}></div> <div bind:this={element}></div>
<script> <script>
import { onMount, onDestroy } from 'svelte' import { createEventDispatcher, onMount, onDestroy } from 'svelte'
import { loadIframeApi, STATE } from '/services/youtube.js' import { loadIframeApi, STATE } from '/services/youtube.js'
import { queue } from '/services/misc.js' import { queue } from '/services/misc.js'
@ -9,48 +9,41 @@
let player let player
let animationFrameId let animationFrameId
// output props let currentTime
export let ready = false let duration
export let ended = false
export let error = false
export let duration = null
export let currentTime = null
// input props // input props
export let id export let id
export let paused export let paused
export let volume export let volume
const dispatch = createEventDispatcher()
$: load(id) $: load(id)
$: setPaused(paused) $: paused ? pause() : play()
$: setVolume(volume) $: setVolume(volume)
$: dispatch('timeupdate', currentTime)
$: dispatch('durationchange', duration)
const { enqueue, run } = queue() const { enqueue, run } = queue()
export const load = (id) => enqueue((player) => { export const load = (id) => enqueue((player) => {
ready = false
ended = false
error = false
currentTime = null currentTime = null
duration = null duration = null
if (paused) { player.cueVideoById(id)
player.cueVideoById(id)
} else { if (!paused) {
player.loadVideoById(id) player.playVideo()
} }
}) })
const setPaused = paused => enqueue(player => { export const play = () => enqueue((player) => {
if (paused) { player.playVideo()
if (player.getPlayerState() === STATE.PLAYING) { })
player.pauseVideo()
} export const pause = () => enqueue((player) => {
} else { player.pauseVideo()
if (player.getPlayerState() !== STATE.PLAYING) {
player.playVideo()
}
}
}) })
const setVolume = volume => enqueue(player => { const setVolume = volume => enqueue(player => {
@ -61,68 +54,82 @@
player.seekTo(seconds, allowSeekAhead) player.seekTo(seconds, allowSeekAhead)
}) })
onMount(() => { onMount(async () => {
loadIframeApi().then(api => { const api = await loadIframeApi()
element.id = Math.random().toString(16).slice(2, 8)
const onReady = ({ target: player }) => { element.id = Math.random().toString(16).slice(2, 8)
if (player.isMuted()) {
player.unMute()
}
run(player) const onReady = ({ target: player }) => {
if (player.isMuted()) {
player.unMute()
} }
const onStateChange = ({ data: state, target: player }) => { run(player)
switch (state) { }
case STATE.UNSTARTED:
ready = true
break
case STATE.PLAYING: const onStateChange = ({ data: state, target: player }) => {
if (duration === null) { switch (state) {
duration = player.getDuration() case STATE.UNSTARTED:
} break
break case STATE.ENDED:
dispatch('ended')
break
case STATE.ENDED: case STATE.PLAYING:
ended = true dispatch('play')
break
}
if (state === STATE.PLAYING) { const newDuration = player.getDuration()
const step = () => {
currentTime = player.getCurrentTime() if (duration !== newDuration) {
animationFrameId = requestAnimationFrame(step) duration = newDuration
dispatch('durationchange', duration)
} }
break
case STATE.PAUSED:
dispatch('pause')
break
case STATE.CUED:
dispatch('canplay')
break
}
if (state === STATE.PLAYING) {
const step = () => {
currentTime = player.getCurrentTime()
animationFrameId = requestAnimationFrame(step) animationFrameId = requestAnimationFrame(step)
} else { }
if (animationFrameId) {
cancelAnimationFrame(animationFrameId) step()
} } else {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId)
} }
} }
}
const onError = () => { const onError = error => {
error = true dispatch('error', error)
}
player = new api.Player(element.id, {
playerVars: {
autoplay: 0,
controls: 0,
enablejsapi: 1,
modestbranding: 1,
rel: 0
},
events: {
onReady,
onStateChange,
onError
} }
player = new api.Player(element.id, {
playerVars: {
autoplay: 0,
controls: 0,
enablejsapi: 1,
modestbranding: 1,
rel: 0
},
events: {
onReady,
onStateChange,
onError
}
})
}) })
}) })

View File

@ -10,7 +10,7 @@
value={value} value={value}
on:input on:input
on:change on:change
disabled={currentTime === null || duration === null} disabled={disabled}
> >
</div> </div>
<div class="playerProgress__timecode"> <div class="playerProgress__timecode">
@ -21,13 +21,16 @@
<script> <script>
import { secondsToElapsedTime } from '/services/misc.js' import { secondsToElapsedTime } from '/services/misc.js'
export let duration
export let currentTime
export let ready export let ready
export let currentTime
export let duration
$: value = currentTime != null ? currentTime : 0 $: value = currentTime != null ? currentTime : 0
$: max = duration != null ? duration : 100 $: max = duration != null ? duration : 0
$: currentPercent = currentTime != null ? (currentTime / duration) * 100 : 0 $: disabled = currentTime == null || duration == null
$: currentTimeText = currentTime != null ? secondsToElapsedTime(currentTime) : '--:--' $: currentTimeText = currentTime != null ? secondsToElapsedTime(currentTime) : '--:--'
$: durationText = duration != null ? secondsToElapsedTime(duration) : '--:--' $: durationText = duration != null ? secondsToElapsedTime(duration) : '--:--'
$: currentPercent = currentTime != null ? (currentTime / duration) * 100 : 0
</script> </script>

View File

@ -25,6 +25,7 @@ export const queue = () => {
const enqueue = f => { const enqueue = f => {
promise = promise.then(tap(f)) promise = promise.then(tap(f))
return promise
} }
return { enqueue, run: deferred.resolve } return { enqueue, run: deferred.resolve }
@ -89,6 +90,7 @@ export async function* tracksIterator(refererGenerator, cache) {
media: { media: {
title: metadata.title, title: metadata.title,
url: mediaUrl, url: mediaUrl,
cover: `https://img.youtube.com/vi/${mediaCredentials.id}/mqdefault.jpg`,
credentials: mediaCredentials credentials: mediaCredentials
} }
} }