Merge pull request #16 from quoije/cursor/player-page-styling-and-links-1590
Player page styling and links
This commit was merged in pull request #16.
This commit is contained in:
@@ -117,7 +117,15 @@ window.onload = async function () {
|
||||
const isChecked = watchedMoviesInYear.some(watchedMovie => watchedMovie.imdb_id === movie.imdb_id);
|
||||
const watchedMovie = watchedMoviesInYear.find(wm => wm.imdb_id === movie.imdb_id);
|
||||
const watchedDate = watchedMovie ? new Date(watchedMovie.watchedDate).toLocaleString() : '';
|
||||
const playerUrl = movie && movie._id ? `/player.html?id=${encodeURIComponent(movie._id)}` : (movie.vod_link || '#');
|
||||
const hasVideoSrc = !!(movie && typeof movie.video_src === 'string' && movie.video_src.trim());
|
||||
const hasEmbedSrc = !!(movie && typeof movie.embed_src === 'string' && movie.embed_src.trim());
|
||||
const hasNewPlayerSource = hasVideoSrc || hasEmbedSrc;
|
||||
const hasLegacy = !!(movie && typeof movie.vod_link === 'string' && movie.vod_link.trim());
|
||||
|
||||
// If the movie only has legacy `vod_link`, skip the player entirely and open the original URL in a new tab.
|
||||
const playerUrl = (movie && movie._id && hasNewPlayerSource)
|
||||
? `/player.html?id=${encodeURIComponent(movie._id)}`
|
||||
: (hasLegacy ? movie.vod_link : (movie && movie._id ? `/player.html?id=${encodeURIComponent(movie._id)}` : '#'));
|
||||
|
||||
const movieDiv = document.createElement('div');
|
||||
movieDiv.classList.add('col-md-4', 'mb-4', 'movie-card');
|
||||
|
||||
+97
-16
@@ -11,9 +11,24 @@ function showAlert(message) {
|
||||
const el = document.getElementById('alert');
|
||||
if (!el) return;
|
||||
el.textContent = message;
|
||||
// Ensure error styling (danger) when using the simple API.
|
||||
el.classList.remove('alert-primary', 'alert-secondary', 'alert-success', 'alert-danger', 'alert-warning', 'alert-info', 'alert-light', 'alert-dark');
|
||||
el.classList.add('alert-danger');
|
||||
el.classList.remove('d-none');
|
||||
}
|
||||
|
||||
function showAlertVariant(message, variant) {
|
||||
const el = document.getElementById('alert');
|
||||
if (!el) return;
|
||||
el.textContent = message;
|
||||
el.classList.remove('d-none');
|
||||
|
||||
// Reset any prior bootstrap alert-* variants
|
||||
el.classList.remove('alert-primary', 'alert-secondary', 'alert-success', 'alert-danger', 'alert-warning', 'alert-info', 'alert-light', 'alert-dark');
|
||||
const v = String(variant || 'danger').trim().toLowerCase();
|
||||
el.classList.add(`alert-${v || 'danger'}`);
|
||||
}
|
||||
|
||||
function setSourceLabel(text) {
|
||||
const el = document.getElementById('source-label');
|
||||
if (!el) return;
|
||||
@@ -140,6 +155,33 @@ function showEmbedPlayer(embedUrl) {
|
||||
}
|
||||
}
|
||||
|
||||
function hideAllPlayers() {
|
||||
const videoEl = document.getElementById('video');
|
||||
const iframeEl = document.getElementById('embed');
|
||||
|
||||
if (videoEl) {
|
||||
try { videoEl.pause(); } catch (_) {}
|
||||
videoEl.classList.add('d-none');
|
||||
try { videoEl.removeAttribute('src'); videoEl.load(); } catch (_) {}
|
||||
}
|
||||
if (iframeEl) {
|
||||
iframeEl.classList.add('d-none');
|
||||
iframeEl.removeAttribute('src');
|
||||
}
|
||||
}
|
||||
|
||||
function setOpenOriginal(raw) {
|
||||
const openOriginal = document.getElementById('open-original');
|
||||
if (!openOriginal) return;
|
||||
if (raw) {
|
||||
openOriginal.href = raw;
|
||||
openOriginal.classList.remove('d-none');
|
||||
} else {
|
||||
openOriginal.removeAttribute('href');
|
||||
openOriginal.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
async function playVodLink(vodLink) {
|
||||
const raw = normalizeVodLink(vodLink);
|
||||
if (!raw) {
|
||||
@@ -149,11 +191,7 @@ async function playVodLink(vodLink) {
|
||||
}
|
||||
|
||||
// "Open original" button
|
||||
const openOriginal = document.getElementById('open-original');
|
||||
if (openOriginal) {
|
||||
openOriginal.href = raw;
|
||||
openOriginal.classList.remove('d-none');
|
||||
}
|
||||
setOpenOriginal(raw);
|
||||
|
||||
// Prefer known embed providers
|
||||
const yt = toYoutubeEmbed(raw);
|
||||
@@ -226,16 +264,27 @@ async function playVodLink(vodLink) {
|
||||
showEmbedPlayer(raw);
|
||||
}
|
||||
|
||||
function cleanUrlValue(v) {
|
||||
const s = typeof v === 'string' ? v.trim() : '';
|
||||
return s ? s : null;
|
||||
}
|
||||
|
||||
function resolveMovieSource(movie) {
|
||||
const mode = String(movie?.player_mode || 'auto').toLowerCase();
|
||||
const legacy = movie?.vod_link;
|
||||
const video = movie?.video_src;
|
||||
const embed = movie?.embed_src;
|
||||
const legacy = cleanUrlValue(movie?.vod_link);
|
||||
const video = cleanUrlValue(movie?.video_src);
|
||||
const embed = cleanUrlValue(movie?.embed_src);
|
||||
|
||||
if (mode === 'video') return video || legacy || embed || null;
|
||||
if (mode === 'embed') return embed || legacy || video || null;
|
||||
// auto
|
||||
return video || embed || legacy || null;
|
||||
// Legacy should never embed in the player (open in new tab only).
|
||||
// So we only pick legacy if there is no other option, and mark it.
|
||||
let picked = null;
|
||||
if (mode === 'video') picked = video || null;
|
||||
else if (mode === 'embed') picked = embed || null;
|
||||
else picked = video || embed || null; // auto
|
||||
|
||||
if (picked) return { src: picked, isLegacy: false };
|
||||
if (legacy) return { src: legacy, isLegacy: true };
|
||||
return { src: null, isLegacy: false };
|
||||
}
|
||||
|
||||
window.onload = async function () {
|
||||
@@ -258,8 +307,20 @@ window.onload = async function () {
|
||||
window.location.href = '/change-password.html';
|
||||
return;
|
||||
}
|
||||
const nameEl = document.getElementById('player-user-name');
|
||||
const nameEl = document.getElementById('user-name');
|
||||
if (nameEl) nameEl.textContent = decoded.name || '';
|
||||
if (decoded.admin) {
|
||||
const adminLink = document.getElementById('admin-control-link');
|
||||
if (adminLink) adminLink.classList.remove('d-none');
|
||||
}
|
||||
|
||||
const logoffBtn = document.getElementById('log-off');
|
||||
if (logoffBtn) {
|
||||
logoffBtn.addEventListener('click', function () {
|
||||
localStorage.removeItem('auth_token');
|
||||
window.location.href = '/';
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
localStorage.removeItem('auth_token');
|
||||
window.location.href = '/';
|
||||
@@ -273,7 +334,10 @@ window.onload = async function () {
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/movies/${encodeURIComponent(id)}`, { method: 'GET' });
|
||||
const res = await fetch(`/api/movies/${encodeURIComponent(id)}`, {
|
||||
method: 'GET',
|
||||
headers: token ? { 'Authorization': `Bearer ${token}` } : undefined,
|
||||
});
|
||||
const movie = await res.json().catch(() => ({}));
|
||||
if (!res.ok) {
|
||||
showAlert(movie?.message || movie?.error || `Failed to load movie (${res.status}).`);
|
||||
@@ -281,8 +345,25 @@ window.onload = async function () {
|
||||
}
|
||||
|
||||
setHeader(movie);
|
||||
const src = resolveMovieSource(movie);
|
||||
await playVodLink(src);
|
||||
const resolved = resolveMovieSource(movie);
|
||||
if (!resolved?.src) {
|
||||
setOpenOriginal(null);
|
||||
setSourceLabel('—');
|
||||
hideAllPlayers();
|
||||
showAlert('No playable source configured for this movie.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolved.isLegacy) {
|
||||
// Legacy source: never embed it in the player.
|
||||
setOpenOriginal(resolved.src);
|
||||
setSourceLabel('Legacy link (opens in new tab)');
|
||||
hideAllPlayers();
|
||||
showAlertVariant('Legacy source: use “Open original” to watch in a new tab.', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
await playVodLink(resolved.src);
|
||||
} catch (err) {
|
||||
showAlert(err.message || 'Network error.');
|
||||
}
|
||||
|
||||
+40
-7
@@ -7,6 +7,24 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="./css/app.css" rel="stylesheet">
|
||||
<style>
|
||||
.navbar-custom {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
.navbar-custom .navbar-brand,
|
||||
.navbar-custom .nav-link {
|
||||
color: white;
|
||||
}
|
||||
.navbar-custom .nav-link:hover {
|
||||
color: #ddd;
|
||||
}
|
||||
.navbar-toggler {
|
||||
border-color: white;
|
||||
}
|
||||
.navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='white' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.player-wrap {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
@@ -43,18 +61,32 @@
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<!-- Black banner with buttons and user info -->
|
||||
<nav class="navbar navbar-expand-lg navbar-custom">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/movies.html">Pool Oscars</a>
|
||||
<div class="d-flex ms-auto align-items-center gap-2">
|
||||
<span id="player-user-name" class="navbar-text text-white-50"></span>
|
||||
<a id="open-original" class="btn btn-outline-light btn-sm d-none" target="_blank" rel="noopener noreferrer">Open original</a>
|
||||
<a class="btn btn-outline-light btn-sm" href="/movies.html">Back to movies</a>
|
||||
<a class="navbar-brand" style="margin-left: -7px;" href="movies.html">
|
||||
<img src="img/logo_w.png" style="max-width: 220px; height: 70px; object-fit: contain;">
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<div class="navbar-nav">
|
||||
<a href="movies.html" class="nav-link btn btn-outline-light me-2">Films</a>
|
||||
<a href="checklist.html" class="nav-link btn btn-outline-light me-2">Checklist</a>
|
||||
<a href="user-stat.html" class="nav-link btn btn-outline-light me-2">Statistiques</a>
|
||||
<a id="admin-control-link" href="control.html" class="nav-link btn btn-warning me-2 d-none">Admin</a>
|
||||
</div>
|
||||
<div class="d-flex ms-auto mt-2 mt-lg-0 align-items-center gap-2">
|
||||
<a id="open-original" class="btn btn-outline-light btn-sm d-none" target="_blank" rel="noopener noreferrer">Open original</a>
|
||||
<span id="user-name" class="navbar-text me-1"></span>
|
||||
<button id="log-off" class="btn btn-danger btn-sm">Déconnexion</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container py-4 player-wrap">
|
||||
<div class="container mt-5 pb-4 player-wrap">
|
||||
<div class="mb-3">
|
||||
<h2 id="movie-title" class="mb-1">Loading…</h2>
|
||||
<div class="meta-muted">
|
||||
@@ -83,6 +115,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- HLS support (m3u8) -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/hls.min.js"></script>
|
||||
<script src="./js/app_player.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user