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,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/providers/youtube.ts", "../../src/stx/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * ts-video-player YouTube Provider\n *\n * YouTube embed provider using the iframe API.\n *\n * @module providers/youtube\n */\n\nimport type { ProviderType, Src, YouTubeSource, PlayerOptions, VideoQuality } from '../types'\nimport { BaseProvider } from './base'\n\n// =============================================================================\n// YouTube URL Parsing\n// =============================================================================\n\n/**\n * YouTube URL patterns\n */\nconst YOUTUBE_PATTERNS = [\n /(?:youtube\\.com\\/watch\\?v=|youtu\\.be\\/|youtube\\.com\\/embed\\/)([a-zA-Z0-9_-]{11})/,\n /youtube\\.com\\/v\\/([a-zA-Z0-9_-]{11})/,\n /youtube\\.com\\/shorts\\/([a-zA-Z0-9_-]{11})/,\n]\n\n/**\n * Extract YouTube video ID from URL\n */\nexport function extractYouTubeId(url: string): string | null {\n for (const pattern of YOUTUBE_PATTERNS) {\n const match = url.match(pattern)\n if (match) return match[1]\n }\n return null\n}\n\n/**\n * Check if source is a YouTube source\n */\nexport function isYouTubeSource(src: Src): boolean {\n if (typeof src === 'string') {\n return extractYouTubeId(src) !== null\n }\n\n if (typeof src === 'object' && 'type' in src) {\n return (src as YouTubeSource).type === 'youtube'\n }\n\n return false\n}\n\n/**\n * Parse YouTube source\n */\nfunction parseYouTubeSource(src: Src): { videoId: string; startTime?: number; endTime?: number } | null {\n if (typeof src === 'string') {\n const videoId = extractYouTubeId(src)\n if (!videoId) return null\n\n // Parse start/end time from URL params\n const url = new URL(src, 'https://youtube.com')\n const start = url.searchParams.get('t') || url.searchParams.get('start')\n const end = url.searchParams.get('end')\n\n return {\n videoId,\n startTime: start ? parseTime(start) : undefined,\n endTime: end ? parseTime(end) : undefined,\n }\n }\n\n if (typeof src === 'object' && 'type' in src && (src as YouTubeSource).type === 'youtube') {\n const ytSrc = src as YouTubeSource\n return {\n videoId: ytSrc.videoId || extractYouTubeId(ytSrc.src) || '',\n startTime: ytSrc.startTime,\n endTime: ytSrc.endTime,\n }\n }\n\n return null\n}\n\n/**\n * Parse time string (e.g., \"1m30s\", \"90\", \"1:30\")\n */\nfunction parseTime(time: string): number {\n // Already a number in seconds\n if (/^\\d+$/.test(time)) return parseInt(time, 10)\n\n // Format: 1m30s\n const match1 = time.match(/(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?/)\n if (match1) {\n const h = parseInt(match1[1] || '0', 10)\n const m = parseInt(match1[2] || '0', 10)\n const s = parseInt(match1[3] || '0', 10)\n return h * 3600 + m * 60 + s\n }\n\n // Format: 1:30 or 1:30:00\n const parts = time.split(':').map((p) => parseInt(p, 10))\n if (parts.length === 2) return parts[0] * 60 + parts[1]\n if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2]\n\n return 0\n}\n\n// =============================================================================\n// YouTube API Types\n// =============================================================================\n\ninterface YTPlayer {\n playVideo(): void\n pauseVideo(): void\n stopVideo(): void\n seekTo(seconds: number, allowSeekAhead: boolean): void\n getCurrentTime(): number\n getDuration(): number\n setVolume(volume: number): void\n getVolume(): number\n mute(): void\n unMute(): void\n isMuted(): boolean\n setPlaybackRate(rate: number): void\n getPlaybackRate(): number\n getAvailablePlaybackRates(): number[]\n setPlaybackQuality(quality: string): void\n getPlaybackQuality(): string\n getAvailableQualityLevels(): string[]\n loadVideoById(options: { videoId: string; startSeconds?: number; endSeconds?: number }): void\n cueVideoById(options: { videoId: string; startSeconds?: number; endSeconds?: number }): void\n getIframe(): HTMLIFrameElement\n getPlayerState(): number\n destroy(): void\n}\n\ninterface YTPlayerEvent {\n target: YTPlayer\n data: number\n}\n\ndeclare global {\n interface Window {\n YT?: {\n Player: new (\n elementId: string | HTMLElement,\n options: {\n videoId?: string\n width?: number | string\n height?: number | string\n playerVars?: Record<string, string | number>\n events?: {\n onReady?: (event: YTPlayerEvent) => void\n onStateChange?: (event: YTPlayerEvent) => void\n onError?: (event: YTPlayerEvent) => void\n onPlaybackQualityChange?: (event: YTPlayerEvent) => void\n onPlaybackRateChange?: (event: YTPlayerEvent) => void\n }\n },\n ) => YTPlayer\n PlayerState: {\n UNSTARTED: -1\n ENDED: 0\n PLAYING: 1\n PAUSED: 2\n BUFFERING: 3\n CUED: 5\n }\n }\n onYouTubeIframeAPIReady?: () => void\n }\n}\n\n// =============================================================================\n// YouTube Provider\n// =============================================================================\n\n/**\n * YouTube embed provider\n */\nexport class YouTubeProvider extends BaseProvider {\n readonly name = 'youtube'\n readonly type: ProviderType = 'youtube'\n\n private player: YTPlayer | null = null\n private iframe: HTMLIFrameElement | null = null\n private pendingVideoId: string | null = null\n private startTime?: number\n private endTime?: number\n private timeUpdateInterval: ReturnType<typeof setInterval> | null = null\n private lastState = -1\n\n get mediaElement(): HTMLIFrameElement | null {\n return this.iframe\n }\n\n // === Lifecycle ===\n\n protected async initialize(): Promise<void> {\n if (!this.container) {\n throw new Error('Container element is required')\n }\n\n // Load YouTube API\n await this.loadYouTubeAPI()\n\n // Create container div for YouTube player\n const playerDiv = document.createElement('div')\n playerDiv.id = `ts-video-player-yt-${Date.now()}`\n playerDiv.className = 'ts-video-player__youtube'\n this.container.appendChild(playerDiv)\n\n // Build player vars\n const playerVars: Record<string, string | number> = {\n autoplay: this.options.autoplay ? 1 : 0,\n controls: 0, // We use our own controls\n disablekb: 1, // Disable keyboard (we handle it)\n enablejsapi: 1,\n iv_load_policy: 3, // Disable annotations\n modestbranding: 1,\n playsinline: this.options.playsinline ? 1 : 0,\n rel: 0, // Don't show related videos\n origin: window.location.origin,\n }\n\n // Apply custom YouTube params\n if (this.options.youtubeParams) {\n Object.assign(playerVars, this.options.youtubeParams)\n }\n\n // Use privacy-enhanced mode\n playerVars.host = 'https://www.youtube-nocookie.com'\n\n // Create player\n this.player = new window.YT!.Player(playerDiv.id, {\n width: '100%',\n height: '100%',\n playerVars,\n events: {\n onReady: () => this.onReady(),\n onStateChange: (e) => this.onStateChange(e),\n onError: (e) => this.onError(e),\n onPlaybackQualityChange: () => this.onQualityChange(),\n onPlaybackRateChange: () => this.onRateChange(),\n },\n })\n\n // Get iframe reference\n this.iframe = this.player.getIframe()\n this.iframe.className = 'ts-video-player__media'\n }\n\n private async loadYouTubeAPI(): Promise<void> {\n if (window.YT?.Player) return\n\n return new Promise((resolve) => {\n // Check if script already loading\n if (document.querySelector('script[src*=\"youtube.com/iframe_api\"]')) {\n const check = () => {\n if (window.YT?.Player) {\n resolve()\n } else {\n setTimeout(check, 100)\n }\n }\n check()\n return\n }\n\n // Load script\n const script = document.createElement('script')\n script.src = 'https://www.youtube.com/iframe_api'\n script.async = true\n\n window.onYouTubeIframeAPIReady = () => {\n resolve()\n }\n\n document.head.appendChild(script)\n })\n }\n\n protected cleanup(): void {\n if (this.timeUpdateInterval) {\n clearInterval(this.timeUpdateInterval)\n this.timeUpdateInterval = null\n }\n\n this.player?.destroy()\n this.player = null\n this.iframe = null\n }\n\n // === Event Handlers ===\n\n private onReady(): void {\n // Start time update polling\n this.timeUpdateInterval = setInterval(() => {\n if (this.player && this.lastState === 1) {\n this.events.emit('timeupdate', this.getCurrentTime())\n }\n }, 250)\n\n // Emit initial state\n this.events.emit('loadedmetadata')\n this.events.emit('canplay')\n\n // Load pending video if any\n if (this.pendingVideoId) {\n this.loadVideo(this.pendingVideoId, this.startTime, this.endTime)\n this.pendingVideoId = null\n }\n }\n\n private onStateChange(event: YTPlayerEvent): void {\n const state = event.data\n this.lastState = state\n\n const PlayerState = window.YT!.PlayerState\n\n switch (state) {\n case PlayerState.UNSTARTED:\n this.events.emit('loadstart')\n break\n case PlayerState.PLAYING:\n this.events.emit('play')\n this.events.emit('playing')\n this.events.emit('statechange', { paused: false, playing: true, waiting: false })\n break\n case PlayerState.PAUSED:\n this.events.emit('pause')\n this.events.emit('statechange', { paused: true, playing: false })\n break\n case PlayerState.BUFFERING:\n this.events.emit('waiting')\n this.events.emit('statechange', { waiting: true })\n break\n case PlayerState.ENDED:\n this.events.emit('ended')\n this.events.emit('statechange', { ended: true, playing: false })\n break\n case PlayerState.CUED:\n this.events.emit('loadeddata')\n this.events.emit('durationchange', this.getDuration())\n break\n }\n }\n\n private onError(event: YTPlayerEvent): void {\n const errorMessages: Record<number, string> = {\n 2: 'Invalid video ID',\n 5: 'HTML5 player error',\n 100: 'Video not found or removed',\n 101: 'Video cannot be embedded',\n 150: 'Video cannot be embedded',\n }\n\n this.emitError(event.data, errorMessages[event.data] || 'Unknown YouTube error')\n }\n\n private onQualityChange(): void {\n const quality = this.player?.getPlaybackQuality() || 'auto'\n const qualities = this.getQualities()\n this.events.emit('qualitychange', qualities.find((q) => q.id === quality) || null)\n }\n\n private onRateChange(): void {\n this.events.emit('ratechange', this.getPlaybackRate())\n }\n\n // === Loading ===\n\n canPlay(src: Src): boolean {\n return isYouTubeSource(src)\n }\n\n async load(src: Src): Promise<void> {\n const parsed = parseYouTubeSource(src)\n if (!parsed) {\n this.emitError(2, 'Invalid YouTube source')\n return\n }\n\n this.startTime = parsed.startTime\n this.endTime = parsed.endTime\n\n if (!this.player || !this._ready) {\n this.pendingVideoId = parsed.videoId\n return\n }\n\n this.loadVideo(parsed.videoId, parsed.startTime, parsed.endTime)\n }\n\n private loadVideo(videoId: string, startTime?: number, endTime?: number): void {\n if (!this.player) return\n\n if (this.options.autoplay) {\n this.player.loadVideoById({\n videoId,\n startSeconds: startTime,\n endSeconds: endTime,\n })\n } else {\n this.player.cueVideoById({\n videoId,\n startSeconds: startTime,\n endSeconds: endTime,\n })\n }\n }\n\n // === Playback ===\n\n async play(): Promise<void> {\n this.player?.playVideo()\n }\n\n pause(): void {\n this.player?.pauseVideo()\n }\n\n stop(): void {\n this.player?.stopVideo()\n }\n\n // === Seeking ===\n\n seekTo(time: number): void {\n this.player?.seekTo(time, true)\n this.events.emit('seeking')\n // YouTube doesn't emit seeked, so we fake it\n setTimeout(() => this.events.emit('seeked'), 100)\n }\n\n getCurrentTime(): number {\n return this.player?.getCurrentTime() || 0\n }\n\n getDuration(): number {\n return this.player?.getDuration() || 0\n }\n\n // === Volume ===\n\n setVolume(volume: number): void {\n this.player?.setVolume(Math.round(volume * 100))\n this.events.emit('volumechange', volume, this.getMuted())\n }\n\n getVolume(): number {\n return (this.player?.getVolume() || 100) / 100\n }\n\n setMuted(muted: boolean): void {\n if (muted) {\n this.player?.mute()\n } else {\n this.player?.unMute()\n }\n this.events.emit('volumechange', this.getVolume(), muted)\n }\n\n getMuted(): boolean {\n return this.player?.isMuted() || false\n }\n\n // === Playback Rate ===\n\n setPlaybackRate(rate: number): void {\n this.player?.setPlaybackRate(rate)\n }\n\n getPlaybackRate(): number {\n return this.player?.getPlaybackRate() || 1\n }\n\n // === Quality ===\n\n getQualities(): VideoQuality[] {\n const levels = this.player?.getAvailableQualityLevels() || []\n const heights: Record<string, number> = {\n highres: 2160,\n hd2160: 2160,\n hd1440: 1440,\n hd1080: 1080,\n hd720: 720,\n large: 480,\n medium: 360,\n small: 240,\n tiny: 144,\n }\n\n const current = this.player?.getPlaybackQuality()\n\n return levels.map((level) => ({\n id: level,\n width: Math.round((heights[level] || 480) * (16 / 9)),\n height: heights[level] || 480,\n bitrate: 0,\n selected: level === current,\n }))\n }\n\n setQuality(quality: VideoQuality | 'auto'): void {\n if (quality === 'auto') {\n this.player?.setPlaybackQuality('default')\n } else {\n this.player?.setPlaybackQuality(quality.id)\n }\n }\n}\n\n/**\n * YouTube Provider Loader\n */\nexport const youtubeLoader = {\n name: 'youtube',\n type: 'youtube' as ProviderType,\n\n canPlay(src: Src): boolean {\n return isYouTubeSource(src)\n },\n\n mediaType(): 'video' {\n return 'video'\n },\n\n preconnect(): string[] {\n return [\n 'https://www.youtube-nocookie.com',\n 'https://www.youtube.com',\n 'https://i.ytimg.com',\n 'https://s.ytimg.com',\n ]\n },\n\n async load(container: HTMLElement, options: PlayerOptions): Promise<YouTubeProvider> {\n const provider = new YouTubeProvider()\n await provider.setup(container, options)\n return provider\n },\n}\n",
|
|
6
|
+
"/**\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"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "CC15566753F52D2564756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|