forked from Mastodon/mastoradio-la-radio-di-mastodon
wip remove yt-player
This commit is contained in:
parent
92e0bbc74a
commit
601652709a
|
@ -4547,11 +4547,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/little-ds-toolkit/-/little-ds-toolkit-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/little-ds-toolkit/-/little-ds-toolkit-1.1.1.tgz",
|
||||||
"integrity": "sha512-Zl5flhnd5W6nhRCyoL1bNlU8M5CWFp6SItMmK4pj39LKgzQD7LGg591OJ0jwDKat7mjHvJVkOyJT+BXOQH4iXw=="
|
"integrity": "sha512-Zl5flhnd5W6nhRCyoL1bNlU8M5CWFp6SItMmK4pj39LKgzQD7LGg591OJ0jwDKat7mjHvJVkOyJT+BXOQH4iXw=="
|
||||||
},
|
},
|
||||||
"load-script2": {
|
|
||||||
"version": "2.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/load-script2/-/load-script2-2.0.4.tgz",
|
|
||||||
"integrity": "sha512-WzZ2Df8QvWwGdjcJ7BYqcaojHGBC+Dg3oFsBg8gyz/mGyjCduBKdja+HkRo7BHoqt8sV5oil1Bc6Eyq6e40jfg=="
|
|
||||||
},
|
|
||||||
"locate-path": {
|
"locate-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||||
|
@ -7536,14 +7531,6 @@
|
||||||
"camelcase": "^5.0.0",
|
"camelcase": "^5.0.0",
|
||||||
"decamelize": "^1.2.0"
|
"decamelize": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"yt-player": {
|
|
||||||
"version": "3.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/yt-player/-/yt-player-3.4.3.tgz",
|
|
||||||
"integrity": "sha512-Ke5oM+qW8CR6aaK40mTxBff3hS4DXjyfmFkjg3eoaGt8fWQGyKFOFIpZqzNTR5c7Lu+xAG9SIvxCvbBPhCBbPg==",
|
|
||||||
"requires": {
|
|
||||||
"load-script2": "^2.0.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,7 @@
|
||||||
"@babel/runtime-corejs3": "^7.7.7",
|
"@babel/runtime-corejs3": "^7.7.7",
|
||||||
"date-fns": "^2.9.0",
|
"date-fns": "^2.9.0",
|
||||||
"get-urls": "^9.2.0",
|
"get-urls": "^9.2.0",
|
||||||
"iter-tools": "^7.0.0-rc.0",
|
"iter-tools": "^7.0.0-rc.0"
|
||||||
"yt-player": "^3.4.3"
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"last 1 chrome versions"
|
"last 1 chrome versions"
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="embed-container" class:hidden={!loaded}>
|
<YoutubePlayer
|
||||||
<div bind:this={element}></div>
|
id={$current ? $current.data.id : null}
|
||||||
|
paused={$paused}
|
||||||
|
muted={$muted}
|
||||||
|
volume={$volume}
|
||||||
|
bind:ready
|
||||||
|
bind:ended
|
||||||
|
bind:currentTime
|
||||||
|
bind:duration
|
||||||
|
bind:seek={seek}>
|
||||||
|
</YoutubePlayer>
|
||||||
|
|
||||||
|
<div class="embed-container" class:hidden={!ready}>
|
||||||
<div class="embed-overlay" on:click={() => $paused = !$paused}></div>
|
<div class="embed-overlay" on:click={() => $paused = !$paused}></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if !loaded}
|
{#if !ready}
|
||||||
LOADING TRACK
|
LOADING TRACK
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -15,120 +26,30 @@
|
||||||
min="0"
|
min="0"
|
||||||
max={duration}
|
max={duration}
|
||||||
value={currentTime}
|
value={currentTime}
|
||||||
on:input={ (e) => updatePlayerCurrentTime(e.target.value) }
|
on:input={(e) => seek(e.target.value, false)}
|
||||||
on:mousedown={() => { if (player && !$paused) player.pause() }}
|
on:change={(e) => seek(e.target.value, true)}>
|
||||||
on:mouseup={() => { if (player && !$paused) player.play() }}>
|
|
||||||
{durationText}
|
{durationText}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { onMount, onDestroy } from 'svelte'
|
|
||||||
import { get } from 'svelte/store'
|
import { get } from 'svelte/store'
|
||||||
import YoutubePlayer from 'yt-player'
|
import YoutubePlayer from '/components/YoutubePlayer'
|
||||||
import { secondsToElapsedTime } from '/util.js'
|
import { secondsToElapsedTime } from '/util.js'
|
||||||
import { paused, muted, volume, current, selectNext, loading } from '/store.js'
|
import { paused, muted, volume, current, selectNext, loading } from '/store.js'
|
||||||
|
|
||||||
let element
|
let ready = null
|
||||||
let player
|
let ended = null
|
||||||
|
|
||||||
let currentTime = null
|
let currentTime = null
|
||||||
let duration = null
|
let duration = null
|
||||||
let loaded = false
|
let seek = null
|
||||||
|
|
||||||
let currentTimeText = null
|
|
||||||
let durationText = null
|
|
||||||
|
|
||||||
$: currentTimeText = currentTime !== null ? secondsToElapsedTime(currentTime) : null
|
$: currentTimeText = currentTime !== null ? secondsToElapsedTime(currentTime) : null
|
||||||
$: durationText = duration !== null ? secondsToElapsedTime(duration) : null
|
$: durationText = duration !== null ? secondsToElapsedTime(duration) : null
|
||||||
|
|
||||||
$: updatePlayerVideoId($current)
|
$: if (ended) {
|
||||||
$: updatePlayerPaused($paused)
|
selectNext()
|
||||||
$: updatePlayerMuted($muted)
|
|
||||||
$: updatePlayerVolume($volume)
|
|
||||||
|
|
||||||
const updateViewerDurationCallback = () => {
|
|
||||||
if (player) {
|
|
||||||
duration = player.getDuration()
|
|
||||||
currentTime = player.getCurrentTime()
|
|
||||||
$loading = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatePlayerVideoId = ($current) => {
|
|
||||||
if (player && $current) {
|
|
||||||
duration = null
|
|
||||||
currentTime = null
|
|
||||||
$loading = true
|
|
||||||
loaded = false
|
|
||||||
player.off('playing', updateViewerDurationCallback)
|
|
||||||
|
|
||||||
player.load($current.data.id, !$paused)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePlayerPaused = (paused) => {
|
|
||||||
if (player) paused ? player.pause() : player.play()
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePlayerMuted = (muted) => {
|
|
||||||
if (player) muted ? player.mute() : player.unMute()
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePlayerVolume = (volume) => {
|
|
||||||
if (player) player.setVolume(volume)
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatePlayerCurrentTime = (seconds) => {
|
|
||||||
if (player) player.seek(seconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
player = new YoutubePlayer(element, {
|
|
||||||
width: 300,
|
|
||||||
height: 300,
|
|
||||||
autoplay: !$paused,
|
|
||||||
controls: false,
|
|
||||||
keyboard: false,
|
|
||||||
fullscreen: false,
|
|
||||||
modestBranding: true,
|
|
||||||
related: false
|
|
||||||
})
|
|
||||||
|
|
||||||
updatePlayerPaused($paused)
|
|
||||||
updatePlayerMuted($muted)
|
|
||||||
updatePlayerVolume($volume)
|
|
||||||
|
|
||||||
player.on('unstarted', () => {
|
|
||||||
loaded = true
|
|
||||||
player.once('playing', updateViewerDurationCallback)
|
|
||||||
})
|
|
||||||
|
|
||||||
player.on('timeupdate', (time) => {
|
|
||||||
currentTime = time
|
|
||||||
})
|
|
||||||
|
|
||||||
player.on('ended', () => {
|
|
||||||
selectNext()
|
|
||||||
})
|
|
||||||
|
|
||||||
player.on('unplayable', (...args) => {
|
|
||||||
console.log('unplayable', ...args)
|
|
||||||
selectNext()
|
|
||||||
})
|
|
||||||
|
|
||||||
player.on('error', (...args) => {
|
|
||||||
console.log('error', ...args)
|
|
||||||
selectNext()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
if (player) {
|
|
||||||
player.destroy()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
<div bind:this={element}></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { onMount, onDestroy } from 'svelte'
|
||||||
|
import { loadIframeApi, STATUS } from '/youtube.js'
|
||||||
|
import { queue } from '/util.js'
|
||||||
|
|
||||||
|
let element
|
||||||
|
let player
|
||||||
|
|
||||||
|
// output props
|
||||||
|
export let ready = false
|
||||||
|
export let ended = false
|
||||||
|
export let duration = null
|
||||||
|
export let currentTime = null
|
||||||
|
|
||||||
|
// input props
|
||||||
|
export let id
|
||||||
|
export let paused
|
||||||
|
export let muted
|
||||||
|
export let volume
|
||||||
|
|
||||||
|
$: load(id)
|
||||||
|
$: setPaused(paused)
|
||||||
|
$: setMuted(muted)
|
||||||
|
$: setVolume(volume)
|
||||||
|
|
||||||
|
const { enqueue, run } = queue()
|
||||||
|
|
||||||
|
export const load = (id) => enqueue((player) => {
|
||||||
|
ready = false
|
||||||
|
ended = false
|
||||||
|
currentTime = null
|
||||||
|
duration = null
|
||||||
|
|
||||||
|
if (paused) {
|
||||||
|
player.cueVideoById(id)
|
||||||
|
} else {
|
||||||
|
player.loadVideoById(id)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const setPaused = paused => enqueue(player => {
|
||||||
|
if (paused) {
|
||||||
|
if (player.getPlayerState() === STATUS.PLAYING) {
|
||||||
|
player.pauseVideo()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (player.getPlayerState() !== STATUS.PLAYING) {
|
||||||
|
player.playVideo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const setMuted = muted => enqueue(player => {
|
||||||
|
if (muted) {
|
||||||
|
if (!player.isMuted()) {
|
||||||
|
player.mute()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (player.isMuted()) {
|
||||||
|
player.unMute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const setVolume = volume => enqueue(player => {
|
||||||
|
player.setVolume(volume)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export const seek = (seconds, allowSeekAhead) => enqueue((player) => {
|
||||||
|
player.seekTo(seconds, allowSeekAhead)
|
||||||
|
// currentTime = player.getCurrentTime()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loadIframeApi().then(api => {
|
||||||
|
element.id = Math.random().toString(16).slice(2, 8)
|
||||||
|
|
||||||
|
const onReady = (event) => {
|
||||||
|
run(event.target)
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
currentTime = event.target.getCurrentTime()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onStateChange = (event) => {
|
||||||
|
console.log('stateChange', event)
|
||||||
|
|
||||||
|
switch (event.data) {
|
||||||
|
case STATUS.CUED:
|
||||||
|
ready = true
|
||||||
|
break
|
||||||
|
|
||||||
|
case STATUS.PLAYING:
|
||||||
|
duration = event.target.getDuration()
|
||||||
|
break
|
||||||
|
|
||||||
|
case STATUS.ENDED:
|
||||||
|
ended = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onError = () => {
|
||||||
|
console.log('error', event)
|
||||||
|
}
|
||||||
|
|
||||||
|
player = new api.Player(element.id, {
|
||||||
|
playerVars: {
|
||||||
|
autoplay: 0,
|
||||||
|
controls: 0,
|
||||||
|
enablejsapi: 1,
|
||||||
|
modestbranding: 1,
|
||||||
|
rel: 0
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
onReady,
|
||||||
|
onStateChange,
|
||||||
|
onError
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (player) {
|
||||||
|
player.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
28
src/util.js
28
src/util.js
|
@ -2,6 +2,34 @@ import { writable } from 'svelte/store'
|
||||||
import getUrls from 'get-urls'
|
import getUrls from 'get-urls'
|
||||||
import { execPipe, asyncFilter, asyncMap } from 'iter-tools'
|
import { execPipe, asyncFilter, asyncMap } from 'iter-tools'
|
||||||
|
|
||||||
|
export const tap = f => x => {
|
||||||
|
f(x)
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queue = () => {
|
||||||
|
const deferred = defer()
|
||||||
|
let promise = deferred.promise
|
||||||
|
|
||||||
|
const enqueue = f => {
|
||||||
|
promise = promise.then(tap(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
return { enqueue, run: deferred.resolve }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defer = () => {
|
||||||
|
let resolve
|
||||||
|
let reject
|
||||||
|
|
||||||
|
const promise = new Promise((res, rej) => {
|
||||||
|
resolve = res
|
||||||
|
reject = rej
|
||||||
|
})
|
||||||
|
|
||||||
|
return { resolve, reject, promise }
|
||||||
|
}
|
||||||
|
|
||||||
export const writableLocalStorage = (key, value) => {
|
export const writableLocalStorage = (key, value) => {
|
||||||
const item = JSON.parse(localStorage.getItem(key))
|
const item = JSON.parse(localStorage.getItem(key))
|
||||||
const store = writable(item === null ? value : item)
|
const store = writable(item === null ? value : item)
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
const IFRAME_API_URL = 'https://www.youtube.com/iframe_api'
|
||||||
|
|
||||||
|
export const STATUS = {
|
||||||
|
UNSTARTED: -1,
|
||||||
|
ENDED: 0,
|
||||||
|
PLAYING: 1,
|
||||||
|
PAUSED: 2,
|
||||||
|
BUFFERING: 3,
|
||||||
|
CUED: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadScript = (attributes) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!attributes.hasOwnProperty('src')) {
|
||||||
|
throw new Error('src is required')
|
||||||
|
}
|
||||||
|
|
||||||
|
// const scripts = Array.from(document.getElementsByTagName('script'))
|
||||||
|
// .filter(script => script.src === attribute.src)
|
||||||
|
|
||||||
|
// if (scripts.length) {
|
||||||
|
// if (scripts.some(script => script.readyState.complete)) {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
const script = document.createElement('script')
|
||||||
|
|
||||||
|
for (const [name, value] of Object.entries(attributes)) {
|
||||||
|
script[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
script.onload = (e) => {
|
||||||
|
script.onload = script.onerror = null
|
||||||
|
resolve(e.target)
|
||||||
|
}
|
||||||
|
|
||||||
|
script.onerror = (e) => {
|
||||||
|
script.onload = script.onerror = null
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.head.appendChild(script)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadIframeApi = async () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (window.YT && typeof window.YT.Player === 'function') {
|
||||||
|
resolve(window.YT)
|
||||||
|
} else {
|
||||||
|
const previousInitializer = window.onYouTubeIframeAPIReady
|
||||||
|
|
||||||
|
window.onYouTubeIframeAPIReady = () => {
|
||||||
|
window.onYouTubeIframeAPIReady = previousInitializer
|
||||||
|
resolve(window.YT)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadScript({ src: IFRAME_API_URL })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue