type VideoEndCallback = () => void type TimeUpdateCallback = (time: number) => void export class VideoManager { private videoEl: HTMLVideoElement | null = null private onEndCallback: VideoEndCallback | null = null private onTimeCallback: TimeUpdateCallback | null = null private lastSrc: string = '' attach(videoEl: HTMLVideoElement) { this.videoEl = videoEl videoEl.addEventListener('ended', this.handleEnded) videoEl.addEventListener('timeupdate', this.handleTimeUpdate) } detach() { if (!this.videoEl) return this.videoEl.removeEventListener('ended', this.handleEnded) this.videoEl.removeEventListener('timeupdate', this.handleTimeUpdate) this.videoEl = null } play(src: string) { if (!this.videoEl) return if (this.lastSrc !== src) { this.videoEl.src = src this.lastSrc = src if (this.videoEl.readyState >= 1) { this.videoEl.currentTime = 0 this.videoEl.play().catch(() => {}) } else { const onReady = () => { if (this.videoEl) { this.videoEl.currentTime = 0 this.videoEl.play().catch(() => {}) } } this.videoEl.addEventListener('loadedmetadata', onReady, { once: true }) } } else { this.videoEl.currentTime = 0 this.videoEl.play().catch(() => {}) } } pause() { this.videoEl?.pause() } getCurrentTime(): number { return this.videoEl?.currentTime ?? 0 } onEnd(cb: VideoEndCallback) { this.onEndCallback = cb } onTimeUpdate(cb: TimeUpdateCallback) { this.onTimeCallback = cb } private handleEnded = () => { this.onEndCallback?.() } private handleTimeUpdate = () => { if (this.videoEl) { this.onTimeCallback?.(this.videoEl.currentTime) } } }