diff --git a/make_post_sell/templates/product.j2 b/make_post_sell/templates/product.j2 index 56c4124..d4b8447 100644 --- a/make_post_sell/templates/product.j2 +++ b/make_post_sell/templates/product.j2 @@ -37,11 +37,14 @@ {% set video_extensions = ["mp4", "avi", "mov", "wmv", "flv", "mkv", "webm", "m4v"] %} {% if signed_get_object_url and product.extensions.get("product") in video_extensions %} {% if request.popout_player_enabled and product.visibility == 1 and "preview" in product.extensions %} - - -
-
-
click ▶ to play in pop-out player
+
+ + +
+
+ +
+
click ▶ for pop-out or 📺 for picture-in-picture
{% else %} @@ -179,6 +182,191 @@ function openMediaPlayer(productId) { const features = 'width=800,height=600,menubar=no,toolbar=no,location=no,status=no'; window.open(`/player/${productId}`, 'mediaPlayer', features); } + +// Open directly in Document Picture-in-Picture mode +async function openPipPlayer(productId) { + console.log('Opening PiP for product:', productId); + + // Check for Document PiP support + if (!('documentPictureInPicture' in window)) { + alert('Document Picture-in-Picture is not supported in this browser.\n\nPlease use Chrome 111+ or Edge 111+.\n\nFalling back to pop-up window.'); + openMediaPlayer(productId); + return; + } + + try { + // Fetch the player page to get video URL and metadata + console.log('Fetching player page...'); + const response = await fetch(`/player/${productId}`); + const html = await response.text(); + + // Parse HTML to extract video URL and metadata + const parser = new DOMParser(); + const doc = parser.parseFromString(html, 'text/html'); + + // Find video element and get source + const videoEl = doc.querySelector('video'); + if (!videoEl || !videoEl.src) { + console.error('No video element or source found'); + alert('Could not find video. Opening in pop-up window instead.'); + openMediaPlayer(productId); + return; + } + + const videoSrc = videoEl.src; + console.log('Video source:', videoSrc); + + // Extract player data from script tag + const scriptContent = doc.querySelector('script:not([src])').textContent; + const playerDataMatch = scriptContent.match(/window\.playerData\s*=\s*({[^}]+})/); + let playerData = { prevProductId: null, nextProductId: null }; + if (playerDataMatch) { + try { + // Clean up the matched content and parse it + const dataStr = playerDataMatch[1] + .replace(/"/g, '"') + .replace(/'/g, '"') + .replace(/(\w+):/g, '"$1":') + .replace(/null/g, 'null'); + playerData = JSON.parse(dataStr); + console.log('Parsed player data:', playerData); + } catch (e) { + console.warn('Could not parse player data:', e); + } + } + + // Get product title + const titleEl = doc.querySelector('.product-title'); + const productTitle = titleEl ? titleEl.textContent : 'Video'; + + // Open Document PiP window with custom controls + console.log('Opening Document PiP window...'); + const pipWindow = await window.documentPictureInPicture.requestWindow({ + width: 640, + height: 420 // 360 for 16:9 video + 60 for controls + }); + + console.log('Document PiP window opened'); + + // Add styles to PiP window + const pipStyle = pipWindow.document.createElement('style'); + pipStyle.textContent = ` + body { + margin: 0; + padding: 0; + background: #000; + overflow: hidden; + font-family: sans-serif; + } + .pip-container { + display: grid; + grid-template-rows: 1fr auto; + width: 100vw; + height: 100vh; + } + .pip-video { + width: 100%; + height: 100%; + object-fit: contain; + } + .pip-controls { + display: grid; + grid-template-columns: auto 1fr auto; + gap: 0.5rem; + padding: 0.5rem; + background: rgba(0,0,0,0.9); + align-items: center; + } + .pip-controls button { + background: rgba(255,255,255,0.2); + border: none; + color: white; + padding: 0.5rem 1rem; + border-radius: 4px; + cursor: pointer; + font-size: 0.9rem; + } + .pip-controls button:hover:not(:disabled) { + background: rgba(255,255,255,0.4); + } + .pip-controls button:disabled { + opacity: 0.3; + cursor: not-allowed; + } + .pip-title { + color: white; + font-size: 0.9rem; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + `; + pipWindow.document.head.appendChild(pipStyle); + + // Build PiP content + const container = pipWindow.document.createElement('div'); + container.className = 'pip-container'; + + // Create video element + const video = pipWindow.document.createElement('video'); + video.className = 'pip-video'; + video.src = videoSrc; + video.controls = true; + video.autoplay = true; + + // Create controls + const controls = pipWindow.document.createElement('div'); + controls.className = 'pip-controls'; + + // Previous button + const prevBtn = pipWindow.document.createElement('button'); + prevBtn.textContent = 'â—€ Prev'; + prevBtn.disabled = !playerData.prevProductId; + if (playerData.prevProductId) { + prevBtn.onclick = () => { + window.location.href = `/product/${playerData.prevProductId}`; + }; + } + + // Title + const title = pipWindow.document.createElement('div'); + title.className = 'pip-title'; + title.textContent = productTitle; + + // Next button + const nextBtn = pipWindow.document.createElement('button'); + nextBtn.textContent = 'Next â–¶'; + nextBtn.disabled = !playerData.nextProductId; + if (playerData.nextProductId) { + nextBtn.onclick = () => { + window.location.href = `/product/${playerData.nextProductId}`; + }; + } + + controls.appendChild(prevBtn); + controls.appendChild(title); + controls.appendChild(nextBtn); + + container.appendChild(video); + container.appendChild(controls); + pipWindow.document.body.appendChild(container); + + // Auto-advance when video ends + video.addEventListener('ended', () => { + if (playerData.nextProductId) { + window.location.href = `/product/${playerData.nextProductId}`; + } + }); + + console.log('Document PiP setup complete'); + + } catch (err) { + console.error('Failed to open Document PiP:', err); + alert('Failed to open Picture-in-Picture: ' + err.message + '\n\nOpening in pop-up window instead.'); + openMediaPlayer(productId); + } +} {%- endblock -%}