ts-video-player 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +438 -0
- package/dist/cdn.d.ts +10 -0
- package/dist/cdn.d.ts.map +1 -0
- package/dist/cdn.min.js +1046 -0
- package/dist/cdn.min.js.map +54 -0
- package/dist/cdn.test.d.ts +2 -0
- package/dist/cdn.test.d.ts.map +1 -0
- package/dist/chunk-36x4yhjn.js +765 -0
- package/dist/chunk-36x4yhjn.js.map +12 -0
- package/dist/chunk-eajts2jv.js +4243 -0
- package/dist/chunk-eajts2jv.js.map +42 -0
- package/dist/chunk-z1ehbz9f.js +380 -0
- package/dist/chunk-z1ehbz9f.js.map +19 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/core/audio-gain.d.ts +67 -0
- package/dist/core/audio-gain.d.ts.map +1 -0
- package/dist/core/caption-styles.d.ts +121 -0
- package/dist/core/caption-styles.d.ts.map +1 -0
- package/dist/core/chapters.d.ts +150 -0
- package/dist/core/chapters.d.ts.map +1 -0
- package/dist/core/error-overlay.d.ts +71 -0
- package/dist/core/error-overlay.d.ts.map +1 -0
- package/dist/core/events.d.ts +119 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/features.d.ts +64 -0
- package/dist/core/features.d.ts.map +1 -0
- package/dist/core/i18n.d.ts +132 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/index.d.ts +37 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/live.d.ts +130 -0
- package/dist/core/live.d.ts.map +1 -0
- package/dist/core/media-session.d.ts +38 -0
- package/dist/core/media-session.d.ts.map +1 -0
- package/dist/core/orientation.d.ts +22 -0
- package/dist/core/orientation.d.ts.map +1 -0
- package/dist/core/popover.d.ts +42 -0
- package/dist/core/popover.d.ts.map +1 -0
- package/dist/core/remote-playback.d.ts +195 -0
- package/dist/core/remote-playback.d.ts.map +1 -0
- package/dist/core/settings-menu.d.ts +132 -0
- package/dist/core/settings-menu.d.ts.map +1 -0
- package/dist/core/spinner.d.ts +67 -0
- package/dist/core/spinner.d.ts.map +1 -0
- package/dist/core/state.d.ts +142 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/text-tracks.d.ts +162 -0
- package/dist/core/text-tracks.d.ts.map +1 -0
- package/dist/core/themes.d.ts +213 -0
- package/dist/core/themes.d.ts.map +1 -0
- package/dist/core/thumbnails.d.ts +124 -0
- package/dist/core/thumbnails.d.ts.map +1 -0
- package/dist/core/tooltips.d.ts +100 -0
- package/dist/core/tooltips.d.ts.map +1 -0
- package/dist/elements/base.d.ts +3 -0
- package/dist/elements/base.d.ts.map +1 -0
- package/dist/elements/index.d.ts +33 -0
- package/dist/elements/index.d.ts.map +1 -0
- package/dist/elements/index.js +55 -0
- package/dist/elements/index.js.map +9 -0
- package/dist/elements/media-airplay-button.d.ts +13 -0
- package/dist/elements/media-airplay-button.d.ts.map +1 -0
- package/dist/elements/media-error-display.d.ts +13 -0
- package/dist/elements/media-error-display.d.ts.map +1 -0
- package/dist/elements/media-fullscreen-button.d.ts +13 -0
- package/dist/elements/media-fullscreen-button.d.ts.map +1 -0
- package/dist/elements/media-live-button.d.ts +13 -0
- package/dist/elements/media-live-button.d.ts.map +1 -0
- package/dist/elements/media-mute-button.d.ts +13 -0
- package/dist/elements/media-mute-button.d.ts.map +1 -0
- package/dist/elements/media-pip-button.d.ts +13 -0
- package/dist/elements/media-pip-button.d.ts.map +1 -0
- package/dist/elements/media-play-button.d.ts +14 -0
- package/dist/elements/media-play-button.d.ts.map +1 -0
- package/dist/elements/media-playback-rate-button.d.ts +10 -0
- package/dist/elements/media-playback-rate-button.d.ts.map +1 -0
- package/dist/elements/media-preview-time.d.ts +18 -0
- package/dist/elements/media-preview-time.d.ts.map +1 -0
- package/dist/elements/media-progress-bar.d.ts +16 -0
- package/dist/elements/media-progress-bar.d.ts.map +1 -0
- package/dist/elements/media-remote-playback-button.d.ts +16 -0
- package/dist/elements/media-remote-playback-button.d.ts.map +1 -0
- package/dist/elements/media-seek-button.d.ts +9 -0
- package/dist/elements/media-seek-button.d.ts.map +1 -0
- package/dist/elements/media-settings-menu.d.ts +32 -0
- package/dist/elements/media-settings-menu.d.ts.map +1 -0
- package/dist/elements/media-time-display.d.ts +21 -0
- package/dist/elements/media-time-display.d.ts.map +1 -0
- package/dist/elements/media-time-group.d.ts +22 -0
- package/dist/elements/media-time-group.d.ts.map +1 -0
- package/dist/elements/media-time-separator.d.ts +13 -0
- package/dist/elements/media-time-separator.d.ts.map +1 -0
- package/dist/elements/media-volume-slider.d.ts +16 -0
- package/dist/elements/media-volume-slider.d.ts.map +1 -0
- package/dist/elements/utils.d.ts +27 -0
- package/dist/elements/utils.d.ts.map +1 -0
- package/dist/elements/video-player.d.ts +23 -0
- package/dist/elements/video-player.d.ts.map +1 -0
- package/dist/elements/video-skin.d.ts +23 -0
- package/dist/elements/video-skin.d.ts.map +1 -0
- package/dist/features/captions.d.ts +8 -0
- package/dist/features/captions.d.ts.map +1 -0
- package/dist/features/create-player.d.ts +45 -0
- package/dist/features/create-player.d.ts.map +1 -0
- package/dist/features/fullscreen.d.ts +8 -0
- package/dist/features/fullscreen.d.ts.map +1 -0
- package/dist/features/gestures.d.ts +8 -0
- package/dist/features/gestures.d.ts.map +1 -0
- package/dist/features/index.d.ts +26 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/index.js +31 -0
- package/dist/features/index.js.map +9 -0
- package/dist/features/keyboard.d.ts +8 -0
- package/dist/features/keyboard.d.ts.map +1 -0
- package/dist/features/pip.d.ts +8 -0
- package/dist/features/pip.d.ts.map +1 -0
- package/dist/features/playback.d.ts +8 -0
- package/dist/features/playback.d.ts.map +1 -0
- package/dist/features/quality.d.ts +8 -0
- package/dist/features/quality.d.ts.map +1 -0
- package/dist/features/types.d.ts +26 -0
- package/dist/features/types.d.ts.map +1 -0
- package/dist/features/volume.d.ts +8 -0
- package/dist/features/volume.d.ts.map +1 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8149 -0
- package/dist/index.js.map +31 -0
- package/dist/layouts/index.d.ts +58 -0
- package/dist/layouts/index.d.ts.map +1 -0
- package/dist/player.d.ts +90 -0
- package/dist/player.d.ts.map +1 -0
- package/dist/plugins/ads.d.ts +311 -0
- package/dist/plugins/ads.d.ts.map +1 -0
- package/dist/plugins/analytics.d.ts +203 -0
- package/dist/plugins/analytics.d.ts.map +1 -0
- package/dist/plugins/end-screen.d.ts +184 -0
- package/dist/plugins/end-screen.d.ts.map +1 -0
- package/dist/plugins/index.d.ts +13 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +20 -0
- package/dist/plugins/index.js.map +9 -0
- package/dist/plugins/skip-segments.d.ts +159 -0
- package/dist/plugins/skip-segments.d.ts.map +1 -0
- package/dist/plugins/watermark.d.ts +157 -0
- package/dist/plugins/watermark.d.ts.map +1 -0
- package/dist/providers/base.d.ts +56 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/dash.d.ts +94 -0
- package/dist/providers/dash.d.ts.map +1 -0
- package/dist/providers/hls.d.ts +182 -0
- package/dist/providers/hls.d.ts.map +1 -0
- package/dist/providers/html5.d.ts +64 -0
- package/dist/providers/html5.d.ts.map +1 -0
- package/dist/providers/index.d.ts +41 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/vimeo.d.ts +157 -0
- package/dist/providers/vimeo.d.ts.map +1 -0
- package/dist/providers/youtube.d.ts +134 -0
- package/dist/providers/youtube.d.ts.map +1 -0
- package/dist/stx/index.d.ts +59 -0
- package/dist/stx/index.d.ts.map +1 -0
- package/dist/stx/index.js +480 -0
- package/dist/stx/index.js.map +11 -0
- package/dist/types.d.ts +724 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/ui/components.d.ts +121 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +16 -0
- package/dist/ui/index.js.map +9 -0
- package/package.json +109 -0
- package/src/cdn.test.ts +10 -0
- package/src/cdn.ts +60 -0
- package/src/config.ts +25 -0
- package/src/core/audio-gain.ts +171 -0
- package/src/core/caption-styles.ts +568 -0
- package/src/core/chapters.ts +551 -0
- package/src/core/error-overlay.ts +354 -0
- package/src/core/events.ts +552 -0
- package/src/core/features.ts +287 -0
- package/src/core/i18n.ts +590 -0
- package/src/core/index.ts +249 -0
- package/src/core/live.ts +472 -0
- package/src/core/media-session.ts +137 -0
- package/src/core/orientation.ts +57 -0
- package/src/core/popover.ts +143 -0
- package/src/core/remote-playback.ts +661 -0
- package/src/core/settings-menu.ts +630 -0
- package/src/core/spinner.ts +310 -0
- package/src/core/state.ts +476 -0
- package/src/core/text-tracks.ts +599 -0
- package/src/core/themes.ts +607 -0
- package/src/core/thumbnails.ts +457 -0
- package/src/core/tooltips.ts +370 -0
- package/src/elements/base.ts +2 -0
- package/src/elements/index.ts +82 -0
- package/src/elements/media-airplay-button.ts +78 -0
- package/src/elements/media-error-display.ts +47 -0
- package/src/elements/media-fullscreen-button.ts +72 -0
- package/src/elements/media-live-button.ts +65 -0
- package/src/elements/media-mute-button.ts +69 -0
- package/src/elements/media-pip-button.ts +66 -0
- package/src/elements/media-play-button.ts +69 -0
- package/src/elements/media-playback-rate-button.ts +59 -0
- package/src/elements/media-preview-time.ts +75 -0
- package/src/elements/media-progress-bar.ts +200 -0
- package/src/elements/media-remote-playback-button.ts +108 -0
- package/src/elements/media-seek-button.ts +50 -0
- package/src/elements/media-settings-menu.ts +302 -0
- package/src/elements/media-time-display.ts +99 -0
- package/src/elements/media-time-group.ts +41 -0
- package/src/elements/media-time-separator.ts +35 -0
- package/src/elements/media-volume-slider.ts +140 -0
- package/src/elements/utils.ts +82 -0
- package/src/elements/video-player.ts +175 -0
- package/src/elements/video-skin.ts +127 -0
- package/src/features/captions.ts +56 -0
- package/src/features/create-player.ts +91 -0
- package/src/features/fullscreen.ts +44 -0
- package/src/features/gestures.ts +44 -0
- package/src/features/index.ts +44 -0
- package/src/features/keyboard.ts +89 -0
- package/src/features/pip.ts +47 -0
- package/src/features/playback.ts +68 -0
- package/src/features/quality.ts +19 -0
- package/src/features/types.ts +28 -0
- package/src/features/volume.ts +46 -0
- package/src/index.ts +635 -0
- package/src/layouts/index.ts +672 -0
- package/src/player.ts +821 -0
- package/src/plugins/ads.ts +1045 -0
- package/src/plugins/analytics.ts +673 -0
- package/src/plugins/end-screen.ts +793 -0
- package/src/plugins/index.ts +61 -0
- package/src/plugins/skip-segments.ts +537 -0
- package/src/plugins/watermark.ts +537 -0
- package/src/providers/base.ts +197 -0
- package/src/providers/dash.ts +588 -0
- package/src/providers/hls.ts +624 -0
- package/src/providers/html5.ts +473 -0
- package/src/providers/index.ts +97 -0
- package/src/providers/vimeo.ts +533 -0
- package/src/providers/youtube.ts +542 -0
- package/src/stx/index.ts +606 -0
- package/src/types.ts +874 -0
- package/src/ui/components.ts +910 -0
- package/src/ui/index.ts +29 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/audio-gain.ts", "../src/core/text-tracks.ts", "../src/core/themes.ts", "../src/core/thumbnails.ts", "../src/core/i18n.ts", "../src/core/live.ts", "../src/core/remote-playback.ts", "../src/core/chapters.ts", "../src/core/spinner.ts", "../src/core/caption-styles.ts", "../src/core/tooltips.ts", "../src/core/error-overlay.ts", "../src/core/settings-menu.ts", "../src/layouts/index.ts", "../src/ui/components.ts", "../src/stx/index.ts", "../src/plugins/analytics.ts", "../src/plugins/ads.ts", "../src/plugins/skip-segments.ts", "../src/plugins/end-screen.ts", "../src/plugins/watermark.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * ts-video-player Audio Gain\n *\n * Audio gain/boost using Web Audio API.\n *\n * @module core/audio-gain\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface AudioGainAdapter {\n /** Current gain value (1.0 = normal, >1.0 = boosted) */\n readonly currentGain: number\n /** Whether audio gain is supported */\n readonly supported: boolean\n /** Set the gain value */\n setGain(gain: number): void\n /** Remove gain (reset to 1.0) */\n removeGain(): void\n /** Destroy the gain node */\n destroy(): void\n}\n\n// =============================================================================\n// Audio Gain Implementation\n// =============================================================================\n\n/**\n * Audio gain controller using Web Audio API\n */\nexport class AudioGain implements AudioGainAdapter {\n private media: HTMLMediaElement\n private audioContext: AudioContext | null = null\n private sourceNode: MediaElementAudioSourceNode | null = null\n private gainNode: GainNode | null = null\n private _currentGain = 1\n\n constructor(media: HTMLMediaElement) {\n this.media = media\n }\n\n get currentGain(): number {\n return this._currentGain\n }\n\n get supported(): boolean {\n return typeof AudioContext !== 'undefined' || typeof (window as any).webkitAudioContext !== 'undefined'\n }\n\n /**\n * Set the gain value\n *\n * @param gain - Gain value (1.0 = normal, 2.0 = 2x volume)\n */\n setGain(gain: number): void {\n if (!this.supported) return\n\n // Clamp gain to reasonable range\n const clampedGain = Math.max(0, Math.min(10, gain))\n\n // If gain is 1, just remove the effect\n if (clampedGain === 1 && this._currentGain !== 1) {\n this.removeGain()\n return\n }\n\n // Create audio context if needed\n if (!this.audioContext) {\n this.createAudioContext()\n }\n\n if (!this.gainNode) return\n\n this._currentGain = clampedGain\n this.gainNode.gain.value = clampedGain\n }\n\n /**\n * Remove gain (reset to 1.0)\n */\n removeGain(): void {\n this._currentGain = 1\n\n if (this.gainNode) {\n this.gainNode.gain.value = 1\n }\n\n // Optionally disconnect to save resources\n // this.disconnectNodes()\n }\n\n /**\n * Destroy the audio context and nodes\n */\n destroy(): void {\n this.disconnectNodes()\n\n if (this.audioContext) {\n this.audioContext.close().catch(() => {})\n this.audioContext = null\n }\n\n this._currentGain = 1\n }\n\n private createAudioContext(): void {\n try {\n // Create audio context\n const AudioContextClass = window.AudioContext || (window as any).webkitAudioContext\n this.audioContext = new AudioContextClass()\n\n // Create source node from media element\n this.sourceNode = this.audioContext.createMediaElementSource(this.media)\n\n // Create gain node\n this.gainNode = this.audioContext.createGain()\n this.gainNode.gain.value = this._currentGain\n\n // Connect: source -> gain -> destination\n this.sourceNode.connect(this.gainNode)\n this.gainNode.connect(this.audioContext.destination)\n } catch (error) {\n console.warn('[ts-video-player] Failed to create audio gain context:', error)\n }\n }\n\n private disconnectNodes(): void {\n try {\n this.sourceNode?.disconnect()\n this.gainNode?.disconnect()\n } catch {\n // Ignore disconnect errors\n }\n\n this.sourceNode = null\n this.gainNode = null\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Create an audio gain controller for a media element\n */\nexport function createAudioGain(media: HTMLMediaElement): AudioGainAdapter {\n return new AudioGain(media)\n}\n\n// =============================================================================\n// Presets\n// =============================================================================\n\n/**\n * Common gain presets\n */\nexport const GAIN_PRESETS = {\n normal: 1.0,\n boost_small: 1.5,\n boost_medium: 2.0,\n boost_large: 3.0,\n boost_max: 5.0,\n} as const\n\n/**\n * Available gain steps for UI\n */\nexport const GAIN_STEPS = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5] as const\n",
|
|
6
|
+
"/**\n * ts-video-player Text Tracks\n *\n * Comprehensive text track management for captions and subtitles.\n * Supports VTT, SRT, and external track loading.\n *\n * @module core/text-tracks\n */\n\nimport type { TextTrack, TextTrackCue } from '../types'\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Text track ready state\n */\nexport enum TextTrackReadyState {\n /** Not loaded */\n NotLoaded = 0,\n /** Loading */\n Loading = 1,\n /** Ready */\n Ready = 2,\n /** Error */\n Error = 3,\n}\n\n/**\n * Text track init options\n */\nexport interface TextTrackInit {\n /** Track kind */\n kind: TextTrack['kind']\n /** Track label */\n label: string\n /** Track language (BCP 47) */\n language: string\n /** Track source URL */\n src?: string\n /** Whether this is the default track */\n default?: boolean\n /** Track content (VTT/SRT string) */\n content?: string\n}\n\n/**\n * VTT cue init options\n */\nexport interface VTTCueInit {\n /** Cue ID */\n id?: string\n /** Start time in seconds */\n startTime: number\n /** End time in seconds */\n endTime: number\n /** Cue text */\n text: string\n /** Vertical setting */\n vertical?: '' | 'rl' | 'lr'\n /** Line position */\n line?: number | 'auto'\n /** Line alignment */\n lineAlign?: 'start' | 'center' | 'end'\n /** Position */\n position?: number | 'auto'\n /** Position alignment */\n positionAlign?: 'line-left' | 'center' | 'line-right' | 'auto'\n /** Size (percentage) */\n size?: number\n /** Alignment */\n align?: 'start' | 'center' | 'end' | 'left' | 'right'\n}\n\n// =============================================================================\n// Text Track List\n// =============================================================================\n\n/**\n * Manages a list of text tracks\n */\nexport class TextTrackList {\n private tracks: ManagedTextTrack[] = []\n private listeners = new Set<(tracks: TextTrack[]) => void>()\n private video: HTMLVideoElement | null = null\n\n constructor(video?: HTMLVideoElement) {\n this.video = video || null\n }\n\n /**\n * Get all tracks\n */\n getAll(): TextTrack[] {\n return this.tracks.map((t) => t.toJSON())\n }\n\n /**\n * Get track by ID\n */\n get(id: string): TextTrack | null {\n const track = this.tracks.find((t) => t.id === id)\n return track?.toJSON() || null\n }\n\n /**\n * Get currently showing track\n */\n getShowing(): TextTrack | null {\n const track = this.tracks.find((t) => t.mode === 'showing')\n return track?.toJSON() || null\n }\n\n /**\n * Add a new text track\n */\n async add(init: TextTrackInit): Promise<TextTrack> {\n const id = `track-${this.tracks.length}`\n const track = new ManagedTextTrack({\n id,\n ...init,\n })\n\n this.tracks.push(track)\n\n // Load track if src provided\n if (init.src) {\n await track.load(init.src)\n } else if (init.content) {\n track.parseContent(init.content)\n }\n\n // Add to video element if available\n if (this.video) {\n this.addToVideo(track)\n }\n\n this.notify()\n return track.toJSON()\n }\n\n /**\n * Remove a text track\n */\n remove(id: string): void {\n const index = this.tracks.findIndex((t) => t.id === id)\n if (index !== -1) {\n const track = this.tracks[index]\n track.dispose()\n this.tracks.splice(index, 1)\n this.notify()\n }\n }\n\n /**\n * Set track mode\n */\n setMode(id: string, mode: TextTrack['mode']): void {\n const track = this.tracks.find((t) => t.id === id)\n if (!track) return\n\n // Disable other showing tracks if enabling this one\n if (mode === 'showing') {\n for (const t of this.tracks) {\n if (t.id !== id && t.mode === 'showing') {\n t.mode = 'disabled'\n }\n }\n }\n\n track.mode = mode\n this.notify()\n }\n\n /**\n * Get active cues at a specific time\n */\n getActiveCues(time: number): TextTrackCue[] {\n const showing = this.tracks.find((t) => t.mode === 'showing')\n if (!showing) return []\n\n return showing.cues.filter((cue) => time >= cue.startTime && time <= cue.endTime)\n }\n\n /**\n * Subscribe to track changes\n */\n subscribe(listener: (tracks: TextTrack[]) => void): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n /**\n * Clear all tracks\n */\n clear(): void {\n for (const track of this.tracks) {\n track.dispose()\n }\n this.tracks = []\n this.notify()\n }\n\n private addToVideo(track: ManagedTextTrack): void {\n if (!this.video) return\n\n // Create native track element\n const trackEl = document.createElement('track')\n trackEl.kind = track.kind\n trackEl.label = track.label\n trackEl.srclang = track.language\n if (track.src) trackEl.src = track.src\n if (track.default) trackEl.default = true\n\n this.video.appendChild(trackEl)\n }\n\n private notify(): void {\n const tracks = this.getAll()\n this.listeners.forEach((l) => l(tracks))\n }\n}\n\n// =============================================================================\n// Managed Text Track\n// =============================================================================\n\nclass ManagedTextTrack {\n readonly id: string\n readonly kind: TextTrack['kind']\n readonly label: string\n readonly language: string\n readonly src?: string\n readonly default: boolean\n\n private _mode: TextTrack['mode'] = 'disabled'\n private _cues: TextTrackCue[] = []\n private _readyState: TextTrackReadyState = TextTrackReadyState.NotLoaded\n\n constructor(init: TextTrackInit & { id: string }) {\n this.id = init.id\n this.kind = init.kind\n this.label = init.label\n this.language = init.language\n this.src = init.src\n this.default = init.default || false\n\n if (init.default) {\n this._mode = 'showing'\n }\n }\n\n get mode(): TextTrack['mode'] {\n return this._mode\n }\n\n set mode(value: TextTrack['mode']) {\n this._mode = value\n }\n\n get cues(): TextTrackCue[] {\n return this._cues\n }\n\n get readyState(): TextTrackReadyState {\n return this._readyState\n }\n\n /**\n * Load track from URL\n */\n async load(url: string): Promise<void> {\n this._readyState = TextTrackReadyState.Loading\n\n try {\n const response = await fetch(url)\n if (!response.ok) {\n throw new Error(`Failed to load track: ${response.status}`)\n }\n\n const content = await response.text()\n this.parseContent(content)\n this._readyState = TextTrackReadyState.Ready\n } catch (error) {\n this._readyState = TextTrackReadyState.Error\n console.warn('[ts-video-player] Failed to load text track:', error)\n }\n }\n\n /**\n * Parse track content (VTT or SRT)\n */\n parseContent(content: string): void {\n // Detect format\n if (content.trim().startsWith('WEBVTT')) {\n this._cues = parseVTT(content)\n } else {\n this._cues = parseSRT(content)\n }\n this._readyState = TextTrackReadyState.Ready\n }\n\n /**\n * Add a cue\n */\n addCue(cue: VTTCueInit): void {\n this._cues.push({\n id: cue.id || `cue-${this._cues.length}`,\n startTime: cue.startTime,\n endTime: cue.endTime,\n text: cue.text,\n position: cue.position,\n line: cue.line,\n align: cue.align,\n })\n }\n\n /**\n * Remove a cue\n */\n removeCue(id: string): void {\n const index = this._cues.findIndex((c) => c.id === id)\n if (index !== -1) {\n this._cues.splice(index, 1)\n }\n }\n\n /**\n * Convert to plain object\n */\n toJSON(): TextTrack {\n return {\n id: this.id,\n kind: this.kind,\n label: this.label,\n language: this.language,\n src: this.src,\n default: this.default,\n mode: this._mode,\n cues: this._cues,\n }\n }\n\n /**\n * Dispose the track\n */\n dispose(): void {\n this._cues = []\n }\n}\n\n// =============================================================================\n// VTT Parser\n// =============================================================================\n\n/**\n * Parse VTT content\n */\nfunction parseVTT(content: string): TextTrackCue[] {\n const cues: TextTrackCue[] = []\n const lines = content.split(/\\r?\\n/)\n\n let i = 0\n\n // Skip header\n while (i < lines.length && !lines[i].includes('-->')) {\n i++\n }\n\n while (i < lines.length) {\n const line = lines[i].trim()\n\n // Check for cue timing line\n if (line.includes('-->')) {\n const [start, end] = line.split('-->').map((t) => parseVTTTime(t.trim()))\n\n // Collect cue text\n i++\n const textLines: string[] = []\n while (i < lines.length && lines[i].trim() !== '') {\n textLines.push(lines[i])\n i++\n }\n\n cues.push({\n id: `cue-${cues.length}`,\n startTime: start,\n endTime: end,\n text: textLines.join('\\n'),\n })\n }\n\n i++\n }\n\n return cues\n}\n\n/**\n * Parse VTT time format (00:00:00.000 or 00:00.000)\n */\nfunction parseVTTTime(time: string): number {\n const parts = time.split(':')\n let seconds = 0\n\n if (parts.length === 3) {\n // HH:MM:SS.mmm\n seconds = parseInt(parts[0], 10) * 3600 + parseInt(parts[1], 10) * 60 + parseFloat(parts[2])\n } else if (parts.length === 2) {\n // MM:SS.mmm\n seconds = parseInt(parts[0], 10) * 60 + parseFloat(parts[1])\n }\n\n return seconds\n}\n\n// =============================================================================\n// SRT Parser\n// =============================================================================\n\n/**\n * Parse SRT content\n */\nfunction parseSRT(content: string): TextTrackCue[] {\n const cues: TextTrackCue[] = []\n const blocks = content.trim().split(/\\r?\\n\\r?\\n/)\n\n for (const block of blocks) {\n const lines = block.split(/\\r?\\n/)\n if (lines.length < 2) continue\n\n // Find timing line\n let timingIndex = 0\n while (timingIndex < lines.length && !lines[timingIndex].includes('-->')) {\n timingIndex++\n }\n\n if (timingIndex >= lines.length) continue\n\n const timingLine = lines[timingIndex]\n const [start, end] = timingLine.split('-->').map((t) => parseSRTTime(t.trim()))\n\n // Collect text\n const textLines = lines.slice(timingIndex + 1)\n\n cues.push({\n id: lines[0] || `cue-${cues.length}`,\n startTime: start,\n endTime: end,\n text: textLines.join('\\n'),\n })\n }\n\n return cues\n}\n\n/**\n * Parse SRT time format (00:00:00,000)\n */\nfunction parseSRTTime(time: string): number {\n // SRT uses comma for milliseconds\n const normalized = time.replace(',', '.')\n return parseVTTTime(normalized)\n}\n\n// =============================================================================\n// Caption Renderer\n// =============================================================================\n\nexport interface CaptionRendererOptions {\n /** Container element */\n container: HTMLElement\n /** Font size */\n fontSize?: number\n /** Font family */\n fontFamily?: string\n /** Text color */\n color?: string\n /** Background color */\n backgroundColor?: string\n /** Background opacity */\n backgroundOpacity?: number\n /** Text shadow */\n textShadow?: boolean\n}\n\n/**\n * Render captions on screen\n */\nexport class CaptionRenderer {\n private container: HTMLElement\n private element: HTMLElement | null = null\n private options: Required<Omit<CaptionRendererOptions, 'container'>>\n\n constructor(options: CaptionRendererOptions) {\n this.container = options.container\n this.options = {\n fontSize: options.fontSize || 18,\n fontFamily: options.fontFamily || 'sans-serif',\n color: options.color || '#ffffff',\n backgroundColor: options.backgroundColor || '#000000',\n backgroundOpacity: options.backgroundOpacity || 0.75,\n textShadow: options.textShadow !== false,\n }\n\n this.createCaptionElement()\n }\n\n private createCaptionElement(): void {\n this.element = document.createElement('div')\n this.element.className = 'ts-video-player__captions'\n this.element.setAttribute('aria-live', 'polite')\n this.element.setAttribute('aria-atomic', 'true')\n this.applyStyles()\n this.container.appendChild(this.element)\n }\n\n private applyStyles(): void {\n if (!this.element) return\n\n const bgColor = this.hexToRgba(this.options.backgroundColor, this.options.backgroundOpacity)\n\n Object.assign(this.element.style, {\n position: 'absolute',\n bottom: '10%',\n left: '50%',\n transform: 'translateX(-50%)',\n maxWidth: '80%',\n textAlign: 'center',\n fontSize: `${this.options.fontSize}px`,\n fontFamily: this.options.fontFamily,\n color: this.options.color,\n backgroundColor: bgColor,\n padding: '4px 8px',\n borderRadius: '4px',\n lineHeight: '1.4',\n pointerEvents: 'none',\n zIndex: '10',\n whiteSpace: 'pre-wrap',\n textShadow: this.options.textShadow ? '0 1px 2px rgba(0,0,0,0.5)' : 'none',\n })\n }\n\n /**\n * Update the displayed text\n */\n setText(text: string): void {\n if (!this.element) return\n\n if (text) {\n this.element.textContent = text\n this.element.style.display = 'block'\n } else {\n this.element.style.display = 'none'\n }\n }\n\n /**\n * Update renderer options\n */\n setOptions(options: Partial<Omit<CaptionRendererOptions, 'container'>>): void {\n Object.assign(this.options, options)\n this.applyStyles()\n }\n\n /**\n * Destroy the renderer\n */\n destroy(): void {\n this.element?.remove()\n this.element = null\n }\n\n private hexToRgba(hex: string, alpha: number): string {\n const r = parseInt(hex.slice(1, 3), 16)\n const g = parseInt(hex.slice(3, 5), 16)\n const b = parseInt(hex.slice(5, 7), 16)\n return `rgba(${r}, ${g}, ${b}, ${alpha})`\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a new text track list\n */\nexport function createTextTrackList(video?: HTMLVideoElement): TextTrackList {\n return new TextTrackList(video)\n}\n\n/**\n * Create a caption renderer\n */\nexport function createCaptionRenderer(options: CaptionRendererOptions): CaptionRenderer {\n return new CaptionRenderer(options)\n}\n",
|
|
7
|
+
"/**\n * CSS Variables / Theming System\n *\n * Provides a comprehensive theming system using CSS custom properties.\n *\n * @module core/themes\n */\n\n// =============================================================================\n// Theme Types\n// =============================================================================\n\nexport interface ThemeColors {\n /** Primary brand color (default: #00a8ff) */\n primary?: string\n /** Secondary accent color */\n secondary?: string\n /** Background color */\n background?: string\n /** Text color */\n text?: string\n /** Text color on primary background */\n textOnPrimary?: string\n /** Border color */\n border?: string\n /** Focus ring color */\n focus?: string\n /** Error/danger color */\n error?: string\n /** Success color */\n success?: string\n /** Warning color */\n warning?: string\n}\n\nexport interface ThemeTypography {\n /** Font family for UI elements */\n fontFamily?: string\n /** Base font size */\n fontSize?: string\n /** Small font size */\n fontSizeSmall?: string\n /** Large font size */\n fontSizeLarge?: string\n /** Font weight */\n fontWeight?: string\n /** Bold font weight */\n fontWeightBold?: string\n /** Line height */\n lineHeight?: string\n}\n\nexport interface ThemeSpacing {\n /** Extra small spacing (4px) */\n xs?: string\n /** Small spacing (8px) */\n sm?: string\n /** Medium spacing (12px) */\n md?: string\n /** Large spacing (16px) */\n lg?: string\n /** Extra large spacing (24px) */\n xl?: string\n}\n\nexport interface ThemeBorderRadius {\n /** Small border radius */\n sm?: string\n /** Medium border radius */\n md?: string\n /** Large border radius */\n lg?: string\n /** Full/round border radius */\n full?: string\n}\n\nexport interface ThemeShadows {\n /** Small shadow */\n sm?: string\n /** Medium shadow */\n md?: string\n /** Large shadow */\n lg?: string\n}\n\nexport interface ThemeTransitions {\n /** Fast transition duration */\n fast?: string\n /** Normal transition duration */\n normal?: string\n /** Slow transition duration */\n slow?: string\n /** Default easing function */\n easing?: string\n}\n\nexport interface ThemeSlider {\n /** Slider track height */\n trackHeight?: string\n /** Slider thumb size */\n thumbSize?: string\n /** Slider track background */\n trackBg?: string\n /** Slider track fill background */\n trackFillBg?: string\n /** Slider buffered background */\n bufferedBg?: string\n /** Slider thumb background */\n thumbBg?: string\n /** Slider thumb border */\n thumbBorder?: string\n}\n\nexport interface ThemeControls {\n /** Controls bar height */\n height?: string\n /** Controls background */\n background?: string\n /** Controls padding */\n padding?: string\n /** Button size */\n buttonSize?: string\n /** Icon size */\n iconSize?: string\n /** Tooltip background */\n tooltipBg?: string\n /** Tooltip text color */\n tooltipText?: string\n}\n\nexport interface ThemeMenu {\n /** Menu background */\n background?: string\n /** Menu border radius */\n borderRadius?: string\n /** Menu shadow */\n shadow?: string\n /** Menu item height */\n itemHeight?: string\n /** Menu item hover background */\n itemHoverBg?: string\n /** Menu item active background */\n itemActiveBg?: string\n}\n\nexport interface ThemeCaptions {\n /** Caption font family */\n fontFamily?: string\n /** Caption font size */\n fontSize?: string\n /** Caption text color */\n textColor?: string\n /** Caption background */\n background?: string\n /** Caption text shadow */\n textShadow?: string\n /** Caption border radius */\n borderRadius?: string\n /** Caption padding */\n padding?: string\n}\n\nexport interface ThemeThumbnails {\n /** Thumbnail width */\n width?: string\n /** Thumbnail height */\n height?: string\n /** Thumbnail border radius */\n borderRadius?: string\n /** Thumbnail border */\n border?: string\n /** Thumbnail shadow */\n shadow?: string\n}\n\nexport interface ThemeLive {\n /** Live indicator color */\n indicatorColor?: string\n /** Live indicator size */\n indicatorSize?: string\n /** Live badge background */\n badgeBg?: string\n /** Live badge text color */\n badgeText?: string\n}\n\nexport interface Theme {\n colors?: ThemeColors\n typography?: ThemeTypography\n spacing?: ThemeSpacing\n borderRadius?: ThemeBorderRadius\n shadows?: ThemeShadows\n transitions?: ThemeTransitions\n slider?: ThemeSlider\n controls?: ThemeControls\n menu?: ThemeMenu\n captions?: ThemeCaptions\n thumbnails?: ThemeThumbnails\n live?: ThemeLive\n}\n\n// =============================================================================\n// Default Theme\n// =============================================================================\n\nexport const defaultTheme: Required<Theme> = {\n colors: {\n primary: '#00a8ff',\n secondary: '#6c5ce7',\n background: 'rgba(0, 0, 0, 0.85)',\n text: '#ffffff',\n textOnPrimary: '#ffffff',\n border: 'rgba(255, 255, 255, 0.1)',\n focus: '#00a8ff',\n error: '#ff4757',\n success: '#2ed573',\n warning: '#ffa502',\n },\n typography: {\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif',\n fontSize: '14px',\n fontSizeSmall: '12px',\n fontSizeLarge: '16px',\n fontWeight: '400',\n fontWeightBold: '600',\n lineHeight: '1.5',\n },\n spacing: {\n xs: '4px',\n sm: '8px',\n md: '12px',\n lg: '16px',\n xl: '24px',\n },\n borderRadius: {\n sm: '4px',\n md: '8px',\n lg: '12px',\n full: '9999px',\n },\n shadows: {\n sm: '0 1px 2px rgba(0, 0, 0, 0.2)',\n md: '0 4px 6px rgba(0, 0, 0, 0.3)',\n lg: '0 10px 15px rgba(0, 0, 0, 0.4)',\n },\n transitions: {\n fast: '100ms',\n normal: '200ms',\n slow: '300ms',\n easing: 'cubic-bezier(0.4, 0, 0.2, 1)',\n },\n slider: {\n trackHeight: '4px',\n thumbSize: '14px',\n trackBg: 'rgba(255, 255, 255, 0.2)',\n trackFillBg: 'var(--tsvp-color-primary)',\n bufferedBg: 'rgba(255, 255, 255, 0.4)',\n thumbBg: '#ffffff',\n thumbBorder: 'none',\n },\n controls: {\n height: '48px',\n background: 'linear-gradient(transparent, rgba(0, 0, 0, 0.7))',\n padding: '0 12px',\n buttonSize: '40px',\n iconSize: '24px',\n tooltipBg: 'rgba(0, 0, 0, 0.9)',\n tooltipText: '#ffffff',\n },\n menu: {\n background: 'rgba(20, 20, 20, 0.95)',\n borderRadius: '8px',\n shadow: '0 4px 20px rgba(0, 0, 0, 0.5)',\n itemHeight: '40px',\n itemHoverBg: 'rgba(255, 255, 255, 0.1)',\n itemActiveBg: 'rgba(0, 168, 255, 0.2)',\n },\n captions: {\n fontFamily: 'inherit',\n fontSize: '1.5em',\n textColor: '#ffffff',\n background: 'rgba(0, 0, 0, 0.75)',\n textShadow: '0 1px 2px rgba(0, 0, 0, 0.5)',\n borderRadius: '4px',\n padding: '4px 8px',\n },\n thumbnails: {\n width: '160px',\n height: '90px',\n borderRadius: '4px',\n border: '2px solid rgba(255, 255, 255, 0.2)',\n shadow: '0 4px 12px rgba(0, 0, 0, 0.4)',\n },\n live: {\n indicatorColor: '#ff0000',\n indicatorSize: '8px',\n badgeBg: '#ff0000',\n badgeText: '#ffffff',\n },\n}\n\n// =============================================================================\n// Preset Themes\n// =============================================================================\n\nexport const darkTheme: Theme = {\n colors: {\n primary: '#00a8ff',\n background: 'rgba(0, 0, 0, 0.9)',\n text: '#ffffff',\n },\n}\n\nexport const lightTheme: Theme = {\n colors: {\n primary: '#0070f3',\n background: 'rgba(255, 255, 255, 0.95)',\n text: '#1a1a1a',\n textOnPrimary: '#ffffff',\n border: 'rgba(0, 0, 0, 0.1)',\n },\n controls: {\n background: 'linear-gradient(transparent, rgba(255, 255, 255, 0.9))',\n tooltipBg: 'rgba(0, 0, 0, 0.8)',\n },\n menu: {\n background: 'rgba(255, 255, 255, 0.98)',\n shadow: '0 4px 20px rgba(0, 0, 0, 0.15)',\n itemHoverBg: 'rgba(0, 0, 0, 0.05)',\n itemActiveBg: 'rgba(0, 112, 243, 0.1)',\n },\n captions: {\n background: 'rgba(255, 255, 255, 0.9)',\n textColor: '#1a1a1a',\n },\n}\n\nexport const minimalTheme: Theme = {\n colors: {\n primary: '#ffffff',\n background: 'transparent',\n },\n controls: {\n height: '40px',\n background: 'transparent',\n },\n slider: {\n trackHeight: '2px',\n thumbSize: '10px',\n },\n}\n\nexport const netflixTheme: Theme = {\n colors: {\n primary: '#e50914',\n background: 'rgba(0, 0, 0, 0.85)',\n },\n slider: {\n trackFillBg: '#e50914',\n },\n live: {\n indicatorColor: '#e50914',\n badgeBg: '#e50914',\n },\n}\n\nexport const youtubeTheme: Theme = {\n colors: {\n primary: '#ff0000',\n background: 'rgba(0, 0, 0, 0.85)',\n },\n slider: {\n trackHeight: '3px',\n thumbSize: '13px',\n trackFillBg: '#ff0000',\n },\n controls: {\n height: '48px',\n },\n live: {\n indicatorColor: '#ff0000',\n badgeBg: '#ff0000',\n },\n}\n\nexport const vimeoTheme: Theme = {\n colors: {\n primary: '#00adef',\n background: 'rgba(0, 0, 0, 0.85)',\n },\n slider: {\n trackFillBg: '#00adef',\n },\n}\n\nexport const presetThemes: Record<string, Theme> = {\n dark: darkTheme,\n light: lightTheme,\n minimal: minimalTheme,\n netflix: netflixTheme,\n youtube: youtubeTheme,\n vimeo: vimeoTheme,\n}\n\nexport type PresetThemeName = keyof typeof presetThemes\n\n// =============================================================================\n// Theme Utilities\n// =============================================================================\n\n/**\n * Deep merge two theme objects\n */\nexport function mergeThemes(base: Theme, override: Theme): Theme {\n const result: Theme = { ...base }\n\n for (const key of Object.keys(override) as (keyof Theme)[]) {\n const baseValue = base[key]\n const overrideValue = override[key]\n\n if (overrideValue && typeof overrideValue === 'object') {\n result[key] = {\n ...(baseValue || {}),\n ...overrideValue,\n } as any\n }\n else if (overrideValue !== undefined) {\n result[key] = overrideValue as any\n }\n }\n\n return result\n}\n\n/**\n * Generate CSS custom properties from a theme\n */\nexport function generateThemeCSS(theme: Theme, prefix = 'tsvp'): string {\n const merged = mergeThemes(defaultTheme, theme)\n const vars: string[] = []\n\n // Colors\n if (merged.colors) {\n vars.push(`--${prefix}-color-primary: ${merged.colors.primary};`)\n vars.push(`--${prefix}-color-secondary: ${merged.colors.secondary};`)\n vars.push(`--${prefix}-color-background: ${merged.colors.background};`)\n vars.push(`--${prefix}-color-text: ${merged.colors.text};`)\n vars.push(`--${prefix}-color-text-on-primary: ${merged.colors.textOnPrimary};`)\n vars.push(`--${prefix}-color-border: ${merged.colors.border};`)\n vars.push(`--${prefix}-color-focus: ${merged.colors.focus};`)\n vars.push(`--${prefix}-color-error: ${merged.colors.error};`)\n vars.push(`--${prefix}-color-success: ${merged.colors.success};`)\n vars.push(`--${prefix}-color-warning: ${merged.colors.warning};`)\n }\n\n // Typography\n if (merged.typography) {\n vars.push(`--${prefix}-font-family: ${merged.typography.fontFamily};`)\n vars.push(`--${prefix}-font-size: ${merged.typography.fontSize};`)\n vars.push(`--${prefix}-font-size-sm: ${merged.typography.fontSizeSmall};`)\n vars.push(`--${prefix}-font-size-lg: ${merged.typography.fontSizeLarge};`)\n vars.push(`--${prefix}-font-weight: ${merged.typography.fontWeight};`)\n vars.push(`--${prefix}-font-weight-bold: ${merged.typography.fontWeightBold};`)\n vars.push(`--${prefix}-line-height: ${merged.typography.lineHeight};`)\n }\n\n // Spacing\n if (merged.spacing) {\n vars.push(`--${prefix}-spacing-xs: ${merged.spacing.xs};`)\n vars.push(`--${prefix}-spacing-sm: ${merged.spacing.sm};`)\n vars.push(`--${prefix}-spacing-md: ${merged.spacing.md};`)\n vars.push(`--${prefix}-spacing-lg: ${merged.spacing.lg};`)\n vars.push(`--${prefix}-spacing-xl: ${merged.spacing.xl};`)\n }\n\n // Border Radius\n if (merged.borderRadius) {\n vars.push(`--${prefix}-radius-sm: ${merged.borderRadius.sm};`)\n vars.push(`--${prefix}-radius-md: ${merged.borderRadius.md};`)\n vars.push(`--${prefix}-radius-lg: ${merged.borderRadius.lg};`)\n vars.push(`--${prefix}-radius-full: ${merged.borderRadius.full};`)\n }\n\n // Shadows\n if (merged.shadows) {\n vars.push(`--${prefix}-shadow-sm: ${merged.shadows.sm};`)\n vars.push(`--${prefix}-shadow-md: ${merged.shadows.md};`)\n vars.push(`--${prefix}-shadow-lg: ${merged.shadows.lg};`)\n }\n\n // Transitions\n if (merged.transitions) {\n vars.push(`--${prefix}-transition-fast: ${merged.transitions.fast};`)\n vars.push(`--${prefix}-transition-normal: ${merged.transitions.normal};`)\n vars.push(`--${prefix}-transition-slow: ${merged.transitions.slow};`)\n vars.push(`--${prefix}-transition-easing: ${merged.transitions.easing};`)\n }\n\n // Slider\n if (merged.slider) {\n vars.push(`--${prefix}-slider-track-height: ${merged.slider.trackHeight};`)\n vars.push(`--${prefix}-slider-thumb-size: ${merged.slider.thumbSize};`)\n vars.push(`--${prefix}-slider-track-bg: ${merged.slider.trackBg};`)\n vars.push(`--${prefix}-slider-track-fill-bg: ${merged.slider.trackFillBg};`)\n vars.push(`--${prefix}-slider-buffered-bg: ${merged.slider.bufferedBg};`)\n vars.push(`--${prefix}-slider-thumb-bg: ${merged.slider.thumbBg};`)\n vars.push(`--${prefix}-slider-thumb-border: ${merged.slider.thumbBorder};`)\n }\n\n // Controls\n if (merged.controls) {\n vars.push(`--${prefix}-controls-height: ${merged.controls.height};`)\n vars.push(`--${prefix}-controls-bg: ${merged.controls.background};`)\n vars.push(`--${prefix}-controls-padding: ${merged.controls.padding};`)\n vars.push(`--${prefix}-controls-button-size: ${merged.controls.buttonSize};`)\n vars.push(`--${prefix}-controls-icon-size: ${merged.controls.iconSize};`)\n vars.push(`--${prefix}-controls-tooltip-bg: ${merged.controls.tooltipBg};`)\n vars.push(`--${prefix}-controls-tooltip-text: ${merged.controls.tooltipText};`)\n }\n\n // Menu\n if (merged.menu) {\n vars.push(`--${prefix}-menu-bg: ${merged.menu.background};`)\n vars.push(`--${prefix}-menu-radius: ${merged.menu.borderRadius};`)\n vars.push(`--${prefix}-menu-shadow: ${merged.menu.shadow};`)\n vars.push(`--${prefix}-menu-item-height: ${merged.menu.itemHeight};`)\n vars.push(`--${prefix}-menu-item-hover-bg: ${merged.menu.itemHoverBg};`)\n vars.push(`--${prefix}-menu-item-active-bg: ${merged.menu.itemActiveBg};`)\n }\n\n // Captions\n if (merged.captions) {\n vars.push(`--${prefix}-captions-font-family: ${merged.captions.fontFamily};`)\n vars.push(`--${prefix}-captions-font-size: ${merged.captions.fontSize};`)\n vars.push(`--${prefix}-captions-text-color: ${merged.captions.textColor};`)\n vars.push(`--${prefix}-captions-bg: ${merged.captions.background};`)\n vars.push(`--${prefix}-captions-text-shadow: ${merged.captions.textShadow};`)\n vars.push(`--${prefix}-captions-radius: ${merged.captions.borderRadius};`)\n vars.push(`--${prefix}-captions-padding: ${merged.captions.padding};`)\n }\n\n // Thumbnails\n if (merged.thumbnails) {\n vars.push(`--${prefix}-thumbnail-width: ${merged.thumbnails.width};`)\n vars.push(`--${prefix}-thumbnail-height: ${merged.thumbnails.height};`)\n vars.push(`--${prefix}-thumbnail-radius: ${merged.thumbnails.borderRadius};`)\n vars.push(`--${prefix}-thumbnail-border: ${merged.thumbnails.border};`)\n vars.push(`--${prefix}-thumbnail-shadow: ${merged.thumbnails.shadow};`)\n }\n\n // Live\n if (merged.live) {\n vars.push(`--${prefix}-live-indicator-color: ${merged.live.indicatorColor};`)\n vars.push(`--${prefix}-live-indicator-size: ${merged.live.indicatorSize};`)\n vars.push(`--${prefix}-live-badge-bg: ${merged.live.badgeBg};`)\n vars.push(`--${prefix}-live-badge-text: ${merged.live.badgeText};`)\n }\n\n return vars.join('\\n ')\n}\n\n/**\n * Create a style element with theme CSS\n */\nexport function createThemeStyleElement(theme: Theme, selector = '.tsvp-player'): HTMLStyleElement {\n const style = document.createElement('style')\n style.setAttribute('data-tsvp-theme', 'true')\n style.textContent = `\n${selector} {\n ${generateThemeCSS(theme)}\n}\n`\n return style\n}\n\n/**\n * Apply theme to a player element\n */\nexport function applyTheme(element: HTMLElement, theme: Theme | PresetThemeName): void {\n const themeObj = typeof theme === 'string' ? presetThemes[theme] : theme\n\n // Remove existing inline theme\n element.querySelectorAll('style[data-tsvp-theme]').forEach(el => el.remove())\n\n // Apply new theme\n const css = generateThemeCSS(themeObj)\n element.style.cssText += css.split('\\n').map(line => line.trim().replace(';', '')).join('; ')\n}\n\n/**\n * Inject global theme styles\n */\nexport function injectThemeStyles(theme: Theme | PresetThemeName = {}, selector = '.tsvp-player'): void {\n const themeObj = typeof theme === 'string' ? presetThemes[theme] : theme\n\n // Remove existing global theme\n document.querySelectorAll('style[data-tsvp-theme-global]').forEach(el => el.remove())\n\n const style = document.createElement('style')\n style.setAttribute('data-tsvp-theme-global', 'true')\n style.textContent = `\n${selector} {\n ${generateThemeCSS(themeObj)}\n}\n`\n document.head.appendChild(style)\n}\n",
|
|
8
|
+
"/**\n * Thumbnail Sprites / Seek Preview\n *\n * Provides thumbnail preview functionality during seeking.\n *\n * @module core/thumbnails\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ThumbnailSprite {\n /** URL to the sprite image */\n url: string\n /** Width of each thumbnail in pixels */\n width: number\n /** Height of each thumbnail in pixels */\n height: number\n /** Number of columns in the sprite */\n cols: number\n /** Number of rows in the sprite */\n rows: number\n /** Duration each thumbnail represents in seconds */\n interval: number\n /** Starting time offset in seconds */\n startTime?: number\n}\n\nexport interface ThumbnailCue {\n /** Start time in seconds */\n startTime: number\n /** End time in seconds */\n endTime: number\n /** URL to the thumbnail image (or sprite) */\n url: string\n /** X position in sprite (if using sprite) */\n x?: number\n /** Y position in sprite (if using sprite) */\n y?: number\n /** Width of thumbnail */\n width: number\n /** Height of thumbnail */\n height: number\n}\n\nexport interface ThumbnailsConfig {\n /** Source for thumbnails - URL to VTT file or sprite config */\n src?: string | ThumbnailSprite | ThumbnailCue[]\n /** Whether to preload thumbnail images */\n preload?: boolean\n /** Custom thumbnail renderer */\n renderer?: (cue: ThumbnailCue, container: HTMLElement) => void\n}\n\n// =============================================================================\n// VTT Thumbnail Parser\n// =============================================================================\n\n/**\n * Parse a WebVTT file containing thumbnail cues\n *\n * VTT format for thumbnails:\n * ```\n * WEBVTT\n *\n * 00:00:00.000 --> 00:00:05.000\n * sprite.jpg#xywh=0,0,160,90\n *\n * 00:00:05.000 --> 00:00:10.000\n * sprite.jpg#xywh=160,0,160,90\n * ```\n */\nexport async function parseThumbnailVTT(url: string): Promise<ThumbnailCue[]> {\n const response = await fetch(url)\n const text = await response.text()\n\n const cues: ThumbnailCue[] = []\n const lines = text.split('\\n')\n\n let i = 0\n\n // Skip WEBVTT header\n while (i < lines.length && !lines[i].includes('-->')) {\n i++\n }\n\n // Parse cues\n while (i < lines.length) {\n const line = lines[i].trim()\n\n // Find timestamp line\n if (line.includes('-->')) {\n const [startStr, endStr] = line.split('-->')\n const startTime = parseVTTTime(startStr.trim())\n const endTime = parseVTTTime(endStr.trim())\n\n // Next line should be the thumbnail URL\n i++\n if (i < lines.length) {\n const urlLine = lines[i].trim()\n if (urlLine) {\n const cue = parseThumbnailUrl(urlLine, startTime, endTime)\n if (cue) {\n cues.push(cue)\n }\n }\n }\n }\n\n i++\n }\n\n return cues\n}\n\n/**\n * Parse VTT timestamp to seconds\n */\nfunction parseVTTTime(timeStr: string): number {\n const parts = timeStr.split(':')\n let seconds = 0\n\n if (parts.length === 3) {\n // HH:MM:SS.mmm\n seconds += Number.parseFloat(parts[0]) * 3600\n seconds += Number.parseFloat(parts[1]) * 60\n seconds += Number.parseFloat(parts[2])\n }\n else if (parts.length === 2) {\n // MM:SS.mmm\n seconds += Number.parseFloat(parts[0]) * 60\n seconds += Number.parseFloat(parts[1])\n }\n\n return seconds\n}\n\n/**\n * Parse thumbnail URL with optional sprite coordinates\n */\nfunction parseThumbnailUrl(urlLine: string, startTime: number, endTime: number): ThumbnailCue | null {\n // Check for xywh fragment\n const xywhMatch = urlLine.match(/(.+)#xywh=(\\d+),(\\d+),(\\d+),(\\d+)/)\n\n if (xywhMatch) {\n return {\n startTime,\n endTime,\n url: xywhMatch[1],\n x: Number.parseInt(xywhMatch[2], 10),\n y: Number.parseInt(xywhMatch[3], 10),\n width: Number.parseInt(xywhMatch[4], 10),\n height: Number.parseInt(xywhMatch[5], 10),\n }\n }\n\n // Plain URL - try to extract dimensions from filename or use defaults\n return {\n startTime,\n endTime,\n url: urlLine,\n width: 160,\n height: 90,\n }\n}\n\n// =============================================================================\n// Sprite Thumbnail Generator\n// =============================================================================\n\n/**\n * Generate thumbnail cues from a sprite configuration\n */\nexport function generateSpriteCues(sprite: ThumbnailSprite, duration: number): ThumbnailCue[] {\n const cues: ThumbnailCue[] = []\n const totalThumbnails = sprite.cols * sprite.rows\n const startTime = sprite.startTime || 0\n\n for (let i = 0; i < totalThumbnails; i++) {\n const cueStart = startTime + i * sprite.interval\n const cueEnd = cueStart + sprite.interval\n\n // Don't create cues beyond video duration\n if (cueStart >= duration) break\n\n const col = i % sprite.cols\n const row = Math.floor(i / sprite.cols)\n\n cues.push({\n startTime: cueStart,\n endTime: Math.min(cueEnd, duration),\n url: sprite.url,\n x: col * sprite.width,\n y: row * sprite.height,\n width: sprite.width,\n height: sprite.height,\n })\n }\n\n return cues\n}\n\n// =============================================================================\n// Thumbnails Class\n// =============================================================================\n\nexport class Thumbnails {\n private cues: ThumbnailCue[] = []\n private preloadedImages = new Map<string, HTMLImageElement>()\n private container: HTMLElement | null = null\n private _currentCue: ThumbnailCue | null = null\n private config: ThumbnailsConfig\n\n constructor(config: ThumbnailsConfig = {}) {\n this.config = config\n }\n\n /**\n * Initialize thumbnails from config\n */\n async initialize(duration: number): Promise<void> {\n const { src } = this.config\n\n if (!src) return\n\n if (typeof src === 'string') {\n // VTT URL\n this.cues = await parseThumbnailVTT(src)\n }\n else if (Array.isArray(src)) {\n // Direct cue array\n this.cues = src\n }\n else {\n // Sprite config\n this.cues = generateSpriteCues(src, duration)\n }\n\n // Preload images if enabled\n if (this.config.preload) {\n this.preloadImages()\n }\n }\n\n /**\n * Preload all unique thumbnail images\n */\n private preloadImages(): void {\n const uniqueUrls = new Set(this.cues.map(cue => cue.url))\n\n for (const url of uniqueUrls) {\n if (!this.preloadedImages.has(url)) {\n const img = new Image()\n img.src = url\n this.preloadedImages.set(url, img)\n }\n }\n }\n\n /**\n * Get thumbnail cue for a specific time\n */\n getCueForTime(time: number): ThumbnailCue | null {\n for (const cue of this.cues) {\n if (time >= cue.startTime && time < cue.endTime) {\n return cue\n }\n }\n return null\n }\n\n /**\n * Create thumbnail preview element\n */\n createPreviewElement(): HTMLElement {\n this.container = document.createElement('div')\n this.container.className = 'tsvp-thumbnail-preview'\n this.container.style.cssText = `\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n margin-bottom: 12px;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal, 200ms) var(--tsvp-transition-easing, ease);\n z-index: 10;\n background: var(--tsvp-thumbnail-border, rgba(0, 0, 0, 0.8));\n border-radius: var(--tsvp-thumbnail-radius, 4px);\n padding: 2px;\n box-shadow: var(--tsvp-thumbnail-shadow, 0 4px 12px rgba(0, 0, 0, 0.4));\n overflow: hidden;\n `\n\n const inner = document.createElement('div')\n inner.className = 'tsvp-thumbnail-inner'\n inner.style.cssText = `\n width: var(--tsvp-thumbnail-width, 160px);\n height: var(--tsvp-thumbnail-height, 90px);\n background-size: cover;\n background-position: center;\n border-radius: calc(var(--tsvp-thumbnail-radius, 4px) - 2px);\n overflow: hidden;\n `\n this.container.appendChild(inner)\n\n const time = document.createElement('div')\n time.className = 'tsvp-thumbnail-time'\n time.style.cssText = `\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));\n color: #fff;\n font-size: var(--tsvp-font-size-sm, 12px);\n padding: 4px 6px 2px;\n text-align: center;\n font-family: var(--tsvp-font-family, sans-serif);\n `\n this.container.appendChild(time)\n\n return this.container\n }\n\n /**\n * Update thumbnail preview for a specific time\n */\n updatePreview(time: number, position?: number): void {\n if (!this.container) return\n\n const cue = this.getCueForTime(time)\n const inner = this.container.querySelector('.tsvp-thumbnail-inner') as HTMLElement\n const timeEl = this.container.querySelector('.tsvp-thumbnail-time') as HTMLElement\n\n if (cue && inner) {\n // Custom renderer\n if (this.config.renderer) {\n this.config.renderer(cue, inner)\n }\n else {\n // Default rendering\n if (cue.x !== undefined && cue.y !== undefined) {\n // Sprite thumbnail\n inner.style.backgroundImage = `url(${cue.url})`\n inner.style.backgroundPosition = `-${cue.x}px -${cue.y}px`\n inner.style.backgroundSize = 'auto'\n inner.style.width = `${cue.width}px`\n inner.style.height = `${cue.height}px`\n }\n else {\n // Individual thumbnail\n inner.style.backgroundImage = `url(${cue.url})`\n inner.style.backgroundPosition = 'center'\n inner.style.backgroundSize = 'cover'\n inner.style.width = `${cue.width}px`\n inner.style.height = `${cue.height}px`\n }\n }\n\n // Update time display\n if (timeEl) {\n timeEl.textContent = formatTime(time)\n }\n\n this._currentCue = cue\n }\n\n // Update horizontal position if provided\n if (position !== undefined) {\n this.container.style.left = `${position}px`\n }\n }\n\n /**\n * Show thumbnail preview\n */\n show(): void {\n if (this.container) {\n this.container.style.opacity = '1'\n }\n }\n\n /**\n * Hide thumbnail preview\n */\n hide(): void {\n if (this.container) {\n this.container.style.opacity = '0'\n }\n }\n\n /**\n * Get all cues\n */\n getCues(): ThumbnailCue[] {\n return [...this.cues]\n }\n\n /**\n * Add cues manually\n */\n addCues(cues: ThumbnailCue[]): void {\n this.cues.push(...cues)\n }\n\n /**\n * Get current cue\n */\n getCurrentCue(): ThumbnailCue | null {\n return this._currentCue\n }\n\n /**\n * Clear all cues\n */\n clear(): void {\n this.cues = []\n this.preloadedImages.clear()\n this._currentCue = null\n }\n\n /**\n * Destroy thumbnails instance\n */\n destroy(): void {\n this.clear()\n if (this.container?.parentNode) {\n this.container.parentNode.removeChild(this.container)\n }\n this.container = null\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createThumbnails(config?: ThumbnailsConfig): Thumbnails {\n return new Thumbnails(config)\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction formatTime(seconds: number): string {\n const h = Math.floor(seconds / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = Math.floor(seconds % 60)\n\n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`\n }\n return `${m}:${s.toString().padStart(2, '0')}`\n}\n",
|
|
9
|
+
"/**\n * Internationalization (i18n) System\n *\n * Provides translation support for all player UI strings.\n *\n * @module core/i18n\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface TranslationStrings {\n // Playback\n play: string\n pause: string\n replay: string\n // Volume\n mute: string\n unmute: string\n volume: string\n // Progress\n seekBackward: string\n seekForward: string\n seekTo: string\n duration: string\n currentTime: string\n played: string\n buffered: string\n // Fullscreen\n enterFullscreen: string\n exitFullscreen: string\n // PiP\n enterPiP: string\n exitPiP: string\n // Captions\n enableCaptions: string\n disableCaptions: string\n captions: string\n captionsOff: string\n // Settings\n settings: string\n quality: string\n auto: string\n speed: string\n normal: string\n loop: string\n loopEnabled: string\n loopDisabled: string\n // Audio\n audioBoost: string\n audioTrack: string\n // Live\n live: string\n liveEdge: string\n seekToLive: string\n behindLive: string\n // Errors\n networkError: string\n mediaError: string\n unknownError: string\n // Loading\n loading: string\n buffering: string\n // Chapters\n chapters: string\n // AirPlay / Cast\n airPlay: string\n googleCast: string\n casting: string\n // Misc\n download: string\n share: string\n close: string\n menu: string\n back: string\n}\n\nexport type TranslationKey = keyof TranslationStrings\n\nexport interface I18nConfig {\n /** Current language code */\n language?: string\n /** Fallback language code */\n fallbackLanguage?: string\n /** Custom translations */\n translations?: Partial<Record<string, Partial<TranslationStrings>>>\n /** Interpolation pattern (default: {key}) */\n interpolationPattern?: RegExp\n}\n\n// =============================================================================\n// Default Translations\n// =============================================================================\n\nexport const englishStrings: TranslationStrings = {\n // Playback\n play: 'Play',\n pause: 'Pause',\n replay: 'Replay',\n // Volume\n mute: 'Mute',\n unmute: 'Unmute',\n volume: 'Volume',\n // Progress\n seekBackward: 'Seek backward {seconds} seconds',\n seekForward: 'Seek forward {seconds} seconds',\n seekTo: 'Seek to {time}',\n duration: 'Duration',\n currentTime: 'Current time',\n played: 'Played',\n buffered: 'Buffered',\n // Fullscreen\n enterFullscreen: 'Enter fullscreen',\n exitFullscreen: 'Exit fullscreen',\n // PiP\n enterPiP: 'Enter picture-in-picture',\n exitPiP: 'Exit picture-in-picture',\n // Captions\n enableCaptions: 'Enable captions',\n disableCaptions: 'Disable captions',\n captions: 'Captions',\n captionsOff: 'Off',\n // Settings\n settings: 'Settings',\n quality: 'Quality',\n auto: 'Auto',\n speed: 'Speed',\n normal: 'Normal',\n loop: 'Loop',\n loopEnabled: 'Loop enabled',\n loopDisabled: 'Loop disabled',\n // Audio\n audioBoost: 'Audio boost',\n audioTrack: 'Audio track',\n // Live\n live: 'LIVE',\n liveEdge: 'At live edge',\n seekToLive: 'Seek to live edge',\n behindLive: '{seconds}s behind',\n // Errors\n networkError: 'A network error occurred',\n mediaError: 'The media could not be loaded',\n unknownError: 'An unknown error occurred',\n // Loading\n loading: 'Loading',\n buffering: 'Buffering',\n // Chapters\n chapters: 'Chapters',\n // AirPlay / Cast\n airPlay: 'AirPlay',\n googleCast: 'Cast',\n casting: 'Casting to {device}',\n // Misc\n download: 'Download',\n share: 'Share',\n close: 'Close',\n menu: 'Menu',\n back: 'Back',\n}\n\nexport const spanishStrings: TranslationStrings = {\n play: 'Reproducir',\n pause: 'Pausar',\n replay: 'Repetir',\n mute: 'Silenciar',\n unmute: 'Activar sonido',\n volume: 'Volumen',\n seekBackward: 'Retroceder {seconds} segundos',\n seekForward: 'Avanzar {seconds} segundos',\n seekTo: 'Ir a {time}',\n duration: 'Duración',\n currentTime: 'Tiempo actual',\n played: 'Reproducido',\n buffered: 'Almacenado',\n enterFullscreen: 'Pantalla completa',\n exitFullscreen: 'Salir de pantalla completa',\n enterPiP: 'Imagen en imagen',\n exitPiP: 'Salir de imagen en imagen',\n enableCaptions: 'Activar subtítulos',\n disableCaptions: 'Desactivar subtítulos',\n captions: 'Subtítulos',\n captionsOff: 'Desactivados',\n settings: 'Ajustes',\n quality: 'Calidad',\n auto: 'Automático',\n speed: 'Velocidad',\n normal: 'Normal',\n loop: 'Bucle',\n loopEnabled: 'Bucle activado',\n loopDisabled: 'Bucle desactivado',\n audioBoost: 'Aumento de audio',\n audioTrack: 'Pista de audio',\n live: 'EN VIVO',\n liveEdge: 'En vivo',\n seekToLive: 'Ir al directo',\n behindLive: '{seconds}s de retraso',\n networkError: 'Error de red',\n mediaError: 'No se pudo cargar el medio',\n unknownError: 'Error desconocido',\n loading: 'Cargando',\n buffering: 'Almacenando',\n chapters: 'Capítulos',\n airPlay: 'AirPlay',\n googleCast: 'Enviar',\n casting: 'Enviando a {device}',\n download: 'Descargar',\n share: 'Compartir',\n close: 'Cerrar',\n menu: 'Menú',\n back: 'Volver',\n}\n\nexport const frenchStrings: TranslationStrings = {\n play: 'Lecture',\n pause: 'Pause',\n replay: 'Relire',\n mute: 'Muet',\n unmute: 'Son activé',\n volume: 'Volume',\n seekBackward: 'Reculer de {seconds} secondes',\n seekForward: 'Avancer de {seconds} secondes',\n seekTo: 'Aller à {time}',\n duration: 'Durée',\n currentTime: 'Temps actuel',\n played: 'Joué',\n buffered: 'Mis en mémoire tampon',\n enterFullscreen: 'Plein écran',\n exitFullscreen: 'Quitter le plein écran',\n enterPiP: 'Image dans l\\'image',\n exitPiP: 'Quitter l\\'image dans l\\'image',\n enableCaptions: 'Activer les sous-titres',\n disableCaptions: 'Désactiver les sous-titres',\n captions: 'Sous-titres',\n captionsOff: 'Désactivés',\n settings: 'Paramètres',\n quality: 'Qualité',\n auto: 'Auto',\n speed: 'Vitesse',\n normal: 'Normal',\n loop: 'Boucle',\n loopEnabled: 'Boucle activée',\n loopDisabled: 'Boucle désactivée',\n audioBoost: 'Amplification audio',\n audioTrack: 'Piste audio',\n live: 'EN DIRECT',\n liveEdge: 'En direct',\n seekToLive: 'Aller au direct',\n behindLive: '{seconds}s de retard',\n networkError: 'Erreur réseau',\n mediaError: 'Le média n\\'a pas pu être chargé',\n unknownError: 'Erreur inconnue',\n loading: 'Chargement',\n buffering: 'Mise en mémoire tampon',\n chapters: 'Chapitres',\n airPlay: 'AirPlay',\n googleCast: 'Caster',\n casting: 'Diffusion sur {device}',\n download: 'Télécharger',\n share: 'Partager',\n close: 'Fermer',\n menu: 'Menu',\n back: 'Retour',\n}\n\nexport const germanStrings: TranslationStrings = {\n play: 'Abspielen',\n pause: 'Pause',\n replay: 'Wiederholen',\n mute: 'Stumm',\n unmute: 'Ton an',\n volume: 'Lautstärke',\n seekBackward: '{seconds} Sekunden zurück',\n seekForward: '{seconds} Sekunden vor',\n seekTo: 'Zu {time} springen',\n duration: 'Dauer',\n currentTime: 'Aktuelle Zeit',\n played: 'Abgespielt',\n buffered: 'Gepuffert',\n enterFullscreen: 'Vollbild',\n exitFullscreen: 'Vollbild beenden',\n enterPiP: 'Bild-in-Bild',\n exitPiP: 'Bild-in-Bild beenden',\n enableCaptions: 'Untertitel aktivieren',\n disableCaptions: 'Untertitel deaktivieren',\n captions: 'Untertitel',\n captionsOff: 'Aus',\n settings: 'Einstellungen',\n quality: 'Qualität',\n auto: 'Auto',\n speed: 'Geschwindigkeit',\n normal: 'Normal',\n loop: 'Schleife',\n loopEnabled: 'Schleife aktiviert',\n loopDisabled: 'Schleife deaktiviert',\n audioBoost: 'Audio-Verstärkung',\n audioTrack: 'Audiospur',\n live: 'LIVE',\n liveEdge: 'Am Live-Punkt',\n seekToLive: 'Zum Live-Punkt',\n behindLive: '{seconds}s hinter Live',\n networkError: 'Netzwerkfehler',\n mediaError: 'Medium konnte nicht geladen werden',\n unknownError: 'Unbekannter Fehler',\n loading: 'Wird geladen',\n buffering: 'Puffern',\n chapters: 'Kapitel',\n airPlay: 'AirPlay',\n googleCast: 'Streamen',\n casting: 'Streame zu {device}',\n download: 'Herunterladen',\n share: 'Teilen',\n close: 'Schließen',\n menu: 'Menü',\n back: 'Zurück',\n}\n\nexport const japaneseStrings: TranslationStrings = {\n play: '再生',\n pause: '一時停止',\n replay: 'リプレイ',\n mute: 'ミュート',\n unmute: 'ミュート解除',\n volume: '音量',\n seekBackward: '{seconds}秒戻る',\n seekForward: '{seconds}秒進む',\n seekTo: '{time}にシーク',\n duration: '長さ',\n currentTime: '現在の時間',\n played: '再生済み',\n buffered: 'バッファ済み',\n enterFullscreen: 'フルスクリーン',\n exitFullscreen: 'フルスクリーン終了',\n enterPiP: 'ピクチャーインピクチャー',\n exitPiP: 'ピクチャーインピクチャー終了',\n enableCaptions: '字幕を有効化',\n disableCaptions: '字幕を無効化',\n captions: '字幕',\n captionsOff: 'オフ',\n settings: '設定',\n quality: '画質',\n auto: '自動',\n speed: '再生速度',\n normal: '通常',\n loop: 'ループ',\n loopEnabled: 'ループ有効',\n loopDisabled: 'ループ無効',\n audioBoost: 'オーディオブースト',\n audioTrack: 'オーディオトラック',\n live: 'ライブ',\n liveEdge: 'ライブ中',\n seekToLive: 'ライブに移動',\n behindLive: '{seconds}秒遅れ',\n networkError: 'ネットワークエラー',\n mediaError: 'メディアを読み込めません',\n unknownError: '不明なエラー',\n loading: '読み込み中',\n buffering: 'バッファリング',\n chapters: 'チャプター',\n airPlay: 'AirPlay',\n googleCast: 'キャスト',\n casting: '{device}にキャスト中',\n download: 'ダウンロード',\n share: '共有',\n close: '閉じる',\n menu: 'メニュー',\n back: '戻る',\n}\n\nexport const chineseStrings: TranslationStrings = {\n play: '播放',\n pause: '暂停',\n replay: '重播',\n mute: '静音',\n unmute: '取消静音',\n volume: '音量',\n seekBackward: '后退{seconds}秒',\n seekForward: '前进{seconds}秒',\n seekTo: '跳转到{time}',\n duration: '时长',\n currentTime: '当前时间',\n played: '已播放',\n buffered: '已缓冲',\n enterFullscreen: '全屏',\n exitFullscreen: '退出全屏',\n enterPiP: '画中画',\n exitPiP: '退出画中画',\n enableCaptions: '开启字幕',\n disableCaptions: '关闭字幕',\n captions: '字幕',\n captionsOff: '关闭',\n settings: '设置',\n quality: '画质',\n auto: '自动',\n speed: '播放速度',\n normal: '正常',\n loop: '循环',\n loopEnabled: '循环已开启',\n loopDisabled: '循环已关闭',\n audioBoost: '音频增强',\n audioTrack: '音轨',\n live: '直播',\n liveEdge: '直播中',\n seekToLive: '跳转到直播',\n behindLive: '落后{seconds}秒',\n networkError: '网络错误',\n mediaError: '无法加载媒体',\n unknownError: '未知错误',\n loading: '加载中',\n buffering: '缓冲中',\n chapters: '章节',\n airPlay: 'AirPlay',\n googleCast: '投射',\n casting: '正在投射到{device}',\n download: '下载',\n share: '分享',\n close: '关闭',\n menu: '菜单',\n back: '返回',\n}\n\nexport const defaultTranslations: Record<string, TranslationStrings> = {\n en: englishStrings,\n es: spanishStrings,\n fr: frenchStrings,\n de: germanStrings,\n ja: japaneseStrings,\n zh: chineseStrings,\n}\n\n// =============================================================================\n// I18n Class\n// =============================================================================\n\nexport class I18n {\n private language: string\n private fallbackLanguage: string\n private translations: Record<string, Partial<TranslationStrings>>\n private interpolationPattern: RegExp\n\n constructor(config: I18nConfig = {}) {\n this.language = config.language || this.detectLanguage() || 'en'\n this.fallbackLanguage = config.fallbackLanguage || 'en'\n this.translations = { ...defaultTranslations }\n\n // Merge custom translations\n if (config.translations) {\n for (const [lang, strings] of Object.entries(config.translations)) {\n if (strings) {\n this.translations[lang] = { ...this.translations[lang], ...strings }\n }\n }\n }\n\n this.interpolationPattern = config.interpolationPattern || /\\{(\\w+)\\}/g\n }\n\n /**\n * Detect browser language\n */\n private detectLanguage(): string | null {\n if (typeof navigator === 'undefined') return null\n\n const lang = navigator.language || (navigator as any).userLanguage\n if (!lang) return null\n\n // Try exact match first\n if (this.translations[lang]) {\n return lang\n }\n\n // Try base language (e.g., 'en' from 'en-US')\n const baseLang = lang.split('-')[0]\n if (this.translations[baseLang]) {\n return baseLang\n }\n\n return null\n }\n\n /**\n * Get current language\n */\n getLanguage(): string {\n return this.language\n }\n\n /**\n * Set current language\n */\n setLanguage(language: string): void {\n this.language = language\n }\n\n /**\n * Get translation for a key\n */\n t(key: TranslationKey, params?: Record<string, string | number>): string {\n // Try current language\n let str = this.translations[this.language]?.[key]\n\n // Fall back to fallback language\n if (!str) {\n str = this.translations[this.fallbackLanguage]?.[key]\n }\n\n // Return key if no translation found\n if (!str) {\n return key\n }\n\n // Interpolate params\n if (params) {\n str = str.replace(this.interpolationPattern, (match, paramKey) => {\n const value = params[paramKey]\n return value !== undefined ? String(value) : match\n })\n }\n\n return str\n }\n\n /**\n * Add or update translations for a language\n */\n addTranslations(language: string, strings: Partial<TranslationStrings>): void {\n this.translations[language] = {\n ...this.translations[language],\n ...strings,\n }\n }\n\n /**\n * Get all translations for a language\n */\n getTranslations(language: string): Partial<TranslationStrings> | undefined {\n return this.translations[language]\n }\n\n /**\n * Get all available languages\n */\n getAvailableLanguages(): string[] {\n return Object.keys(this.translations)\n }\n\n /**\n * Check if a language is available\n */\n hasLanguage(language: string): boolean {\n return language in this.translations\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nlet globalI18n: I18n | null = null\n\n/**\n * Create an I18n instance\n */\nexport function createI18n(config?: I18nConfig): I18n {\n return new I18n(config)\n}\n\n/**\n * Get or create global I18n instance\n */\nexport function getI18n(config?: I18nConfig): I18n {\n if (!globalI18n) {\n globalI18n = createI18n(config)\n }\n return globalI18n\n}\n\n/**\n * Set global I18n instance\n */\nexport function setI18n(i18n: I18n): void {\n globalI18n = i18n\n}\n\n/**\n * Quick translation helper using global instance\n */\nexport function t(key: TranslationKey, params?: Record<string, string | number>): string {\n return getI18n().t(key, params)\n}\n",
|
|
10
|
+
"/**\n * Live Streaming UI\n *\n * Components and utilities for live streaming playback.\n *\n * @module core/live\n */\n\nimport type { StateStore } from './state'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface LiveConfig {\n /** Threshold in seconds to consider \"at live edge\" (default: 10) */\n liveEdgeThreshold?: number\n /** Auto-sync to live when seeking within threshold (default: true) */\n autoSyncToLive?: boolean\n /** Show live indicator badge (default: true) */\n showLiveIndicator?: boolean\n /** Show time behind live (default: true) */\n showTimeBehind?: boolean\n}\n\nexport interface LiveState {\n /** Whether the stream is live */\n isLive: boolean\n /** Whether currently at the live edge */\n isAtLiveEdge: boolean\n /** Seconds behind the live edge */\n secondsBehindLive: number\n /** Whether DVR (seeking) is available */\n isDVR: boolean\n /** The live edge time (seekable end) */\n liveEdgeTime: number\n}\n\n// =============================================================================\n// Live Indicator Component\n// =============================================================================\n\nexport class LiveIndicator {\n private container: HTMLElement\n private dot: HTMLElement\n private label: HTMLElement\n private timeBehind: HTMLElement\n private state: LiveState = {\n isLive: false,\n isAtLiveEdge: false,\n secondsBehindLive: 0,\n isDVR: false,\n liveEdgeTime: 0,\n }\n\n private config: Required<LiveConfig>\n\n constructor(config: LiveConfig = {}) {\n this.config = {\n liveEdgeThreshold: config.liveEdgeThreshold ?? 10,\n autoSyncToLive: config.autoSyncToLive ?? true,\n showLiveIndicator: config.showLiveIndicator ?? true,\n showTimeBehind: config.showTimeBehind ?? true,\n }\n\n // Create container\n this.container = document.createElement('div')\n this.container.className = 'tsvp-live-indicator'\n this.container.setAttribute('role', 'status')\n this.container.setAttribute('aria-live', 'polite')\n\n // Create dot\n this.dot = document.createElement('span')\n this.dot.className = 'tsvp-live-dot'\n\n // Create label\n this.label = document.createElement('span')\n this.label.className = 'tsvp-live-label'\n this.label.textContent = 'LIVE'\n\n // Create time behind indicator\n this.timeBehind = document.createElement('span')\n this.timeBehind.className = 'tsvp-live-time-behind'\n this.timeBehind.style.display = 'none'\n\n this.container.appendChild(this.dot)\n this.container.appendChild(this.label)\n this.container.appendChild(this.timeBehind)\n\n this.applyStyles()\n this.hide()\n }\n\n /**\n * Apply default styles\n */\n private applyStyles(): void {\n this.container.style.cssText = `\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--tsvp-radius-full, 9999px);\n background: var(--tsvp-live-badge-bg, #ff0000);\n color: var(--tsvp-live-badge-text, #ffffff);\n font-size: var(--tsvp-font-size-sm, 12px);\n font-weight: var(--tsvp-font-weight-bold, 600);\n font-family: var(--tsvp-font-family, sans-serif);\n cursor: pointer;\n transition: all var(--tsvp-transition-normal, 200ms) var(--tsvp-transition-easing, ease);\n user-select: none;\n `\n\n this.dot.style.cssText = `\n width: var(--tsvp-live-indicator-size, 8px);\n height: var(--tsvp-live-indicator-size, 8px);\n border-radius: 50%;\n background: currentColor;\n animation: tsvp-live-pulse 1.5s ease-in-out infinite;\n `\n\n this.label.style.cssText = `\n text-transform: uppercase;\n letter-spacing: 0.5px;\n `\n\n this.timeBehind.style.cssText = `\n opacity: 0.8;\n font-weight: normal;\n `\n\n // Add pulse animation\n if (!document.querySelector('#tsvp-live-styles')) {\n const style = document.createElement('style')\n style.id = 'tsvp-live-styles'\n style.textContent = `\n @keyframes tsvp-live-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n .tsvp-live-indicator.tsvp-live-behind {\n background: rgba(255, 255, 255, 0.2) !important;\n }\n\n .tsvp-live-indicator.tsvp-live-behind .tsvp-live-dot {\n animation: none;\n opacity: 0.5;\n }\n\n .tsvp-live-indicator:hover {\n transform: scale(1.05);\n }\n\n .tsvp-live-indicator:active {\n transform: scale(0.95);\n }\n `\n document.head.appendChild(style)\n }\n }\n\n /**\n * Get the element\n */\n getElement(): HTMLElement {\n return this.container\n }\n\n /**\n * Update live state\n */\n update(state: Partial<LiveState>): void {\n this.state = { ...this.state, ...state }\n\n if (!this.state.isLive) {\n this.hide()\n return\n }\n\n this.show()\n\n // Update live edge status\n const isAtEdge = this.state.secondsBehindLive <= this.config.liveEdgeThreshold\n this.state.isAtLiveEdge = isAtEdge\n\n if (isAtEdge) {\n this.container.classList.remove('tsvp-live-behind')\n this.label.textContent = 'LIVE'\n this.timeBehind.style.display = 'none'\n this.container.setAttribute('aria-label', 'Live - At live edge')\n }\n else {\n this.container.classList.add('tsvp-live-behind')\n\n if (this.config.showTimeBehind && this.state.secondsBehindLive > 0) {\n this.timeBehind.textContent = `-${Math.floor(this.state.secondsBehindLive)}s`\n this.timeBehind.style.display = 'inline'\n }\n\n this.container.setAttribute('aria-label', `Live - ${Math.floor(this.state.secondsBehindLive)} seconds behind`)\n }\n }\n\n /**\n * Show indicator\n */\n show(): void {\n if (this.config.showLiveIndicator) {\n this.container.style.display = 'inline-flex'\n }\n }\n\n /**\n * Hide indicator\n */\n hide(): void {\n this.container.style.display = 'none'\n }\n\n /**\n * Set click handler (for seeking to live)\n */\n onClick(handler: () => void): void {\n this.container.addEventListener('click', handler)\n }\n\n /**\n * Get current state\n */\n getState(): LiveState {\n return { ...this.state }\n }\n\n /**\n * Destroy component\n */\n destroy(): void {\n this.container.remove()\n }\n}\n\n// =============================================================================\n// Seek to Live Button\n// =============================================================================\n\nexport class SeekToLiveButton {\n private button: HTMLButtonElement\n private icon: HTMLSpanElement\n private label: HTMLSpanElement\n private visible = false\n\n constructor() {\n this.button = document.createElement('button')\n this.button.className = 'tsvp-seek-live-button'\n this.button.type = 'button'\n this.button.setAttribute('aria-label', 'Seek to live')\n\n this.icon = document.createElement('span')\n this.icon.className = 'tsvp-seek-live-icon'\n this.icon.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"16\" height=\"16\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n `\n\n this.label = document.createElement('span')\n this.label.className = 'tsvp-seek-live-label'\n this.label.textContent = 'Go Live'\n\n this.button.appendChild(this.icon)\n this.button.appendChild(this.label)\n\n this.applyStyles()\n this.hide()\n }\n\n private applyStyles(): void {\n this.button.style.cssText = `\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 6px 12px;\n border: none;\n border-radius: var(--tsvp-radius-full, 9999px);\n background: var(--tsvp-live-badge-bg, #ff0000);\n color: var(--tsvp-live-badge-text, #ffffff);\n font-size: var(--tsvp-font-size-sm, 12px);\n font-weight: var(--tsvp-font-weight-bold, 600);\n font-family: var(--tsvp-font-family, sans-serif);\n cursor: pointer;\n transition: all var(--tsvp-transition-normal, 200ms) var(--tsvp-transition-easing, ease);\n `\n\n this.icon.style.cssText = `\n display: flex;\n align-items: center;\n `\n }\n\n getElement(): HTMLButtonElement {\n return this.button\n }\n\n show(): void {\n this.visible = true\n this.button.style.display = 'inline-flex'\n }\n\n hide(): void {\n this.visible = false\n this.button.style.display = 'none'\n }\n\n isVisible(): boolean {\n return this.visible\n }\n\n onClick(handler: () => void): void {\n this.button.addEventListener('click', handler)\n }\n\n destroy(): void {\n this.button.remove()\n }\n}\n\n// =============================================================================\n// Live Controller\n// =============================================================================\n\nexport class LiveController {\n private indicator: LiveIndicator\n private seekToLiveButton: SeekToLiveButton\n private store: StateStore\n private config: Required<LiveConfig>\n private unsubscribe: (() => void) | null = null\n private onSeekToLive: (() => void) | null = null\n\n constructor(store: StateStore, config: LiveConfig = {}) {\n this.store = store\n this.config = {\n liveEdgeThreshold: config.liveEdgeThreshold ?? 10,\n autoSyncToLive: config.autoSyncToLive ?? true,\n showLiveIndicator: config.showLiveIndicator ?? true,\n showTimeBehind: config.showTimeBehind ?? true,\n }\n\n this.indicator = new LiveIndicator(this.config)\n this.seekToLiveButton = new SeekToLiveButton()\n\n // Subscribe to state changes\n this.unsubscribe = this.store.subscribe('*', () => {\n this.updateFromState()\n })\n\n // Handle indicator click\n this.indicator.onClick(() => {\n this.seekToLive()\n })\n\n // Handle button click\n this.seekToLiveButton.onClick(() => {\n this.seekToLive()\n })\n }\n\n /**\n * Update from player state\n */\n private updateFromState(): void {\n const state = this.store.getState()\n\n // Calculate live state\n const isLive = state.streamType === 'live' || state.streamType === 'live:dvr'\n const isDVR = state.streamType === 'live:dvr'\n\n // Calculate seconds behind live\n const seekableEnd = state.buffered.length > 0\n ? state.buffered[state.buffered.length - 1].end\n : state.duration\n const secondsBehindLive = Math.max(0, seekableEnd - state.currentTime)\n const isAtLiveEdge = secondsBehindLive <= this.config.liveEdgeThreshold\n\n this.indicator.update({\n isLive,\n isAtLiveEdge,\n secondsBehindLive,\n isDVR,\n liveEdgeTime: seekableEnd,\n })\n\n // Show/hide seek to live button\n if (isLive && isDVR && !isAtLiveEdge) {\n this.seekToLiveButton.show()\n }\n else {\n this.seekToLiveButton.hide()\n }\n }\n\n /**\n * Seek to live edge\n */\n seekToLive(): void {\n if (this.onSeekToLive) {\n this.onSeekToLive()\n }\n }\n\n /**\n * Set seek to live callback\n */\n setSeekToLiveHandler(handler: () => void): void {\n this.onSeekToLive = handler\n }\n\n /**\n * Get live indicator element\n */\n getIndicator(): HTMLElement {\n return this.indicator.getElement()\n }\n\n /**\n * Get seek to live button element\n */\n getSeekToLiveButton(): HTMLElement {\n return this.seekToLiveButton.getElement()\n }\n\n /**\n * Get current live state\n */\n getState(): LiveState {\n return this.indicator.getState()\n }\n\n /**\n * Check if at live edge\n */\n isAtLiveEdge(): boolean {\n return this.indicator.getState().isAtLiveEdge\n }\n\n /**\n * Destroy controller\n */\n destroy(): void {\n if (this.unsubscribe) {\n this.unsubscribe()\n }\n this.indicator.destroy()\n this.seekToLiveButton.destroy()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createLiveIndicator(config?: LiveConfig): LiveIndicator {\n return new LiveIndicator(config)\n}\n\nexport function createSeekToLiveButton(): SeekToLiveButton {\n return new SeekToLiveButton()\n}\n\nexport function createLiveController(store: StateStore, config?: LiveConfig): LiveController {\n return new LiveController(store, config)\n}\n",
|
|
11
|
+
"/**\n * Remote Playback (AirPlay & Google Cast)\n *\n * Provides support for remote playback via AirPlay and Google Cast.\n *\n * @module core/remote-playback\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type RemotePlaybackType = 'airplay' | 'googlecast' | 'none'\n\nexport type RemotePlaybackState = 'disconnected' | 'connecting' | 'connected'\n\nexport interface RemoteDevice {\n /** Device name */\n name: string\n /** Device type */\n type: RemotePlaybackType\n /** Device ID (if available) */\n id?: string\n}\n\nexport interface RemotePlaybackConfig {\n /** Enable AirPlay (default: true on Safari) */\n airPlay?: boolean\n /** Enable Google Cast (default: true) */\n googleCast?: boolean\n /** Google Cast application ID (default: Chrome default receiver) */\n castApplicationId?: string\n /** Callback when device availability changes */\n onAvailabilityChange?: (type: RemotePlaybackType, available: boolean) => void\n /** Callback when connection state changes */\n onStateChange?: (state: RemotePlaybackState, device?: RemoteDevice) => void\n}\n\n// =============================================================================\n// AirPlay Support\n// =============================================================================\n\n/**\n * Check if AirPlay is supported\n */\nexport function isAirPlaySupported(): boolean {\n if (typeof window === 'undefined') return false\n return 'WebKitPlaybackTargetAvailabilityEvent' in window\n}\n\n/**\n * Check if an element has AirPlay available\n */\nexport function isAirPlayAvailable(video: HTMLVideoElement): boolean {\n return (video as any).webkitCurrentPlaybackTargetIsWireless !== undefined\n}\n\n/**\n * Show AirPlay picker\n */\nexport function showAirPlayPicker(video: HTMLVideoElement): void {\n if ((video as any).webkitShowPlaybackTargetPicker) {\n (video as any).webkitShowPlaybackTargetPicker()\n }\n}\n\n/**\n * Check if currently playing via AirPlay\n */\nexport function isAirPlaying(video: HTMLVideoElement): boolean {\n return (video as any).webkitCurrentPlaybackTargetIsWireless === true\n}\n\n// =============================================================================\n// Google Cast Support\n// =============================================================================\n\ndeclare global {\n interface Window {\n chrome?: {\n cast?: any\n }\n __onGCastApiAvailable?: (isAvailable: boolean) => void\n }\n}\n\nlet castInitialized = false\nlet castSession: any = null\n\n/**\n * Check if Google Cast is supported\n */\nexport function isGoogleCastSupported(): boolean {\n if (typeof window === 'undefined') return false\n return !!window.chrome?.cast\n}\n\n/**\n * Load the Google Cast SDK\n */\nexport function loadGoogleCastSDK(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (isGoogleCastSupported()) {\n resolve()\n return\n }\n\n // Check if already loading\n if (document.querySelector('script[src*=\"cast_sender\"]')) {\n // Wait for it to load\n window.__onGCastApiAvailable = (isAvailable) => {\n if (isAvailable) {\n resolve()\n }\n else {\n reject(new Error('Google Cast SDK not available'))\n }\n }\n return\n }\n\n const script = document.createElement('script')\n script.src = 'https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1'\n script.async = true\n\n window.__onGCastApiAvailable = (isAvailable) => {\n if (isAvailable) {\n resolve()\n }\n else {\n reject(new Error('Google Cast SDK not available'))\n }\n }\n\n script.onerror = () => reject(new Error('Failed to load Google Cast SDK'))\n document.head.appendChild(script)\n })\n}\n\n/**\n * Initialize Google Cast\n */\nexport function initializeGoogleCast(applicationId?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!isGoogleCastSupported()) {\n reject(new Error('Google Cast not supported'))\n return\n }\n\n if (castInitialized) {\n resolve()\n return\n }\n\n const cast = window.chrome!.cast!\n const castContext = cast.framework.CastContext.getInstance()\n\n castContext.setOptions({\n receiverApplicationId: applicationId || cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,\n autoJoinPolicy: cast.AutoJoinPolicy.ORIGIN_SCOPED,\n })\n\n castInitialized = true\n resolve()\n })\n}\n\n/**\n * Request a Google Cast session\n */\nexport async function requestCastSession(): Promise<any> {\n if (!isGoogleCastSupported()) {\n throw new Error('Google Cast not supported')\n }\n\n const cast = window.chrome!.cast!\n const castContext = cast.framework.CastContext.getInstance()\n\n try {\n await castContext.requestSession()\n castSession = castContext.getCurrentSession()\n return castSession\n }\n catch (error) {\n throw new Error('Failed to start Cast session')\n }\n}\n\n/**\n * End Google Cast session\n */\nexport function endCastSession(): void {\n if (castSession) {\n castSession.endSession(true)\n castSession = null\n }\n}\n\n/**\n * Check if currently casting\n */\nexport function isCasting(): boolean {\n return castSession !== null\n}\n\n/**\n * Get current cast session\n */\nexport function getCastSession(): any {\n return castSession\n}\n\n/**\n * Cast media to connected device\n */\nexport function castMedia(\n url: string,\n contentType: string,\n metadata?: {\n title?: string\n subtitle?: string\n images?: string[]\n }\n): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!castSession) {\n reject(new Error('No active Cast session'))\n return\n }\n\n const cast = window.chrome!.cast!\n const mediaInfo = new cast.media.MediaInfo(url, contentType)\n\n if (metadata) {\n const meta = new cast.media.GenericMediaMetadata()\n if (metadata.title) meta.title = metadata.title\n if (metadata.subtitle) meta.subtitle = metadata.subtitle\n if (metadata.images) {\n meta.images = metadata.images.map((img: string) => new cast.Image(img))\n }\n mediaInfo.metadata = meta\n }\n\n const request = new cast.media.LoadRequest(mediaInfo)\n\n castSession.loadMedia(request).then(\n () => resolve(),\n (error: any) => reject(new Error(`Cast failed: ${error.description}`))\n )\n })\n}\n\n// =============================================================================\n// Remote Playback Controller\n// =============================================================================\n\nexport class RemotePlaybackController {\n private video: HTMLVideoElement | null = null\n private config: Required<RemotePlaybackConfig>\n private state: RemotePlaybackState = 'disconnected'\n private connectedDevice: RemoteDevice | null = null\n private airPlayAvailable = false\n private googleCastAvailable = false\n private listeners: Map<string, Set<(...args: any[]) => void>> = new Map()\n\n constructor(config: RemotePlaybackConfig = {}) {\n this.config = {\n airPlay: config.airPlay ?? true,\n googleCast: config.googleCast ?? true,\n castApplicationId: config.castApplicationId || '',\n onAvailabilityChange: config.onAvailabilityChange || (() => {}),\n onStateChange: config.onStateChange || (() => {}),\n }\n }\n\n /**\n * Attach to a video element\n */\n async attach(video: HTMLVideoElement): Promise<void> {\n this.video = video\n\n // Setup AirPlay\n if (this.config.airPlay && isAirPlaySupported()) {\n this.setupAirPlay()\n }\n\n // Setup Google Cast\n if (this.config.googleCast) {\n try {\n await loadGoogleCastSDK()\n await initializeGoogleCast(this.config.castApplicationId)\n this.setupGoogleCast()\n }\n catch (error) {\n console.warn('Google Cast initialization failed:', error)\n }\n }\n }\n\n /**\n * Setup AirPlay event listeners\n */\n private setupAirPlay(): void {\n if (!this.video) return\n\n // Listen for availability\n this.video.addEventListener('webkitplaybacktargetavailabilitychanged', ((event: any) => {\n this.airPlayAvailable = event.availability === 'available'\n this.config.onAvailabilityChange('airplay', this.airPlayAvailable)\n this.emit('airplayavailabilitychange', this.airPlayAvailable)\n }) as EventListener)\n\n // Listen for connection state\n this.video.addEventListener('webkitcurrentplaybacktargetiswirelesschanged', (() => {\n const isWireless = isAirPlaying(this.video!)\n\n if (isWireless) {\n this.state = 'connected'\n this.connectedDevice = {\n name: 'AirPlay Device',\n type: 'airplay',\n }\n }\n else {\n this.state = 'disconnected'\n this.connectedDevice = null\n }\n\n this.config.onStateChange(this.state, this.connectedDevice || undefined)\n this.emit('statechange', this.state, this.connectedDevice)\n }) as EventListener)\n }\n\n /**\n * Setup Google Cast event listeners\n */\n private setupGoogleCast(): void {\n if (!isGoogleCastSupported()) return\n\n const cast = window.chrome!.cast!\n const castContext = cast.framework.CastContext.getInstance()\n\n // Check initial availability\n this.googleCastAvailable = castContext.getCastState() !== cast.framework.CastState.NO_DEVICES_AVAILABLE\n this.config.onAvailabilityChange('googlecast', this.googleCastAvailable)\n\n // Listen for cast state changes\n castContext.addEventListener(\n cast.framework.CastContextEventType.CAST_STATE_CHANGED,\n (event: any) => {\n this.googleCastAvailable = event.castState !== cast.framework.CastState.NO_DEVICES_AVAILABLE\n this.config.onAvailabilityChange('googlecast', this.googleCastAvailable)\n this.emit('googlecastavailabilitychange', this.googleCastAvailable)\n\n // Update connection state\n switch (event.castState) {\n case cast.framework.CastState.CONNECTED:\n this.state = 'connected'\n const session = castContext.getCurrentSession()\n this.connectedDevice = {\n name: session?.getCastDevice()?.friendlyName || 'Cast Device',\n type: 'googlecast',\n id: session?.getCastDevice()?.id,\n }\n break\n case cast.framework.CastState.CONNECTING:\n this.state = 'connecting'\n break\n default:\n this.state = 'disconnected'\n this.connectedDevice = null\n }\n\n this.config.onStateChange(this.state, this.connectedDevice || undefined)\n this.emit('statechange', this.state, this.connectedDevice)\n }\n )\n }\n\n /**\n * Show remote playback picker\n */\n async showPicker(type?: RemotePlaybackType): Promise<void> {\n if (type === 'airplay' || (!type && this.airPlayAvailable)) {\n if (this.video) {\n showAirPlayPicker(this.video)\n }\n }\n else if (type === 'googlecast' || (!type && this.googleCastAvailable)) {\n await requestCastSession()\n }\n }\n\n /**\n * Disconnect from remote device\n */\n disconnect(): void {\n if (this.connectedDevice?.type === 'googlecast') {\n endCastSession()\n }\n // AirPlay disconnect is handled by the system\n\n this.state = 'disconnected'\n this.connectedDevice = null\n this.emit('statechange', this.state, null)\n }\n\n /**\n * Check if AirPlay is available\n */\n isAirPlayAvailable(): boolean {\n return this.airPlayAvailable\n }\n\n /**\n * Check if Google Cast is available\n */\n isGoogleCastAvailable(): boolean {\n return this.googleCastAvailable\n }\n\n /**\n * Check if any remote playback is available\n */\n isRemotePlaybackAvailable(): boolean {\n return this.airPlayAvailable || this.googleCastAvailable\n }\n\n /**\n * Get current state\n */\n getState(): RemotePlaybackState {\n return this.state\n }\n\n /**\n * Get connected device\n */\n getConnectedDevice(): RemoteDevice | null {\n return this.connectedDevice\n }\n\n /**\n * Check if currently connected to a remote device\n */\n isConnected(): boolean {\n return this.state === 'connected'\n }\n\n /**\n * Add event listener\n */\n on(event: string, handler: (...args: any[]) => void): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set())\n }\n this.listeners.get(event)!.add(handler)\n }\n\n /**\n * Remove event listener\n */\n off(event: string, handler: (...args: any[]) => void): void {\n this.listeners.get(event)?.delete(handler)\n }\n\n /**\n * Emit event\n */\n private emit(event: string, ...args: any[]): void {\n this.listeners.get(event)?.forEach(handler => handler(...args))\n }\n\n /**\n * Detach from video element\n */\n detach(): void {\n this.disconnect()\n this.video = null\n }\n\n /**\n * Destroy controller\n */\n destroy(): void {\n this.detach()\n this.listeners.clear()\n }\n}\n\n// =============================================================================\n// UI Components\n// =============================================================================\n\nexport class AirPlayButton {\n private button: HTMLButtonElement\n private _available = false\n\n constructor() {\n this.button = document.createElement('button')\n this.button.className = 'tsvp-airplay-button'\n this.button.type = 'button'\n this.button.setAttribute('aria-label', 'AirPlay')\n this.button.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"24\" height=\"24\">\n <path d=\"M6 22h12l-6-6-6 6zM21 3H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h4v-2H3V5h18v12h-4v2h4c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z\"/>\n </svg>\n `\n\n this.applyStyles()\n this.hide()\n }\n\n private applyStyles(): void {\n this.button.style.cssText = `\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--tsvp-controls-button-size, 40px);\n height: var(--tsvp-controls-button-size, 40px);\n padding: 0;\n border: none;\n background: transparent;\n color: var(--tsvp-color-text, #ffffff);\n cursor: pointer;\n opacity: 0.8;\n transition: opacity var(--tsvp-transition-normal, 200ms);\n `\n }\n\n getElement(): HTMLButtonElement {\n return this.button\n }\n\n setAvailable(available: boolean): void {\n this._available = available\n if (available) {\n this.show()\n }\n else {\n this.hide()\n }\n }\n\n isAvailable(): boolean {\n return this._available\n }\n\n show(): void {\n this.button.style.display = 'flex'\n }\n\n hide(): void {\n this.button.style.display = 'none'\n }\n\n setActive(active: boolean): void {\n this.button.classList.toggle('tsvp-active', active)\n this.button.style.color = active ? 'var(--tsvp-color-primary, #00a8ff)' : ''\n }\n\n onClick(handler: () => void): void {\n this.button.addEventListener('click', handler)\n }\n\n destroy(): void {\n this.button.remove()\n }\n}\n\nexport class GoogleCastButton {\n private button: HTMLButtonElement\n private _available = false\n\n constructor() {\n this.button = document.createElement('button')\n this.button.className = 'tsvp-googlecast-button'\n this.button.type = 'button'\n this.button.setAttribute('aria-label', 'Cast')\n this.button.innerHTML = `\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"24\" height=\"24\">\n <path d=\"M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM1 18v3h3c0-1.66-1.34-3-3-3zm0-4v2c2.76 0 5 2.24 5 5h2c0-3.87-3.13-7-7-7zm0-4v2c4.97 0 9 4.03 9 9h2c0-6.08-4.93-11-11-11z\"/>\n </svg>\n `\n\n this.applyStyles()\n this.hide()\n }\n\n private applyStyles(): void {\n this.button.style.cssText = `\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--tsvp-controls-button-size, 40px);\n height: var(--tsvp-controls-button-size, 40px);\n padding: 0;\n border: none;\n background: transparent;\n color: var(--tsvp-color-text, #ffffff);\n cursor: pointer;\n opacity: 0.8;\n transition: opacity var(--tsvp-transition-normal, 200ms);\n `\n }\n\n getElement(): HTMLButtonElement {\n return this.button\n }\n\n setAvailable(available: boolean): void {\n this._available = available\n if (available) {\n this.show()\n }\n else {\n this.hide()\n }\n }\n\n isAvailable(): boolean {\n return this._available\n }\n\n show(): void {\n this.button.style.display = 'flex'\n }\n\n hide(): void {\n this.button.style.display = 'none'\n }\n\n setActive(active: boolean): void {\n this.button.classList.toggle('tsvp-active', active)\n this.button.style.color = active ? 'var(--tsvp-color-primary, #00a8ff)' : ''\n }\n\n onClick(handler: () => void): void {\n this.button.addEventListener('click', handler)\n }\n\n destroy(): void {\n this.button.remove()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createRemotePlaybackController(config?: RemotePlaybackConfig): RemotePlaybackController {\n return new RemotePlaybackController(config)\n}\n\nexport function createAirPlayButton(): AirPlayButton {\n return new AirPlayButton()\n}\n\nexport function createGoogleCastButton(): GoogleCastButton {\n return new GoogleCastButton()\n}\n",
|
|
12
|
+
"/**\n * Chapters Support\n *\n * VTT chapters parsing and chapter markers for the progress bar.\n *\n * @module core/chapters\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface Chapter {\n /** Chapter ID */\n id: string\n /** Start time in seconds */\n startTime: number\n /** End time in seconds */\n endTime: number\n /** Chapter title */\n title: string\n /** Optional thumbnail URL */\n thumbnail?: string\n}\n\nexport interface ChaptersConfig {\n /** VTT chapters URL or array of chapters */\n src?: string | Chapter[]\n /** Show chapter markers on progress bar */\n showMarkers?: boolean\n /** Show chapter title on hover */\n showTitles?: boolean\n /** Marker color */\n markerColor?: string\n /** Active marker color */\n activeMarkerColor?: string\n}\n\n// =============================================================================\n// VTT Chapters Parser\n// =============================================================================\n\n/**\n * Parse VTT file for chapters\n */\nexport async function parseChaptersVTT(url: string): Promise<Chapter[]> {\n const response = await fetch(url)\n const text = await response.text()\n\n const chapters: Chapter[] = []\n const lines = text.split('\\n')\n\n let i = 0\n let chapterIndex = 0\n\n // Skip WEBVTT header\n while (i < lines.length && !lines[i].includes('-->')) {\n i++\n }\n\n // Parse cues\n while (i < lines.length) {\n const line = lines[i].trim()\n\n // Find timestamp line\n if (line.includes('-->')) {\n const [startStr, endStr] = line.split('-->')\n const startTime = parseVTTTime(startStr.trim())\n const endTime = parseVTTTime(endStr.trim())\n\n // Next line(s) should be the chapter title\n i++\n let title = ''\n while (i < lines.length && lines[i].trim() !== '') {\n if (title) title += ' '\n title += lines[i].trim()\n i++\n }\n\n if (title) {\n chapters.push({\n id: `chapter-${chapterIndex++}`,\n startTime,\n endTime,\n title,\n })\n }\n }\n\n i++\n }\n\n return chapters\n}\n\n/**\n * Parse VTT timestamp to seconds\n */\nfunction parseVTTTime(timeStr: string): number {\n const parts = timeStr.split(':')\n let seconds = 0\n\n if (parts.length === 3) {\n seconds += Number.parseFloat(parts[0]) * 3600\n seconds += Number.parseFloat(parts[1]) * 60\n seconds += Number.parseFloat(parts[2])\n }\n else if (parts.length === 2) {\n seconds += Number.parseFloat(parts[0]) * 60\n seconds += Number.parseFloat(parts[1])\n }\n\n return seconds\n}\n\n// =============================================================================\n// Chapters Manager\n// =============================================================================\n\nexport class ChaptersManager {\n private chapters: Chapter[] = []\n private config: Required<ChaptersConfig>\n private markersContainer: HTMLElement | null = null\n private titleElement: HTMLElement | null = null\n private currentChapter: Chapter | null = null\n private duration = 0\n\n constructor(config: ChaptersConfig = {}) {\n this.config = {\n src: config.src || [],\n showMarkers: config.showMarkers ?? true,\n showTitles: config.showTitles ?? true,\n markerColor: config.markerColor || 'rgba(255, 255, 255, 0.5)',\n activeMarkerColor: config.activeMarkerColor || 'var(--tsvp-color-primary, #00a8ff)',\n }\n }\n\n /**\n * Initialize chapters\n */\n async initialize(duration: number): Promise<void> {\n this.duration = duration\n const { src } = this.config\n\n if (!src) return\n\n if (typeof src === 'string') {\n this.chapters = await parseChaptersVTT(src)\n }\n else if (Array.isArray(src)) {\n this.chapters = src\n }\n }\n\n /**\n * Get all chapters\n */\n getChapters(): Chapter[] {\n return [...this.chapters]\n }\n\n /**\n * Get chapter at specific time\n */\n getChapterAtTime(time: number): Chapter | null {\n for (const chapter of this.chapters) {\n if (time >= chapter.startTime && time < chapter.endTime) {\n return chapter\n }\n }\n return null\n }\n\n /**\n * Get current chapter\n */\n getCurrentChapter(): Chapter | null {\n return this.currentChapter\n }\n\n /**\n * Update current time and chapter\n */\n updateTime(time: number): Chapter | null {\n const chapter = this.getChapterAtTime(time)\n\n if (chapter !== this.currentChapter) {\n this.currentChapter = chapter\n this.updateActiveMarker()\n }\n\n return chapter\n }\n\n /**\n * Seek to chapter by ID\n */\n seekToChapter(id: string): number | null {\n const chapter = this.chapters.find(c => c.id === id)\n return chapter ? chapter.startTime : null\n }\n\n /**\n * Get next chapter\n */\n getNextChapter(currentTime: number): Chapter | null {\n for (const chapter of this.chapters) {\n if (chapter.startTime > currentTime) {\n return chapter\n }\n }\n return null\n }\n\n /**\n * Get previous chapter\n */\n getPreviousChapter(currentTime: number): Chapter | null {\n let prev: Chapter | null = null\n for (const chapter of this.chapters) {\n if (chapter.startTime >= currentTime) {\n break\n }\n prev = chapter\n }\n return prev\n }\n\n /**\n * Create chapter markers element for progress bar\n */\n createMarkersElement(): HTMLElement {\n this.markersContainer = document.createElement('div')\n this.markersContainer.className = 'tsvp-chapter-markers'\n this.markersContainer.style.cssText = `\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n pointer-events: none;\n `\n\n this.renderMarkers()\n return this.markersContainer\n }\n\n /**\n * Render chapter markers\n */\n private renderMarkers(): void {\n if (!this.markersContainer || !this.duration) return\n\n this.markersContainer.innerHTML = ''\n\n for (const chapter of this.chapters) {\n const marker = document.createElement('div')\n marker.className = 'tsvp-chapter-marker'\n marker.dataset.chapterId = chapter.id\n\n const position = (chapter.startTime / this.duration) * 100\n\n marker.style.cssText = `\n position: absolute;\n left: ${position}%;\n top: 0;\n bottom: 0;\n width: 2px;\n background: ${this.config.markerColor};\n transform: translateX(-50%);\n transition: background var(--tsvp-transition-fast, 100ms);\n `\n\n this.markersContainer.appendChild(marker)\n }\n }\n\n /**\n * Update active marker styling\n */\n private updateActiveMarker(): void {\n if (!this.markersContainer) return\n\n const markers = this.markersContainer.querySelectorAll('.tsvp-chapter-marker')\n markers.forEach((marker) => {\n const el = marker as HTMLElement\n const isActive = this.currentChapter && el.dataset.chapterId === this.currentChapter.id\n el.style.background = isActive ? this.config.activeMarkerColor : this.config.markerColor\n })\n }\n\n /**\n * Create chapter title element\n */\n createTitleElement(): HTMLElement {\n this.titleElement = document.createElement('div')\n this.titleElement.className = 'tsvp-chapter-title'\n this.titleElement.style.cssText = `\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n margin-bottom: 8px;\n padding: 6px 12px;\n background: var(--tsvp-controls-tooltip-bg, rgba(0, 0, 0, 0.9));\n color: var(--tsvp-controls-tooltip-text, #ffffff);\n font-size: var(--tsvp-font-size-sm, 12px);\n font-family: var(--tsvp-font-family, sans-serif);\n border-radius: var(--tsvp-radius-sm, 4px);\n white-space: nowrap;\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal, 200ms);\n pointer-events: none;\n z-index: 10;\n `\n\n return this.titleElement\n }\n\n /**\n * Show chapter title\n */\n showTitle(chapter: Chapter): void {\n if (this.titleElement && this.config.showTitles) {\n this.titleElement.textContent = chapter.title\n this.titleElement.style.opacity = '1'\n }\n }\n\n /**\n * Hide chapter title\n */\n hideTitle(): void {\n if (this.titleElement) {\n this.titleElement.style.opacity = '0'\n }\n }\n\n /**\n * Get chapter at position (for hover preview)\n */\n getChapterAtPosition(percent: number): Chapter | null {\n const time = (percent / 100) * this.duration\n return this.getChapterAtTime(time)\n }\n\n /**\n * Add chapters\n */\n addChapters(chapters: Chapter[]): void {\n this.chapters.push(...chapters)\n this.renderMarkers()\n }\n\n /**\n * Clear all chapters\n */\n clear(): void {\n this.chapters = []\n this.currentChapter = null\n if (this.markersContainer) {\n this.markersContainer.innerHTML = ''\n }\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n this.clear()\n this.markersContainer?.remove()\n this.titleElement?.remove()\n this.markersContainer = null\n this.titleElement = null\n }\n}\n\n// =============================================================================\n// Chapter Menu Component\n// =============================================================================\n\nexport class ChapterMenu {\n private container: HTMLElement\n private chapters: Chapter[] = []\n private onSelect: ((_chapter: Chapter) => void) | null = null\n\n constructor() {\n this.container = document.createElement('div')\n this.container.className = 'tsvp-chapter-menu'\n this.applyStyles()\n }\n\n private applyStyles(): void {\n this.container.style.cssText = `\n max-height: 300px;\n overflow-y: auto;\n background: var(--tsvp-menu-bg, rgba(20, 20, 20, 0.95));\n border-radius: var(--tsvp-menu-radius, 8px);\n box-shadow: var(--tsvp-menu-shadow, 0 4px 20px rgba(0, 0, 0, 0.5));\n `\n }\n\n /**\n * Set chapters\n */\n setChapters(chapters: Chapter[]): void {\n this.chapters = chapters\n this.render()\n }\n\n /**\n * Render chapter list\n */\n private render(): void {\n this.container.innerHTML = ''\n\n for (const chapter of this.chapters) {\n const item = document.createElement('button')\n item.className = 'tsvp-chapter-menu-item'\n item.type = 'button'\n\n item.style.cssText = `\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: transparent;\n color: var(--tsvp-color-text, #ffffff);\n font-size: var(--tsvp-font-size, 14px);\n font-family: var(--tsvp-font-family, sans-serif);\n text-align: left;\n cursor: pointer;\n transition: background var(--tsvp-transition-fast, 100ms);\n `\n\n // Thumbnail\n if (chapter.thumbnail) {\n const thumb = document.createElement('img')\n thumb.src = chapter.thumbnail\n thumb.style.cssText = `\n width: 80px;\n height: 45px;\n object-fit: cover;\n border-radius: 4px;\n `\n item.appendChild(thumb)\n }\n\n // Info\n const info = document.createElement('div')\n info.style.cssText = 'flex: 1;'\n\n const title = document.createElement('div')\n title.textContent = chapter.title\n title.style.fontWeight = 'var(--tsvp-font-weight-bold, 600)'\n info.appendChild(title)\n\n const time = document.createElement('div')\n time.textContent = formatTime(chapter.startTime)\n time.style.cssText = `\n font-size: var(--tsvp-font-size-sm, 12px);\n opacity: 0.7;\n margin-top: 2px;\n `\n info.appendChild(time)\n\n item.appendChild(info)\n\n // Hover effect\n item.addEventListener('mouseenter', () => {\n item.style.background = 'var(--tsvp-menu-item-hover-bg, rgba(255, 255, 255, 0.1))'\n })\n item.addEventListener('mouseleave', () => {\n item.style.background = 'transparent'\n })\n\n // Click handler\n item.addEventListener('click', () => {\n if (this.onSelect) {\n this.onSelect(chapter)\n }\n })\n\n this.container.appendChild(item)\n }\n }\n\n /**\n * Set select handler\n */\n onChapterSelect(handler: (chapter: Chapter) => void): void {\n this.onSelect = handler\n }\n\n /**\n * Get element\n */\n getElement(): HTMLElement {\n return this.container\n }\n\n /**\n * Highlight current chapter\n */\n setActiveChapter(id: string): void {\n const items = this.container.querySelectorAll('.tsvp-chapter-menu-item')\n items.forEach((item, index) => {\n const el = item as HTMLElement\n const isActive = this.chapters[index]?.id === id\n el.style.background = isActive\n ? 'var(--tsvp-menu-item-active-bg, rgba(0, 168, 255, 0.2))'\n : 'transparent'\n })\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n this.container.remove()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createChaptersManager(config?: ChaptersConfig): ChaptersManager {\n return new ChaptersManager(config)\n}\n\nexport function createChapterMenu(): ChapterMenu {\n return new ChapterMenu()\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction formatTime(seconds: number): string {\n const h = Math.floor(seconds / 3600)\n const m = Math.floor((seconds % 3600) / 60)\n const s = Math.floor(seconds % 60)\n\n if (h > 0) {\n return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`\n }\n return `${m}:${s.toString().padStart(2, '0')}`\n}\n",
|
|
13
|
+
"/**\n * Buffering Spinner\n *\n * Loading indicator for buffering states.\n *\n * @module core/spinner\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SpinnerConfig {\n /** Spinner size in pixels */\n size?: number\n /** Track width */\n trackWidth?: number\n /** Track color */\n trackColor?: string\n /** Fill color */\n fillColor?: string\n /** Animation duration in ms */\n duration?: number\n /** Show/hide delay in ms */\n delay?: number\n}\n\n// =============================================================================\n// Spinner Component\n// =============================================================================\n\nexport class Spinner {\n private container: HTMLElement\n private svg: SVGSVGElement\n private track: SVGCircleElement\n private fill: SVGCircleElement\n private config: Required<SpinnerConfig>\n private visible = false\n private showTimeout: ReturnType<typeof setTimeout> | null = null\n\n constructor(config: SpinnerConfig = {}) {\n this.config = {\n size: config.size ?? 48,\n trackWidth: config.trackWidth ?? 4,\n trackColor: config.trackColor || 'rgba(255, 255, 255, 0.2)',\n fillColor: config.fillColor || 'var(--tsvp-color-primary, #00a8ff)',\n duration: config.duration ?? 1000,\n delay: config.delay ?? 200,\n }\n\n this.container = document.createElement('div')\n this.container.className = 'tsvp-spinner'\n\n // Create SVG\n const size = this.config.size\n const center = size / 2\n const radius = (size - this.config.trackWidth) / 2\n\n this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n this.svg.setAttribute('width', String(size))\n this.svg.setAttribute('height', String(size))\n this.svg.setAttribute('viewBox', `0 0 ${size} ${size}`)\n\n // Track circle\n this.track = document.createElementNS('http://www.w3.org/2000/svg', 'circle')\n this.track.setAttribute('cx', String(center))\n this.track.setAttribute('cy', String(center))\n this.track.setAttribute('r', String(radius))\n this.track.setAttribute('fill', 'none')\n this.track.setAttribute('stroke', this.config.trackColor)\n this.track.setAttribute('stroke-width', String(this.config.trackWidth))\n\n // Fill circle (animated)\n this.fill = document.createElementNS('http://www.w3.org/2000/svg', 'circle')\n this.fill.setAttribute('cx', String(center))\n this.fill.setAttribute('cy', String(center))\n this.fill.setAttribute('r', String(radius))\n this.fill.setAttribute('fill', 'none')\n this.fill.setAttribute('stroke', this.config.fillColor)\n this.fill.setAttribute('stroke-width', String(this.config.trackWidth))\n this.fill.setAttribute('stroke-linecap', 'round')\n\n // Calculate stroke dash for partial circle\n const circumference = 2 * Math.PI * radius\n this.fill.setAttribute('stroke-dasharray', `${circumference * 0.25} ${circumference * 0.75}`)\n this.fill.setAttribute('transform', `rotate(-90 ${center} ${center})`)\n\n this.svg.appendChild(this.track)\n this.svg.appendChild(this.fill)\n this.container.appendChild(this.svg)\n\n this.applyStyles()\n this.hide()\n }\n\n private applyStyles(): void {\n this.container.style.cssText = `\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 5;\n pointer-events: none;\n `\n\n // Add keyframe animation\n if (!document.querySelector('#tsvp-spinner-styles')) {\n const style = document.createElement('style')\n style.id = 'tsvp-spinner-styles'\n style.textContent = `\n @keyframes tsvp-spinner-rotate {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n .tsvp-spinner svg {\n animation: tsvp-spinner-rotate ${this.config.duration}ms linear infinite;\n }\n\n .tsvp-spinner-fade-in {\n animation: tsvp-spinner-fade-in 200ms ease forwards;\n }\n\n .tsvp-spinner-fade-out {\n animation: tsvp-spinner-fade-out 200ms ease forwards;\n }\n\n @keyframes tsvp-spinner-fade-in {\n from { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }\n to { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n }\n\n @keyframes tsvp-spinner-fade-out {\n from { opacity: 1; transform: translate(-50%, -50%) scale(1); }\n to { opacity: 0; transform: translate(-50%, -50%) scale(0.8); }\n }\n `\n document.head.appendChild(style)\n }\n }\n\n /**\n * Get the element\n */\n getElement(): HTMLElement {\n return this.container\n }\n\n /**\n * Show spinner (with optional delay)\n */\n show(immediate = false): void {\n if (this.visible) return\n\n if (this.showTimeout) {\n clearTimeout(this.showTimeout)\n }\n\n const doShow = () => {\n this.visible = true\n this.container.style.display = 'block'\n this.container.classList.remove('tsvp-spinner-fade-out')\n this.container.classList.add('tsvp-spinner-fade-in')\n }\n\n if (immediate || this.config.delay === 0) {\n doShow()\n }\n else {\n this.showTimeout = setTimeout(doShow, this.config.delay)\n }\n }\n\n /**\n * Hide spinner\n */\n hide(): void {\n if (this.showTimeout) {\n clearTimeout(this.showTimeout)\n this.showTimeout = null\n }\n\n if (!this.visible) {\n this.container.style.display = 'none'\n return\n }\n\n this.visible = false\n this.container.classList.remove('tsvp-spinner-fade-in')\n this.container.classList.add('tsvp-spinner-fade-out')\n\n // Hide after animation\n setTimeout(() => {\n if (!this.visible) {\n this.container.style.display = 'none'\n }\n }, 200)\n }\n\n /**\n * Check if visible\n */\n isVisible(): boolean {\n return this.visible\n }\n\n /**\n * Set fill percentage (0-100)\n */\n setProgress(percent: number): void {\n const size = this.config.size\n const radius = (size - this.config.trackWidth) / 2\n const circumference = 2 * Math.PI * radius\n const filled = (percent / 100) * circumference\n\n this.fill.setAttribute('stroke-dasharray', `${filled} ${circumference - filled}`)\n }\n\n /**\n * Reset to indeterminate\n */\n resetProgress(): void {\n const size = this.config.size\n const radius = (size - this.config.trackWidth) / 2\n const circumference = 2 * Math.PI * radius\n\n this.fill.setAttribute('stroke-dasharray', `${circumference * 0.25} ${circumference * 0.75}`)\n }\n\n /**\n * Update config\n */\n updateConfig(config: Partial<SpinnerConfig>): void {\n Object.assign(this.config, config)\n\n if (config.size || config.trackWidth) {\n // Rebuild SVG\n const size = this.config.size\n const center = size / 2\n const radius = (size - this.config.trackWidth) / 2\n\n this.svg.setAttribute('width', String(size))\n this.svg.setAttribute('height', String(size))\n this.svg.setAttribute('viewBox', `0 0 ${size} ${size}`)\n\n this.track.setAttribute('cx', String(center))\n this.track.setAttribute('cy', String(center))\n this.track.setAttribute('r', String(radius))\n this.track.setAttribute('stroke-width', String(this.config.trackWidth))\n\n this.fill.setAttribute('cx', String(center))\n this.fill.setAttribute('cy', String(center))\n this.fill.setAttribute('r', String(radius))\n this.fill.setAttribute('stroke-width', String(this.config.trackWidth))\n this.fill.setAttribute('transform', `rotate(-90 ${center} ${center})`)\n\n this.resetProgress()\n }\n\n if (config.trackColor) {\n this.track.setAttribute('stroke', this.config.trackColor)\n }\n\n if (config.fillColor) {\n this.fill.setAttribute('stroke', this.config.fillColor)\n }\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n if (this.showTimeout) {\n clearTimeout(this.showTimeout)\n }\n this.container.remove()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createSpinner(config?: SpinnerConfig): Spinner {\n return new Spinner(config)\n}\n\n// =============================================================================\n// CSS Styles\n// =============================================================================\n\nexport const SPINNER_STYLES = `\n.tsvp-spinner {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n z-index: 5;\n pointer-events: none;\n}\n\n.tsvp-spinner svg {\n animation: tsvp-spinner-rotate 1s linear infinite;\n}\n\n@keyframes tsvp-spinner-rotate {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n}\n`\n",
|
|
14
|
+
"/**\n * Caption Styling\n *\n * Customizable caption/subtitle appearance.\n *\n * @module core/caption-styles\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface CaptionStyleOptions {\n /** Font family */\n fontFamily?: string\n /** Font size (CSS value) */\n fontSize?: string\n /** Font weight */\n fontWeight?: string | number\n /** Font color */\n fontColor?: string\n /** Text opacity (0-1) */\n fontOpacity?: number\n /** Background color */\n backgroundColor?: string\n /** Background opacity (0-1) */\n backgroundOpacity?: number\n /** Text edge style */\n edgeStyle?: 'none' | 'raised' | 'depressed' | 'uniform' | 'drop-shadow'\n /** Edge color */\n edgeColor?: string\n /** Window color (container background) */\n windowColor?: string\n /** Window opacity (0-1) */\n windowOpacity?: number\n /** Text alignment */\n textAlign?: 'left' | 'center' | 'right'\n /** Vertical position (percentage from bottom) */\n verticalPosition?: number\n /** Horizontal padding */\n horizontalPadding?: string\n /** Vertical padding */\n verticalPadding?: string\n /** Border radius */\n borderRadius?: string\n /** Line height */\n lineHeight?: string | number\n /** Letter spacing */\n letterSpacing?: string\n}\n\nexport interface CaptionStylePreset {\n name: string\n styles: CaptionStyleOptions\n}\n\n// =============================================================================\n// Default Values\n// =============================================================================\n\nexport const defaultCaptionStyles: Required<CaptionStyleOptions> = {\n fontFamily: 'Arial, Helvetica, sans-serif',\n fontSize: '1.5em',\n fontWeight: 'normal',\n fontColor: '#ffffff',\n fontOpacity: 1,\n backgroundColor: '#000000',\n backgroundOpacity: 0.75,\n edgeStyle: 'none',\n edgeColor: '#000000',\n windowColor: 'transparent',\n windowOpacity: 0,\n textAlign: 'center',\n verticalPosition: 10,\n horizontalPadding: '8px',\n verticalPadding: '4px',\n borderRadius: '4px',\n lineHeight: 1.4,\n letterSpacing: 'normal',\n}\n\n// =============================================================================\n// Style Presets\n// =============================================================================\n\nexport const captionPresets: Record<string, CaptionStylePreset> = {\n default: {\n name: 'Default',\n styles: {},\n },\n cinema: {\n name: 'Cinema',\n styles: {\n fontFamily: '\"Helvetica Neue\", Helvetica, Arial, sans-serif',\n fontSize: '1.8em',\n fontWeight: 'bold',\n backgroundColor: 'transparent',\n backgroundOpacity: 0,\n edgeStyle: 'drop-shadow',\n edgeColor: '#000000',\n },\n },\n classic: {\n name: 'Classic',\n styles: {\n fontFamily: '\"Times New Roman\", Times, serif',\n fontSize: '1.4em',\n fontColor: '#ffff00',\n backgroundColor: '#000000',\n backgroundOpacity: 0.8,\n },\n },\n highContrast: {\n name: 'High Contrast',\n styles: {\n fontFamily: 'Arial, sans-serif',\n fontSize: '1.6em',\n fontWeight: 'bold',\n fontColor: '#ffff00',\n backgroundColor: '#000000',\n backgroundOpacity: 1,\n edgeStyle: 'uniform',\n edgeColor: '#000000',\n },\n },\n minimal: {\n name: 'Minimal',\n styles: {\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n fontSize: '1.3em',\n backgroundColor: 'transparent',\n backgroundOpacity: 0,\n edgeStyle: 'drop-shadow',\n },\n },\n netflix: {\n name: 'Netflix Style',\n styles: {\n fontFamily: 'Netflix Sans, Helvetica Neue, Helvetica, Arial, sans-serif',\n fontSize: '1.6em',\n fontWeight: 'bold',\n backgroundColor: 'transparent',\n backgroundOpacity: 0,\n edgeStyle: 'drop-shadow',\n edgeColor: '#000000',\n },\n },\n youtube: {\n name: 'YouTube Style',\n styles: {\n fontFamily: '\"YouTube Noto\", Roboto, Arial, Helvetica, sans-serif',\n fontSize: '1.5em',\n fontWeight: 500,\n backgroundColor: '#000000',\n backgroundOpacity: 0.75,\n borderRadius: '2px',\n horizontalPadding: '4px',\n verticalPadding: '2px',\n },\n },\n}\n\n// =============================================================================\n// Caption Style Manager\n// =============================================================================\n\nexport class CaptionStyleManager {\n private styles: CaptionStyleOptions\n private container: HTMLElement | null = null\n private styleElement: HTMLStyleElement | null = null\n\n constructor(initialStyles: CaptionStyleOptions = {}) {\n this.styles = { ...defaultCaptionStyles, ...initialStyles }\n }\n\n /**\n * Get current styles\n */\n getStyles(): CaptionStyleOptions {\n return { ...this.styles }\n }\n\n /**\n * Update styles\n */\n setStyles(styles: Partial<CaptionStyleOptions>): void {\n this.styles = { ...this.styles, ...styles }\n this.applyStyles()\n }\n\n /**\n * Apply a preset\n */\n applyPreset(presetName: string): void {\n const preset = captionPresets[presetName]\n if (preset) {\n this.styles = { ...defaultCaptionStyles, ...preset.styles }\n this.applyStyles()\n }\n }\n\n /**\n * Reset to defaults\n */\n reset(): void {\n this.styles = { ...defaultCaptionStyles }\n this.applyStyles()\n }\n\n /**\n * Attach to caption container\n */\n attach(container: HTMLElement): void {\n this.container = container\n this.applyStyles()\n }\n\n /**\n * Generate CSS from styles\n */\n generateCSS(selector = '.tsvp-captions'): string {\n const s = this.styles as Required<CaptionStyleOptions>\n\n // Build text shadow based on edge style\n let textShadow = 'none'\n switch (s.edgeStyle) {\n case 'raised':\n textShadow = `1px 1px 0 ${s.edgeColor}, 2px 2px 0 ${s.edgeColor}`\n break\n case 'depressed':\n textShadow = `-1px -1px 0 ${s.edgeColor}, -2px -2px 0 ${s.edgeColor}`\n break\n case 'uniform':\n textShadow = `\n -1px -1px 0 ${s.edgeColor},\n 1px -1px 0 ${s.edgeColor},\n -1px 1px 0 ${s.edgeColor},\n 1px 1px 0 ${s.edgeColor}\n `\n break\n case 'drop-shadow':\n textShadow = `2px 2px 4px ${s.edgeColor}, 0 0 8px rgba(0, 0, 0, 0.5)`\n break\n }\n\n // Build background with opacity\n const bgOpacity = Math.round(s.backgroundOpacity * 255).toString(16).padStart(2, '0')\n const bgColor = s.backgroundColor === 'transparent'\n ? 'transparent'\n : `${s.backgroundColor}${bgOpacity}`\n\n // Build font color with opacity\n const fontOpacity = Math.round(s.fontOpacity * 255).toString(16).padStart(2, '0')\n const fontColor = `${s.fontColor}${fontOpacity}`\n\n // Window background\n const windowOpacity = Math.round(s.windowOpacity * 255).toString(16).padStart(2, '0')\n const windowBg = s.windowColor === 'transparent'\n ? 'transparent'\n : `${s.windowColor}${windowOpacity}`\n\n return `\n${selector} {\n --tsvp-captions-font-family: ${s.fontFamily};\n --tsvp-captions-font-size: ${s.fontSize};\n --tsvp-captions-font-weight: ${s.fontWeight};\n --tsvp-captions-text-color: ${fontColor};\n --tsvp-captions-bg: ${bgColor};\n --tsvp-captions-text-shadow: ${textShadow};\n --tsvp-captions-text-align: ${s.textAlign};\n --tsvp-captions-padding: ${s.verticalPadding} ${s.horizontalPadding};\n --tsvp-captions-radius: ${s.borderRadius};\n --tsvp-captions-line-height: ${s.lineHeight};\n --tsvp-captions-letter-spacing: ${s.letterSpacing};\n --tsvp-captions-bottom: ${s.verticalPosition}%;\n\n position: absolute;\n bottom: var(--tsvp-captions-bottom);\n left: 50%;\n transform: translateX(-50%);\n max-width: 80%;\n text-align: var(--tsvp-captions-text-align);\n background: ${windowBg};\n padding: 8px;\n border-radius: 4px;\n}\n\n${selector} span,\n${selector} .tsvp-caption-text {\n font-family: var(--tsvp-captions-font-family);\n font-size: var(--tsvp-captions-font-size);\n font-weight: var(--tsvp-captions-font-weight);\n color: var(--tsvp-captions-text-color);\n background: var(--tsvp-captions-bg);\n text-shadow: var(--tsvp-captions-text-shadow);\n padding: var(--tsvp-captions-padding);\n border-radius: var(--tsvp-captions-radius);\n line-height: var(--tsvp-captions-line-height);\n letter-spacing: var(--tsvp-captions-letter-spacing);\n display: inline;\n box-decoration-break: clone;\n -webkit-box-decoration-break: clone;\n}\n`\n }\n\n /**\n * Apply styles to container\n */\n private applyStyles(): void {\n if (!this.container) return\n\n // Remove existing style element\n if (this.styleElement) {\n this.styleElement.remove()\n }\n\n // Create new style element\n this.styleElement = document.createElement('style')\n this.styleElement.setAttribute('data-tsvp-caption-styles', 'true')\n this.styleElement.textContent = this.generateCSS()\n document.head.appendChild(this.styleElement)\n }\n\n /**\n * Get available presets\n */\n static getPresets(): CaptionStylePreset[] {\n return Object.values(captionPresets)\n }\n\n /**\n * Detach and cleanup\n */\n destroy(): void {\n if (this.styleElement) {\n this.styleElement.remove()\n this.styleElement = null\n }\n this.container = null\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createCaptionStyleManager(styles?: CaptionStyleOptions): CaptionStyleManager {\n return new CaptionStyleManager(styles)\n}\n\n// =============================================================================\n// Caption Settings Menu Component\n// =============================================================================\n\nexport class CaptionSettingsMenu {\n private container: HTMLElement\n private manager: CaptionStyleManager\n private onChange: ((_styles: CaptionStyleOptions) => void) | null = null\n\n constructor(manager: CaptionStyleManager) {\n this.manager = manager\n this.container = document.createElement('div')\n this.container.className = 'tsvp-caption-settings'\n this.render()\n }\n\n private render(): void {\n this.container.innerHTML = ''\n this.container.style.cssText = `\n padding: 16px;\n background: var(--tsvp-menu-bg, rgba(20, 20, 20, 0.95));\n border-radius: var(--tsvp-menu-radius, 8px);\n color: var(--tsvp-color-text, #ffffff);\n font-family: var(--tsvp-font-family, sans-serif);\n font-size: var(--tsvp-font-size, 14px);\n `\n\n const styles = this.manager.getStyles()\n\n // Preset selector\n this.addPresetSelector()\n\n // Font size\n this.addSlider('Font Size', 'fontSize', ['0.8em', '1em', '1.2em', '1.5em', '1.8em', '2em'], styles.fontSize || '1.5em')\n\n // Font color\n this.addColorPicker('Text Color', 'fontColor', styles.fontColor || '#ffffff')\n\n // Background opacity\n this.addSlider('Background', 'backgroundOpacity', ['0', '0.25', '0.5', '0.75', '1'], String(styles.backgroundOpacity ?? 0.75))\n\n // Edge style\n this.addSelect('Text Edge', 'edgeStyle', [\n { value: 'none', label: 'None' },\n { value: 'raised', label: 'Raised' },\n { value: 'depressed', label: 'Depressed' },\n { value: 'uniform', label: 'Uniform' },\n { value: 'drop-shadow', label: 'Drop Shadow' },\n ], styles.edgeStyle || 'none')\n\n // Reset button\n const resetBtn = document.createElement('button')\n resetBtn.textContent = 'Reset to Default'\n resetBtn.style.cssText = `\n width: 100%;\n margin-top: 16px;\n padding: 8px 16px;\n border: 1px solid var(--tsvp-color-border, rgba(255, 255, 255, 0.2));\n border-radius: var(--tsvp-radius-sm, 4px);\n background: transparent;\n color: var(--tsvp-color-text, #ffffff);\n cursor: pointer;\n `\n resetBtn.addEventListener('click', () => {\n this.manager.reset()\n this.render()\n if (this.onChange) {\n this.onChange(this.manager.getStyles())\n }\n })\n this.container.appendChild(resetBtn)\n }\n\n private addPresetSelector(): void {\n const row = this.createRow('Preset')\n const select = document.createElement('select')\n select.style.cssText = this.getInputStyle()\n\n for (const [key, preset] of Object.entries(captionPresets)) {\n const option = document.createElement('option')\n option.value = key\n option.textContent = preset.name\n select.appendChild(option)\n }\n\n select.addEventListener('change', () => {\n this.manager.applyPreset(select.value)\n this.render()\n if (this.onChange) {\n this.onChange(this.manager.getStyles())\n }\n })\n\n row.appendChild(select)\n this.container.appendChild(row)\n }\n\n private addSlider(label: string, key: keyof CaptionStyleOptions, values: string[], current: string): void {\n const row = this.createRow(label)\n const slider = document.createElement('input')\n slider.type = 'range'\n slider.min = '0'\n slider.max = String(values.length - 1)\n slider.value = String(values.indexOf(current))\n slider.style.cssText = 'flex: 1;'\n\n slider.addEventListener('input', () => {\n const value = values[Number.parseInt(slider.value, 10)]\n this.manager.setStyles({ [key]: key === 'backgroundOpacity' ? Number.parseFloat(value) : value })\n if (this.onChange) {\n this.onChange(this.manager.getStyles())\n }\n })\n\n row.appendChild(slider)\n this.container.appendChild(row)\n }\n\n private addColorPicker(label: string, key: keyof CaptionStyleOptions, current: string): void {\n const row = this.createRow(label)\n const input = document.createElement('input')\n input.type = 'color'\n input.value = current\n input.style.cssText = 'width: 40px; height: 30px; border: none; cursor: pointer;'\n\n input.addEventListener('input', () => {\n this.manager.setStyles({ [key]: input.value })\n if (this.onChange) {\n this.onChange(this.manager.getStyles())\n }\n })\n\n row.appendChild(input)\n this.container.appendChild(row)\n }\n\n private addSelect(label: string, key: keyof CaptionStyleOptions, options: Array<{ value: string, label: string }>, current: string): void {\n const row = this.createRow(label)\n const select = document.createElement('select')\n select.style.cssText = this.getInputStyle()\n\n for (const opt of options) {\n const option = document.createElement('option')\n option.value = opt.value\n option.textContent = opt.label\n option.selected = opt.value === current\n select.appendChild(option)\n }\n\n select.addEventListener('change', () => {\n this.manager.setStyles({ [key]: select.value as any })\n if (this.onChange) {\n this.onChange(this.manager.getStyles())\n }\n })\n\n row.appendChild(select)\n this.container.appendChild(row)\n }\n\n private createRow(label: string): HTMLElement {\n const row = document.createElement('div')\n row.style.cssText = `\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n margin-bottom: 12px;\n `\n\n const labelEl = document.createElement('span')\n labelEl.textContent = label\n labelEl.style.opacity = '0.8'\n row.appendChild(labelEl)\n\n return row\n }\n\n private getInputStyle(): string {\n return `\n padding: 6px 10px;\n border: 1px solid var(--tsvp-color-border, rgba(255, 255, 255, 0.2));\n border-radius: var(--tsvp-radius-sm, 4px);\n background: rgba(0, 0, 0, 0.3);\n color: var(--tsvp-color-text, #ffffff);\n `\n }\n\n /**\n * Set change handler\n */\n onStyleChange(handler: (styles: CaptionStyleOptions) => void): void {\n this.onChange = handler\n }\n\n /**\n * Get element\n */\n getElement(): HTMLElement {\n return this.container\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n this.container.remove()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createCaptionSettingsMenu(manager: CaptionStyleManager): CaptionSettingsMenu {\n return new CaptionSettingsMenu(manager)\n}\n",
|
|
15
|
+
"/**\n * Tooltips System\n *\n * Tooltips for player controls with smart positioning.\n *\n * @module core/tooltips\n */\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type TooltipPlacement = 'top' | 'bottom' | 'left' | 'right'\n\nexport interface TooltipConfig {\n /** Tooltip placement */\n placement?: TooltipPlacement\n /** Offset from target in pixels */\n offset?: number\n /** Show delay in ms */\n showDelay?: number\n /** Hide delay in ms */\n hideDelay?: number\n /** Container for boundary detection */\n container?: HTMLElement\n /** Custom class name */\n className?: string\n}\n\n// =============================================================================\n// Tooltip Component\n// =============================================================================\n\nexport class Tooltip {\n private element: HTMLElement\n private target: HTMLElement | null = null\n private config: Required<TooltipConfig>\n private showTimeout: ReturnType<typeof setTimeout> | null = null\n private hideTimeout: ReturnType<typeof setTimeout> | null = null\n private visible = false\n\n constructor(config: TooltipConfig = {}) {\n this.config = {\n placement: config.placement || 'top',\n offset: config.offset ?? 8,\n showDelay: config.showDelay ?? 300,\n hideDelay: config.hideDelay ?? 0,\n container: config.container || document.body,\n className: config.className || '',\n }\n\n this.element = document.createElement('div')\n this.element.className = `tsvp-tooltip ${this.config.className}`.trim()\n this.element.setAttribute('role', 'tooltip')\n this.element.style.cssText = `\n position: absolute;\n z-index: 1000;\n padding: 6px 10px;\n background: var(--tsvp-controls-tooltip-bg, rgba(0, 0, 0, 0.9));\n color: var(--tsvp-controls-tooltip-text, #ffffff);\n font-size: var(--tsvp-font-size-sm, 12px);\n font-family: var(--tsvp-font-family, sans-serif);\n border-radius: var(--tsvp-radius-sm, 4px);\n white-space: nowrap;\n pointer-events: none;\n opacity: 0;\n transform: scale(0.9);\n transition: opacity 150ms ease, transform 150ms ease;\n box-shadow: var(--tsvp-shadow-md, 0 4px 6px rgba(0, 0, 0, 0.3));\n `\n\n this.hide()\n }\n\n /**\n * Attach tooltip to a target element\n */\n attach(target: HTMLElement, text: string): void {\n this.target = target\n this.element.textContent = text\n\n // Add to DOM if not already\n if (!this.element.parentElement) {\n this.config.container.appendChild(this.element)\n }\n\n // Event listeners\n target.addEventListener('mouseenter', this.handleMouseEnter)\n target.addEventListener('mouseleave', this.handleMouseLeave)\n target.addEventListener('focus', this.handleFocus)\n target.addEventListener('blur', this.handleBlur)\n }\n\n /**\n * Detach from target\n */\n detach(): void {\n if (this.target) {\n this.target.removeEventListener('mouseenter', this.handleMouseEnter)\n this.target.removeEventListener('mouseleave', this.handleMouseLeave)\n this.target.removeEventListener('focus', this.handleFocus)\n this.target.removeEventListener('blur', this.handleBlur)\n this.target = null\n }\n this.hide()\n }\n\n /**\n * Update tooltip text\n */\n setText(text: string): void {\n this.element.textContent = text\n if (this.visible) {\n this.position()\n }\n }\n\n /**\n * Show tooltip\n */\n show(): void {\n if (this.hideTimeout) {\n clearTimeout(this.hideTimeout)\n this.hideTimeout = null\n }\n\n if (this.visible) return\n\n this.showTimeout = setTimeout(() => {\n this.visible = true\n this.element.style.display = 'block'\n this.position()\n\n // Trigger animation\n requestAnimationFrame(() => {\n this.element.style.opacity = '1'\n this.element.style.transform = 'scale(1)'\n })\n }, this.config.showDelay)\n }\n\n /**\n * Hide tooltip\n */\n hide(): void {\n if (this.showTimeout) {\n clearTimeout(this.showTimeout)\n this.showTimeout = null\n }\n\n if (!this.visible) {\n this.element.style.display = 'none'\n return\n }\n\n this.hideTimeout = setTimeout(() => {\n this.visible = false\n this.element.style.opacity = '0'\n this.element.style.transform = 'scale(0.9)'\n\n setTimeout(() => {\n if (!this.visible) {\n this.element.style.display = 'none'\n }\n }, 150)\n }, this.config.hideDelay)\n }\n\n /**\n * Position tooltip relative to target\n */\n private position(): void {\n if (!this.target) return\n\n const targetRect = this.target.getBoundingClientRect()\n const tooltipRect = this.element.getBoundingClientRect()\n const containerRect = this.config.container.getBoundingClientRect()\n\n let top = 0\n let left = 0\n\n switch (this.config.placement) {\n case 'top':\n top = targetRect.top - tooltipRect.height - this.config.offset\n left = targetRect.left + (targetRect.width - tooltipRect.width) / 2\n break\n case 'bottom':\n top = targetRect.bottom + this.config.offset\n left = targetRect.left + (targetRect.width - tooltipRect.width) / 2\n break\n case 'left':\n top = targetRect.top + (targetRect.height - tooltipRect.height) / 2\n left = targetRect.left - tooltipRect.width - this.config.offset\n break\n case 'right':\n top = targetRect.top + (targetRect.height - tooltipRect.height) / 2\n left = targetRect.right + this.config.offset\n break\n }\n\n // Keep within container bounds\n const minLeft = containerRect.left + 4\n const maxLeft = containerRect.right - tooltipRect.width - 4\n const minTop = containerRect.top + 4\n const maxTop = containerRect.bottom - tooltipRect.height - 4\n\n left = Math.max(minLeft, Math.min(maxLeft, left))\n top = Math.max(minTop, Math.min(maxTop, top))\n\n // Adjust for scroll\n left += window.scrollX\n top += window.scrollY\n\n this.element.style.left = `${left}px`\n this.element.style.top = `${top}px`\n }\n\n private handleMouseEnter = (): void => {\n this.show()\n }\n\n private handleMouseLeave = (): void => {\n this.hide()\n }\n\n private handleFocus = (): void => {\n this.show()\n }\n\n private handleBlur = (): void => {\n this.hide()\n }\n\n /**\n * Get element\n */\n getElement(): HTMLElement {\n return this.element\n }\n\n /**\n * Check if visible\n */\n isVisible(): boolean {\n return this.visible\n }\n\n /**\n * Destroy tooltip\n */\n destroy(): void {\n this.detach()\n this.element.remove()\n }\n}\n\n// =============================================================================\n// Tooltip Manager\n// =============================================================================\n\nexport class TooltipManager {\n private tooltips = new Map<HTMLElement, Tooltip>()\n private config: TooltipConfig\n\n constructor(config: TooltipConfig = {}) {\n this.config = config\n }\n\n /**\n * Add tooltip to an element\n */\n add(target: HTMLElement, text: string, config?: Partial<TooltipConfig>): Tooltip {\n // Remove existing tooltip if any\n this.remove(target)\n\n const tooltip = new Tooltip({ ...this.config, ...config })\n tooltip.attach(target, text)\n this.tooltips.set(target, tooltip)\n\n return tooltip\n }\n\n /**\n * Update tooltip text\n */\n update(target: HTMLElement, text: string): void {\n const tooltip = this.tooltips.get(target)\n if (tooltip) {\n tooltip.setText(text)\n }\n }\n\n /**\n * Remove tooltip from element\n */\n remove(target: HTMLElement): void {\n const tooltip = this.tooltips.get(target)\n if (tooltip) {\n tooltip.destroy()\n this.tooltips.delete(target)\n }\n }\n\n /**\n * Hide all tooltips\n */\n hideAll(): void {\n this.tooltips.forEach(tooltip => tooltip.hide())\n }\n\n /**\n * Destroy all tooltips\n */\n destroy(): void {\n this.tooltips.forEach(tooltip => tooltip.destroy())\n this.tooltips.clear()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createTooltip(config?: TooltipConfig): Tooltip {\n return new Tooltip(config)\n}\n\nexport function createTooltipManager(config?: TooltipConfig): TooltipManager {\n return new TooltipManager(config)\n}\n\n// =============================================================================\n// CSS Styles\n// =============================================================================\n\nexport const TOOLTIP_STYLES = `\n.tsvp-tooltip {\n position: absolute;\n z-index: 1000;\n padding: 6px 10px;\n background: var(--tsvp-controls-tooltip-bg, rgba(0, 0, 0, 0.9));\n color: var(--tsvp-controls-tooltip-text, #ffffff);\n font-size: var(--tsvp-font-size-sm, 12px);\n font-family: var(--tsvp-font-family, sans-serif);\n border-radius: var(--tsvp-radius-sm, 4px);\n white-space: nowrap;\n pointer-events: none;\n box-shadow: var(--tsvp-shadow-md, 0 4px 6px rgba(0, 0, 0, 0.3));\n}\n\n.tsvp-tooltip::after {\n content: '';\n position: absolute;\n border: 6px solid transparent;\n}\n\n.tsvp-tooltip--top::after {\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-top-color: var(--tsvp-controls-tooltip-bg, rgba(0, 0, 0, 0.9));\n}\n\n.tsvp-tooltip--bottom::after {\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border-bottom-color: var(--tsvp-controls-tooltip-bg, rgba(0, 0, 0, 0.9));\n}\n`\n",
|
|
16
|
+
"/**\n * Error Overlay\n *\n * Visual error display with retry functionality.\n *\n * @module core/error-overlay\n */\n\nimport type { MediaError } from '../types'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ErrorOverlayConfig {\n /** Show retry button */\n showRetry?: boolean\n /** Retry button text */\n retryText?: string\n /** Custom error messages by code */\n messages?: Record<number, string>\n /** Show error details (for debugging) */\n showDetails?: boolean\n /** Auto-hide delay (0 = no auto-hide) */\n autoHideDelay?: number\n /** Custom icon SVG */\n icon?: string\n}\n\n// =============================================================================\n// Default Error Messages\n// =============================================================================\n\nconst defaultErrorMessages: Record<number, string> = {\n 1: 'The media playback was aborted.',\n 2: 'A network error occurred while loading the media.',\n 3: 'The media could not be decoded.',\n 4: 'The media format is not supported.',\n}\n\n// =============================================================================\n// Error Overlay Component\n// =============================================================================\n\nexport class ErrorOverlay {\n private container: HTMLElement\n private config: Required<ErrorOverlayConfig>\n private currentError: MediaError | null = null\n private onRetry: (() => void) | null = null\n private autoHideTimeout: ReturnType<typeof setTimeout> | null = null\n\n constructor(config: ErrorOverlayConfig = {}) {\n this.config = {\n showRetry: config.showRetry ?? true,\n retryText: config.retryText || 'Try Again',\n messages: { ...defaultErrorMessages, ...config.messages },\n showDetails: config.showDetails ?? false,\n autoHideDelay: config.autoHideDelay ?? 0,\n icon: config.icon || this.getDefaultIcon(),\n }\n\n this.container = document.createElement('div')\n this.container.className = 'tsvp-error-overlay'\n this.container.setAttribute('role', 'alert')\n this.container.setAttribute('aria-live', 'assertive')\n\n this.applyStyles()\n this.hide()\n }\n\n private applyStyles(): void {\n this.container.style.cssText = `\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n background: rgba(0, 0, 0, 0.85);\n color: var(--tsvp-color-text, #ffffff);\n font-family: var(--tsvp-font-family, sans-serif);\n z-index: 20;\n padding: 24px;\n text-align: center;\n `\n }\n\n private getDefaultIcon(): string {\n return `\n <svg viewBox=\"0 0 24 24\" width=\"48\" height=\"48\" fill=\"currentColor\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\"/>\n </svg>\n `\n }\n\n /**\n * Get error message\n */\n private getMessage(error: MediaError): string {\n return this.config.messages[error.code] || error.message || 'An error occurred.'\n }\n\n /**\n * Render error content\n */\n private render(): void {\n if (!this.currentError) return\n\n this.container.innerHTML = ''\n\n // Icon\n const icon = document.createElement('div')\n icon.className = 'tsvp-error-icon'\n icon.innerHTML = this.config.icon\n icon.style.cssText = `\n color: var(--tsvp-color-error, #ff4757);\n opacity: 0.9;\n `\n this.container.appendChild(icon)\n\n // Title\n const title = document.createElement('h3')\n title.className = 'tsvp-error-title'\n title.textContent = 'Playback Error'\n title.style.cssText = `\n margin: 0;\n font-size: var(--tsvp-font-size-lg, 18px);\n font-weight: var(--tsvp-font-weight-bold, 600);\n `\n this.container.appendChild(title)\n\n // Message\n const message = document.createElement('p')\n message.className = 'tsvp-error-message'\n message.textContent = this.getMessage(this.currentError)\n message.style.cssText = `\n margin: 0;\n font-size: var(--tsvp-font-size, 14px);\n opacity: 0.8;\n max-width: 400px;\n `\n this.container.appendChild(message)\n\n // Details (optional)\n if (this.config.showDetails && this.currentError.details) {\n const details = document.createElement('pre')\n details.className = 'tsvp-error-details'\n details.textContent = typeof this.currentError.details === 'string'\n ? this.currentError.details\n : JSON.stringify(this.currentError.details, null, 2)\n details.style.cssText = `\n margin: 8px 0 0;\n padding: 8px 12px;\n background: rgba(0, 0, 0, 0.3);\n border-radius: var(--tsvp-radius-sm, 4px);\n font-size: 11px;\n font-family: monospace;\n text-align: left;\n max-width: 100%;\n overflow-x: auto;\n opacity: 0.7;\n `\n this.container.appendChild(details)\n }\n\n // Retry button\n if (this.config.showRetry) {\n const retryBtn = document.createElement('button')\n retryBtn.className = 'tsvp-error-retry'\n retryBtn.type = 'button'\n retryBtn.textContent = this.config.retryText\n\n retryBtn.style.cssText = `\n margin-top: 8px;\n padding: 10px 24px;\n border: none;\n border-radius: var(--tsvp-radius-full, 9999px);\n background: var(--tsvp-color-primary, #00a8ff);\n color: var(--tsvp-color-text-on-primary, #ffffff);\n font-size: var(--tsvp-font-size, 14px);\n font-weight: var(--tsvp-font-weight-bold, 600);\n font-family: var(--tsvp-font-family, sans-serif);\n cursor: pointer;\n transition: transform 150ms ease, background 150ms ease;\n `\n\n retryBtn.addEventListener('mouseenter', () => {\n retryBtn.style.transform = 'scale(1.05)'\n })\n\n retryBtn.addEventListener('mouseleave', () => {\n retryBtn.style.transform = 'scale(1)'\n })\n\n retryBtn.addEventListener('click', () => {\n if (this.onRetry) {\n this.onRetry()\n }\n this.hide()\n })\n\n this.container.appendChild(retryBtn)\n }\n\n // Dismiss on background click\n this.container.addEventListener('click', (e) => {\n if (e.target === this.container) {\n this.hide()\n }\n })\n }\n\n /**\n * Show error\n */\n show(error: MediaError): void {\n this.currentError = error\n this.render()\n this.container.style.display = 'flex'\n\n // Fade in\n this.container.style.opacity = '0'\n requestAnimationFrame(() => {\n this.container.style.transition = 'opacity 200ms ease'\n this.container.style.opacity = '1'\n })\n\n // Auto-hide if configured\n if (this.config.autoHideDelay > 0) {\n if (this.autoHideTimeout) {\n clearTimeout(this.autoHideTimeout)\n }\n this.autoHideTimeout = setTimeout(() => {\n this.hide()\n }, this.config.autoHideDelay)\n }\n }\n\n /**\n * Hide error overlay\n */\n hide(): void {\n if (this.autoHideTimeout) {\n clearTimeout(this.autoHideTimeout)\n this.autoHideTimeout = null\n }\n\n this.container.style.opacity = '0'\n setTimeout(() => {\n this.container.style.display = 'none'\n this.currentError = null\n }, 200)\n }\n\n /**\n * Set retry handler\n */\n setRetryHandler(handler: () => void): void {\n this.onRetry = handler\n }\n\n /**\n * Get element\n */\n getElement(): HTMLElement {\n return this.container\n }\n\n /**\n * Check if visible\n */\n isVisible(): boolean {\n return this.container.style.display !== 'none'\n }\n\n /**\n * Get current error\n */\n getError(): MediaError | null {\n return this.currentError\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n if (this.autoHideTimeout) {\n clearTimeout(this.autoHideTimeout)\n }\n this.container.remove()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createErrorOverlay(config?: ErrorOverlayConfig): ErrorOverlay {\n return new ErrorOverlay(config)\n}\n\n// =============================================================================\n// CSS Styles\n// =============================================================================\n\nexport const ERROR_OVERLAY_STYLES = `\n.tsvp-error-overlay {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n background: rgba(0, 0, 0, 0.85);\n color: var(--tsvp-color-text, #ffffff);\n font-family: var(--tsvp-font-family, sans-serif);\n z-index: 20;\n padding: 24px;\n text-align: center;\n}\n\n.tsvp-error-icon {\n color: var(--tsvp-color-error, #ff4757);\n}\n\n.tsvp-error-title {\n margin: 0;\n font-size: var(--tsvp-font-size-lg, 18px);\n font-weight: var(--tsvp-font-weight-bold, 600);\n}\n\n.tsvp-error-message {\n margin: 0;\n opacity: 0.8;\n max-width: 400px;\n}\n\n.tsvp-error-retry {\n padding: 10px 24px;\n border: none;\n border-radius: var(--tsvp-radius-full, 9999px);\n background: var(--tsvp-color-primary, #00a8ff);\n color: var(--tsvp-color-text-on-primary, #ffffff);\n font-weight: var(--tsvp-font-weight-bold, 600);\n cursor: pointer;\n transition: transform 150ms ease;\n}\n\n.tsvp-error-retry:hover {\n transform: scale(1.05);\n}\n`\n",
|
|
17
|
+
"/**\n * Settings Menu\n *\n * Unified settings menu for quality, speed, captions, and audio.\n *\n * @module core/settings-menu\n */\n\nimport type { VideoQuality, AudioTrack, TextTrack } from '../types'\n\n// Helper to generate quality label\nfunction getQualityLabel(quality: VideoQuality): string {\n if (quality.height >= 2160) return '4K'\n if (quality.height >= 1440) return '1440p'\n if (quality.height >= 1080) return '1080p'\n if (quality.height >= 720) return '720p'\n if (quality.height >= 480) return '480p'\n if (quality.height >= 360) return '360p'\n return `${quality.height}p`\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface SettingsMenuConfig {\n /** Show quality options */\n quality?: boolean\n /** Show playback speed options */\n speed?: boolean\n /** Show captions options */\n captions?: boolean\n /** Show audio track options */\n audioTracks?: boolean\n /** Show audio boost options */\n audioBoost?: boolean\n /** Custom speed values */\n speedValues?: number[]\n /** Custom boost values */\n boostValues?: number[]\n}\n\nexport interface SettingsMenuState {\n qualities: VideoQuality[]\n currentQuality: VideoQuality | null\n isAutoQuality: boolean\n speeds: number[]\n currentSpeed: number\n textTracks: TextTrack[]\n currentTextTrack: TextTrack | null\n audioTracks: AudioTrack[]\n currentAudioTrack: AudioTrack | null\n audioBoost: number\n}\n\ntype MenuView = 'main' | 'quality' | 'speed' | 'captions' | 'audio' | 'boost'\n\n// =============================================================================\n// Settings Menu Component\n// =============================================================================\n\nexport class SettingsMenu {\n private container: HTMLElement\n private config: Required<SettingsMenuConfig>\n private state: SettingsMenuState\n private currentView: MenuView = 'main'\n private visible = false\n\n // Event handlers\n private onQualityChange: ((_quality: VideoQuality | 'auto') => void) | null = null\n private onSpeedChange: ((_speed: number) => void) | null = null\n private onTextTrackChange: ((_track: TextTrack | null) => void) | null = null\n private onAudioTrackChange: ((_track: AudioTrack) => void) | null = null\n private onBoostChange: ((_boost: number) => void) | null = null\n\n constructor(config: SettingsMenuConfig = {}) {\n this.config = {\n quality: config.quality ?? true,\n speed: config.speed ?? true,\n captions: config.captions ?? true,\n audioTracks: config.audioTracks ?? true,\n audioBoost: config.audioBoost ?? true,\n speedValues: config.speedValues || [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],\n boostValues: config.boostValues || [1, 1.25, 1.5, 1.75, 2, 2.5, 3],\n }\n\n this.state = {\n qualities: [],\n currentQuality: null,\n isAutoQuality: true,\n speeds: this.config.speedValues,\n currentSpeed: 1,\n textTracks: [],\n currentTextTrack: null,\n audioTracks: [],\n currentAudioTrack: null,\n audioBoost: 1,\n }\n\n this.container = document.createElement('div')\n this.container.className = 'tsvp-settings-menu'\n this.container.setAttribute('role', 'menu')\n\n this.applyStyles()\n this.hide()\n }\n\n private applyStyles(): void {\n this.container.style.cssText = `\n position: absolute;\n bottom: 100%;\n right: 0;\n margin-bottom: 8px;\n min-width: 200px;\n max-height: 300px;\n overflow-y: auto;\n background: var(--tsvp-menu-bg, rgba(20, 20, 20, 0.95));\n border-radius: var(--tsvp-menu-radius, 8px);\n box-shadow: var(--tsvp-menu-shadow, 0 4px 20px rgba(0, 0, 0, 0.5));\n font-family: var(--tsvp-font-family, sans-serif);\n font-size: var(--tsvp-font-size, 14px);\n color: var(--tsvp-color-text, #ffffff);\n z-index: 100;\n `\n }\n\n /**\n * Update state\n */\n updateState(state: Partial<SettingsMenuState>): void {\n Object.assign(this.state, state)\n if (this.visible) {\n this.render()\n }\n }\n\n /**\n * Render menu\n */\n private render(): void {\n this.container.innerHTML = ''\n\n switch (this.currentView) {\n case 'main':\n this.renderMainMenu()\n break\n case 'quality':\n this.renderQualityMenu()\n break\n case 'speed':\n this.renderSpeedMenu()\n break\n case 'captions':\n this.renderCaptionsMenu()\n break\n case 'audio':\n this.renderAudioMenu()\n break\n case 'boost':\n this.renderBoostMenu()\n break\n }\n }\n\n /**\n * Render main menu\n */\n private renderMainMenu(): void {\n // Quality\n if (this.config.quality && this.state.qualities.length > 0) {\n let currentLabel = 'Auto'\n if (this.state.isAutoQuality) {\n currentLabel = this.state.currentQuality\n ? `Auto (${getQualityLabel(this.state.currentQuality)})`\n : 'Auto'\n }\n else if (this.state.currentQuality) {\n currentLabel = getQualityLabel(this.state.currentQuality)\n }\n\n this.addMenuItem('Quality', currentLabel, () => {\n this.currentView = 'quality'\n this.render()\n })\n }\n\n // Speed\n if (this.config.speed) {\n const speedLabel = this.state.currentSpeed === 1 ? 'Normal' : `${this.state.currentSpeed}x`\n this.addMenuItem('Speed', speedLabel, () => {\n this.currentView = 'speed'\n this.render()\n })\n }\n\n // Captions\n if (this.config.captions && this.state.textTracks.length > 0) {\n const captionLabel = this.state.currentTextTrack?.label || 'Off'\n this.addMenuItem('Captions', captionLabel, () => {\n this.currentView = 'captions'\n this.render()\n })\n }\n\n // Audio tracks\n if (this.config.audioTracks && this.state.audioTracks.length > 1) {\n const audioLabel = this.state.currentAudioTrack?.label || 'Default'\n this.addMenuItem('Audio', audioLabel, () => {\n this.currentView = 'audio'\n this.render()\n })\n }\n\n // Audio boost\n if (this.config.audioBoost) {\n const boostLabel = this.state.audioBoost === 1 ? 'Off' : `${Math.round(this.state.audioBoost * 100)}%`\n this.addMenuItem('Audio Boost', boostLabel, () => {\n this.currentView = 'boost'\n this.render()\n })\n }\n }\n\n /**\n * Render quality submenu\n */\n private renderQualityMenu(): void {\n this.addBackButton('Quality')\n\n // Auto option\n const autoLabel = this.state.isAutoQuality && this.state.currentQuality\n ? `Auto (${getQualityLabel(this.state.currentQuality)})`\n : 'Auto'\n\n this.addRadioItem(\n autoLabel,\n this.state.isAutoQuality,\n () => {\n if (this.onQualityChange) {\n this.onQualityChange('auto')\n }\n this.goBack()\n }\n )\n\n // Quality options\n for (const quality of this.state.qualities) {\n const isSelected = !this.state.isAutoQuality\n && this.state.currentQuality !== null\n && this.state.currentQuality.id === quality.id\n\n this.addRadioItem(\n getQualityLabel(quality),\n isSelected,\n () => {\n if (this.onQualityChange) {\n this.onQualityChange(quality)\n }\n this.goBack()\n }\n )\n }\n }\n\n /**\n * Render speed submenu\n */\n private renderSpeedMenu(): void {\n this.addBackButton('Speed')\n\n for (const speed of this.state.speeds) {\n const label = speed === 1 ? 'Normal' : `${speed}x`\n this.addRadioItem(\n label,\n this.state.currentSpeed === speed,\n () => {\n if (this.onSpeedChange) {\n this.onSpeedChange(speed)\n }\n this.goBack()\n }\n )\n }\n }\n\n /**\n * Render captions submenu\n */\n private renderCaptionsMenu(): void {\n this.addBackButton('Captions')\n\n // Off option\n this.addRadioItem(\n 'Off',\n this.state.currentTextTrack === null,\n () => {\n if (this.onTextTrackChange) {\n this.onTextTrackChange(null)\n }\n this.goBack()\n }\n )\n\n // Track options\n for (const track of this.state.textTracks) {\n this.addRadioItem(\n track.label || track.language || 'Unknown',\n this.state.currentTextTrack?.id === track.id,\n () => {\n if (this.onTextTrackChange) {\n this.onTextTrackChange(track)\n }\n this.goBack()\n }\n )\n }\n }\n\n /**\n * Render audio submenu\n */\n private renderAudioMenu(): void {\n this.addBackButton('Audio')\n\n for (const track of this.state.audioTracks) {\n this.addRadioItem(\n track.label || track.language || 'Unknown',\n this.state.currentAudioTrack?.id === track.id,\n () => {\n if (this.onAudioTrackChange) {\n this.onAudioTrackChange(track)\n }\n this.goBack()\n }\n )\n }\n }\n\n /**\n * Render audio boost submenu\n */\n private renderBoostMenu(): void {\n this.addBackButton('Audio Boost')\n\n for (const boost of this.config.boostValues) {\n const label = boost === 1 ? 'Off' : `${Math.round(boost * 100)}%`\n this.addRadioItem(\n label,\n this.state.audioBoost === boost,\n () => {\n if (this.onBoostChange) {\n this.onBoostChange(boost)\n }\n this.goBack()\n }\n )\n }\n }\n\n /**\n * Add menu item with value\n */\n private addMenuItem(label: string, value: string, onClick: () => void): void {\n const item = document.createElement('button')\n item.type = 'button'\n item.className = 'tsvp-settings-item'\n item.setAttribute('role', 'menuitem')\n\n item.style.cssText = `\n display: flex;\n align-items: center;\n justify-content: space-between;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: transparent;\n color: inherit;\n font-size: inherit;\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n transition: background var(--tsvp-transition-fast, 100ms);\n `\n\n const labelSpan = document.createElement('span')\n labelSpan.textContent = label\n\n const valueSpan = document.createElement('span')\n valueSpan.textContent = value\n valueSpan.style.cssText = `\n display: flex;\n align-items: center;\n gap: 4px;\n opacity: 0.7;\n `\n\n // Arrow icon\n const arrow = document.createElement('span')\n arrow.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\">\n <path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"/>\n </svg>\n `\n valueSpan.appendChild(arrow)\n\n item.appendChild(labelSpan)\n item.appendChild(valueSpan)\n\n item.addEventListener('mouseenter', () => {\n item.style.background = 'var(--tsvp-menu-item-hover-bg, rgba(255, 255, 255, 0.1))'\n })\n item.addEventListener('mouseleave', () => {\n item.style.background = 'transparent'\n })\n item.addEventListener('click', onClick)\n\n this.container.appendChild(item)\n }\n\n /**\n * Add radio item\n */\n private addRadioItem(label: string, selected: boolean, onClick: () => void): void {\n const item = document.createElement('button')\n item.type = 'button'\n item.className = 'tsvp-settings-radio'\n item.setAttribute('role', 'menuitemradio')\n item.setAttribute('aria-checked', String(selected))\n\n item.style.cssText = `\n display: flex;\n align-items: center;\n gap: 12px;\n width: 100%;\n padding: 10px 16px;\n border: none;\n background: ${selected ? 'var(--tsvp-menu-item-active-bg, rgba(0, 168, 255, 0.2))' : 'transparent'};\n color: inherit;\n font-size: inherit;\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n transition: background var(--tsvp-transition-fast, 100ms);\n `\n\n // Check icon\n const check = document.createElement('span')\n check.style.cssText = `\n width: 16px;\n height: 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n `\n check.innerHTML = selected\n ? `<svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"var(--tsvp-color-primary, #00a8ff)\">\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\n </svg>`\n : ''\n\n const labelSpan = document.createElement('span')\n labelSpan.textContent = label\n\n item.appendChild(check)\n item.appendChild(labelSpan)\n\n item.addEventListener('mouseenter', () => {\n if (!selected) {\n item.style.background = 'var(--tsvp-menu-item-hover-bg, rgba(255, 255, 255, 0.1))'\n }\n })\n item.addEventListener('mouseleave', () => {\n item.style.background = selected\n ? 'var(--tsvp-menu-item-active-bg, rgba(0, 168, 255, 0.2))'\n : 'transparent'\n })\n item.addEventListener('click', onClick)\n\n this.container.appendChild(item)\n }\n\n /**\n * Add back button\n */\n private addBackButton(title: string): void {\n const header = document.createElement('button')\n header.type = 'button'\n header.className = 'tsvp-settings-back'\n\n header.style.cssText = `\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 10px 16px;\n border: none;\n border-bottom: 1px solid var(--tsvp-color-border, rgba(255, 255, 255, 0.1));\n background: transparent;\n color: inherit;\n font-size: inherit;\n font-weight: var(--tsvp-font-weight-bold, 600);\n font-family: inherit;\n text-align: left;\n cursor: pointer;\n `\n\n const arrow = document.createElement('span')\n arrow.innerHTML = `\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\">\n <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"/>\n </svg>\n `\n\n const titleSpan = document.createElement('span')\n titleSpan.textContent = title\n\n header.appendChild(arrow)\n header.appendChild(titleSpan)\n\n header.addEventListener('click', () => this.goBack())\n\n this.container.appendChild(header)\n }\n\n /**\n * Go back to main menu\n */\n private goBack(): void {\n this.currentView = 'main'\n this.render()\n }\n\n /**\n * Show menu\n */\n show(): void {\n this.visible = true\n this.currentView = 'main'\n this.render()\n this.container.style.display = 'block'\n\n // Fade in\n this.container.style.opacity = '0'\n this.container.style.transform = 'translateY(8px)'\n requestAnimationFrame(() => {\n this.container.style.transition = 'opacity 150ms ease, transform 150ms ease'\n this.container.style.opacity = '1'\n this.container.style.transform = 'translateY(0)'\n })\n }\n\n /**\n * Hide menu\n */\n hide(): void {\n this.visible = false\n this.container.style.opacity = '0'\n this.container.style.transform = 'translateY(8px)'\n\n setTimeout(() => {\n if (!this.visible) {\n this.container.style.display = 'none'\n }\n }, 150)\n }\n\n /**\n * Toggle menu\n */\n toggle(): void {\n if (this.visible) {\n this.hide()\n }\n else {\n this.show()\n }\n }\n\n /**\n * Check if visible\n */\n isVisible(): boolean {\n return this.visible\n }\n\n /**\n * Set event handlers\n */\n onQuality(handler: (quality: VideoQuality | 'auto') => void): void {\n this.onQualityChange = handler\n }\n\n onSpeed(handler: (speed: number) => void): void {\n this.onSpeedChange = handler\n }\n\n onTextTrack(handler: (track: TextTrack | null) => void): void {\n this.onTextTrackChange = handler\n }\n\n onAudioTrack(handler: (track: AudioTrack) => void): void {\n this.onAudioTrackChange = handler\n }\n\n onBoost(handler: (boost: number) => void): void {\n this.onBoostChange = handler\n }\n\n /**\n * Get element\n */\n getElement(): HTMLElement {\n return this.container\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n this.container.remove()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createSettingsMenu(config?: SettingsMenuConfig): SettingsMenu {\n return new SettingsMenu(config)\n}\n",
|
|
18
|
+
"/**\n * Player Layouts\n *\n * Pre-built player layouts for quick setup.\n *\n * @module layouts\n */\n\nimport type { PlayerOptions } from '../types'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface LayoutConfig {\n /** Layout name */\n name: string\n /** Layout description */\n description: string\n /** CSS styles to inject */\n styles: string\n /** HTML structure template */\n template: string\n /** Default player options for this layout */\n defaultOptions?: Partial<PlayerOptions>\n /** Initialize layout-specific behavior */\n init?: (container: HTMLElement) => void\n /** Cleanup layout */\n destroy?: (container: HTMLElement) => void\n}\n\nexport type LayoutName = 'default' | 'minimal' | 'cinema' | 'youtube' | 'vimeo' | 'audio'\n\n// =============================================================================\n// Default Layout\n// =============================================================================\n\nexport const defaultLayout: LayoutConfig = {\n name: 'default',\n description: 'Full-featured default layout with all controls',\n styles: `\n .tsvp-layout-default {\n --tsvp-controls-height: 48px;\n }\n\n .tsvp-layout-default .tsvp-controls {\n display: flex;\n flex-direction: column;\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: var(--tsvp-controls-bg);\n padding: var(--tsvp-controls-padding);\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal) var(--tsvp-transition-easing);\n }\n\n .tsvp-layout-default:hover .tsvp-controls,\n .tsvp-layout-default.tsvp-controls-visible .tsvp-controls {\n opacity: 1;\n }\n\n .tsvp-layout-default .tsvp-controls-row {\n display: flex;\n align-items: center;\n gap: var(--tsvp-spacing-sm);\n }\n\n .tsvp-layout-default .tsvp-controls-row-progress {\n padding: 0 var(--tsvp-spacing-sm);\n }\n\n .tsvp-layout-default .tsvp-controls-row-buttons {\n height: var(--tsvp-controls-height);\n }\n\n .tsvp-layout-default .tsvp-controls-left,\n .tsvp-layout-default .tsvp-controls-right {\n display: flex;\n align-items: center;\n gap: var(--tsvp-spacing-xs);\n }\n\n .tsvp-layout-default .tsvp-controls-center {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .tsvp-layout-default .tsvp-controls-spacer {\n flex: 1;\n }\n `,\n template: `\n <div class=\"tsvp-video-container\">\n <slot name=\"video\"></slot>\n </div>\n <div class=\"tsvp-poster\"></div>\n <div class=\"tsvp-captions\"></div>\n <div class=\"tsvp-gestures\"></div>\n <div class=\"tsvp-buffering-indicator\"></div>\n <div class=\"tsvp-controls\">\n <div class=\"tsvp-controls-row tsvp-controls-row-progress\">\n <slot name=\"progress\"></slot>\n </div>\n <div class=\"tsvp-controls-row tsvp-controls-row-buttons\">\n <div class=\"tsvp-controls-left\">\n <slot name=\"play-button\"></slot>\n <slot name=\"volume\"></slot>\n <slot name=\"time\"></slot>\n <slot name=\"live-indicator\"></slot>\n </div>\n <div class=\"tsvp-controls-spacer\"></div>\n <div class=\"tsvp-controls-right\">\n <slot name=\"captions-button\"></slot>\n <slot name=\"settings-button\"></slot>\n <slot name=\"pip-button\"></slot>\n <slot name=\"airplay-button\"></slot>\n <slot name=\"cast-button\"></slot>\n <slot name=\"fullscreen-button\"></slot>\n </div>\n </div>\n </div>\n `,\n}\n\n// =============================================================================\n// Minimal Layout\n// =============================================================================\n\nexport const minimalLayout: LayoutConfig = {\n name: 'minimal',\n description: 'Clean, minimal layout with essential controls only',\n styles: `\n .tsvp-layout-minimal {\n --tsvp-controls-height: 36px;\n --tsvp-slider-track-height: 2px;\n --tsvp-slider-thumb-size: 10px;\n }\n\n .tsvp-layout-minimal .tsvp-controls {\n display: flex;\n align-items: center;\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n height: var(--tsvp-controls-height);\n background: linear-gradient(transparent, rgba(0,0,0,0.5));\n padding: 0 var(--tsvp-spacing-sm);\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal);\n }\n\n .tsvp-layout-minimal:hover .tsvp-controls {\n opacity: 1;\n }\n\n .tsvp-layout-minimal .tsvp-progress {\n flex: 1;\n margin: 0 var(--tsvp-spacing-sm);\n }\n `,\n template: `\n <div class=\"tsvp-video-container\">\n <slot name=\"video\"></slot>\n </div>\n <div class=\"tsvp-controls\">\n <slot name=\"play-button\"></slot>\n <slot name=\"progress\"></slot>\n <slot name=\"time\"></slot>\n <slot name=\"fullscreen-button\"></slot>\n </div>\n `,\n}\n\n// =============================================================================\n// Cinema Layout\n// =============================================================================\n\nexport const cinemaLayout: LayoutConfig = {\n name: 'cinema',\n description: 'Immersive cinema-style layout with center play button',\n styles: `\n .tsvp-layout-cinema {\n --tsvp-controls-height: 60px;\n }\n\n .tsvp-layout-cinema .tsvp-center-play {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: rgba(0, 0, 0, 0.6);\n display: flex;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal), transform var(--tsvp-transition-normal);\n }\n\n .tsvp-layout-cinema:hover .tsvp-center-play,\n .tsvp-layout-cinema.tsvp-paused .tsvp-center-play {\n opacity: 1;\n }\n\n .tsvp-layout-cinema .tsvp-center-play:hover {\n transform: translate(-50%, -50%) scale(1.1);\n background: rgba(0, 0, 0, 0.8);\n }\n\n .tsvp-layout-cinema .tsvp-center-play svg {\n width: 40px;\n height: 40px;\n fill: white;\n }\n\n .tsvp-layout-cinema .tsvp-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: linear-gradient(transparent, rgba(0,0,0,0.8));\n padding: var(--tsvp-spacing-lg);\n opacity: 0;\n transform: translateY(10px);\n transition: all var(--tsvp-transition-slow);\n }\n\n .tsvp-layout-cinema:hover .tsvp-controls {\n opacity: 1;\n transform: translateY(0);\n }\n\n .tsvp-layout-cinema .tsvp-title {\n color: white;\n font-size: var(--tsvp-font-size-lg);\n font-weight: var(--tsvp-font-weight-bold);\n margin-bottom: var(--tsvp-spacing-md);\n }\n `,\n template: `\n <div class=\"tsvp-video-container\">\n <slot name=\"video\"></slot>\n </div>\n <div class=\"tsvp-poster\"></div>\n <div class=\"tsvp-captions\"></div>\n <div class=\"tsvp-center-play\">\n <svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\"/></svg>\n </div>\n <div class=\"tsvp-controls\">\n <div class=\"tsvp-title\"></div>\n <div class=\"tsvp-controls-row\">\n <slot name=\"progress\"></slot>\n </div>\n <div class=\"tsvp-controls-row\">\n <slot name=\"play-button\"></slot>\n <slot name=\"volume\"></slot>\n <slot name=\"time\"></slot>\n <div style=\"flex:1\"></div>\n <slot name=\"captions-button\"></slot>\n <slot name=\"settings-button\"></slot>\n <slot name=\"fullscreen-button\"></slot>\n </div>\n </div>\n `,\n init: (container) => {\n const centerPlay = container.querySelector('.tsvp-center-play')\n if (centerPlay) {\n centerPlay.addEventListener('click', () => {\n container.dispatchEvent(new CustomEvent('tsvp:toggleplay'))\n })\n }\n },\n}\n\n// =============================================================================\n// YouTube-style Layout\n// =============================================================================\n\nexport const youtubeLayout: LayoutConfig = {\n name: 'youtube',\n description: 'YouTube-inspired layout with familiar controls',\n styles: `\n .tsvp-layout-youtube {\n --tsvp-color-primary: #ff0000;\n --tsvp-slider-track-height: 3px;\n --tsvp-slider-thumb-size: 13px;\n --tsvp-controls-height: 48px;\n }\n\n .tsvp-layout-youtube .tsvp-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: linear-gradient(transparent, rgba(0,0,0,0.7));\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal);\n }\n\n .tsvp-layout-youtube:hover .tsvp-controls,\n .tsvp-layout-youtube.tsvp-paused .tsvp-controls {\n opacity: 1;\n }\n\n .tsvp-layout-youtube .tsvp-progress-container {\n position: relative;\n padding: 0 12px;\n margin-bottom: -4px;\n }\n\n .tsvp-layout-youtube .tsvp-controls-row {\n display: flex;\n align-items: center;\n height: var(--tsvp-controls-height);\n padding: 0 4px;\n }\n\n .tsvp-layout-youtube .tsvp-chapter-markers {\n position: absolute;\n left: 12px;\n right: 12px;\n top: 0;\n height: 100%;\n pointer-events: none;\n }\n\n .tsvp-layout-youtube .tsvp-end-screen {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.85);\n display: none;\n align-items: center;\n justify-content: center;\n }\n\n .tsvp-layout-youtube.tsvp-ended .tsvp-end-screen {\n display: flex;\n }\n `,\n template: `\n <div class=\"tsvp-video-container\">\n <slot name=\"video\"></slot>\n </div>\n <div class=\"tsvp-poster\"></div>\n <div class=\"tsvp-captions\"></div>\n <div class=\"tsvp-controls\">\n <div class=\"tsvp-progress-container\">\n <slot name=\"progress\"></slot>\n <div class=\"tsvp-chapter-markers\"></div>\n <slot name=\"thumbnail-preview\"></slot>\n </div>\n <div class=\"tsvp-controls-row\">\n <slot name=\"play-button\"></slot>\n <slot name=\"next-button\"></slot>\n <slot name=\"volume\"></slot>\n <slot name=\"time\"></slot>\n <div style=\"flex:1\"></div>\n <slot name=\"autoplay-toggle\"></slot>\n <slot name=\"captions-button\"></slot>\n <slot name=\"settings-button\"></slot>\n <slot name=\"miniplayer-button\"></slot>\n <slot name=\"theater-button\"></slot>\n <slot name=\"fullscreen-button\"></slot>\n </div>\n </div>\n <div class=\"tsvp-end-screen\">\n <slot name=\"end-screen\"></slot>\n </div>\n `,\n}\n\n// =============================================================================\n// Vimeo-style Layout\n// =============================================================================\n\nexport const vimeoLayout: LayoutConfig = {\n name: 'vimeo',\n description: 'Vimeo-inspired clean layout',\n styles: `\n .tsvp-layout-vimeo {\n --tsvp-color-primary: #00adef;\n --tsvp-controls-height: 44px;\n --tsvp-slider-track-height: 4px;\n }\n\n .tsvp-layout-vimeo .tsvp-controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n display: flex;\n flex-direction: column;\n background: linear-gradient(transparent, rgba(0,0,0,0.6));\n padding: var(--tsvp-spacing-sm);\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal);\n }\n\n .tsvp-layout-vimeo:hover .tsvp-controls {\n opacity: 1;\n }\n\n .tsvp-layout-vimeo .tsvp-controls-row {\n display: flex;\n align-items: center;\n gap: var(--tsvp-spacing-sm);\n }\n\n .tsvp-layout-vimeo .tsvp-title-bar {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n padding: var(--tsvp-spacing-md);\n background: linear-gradient(rgba(0,0,0,0.6), transparent);\n color: white;\n opacity: 0;\n transition: opacity var(--tsvp-transition-normal);\n }\n\n .tsvp-layout-vimeo:hover .tsvp-title-bar {\n opacity: 1;\n }\n\n .tsvp-layout-vimeo .tsvp-like-button,\n .tsvp-layout-vimeo .tsvp-share-button {\n position: absolute;\n right: var(--tsvp-spacing-md);\n background: rgba(0,0,0,0.5);\n border-radius: var(--tsvp-radius-full);\n padding: var(--tsvp-spacing-sm);\n }\n\n .tsvp-layout-vimeo .tsvp-like-button { top: var(--tsvp-spacing-md); }\n .tsvp-layout-vimeo .tsvp-share-button { top: calc(var(--tsvp-spacing-md) + 48px); }\n `,\n template: `\n <div class=\"tsvp-video-container\">\n <slot name=\"video\"></slot>\n </div>\n <div class=\"tsvp-poster\"></div>\n <div class=\"tsvp-captions\"></div>\n <div class=\"tsvp-title-bar\">\n <slot name=\"title\"></slot>\n </div>\n <div class=\"tsvp-controls\">\n <div class=\"tsvp-controls-row\">\n <slot name=\"progress\"></slot>\n </div>\n <div class=\"tsvp-controls-row\">\n <slot name=\"play-button\"></slot>\n <slot name=\"volume\"></slot>\n <slot name=\"time\"></slot>\n <div style=\"flex:1\"></div>\n <slot name=\"captions-button\"></slot>\n <slot name=\"settings-button\"></slot>\n <slot name=\"pip-button\"></slot>\n <slot name=\"fullscreen-button\"></slot>\n </div>\n </div>\n `,\n}\n\n// =============================================================================\n// Audio Layout\n// =============================================================================\n\nexport const audioLayout: LayoutConfig = {\n name: 'audio',\n description: 'Compact layout for audio-only playback',\n styles: `\n .tsvp-layout-audio {\n --tsvp-controls-height: 64px;\n height: var(--tsvp-controls-height);\n background: var(--tsvp-color-background);\n border-radius: var(--tsvp-radius-lg);\n }\n\n .tsvp-layout-audio .tsvp-controls {\n display: flex;\n align-items: center;\n height: 100%;\n padding: 0 var(--tsvp-spacing-md);\n gap: var(--tsvp-spacing-md);\n }\n\n .tsvp-layout-audio .tsvp-artwork {\n width: 48px;\n height: 48px;\n border-radius: var(--tsvp-radius-sm);\n background: rgba(255,255,255,0.1);\n overflow: hidden;\n }\n\n .tsvp-layout-audio .tsvp-artwork img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .tsvp-layout-audio .tsvp-info {\n flex: 0 0 auto;\n max-width: 200px;\n }\n\n .tsvp-layout-audio .tsvp-track-title {\n color: var(--tsvp-color-text);\n font-weight: var(--tsvp-font-weight-bold);\n font-size: var(--tsvp-font-size);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .tsvp-layout-audio .tsvp-track-artist {\n color: var(--tsvp-color-text);\n opacity: 0.7;\n font-size: var(--tsvp-font-size-sm);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .tsvp-layout-audio .tsvp-progress-section {\n flex: 1;\n display: flex;\n align-items: center;\n gap: var(--tsvp-spacing-sm);\n }\n\n .tsvp-layout-audio .tsvp-progress-bar {\n flex: 1;\n }\n `,\n template: `\n <div class=\"tsvp-controls\">\n <div class=\"tsvp-artwork\">\n <slot name=\"artwork\"></slot>\n </div>\n <slot name=\"play-button\"></slot>\n <div class=\"tsvp-info\">\n <div class=\"tsvp-track-title\"><slot name=\"title\"></slot></div>\n <div class=\"tsvp-track-artist\"><slot name=\"artist\"></slot></div>\n </div>\n <div class=\"tsvp-progress-section\">\n <slot name=\"current-time\"></slot>\n <div class=\"tsvp-progress-bar\">\n <slot name=\"progress\"></slot>\n </div>\n <slot name=\"duration\"></slot>\n </div>\n <slot name=\"volume\"></slot>\n </div>\n `,\n}\n\n// =============================================================================\n// Layout Registry\n// =============================================================================\n\nexport const layouts: Record<LayoutName, LayoutConfig> = {\n default: defaultLayout,\n minimal: minimalLayout,\n cinema: cinemaLayout,\n youtube: youtubeLayout,\n vimeo: vimeoLayout,\n audio: audioLayout,\n}\n\n// =============================================================================\n// Layout Manager\n// =============================================================================\n\nexport class LayoutManager {\n private container: HTMLElement\n private currentLayout: LayoutConfig | null = null\n private styleElement: HTMLStyleElement | null = null\n\n constructor(container: HTMLElement) {\n this.container = container\n }\n\n /**\n * Apply a layout to the container\n */\n apply(layout: LayoutName | LayoutConfig): void {\n // Get layout config\n const config = typeof layout === 'string' ? layouts[layout] : layout\n\n // Cleanup previous layout\n this.cleanup()\n\n // Apply new layout class\n this.container.classList.add(`tsvp-layout-${config.name}`)\n\n // Inject styles\n this.styleElement = document.createElement('style')\n this.styleElement.textContent = config.styles\n document.head.appendChild(this.styleElement)\n\n // Set template\n this.container.innerHTML = config.template\n\n // Initialize layout\n if (config.init) {\n config.init(this.container)\n }\n\n this.currentLayout = config\n }\n\n /**\n * Get current layout\n */\n getLayout(): LayoutConfig | null {\n return this.currentLayout\n }\n\n /**\n * Cleanup current layout\n */\n cleanup(): void {\n if (this.currentLayout) {\n // Remove layout class\n this.container.classList.remove(`tsvp-layout-${this.currentLayout.name}`)\n\n // Call destroy\n if (this.currentLayout.destroy) {\n this.currentLayout.destroy(this.container)\n }\n\n // Remove styles\n if (this.styleElement) {\n this.styleElement.remove()\n this.styleElement = null\n }\n\n this.currentLayout = null\n }\n }\n\n /**\n * Destroy layout manager\n */\n destroy(): void {\n this.cleanup()\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createLayoutManager(container: HTMLElement): LayoutManager {\n return new LayoutManager(container)\n}\n\nexport function getLayout(name: LayoutName): LayoutConfig {\n return layouts[name]\n}\n\nexport function registerLayout(config: LayoutConfig): void {\n (layouts as any)[config.name] = config\n}\n",
|
|
19
|
+
"/**\n * ts-video-player UI Components\n *\n * UI components for building video player interfaces.\n *\n * @module ui/components\n */\n\nimport type { Player, PlayerState, UIComponent, SliderOptions, MenuItem } from '../types'\n\n// =============================================================================\n// Base Component\n// =============================================================================\n\n/**\n * Base class for UI components\n */\nexport abstract class BaseComponent implements UIComponent {\n abstract name: string\n protected player: Player | null = null\n protected element: HTMLElement | null = null\n protected unsubscribes: Array<() => void> = []\n\n render(player: Player): HTMLElement {\n this.player = player\n this.element = this.createElement()\n this.attachEvents()\n this.subscribeToState()\n return this.element\n }\n\n protected abstract createElement(): HTMLElement\n protected abstract attachEvents(): void\n\n protected subscribeToState(): void {\n // Override in subclass\n }\n\n update(_state: PlayerState): void {\n // Override in subclass\n }\n\n destroy(): void {\n this.unsubscribes.forEach((fn) => fn())\n this.unsubscribes = []\n this.element?.remove()\n this.element = null\n this.player = null\n }\n\n protected el<T extends HTMLElement>(\n tag: string,\n attrs: Record<string, string> = {},\n children?: (HTMLElement | string)[],\n ): T {\n const element = document.createElement(tag) as T\n for (const [key, value] of Object.entries(attrs)) {\n if (key === 'class') {\n element.className = value\n } else {\n element.setAttribute(key, value)\n }\n }\n if (children) {\n for (const child of children) {\n if (typeof child === 'string') {\n element.appendChild(document.createTextNode(child))\n } else {\n element.appendChild(child)\n }\n }\n }\n return element\n }\n}\n\n// =============================================================================\n// Play Button\n// =============================================================================\n\nexport class PlayButton extends BaseComponent {\n name = 'play-button'\n\n protected createElement(): HTMLElement {\n return this.el('button', {\n class: 'ts-video-btn ts-video-btn--play',\n 'aria-label': 'Play',\n type: 'button',\n }, [this.getIcon()])\n }\n\n protected attachEvents(): void {\n this.element?.addEventListener('click', () => {\n this.player?.togglePlay()\n })\n }\n\n protected subscribeToState(): void {\n if (!this.player) return\n const unsub = (this.player as any)._store?.subscribe('paused', (state: PlayerState) => {\n this.update(state)\n })\n if (unsub) this.unsubscribes.push(unsub)\n }\n\n update(state: PlayerState): void {\n if (!this.element) return\n this.element.setAttribute('aria-label', state.paused ? 'Play' : 'Pause')\n this.element.innerHTML = ''\n this.element.appendChild(this.getIcon(state.paused))\n }\n\n private getIcon(paused = true): HTMLElement {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('width', '24')\n svg.setAttribute('height', '24')\n svg.setAttribute('fill', 'currentColor')\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n path.setAttribute('d', paused ? 'M8 5v14l11-7z' : 'M6 19h4V5H6v14zm8-14v14h4V5h-4z')\n svg.appendChild(path)\n\n return svg as unknown as HTMLElement\n }\n}\n\n// =============================================================================\n// Mute Button\n// =============================================================================\n\nexport class MuteButton extends BaseComponent {\n name = 'mute-button'\n\n protected createElement(): HTMLElement {\n return this.el('button', {\n class: 'ts-video-btn ts-video-btn--mute',\n 'aria-label': 'Mute',\n type: 'button',\n }, [this.getIcon(false, 1)])\n }\n\n protected attachEvents(): void {\n this.element?.addEventListener('click', () => {\n this.player?.toggleMute()\n })\n }\n\n update(state: PlayerState): void {\n if (!this.element) return\n this.element.setAttribute('aria-label', state.muted ? 'Unmute' : 'Mute')\n this.element.innerHTML = ''\n this.element.appendChild(this.getIcon(state.muted, state.volume))\n }\n\n private getIcon(muted: boolean, volume: number): HTMLElement {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('width', '24')\n svg.setAttribute('height', '24')\n svg.setAttribute('fill', 'currentColor')\n\n let d: string\n if (muted || volume === 0) {\n d = 'M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.796 8.796 0 0 0 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3 3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06a8.99 8.99 0 0 0 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4 9.91 6.09 12 8.18V4z'\n } else if (volume < 0.5) {\n d = 'M18.5 12A4.5 4.5 0 0 0 16 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z'\n } else {\n d = 'M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z'\n }\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n path.setAttribute('d', d)\n svg.appendChild(path)\n\n return svg as unknown as HTMLElement\n }\n}\n\n// =============================================================================\n// Fullscreen Button\n// =============================================================================\n\nexport class FullscreenButton extends BaseComponent {\n name = 'fullscreen-button'\n\n protected createElement(): HTMLElement {\n return this.el('button', {\n class: 'ts-video-btn ts-video-btn--fullscreen',\n 'aria-label': 'Enter fullscreen',\n type: 'button',\n }, [this.getIcon(false)])\n }\n\n protected attachEvents(): void {\n this.element?.addEventListener('click', () => {\n this.player?.toggleFullscreen()\n })\n }\n\n update(state: PlayerState): void {\n if (!this.element) return\n const btn = this.element as HTMLButtonElement\n const disabled = state.fullscreenAvailability !== 'available'\n btn.disabled = disabled\n btn.style.display = state.fullscreenAvailability === 'unsupported' ? 'none' : ''\n btn.setAttribute('aria-label', state.fullscreen ? 'Exit fullscreen' : 'Enter fullscreen')\n btn.innerHTML = ''\n btn.appendChild(this.getIcon(state.fullscreen))\n }\n\n private getIcon(fullscreen: boolean): HTMLElement {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('width', '24')\n svg.setAttribute('height', '24')\n svg.setAttribute('fill', 'currentColor')\n\n const d = fullscreen\n ? 'M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z'\n : 'M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z'\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n path.setAttribute('d', d)\n svg.appendChild(path)\n\n return svg as unknown as HTMLElement\n }\n}\n\n// =============================================================================\n// PiP Button\n// =============================================================================\n\nexport class PiPButton extends BaseComponent {\n name = 'pip-button'\n\n protected createElement(): HTMLElement {\n return this.el('button', {\n class: 'ts-video-btn ts-video-btn--pip',\n 'aria-label': 'Picture in picture',\n type: 'button',\n }, [this.getIcon()])\n }\n\n protected attachEvents(): void {\n this.element?.addEventListener('click', () => {\n this.player?.togglePiP()\n })\n }\n\n update(state: PlayerState): void {\n if (!this.element) return\n const btn = this.element as HTMLButtonElement\n const disabled = state.pipAvailability !== 'available'\n btn.disabled = disabled\n btn.style.display = state.pipAvailability === 'unsupported' ? 'none' : ''\n }\n\n private getIcon(): HTMLElement {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('width', '24')\n svg.setAttribute('height', '24')\n svg.setAttribute('fill', 'currentColor')\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n path.setAttribute('d', 'M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3V5h18v14z')\n svg.appendChild(path)\n\n return svg as unknown as HTMLElement\n }\n}\n\n// =============================================================================\n// Time Display\n// =============================================================================\n\nexport class TimeDisplay extends BaseComponent {\n name = 'time-display'\n private showRemaining = false\n\n protected createElement(): HTMLElement {\n const container = this.el('div', { class: 'ts-video-time' })\n\n const current = this.el('span', { class: 'ts-video-time__current' }, ['0:00'])\n const separator = this.el('span', { class: 'ts-video-time__separator' }, [' / '])\n const duration = this.el('span', { class: 'ts-video-time__duration' }, ['0:00'])\n\n container.appendChild(current)\n container.appendChild(separator)\n container.appendChild(duration)\n\n return container\n }\n\n protected attachEvents(): void {\n this.element?.addEventListener('click', () => {\n this.showRemaining = !this.showRemaining\n if (this.player) {\n this.update(this.player.state)\n }\n })\n }\n\n update(state: PlayerState): void {\n if (!this.element) return\n\n const current = this.element.querySelector('.ts-video-time__current')\n const duration = this.element.querySelector('.ts-video-time__duration')\n\n if (current) {\n current.textContent = formatTime(state.currentTime)\n }\n\n if (duration) {\n if (this.showRemaining) {\n duration.textContent = `-${formatTime(state.duration - state.currentTime)}`\n } else {\n duration.textContent = formatTime(state.duration)\n }\n }\n }\n}\n\n// =============================================================================\n// Slider Component\n// =============================================================================\n\nexport class Slider extends BaseComponent {\n name = 'slider'\n private options: SliderOptions\n private track: HTMLElement | null = null\n private fill: HTMLElement | null = null\n private thumb: HTMLElement | null = null\n private isDragging = false\n private boundPointerMove: (e: PointerEvent) => void\n private boundPointerUp: (e: PointerEvent) => void\n\n constructor(options: SliderOptions) {\n super()\n this.options = {\n orientation: 'horizontal',\n disabled: false,\n ...options,\n }\n this.boundPointerMove = this.onPointerMove.bind(this)\n this.boundPointerUp = this.onPointerUp.bind(this)\n }\n\n protected createElement(): HTMLElement {\n const container = this.el('div', {\n class: `ts-video-slider ts-video-slider--${this.options.orientation}`,\n role: 'slider',\n 'aria-valuemin': String(this.options.min),\n 'aria-valuemax': String(this.options.max),\n 'aria-valuenow': String(this.options.value),\n tabindex: '0',\n })\n\n this.track = this.el('div', { class: 'ts-video-slider__track' })\n this.fill = this.el('div', { class: 'ts-video-slider__fill' })\n this.thumb = this.el('div', { class: 'ts-video-slider__thumb' })\n\n this.track.appendChild(this.fill)\n this.track.appendChild(this.thumb)\n container.appendChild(this.track)\n\n this.updateVisual(this.options.value)\n\n return container\n }\n\n protected attachEvents(): void {\n if (!this.element) return\n\n // Pointer events\n this.element.addEventListener('pointerdown', this.onPointerDown.bind(this))\n document.addEventListener('pointermove', this.boundPointerMove)\n document.addEventListener('pointerup', this.boundPointerUp)\n\n // Keyboard events\n this.element.addEventListener('keydown', this.onKeyDown.bind(this))\n }\n\n private onPointerDown(e: PointerEvent): void {\n if (this.options.disabled) return\n this.isDragging = true\n this.element?.setPointerCapture(e.pointerId)\n this.updateFromPointer(e)\n }\n\n private onPointerMove(e: PointerEvent): void {\n if (!this.isDragging) return\n this.updateFromPointer(e)\n }\n\n private onPointerUp(e: PointerEvent): void {\n if (!this.isDragging) return\n this.isDragging = false\n this.element?.releasePointerCapture(e.pointerId)\n this.options.onChange?.(this.options.value)\n }\n\n private onKeyDown(e: KeyboardEvent): void {\n if (this.options.disabled) return\n\n let newValue = this.options.value\n const step = this.options.step\n\n switch (e.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n newValue = Math.min(this.options.max, newValue + step)\n break\n case 'ArrowLeft':\n case 'ArrowDown':\n newValue = Math.max(this.options.min, newValue - step)\n break\n case 'Home':\n newValue = this.options.min\n break\n case 'End':\n newValue = this.options.max\n break\n default:\n return\n }\n\n e.preventDefault()\n this.setValue(newValue)\n this.options.onChange?.(newValue)\n }\n\n private updateFromPointer(e: PointerEvent): void {\n if (!this.track) return\n\n const rect = this.track.getBoundingClientRect()\n const isHorizontal = this.options.orientation === 'horizontal'\n\n let percent: number\n if (isHorizontal) {\n percent = (e.clientX - rect.left) / rect.width\n } else {\n percent = 1 - (e.clientY - rect.top) / rect.height\n }\n\n percent = Math.max(0, Math.min(1, percent))\n const value = this.options.min + percent * (this.options.max - this.options.min)\n\n // Snap to step\n const snapped = Math.round(value / this.options.step) * this.options.step\n\n this.setValue(snapped)\n this.options.onInput?.(snapped)\n }\n\n setValue(value: number): void {\n this.options.value = Math.max(this.options.min, Math.min(this.options.max, value))\n this.updateVisual(this.options.value)\n this.element?.setAttribute('aria-valuenow', String(this.options.value))\n }\n\n private updateVisual(value: number): void {\n const percent = ((value - this.options.min) / (this.options.max - this.options.min)) * 100\n\n if (this.fill) {\n if (this.options.orientation === 'horizontal') {\n this.fill.style.width = `${percent}%`\n } else {\n this.fill.style.height = `${percent}%`\n }\n }\n\n if (this.thumb) {\n if (this.options.orientation === 'horizontal') {\n this.thumb.style.left = `${percent}%`\n } else {\n this.thumb.style.bottom = `${percent}%`\n }\n }\n }\n\n destroy(): void {\n document.removeEventListener('pointermove', this.boundPointerMove)\n document.removeEventListener('pointerup', this.boundPointerUp)\n super.destroy()\n }\n}\n\n// =============================================================================\n// Progress Bar (Time Slider)\n// =============================================================================\n\nexport class ProgressBar extends BaseComponent {\n name = 'progress-bar'\n private slider: Slider | null = null\n private buffered: HTMLElement | null = null\n private isSeeking = false\n\n protected createElement(): HTMLElement {\n const container = this.el('div', { class: 'ts-video-progress' })\n\n // Buffered indicator\n this.buffered = this.el('div', { class: 'ts-video-progress__buffered' })\n\n // Slider\n this.slider = new Slider({\n min: 0,\n max: 100,\n step: 0.1,\n value: 0,\n onInput: (value) => {\n this.isSeeking = true\n const time = (value / 100) * (this.player?.state.duration || 0)\n this.player?.seekTo(time)\n },\n onChange: () => {\n this.isSeeking = false\n },\n })\n\n const sliderEl = this.slider.render(this.player!)\n sliderEl.classList.add('ts-video-progress__slider')\n\n // Insert buffered before slider track\n const track = sliderEl.querySelector('.ts-video-slider__track')\n if (track) {\n track.insertBefore(this.buffered, track.firstChild)\n }\n\n container.appendChild(sliderEl)\n\n return container\n }\n\n protected attachEvents(): void {\n // Events handled by slider\n }\n\n update(state: PlayerState): void {\n if (!this.slider || this.isSeeking) return\n\n // Update progress\n const percent = state.duration > 0 ? (state.currentTime / state.duration) * 100 : 0\n this.slider.setValue(percent)\n\n // Update buffered\n if (this.buffered) {\n const bufferedPercent = state.bufferedAmount * 100\n this.buffered.style.width = `${bufferedPercent}%`\n }\n }\n\n destroy(): void {\n this.slider?.destroy()\n super.destroy()\n }\n}\n\n// =============================================================================\n// Volume Slider\n// =============================================================================\n\nexport class VolumeSlider extends BaseComponent {\n name = 'volume-slider'\n private slider: Slider | null = null\n\n protected createElement(): HTMLElement {\n const container = this.el('div', { class: 'ts-video-volume' })\n\n this.slider = new Slider({\n min: 0,\n max: 1,\n step: 0.01,\n value: this.player?.state.volume || 1,\n onInput: (value) => {\n this.player?.setVolume(value)\n },\n })\n\n container.appendChild(this.slider.render(this.player!))\n\n return container\n }\n\n protected attachEvents(): void {\n // Events handled by slider\n }\n\n update(state: PlayerState): void {\n if (!this.slider) return\n if (this.element) {\n this.element.style.display = state.volumeAvailability === 'unsupported' ? 'none' : ''\n }\n this.slider.setValue(state.muted ? 0 : state.volume)\n }\n\n destroy(): void {\n this.slider?.destroy()\n super.destroy()\n }\n}\n\n// =============================================================================\n// Menu\n// =============================================================================\n\nexport class Menu extends BaseComponent {\n name = 'menu'\n private items: MenuItem[]\n private isOpen = false\n private menuEl: HTMLElement | null = null\n private onSelect?: (item: MenuItem) => void\n\n constructor(items: MenuItem[], onSelect?: (item: MenuItem) => void) {\n super()\n this.items = items\n this.onSelect = onSelect\n }\n\n protected createElement(): HTMLElement {\n const container = this.el('div', { class: 'ts-video-menu' })\n\n // Trigger button\n const trigger = this.el('button', {\n class: 'ts-video-btn ts-video-menu__trigger',\n 'aria-label': 'Settings',\n 'aria-haspopup': 'true',\n 'aria-expanded': 'false',\n type: 'button',\n }, [this.getIcon()])\n\n // Menu content\n this.menuEl = this.el('div', {\n class: 'ts-video-menu__content',\n role: 'menu',\n 'aria-hidden': 'true',\n })\n\n this.renderItems()\n\n container.appendChild(trigger)\n container.appendChild(this.menuEl)\n\n return container\n }\n\n private renderItems(): void {\n if (!this.menuEl) return\n this.menuEl.innerHTML = ''\n\n for (const item of this.items) {\n const itemEl = this.el('button', {\n class: `ts-video-menu__item${item.checked ? ' ts-video-menu__item--checked' : ''}${item.disabled ? ' ts-video-menu__item--disabled' : ''}`,\n role: 'menuitem',\n 'data-id': item.id,\n type: 'button',\n }, [item.label])\n\n itemEl.addEventListener('click', () => {\n if (!item.disabled) {\n this.onSelect?.(item)\n this.close()\n }\n })\n\n this.menuEl.appendChild(itemEl)\n }\n }\n\n protected attachEvents(): void {\n const trigger = this.element?.querySelector('.ts-video-menu__trigger')\n trigger?.addEventListener('click', () => this.toggle())\n\n // Close on outside click\n const onDocClick = (e: Event) => {\n if (this.isOpen && !this.element?.contains(e.target as Node)) {\n this.close()\n }\n }\n document.addEventListener('click', onDocClick)\n this.unsubscribes.push(() => document.removeEventListener('click', onDocClick))\n\n // Close on escape\n const onDocKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.isOpen) {\n this.close()\n }\n }\n document.addEventListener('keydown', onDocKeydown)\n this.unsubscribes.push(() => document.removeEventListener('keydown', onDocKeydown))\n }\n\n toggle(): void {\n if (this.isOpen) {\n this.close()\n } else {\n this.open()\n }\n }\n\n open(): void {\n this.isOpen = true\n this.menuEl?.setAttribute('aria-hidden', 'false')\n this.element?.querySelector('.ts-video-menu__trigger')?.setAttribute('aria-expanded', 'true')\n this.element?.classList.add('ts-video-menu--open')\n }\n\n close(): void {\n this.isOpen = false\n this.menuEl?.setAttribute('aria-hidden', 'true')\n this.element?.querySelector('.ts-video-menu__trigger')?.setAttribute('aria-expanded', 'false')\n this.element?.classList.remove('ts-video-menu--open')\n }\n\n setItems(items: MenuItem[]): void {\n this.items = items\n this.renderItems()\n }\n\n private getIcon(): HTMLElement {\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('width', '24')\n svg.setAttribute('height', '24')\n svg.setAttribute('fill', 'currentColor')\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n path.setAttribute('d', 'M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58a.49.49 0 0 0 .12-.61l-1.92-3.32a.488.488 0 0 0-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 0 0-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58a.49.49 0 0 0-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z')\n svg.appendChild(path)\n\n return svg as unknown as HTMLElement\n }\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Format time in seconds to MM:SS or HH:MM:SS\n */\nexport function formatTime(seconds: number): string {\n if (!isFinite(seconds) || isNaN(seconds)) return '0:00'\n\n const negative = seconds < 0\n const abs = Math.abs(seconds)\n\n const h = Math.floor(abs / 3600)\n const m = Math.floor((abs % 3600) / 60)\n const s = Math.floor(abs % 60)\n\n const prefix = negative ? '-' : ''\n\n if (h > 0) {\n return `${prefix}${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`\n }\n\n return `${prefix}${m}:${s.toString().padStart(2, '0')}`\n}\n\n// =============================================================================\n// CSS Styles\n// =============================================================================\n\nexport const UI_STYLES = `\n.ts-video-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n padding: 8px;\n background: transparent;\n border: none;\n color: white;\n cursor: pointer;\n transition: opacity 0.2s;\n}\n\n.ts-video-btn:hover {\n opacity: 0.8;\n}\n\n.ts-video-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.ts-video-time {\n font-size: 14px;\n color: white;\n font-variant-numeric: tabular-nums;\n user-select: none;\n cursor: pointer;\n}\n\n.ts-video-slider {\n position: relative;\n width: 100%;\n height: 20px;\n cursor: pointer;\n}\n\n.ts-video-slider__track {\n position: absolute;\n top: 50%;\n left: 0;\n right: 0;\n height: 4px;\n background: rgba(255, 255, 255, 0.3);\n border-radius: 2px;\n transform: translateY(-50%);\n}\n\n.ts-video-slider__fill {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: #fff;\n border-radius: 2px;\n}\n\n.ts-video-slider__thumb {\n position: absolute;\n top: 50%;\n width: 12px;\n height: 12px;\n background: #fff;\n border-radius: 50%;\n transform: translate(-50%, -50%);\n transition: transform 0.1s;\n}\n\n.ts-video-slider:hover .ts-video-slider__thumb {\n transform: translate(-50%, -50%) scale(1.2);\n}\n\n.ts-video-progress {\n flex: 1;\n margin: 0 12px;\n}\n\n.ts-video-progress__buffered {\n position: absolute;\n top: 0;\n left: 0;\n height: 100%;\n background: rgba(255, 255, 255, 0.4);\n border-radius: 2px;\n}\n\n.ts-video-volume {\n width: 80px;\n}\n\n.ts-video-menu {\n position: relative;\n}\n\n.ts-video-menu__content {\n position: absolute;\n bottom: 100%;\n right: 0;\n min-width: 150px;\n max-height: 200px;\n overflow-y: auto;\n background: rgba(28, 28, 28, 0.95);\n border-radius: 4px;\n padding: 4px 0;\n opacity: 0;\n visibility: hidden;\n transform: translateY(10px);\n transition: opacity 0.2s, transform 0.2s, visibility 0.2s;\n}\n\n.ts-video-menu--open .ts-video-menu__content {\n opacity: 1;\n visibility: visible;\n transform: translateY(0);\n}\n\n.ts-video-menu__item {\n display: block;\n width: 100%;\n padding: 8px 16px;\n background: none;\n border: none;\n color: white;\n text-align: left;\n cursor: pointer;\n font-size: 14px;\n}\n\n.ts-video-menu__item:hover {\n background: rgba(255, 255, 255, 0.1);\n}\n\n.ts-video-menu__item--checked::before {\n content: '✓';\n margin-right: 8px;\n}\n\n.ts-video-menu__item--disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n`\n",
|
|
20
|
+
"/**\n * ts-video-player stx Integration\n *\n * Deep integration with the stx templating framework.\n * Provides components, directives, and runtime for seamless video embedding.\n *\n * @module stx\n */\n\nimport type {\n VideoComponentProps,\n VideoRenderResult,\n PlayerOptions,\n Src,\n} from '../types'\nimport { extractYouTubeId } from '../providers/youtube'\n\n// =============================================================================\n// Video Component\n// =============================================================================\n\n/**\n * Render the <Video> component for stx\n *\n * @example\n * ```typescript\n * const result = await renderVideoComponent({\n * src: '/videos/intro.mp4',\n * poster: '/images/poster.jpg',\n * controls: true,\n * lazy: true,\n * })\n * ```\n */\nexport async function renderVideoComponent(\n props: VideoComponentProps,\n _context: { isDev?: boolean } = {},\n): Promise<VideoRenderResult> {\n const {\n src,\n poster,\n title,\n class: className,\n style,\n id = `ts-video-${randomId()}`,\n width,\n height,\n options = {},\n lazy = true,\n autoplay = false,\n loop = false,\n muted = false,\n controls = true,\n playsinline = true,\n preload = 'metadata',\n crossorigin,\n controlsList,\n disablePictureInPicture = false,\n theme = 'default',\n } = props\n\n // Detect provider type\n const providerType = detectProviderType(src)\n const isEmbed = providerType === 'youtube' || providerType === 'vimeo'\n\n // Build player options\n const playerOptions: PlayerOptions = {\n ...options,\n autoplay,\n loop,\n muted,\n playsinline,\n preload,\n poster,\n title,\n controls,\n crossorigin,\n controlsList,\n disablePictureInPicture,\n }\n\n // Build container attributes\n const attrs: string[] = [\n `id=\"${id}\"`,\n `class=\"ts-video-player ts-video-player--${theme}${className ? ' ' + escapeAttr(className) : ''}\"`,\n `data-ts-video-player`,\n `data-provider=\"${providerType}\"`,\n ]\n\n if (style) attrs.push(`style=\"${escapeAttr(style)}\"`)\n if (width) attrs.push(`data-width=\"${width}\"`)\n if (height) attrs.push(`data-height=\"${height}\"`)\n if (lazy) attrs.push(`data-lazy`)\n\n // Build data config\n const config = {\n src: normalizeSrc(src),\n options: playerOptions,\n }\n attrs.push(`data-config='${JSON.stringify(config)}'`)\n\n // Build HTML\n let html = ''\n\n if (isEmbed) {\n // Embed placeholder with poster\n html = buildEmbedPlaceholder(id, attrs, poster, title, providerType, src)\n } else {\n // Native video with poster\n html = buildNativeVideoPlaceholder(id, attrs, poster, title, lazy, preload, src, {\n crossorigin,\n controlsList,\n disablePictureInPicture,\n })\n }\n\n // Build initialization script\n const script = generateInitScript(id, lazy)\n\n // Build styles\n const css = generatePlayerCSS(theme)\n\n return { html, script, css }\n}\n\n/**\n * Parse <Video> component from template\n */\nexport function parseVideoComponent(content: string): VideoComponentProps | null {\n // Simple attribute parsing\n const srcMatch = content.match(/src=[\"']([^\"']+)[\"']/)\n const posterMatch = content.match(/poster=[\"']([^\"']+)[\"']/)\n const titleMatch = content.match(/title=[\"']([^\"']+)[\"']/)\n const classMatch = content.match(/class=[\"']([^\"']+)[\"']/)\n const idMatch = content.match(/id=[\"']([^\"']+)[\"']/)\n const widthMatch = content.match(/width=[\"']?(\\d+)[\"']?/)\n const heightMatch = content.match(/height=[\"']?(\\d+)[\"']?/)\n\n if (!srcMatch) return null\n\n return {\n src: srcMatch[1],\n poster: posterMatch?.[1],\n title: titleMatch?.[1],\n class: classMatch?.[1],\n id: idMatch?.[1],\n width: widthMatch ? parseInt(widthMatch[1], 10) : undefined,\n height: heightMatch ? parseInt(heightMatch[1], 10) : undefined,\n autoplay: content.includes('autoplay'),\n loop: content.includes('loop'),\n muted: content.includes('muted'),\n controls: !content.includes('controls=\"false\"') && !content.includes(':controls=\"false\"'),\n playsinline: !content.includes('playsinline=\"false\"'),\n lazy: !content.includes('lazy=\"false\"') && !content.includes(':lazy=\"false\"'),\n }\n}\n\n// =============================================================================\n// Video Directive\n// =============================================================================\n\n/**\n * Create the @video directive for stx\n *\n * Usage:\n * ```html\n * @video('/videos/intro.mp4', { poster: '/poster.jpg', controls: true })\n * @video(youtubeUrl, { lazy: true })\n * ```\n */\nexport function createVideoDirective(): { name: string, hasEndTag: boolean, description: string, transform: (content: string, params: Record<string, unknown>, context: Record<string, unknown>) => Promise<string> } {\n return {\n name: 'video',\n hasEndTag: false,\n description: 'Embed a video player with full playback controls',\n\n transform: async (\n content: string,\n params: Record<string, unknown>,\n context: Record<string, unknown>,\n ): Promise<string> => {\n const props = parseVideoDirectiveArgs(content, params, context)\n\n if (!props.src) {\n console.warn('@video directive: missing src argument')\n return '<!-- @video: missing src -->'\n }\n\n const isDev = context.__isDev as boolean || false\n const result = await renderVideoComponent(props, { isDev })\n\n let output = result.html\n if (result.css) {\n output += `\\n<style>${result.css}</style>`\n }\n if (result.script) {\n output += `\\n<script>${result.script}</script>`\n }\n\n return output\n },\n }\n}\n\n/**\n * Parse @video directive arguments\n */\nfunction parseVideoDirectiveArgs(\n content: string,\n params: Record<string, unknown>,\n context: Record<string, unknown>,\n): VideoComponentProps {\n // If params already structured\n if (params.src) {\n return {\n src: String(params.src),\n ...(params.options || params) as Partial<VideoComponentProps>,\n }\n }\n\n // Parse from content: @video('/path/to/video.mp4', { options })\n let args = content.trim()\n if (args.startsWith('@video(') || args.startsWith('(')) {\n args = args.replace(/^@video\\(/, '').replace(/^\\(/, '').replace(/\\)$/, '')\n }\n\n // Split into src and options\n const parts = splitArgs(args)\n const src = resolveValue(parts[0], context) as string || ''\n let options: Partial<VideoComponentProps> = {}\n\n if (parts[1]) {\n const resolved = resolveValue(parts[1], context)\n if (typeof resolved === 'object' && resolved !== null) {\n options = resolved as Partial<VideoComponentProps>\n }\n }\n\n return { src, ...options }\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction detectProviderType(src: Src | Src[]): string {\n const firstSrc = Array.isArray(src) ? src[0] : src\n const url = typeof firstSrc === 'string' ? firstSrc : (firstSrc as any).src || ''\n\n if (extractYouTubeId(url)) return 'youtube'\n if (url.includes('vimeo.com')) return 'vimeo'\n if (url.endsWith('.m3u8') || url.includes('application/x-mpegURL')) return 'hls'\n if (url.endsWith('.mpd') || url.includes('application/dash+xml')) return 'dash'\n\n return 'html5'\n}\n\nfunction normalizeSrc(src: Src | Src[]): Src | Src[] {\n return src\n}\n\nfunction buildEmbedPlaceholder(\n _id: string,\n attrs: string[],\n poster: string | undefined,\n title: string | undefined,\n _provider: string,\n _src: Src | Src[],\n): string {\n const posterStyle = poster ? `background-image: url('${escapeAttr(poster)}');` : ''\n\n return `\n<div ${attrs.join(' ')}>\n <div class=\"ts-video-player__container\">\n <div class=\"ts-video-player__placeholder\" style=\"${posterStyle}\" data-poster>\n <button class=\"ts-video-player__play-button\" aria-label=\"Play ${escapeAttr(title || 'video')}\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"64\" height=\"64\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n </button>\n </div>\n </div>\n</div>\n`.trim()\n}\n\nfunction buildNativeVideoPlaceholder(\n _id: string,\n attrs: string[],\n poster: string | undefined,\n title: string | undefined,\n lazy: boolean,\n preload: string,\n src: Src | Src[],\n options: Pick<VideoComponentProps, 'crossorigin' | 'controlsList' | 'disablePictureInPicture'>,\n): string {\n const firstSrc = Array.isArray(src) ? src[0] : src\n const url = typeof firstSrc === 'string' ? firstSrc : (firstSrc as any).src || ''\n\n const videoAttrs = [\n `class=\"ts-video-player__media\"`,\n `playsinline`,\n lazy ? 'preload=\"none\"' : `preload=\"${preload}\"`,\n ]\n\n if (lazy) {\n videoAttrs.push(`data-poster=\"${escapeAttr(poster || '')}\"`)\n videoAttrs.push(`data-src=\"${escapeAttr(url)}\"`)\n } else {\n if (poster) videoAttrs.push(`poster=\"${escapeAttr(poster)}\"`)\n videoAttrs.push(`src=\"${escapeAttr(url)}\"`)\n }\n\n if (title) videoAttrs.push(`title=\"${escapeAttr(title)}\"`)\n if (options.crossorigin !== undefined) videoAttrs.push(`crossorigin=\"${escapeAttr(options.crossorigin)}\"`)\n if (options.controlsList?.length) videoAttrs.push(`controlslist=\"${escapeAttr(options.controlsList.join(' '))}\"`)\n if (options.disablePictureInPicture) videoAttrs.push('disablepictureinpicture')\n\n return `\n<div ${attrs.join(' ')}>\n <div class=\"ts-video-player__container\">\n <video ${videoAttrs.join(' ')}>\n ${!lazy ? buildSourceTags(src) : ''}\n </video>\n </div>\n</div>\n`.trim()\n}\n\nfunction buildSourceTags(src: Src | Src[]): string {\n const sources = Array.isArray(src) ? src : [src]\n return sources\n .map((s) => {\n if (typeof s === 'string') {\n return `<source src=\"${escapeAttr(s)}\" type=\"${getMimeType(s)}\" />`\n }\n const mediaSource = s as { src: string; type?: string }\n return `<source src=\"${escapeAttr(mediaSource.src)}\" type=\"${mediaSource.type || getMimeType(mediaSource.src)}\" />`\n })\n .join('\\n')\n}\n\nfunction getMimeType(src: string): string {\n const path = src.split(/[?#]/, 1)[0]\n const ext = path.slice(path.lastIndexOf('.') + 1).toLowerCase()\n const types: Record<string, string> = {\n mp4: 'video/mp4',\n webm: 'video/webm',\n ogg: 'video/ogg',\n ogv: 'video/ogg',\n mov: 'video/quicktime',\n m3u8: 'application/x-mpegURL',\n mpd: 'application/dash+xml',\n mp3: 'audio/mpeg',\n wav: 'audio/wav',\n flac: 'audio/flac',\n m4a: 'audio/mp4',\n aac: 'audio/aac',\n oga: 'audio/ogg',\n opus: 'audio/ogg; codecs=opus',\n }\n return types[ext || ''] || 'video/mp4'\n}\n\nfunction generateInitScript(id: string, _lazy: boolean): string {\n return `\n(function() {\n const container = document.getElementById('${id}');\n if (!container) return;\n\n const config = JSON.parse(container.dataset.config || '{}');\n const isLazy = container.hasAttribute('data-lazy');\n\n function initPlayer() {\n if (typeof VideoPlayer === 'undefined') {\n // Load the player library\n const script = document.createElement('script');\n script.src = 'https://cdn.jsdelivr.net/npm/ts-video-player@latest/dist/index.min.js';\n script.onload = function() { createPlayer(); };\n document.head.appendChild(script);\n } else {\n createPlayer();\n }\n }\n\n function createPlayer() {\n const player = VideoPlayer.createPlayer(container, config.options || {});\n if (config.src) {\n player.setSrc(config.src);\n }\n container.__videoPlayer = player;\n }\n\n if (isLazy) {\n // Use IntersectionObserver for lazy loading\n const observer = new IntersectionObserver(function(entries) {\n entries.forEach(function(entry) {\n if (entry.isIntersecting) {\n initPlayer();\n observer.disconnect();\n }\n });\n }, { rootMargin: '50px' });\n observer.observe(container);\n } else {\n // Initialize immediately\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initPlayer);\n } else {\n initPlayer();\n }\n }\n\n // Handle play button click (for embed placeholders)\n const playButton = container.querySelector('.ts-video-player__play-button');\n if (playButton) {\n playButton.addEventListener('click', function() {\n initPlayer();\n // Wait for player to initialize then play\n const checkPlayer = setInterval(function() {\n if (container.__videoPlayer && container.__videoPlayer.ready) {\n container.__videoPlayer.play();\n clearInterval(checkPlayer);\n }\n }, 100);\n });\n }\n})();\n`.trim()\n}\n\nfunction generatePlayerCSS(_theme: string): string {\n return `\n.ts-video-player {\n position: relative;\n width: 100%;\n background: #000;\n}\n\n.ts-video-player__container {\n position: relative;\n width: 100%;\n padding-bottom: 56.25%; /* 16:9 */\n}\n\n.ts-video-player__media,\n.ts-video-player__placeholder {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: contain;\n}\n\n.ts-video-player__placeholder {\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: #1a1a1a;\n background-size: cover;\n background-position: center;\n cursor: pointer;\n}\n\n.ts-video-player__play-button {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 80px;\n height: 80px;\n background: rgba(0, 0, 0, 0.7);\n border: none;\n border-radius: 50%;\n color: white;\n cursor: pointer;\n transition: transform 0.2s, background 0.2s;\n}\n\n.ts-video-player__play-button:hover {\n transform: scale(1.1);\n background: rgba(0, 0, 0, 0.9);\n}\n\n.ts-video-player__play-button svg {\n margin-left: 4px;\n}\n\n/* Loading state */\n.ts-video-player[data-loading=\"true\"]::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 50%;\n width: 40px;\n height: 40px;\n margin: -20px 0 0 -20px;\n border: 3px solid rgba(255, 255, 255, 0.3);\n border-top-color: white;\n border-radius: 50%;\n animation: ts-video-spin 0.8s linear infinite;\n}\n\n@keyframes ts-video-spin {\n to { transform: rotate(360deg); }\n}\n\n/* Controls hidden */\n.ts-video-player[data-controls-visible=\"false\"] .ts-video-player__controls {\n opacity: 0;\n pointer-events: none;\n}\n`.trim()\n}\n\nfunction escapeAttr(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n}\n\nfunction randomId(): string {\n return Math.random().toString(36).slice(2, 8)\n}\n\nfunction splitArgs(str: string): string[] {\n const args: string[] = []\n let current = ''\n let depth = 0\n let inString = false\n let stringChar = ''\n\n for (const char of str) {\n if ((char === '\"' || char === '\\'') && !inString) {\n inString = true\n stringChar = char\n } else if (char === stringChar && inString) {\n inString = false\n stringChar = ''\n }\n\n if (!inString) {\n if (char === '{' || char === '[') depth++\n if (char === '}' || char === ']') depth--\n if (char === ',' && depth === 0) {\n args.push(current.trim())\n current = ''\n continue\n }\n }\n\n current += char\n }\n\n if (current.trim()) {\n args.push(current.trim())\n }\n\n return args\n}\n\nfunction resolveValue(value: string | undefined, context: Record<string, unknown>): unknown {\n if (!value) return undefined\n\n const trimmed = value.trim()\n\n // Remove quotes\n if ((trimmed.startsWith('\"') && trimmed.endsWith('\"')) || (trimmed.startsWith('\\'') && trimmed.endsWith('\\''))) {\n return trimmed.slice(1, -1)\n }\n\n // Try to parse as JSON\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n return JSON.parse(trimmed)\n } catch {\n // Not valid JSON\n }\n }\n\n // Check context\n if (trimmed in context) {\n return context[trimmed]\n }\n\n return trimmed\n}\n\n// =============================================================================\n// Exports\n// =============================================================================\n\nexport const videoDirective: { name: string, hasEndTag: boolean, description: string, transform: (content: string, params: Record<string, unknown>, context: Record<string, unknown>) => Promise<string> } = createVideoDirective()\n\n/**\n * Register ts-video-player directives with stx\n */\nexport function registerVideoDirectives(stx: { config: { customDirectives?: any[] } }): void {\n if (!stx.config.customDirectives) {\n stx.config.customDirectives = []\n }\n stx.config.customDirectives.push(videoDirective)\n}\n",
|
|
21
|
+
"/**\n * Video Analytics Plugin\n *\n * Deep integration with ts-analytics for comprehensive video tracking.\n *\n * @module plugins/analytics\n */\n\nimport type { Player } from '../player'\nimport type { VideoQuality, TextTrack, AudioTrack } from '../types'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface VideoAnalyticsConfig {\n /** Site ID for analytics */\n siteId: string\n /** Analytics API endpoint */\n apiEndpoint: string\n /** Video ID (unique identifier) */\n videoId: string\n /** Video title */\n videoTitle?: string\n /** Video duration (auto-detected if not provided) */\n duration?: number\n /** Content type (vod, live, etc.) */\n contentType?: 'vod' | 'live' | 'dvr'\n /** Content category */\n category?: string\n /** Content tags */\n tags?: string[]\n /** Creator/channel ID */\n creatorId?: string\n /** Series/playlist ID */\n seriesId?: string\n /** Episode number */\n episode?: number\n /** Season number */\n season?: number\n /** Track playback milestones (percentages) */\n milestones?: number[]\n /** Track heartbeat interval (seconds) */\n heartbeatInterval?: number\n /** Track quality changes */\n trackQualityChanges?: boolean\n /** Track seeking behavior */\n trackSeeking?: boolean\n /** Track buffering */\n trackBuffering?: boolean\n /** Track errors */\n trackErrors?: boolean\n /** Track engagement (pauses, replays) */\n trackEngagement?: boolean\n /** Custom properties to include */\n customProperties?: Record<string, string | number | boolean>\n /** Session ID (auto-generated if not provided) */\n sessionId?: string\n /** Visitor ID (auto-generated if not provided) */\n visitorId?: string\n /** Disable analytics */\n disabled?: boolean\n}\n\nexport interface VideoEvent {\n /** Event name */\n name: VideoEventName\n /** Timestamp */\n timestamp: Date\n /** Current playback time */\n currentTime: number\n /** Video duration */\n duration: number\n /** Additional properties */\n properties?: Record<string, unknown>\n}\n\nexport type VideoEventName =\n | 'video_start'\n | 'video_play'\n | 'video_pause'\n | 'video_resume'\n | 'video_seek'\n | 'video_buffer_start'\n | 'video_buffer_end'\n | 'video_quality_change'\n | 'video_volume_change'\n | 'video_mute'\n | 'video_unmute'\n | 'video_fullscreen_enter'\n | 'video_fullscreen_exit'\n | 'video_pip_enter'\n | 'video_pip_exit'\n | 'video_caption_enable'\n | 'video_caption_disable'\n | 'video_caption_change'\n | 'video_audio_track_change'\n | 'video_speed_change'\n | 'video_error'\n | 'video_milestone'\n | 'video_quartile'\n | 'video_complete'\n | 'video_heartbeat'\n | 'video_engagement'\n | 'video_ad_start'\n | 'video_ad_complete'\n | 'video_ad_skip'\n | 'video_ad_click'\n\nexport interface VideoAnalyticsState {\n /** Has video started playing */\n hasStarted: boolean\n /** Has video completed */\n hasCompleted: boolean\n /** Total watch time in seconds */\n watchTime: number\n /** Number of pauses */\n pauseCount: number\n /** Number of seeks */\n seekCount: number\n /** Number of buffer events */\n bufferCount: number\n /** Total buffer time in seconds */\n bufferTime: number\n /** Number of quality changes */\n qualityChangeCount: number\n /** Reached milestones */\n reachedMilestones: number[]\n /** Reached quartiles */\n reachedQuartiles: number[]\n /** Playback start time */\n playbackStartTime: number | null\n /** Last heartbeat time */\n lastHeartbeat: number\n /** Is currently buffering */\n isBuffering: boolean\n /** Buffer start time */\n bufferStartTime: number | null\n}\n\n// =============================================================================\n// Default Configuration\n// =============================================================================\n\nconst DEFAULT_MILESTONES = [10, 25, 50, 75, 90, 100]\nconst DEFAULT_HEARTBEAT_INTERVAL = 30 // seconds\n\n// =============================================================================\n// Video Analytics Class\n// =============================================================================\n\nexport class VideoAnalytics {\n private player: Player | null = null\n private config: VideoAnalyticsConfig\n private state: VideoAnalyticsState\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\n private eventQueue: VideoEvent[] = []\n private flushTimer: ReturnType<typeof setTimeout> | null = null\n private sessionId: string\n private visitorId: string\n private unsubscribers: Array<() => void> = []\n\n constructor(config: VideoAnalyticsConfig) {\n this.config = {\n milestones: DEFAULT_MILESTONES,\n heartbeatInterval: DEFAULT_HEARTBEAT_INTERVAL,\n trackQualityChanges: true,\n trackSeeking: true,\n trackBuffering: true,\n trackErrors: true,\n trackEngagement: true,\n ...config,\n }\n\n this.sessionId = config.sessionId || this.generateId()\n this.visitorId = config.visitorId || this.getOrCreateVisitorId()\n\n this.state = this.createInitialState()\n }\n\n /**\n * Create initial analytics state\n */\n private createInitialState(): VideoAnalyticsState {\n return {\n hasStarted: false,\n hasCompleted: false,\n watchTime: 0,\n pauseCount: 0,\n seekCount: 0,\n bufferCount: 0,\n bufferTime: 0,\n qualityChangeCount: 0,\n reachedMilestones: [],\n reachedQuartiles: [],\n playbackStartTime: null,\n lastHeartbeat: 0,\n isBuffering: false,\n bufferStartTime: null,\n }\n }\n\n /**\n * Attach to a player instance\n */\n attach(player: Player): void {\n if (this.config.disabled) return\n\n this.player = player\n this.attachEventListeners()\n this.startHeartbeat()\n }\n\n /**\n * Detach from player\n */\n detach(): void {\n this.stopHeartbeat()\n this.unsubscribers.forEach(unsub => unsub())\n this.unsubscribers = []\n this.flush()\n this.player = null\n }\n\n /**\n * Attach event listeners to player\n */\n private attachEventListeners(): void {\n if (!this.player) return\n\n // Play events\n this.on('play', () => {\n if (!this.state.hasStarted) {\n this.state.hasStarted = true\n this.state.playbackStartTime = Date.now()\n this.trackEvent('video_start')\n }\n else {\n this.trackEvent('video_resume')\n }\n this.trackEvent('video_play')\n })\n\n // Pause\n this.on('pause', () => {\n this.state.pauseCount++\n this.updateWatchTime()\n this.trackEvent('video_pause')\n })\n\n // Seeking\n if (this.config.trackSeeking) {\n this.on('seeking', () => {\n this.state.seekCount++\n })\n\n this.on('seeked', () => {\n const playerState = this.player?.state\n this.trackEvent('video_seek', {\n seekTo: playerState?.currentTime || 0,\n })\n })\n }\n\n // Buffering\n if (this.config.trackBuffering) {\n this.on('waiting', () => {\n if (!this.state.isBuffering) {\n this.state.isBuffering = true\n this.state.bufferStartTime = Date.now()\n this.state.bufferCount++\n this.trackEvent('video_buffer_start')\n }\n })\n\n this.on('playing', () => {\n if (this.state.isBuffering) {\n this.state.isBuffering = false\n if (this.state.bufferStartTime) {\n this.state.bufferTime += (Date.now() - this.state.bufferStartTime) / 1000\n }\n this.trackEvent('video_buffer_end', {\n bufferDuration: this.state.bufferStartTime\n ? (Date.now() - this.state.bufferStartTime) / 1000\n : 0,\n })\n this.state.bufferStartTime = null\n }\n })\n }\n\n // Time update for milestones\n this.on('timeupdate', () => {\n this.checkMilestones()\n })\n\n // Quality changes\n if (this.config.trackQualityChanges) {\n this.on('qualitychange', (quality: VideoQuality | null) => {\n this.state.qualityChangeCount++\n this.trackEvent('video_quality_change', {\n quality: quality ? `${quality.height}p` : 'auto',\n bitrate: quality?.bitrate,\n })\n })\n }\n\n // Volume changes\n this.on('volumechange', () => {\n const playerState = this.player?.state\n if (playerState?.muted) {\n this.trackEvent('video_mute')\n }\n else {\n this.trackEvent('video_volume_change', {\n volume: playerState?.volume,\n })\n }\n })\n\n // Fullscreen\n this.on('fullscreenchange', (isFullscreen: boolean) => {\n this.trackEvent(isFullscreen ? 'video_fullscreen_enter' : 'video_fullscreen_exit')\n })\n\n // PiP\n this.on('pipchange', (isPiP: boolean) => {\n this.trackEvent(isPiP ? 'video_pip_enter' : 'video_pip_exit')\n })\n\n // Captions\n this.on('texttrackchange', (track: TextTrack | null) => {\n if (track) {\n this.trackEvent('video_caption_change', {\n language: track.language,\n label: track.label,\n })\n }\n else {\n this.trackEvent('video_caption_disable')\n }\n })\n\n // Audio track\n this.on('audiotrackchange', (track: AudioTrack | null) => {\n if (track) {\n this.trackEvent('video_audio_track_change', {\n language: track.language,\n label: track.label,\n })\n }\n })\n\n // Speed change\n this.on('ratechange', (rate: number) => {\n this.trackEvent('video_speed_change', { speed: rate })\n })\n\n // Ended\n this.on('ended', () => {\n if (!this.state.hasCompleted) {\n this.state.hasCompleted = true\n this.updateWatchTime()\n this.trackEvent('video_complete', {\n watchTime: this.state.watchTime,\n pauseCount: this.state.pauseCount,\n seekCount: this.state.seekCount,\n bufferCount: this.state.bufferCount,\n bufferTime: this.state.bufferTime,\n })\n }\n })\n\n // Errors\n if (this.config.trackErrors) {\n this.on('error', (error: any) => {\n this.trackEvent('video_error', {\n code: error?.code,\n message: error?.message,\n })\n })\n }\n }\n\n /**\n * Helper to subscribe to player events\n */\n private on(event: string, handler: (...args: any[]) => void): void {\n if (!this.player) return\n this.player.on(event as any, handler)\n this.unsubscribers.push(() => this.player?.off(event as any, handler))\n }\n\n /**\n * Check and track milestones\n */\n private checkMilestones(): void {\n const playerState = this.player?.state\n if (!playerState || !playerState.duration) return\n\n const progress = (playerState.currentTime / playerState.duration) * 100\n\n // Check custom milestones\n for (const milestone of this.config.milestones || []) {\n if (progress >= milestone && !this.state.reachedMilestones.includes(milestone)) {\n this.state.reachedMilestones.push(milestone)\n this.trackEvent('video_milestone', { milestone })\n }\n }\n\n // Check quartiles (25%, 50%, 75%, 100%)\n const quartiles = [25, 50, 75, 100]\n for (const quartile of quartiles) {\n if (progress >= quartile && !this.state.reachedQuartiles.includes(quartile)) {\n this.state.reachedQuartiles.push(quartile)\n this.trackEvent('video_quartile', { quartile })\n }\n }\n }\n\n /**\n * Update watch time\n */\n private updateWatchTime(): void {\n if (this.state.playbackStartTime) {\n this.state.watchTime += (Date.now() - this.state.playbackStartTime) / 1000\n this.state.playbackStartTime = Date.now()\n }\n }\n\n /**\n * Start heartbeat tracking\n */\n private startHeartbeat(): void {\n if (this.heartbeatTimer) return\n\n const interval = (this.config.heartbeatInterval || DEFAULT_HEARTBEAT_INTERVAL) * 1000\n\n this.heartbeatTimer = setInterval(() => {\n const playerState = this.player?.state\n if (playerState?.playbackState === 'playing') {\n this.updateWatchTime()\n this.trackEvent('video_heartbeat', {\n watchTime: this.state.watchTime,\n currentTime: playerState.currentTime,\n })\n }\n }, interval)\n }\n\n /**\n * Stop heartbeat tracking\n */\n private stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n }\n\n /**\n * Track an event\n */\n trackEvent(name: VideoEventName, properties?: Record<string, unknown>): void {\n if (this.config.disabled) return\n\n const playerState = this.player?.state\n\n const event: VideoEvent = {\n name,\n timestamp: new Date(),\n currentTime: playerState?.currentTime || 0,\n duration: playerState?.duration || this.config.duration || 0,\n properties,\n }\n\n this.eventQueue.push(event)\n this.scheduleFlush()\n }\n\n /**\n * Schedule event flush\n */\n private scheduleFlush(): void {\n if (this.flushTimer) return\n\n this.flushTimer = setTimeout(() => {\n this.flush()\n this.flushTimer = null\n }, 1000) // Batch events for 1 second\n }\n\n /**\n * Flush events to analytics endpoint\n */\n async flush(): Promise<void> {\n if (this.eventQueue.length === 0) return\n\n const events = [...this.eventQueue]\n this.eventQueue = []\n\n try {\n await this.sendEvents(events)\n }\n catch (error) {\n // Re-queue on failure\n console.error('Failed to send analytics events:', error)\n this.eventQueue.unshift(...events)\n }\n }\n\n /**\n * Send events to analytics endpoint\n */\n private async sendEvents(events: VideoEvent[]): Promise<void> {\n const payload = events.map(event => this.buildEventPayload(event))\n\n // Send to ts-analytics /collect endpoint\n for (const p of payload) {\n await fetch(this.config.apiEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(p),\n keepalive: true,\n })\n }\n }\n\n /**\n * Build event payload for ts-analytics\n */\n private buildEventPayload(event: VideoEvent): Record<string, unknown> {\n return {\n s: this.config.siteId, // siteId\n sid: this.sessionId, // sessionId\n vid: this.visitorId, // visitorId\n e: 'event', // event type (custom event)\n u: typeof window !== 'undefined' ? window.location.href : '', // URL\n p: {\n // Event properties\n eventName: event.name,\n videoId: this.config.videoId,\n videoTitle: this.config.videoTitle,\n contentType: this.config.contentType,\n category: this.config.category,\n creatorId: this.config.creatorId,\n seriesId: this.config.seriesId,\n episode: this.config.episode,\n season: this.config.season,\n currentTime: event.currentTime,\n duration: event.duration,\n progress: event.duration > 0 ? (event.currentTime / event.duration) * 100 : 0,\n watchTime: this.state.watchTime,\n ...this.config.customProperties,\n ...event.properties,\n },\n }\n }\n\n /**\n * Generate unique ID\n */\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n\n /**\n * Get or create visitor ID\n */\n private getOrCreateVisitorId(): string {\n if (typeof localStorage === 'undefined') {\n return this.generateId()\n }\n\n const key = 'tsvp_visitor_id'\n let visitorId = localStorage.getItem(key)\n\n if (!visitorId) {\n visitorId = this.generateId()\n localStorage.setItem(key, visitorId)\n }\n\n return visitorId\n }\n\n /**\n * Get current analytics state\n */\n getState(): VideoAnalyticsState {\n return { ...this.state }\n }\n\n /**\n * Get session summary\n */\n getSummary(): Record<string, unknown> {\n return {\n videoId: this.config.videoId,\n sessionId: this.sessionId,\n hasStarted: this.state.hasStarted,\n hasCompleted: this.state.hasCompleted,\n watchTime: this.state.watchTime,\n pauseCount: this.state.pauseCount,\n seekCount: this.state.seekCount,\n bufferCount: this.state.bufferCount,\n bufferTime: this.state.bufferTime,\n qualityChangeCount: this.state.qualityChangeCount,\n reachedMilestones: this.state.reachedMilestones,\n completionRate: this.state.reachedQuartiles.length > 0\n ? Math.max(...this.state.reachedQuartiles)\n : 0,\n }\n }\n\n /**\n * Reset analytics state\n */\n reset(): void {\n this.flush()\n this.state = this.createInitialState()\n this.sessionId = this.generateId()\n }\n\n /**\n * Destroy analytics instance\n */\n destroy(): void {\n this.detach()\n if (this.flushTimer) {\n clearTimeout(this.flushTimer)\n }\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createVideoAnalytics(config: VideoAnalyticsConfig): VideoAnalytics {\n return new VideoAnalytics(config)\n}\n\n// =============================================================================\n// Player Plugin Integration\n// =============================================================================\n\n/**\n * Analytics plugin for Player\n */\nexport function analyticsPlugin(config: Omit<VideoAnalyticsConfig, 'videoId'>): { name: string, install: (player: Player, videoConfig: { videoId: string, videoTitle?: string }) => () => void } {\n return {\n name: 'analytics',\n\n install(player: Player, videoConfig: { videoId: string; videoTitle?: string }) {\n const analytics = createVideoAnalytics({\n ...config,\n ...videoConfig,\n })\n\n analytics.attach(player)\n\n // Expose on player\n ;(player as any).analytics = analytics\n\n return () => {\n analytics.destroy()\n delete (player as any).analytics\n }\n },\n }\n}\n",
|
|
22
|
+
"/**\n * Video Ads Plugin\n *\n * VAST/VPAID ad support for video playback.\n *\n * @module plugins/ads\n */\n\nimport type { Player } from '../player'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type AdType = 'linear' | 'nonlinear' | 'companion'\nexport type AdBreakType = 'preroll' | 'midroll' | 'postroll'\n\nexport interface AdConfig {\n /** VAST tag URL or inline VAST XML */\n vastUrl?: string\n /** VAST XML content */\n vastXml?: string\n /** VMAP URL for ad schedule */\n vmapUrl?: string\n /** Ad schedule */\n schedule?: AdBreak[]\n /** Skip delay in seconds (0 = no skip, -1 = use VAST value) */\n skipDelay?: number\n /** Maximum number of redirects to follow */\n maxRedirects?: number\n /** Timeout for ad requests in ms */\n timeout?: number\n /** Enable VPAID */\n vpaidEnabled?: boolean\n /** Companion ad container selectors */\n companionContainers?: Record<string, string>\n /** Preload ads */\n preloadAds?: boolean\n /** Debug mode */\n debug?: boolean\n}\n\nexport interface AdBreak {\n /** Break type */\n type: AdBreakType\n /** Time offset in seconds (for midrolls) */\n timeOffset?: number\n /** VAST URL */\n vastUrl?: string\n /** VAST XML */\n vastXml?: string\n /** Has played */\n played?: boolean\n}\n\nexport interface Ad {\n /** Ad ID */\n id: string\n /** Ad type */\n type: AdType\n /** Ad system */\n adSystem?: string\n /** Ad title */\n title?: string\n /** Ad description */\n description?: string\n /** Ad duration in seconds */\n duration: number\n /** Skip offset in seconds */\n skipOffset?: number\n /** Click through URL */\n clickThroughUrl?: string\n /** Media files */\n mediaFiles: AdMediaFile[]\n /** Companion ads */\n companions?: CompanionAd[]\n /** Tracking events */\n trackingEvents: AdTrackingEvent[]\n /** VPAID script */\n vpaidScript?: string\n /** Icons */\n icons?: AdIcon[]\n}\n\nexport interface AdMediaFile {\n /** Media URL */\n url: string\n /** MIME type */\n type: string\n /** Width */\n width: number\n /** Height */\n height: number\n /** Bitrate */\n bitrate?: number\n /** Codec */\n codec?: string\n /** Delivery type */\n delivery: 'progressive' | 'streaming'\n}\n\nexport interface CompanionAd {\n /** Width */\n width: number\n /** Height */\n height: number\n /** Resource URL (image, iframe, HTML) */\n resourceUrl: string\n /** Resource type */\n resourceType: 'static' | 'iframe' | 'html'\n /** Click through URL */\n clickThroughUrl?: string\n /** Alt text */\n altText?: string\n /** Tracking pixel */\n trackingUrl?: string\n}\n\nexport interface AdTrackingEvent {\n /** Event name */\n event: string\n /** Tracking URL */\n url: string\n /** Offset (for progress events) */\n offset?: number\n}\n\nexport interface AdIcon {\n /** Icon URL */\n url: string\n /** Width */\n width: number\n /** Height */\n height: number\n /** Position */\n xPosition: string\n /** Position */\n yPosition: string\n /** Click URL */\n clickUrl?: string\n}\n\nexport interface AdState {\n /** Is ad playing */\n isPlaying: boolean\n /** Current ad */\n currentAd: Ad | null\n /** Current ad break */\n currentBreak: AdBreak | null\n /** Time remaining */\n timeRemaining: number\n /** Can skip */\n canSkip: boolean\n /** Skip countdown */\n skipCountdown: number\n /** Is loading */\n isLoading: boolean\n /** Error */\n error: string | null\n}\n\n// =============================================================================\n// VAST Parser\n// =============================================================================\n\nexport class VASTParser {\n private maxRedirects: number\n private timeout: number\n private redirectCount = 0\n\n constructor(options: { maxRedirects?: number; timeout?: number } = {}) {\n this.maxRedirects = options.maxRedirects ?? 5\n this.timeout = options.timeout ?? 10000\n }\n\n /**\n * Parse VAST from URL\n */\n async parseFromUrl(url: string): Promise<Ad[]> {\n this.redirectCount = 0\n return this.fetchAndParse(url)\n }\n\n /**\n * Parse VAST from XML string\n */\n parseFromXml(xml: string): Ad[] {\n const parser = new DOMParser()\n const doc = parser.parseFromString(xml, 'text/xml')\n return this.parseDocument(doc)\n }\n\n /**\n * Fetch and parse VAST\n */\n private async fetchAndParse(url: string): Promise<Ad[]> {\n if (this.redirectCount >= this.maxRedirects) {\n throw new Error('Too many VAST redirects')\n }\n\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.timeout)\n\n try {\n const response = await fetch(url, { signal: controller.signal })\n const text = await response.text()\n const parser = new DOMParser()\n const doc = parser.parseFromString(text, 'text/xml')\n\n // Check for wrapper (redirect)\n const wrapper = doc.querySelector('Wrapper')\n if (wrapper) {\n const vastAdTagUri = wrapper.querySelector('VASTAdTagURI')?.textContent?.trim()\n if (vastAdTagUri) {\n this.redirectCount++\n return this.fetchAndParse(vastAdTagUri)\n }\n }\n\n return this.parseDocument(doc)\n }\n finally {\n clearTimeout(timeoutId)\n }\n }\n\n /**\n * Parse VAST document\n */\n private parseDocument(doc: Document): Ad[] {\n const ads: Ad[] = []\n const adElements = doc.querySelectorAll('Ad')\n\n adElements.forEach((adEl) => {\n const inLine = adEl.querySelector('InLine')\n if (!inLine) return\n\n const ad: Ad = {\n id: adEl.getAttribute('id') || this.generateId(),\n type: 'linear',\n adSystem: inLine.querySelector('AdSystem')?.textContent?.trim(),\n title: inLine.querySelector('AdTitle')?.textContent?.trim(),\n description: inLine.querySelector('Description')?.textContent?.trim(),\n duration: 0,\n mediaFiles: [],\n trackingEvents: [],\n }\n\n // Parse Linear\n const linear = inLine.querySelector('Linear')\n if (linear) {\n // Duration\n const durationStr = linear.querySelector('Duration')?.textContent?.trim()\n if (durationStr) {\n ad.duration = this.parseDuration(durationStr)\n }\n\n // Skip offset\n const skipOffset = linear.getAttribute('skipoffset')\n if (skipOffset) {\n ad.skipOffset = this.parseOffset(skipOffset, ad.duration)\n }\n\n // Media files\n linear.querySelectorAll('MediaFile').forEach((mf) => {\n const url = mf.textContent?.trim()\n if (url) {\n ad.mediaFiles.push({\n url,\n type: mf.getAttribute('type') || 'video/mp4',\n width: Number.parseInt(mf.getAttribute('width') || '0', 10),\n height: Number.parseInt(mf.getAttribute('height') || '0', 10),\n bitrate: mf.getAttribute('bitrate')\n ? Number.parseInt(mf.getAttribute('bitrate')!, 10)\n : undefined,\n delivery: (mf.getAttribute('delivery') as any) || 'progressive',\n })\n }\n })\n\n // Click through\n const clickThrough = linear.querySelector('VideoClicks > ClickThrough')\n if (clickThrough) {\n ad.clickThroughUrl = clickThrough.textContent?.trim()\n }\n\n // Tracking events\n linear.querySelectorAll('TrackingEvents > Tracking').forEach((tracking) => {\n const event = tracking.getAttribute('event')\n const url = tracking.textContent?.trim()\n if (event && url) {\n const trackingEvent: AdTrackingEvent = { event, url }\n\n // Handle progress offset\n if (event === 'progress') {\n const offset = tracking.getAttribute('offset')\n if (offset) {\n trackingEvent.offset = this.parseOffset(offset, ad.duration)\n }\n }\n\n ad.trackingEvents.push(trackingEvent)\n }\n })\n\n // Icons\n linear.querySelectorAll('Icons > Icon').forEach((icon) => {\n const staticResource = icon.querySelector('StaticResource')\n if (staticResource?.textContent) {\n ad.icons = ad.icons || []\n ad.icons.push({\n url: staticResource.textContent.trim(),\n width: Number.parseInt(icon.getAttribute('width') || '0', 10),\n height: Number.parseInt(icon.getAttribute('height') || '0', 10),\n xPosition: icon.getAttribute('xPosition') || 'right',\n yPosition: icon.getAttribute('yPosition') || 'top',\n clickUrl: icon.querySelector('IconClicks > IconClickThrough')?.textContent?.trim(),\n })\n }\n })\n }\n\n // Parse Companion ads\n inLine.querySelectorAll('CompanionAds > Companion').forEach((comp) => {\n ad.companions = ad.companions || []\n\n const staticResource = comp.querySelector('StaticResource')\n const iframeResource = comp.querySelector('IFrameResource')\n const htmlResource = comp.querySelector('HTMLResource')\n\n let resourceUrl = ''\n let resourceType: CompanionAd['resourceType'] = 'static'\n\n if (staticResource?.textContent) {\n resourceUrl = staticResource.textContent.trim()\n resourceType = 'static'\n }\n else if (iframeResource?.textContent) {\n resourceUrl = iframeResource.textContent.trim()\n resourceType = 'iframe'\n }\n else if (htmlResource?.textContent) {\n resourceUrl = htmlResource.textContent.trim()\n resourceType = 'html'\n }\n\n if (resourceUrl) {\n ad.companions.push({\n width: Number.parseInt(comp.getAttribute('width') || '0', 10),\n height: Number.parseInt(comp.getAttribute('height') || '0', 10),\n resourceUrl,\n resourceType,\n clickThroughUrl: comp.querySelector('CompanionClickThrough')?.textContent?.trim(),\n altText: comp.getAttribute('altText') || undefined,\n trackingUrl: comp.querySelector('TrackingEvents > Tracking')?.textContent?.trim(),\n })\n }\n })\n\n if (ad.mediaFiles.length > 0) {\n ads.push(ad)\n }\n })\n\n return ads\n }\n\n /**\n * Parse duration string (HH:MM:SS.mmm)\n */\n private parseDuration(duration: string): number {\n const parts = duration.split(':')\n if (parts.length !== 3) return 0\n\n const hours = Number.parseFloat(parts[0])\n const minutes = Number.parseFloat(parts[1])\n const seconds = Number.parseFloat(parts[2])\n\n return hours * 3600 + minutes * 60 + seconds\n }\n\n /**\n * Parse offset (time or percentage)\n */\n private parseOffset(offset: string, duration: number): number {\n if (offset.endsWith('%')) {\n const percent = Number.parseFloat(offset) / 100\n return duration * percent\n }\n return this.parseDuration(offset)\n }\n\n /**\n * Generate unique ID\n */\n private generateId(): string {\n return `ad-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`\n }\n}\n\n// =============================================================================\n// Ads Manager\n// =============================================================================\n\nexport class AdsManager {\n private player: Player | null = null\n private config: AdConfig\n private parser: VASTParser\n private state: AdState\n private container: HTMLElement | null = null\n private adVideo: HTMLVideoElement | null = null\n private adContainer: HTMLElement | null = null\n private skipButton: HTMLButtonElement | null = null\n private adBreaks: AdBreak[] = []\n private currentAds: Ad[] = []\n private currentAdIndex = 0\n private skipTimer: ReturnType<typeof setInterval> | null = null\n private contentPaused = false\n\n // Event handlers\n // eslint-disable-next-line pickier/no-unused-vars\n private onAdStart: ((_ad: Ad) => void) | null = null\n // eslint-disable-next-line pickier/no-unused-vars\n private onAdComplete: ((_ad: Ad) => void) | null = null\n // eslint-disable-next-line pickier/no-unused-vars\n private onAdSkip: ((_ad: Ad) => void) | null = null\n // eslint-disable-next-line pickier/no-unused-vars\n private onAdClick: ((_ad: Ad) => void) | null = null\n // eslint-disable-next-line pickier/no-unused-vars\n private onAdErrorCallback: ((_error: string) => void) | null = null\n private onAllAdsComplete: (() => void) | null = null\n\n constructor(config: AdConfig = {}) {\n this.config = {\n skipDelay: 5,\n maxRedirects: 5,\n timeout: 10000,\n vpaidEnabled: false,\n preloadAds: true,\n debug: false,\n ...config,\n }\n\n this.parser = new VASTParser({\n maxRedirects: this.config.maxRedirects,\n timeout: this.config.timeout,\n })\n\n this.state = {\n isPlaying: false,\n currentAd: null,\n currentBreak: null,\n timeRemaining: 0,\n canSkip: false,\n skipCountdown: 0,\n isLoading: false,\n error: null,\n }\n\n // Parse schedule\n if (this.config.schedule) {\n this.adBreaks = this.config.schedule\n }\n else if (this.config.vastUrl || this.config.vastXml) {\n // Single preroll\n this.adBreaks = [{\n type: 'preroll',\n vastUrl: this.config.vastUrl,\n vastXml: this.config.vastXml,\n }]\n }\n }\n\n /**\n * Attach to player\n */\n attach(player: Player, container: HTMLElement): void {\n this.player = player\n this.container = container\n\n this.createAdContainer()\n this.attachPlayerEvents()\n\n // Preload ads\n if (this.config.preloadAds) {\n this.preloadAds()\n }\n }\n\n /**\n * Create ad container\n */\n private createAdContainer(): void {\n if (!this.container) return\n\n this.adContainer = document.createElement('div')\n this.adContainer.className = 'tsvp-ad-container'\n this.adContainer.style.cssText = `\n position: absolute;\n inset: 0;\n z-index: 50;\n display: none;\n background: #000;\n `\n\n // Ad video\n this.adVideo = document.createElement('video')\n this.adVideo.style.cssText = `\n width: 100%;\n height: 100%;\n object-fit: contain;\n `\n this.adVideo.playsInline = true\n\n // Ad overlay\n const overlay = document.createElement('div')\n overlay.className = 'tsvp-ad-overlay'\n overlay.style.cssText = `\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n pointer-events: none;\n `\n\n // Ad info\n const adInfo = document.createElement('div')\n adInfo.className = 'tsvp-ad-info'\n adInfo.style.cssText = `\n position: absolute;\n top: 12px;\n left: 12px;\n padding: 6px 12px;\n background: rgba(0, 0, 0, 0.7);\n color: #fff;\n font-size: 12px;\n font-family: var(--tsvp-font-family, sans-serif);\n border-radius: 4px;\n `\n adInfo.textContent = 'Ad'\n\n // Skip button\n this.skipButton = document.createElement('button')\n this.skipButton.className = 'tsvp-ad-skip'\n this.skipButton.style.cssText = `\n position: absolute;\n bottom: 80px;\n right: 12px;\n padding: 10px 16px;\n background: rgba(0, 0, 0, 0.8);\n color: #fff;\n border: 1px solid rgba(255, 255, 255, 0.3);\n border-radius: 4px;\n font-size: 14px;\n font-family: var(--tsvp-font-family, sans-serif);\n cursor: pointer;\n pointer-events: auto;\n display: none;\n `\n this.skipButton.addEventListener('click', () => this.skipAd())\n\n // Learn more button\n const learnMore = document.createElement('button')\n learnMore.className = 'tsvp-ad-learn-more'\n learnMore.style.cssText = `\n position: absolute;\n bottom: 80px;\n left: 12px;\n padding: 10px 16px;\n background: var(--tsvp-color-primary, #00a8ff);\n color: #fff;\n border: none;\n border-radius: 4px;\n font-size: 14px;\n font-family: var(--tsvp-font-family, sans-serif);\n cursor: pointer;\n pointer-events: auto;\n display: none;\n `\n learnMore.textContent = 'Learn More'\n learnMore.addEventListener('click', () => this.handleAdClick())\n\n overlay.appendChild(adInfo)\n overlay.appendChild(this.skipButton)\n overlay.appendChild(learnMore)\n\n this.adContainer.appendChild(this.adVideo)\n this.adContainer.appendChild(overlay)\n this.container.appendChild(this.adContainer)\n\n // Ad video events\n this.adVideo.addEventListener('timeupdate', () => this.onAdTimeUpdate())\n this.adVideo.addEventListener('ended', () => this.onAdEnded())\n this.adVideo.addEventListener('error', () => this.handleAdError())\n }\n\n /**\n * Attach player events\n */\n private attachPlayerEvents(): void {\n if (!this.player) return\n\n // Check for preroll on first play\n let firstPlay = true\n this.player.on('play', async () => {\n if (firstPlay) {\n firstPlay = false\n const preroll = this.adBreaks.find(b => b.type === 'preroll' && !b.played)\n if (preroll) {\n await this.playAdBreak(preroll)\n }\n }\n })\n\n // Check for midrolls\n this.player.on('timeupdate', () => {\n const state = this.player?.state\n if (!state) return\n\n for (const adBreak of this.adBreaks) {\n if (\n adBreak.type === 'midroll'\n && !adBreak.played\n && adBreak.timeOffset\n && state.currentTime >= adBreak.timeOffset\n ) {\n this.playAdBreak(adBreak)\n break\n }\n }\n })\n\n // Check for postroll\n this.player.on('ended', async () => {\n const postroll = this.adBreaks.find(b => b.type === 'postroll' && !b.played)\n if (postroll) {\n await this.playAdBreak(postroll)\n }\n })\n }\n\n /**\n * Preload ads\n */\n private async preloadAds(): Promise<void> {\n for (const adBreak of this.adBreaks) {\n if (adBreak.vastUrl) {\n try {\n // Pre-parse VAST\n await this.parser.parseFromUrl(adBreak.vastUrl)\n }\n catch (error) {\n this.log('Failed to preload ad:', error)\n }\n }\n }\n }\n\n /**\n * Play ad break\n */\n async playAdBreak(adBreak: AdBreak): Promise<void> {\n if (this.state.isPlaying) return\n\n this.state.currentBreak = adBreak\n this.state.isLoading = true\n\n // Pause content\n this.pauseContent()\n\n try {\n // Parse VAST\n if (adBreak.vastUrl) {\n this.currentAds = await this.parser.parseFromUrl(adBreak.vastUrl)\n }\n else if (adBreak.vastXml) {\n this.currentAds = this.parser.parseFromXml(adBreak.vastXml)\n }\n\n if (this.currentAds.length === 0) {\n throw new Error('No ads found in VAST response')\n }\n\n this.currentAdIndex = 0\n adBreak.played = true\n\n await this.playAd(this.currentAds[0])\n }\n catch (error) {\n this.state.error = (error as Error).message\n this.state.isLoading = false\n this.onAdErrorCallback?.((error as Error).message)\n this.resumeContent()\n }\n }\n\n /**\n * Play single ad\n */\n private async playAd(ad: Ad): Promise<void> {\n if (!this.adVideo || !this.adContainer) return\n\n this.state.currentAd = ad\n this.state.isPlaying = true\n this.state.isLoading = false\n this.state.timeRemaining = ad.duration\n this.state.canSkip = false\n this.state.skipCountdown = this.config.skipDelay || 0\n\n // Select best media file\n const mediaFile = this.selectMediaFile(ad.mediaFiles)\n if (!mediaFile) {\n throw new Error('No suitable media file found')\n }\n\n // Show ad container\n this.adContainer.style.display = 'block'\n\n // Set source and play\n this.adVideo.src = mediaFile.url\n await this.adVideo.play()\n\n // Track impression\n this.trackEvent(ad, 'impression')\n this.trackEvent(ad, 'start')\n\n // Start skip timer\n this.startSkipTimer(ad)\n\n this.onAdStart?.(ad)\n }\n\n /**\n * Select best media file\n */\n private selectMediaFile(mediaFiles: AdMediaFile[]): AdMediaFile | null {\n // Sort by bitrate (highest first) and prefer mp4\n const sorted = [...mediaFiles].sort((a, b) => {\n if (a.type.includes('mp4') && !b.type.includes('mp4')) return -1\n if (!a.type.includes('mp4') && b.type.includes('mp4')) return 1\n return (b.bitrate || 0) - (a.bitrate || 0)\n })\n\n return sorted[0] || null\n }\n\n /**\n * Start skip timer\n */\n private startSkipTimer(ad: Ad): void {\n const skipDelay = this.config.skipDelay === -1\n ? (ad.skipOffset || 5)\n : (this.config.skipDelay || 0)\n\n if (skipDelay <= 0) {\n this.state.canSkip = true\n this.updateSkipButton()\n return\n }\n\n this.state.skipCountdown = skipDelay\n\n this.skipTimer = setInterval(() => {\n this.state.skipCountdown--\n\n if (this.state.skipCountdown <= 0) {\n this.state.canSkip = true\n if (this.skipTimer) {\n clearInterval(this.skipTimer)\n this.skipTimer = null\n }\n }\n\n this.updateSkipButton()\n }, 1000)\n }\n\n /**\n * Update skip button\n */\n private updateSkipButton(): void {\n if (!this.skipButton) return\n\n if (this.state.canSkip) {\n this.skipButton.textContent = 'Skip Ad'\n this.skipButton.style.display = 'block'\n }\n else if (this.state.skipCountdown > 0) {\n this.skipButton.textContent = `Skip in ${this.state.skipCountdown}s`\n this.skipButton.style.display = 'block'\n this.skipButton.style.cursor = 'default'\n }\n else {\n this.skipButton.style.display = 'none'\n }\n }\n\n /**\n * Skip current ad\n */\n skipAd(): void {\n if (!this.state.canSkip || !this.state.currentAd) return\n\n this.trackEvent(this.state.currentAd, 'skip')\n this.onAdSkip?.(this.state.currentAd)\n this.onAdEnded()\n }\n\n /**\n * Handle ad click\n */\n private handleAdClick(): void {\n if (!this.state.currentAd?.clickThroughUrl) return\n\n // Pause ad\n this.adVideo?.pause()\n\n // Track click\n this.trackEvent(this.state.currentAd, 'clickThrough')\n this.onAdClick?.(this.state.currentAd)\n\n // Open URL\n window.open(this.state.currentAd.clickThroughUrl, '_blank')\n }\n\n /**\n * Handle ad time update\n */\n private onAdTimeUpdate(): void {\n if (!this.adVideo || !this.state.currentAd) return\n\n const currentTime = this.adVideo.currentTime\n const duration = this.state.currentAd.duration\n\n this.state.timeRemaining = Math.max(0, duration - currentTime)\n\n // Track quartiles\n const progress = currentTime / duration\n if (progress >= 0.25) this.trackEvent(this.state.currentAd, 'firstQuartile')\n if (progress >= 0.5) this.trackEvent(this.state.currentAd, 'midpoint')\n if (progress >= 0.75) this.trackEvent(this.state.currentAd, 'thirdQuartile')\n\n // Track progress events\n for (const event of this.state.currentAd.trackingEvents) {\n if (event.event === 'progress' && event.offset && currentTime >= event.offset) {\n this.trackUrl(event.url)\n }\n }\n }\n\n /**\n * Handle ad ended\n */\n private onAdEnded(): void {\n if (this.state.currentAd) {\n this.trackEvent(this.state.currentAd, 'complete')\n this.onAdComplete?.(this.state.currentAd)\n }\n\n // Clean up skip timer\n if (this.skipTimer) {\n clearInterval(this.skipTimer)\n this.skipTimer = null\n }\n\n // Play next ad or resume content\n this.currentAdIndex++\n if (this.currentAdIndex < this.currentAds.length) {\n this.playAd(this.currentAds[this.currentAdIndex])\n }\n else {\n this.finishAdBreak()\n }\n }\n\n /**\n * Handle ad error\n */\n private handleAdError(): void {\n const error = 'Ad playback failed'\n this.state.error = error\n this.onAdErrorCallback?.(error)\n this.trackEvent(this.state.currentAd!, 'error')\n this.finishAdBreak()\n }\n\n /**\n * Finish ad break\n */\n private finishAdBreak(): void {\n this.state.isPlaying = false\n this.state.currentAd = null\n this.state.currentBreak = null\n this.currentAds = []\n this.currentAdIndex = 0\n\n // Hide ad container\n if (this.adContainer) {\n this.adContainer.style.display = 'none'\n }\n\n this.onAllAdsComplete?.()\n this.resumeContent()\n }\n\n /**\n * Pause content\n */\n private pauseContent(): void {\n if (!this.player) return\n\n const state = this.player.state\n if (state.playbackState === 'playing') {\n this.contentPaused = true\n this.player.pause()\n }\n }\n\n /**\n * Resume content\n */\n private resumeContent(): void {\n if (!this.player) return\n\n if (this.contentPaused) {\n this.contentPaused = false\n this.player.play()\n }\n }\n\n /**\n * Track ad event\n */\n private trackEvent(ad: Ad, eventName: string): void {\n const events = ad.trackingEvents.filter(e => e.event === eventName)\n for (const event of events) {\n this.trackUrl(event.url)\n }\n }\n\n /**\n * Track URL (fire pixel)\n */\n private trackUrl(url: string): void {\n const img = new Image()\n img.src = url\n }\n\n /**\n * Log debug message\n */\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n // eslint-disable-next-line no-console\n console.log('[VideoAds]', ...args)\n }\n }\n\n /**\n * Get current state\n */\n getState(): AdState {\n return { ...this.state }\n }\n\n /**\n * Set event handlers\n */\n on(event: 'adStart', handler: (ad: Ad) => void): void\n on(event: 'adComplete', handler: (ad: Ad) => void): void\n on(event: 'adSkip', handler: (ad: Ad) => void): void\n on(event: 'adClick', handler: (ad: Ad) => void): void\n on(event: 'adError', handler: (error: string) => void): void\n on(event: 'allAdsComplete', handler: () => void): void\n on(event: string, handler: (...args: any[]) => void): void {\n switch (event) {\n case 'adStart':\n this.onAdStart = handler\n break\n case 'adComplete':\n this.onAdComplete = handler\n break\n case 'adSkip':\n this.onAdSkip = handler\n break\n case 'adClick':\n this.onAdClick = handler\n break\n case 'adError':\n this.onAdErrorCallback = handler\n break\n case 'allAdsComplete':\n this.onAllAdsComplete = handler\n break\n }\n }\n\n /**\n * Destroy ads manager\n */\n destroy(): void {\n if (this.skipTimer) {\n clearInterval(this.skipTimer)\n }\n\n if (this.adContainer) {\n this.adContainer.remove()\n }\n\n this.player = null\n this.container = null\n this.adVideo = null\n this.adContainer = null\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createAdsManager(config?: AdConfig): AdsManager {\n return new AdsManager(config)\n}\n\n/**\n * Ads plugin for Player\n */\nexport function adsPlugin(config: AdConfig): { name: string, install: (player: Player, container: HTMLElement) => () => void } {\n return {\n name: 'ads',\n\n install(player: Player, container: HTMLElement) {\n const ads = createAdsManager(config)\n ads.attach(player, container)\n\n // Expose on player\n ;(player as any).ads = ads\n\n return () => {\n ads.destroy()\n delete (player as any).ads\n }\n },\n }\n}\n",
|
|
23
|
+
"/**\n * Skip Segments Plugin\n *\n * Skip intro, outro, recap, and other segments.\n *\n * @module plugins/skip-segments\n */\n\nimport type { Player } from '../player'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type SegmentType = 'intro' | 'outro' | 'recap' | 'preview' | 'credits' | 'sponsor' | 'custom'\n\nexport interface SkipSegment {\n /** Segment type */\n type: SegmentType\n /** Start time in seconds */\n startTime: number\n /** End time in seconds */\n endTime: number\n /** Skip to time (defaults to endTime) */\n skipTo?: number\n /** Button text */\n buttonText?: string\n /** Auto-skip after delay (0 = no auto-skip) */\n autoSkipDelay?: number\n /** Show countdown */\n showCountdown?: boolean\n /** Custom data */\n data?: Record<string, unknown>\n}\n\nexport interface SkipSegmentsConfig {\n /** Skip segments */\n segments: SkipSegment[]\n /** Default button visibility duration (ms) */\n buttonVisibleDuration?: number\n /** Default auto-skip delay (seconds) */\n defaultAutoSkipDelay?: number\n /** Position of skip button */\n buttonPosition?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'\n /** Show progress bar markers */\n showMarkers?: boolean\n /** Marker color */\n markerColor?: string\n /** Enable keyboard shortcut (Enter to skip) */\n keyboardShortcut?: boolean\n}\n\nexport interface SkipSegmentsState {\n /** Current active segment */\n activeSegment: SkipSegment | null\n /** Is button visible */\n buttonVisible: boolean\n /** Auto-skip countdown */\n countdown: number\n /** Is auto-skipping */\n isAutoSkipping: boolean\n}\n\n// =============================================================================\n// Default Text Labels\n// =============================================================================\n\nconst defaultButtonText: Record<SegmentType, string> = {\n intro: 'Skip Intro',\n outro: 'Skip Outro',\n recap: 'Skip Recap',\n preview: 'Skip Preview',\n credits: 'Skip Credits',\n sponsor: 'Skip',\n custom: 'Skip',\n}\n\n// =============================================================================\n// Skip Segments Manager\n// =============================================================================\n\nexport class SkipSegmentsManager {\n private player: Player | null = null\n private config: Required<SkipSegmentsConfig>\n private state: SkipSegmentsState\n private container: HTMLElement | null = null\n private button: HTMLButtonElement | null = null\n private markersContainer: HTMLElement | null = null\n private countdownTimer: ReturnType<typeof setInterval> | null = null\n private hideTimer: ReturnType<typeof setTimeout> | null = null\n private unsubscribers: Array<() => void> = []\n\n // Event handlers\n // eslint-disable-next-line pickier/no-unused-vars\n private onSkip: ((_segment: SkipSegment) => void) | null = null\n // eslint-disable-next-line pickier/no-unused-vars\n private onSegmentEnter: ((_segment: SkipSegment) => void) | null = null\n // eslint-disable-next-line pickier/no-unused-vars\n private onSegmentExit: ((_segment: SkipSegment) => void) | null = null\n\n constructor(config: SkipSegmentsConfig) {\n this.config = {\n segments: config.segments || [],\n buttonVisibleDuration: config.buttonVisibleDuration ?? 10000,\n defaultAutoSkipDelay: config.defaultAutoSkipDelay ?? 0,\n buttonPosition: config.buttonPosition || 'bottom-right',\n showMarkers: config.showMarkers ?? true,\n markerColor: config.markerColor || 'rgba(255, 255, 0, 0.8)',\n keyboardShortcut: config.keyboardShortcut ?? true,\n }\n\n this.state = {\n activeSegment: null,\n buttonVisible: false,\n countdown: 0,\n isAutoSkipping: false,\n }\n }\n\n /**\n * Attach to player\n */\n attach(player: Player, container: HTMLElement): void {\n this.player = player\n this.container = container\n\n this.createButton()\n this.attachEventListeners()\n\n if (this.config.showMarkers) {\n this.createMarkers()\n }\n }\n\n /**\n * Create skip button\n */\n private createButton(): void {\n if (!this.container) return\n\n this.button = document.createElement('button')\n this.button.className = 'tsvp-skip-button'\n this.button.type = 'button'\n\n const positionStyles = this.getPositionStyles()\n\n this.button.style.cssText = `\n position: absolute;\n ${positionStyles}\n padding: 12px 24px;\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n border: 2px solid rgba(255, 255, 255, 0.8);\n border-radius: 4px;\n font-size: 16px;\n font-weight: 600;\n font-family: var(--tsvp-font-family, sans-serif);\n cursor: pointer;\n z-index: 30;\n display: none;\n transition: transform 150ms ease, background 150ms ease;\n `\n\n this.button.addEventListener('mouseenter', () => {\n this.button!.style.background = 'rgba(255, 255, 255, 0.2)'\n this.button!.style.transform = 'scale(1.05)'\n })\n\n this.button.addEventListener('mouseleave', () => {\n this.button!.style.background = 'rgba(0, 0, 0, 0.85)'\n this.button!.style.transform = 'scale(1)'\n })\n\n this.button.addEventListener('click', () => this.skip())\n\n this.container.appendChild(this.button)\n }\n\n /**\n * Get position styles for button\n */\n private getPositionStyles(): string {\n switch (this.config.buttonPosition) {\n case 'top-left':\n return 'top: 20px; left: 20px;'\n case 'top-right':\n return 'top: 20px; right: 20px;'\n case 'bottom-left':\n return 'bottom: 80px; left: 20px;'\n case 'bottom-right':\n default:\n return 'bottom: 80px; right: 20px;'\n }\n }\n\n /**\n * Create progress bar markers\n */\n private createMarkers(): void {\n // Markers will be created when attached to progress bar\n }\n\n /**\n * Get markers element for progress bar\n */\n getMarkersElement(duration: number): HTMLElement {\n this.markersContainer = document.createElement('div')\n this.markersContainer.className = 'tsvp-skip-markers'\n this.markersContainer.style.cssText = `\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n bottom: 0;\n pointer-events: none;\n `\n\n for (const segment of this.config.segments) {\n const marker = document.createElement('div')\n marker.className = 'tsvp-skip-marker'\n\n const startPercent = (segment.startTime / duration) * 100\n const widthPercent = ((segment.endTime - segment.startTime) / duration) * 100\n\n marker.style.cssText = `\n position: absolute;\n left: ${startPercent}%;\n width: ${widthPercent}%;\n top: 0;\n bottom: 0;\n background: ${this.config.markerColor};\n opacity: 0.6;\n `\n\n this.markersContainer.appendChild(marker)\n }\n\n return this.markersContainer\n }\n\n /**\n * Attach event listeners\n */\n private attachEventListeners(): void {\n if (!this.player) return\n\n // Time update to check for segments\n const handleTimeUpdate = () => {\n const state = this.player?.state\n if (!state) return\n\n this.checkSegments(state.currentTime)\n }\n\n this.player.on('timeupdate', handleTimeUpdate)\n this.unsubscribers.push(() => this.player?.off('timeupdate', handleTimeUpdate))\n\n // Keyboard shortcut\n if (this.config.keyboardShortcut) {\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' && this.state.activeSegment && this.state.buttonVisible) {\n e.preventDefault()\n this.skip()\n }\n }\n\n document.addEventListener('keydown', handleKeydown)\n this.unsubscribers.push(() => document.removeEventListener('keydown', handleKeydown))\n }\n }\n\n /**\n * Check if current time is in any segment\n */\n private checkSegments(currentTime: number): void {\n const previousSegment = this.state.activeSegment\n\n // Find active segment\n let activeSegment: SkipSegment | null = null\n for (const segment of this.config.segments) {\n if (currentTime >= segment.startTime && currentTime < segment.endTime) {\n activeSegment = segment\n break\n }\n }\n\n // Segment changed\n if (activeSegment !== previousSegment) {\n if (previousSegment) {\n this.onSegmentExit?.(previousSegment)\n this.hideButton()\n }\n\n if (activeSegment) {\n this.state.activeSegment = activeSegment\n this.onSegmentEnter?.(activeSegment)\n this.showButton(activeSegment)\n }\n else {\n this.state.activeSegment = null\n }\n }\n }\n\n /**\n * Show skip button\n */\n private showButton(segment: SkipSegment): void {\n if (!this.button) return\n\n // Set button text\n const text = segment.buttonText || defaultButtonText[segment.type]\n this.button.textContent = text\n\n this.state.buttonVisible = true\n this.button.style.display = 'block'\n\n // Animate in\n this.button.style.opacity = '0'\n this.button.style.transform = 'translateX(20px)'\n requestAnimationFrame(() => {\n this.button!.style.transition = 'opacity 200ms ease, transform 200ms ease'\n this.button!.style.opacity = '1'\n this.button!.style.transform = 'translateX(0)'\n })\n\n // Start auto-skip countdown\n const autoSkipDelay = segment.autoSkipDelay ?? this.config.defaultAutoSkipDelay\n if (autoSkipDelay > 0) {\n this.startAutoSkip(autoSkipDelay, segment)\n }\n\n // Auto-hide after duration\n if (this.config.buttonVisibleDuration > 0 && !autoSkipDelay) {\n this.hideTimer = setTimeout(() => {\n this.hideButton()\n }, this.config.buttonVisibleDuration)\n }\n }\n\n /**\n * Hide skip button\n */\n private hideButton(): void {\n if (!this.button) return\n\n this.state.buttonVisible = false\n this.state.isAutoSkipping = false\n this.state.countdown = 0\n\n // Clear timers\n if (this.countdownTimer) {\n clearInterval(this.countdownTimer)\n this.countdownTimer = null\n }\n if (this.hideTimer) {\n clearTimeout(this.hideTimer)\n this.hideTimer = null\n }\n\n // Animate out\n this.button.style.opacity = '0'\n this.button.style.transform = 'translateX(20px)'\n\n setTimeout(() => {\n if (this.button && !this.state.buttonVisible) {\n this.button.style.display = 'none'\n }\n }, 200)\n }\n\n /**\n * Start auto-skip countdown\n */\n private startAutoSkip(delay: number, segment: SkipSegment): void {\n this.state.isAutoSkipping = true\n this.state.countdown = delay\n\n this.updateButtonText(segment)\n\n this.countdownTimer = setInterval(() => {\n this.state.countdown--\n this.updateButtonText(segment)\n\n if (this.state.countdown <= 0) {\n if (this.countdownTimer) {\n clearInterval(this.countdownTimer)\n this.countdownTimer = null\n }\n this.skip()\n }\n }, 1000)\n }\n\n /**\n * Update button text with countdown\n */\n private updateButtonText(segment: SkipSegment): void {\n if (!this.button) return\n\n const baseText = segment.buttonText || defaultButtonText[segment.type]\n\n if (this.state.isAutoSkipping && this.state.countdown > 0 && segment.showCountdown !== false) {\n this.button.textContent = `${baseText} (${this.state.countdown})`\n }\n else {\n this.button.textContent = baseText\n }\n }\n\n /**\n * Skip current segment\n */\n skip(): void {\n if (!this.state.activeSegment || !this.player) return\n\n const segment = this.state.activeSegment\n const skipTo = segment.skipTo ?? segment.endTime\n\n this.player.seekTo(skipTo)\n this.onSkip?.(segment)\n this.hideButton()\n }\n\n /**\n * Add segment\n */\n addSegment(segment: SkipSegment): void {\n this.config.segments.push(segment)\n }\n\n /**\n * Remove segment\n */\n removeSegment(type: SegmentType): void {\n this.config.segments = this.config.segments.filter(s => s.type !== type)\n }\n\n /**\n * Clear all segments\n */\n clearSegments(): void {\n this.config.segments = []\n this.hideButton()\n }\n\n /**\n * Set segments\n */\n setSegments(segments: SkipSegment[]): void {\n this.config.segments = segments\n }\n\n /**\n * Get segments\n */\n getSegments(): SkipSegment[] {\n return [...this.config.segments]\n }\n\n /**\n * Get current state\n */\n getState(): SkipSegmentsState {\n return { ...this.state }\n }\n\n /**\n * Set event handlers\n */\n on(event: 'skip', handler: (segment: SkipSegment) => void): void\n on(event: 'segmentEnter', handler: (segment: SkipSegment) => void): void\n on(event: 'segmentExit', handler: (segment: SkipSegment) => void): void\n on(event: string, handler: (...args: any[]) => void): void {\n switch (event) {\n case 'skip':\n this.onSkip = handler\n break\n case 'segmentEnter':\n this.onSegmentEnter = handler\n break\n case 'segmentExit':\n this.onSegmentExit = handler\n break\n }\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n if (this.countdownTimer) {\n clearInterval(this.countdownTimer)\n }\n if (this.hideTimer) {\n clearTimeout(this.hideTimer)\n }\n\n this.unsubscribers.forEach(unsub => unsub())\n\n this.button?.remove()\n this.markersContainer?.remove()\n\n this.player = null\n this.container = null\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createSkipSegments(config: SkipSegmentsConfig): SkipSegmentsManager {\n return new SkipSegmentsManager(config)\n}\n\n/**\n * Skip segments plugin for Player\n */\nexport function skipSegmentsPlugin(config: SkipSegmentsConfig): { name: string, install: (player: Player, container: HTMLElement) => () => void } {\n return {\n name: 'skipSegments',\n\n install(player: Player, container: HTMLElement): () => void {\n const skipSegments = createSkipSegments(config)\n skipSegments.attach(player, container)\n\n // Expose on player\n ;(player as any).skipSegments = skipSegments\n\n return () => {\n skipSegments.destroy()\n delete (player as any).skipSegments\n }\n },\n }\n}\n",
|
|
24
|
+
"/**\n * End Screen Plugin\n *\n * Display recommendations and actions at video end.\n *\n * @module plugins/end-screen\n */\n\nimport type { Player } from '../player'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface EndScreenConfig {\n /** Show end screen on video end */\n enabled?: boolean\n /** Auto-show delay after video ends (ms) */\n showDelay?: number\n /** Recommendations */\n recommendations?: EndScreenRecommendation[]\n /** Custom actions */\n actions?: EndScreenAction[]\n /** Replay button */\n showReplay?: boolean\n /** Share button */\n showShare?: boolean\n /** Countdown to next video (0 = disabled) */\n autoplayCountdown?: number\n /** Title text */\n title?: string\n /** Custom template */\n template?: string\n /** Background style */\n backgroundStyle?: 'blur' | 'dark' | 'gradient' | 'none'\n}\n\nexport interface EndScreenRecommendation {\n /** Unique ID */\n id: string\n /** Title */\n title: string\n /** Description */\n description?: string\n /** Thumbnail URL */\n thumbnail: string\n /** Duration string (e.g., \"12:34\") */\n duration?: string\n /** Video URL or callback */\n url?: string\n /** Click handler */\n onClick?: () => void\n /** Channel/creator name */\n channel?: string\n /** View count */\n views?: string\n /** Upload date */\n date?: string\n}\n\nexport interface EndScreenAction {\n /** Button text */\n label: string\n /** Icon (SVG or emoji) */\n icon?: string\n /** Click handler */\n onClick: () => void\n /** Button style */\n style?: 'primary' | 'secondary' | 'outline'\n}\n\nexport interface EndScreenState {\n /** Is visible */\n visible: boolean\n /** Autoplay countdown */\n countdown: number\n /** Is countdown active */\n isCountingDown: boolean\n /** Selected recommendation index */\n selectedIndex: number\n}\n\n// =============================================================================\n// End Screen Manager\n// =============================================================================\n\nexport class EndScreenManager {\n private player: Player | null = null\n private config: Required<EndScreenConfig>\n private state: EndScreenState\n private container: HTMLElement | null = null\n private element: HTMLElement | null = null\n private countdownTimer: ReturnType<typeof setInterval> | null = null\n private showTimer: ReturnType<typeof setTimeout> | null = null\n private unsubscribers: Array<() => void> = []\n\n // Event handlers\n // eslint-disable-next-line pickier/no-unused-vars\n private onRecommendationClick: ((_rec: EndScreenRecommendation) => void) | null = null\n private onReplay: (() => void) | null = null\n // eslint-disable-next-line pickier/no-unused-vars\n private onAutoplay: ((_rec: EndScreenRecommendation) => void) | null = null\n private onShow: (() => void) | null = null\n private onHide: (() => void) | null = null\n\n constructor(config: EndScreenConfig = {}) {\n this.config = {\n enabled: config.enabled ?? true,\n showDelay: config.showDelay ?? 0,\n recommendations: config.recommendations || [],\n actions: config.actions || [],\n showReplay: config.showReplay ?? true,\n showShare: config.showShare ?? false,\n autoplayCountdown: config.autoplayCountdown ?? 0,\n title: config.title || '',\n template: config.template || '',\n backgroundStyle: config.backgroundStyle || 'blur',\n }\n\n this.state = {\n visible: false,\n countdown: 0,\n isCountingDown: false,\n selectedIndex: 0,\n }\n }\n\n /**\n * Attach to player\n */\n attach(player: Player, container: HTMLElement): void {\n this.player = player\n this.container = container\n\n this.createElement()\n this.attachEventListeners()\n }\n\n /**\n * Create end screen element\n */\n private createElement(): void {\n if (!this.container) return\n\n this.element = document.createElement('div')\n this.element.className = 'tsvp-end-screen'\n\n this.element.style.cssText = `\n position: absolute;\n inset: 0;\n display: none;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n z-index: 40;\n font-family: var(--tsvp-font-family, sans-serif);\n color: var(--tsvp-color-text, #ffffff);\n padding: 24px;\n overflow: auto;\n `\n\n this.applyBackgroundStyle()\n this.container.appendChild(this.element)\n }\n\n /**\n * Apply background style\n */\n private applyBackgroundStyle(): void {\n if (!this.element) return\n\n switch (this.config.backgroundStyle) {\n case 'blur':\n this.element.style.background = 'rgba(0, 0, 0, 0.85)'\n this.element.style.backdropFilter = 'blur(10px)'\n break\n case 'dark':\n this.element.style.background = 'rgba(0, 0, 0, 0.95)'\n break\n case 'gradient':\n this.element.style.background = 'linear-gradient(135deg, rgba(0,0,0,0.9) 0%, rgba(20,20,40,0.95) 100%)'\n break\n case 'none':\n this.element.style.background = 'transparent'\n break\n }\n }\n\n /**\n * Render end screen content\n */\n private render(): void {\n if (!this.element) return\n\n // Use custom template if provided\n if (this.config.template) {\n this.element.innerHTML = this.config.template\n return\n }\n\n this.element.innerHTML = ''\n\n // Title\n if (this.config.title) {\n const title = document.createElement('h2')\n title.className = 'tsvp-end-screen-title'\n title.textContent = this.config.title\n title.style.cssText = `\n margin: 0 0 24px;\n font-size: 24px;\n font-weight: 600;\n text-align: center;\n `\n this.element.appendChild(title)\n }\n\n // Recommendations grid\n if (this.config.recommendations.length > 0) {\n const grid = document.createElement('div')\n grid.className = 'tsvp-end-screen-grid'\n grid.style.cssText = `\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n max-width: 900px;\n width: 100%;\n margin-bottom: 24px;\n `\n\n this.config.recommendations.forEach((rec, index) => {\n const card = this.createRecommendationCard(rec, index)\n grid.appendChild(card)\n })\n\n this.element.appendChild(grid)\n }\n\n // Actions row\n const actionsRow = document.createElement('div')\n actionsRow.className = 'tsvp-end-screen-actions'\n actionsRow.style.cssText = `\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n justify-content: center;\n `\n\n // Replay button\n if (this.config.showReplay) {\n const replayBtn = this.createActionButton({\n label: 'Replay',\n icon: `<svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\">\n <path d=\"M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z\"/>\n </svg>`,\n style: 'secondary',\n onClick: () => this.replay(),\n })\n actionsRow.appendChild(replayBtn)\n }\n\n // Share button\n if (this.config.showShare) {\n const shareBtn = this.createActionButton({\n label: 'Share',\n icon: `<svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\">\n <path d=\"M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z\"/>\n </svg>`,\n style: 'outline',\n onClick: () => this.share(),\n })\n actionsRow.appendChild(shareBtn)\n }\n\n // Custom actions\n for (const action of this.config.actions) {\n const btn = this.createActionButton(action)\n actionsRow.appendChild(btn)\n }\n\n this.element.appendChild(actionsRow)\n\n // Autoplay countdown\n if (this.state.isCountingDown && this.config.recommendations.length > 0) {\n const countdown = document.createElement('div')\n countdown.className = 'tsvp-end-screen-countdown'\n countdown.style.cssText = `\n margin-top: 24px;\n text-align: center;\n font-size: 14px;\n opacity: 0.8;\n `\n countdown.innerHTML = `\n Next video in <strong>${this.state.countdown}</strong> seconds\n <button class=\"tsvp-end-screen-cancel\" style=\"\n margin-left: 12px;\n padding: 4px 12px;\n border: 1px solid rgba(255,255,255,0.3);\n border-radius: 4px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n \">Cancel</button>\n `\n\n const cancelBtn = countdown.querySelector('.tsvp-end-screen-cancel')\n cancelBtn?.addEventListener('click', () => this.cancelAutoplay())\n\n this.element.appendChild(countdown)\n }\n }\n\n /**\n * Create recommendation card\n */\n private createRecommendationCard(rec: EndScreenRecommendation, index: number): HTMLElement {\n const card = document.createElement('div')\n card.className = 'tsvp-end-screen-card'\n card.tabIndex = 0\n\n const isSelected = index === this.state.selectedIndex && this.state.isCountingDown\n\n card.style.cssText = `\n background: rgba(255, 255, 255, 0.1);\n border-radius: 8px;\n overflow: hidden;\n cursor: pointer;\n transition: transform 150ms ease, background 150ms ease;\n ${isSelected ? 'outline: 2px solid var(--tsvp-color-primary, #00a8ff); outline-offset: 2px;' : ''}\n `\n\n // Thumbnail\n const thumbnail = document.createElement('div')\n thumbnail.className = 'tsvp-end-screen-thumbnail'\n thumbnail.style.cssText = `\n position: relative;\n aspect-ratio: 16 / 9;\n background: url(${rec.thumbnail}) center/cover;\n `\n\n // Duration badge\n if (rec.duration) {\n const duration = document.createElement('span')\n duration.className = 'tsvp-end-screen-duration'\n duration.textContent = rec.duration\n duration.style.cssText = `\n position: absolute;\n bottom: 4px;\n right: 4px;\n padding: 2px 6px;\n background: rgba(0, 0, 0, 0.8);\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n `\n thumbnail.appendChild(duration)\n }\n\n card.appendChild(thumbnail)\n\n // Info\n const info = document.createElement('div')\n info.className = 'tsvp-end-screen-info'\n info.style.cssText = `\n padding: 12px;\n `\n\n const title = document.createElement('h3')\n title.className = 'tsvp-end-screen-card-title'\n title.textContent = rec.title\n title.style.cssText = `\n margin: 0 0 4px;\n font-size: 14px;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n `\n info.appendChild(title)\n\n if (rec.channel) {\n const channel = document.createElement('p')\n channel.className = 'tsvp-end-screen-channel'\n channel.textContent = rec.channel\n channel.style.cssText = `\n margin: 0;\n font-size: 12px;\n opacity: 0.7;\n `\n info.appendChild(channel)\n }\n\n if (rec.views || rec.date) {\n const meta = document.createElement('p')\n meta.className = 'tsvp-end-screen-meta'\n meta.textContent = [rec.views, rec.date].filter(Boolean).join(' • ')\n meta.style.cssText = `\n margin: 4px 0 0;\n font-size: 12px;\n opacity: 0.6;\n `\n info.appendChild(meta)\n }\n\n card.appendChild(info)\n\n // Hover effects\n card.addEventListener('mouseenter', () => {\n card.style.transform = 'scale(1.03)'\n card.style.background = 'rgba(255, 255, 255, 0.15)'\n })\n\n card.addEventListener('mouseleave', () => {\n card.style.transform = 'scale(1)'\n card.style.background = 'rgba(255, 255, 255, 0.1)'\n })\n\n // Click handler\n card.addEventListener('click', () => {\n this.onRecommendationClick?.(rec)\n if (rec.onClick) {\n rec.onClick()\n }\n else if (rec.url) {\n window.location.href = rec.url\n }\n })\n\n return card\n }\n\n /**\n * Create action button\n */\n private createActionButton(action: EndScreenAction): HTMLButtonElement {\n const btn = document.createElement('button')\n btn.className = `tsvp-end-screen-btn tsvp-end-screen-btn--${action.style || 'secondary'}`\n btn.type = 'button'\n\n let bgColor = 'rgba(255, 255, 255, 0.2)'\n let borderColor = 'transparent'\n\n switch (action.style) {\n case 'primary':\n bgColor = 'var(--tsvp-color-primary, #00a8ff)'\n break\n case 'outline':\n bgColor = 'transparent'\n borderColor = 'rgba(255, 255, 255, 0.5)'\n break\n }\n\n btn.style.cssText = `\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n background: ${bgColor};\n border: 1px solid ${borderColor};\n border-radius: 6px;\n color: #fff;\n font-size: 14px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n transition: transform 150ms ease, opacity 150ms ease;\n `\n\n if (action.icon) {\n const icon = document.createElement('span')\n icon.innerHTML = action.icon\n icon.style.display = 'flex'\n btn.appendChild(icon)\n }\n\n const label = document.createElement('span')\n label.textContent = action.label\n btn.appendChild(label)\n\n btn.addEventListener('mouseenter', () => {\n btn.style.transform = 'scale(1.05)'\n })\n\n btn.addEventListener('mouseleave', () => {\n btn.style.transform = 'scale(1)'\n })\n\n btn.addEventListener('click', action.onClick)\n\n return btn\n }\n\n /**\n * Attach event listeners\n */\n private attachEventListeners(): void {\n if (!this.player) return\n\n const handleEnded = () => {\n if (this.config.enabled) {\n this.showTimer = setTimeout(() => {\n this.show()\n }, this.config.showDelay)\n }\n }\n\n this.player.on('ended', handleEnded)\n this.unsubscribers.push(() => this.player?.off('ended', handleEnded))\n\n // Hide on play\n const handlePlay = () => {\n if (this.state.visible) {\n this.hide()\n }\n }\n\n this.player.on('play', handlePlay)\n this.unsubscribers.push(() => this.player?.off('play', handlePlay))\n\n // Keyboard navigation\n const handleKeydown = (e: KeyboardEvent) => {\n if (!this.state.visible) return\n\n switch (e.key) {\n case 'Escape':\n this.hide()\n break\n case 'ArrowLeft':\n this.selectPrevious()\n break\n case 'ArrowRight':\n this.selectNext()\n break\n case 'Enter':\n this.selectCurrent()\n break\n }\n }\n\n document.addEventListener('keydown', handleKeydown)\n this.unsubscribers.push(() => document.removeEventListener('keydown', handleKeydown))\n }\n\n /**\n * Show end screen\n */\n show(): void {\n if (!this.element || this.state.visible) return\n\n this.state.visible = true\n this.state.selectedIndex = 0\n\n // Start autoplay countdown\n if (this.config.autoplayCountdown > 0 && this.config.recommendations.length > 0) {\n this.startAutoplayCountdown()\n }\n\n this.render()\n\n this.element.style.display = 'flex'\n this.element.style.opacity = '0'\n requestAnimationFrame(() => {\n this.element!.style.transition = 'opacity 300ms ease'\n this.element!.style.opacity = '1'\n })\n\n this.onShow?.()\n }\n\n /**\n * Hide end screen\n */\n hide(): void {\n if (!this.element || !this.state.visible) return\n\n this.state.visible = false\n this.cancelAutoplay()\n\n this.element.style.opacity = '0'\n setTimeout(() => {\n if (this.element && !this.state.visible) {\n this.element.style.display = 'none'\n }\n }, 300)\n\n this.onHide?.()\n }\n\n /**\n * Start autoplay countdown\n */\n private startAutoplayCountdown(): void {\n this.state.isCountingDown = true\n this.state.countdown = this.config.autoplayCountdown\n\n this.countdownTimer = setInterval(() => {\n this.state.countdown--\n this.render()\n\n if (this.state.countdown <= 0) {\n this.playNextVideo()\n }\n }, 1000)\n }\n\n /**\n * Cancel autoplay\n */\n cancelAutoplay(): void {\n this.state.isCountingDown = false\n this.state.countdown = 0\n\n if (this.countdownTimer) {\n clearInterval(this.countdownTimer)\n this.countdownTimer = null\n }\n\n if (this.state.visible) {\n this.render()\n }\n }\n\n /**\n * Play next video\n */\n private playNextVideo(): void {\n const rec = this.config.recommendations[this.state.selectedIndex]\n if (rec) {\n this.onAutoplay?.(rec)\n if (rec.onClick) {\n rec.onClick()\n }\n else if (rec.url) {\n window.location.href = rec.url\n }\n }\n }\n\n /**\n * Select previous recommendation\n */\n private selectPrevious(): void {\n if (this.config.recommendations.length === 0) return\n this.state.selectedIndex = (this.state.selectedIndex - 1 + this.config.recommendations.length)\n % this.config.recommendations.length\n this.render()\n }\n\n /**\n * Select next recommendation\n */\n private selectNext(): void {\n if (this.config.recommendations.length === 0) return\n this.state.selectedIndex = (this.state.selectedIndex + 1) % this.config.recommendations.length\n this.render()\n }\n\n /**\n * Select current recommendation\n */\n private selectCurrent(): void {\n const rec = this.config.recommendations[this.state.selectedIndex]\n if (rec) {\n this.onRecommendationClick?.(rec)\n if (rec.onClick) {\n rec.onClick()\n }\n else if (rec.url) {\n window.location.href = rec.url\n }\n }\n }\n\n /**\n * Replay video\n */\n private replay(): void {\n this.hide()\n this.player?.seekTo(0)\n this.player?.play()\n this.onReplay?.()\n }\n\n /**\n * Share video\n */\n private share(): void {\n if (navigator.share) {\n navigator.share({\n title: document.title,\n url: window.location.href,\n })\n }\n else {\n // Fallback: copy to clipboard\n navigator.clipboard?.writeText(window.location.href)\n }\n }\n\n /**\n * Update recommendations\n */\n setRecommendations(recommendations: EndScreenRecommendation[]): void {\n this.config.recommendations = recommendations\n if (this.state.visible) {\n this.render()\n }\n }\n\n /**\n * Get state\n */\n getState(): EndScreenState {\n return { ...this.state }\n }\n\n /**\n * Set event handlers\n */\n on(event: 'recommendationClick', handler: (rec: EndScreenRecommendation) => void): void\n on(event: 'replay', handler: () => void): void\n on(event: 'autoplay', handler: (rec: EndScreenRecommendation) => void): void\n on(event: 'show' | 'hide', handler: () => void): void\n on(event: string, handler: (...args: any[]) => void): void {\n switch (event) {\n case 'recommendationClick':\n this.onRecommendationClick = handler\n break\n case 'replay':\n this.onReplay = handler\n break\n case 'autoplay':\n this.onAutoplay = handler\n break\n case 'show':\n this.onShow = handler\n break\n case 'hide':\n this.onHide = handler\n break\n }\n }\n\n /**\n * Destroy\n */\n destroy(): void {\n if (this.countdownTimer) {\n clearInterval(this.countdownTimer)\n }\n if (this.showTimer) {\n clearTimeout(this.showTimer)\n }\n\n this.unsubscribers.forEach(unsub => unsub())\n\n this.element?.remove()\n\n this.player = null\n this.container = null\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createEndScreen(config?: EndScreenConfig): EndScreenManager {\n return new EndScreenManager(config)\n}\n\n/**\n * End screen plugin for Player\n */\nexport function endScreenPlugin(config: EndScreenConfig): { name: string, install: (player: Player, container: HTMLElement) => () => void } {\n return {\n name: 'endScreen',\n\n install(player: Player, container: HTMLElement) {\n const endScreen = createEndScreen(config)\n endScreen.attach(player, container)\n\n // Expose on player\n ;(player as any).endScreen = endScreen\n\n return () => {\n endScreen.destroy()\n delete (player as any).endScreen\n }\n },\n }\n}\n",
|
|
25
|
+
"/**\n * Watermark Plugin\n *\n * Display watermarks on video playback.\n *\n * @module plugins/watermark\n */\n\nimport type { Player } from '../player'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type WatermarkPosition =\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'center-left'\n | 'center'\n | 'center-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right'\n\nexport interface WatermarkConfig {\n /** Image URL */\n image?: string\n /** Text content */\n text?: string\n /** HTML content */\n html?: string\n /** Position */\n position?: WatermarkPosition\n /** Opacity (0-1) */\n opacity?: number\n /** Size (CSS value) */\n size?: string\n /** Offset from edges */\n offset?: {\n x?: number | string\n y?: number | string\n }\n /** Link URL */\n url?: string\n /** Open link in new tab */\n newTab?: boolean\n /** Show only during playback */\n showOnlyDuringPlayback?: boolean\n /** Fade in duration (ms) */\n fadeIn?: number\n /** Fade out duration (ms) */\n fadeOut?: number\n /** Animation */\n animation?: WatermarkAnimation\n /** Custom styles */\n styles?: Partial<CSSStyleDeclaration>\n /** Rotate degrees */\n rotate?: number\n}\n\nexport interface WatermarkAnimation {\n /** Animation type */\n type: 'fade' | 'slide' | 'bounce' | 'pulse' | 'none'\n /** Animation duration (ms) */\n duration?: number\n /** Animation delay (ms) */\n delay?: number\n /** Repeat animation */\n repeat?: boolean\n}\n\nexport interface DynamicWatermark {\n /** User ID or session info */\n userId?: string\n /** Timestamp */\n showTimestamp?: boolean\n /** IP address (for forensic marking) */\n showIp?: boolean\n /** Custom text */\n customText?: string\n /** Update interval (ms) */\n updateInterval?: number\n /** Position randomization */\n randomizePosition?: boolean\n /** Move interval (ms) */\n moveInterval?: number\n}\n\n// =============================================================================\n// Watermark Manager\n// =============================================================================\n\nexport class WatermarkManager {\n private player: Player | null = null\n private container: HTMLElement | null = null\n private elements: Map<string, HTMLElement> = new Map()\n private dynamicTimers: Map<string, ReturnType<typeof setInterval>> = new Map()\n private unsubscribers: Array<() => void> = []\n\n constructor() {}\n\n /**\n * Attach to player\n */\n attach(player: Player, container: HTMLElement): void {\n this.player = player\n this.container = container\n }\n\n /**\n * Add a watermark\n */\n add(id: string, config: WatermarkConfig): HTMLElement {\n if (!this.container) {\n throw new Error('Watermark manager not attached to container')\n }\n\n // Remove existing watermark with same ID\n this.remove(id)\n\n const element = this.createElement(config)\n element.dataset.watermarkId = id\n\n this.elements.set(id, element)\n this.container.appendChild(element)\n\n // Handle playback visibility\n if (config.showOnlyDuringPlayback && this.player) {\n this.setupPlaybackVisibility(id, element)\n }\n\n // Apply animation\n if (config.animation && config.animation.type !== 'none') {\n this.applyAnimation(element, config.animation)\n }\n\n return element\n }\n\n /**\n * Create watermark element\n */\n private createElement(config: WatermarkConfig): HTMLElement {\n const wrapper = document.createElement('div')\n wrapper.className = 'tsvp-watermark'\n\n const positionStyles = this.getPositionStyles(config.position || 'bottom-right', config.offset)\n\n wrapper.style.cssText = `\n position: absolute;\n ${positionStyles}\n z-index: 25;\n pointer-events: ${config.url ? 'auto' : 'none'};\n opacity: ${config.opacity ?? 0.7};\n transition: opacity ${config.fadeIn || 300}ms ease;\n ${config.rotate ? `transform: rotate(${config.rotate}deg);` : ''}\n `\n\n // Apply custom styles\n if (config.styles) {\n Object.assign(wrapper.style, config.styles)\n }\n\n // Create content\n let content: HTMLElement\n\n if (config.html) {\n content = document.createElement('div')\n content.innerHTML = config.html\n }\n else if (config.image) {\n const img = document.createElement('img')\n img.src = config.image\n img.alt = 'Watermark'\n img.style.cssText = `\n display: block;\n max-width: 100%;\n height: auto;\n ${config.size ? `width: ${config.size};` : ''}\n `\n content = img\n }\n else if (config.text) {\n content = document.createElement('span')\n content.textContent = config.text\n content.style.cssText = `\n font-family: var(--tsvp-font-family, sans-serif);\n font-size: ${config.size || '14px'};\n color: rgba(255, 255, 255, 0.8);\n text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);\n `\n }\n else {\n content = document.createElement('span')\n }\n\n // Wrap in link if URL provided\n if (config.url) {\n const link = document.createElement('a')\n link.href = config.url\n link.target = config.newTab ? '_blank' : '_self'\n link.rel = config.newTab ? 'noopener noreferrer' : ''\n link.style.textDecoration = 'none'\n link.appendChild(content)\n wrapper.appendChild(link)\n }\n else {\n wrapper.appendChild(content)\n }\n\n return wrapper\n }\n\n /**\n * Get position styles\n */\n private getPositionStyles(\n position: WatermarkPosition,\n offset?: { x?: number | string; y?: number | string }\n ): string {\n const offsetX = typeof offset?.x === 'number' ? `${offset.x}px` : (offset?.x || '16px')\n const offsetY = typeof offset?.y === 'number' ? `${offset.y}px` : (offset?.y || '16px')\n\n const positions: Record<WatermarkPosition, string> = {\n 'top-left': `top: ${offsetY}; left: ${offsetX};`,\n 'top-center': `top: ${offsetY}; left: 50%; transform: translateX(-50%);`,\n 'top-right': `top: ${offsetY}; right: ${offsetX};`,\n 'center-left': `top: 50%; left: ${offsetX}; transform: translateY(-50%);`,\n 'center': `top: 50%; left: 50%; transform: translate(-50%, -50%);`,\n 'center-right': `top: 50%; right: ${offsetX}; transform: translateY(-50%);`,\n 'bottom-left': `bottom: ${offsetY}; left: ${offsetX};`,\n 'bottom-center': `bottom: ${offsetY}; left: 50%; transform: translateX(-50%);`,\n 'bottom-right': `bottom: ${offsetY}; right: ${offsetX};`,\n }\n\n return positions[position] || positions['bottom-right']\n }\n\n /**\n * Setup playback visibility\n */\n private setupPlaybackVisibility(_id: string, element: HTMLElement): void {\n if (!this.player) return\n\n element.style.opacity = '0'\n\n const handlePlay = () => {\n element.style.opacity = ''\n }\n\n const handlePause = () => {\n element.style.opacity = '0'\n }\n\n this.player.on('play', handlePlay)\n this.player.on('pause', handlePause)\n this.player.on('ended', handlePause)\n\n this.unsubscribers.push(() => {\n this.player?.off('play', handlePlay)\n this.player?.off('pause', handlePause)\n this.player?.off('ended', handlePause)\n })\n }\n\n /**\n * Apply animation to watermark\n */\n private applyAnimation(element: HTMLElement, animation: WatermarkAnimation): void {\n const duration = animation.duration || 1000\n const delay = animation.delay || 0\n const repeat = animation.repeat ? 'infinite' : '1'\n\n // Add keyframes if not exists\n if (!document.querySelector('#tsvp-watermark-animations')) {\n const style = document.createElement('style')\n style.id = 'tsvp-watermark-animations'\n style.textContent = `\n @keyframes tsvp-watermark-fade {\n 0%, 100% { opacity: 0.3; }\n 50% { opacity: 1; }\n }\n @keyframes tsvp-watermark-slide {\n 0% { transform: translateX(-10px); }\n 50% { transform: translateX(10px); }\n 100% { transform: translateX(-10px); }\n }\n @keyframes tsvp-watermark-bounce {\n 0%, 100% { transform: translateY(0); }\n 50% { transform: translateY(-10px); }\n }\n @keyframes tsvp-watermark-pulse {\n 0%, 100% { transform: scale(1); }\n 50% { transform: scale(1.1); }\n }\n `\n document.head.appendChild(style)\n }\n\n const animationName = `tsvp-watermark-${animation.type}`\n element.style.animation = `${animationName} ${duration}ms ease ${delay}ms ${repeat}`\n }\n\n /**\n * Add dynamic watermark (forensic marking)\n */\n addDynamic(id: string, config: DynamicWatermark, baseConfig: WatermarkConfig = {}): HTMLElement {\n const getText = (): string => {\n const parts: string[] = []\n\n if (config.userId) {\n parts.push(config.userId)\n }\n\n if (config.showTimestamp) {\n parts.push(new Date().toISOString())\n }\n\n if (config.customText) {\n parts.push(config.customText)\n }\n\n return parts.join(' | ')\n }\n\n const positions: WatermarkPosition[] = [\n 'top-left', 'top-right', 'bottom-left', 'bottom-right',\n 'center-left', 'center-right',\n ]\n\n let currentPosition = baseConfig.position || 'bottom-right'\n\n const element = this.add(id, {\n ...baseConfig,\n text: getText(),\n position: currentPosition,\n opacity: baseConfig.opacity ?? 0.3,\n })\n\n // Update text periodically\n if (config.updateInterval && config.updateInterval > 0) {\n const textTimer = setInterval(() => {\n const textEl = element.querySelector('span')\n if (textEl) {\n textEl.textContent = getText()\n }\n }, config.updateInterval)\n\n this.dynamicTimers.set(`${id}-text`, textTimer)\n }\n\n // Move position periodically\n if (config.randomizePosition && config.moveInterval && config.moveInterval > 0) {\n const moveTimer = setInterval(() => {\n const randomIndex = Math.floor(Math.random() * positions.length)\n currentPosition = positions[randomIndex]\n\n const positionStyles = this.getPositionStyles(currentPosition, baseConfig.offset)\n element.style.cssText = element.style.cssText.replace(\n /top:.*?;|bottom:.*?;|left:.*?;|right:.*?;|transform:.*?;/g,\n ''\n )\n element.style.cssText += positionStyles\n }, config.moveInterval)\n\n this.dynamicTimers.set(`${id}-move`, moveTimer)\n }\n\n return element\n }\n\n /**\n * Remove watermark\n */\n remove(id: string): void {\n const element = this.elements.get(id)\n if (element) {\n element.remove()\n this.elements.delete(id)\n }\n\n // Clear dynamic timers\n const textTimer = this.dynamicTimers.get(`${id}-text`)\n if (textTimer) {\n clearInterval(textTimer)\n this.dynamicTimers.delete(`${id}-text`)\n }\n\n const moveTimer = this.dynamicTimers.get(`${id}-move`)\n if (moveTimer) {\n clearInterval(moveTimer)\n this.dynamicTimers.delete(`${id}-move`)\n }\n }\n\n /**\n * Remove all watermarks\n */\n removeAll(): void {\n for (const id of this.elements.keys()) {\n this.remove(id)\n }\n }\n\n /**\n * Update watermark\n */\n update(id: string, config: Partial<WatermarkConfig>): void {\n const element = this.elements.get(id)\n if (!element) return\n\n if (config.opacity !== undefined) {\n element.style.opacity = String(config.opacity)\n }\n\n if (config.position) {\n const positionStyles = this.getPositionStyles(config.position, config.offset)\n element.style.cssText = element.style.cssText.replace(\n /top:.*?;|bottom:.*?;|left:.*?;|right:.*?;|transform:.*?;/g,\n ''\n )\n element.style.cssText += positionStyles\n }\n\n if (config.text) {\n const textEl = element.querySelector('span')\n if (textEl) {\n textEl.textContent = config.text\n }\n }\n\n if (config.image) {\n const imgEl = element.querySelector('img')\n if (imgEl) {\n imgEl.src = config.image\n }\n }\n }\n\n /**\n * Show watermark\n */\n show(id: string): void {\n const element = this.elements.get(id)\n if (element) {\n element.style.display = ''\n }\n }\n\n /**\n * Hide watermark\n */\n hide(id: string): void {\n const element = this.elements.get(id)\n if (element) {\n element.style.display = 'none'\n }\n }\n\n /**\n * Show all watermarks\n */\n showAll(): void {\n for (const id of this.elements.keys()) {\n this.show(id)\n }\n }\n\n /**\n * Hide all watermarks\n */\n hideAll(): void {\n for (const id of this.elements.keys()) {\n this.hide(id)\n }\n }\n\n /**\n * Get watermark element\n */\n get(id: string): HTMLElement | undefined {\n return this.elements.get(id)\n }\n\n /**\n * Get all watermark IDs\n */\n getAll(): string[] {\n return Array.from(this.elements.keys())\n }\n\n /**\n * Destroy manager\n */\n destroy(): void {\n this.removeAll()\n this.unsubscribers.forEach(unsub => unsub())\n this.player = null\n this.container = null\n }\n}\n\n// =============================================================================\n// Factory\n// =============================================================================\n\nexport function createWatermarkManager(): WatermarkManager {\n return new WatermarkManager()\n}\n\n/**\n * Watermark plugin for Player\n */\nexport function watermarkPlugin(watermarks: Array<{ id: string } & WatermarkConfig>): { name: string, install: (player: Player, container: HTMLElement) => () => void } {\n return {\n name: 'watermark',\n\n install(player: Player, container: HTMLElement): () => void {\n const manager = createWatermarkManager()\n manager.attach(player, container)\n\n // Add initial watermarks\n for (const { id, ...config } of watermarks) {\n manager.add(id, config)\n }\n\n // Expose on player\n ;(player as any).watermark = manager\n\n return () => {\n manager.destroy()\n delete (player as any).watermark\n }\n },\n }\n}\n",
|
|
26
|
+
"/**\n * ts-video-player\n *\n * A modern, framework-agnostic video player with deep stx integration.\n *\n * @module ts-video-player\n *\n * @example\n * ```typescript\n * import { createPlayer } from 'ts-video-player'\n *\n * const player = createPlayer('#video-container', {\n * src: 'https://example.com/video.mp4',\n * poster: '/poster.jpg',\n * autoplay: false,\n * controls: true,\n * })\n *\n * player.on('playing', () => console.log('Video is playing!'))\n * player.on('ended', () => console.log('Video ended'))\n *\n * // Control playback\n * await player.play()\n * player.pause()\n * player.seekTo(30)\n *\n * // Volume\n * player.setVolume(0.5)\n * player.toggleMute()\n *\n * // Fullscreen\n * await player.toggleFullscreen()\n * ```\n */\n\n// =============================================================================\n// Core\n// =============================================================================\n\nexport { Player, createPlayer, getPlayer } from './player'\n\n// =============================================================================\n// State Management\n// =============================================================================\n\nexport {\n StateStore,\n createSignal,\n computed,\n shallowEqual,\n createDefaultState,\n loadPersistedState,\n savePersistedState,\n createStorageSync,\n // Selectors\n selectBufferedAmount,\n selectProgress,\n selectRemainingTime,\n selectIsIdle,\n selectIsLoading,\n selectIsLive,\n selectIsDVR,\n selectCurrentQuality,\n selectCurrentTextTrack,\n selectCurrentAudioTrack,\n} from './core/state'\n\n// =============================================================================\n// Events\n// =============================================================================\n\nexport {\n EventEmitter,\n MediaEventsNormalizer,\n onFullscreenChange,\n onPiPChange,\n DEFAULT_KEYBOARD_SHORTCUTS,\n createKeyboardHandler,\n createActivityDetector,\n createGestureDetector,\n} from './core/events'\n\n// =============================================================================\n// Audio Gain\n// =============================================================================\n\nexport {\n AudioGain,\n createAudioGain,\n GAIN_PRESETS,\n GAIN_STEPS,\n} from './core/audio-gain'\n\nexport type { AudioGainAdapter } from './core/audio-gain'\n\n// =============================================================================\n// Text Tracks\n// =============================================================================\n\nexport {\n TextTrackList,\n TextTrackReadyState,\n CaptionRenderer,\n createTextTrackList,\n createCaptionRenderer,\n} from './core/text-tracks'\n\nexport type { TextTrackInit, VTTCueInit, CaptionRendererOptions } from './core/text-tracks'\n\n// =============================================================================\n// Themes\n// =============================================================================\n\nexport {\n defaultTheme,\n darkTheme,\n lightTheme,\n minimalTheme,\n netflixTheme,\n youtubeTheme,\n vimeoTheme,\n presetThemes,\n mergeThemes,\n generateThemeCSS,\n createThemeStyleElement,\n applyTheme,\n injectThemeStyles,\n} from './core/themes'\n\nexport type {\n Theme,\n ThemeColors,\n ThemeTypography,\n ThemeSpacing,\n ThemeBorderRadius,\n ThemeShadows,\n ThemeTransitions,\n ThemeSlider,\n ThemeControls,\n ThemeMenu,\n ThemeCaptions,\n ThemeThumbnails,\n ThemeLive,\n PresetThemeName,\n} from './core/themes'\n\n// =============================================================================\n// Thumbnails / Seek Preview\n// =============================================================================\n\nexport {\n Thumbnails,\n createThumbnails,\n parseThumbnailVTT,\n generateSpriteCues,\n} from './core/thumbnails'\n\nexport type {\n ThumbnailSprite,\n ThumbnailCue,\n ThumbnailsConfig,\n} from './core/thumbnails'\n\n// =============================================================================\n// Internationalization (i18n)\n// =============================================================================\n\nexport {\n I18n,\n createI18n,\n getI18n,\n setI18n,\n t,\n englishStrings,\n spanishStrings,\n frenchStrings,\n germanStrings,\n japaneseStrings,\n chineseStrings,\n defaultTranslations,\n} from './core/i18n'\n\nexport type {\n TranslationStrings,\n TranslationKey,\n I18nConfig,\n} from './core/i18n'\n\n// =============================================================================\n// Live Streaming\n// =============================================================================\n\nexport {\n LiveIndicator,\n SeekToLiveButton,\n LiveController,\n createLiveIndicator,\n createSeekToLiveButton,\n createLiveController,\n} from './core/live'\n\nexport type {\n LiveConfig,\n LiveState,\n} from './core/live'\n\n// =============================================================================\n// Remote Playback (AirPlay / Google Cast)\n// =============================================================================\n\nexport {\n RemotePlaybackController,\n AirPlayButton,\n GoogleCastButton,\n createRemotePlaybackController,\n createAirPlayButton,\n createGoogleCastButton,\n isAirPlaySupported,\n isAirPlayAvailable,\n showAirPlayPicker,\n isAirPlaying,\n isGoogleCastSupported,\n loadGoogleCastSDK,\n initializeGoogleCast,\n requestCastSession,\n endCastSession,\n isCasting,\n getCastSession,\n castMedia,\n} from './core/remote-playback'\n\nexport type {\n RemotePlaybackType,\n RemotePlaybackState,\n RemoteDevice,\n RemotePlaybackConfig,\n} from './core/remote-playback'\n\n// =============================================================================\n// Chapters\n// =============================================================================\n\nexport {\n ChaptersManager,\n ChapterMenu,\n createChaptersManager,\n createChapterMenu,\n parseChaptersVTT,\n} from './core/chapters'\n\nexport type {\n Chapter,\n ChaptersConfig,\n} from './core/chapters'\n\n// =============================================================================\n// Spinner / Buffering Indicator\n// =============================================================================\n\nexport {\n Spinner,\n createSpinner,\n SPINNER_STYLES,\n} from './core/spinner'\n\nexport type { SpinnerConfig } from './core/spinner'\n\n// =============================================================================\n// Caption Styling\n// =============================================================================\n\nexport {\n CaptionStyleManager,\n CaptionSettingsMenu,\n createCaptionStyleManager,\n createCaptionSettingsMenu,\n defaultCaptionStyles,\n captionPresets,\n} from './core/caption-styles'\n\nexport type {\n CaptionStyleOptions,\n CaptionStylePreset,\n} from './core/caption-styles'\n\n// =============================================================================\n// Tooltips\n// =============================================================================\n\nexport {\n Tooltip,\n TooltipManager,\n createTooltip,\n createTooltipManager,\n TOOLTIP_STYLES,\n} from './core/tooltips'\n\nexport type {\n TooltipPlacement,\n TooltipConfig,\n} from './core/tooltips'\n\n// =============================================================================\n// Error Overlay\n// =============================================================================\n\nexport {\n ErrorOverlay,\n createErrorOverlay,\n ERROR_OVERLAY_STYLES,\n} from './core/error-overlay'\n\nexport type { ErrorOverlayConfig } from './core/error-overlay'\n\n// =============================================================================\n// Settings Menu\n// =============================================================================\n\nexport {\n SettingsMenu,\n createSettingsMenu,\n} from './core/settings-menu'\n\nexport type {\n SettingsMenuConfig,\n SettingsMenuState,\n} from './core/settings-menu'\n\n// =============================================================================\n// Providers\n// =============================================================================\n\nexport {\n // Base\n BaseProvider,\n // HTML5\n HTML5Provider,\n html5Loader,\n isHTML5Source,\n // YouTube\n YouTubeProvider,\n youtubeLoader,\n isYouTubeSource,\n extractYouTubeId,\n // Vimeo\n VimeoProvider,\n vimeoLoader,\n isVimeoSource,\n extractVimeoId,\n extractVimeoHash,\n // HLS\n HLSProvider,\n hlsLoader,\n isHLSSource,\n isNativeHLSSupported,\n // DASH\n DASHProvider,\n dashLoader,\n isDASHSource,\n // Registry\n defaultLoaders,\n findLoader,\n detectMediaType,\n getPreconnectHints,\n} from './providers'\n\nexport type { DRMConfig, DASHProviderConfig } from './providers'\n\n// =============================================================================\n// Layouts\n// =============================================================================\n\nexport {\n LayoutManager,\n createLayoutManager,\n getLayout,\n registerLayout,\n layouts,\n defaultLayout,\n minimalLayout,\n cinemaLayout,\n youtubeLayout,\n vimeoLayout,\n audioLayout,\n} from './layouts'\n\nexport type { LayoutConfig, LayoutName } from './layouts'\n\n// =============================================================================\n// UI Components\n// =============================================================================\n\nexport {\n // Components\n BaseComponent,\n PlayButton,\n MuteButton,\n FullscreenButton,\n PiPButton,\n TimeDisplay,\n Slider,\n ProgressBar,\n VolumeSlider,\n Menu,\n // Utilities\n formatTime,\n UI_STYLES,\n} from './ui/components'\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type {\n // Media source types\n MediaType,\n StreamType,\n ProviderType,\n MediaSource,\n YouTubeSource,\n VimeoSource,\n HLSSource,\n DASHSource,\n Src,\n\n // Player state\n PreloadStrategy,\n CrossOrigin,\n LoadingState,\n PlaybackState,\n TimeRange,\n VideoQuality,\n AudioTrack,\n TextTrack,\n TextTrackCue,\n PlayerState,\n\n // Player options\n PlayerOptions,\n ControlsConfig,\n CustomControl,\n KeyboardConfig,\n StorageConfig,\n\n // Provider\n Provider,\n ProviderLoader,\n\n // Events\n MediaError,\n ProviderEventMap,\n PlayerEventMap,\n\n // UI\n UIComponent,\n TooltipOptions,\n MenuItem,\n SliderOptions,\n\n // stx integration\n VideoComponentProps,\n VideoDirectiveOptions,\n VideoRenderResult,\n} from './types'\n\n// =============================================================================\n// stx Integration\n// =============================================================================\n\nexport {\n renderVideoComponent,\n parseVideoComponent,\n createVideoDirective,\n videoDirective,\n registerVideoDirectives,\n} from './stx'\n\n// =============================================================================\n// Plugins\n// =============================================================================\n\nexport {\n // Analytics\n VideoAnalytics,\n createVideoAnalytics,\n analyticsPlugin,\n // Ads (VAST/VPAID)\n VASTParser,\n AdsManager,\n createAdsManager,\n adsPlugin,\n // Skip Segments\n SkipSegmentsManager,\n createSkipSegments,\n skipSegmentsPlugin,\n // End Screen\n EndScreenManager,\n createEndScreen,\n endScreenPlugin,\n // Watermarks\n WatermarkManager,\n createWatermarkManager,\n watermarkPlugin,\n} from './plugins'\n\nexport type {\n // Analytics types\n VideoEventName,\n VideoAnalyticsConfig,\n // Ads types\n Ad,\n AdMediaFile,\n AdBreak,\n AdConfig,\n AdState,\n // Skip Segments types\n SegmentType,\n SkipSegment,\n SkipSegmentsConfig,\n SkipSegmentsState,\n // End Screen types\n EndScreenRecommendation,\n EndScreenConfig,\n EndScreenState,\n // Watermark types\n WatermarkPosition,\n WatermarkConfig,\n WatermarkAnimation,\n DynamicWatermark,\n} from './plugins'\n\n// =============================================================================\n// Default Export\n// =============================================================================\n\nimport { createPlayer } from './player'\nexport default createPlayer\n\n// =============================================================================\n// Feature Availability Detection\n// =============================================================================\n\nexport {\n isIOSSafari,\n isSafari,\n isStandalonePWA,\n detectVolumeAvailability,\n probeVolumeAvailability,\n detectFullscreenAvailability,\n isVideoOnlyFullscreen,\n detectPipAvailability,\n detectAllFeatures,\n enterFullscreen,\n exitFullscreen,\n enterPiP,\n exitPiP,\n} from './core/features'\n\n// =============================================================================\n// Custom Elements\n// =============================================================================\n\nexport {\n VideoPlayerElement,\n VideoSkin,\n MediaPlayButton,\n MediaMuteButton,\n MediaFullscreenButton,\n MediaPipButton,\n MediaTimeDisplay,\n MediaTimeGroup,\n MediaTimeSeparator,\n MediaPreviewTime,\n MediaProgressBar,\n MediaVolumeSlider,\n MediaSettingsMenu,\n registerElements,\n formatTime as formatElementTime,\n formatTimePhrase,\n toISODuration,\n} from './elements'\n\n// =============================================================================\n// Composable Features\n// =============================================================================\n\nexport {\n playback,\n volume as volumeFeature,\n fullscreen as fullscreenFeature,\n pip as pipFeature,\n captions as captionsFeature,\n quality as qualityFeature,\n keyboard as keyboardFeature,\n gestures as gesturesFeature,\n videoFeatures,\n audioFeatures,\n minimalFeatures,\n createComposablePlayer,\n} from './features'\n\nexport type { Feature, FeatureContext, ComposablePlayer, ComposablePlayerOptions } from './features'\n\n// =============================================================================\n// Media Session\n// =============================================================================\n\nexport {\n isMediaSessionSupported,\n createMediaSession,\n updateMetadata,\n} from './core/media-session'\n\nexport type { MediaSessionOptions } from './core/media-session'\n\n// =============================================================================\n// Orientation Lock\n// =============================================================================\n\nexport {\n isOrientationLockSupported,\n lockOrientation,\n unlockOrientation,\n} from './core/orientation'\n\n// =============================================================================\n// Popover Positioning\n// =============================================================================\n\nexport {\n computePosition,\n applyPosition,\n} from './core/popover'\n\nexport type { Rect, Placement, PositionOptions, PositionResult } from './core/popover'\n"
|
|
27
|
+
],
|
|
28
|
+
"mappings": "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",
|
|
29
|
+
"debugId": "BDAF4DF6162B920764756E2164756E21",
|
|
30
|
+
"names": []
|
|
31
|
+
}
|