diff --git a/package-lock.json b/package-lock.json index 6d10e76..b643eb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "nodecast-tv", - "version": "2.1.2", + "version": "2.1.3", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index c9a2951..9cf4a5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodecast-tv", - "version": "2.1.2", + "version": "2.1.3", "description": "Modern IPTV Player", "license": "GPL-3.0-only", "main": "server/index.js", diff --git a/public/js/components/VideoPlayer.js b/public/js/components/VideoPlayer.js index 2bc4d6f..f21ec1b 100644 --- a/public/js/components/VideoPlayer.js +++ b/public/js/components/VideoPlayer.js @@ -349,30 +349,47 @@ class VideoPlayer { } /** - * Toggle fullscreen mode + * Toggle fullscreen mode (cross-browser including Safari) */ toggleFullscreen() { - if (document.fullscreenElement) { - document.exitFullscreen(); + const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement; + + if (isFullscreen) { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } } else { - this.container.requestFullscreen().catch(err => { - console.error('Fullscreen error:', err); - }); + const element = this.container; + 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() { try { + // Standard PiP API (Chrome, Edge, Firefox) if (document.pictureInPictureElement) { await document.exitPictureInPicture(); } else if (document.pictureInPictureEnabled && this.video.readyState >= 2) { 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) { - // Silently fail - Firefox users can use native PiP button if (err.name !== 'NotAllowedError') { console.error('Picture-in-Picture error:', err); } diff --git a/public/js/pages/WatchPage.js b/public/js/pages/WatchPage.js index 6e8e25e..50d07d9 100644 --- a/public/js/pages/WatchPage.js +++ b/public/js/pages/WatchPage.js @@ -634,22 +634,40 @@ class WatchPage { toggleFullscreen() { const container = document.querySelector('.watch-video-section'); - if (!document.fullscreenElement) { - container?.requestFullscreen?.() || container?.webkitRequestFullscreen?.(); + const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement; + + if (isFullscreen) { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } } 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() { try { + // Standard PiP API (Chrome, Edge, Firefox) if (document.pictureInPictureElement) { await document.exitPictureInPicture(); } else if (document.pictureInPictureEnabled && this.video.readyState >= 2) { 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) { - // Silently fail - Firefox users can use native PiP button if (err.name !== 'NotAllowedError') { console.error('Picture-in-Picture error:', err); }