/** * Video Player Hydration for MkDocs * * Automatically converts .video-block placeholders into actual video players * when landing pages with video blocks are exported to MkDocs. * * Supports both standard HTML5 video and advanced player with reactions. */ (function() { 'use strict'; // Configuration const MEDIA_API_URL = window.MEDIA_API_URL || 'http://localhost:4100'; const PUBLIC_URL = window.PUBLIC_URL || 'http://localhost:3000'; /** * Fetch video metadata from Media API */ async function fetchVideoMetadata(videoId) { try { const response = await fetch(`${MEDIA_API_URL}/api/videos/${videoId}/metadata`); if (!response.ok) { throw new Error(`Failed to fetch video ${videoId}: ${response.statusText}`); } return await response.json(); } catch (error) { console.error(`Error fetching video ${videoId} metadata:`, error); return null; } } /** * Create standard HTML5 video player */ function createStandardPlayer(metadata, options) { const video = document.createElement('video'); video.src = `${MEDIA_API_URL}/api/videos/${metadata.id}/stream`; video.poster = `${MEDIA_API_URL}/api/videos/${metadata.id}/thumbnail`; video.controls = options.controls !== false; video.autoplay = options.autoplay === true; video.loop = options.loop === true; video.muted = options.muted === true; video.style.width = options.width || '100%'; video.style.height = options.height || 'auto'; video.style.maxWidth = '100%'; video.style.borderRadius = '8px'; video.style.display = 'block'; // Add title attribute for accessibility video.setAttribute('title', metadata.title); video.setAttribute('aria-label', metadata.title); return video; } /** * Create advanced player container with reactions * Note: Full reaction functionality requires React components, * so this is a simplified version for MkDocs */ function createAdvancedPlayer(metadata, options) { const container = document.createElement('div'); container.className = 'advanced-video-container'; container.style.maxWidth = options.width || '100%'; container.style.margin = '0 auto'; // Video player const video = createStandardPlayer(metadata, options); container.appendChild(video); // Reaction placeholder (simplified for MkDocs) if (options.showReactions !== false) { const reactionBar = document.createElement('div'); reactionBar.className = 'video-reactions'; reactionBar.style.marginTop = '12px'; reactionBar.style.padding = '12px'; reactionBar.style.background = '#f5f5f5'; reactionBar.style.borderRadius = '8px'; reactionBar.style.textAlign = 'center'; reactionBar.style.fontSize = '14px'; reactionBar.style.color = '#666'; reactionBar.innerHTML = `
View full player with reactions →
`; container.appendChild(reactionBar); } // Video metadata const metaInfo = document.createElement('div'); metaInfo.className = 'video-meta'; metaInfo.style.marginTop = '8px'; metaInfo.style.fontSize = '13px'; metaInfo.style.color = '#999'; metaInfo.innerHTML = ` ${escapeHtml(metadata.title)} ${metadata.durationSeconds ? `• ${formatDuration(metadata.durationSeconds)}` : ''} ${metadata.width && metadata.height ? `• ${metadata.width}×${metadata.height}` : ''} `; container.appendChild(metaInfo); return container; } /** * Render a video block */ async function renderVideoBlock(blockElement) { const videoId = parseInt(blockElement.dataset.videoId, 10); const playerType = blockElement.dataset.playerType || 'standard'; const width = blockElement.dataset.width || '100%'; const height = blockElement.dataset.height || 'auto'; const autoplay = blockElement.dataset.autoplay === 'true'; const controls = blockElement.dataset.controls !== 'false'; const showReactions = blockElement.dataset.showReactions !== 'false'; if (!videoId || isNaN(videoId)) { console.warn('Invalid video ID in video block:', blockElement); showError(blockElement, 'Invalid video ID'); return; } // Show loading state blockElement.innerHTML = '${escapeHtml(message)}