Spaces:
Paused
Paused
File size: 3,281 Bytes
02f8344 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | /**
* Shared shadowing session — segment sync, continuous playback.
*/
import { ref, computed, watch } from 'vue'
import { useYoutubePlayer } from '@/composables/useYoutubePlayer.js'
import { loadProgress, saveProgress } from '@/utils/shadowingProgress.js'
export function useShadowingSession(videoDataRef) {
const yt = useYoutubePlayer()
const currentIndex = ref(0)
const playbackRate = ref(1)
const showVideo = ref(true)
const showTranslation = ref(true)
const flaggedIds = ref([])
const segments = computed(() => videoDataRef.value?.segments ?? [])
const currentSegment = computed(() => segments.value[currentIndex.value] ?? null)
function loadSaved(videoId) {
const p = loadProgress(videoId)
currentIndex.value = Math.min(p.currentSegment, Math.max(0, segments.value.length - 1))
flaggedIds.value = [...(p.flaggedSegments || [])]
}
function persist(videoId) {
saveProgress(videoId, {
currentSegment: currentIndex.value,
flaggedSegments: [...flaggedIds.value],
})
}
function findIndexAtTime(time) {
const segs = segments.value
if (!segs.length) return 0
let idx = 0
for (let i = 0; i < segs.length; i++) {
if (time >= (segs[i].start ?? 0) - 0.02) idx = i
}
return idx
}
function syncIndexFromTime(time) {
const idx = findIndexAtTime(time)
if (idx !== currentIndex.value) {
currentIndex.value = idx
}
return idx
}
/**
* @param {number} index
* @param {{ autoPlay?: boolean | null }} opts — null = giữ trạng thái play/pause hiện tại
*/
function playSegment(index = currentIndex.value, { autoPlay = true } = {}) {
const seg = segments.value[index]
if (!seg) return
currentIndex.value = index
yt.seekTo(seg.start)
if (autoPlay === true) yt.play()
else if (autoPlay === false) yt.pause()
}
function nextSegment() {
if (currentIndex.value < segments.value.length - 1) {
playSegment(currentIndex.value + 1, { autoPlay: playing.value })
}
}
function prevSegment() {
if (currentIndex.value > 0) {
playSegment(currentIndex.value - 1, { autoPlay: playing.value })
}
}
function replaySegment() {
playSegment(currentIndex.value, { autoPlay: playing.value })
}
function toggleFlag(id) {
const ids = new Set(flaggedIds.value)
if (ids.has(id)) ids.delete(id)
else ids.add(id)
flaggedIds.value = [...ids]
const vid = videoDataRef.value?.video_id
if (vid) persist(vid)
}
/** Chỉ cập nhật highlight theo thời gian — không pause, không seek. */
function onTimeUpdate(time) {
syncIndexFromTime(time)
}
watch(playbackRate, (r) => yt.setRate(r))
watch(currentIndex, (i) => {
const vid = videoDataRef.value?.video_id
if (vid) saveProgress(vid, { currentSegment: i })
})
const playing = yt.playing
return {
playing,
playerReady: yt.ready,
mountPlayer: yt.mount,
destroyPlayer: yt.destroy,
currentIndex,
playbackRate,
showVideo,
showTranslation,
flaggedIds,
segments,
currentSegment,
loadSaved,
persist,
playSegment,
nextSegment,
prevSegment,
replaySegment,
toggleFlag,
onTimeUpdate,
pause: yt.pause,
play: yt.play,
}
}
|