fix: implemented safari webkit support for fullscreen and PiP

This commit is contained in:
Trevor Mears
2026-01-21 20:36:34 -08:00
parent 6f966f1095
commit ea4a0a4577
4 changed files with 49 additions and 14 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "nodecast-tv", "name": "nodecast-tv",
"version": "2.1.2", "version": "2.1.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "nodecast-tv", "name": "nodecast-tv",
"version": "2.1.2", "version": "2.1.3",
"description": "Modern IPTV Player", "description": "Modern IPTV Player",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"main": "server/index.js", "main": "server/index.js",
+24 -7
View File
@@ -349,30 +349,47 @@ class VideoPlayer {
} }
/** /**
* Toggle fullscreen mode * Toggle fullscreen mode (cross-browser including Safari)
*/ */
toggleFullscreen() { toggleFullscreen() {
if (document.fullscreenElement) { const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement;
if (isFullscreen) {
if (document.exitFullscreen) {
document.exitFullscreen(); document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
} else { } else {
this.container.requestFullscreen().catch(err => { const element = this.container;
console.error('Fullscreen error:', err); if (element.requestFullscreen) {
}); element.requestFullscreen().catch(err => console.error('Fullscreen error:', err));
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (this.video.webkitEnterFullscreen) {
// iOS Safari: use native video fullscreen
this.video.webkitEnterFullscreen();
}
} }
} }
/** /**
* Toggle Picture-in-Picture mode * Toggle Picture-in-Picture mode (cross-browser including Safari)
*/ */
async togglePictureInPicture() { async togglePictureInPicture() {
try { try {
// Standard PiP API (Chrome, Edge, Firefox)
if (document.pictureInPictureElement) { if (document.pictureInPictureElement) {
await document.exitPictureInPicture(); await document.exitPictureInPicture();
} else if (document.pictureInPictureEnabled && this.video.readyState >= 2) { } else if (document.pictureInPictureEnabled && this.video.readyState >= 2) {
await this.video.requestPictureInPicture(); await this.video.requestPictureInPicture();
} }
// Safari fallback using webkitPresentationMode
else if (typeof this.video.webkitSetPresentationMode === 'function') {
const mode = this.video.webkitPresentationMode;
this.video.webkitSetPresentationMode(mode === 'picture-in-picture' ? 'inline' : 'picture-in-picture');
}
} catch (err) { } catch (err) {
// Silently fail - Firefox users can use native PiP button
if (err.name !== 'NotAllowedError') { if (err.name !== 'NotAllowedError') {
console.error('Picture-in-Picture error:', err); console.error('Picture-in-Picture error:', err);
} }
+22 -4
View File
@@ -634,22 +634,40 @@ class WatchPage {
toggleFullscreen() { toggleFullscreen() {
const container = document.querySelector('.watch-video-section'); const container = document.querySelector('.watch-video-section');
if (!document.fullscreenElement) { const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement;
container?.requestFullscreen?.() || container?.webkitRequestFullscreen?.();
if (isFullscreen) {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
} else { } else {
document.exitFullscreen?.() || document.webkitExitFullscreen?.(); if (container?.requestFullscreen) {
container.requestFullscreen();
} else if (container?.webkitRequestFullscreen) {
container.webkitRequestFullscreen();
} else if (this.video?.webkitEnterFullscreen) {
// iOS Safari: use native video fullscreen
this.video.webkitEnterFullscreen();
}
} }
} }
async togglePictureInPicture() { async togglePictureInPicture() {
try { try {
// Standard PiP API (Chrome, Edge, Firefox)
if (document.pictureInPictureElement) { if (document.pictureInPictureElement) {
await document.exitPictureInPicture(); await document.exitPictureInPicture();
} else if (document.pictureInPictureEnabled && this.video.readyState >= 2) { } else if (document.pictureInPictureEnabled && this.video.readyState >= 2) {
await this.video.requestPictureInPicture(); await this.video.requestPictureInPicture();
} }
// Safari fallback using webkitPresentationMode
else if (typeof this.video.webkitSetPresentationMode === 'function') {
const mode = this.video.webkitPresentationMode;
this.video.webkitSetPresentationMode(mode === 'picture-in-picture' ? 'inline' : 'picture-in-picture');
}
} catch (err) { } catch (err) {
// Silently fail - Firefox users can use native PiP button
if (err.name !== 'NotAllowedError') { if (err.name !== 'NotAllowedError') {
console.error('Picture-in-Picture error:', err); console.error('Picture-in-Picture error:', err);
} }