linguaielts-api / fronted /src /composables /useYoutubePlayer.js
AnhviNguyen
Forecast, dashboard, pronunciation, nginx fixes (exclude large ML weights)
3092643
Raw
History Blame Contribute Delete
2.67 kB
/**
* YouTube IFrame API — segment playback, auto-pause, speed control.
*/
import { ref, shallowRef, onUnmounted } from 'vue'
let apiPromise = null
function loadYoutubeApi() {
if (window.YT?.Player) return Promise.resolve()
if (!apiPromise) {
apiPromise = new Promise((resolve) => {
const prev = window.onYouTubeIframeAPIReady
window.onYouTubeIframeAPIReady = () => {
prev?.()
resolve()
}
if (!document.querySelector('script[src*="youtube.com/iframe_api"]')) {
const tag = document.createElement('script')
tag.src = 'https://www.youtube.com/iframe_api'
document.head.appendChild(tag)
}
})
}
return apiPromise
}
export function useYoutubePlayer() {
const ready = ref(false)
const playing = ref(false)
const player = shallowRef(null)
const pollId = ref(null)
async function mount(el, videoId, { onStateChange, onTimeUpdate } = {}) {
await loadYoutubeApi()
if (player.value) {
try { player.value.destroy() } catch { /* */ }
player.value = null
}
player.value = new window.YT.Player(el, {
videoId,
width: '100%',
height: '100%',
playerVars: {
rel: 0,
modestbranding: 1,
playsinline: 1,
enablejsapi: 1,
origin: typeof window !== 'undefined' ? window.location.origin : undefined,
},
events: {
onReady: () => {
ready.value = true
},
onStateChange: (e) => {
playing.value = e.data === window.YT.PlayerState.PLAYING
onStateChange?.(e)
},
},
})
stopPoll()
pollId.value = window.setInterval(() => {
if (!player.value?.getCurrentTime) return
const t = player.value.getCurrentTime()
onTimeUpdate?.(t)
}, 100)
}
function stopPoll() {
if (pollId.value) {
clearInterval(pollId.value)
pollId.value = null
}
}
function seekTo(seconds) {
player.value?.seekTo?.(seconds, true)
}
function play() {
player.value?.playVideo?.()
playing.value = true
}
function pause() {
player.value?.pauseVideo?.()
playing.value = false
}
function setRate(rate) {
player.value?.setPlaybackRate?.(rate)
}
function getCurrentTime() {
return player.value?.getCurrentTime?.() ?? 0
}
function destroy() {
stopPoll()
try {
player.value?.destroy?.()
} catch { /* */ }
player.value = null
ready.value = false
playing.value = false
}
onUnmounted(destroy)
return {
ready,
playing,
mount,
seekTo,
play,
pause,
setRate,
getCurrentTime,
destroy,
}
}