Add mobile support and responsive player controls

- Mobile devices open player in new tab (better than PiP)
- Add playsInline attribute for iOS autoplay support
- Responsive controls: hide PiP/auto-resize/always-on-top on mobile
- Implement togglePip function for desktop PiP button
- Optimize button sizes and spacing for touch screens
- Detect mobile via user agent and fallback gracefully
This commit is contained in:
russell@unturf.com 2026-01-21 10:47:57 -05:00
parent a6c76456d4
commit 962a89c3a6
4 changed files with 89 additions and 4 deletions

View file

@ -41,6 +41,26 @@
}
};
/**
* Toggle Picture-in-Picture mode
*/
window.togglePip = async function() {
if (!document.pictureInPictureEnabled || !media || media.tagName !== 'VIDEO') {
alert('Picture-in-Picture is not supported for this media');
return;
}
try {
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
} else {
await media.requestPictureInPicture();
}
} catch (err) {
console.error('Error toggling PiP:', err);
}
};
/**
* Toggle fullscreen mode
*/

View file

@ -128,7 +128,16 @@ async function loadMedia(mediaElement, productId) {
}
async function openMediaPlayer(productId) {
// Fetch player data to get media URL and navigation info
// Mobile detection
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// On mobile, open in new tab (works better than PiP)
if (isMobile) {
window.open(`/player/${productId}`, '_blank');
return;
}
// Desktop: Fetch player data to get media URL and navigation info
try {
const response = await fetch(`/player/${productId}`);
const html = await response.text();
@ -154,6 +163,7 @@ async function openMediaPlayer(productId) {
media.style.zIndex = '9999';
media.controls = true;
media.autoplay = true;
media.playsInline = true; // Important for iOS
document.body.appendChild(media);
// Extract playerData from script

View file

@ -40,6 +40,46 @@
display: block;
}
/* Mobile optimizations */
@media (max-width: 600px) {
.controls-grid {
padding: 1rem;
gap: 0.5rem;
font-size: 0.9rem;
grid-template-columns: 1fr 1fr 1fr;
}
.controls-grid button {
padding: 0.6rem 0.8rem;
font-size: 0.9rem;
min-width: 80px;
}
.controls-grid button.nav-btn,
.controls-grid button.play-btn {
font-size: 1.3rem;
padding: 0.8rem;
}
.product-title {
font-size: 1rem;
padding: 0.4rem 0.8rem;
}
/* Hide desktop-only buttons on mobile */
#pipBtn,
#autoResizeBtn,
#alwaysOnTopBtn {
display: none;
}
/* Adjust grid for mobile - only show essential buttons */
.controls-grid {
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto auto;
}
}
/* Player controls overlay - centered grid */
.player-controls {
grid-area: media;
@ -137,11 +177,11 @@
<body>
<div class="player-container" id="playerContainer">
{% if media_type == 'video' %}
<video id="media" src="{{ presigned_url }}" autoplay controls>
<video id="media" src="{{ presigned_url }}" autoplay controls playsinline>
Your browser does not support the video tag.
</video>
{% elif media_type == 'audio' %}
<audio id="media" src="{{ presigned_url }}" autoplay controls>
<audio id="media" src="{{ presigned_url }}" autoplay controls playsinline>
Your browser does not support the audio tag.
</audio>
{% elif media_type == 'image' %}
@ -170,7 +210,12 @@
<!-- Row 2: Features -->
<button id="alwaysOnTopBtn" onclick="toggleAlwaysOnTop()" title="Always on Top (T)">📌 On Top</button>
<button id="fullscreenBtn" onclick="toggleFullscreen()" title="Fullscreen (F)">⛶ Full</button>
<button id="pipBtn" onclick="togglePip()" title="Picture-in-Picture (P)">📺 PiP</button>
<!-- Row 3: More options -->
<button id="autoResizeBtn" onclick="toggleAutoResize()" title="Auto-Resize Window (R)">↔ Resize</button>
<button onclick="window.close()" title="Close Window (ESC)">✕ Close</button>
<div></div><!-- Empty cell for grid alignment -->
</div>
</div>
</div>

View file

@ -206,7 +206,16 @@ async function loadMedia(mediaElement, productId) {
}
async function openMediaPlayer(productId) {
// Fetch player data to get media URL and navigation info
// Mobile detection
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// On mobile, open in new tab (works better than PiP)
if (isMobile) {
window.open(`/player/${productId}`, '_blank');
return;
}
// Desktop: Fetch player data to get media URL and navigation info
try {
const response = await fetch(`/player/${productId}`);
const html = await response.text();
@ -232,6 +241,7 @@ async function openMediaPlayer(productId) {
media.style.zIndex = '9999';
media.controls = true;
media.autoplay = true;
media.playsInline = true; // Important for iOS
document.body.appendChild(media);
// Extract playerData from script