diff --git a/make_post_sell/templates/player.j2 b/make_post_sell/templates/player.j2
index 75f431f..8ffe70d 100644
--- a/make_post_sell/templates/player.j2
+++ b/make_post_sell/templates/player.j2
@@ -230,7 +230,7 @@
{% if media_type == 'video' %}
-
+
{% endif %}
@@ -260,13 +260,209 @@
window.open(window.playerData.productUrl, '_blank');
}
- // Toggle native Picture-in-Picture (true always-on-top via browser)
- function togglePip() {
+ // Document Picture-in-Picture with custom controls
+ let pipWindow = null;
+
+ async function togglePip() {
const media = document.getElementById('media');
if (!media || media.tagName !== 'VIDEO') {
return;
}
+ // Check for Document Picture-in-Picture API support
+ if ('documentPictureInPicture' in window) {
+ if (pipWindow) {
+ // Close existing PiP window
+ pipWindow.close();
+ pipWindow = null;
+ updatePipButtonState(false);
+ return;
+ }
+
+ try {
+ // Calculate aspect ratio from video
+ const aspectRatio = media.videoWidth / media.videoHeight;
+ let width = 640;
+ let height = Math.round(width / aspectRatio);
+
+ // Constrain to reasonable sizes
+ if (height > 480) {
+ height = 480;
+ width = Math.round(height * aspectRatio);
+ }
+
+ // Open Document PiP window with custom content
+ pipWindow = await window.documentPictureInPicture.requestWindow({
+ width: width,
+ height: height + 60 // Extra space for controls
+ });
+
+ updatePipButtonState(true);
+
+ // Copy stylesheets to PiP window
+ const stylesheets = Array.from(document.styleSheets);
+ stylesheets.forEach(stylesheet => {
+ try {
+ const cssRules = Array.from(stylesheet.cssRules);
+ const style = pipWindow.document.createElement('style');
+ cssRules.forEach(rule => {
+ style.textContent += rule.cssText;
+ });
+ pipWindow.document.head.appendChild(style);
+ } catch (e) {
+ // External stylesheet, link it
+ const link = pipWindow.document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = stylesheet.href;
+ pipWindow.document.head.appendChild(link);
+ }
+ });
+
+ // Add PiP-specific styles
+ const pipStyle = pipWindow.document.createElement('style');
+ pipStyle.textContent = `
+ body {
+ margin: 0;
+ padding: 0;
+ background: #000;
+ overflow: hidden;
+ }
+ .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 with navigation controls
+ const container = pipWindow.document.createElement('div');
+ container.className = 'pip-container';
+
+ // Clone video element
+ const pipVideo = pipWindow.document.createElement('video');
+ pipVideo.className = 'pip-video';
+ pipVideo.src = media.src;
+ pipVideo.currentTime = media.currentTime;
+ pipVideo.controls = true;
+ pipVideo.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 = !window.playerData.prevProductId;
+ prevBtn.onclick = () => {
+ if (window.playerData.prevProductId) {
+ window.location.href = '/player/' + window.playerData.prevProductId;
+ }
+ };
+
+ // Title
+ const title = pipWindow.document.createElement('div');
+ title.className = 'pip-title';
+ title.textContent = '{{ product.title }}';
+
+ // Next button
+ const nextBtn = pipWindow.document.createElement('button');
+ nextBtn.textContent = 'Next â–¶';
+ nextBtn.disabled = !window.playerData.nextProductId;
+ nextBtn.onclick = () => {
+ if (window.playerData.nextProductId) {
+ window.location.href = '/player/' + window.playerData.nextProductId;
+ }
+ };
+
+ controls.appendChild(prevBtn);
+ controls.appendChild(title);
+ controls.appendChild(nextBtn);
+
+ container.appendChild(pipVideo);
+ container.appendChild(controls);
+ pipWindow.document.body.appendChild(container);
+
+ // Sync playback between main window and PiP
+ pipVideo.addEventListener('play', () => media.play());
+ pipVideo.addEventListener('pause', () => media.pause());
+ pipVideo.addEventListener('seeked', () => {
+ media.currentTime = pipVideo.currentTime;
+ });
+
+ // When PiP video ends, navigate to next
+ pipVideo.addEventListener('ended', () => {
+ if (window.playerData.nextProductId) {
+ window.location.href = '/player/' + window.playerData.nextProductId;
+ }
+ });
+
+ // Handle PiP window close
+ pipWindow.addEventListener('pagehide', () => {
+ pipWindow = null;
+ updatePipButtonState(false);
+ // Sync playback state back to main window
+ if (!pipVideo.paused) {
+ media.currentTime = pipVideo.currentTime;
+ media.play();
+ }
+ });
+
+ // Pause main video (playback continues in PiP)
+ media.pause();
+
+ } catch (err) {
+ console.error('Failed to open Document PiP:', err);
+ // Fallback to regular PiP
+ fallbackToPip(media);
+ }
+ } else {
+ // Fallback to regular PiP for unsupported browsers
+ fallbackToPip(media);
+ }
+ }
+
+ // Fallback to regular Picture-in-Picture API
+ function fallbackToPip(media) {
if (document.pictureInPictureElement) {
document.exitPictureInPicture().catch(err => {
console.error('Failed to exit PiP:', err);
@@ -279,19 +475,29 @@
}
// Update PiP button state
- document.addEventListener('enterpictureinpicture', () => {
+ function updatePipButtonState(isActive) {
const pipBtn = document.getElementById('pipBtn');
if (pipBtn) {
- pipBtn.classList.add('active');
- pipBtn.textContent = '📺 Exit PiP';
+ if (isActive) {
+ pipBtn.classList.add('active');
+ pipBtn.textContent = '📺 Exit PiP';
+ } else {
+ pipBtn.classList.remove('active');
+ pipBtn.textContent = '📺 PiP';
+ }
+ }
+ }
+
+ // Listen for regular PiP events (fallback mode)
+ document.addEventListener('enterpictureinpicture', () => {
+ if (!pipWindow) {
+ updatePipButtonState(true);
}
});
document.addEventListener('leavepictureinpicture', () => {
- const pipBtn = document.getElementById('pipBtn');
- if (pipBtn) {
- pipBtn.classList.remove('active');
- pipBtn.textContent = '📺 PiP';
+ if (!pipWindow) {
+ updatePipButtonState(false);
}
});