import { ModeSource, SkipSegment, SubtitleCue, SubtitleTrack, ActiveSegment } from './types'; import { q, qs, dataset } from '../q'; export interface PlayerState { container: HTMLElement; video: HTMLVideoElement; progress: HTMLElement; scrubber: HTMLElement; buffered: HTMLElement; timeDisplay: HTMLElement; durationDisplay: HTMLElement; modeSources: Record; availableModes: string[]; currentMode: string; currentEpisode: string; totalEpisodes: number; malID: number; streamURL: string; initialStreamToken: string; shouldAutoPlay: boolean; parsedSegments: SkipSegment[]; activeSegments: ActiveSegment[]; activeSkipSegment: ActiveSegment | null; activeSubtitles: SubtitleCue[]; currentSubtitleTracks: SubtitleTrack[]; lastKnownVolume: number; pendingSeekTime: number | null; isScrubbing: boolean; isFullscreen: boolean; playerControlsTimeout: number | undefined; progressSaveTimer: number | undefined; transitionEpisode: number | null; completionSent: boolean; completionAttempts: number; lastSavedProgress: { episode: string; seconds: number }; episodeGrid: HTMLElement | null; episodeList: HTMLElement | null; previewPopover: HTMLElement | null; previewTime: HTMLElement | null; videoOverlay: HTMLElement | null; } export const state: PlayerState = { container: null as unknown as HTMLElement, video: null as unknown as HTMLVideoElement, progress: null as unknown as HTMLElement, scrubber: null as unknown as HTMLElement, buffered: null as unknown as HTMLElement, timeDisplay: null as unknown as HTMLElement, durationDisplay: null as unknown as HTMLElement, modeSources: {}, availableModes: [], currentMode: 'dub', currentEpisode: '1', totalEpisodes: 0, malID: 0, streamURL: '/watch/proxy/stream', initialStreamToken: '', shouldAutoPlay: false, parsedSegments: [], activeSegments: [], activeSkipSegment: null, activeSubtitles: [], currentSubtitleTracks: [], lastKnownVolume: 1, pendingSeekTime: null, isScrubbing: false, isFullscreen: false, playerControlsTimeout: undefined, progressSaveTimer: undefined, transitionEpisode: null, completionSent: false, completionAttempts: 0, lastSavedProgress: { episode: '1', seconds: -1 }, episodeGrid: null, episodeList: null, previewPopover: null, previewTime: null, videoOverlay: null, }; export const initState = (c: HTMLElement): void => { state.container = c; state.video = q(c, 'video')!; state.progress = q(c, '[data-progress]'); state.scrubber = q(c, '[data-scrubber]'); state.buffered = q(c, '[data-buffered]'); state.timeDisplay = q(c, '[data-time]'); state.durationDisplay = q(c, '[data-duration]'); state.previewPopover = q(c, '[data-preview-popover]'); state.previewTime = q(c, '[data-preview-time]'); state.videoOverlay = q(c, '[data-video-overlay]'); state.malID = Number.parseInt(dataset(c, 'malId'), 10); state.currentEpisode = dataset(c, 'currentEpisode') || '1'; state.totalEpisodes = Number.parseInt(dataset(c, 'totalEpisodes'), 10); state.streamURL = dataset(c, 'streamUrl') || '/watch/proxy/stream'; state.initialStreamToken = dataset(c, 'streamToken') || ''; state.shouldAutoPlay = sessionStorage.getItem('mal:autoplay-next') === 'true'; sessionStorage.removeItem('mal:autoplay-next'); state.episodeGrid = qs('[data-episode-grid]'); state.episodeList = qs('[data-episode-list]'); const safeJson = (raw: string | undefined, fallback: T): T => { try { return JSON.parse(raw ?? '') as T; } catch { return fallback; } }; state.modeSources = safeJson(dataset(c, 'modeSources'), {} as Record); state.availableModes = safeJson(dataset(c, 'availableModes'), [] as string[]); const backendInitialMode = dataset(c, 'initialMode') || 'dub'; const storedMode = localStorage.getItem('player-audio-mode'); const initialMode = storedMode && state.availableModes.includes(storedMode) ? storedMode : backendInitialMode; const fallbackMode = Object.keys(state.modeSources).find(m => state.modeSources[m]?.token); state.currentMode = state.modeSources[initialMode]?.token ? initialMode : (fallbackMode ?? state.availableModes[0] ?? 'dub'); const segments = safeJson(dataset(c, 'segments'), [] as SkipSegment[]); state.parsedSegments = segments .map(s => ({ ...s, start: Number(s.start) || 0, end: Number(s.end) || 0 })) .filter(s => s.end > s.start); };