Fix safe login submission, mobile fullscreen, and OIDC discovery
This commit is contained in:
@ -1107,7 +1107,8 @@
|
||||
<script src="/js/components/VodDuration.js?v=1"></script>
|
||||
<script src="/js/components/CaptionTracks.js?v=2"></script>
|
||||
<script src="/js/components/HlsAudioRecovery.js?v=1"></script>
|
||||
<script src="/js/components/VideoPlayer.js?v=6"></script>
|
||||
<script src="/js/components/PlayerFullscreen.js?v=1"></script>
|
||||
<script src="/js/components/VideoPlayer.js?v=7"></script>
|
||||
<script src="/js/components/ChannelList.js?v=4"></script>
|
||||
<script src="/js/components/SourceManager.js?v=3"></script>
|
||||
<script src="/js/components/EpgGuide.js?v=2"></script>
|
||||
@ -1117,7 +1118,7 @@
|
||||
<script src="/js/pages/MoviesPage.js?v=4"></script>
|
||||
<script src="/js/pages/SeriesPage.js?v=1"></script>
|
||||
<script src="/js/pages/Settings.js?v=2"></script>
|
||||
<script src="/js/pages/WatchPage.js?v=6"></script>
|
||||
<script src="/js/pages/WatchPage.js?v=7"></script>
|
||||
<script src="/js/app.js?v=5"></script>
|
||||
|
||||
</body>
|
||||
|
||||
31
public/js/components/PlayerFullscreen.js
Normal file
31
public/js/components/PlayerFullscreen.js
Normal file
@ -0,0 +1,31 @@
|
||||
/** Invoke fullscreen synchronously within the click/keyboard gesture, then handle its promise. */
|
||||
const PlayerFullscreen = {
|
||||
async toggle(video, container, doc = document) {
|
||||
try {
|
||||
if (doc.fullscreenElement && doc.exitFullscreen) {
|
||||
await doc.exitFullscreen();
|
||||
} else if (doc.webkitFullscreenElement && doc.webkitExitFullscreen) {
|
||||
await doc.webkitExitFullscreen();
|
||||
} else if (video?.webkitDisplayingFullscreen && video.webkitExitFullscreen) {
|
||||
await video.webkitExitFullscreen();
|
||||
} else {
|
||||
const target = container || video;
|
||||
// Some iPhones expose element methods even though element fullscreen is disabled.
|
||||
const standard = doc.fullscreenEnabled === true ||
|
||||
(doc.fullscreenEnabled === undefined && !video?.webkitEnterFullscreen);
|
||||
const prefixed = doc.webkitFullscreenEnabled === true ||
|
||||
(doc.webkitFullscreenEnabled === undefined && doc.fullscreenEnabled !== false && !video?.webkitEnterFullscreen);
|
||||
if (standard && target?.requestFullscreen) await target.requestFullscreen();
|
||||
else if (prefixed && target?.webkitRequestFullscreen) await target.webkitRequestFullscreen();
|
||||
else if (video?.webkitEnterFullscreen) await video.webkitEnterFullscreen();
|
||||
else return false;
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Fullscreen failed:', error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (typeof window !== 'undefined') window.PlayerFullscreen = PlayerFullscreen;
|
||||
if (typeof module !== 'undefined') module.exports = PlayerFullscreen;
|
||||
@ -422,25 +422,7 @@ class VideoPlayer {
|
||||
* Toggle fullscreen mode (cross-browser including Safari)
|
||||
*/
|
||||
toggleFullscreen() {
|
||||
const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement;
|
||||
|
||||
if (isFullscreen) {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
return PlayerFullscreen.toggle(this.video, this.container, document);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1306,17 +1288,6 @@ class VideoPlayer {
|
||||
window.app.channelList.selectChannel({ channelId: channels[nextIdx].id });
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle fullscreen
|
||||
*/
|
||||
toggleFullscreen() {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else if (this.container) {
|
||||
this.container.requestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
async zapBeforeStreaming(channel, streamUrl, signal) {
|
||||
if (!channel?.zapBeforeStreaming) return;
|
||||
if (!channel?.streamId || !streamUrl) return;
|
||||
|
||||
@ -756,25 +756,7 @@ class WatchPage {
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
const container = document.querySelector('.watch-video-section');
|
||||
const isFullscreen = document.fullscreenElement || document.webkitFullscreenElement;
|
||||
|
||||
if (isFullscreen) {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
document.webkitExitFullscreen();
|
||||
}
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
return PlayerFullscreen.toggle(this.video, document.querySelector('.watch-video-section'), document);
|
||||
}
|
||||
|
||||
async togglePictureInPicture() {
|
||||
|
||||
@ -180,7 +180,7 @@
|
||||
Welcome! Please create your admin account to get started.
|
||||
</div>
|
||||
|
||||
<form id="login-form">
|
||||
<form id="login-form" method="post" action="/api/auth/login">
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
<input type="text" id="username" name="username" required autocomplete="username"
|
||||
@ -193,8 +193,9 @@
|
||||
minlength="6" placeholder="Enter your password">
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-login" id="submit-btn">Sign In</button>
|
||||
<button type="submit" class="btn-login" id="submit-btn" disabled>Sign In</button>
|
||||
</form>
|
||||
<noscript><p class="hint">JavaScript is required to sign in.</p></noscript>
|
||||
|
||||
<div class="sso-divider" style="text-align: center; margin: 20px 0; position: relative;">
|
||||
<span
|
||||
@ -248,7 +249,9 @@
|
||||
async function checkSetupRequired() {
|
||||
try {
|
||||
const response = await fetch('/api/auth/setup-required');
|
||||
if (!response.ok) throw new Error('Sign-in status unavailable');
|
||||
const data = await response.json();
|
||||
if (typeof data.setupRequired !== 'boolean') throw new Error('Invalid sign-in status');
|
||||
|
||||
if (data.setupRequired) {
|
||||
isSetupMode = true;
|
||||
@ -260,8 +263,12 @@
|
||||
document.querySelector('.sso-divider').style.display = 'none';
|
||||
document.getElementById('btn-sso-login').style.display = 'none';
|
||||
}
|
||||
document.getElementById('submit-btn').disabled = false;
|
||||
} catch (err) {
|
||||
console.error('Error checking setup status:', err);
|
||||
const message = document.getElementById('error-message');
|
||||
message.textContent = 'Could not load sign-in status. Reload the page to try again.';
|
||||
message.classList.add('show');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user