From 6eed3d7775f6847dabdb6f68d39ceeea37c89ac0 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Mon, 15 Jun 2026 09:50:07 -0700 Subject: [PATCH] video: best-in-class detail billboards (Play CTA, collections, recs, next-ep) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Movie + TV detail pages get the things a premium app surfaces: - Primary 'Play on Plex/Jellyfin' button (white Netflix-style CTA with the server logo) in the billboard for owned items — deep-links straight to the item. - 'Directed by' (movies) / 'Created by' (shows) line in the hero. - Movies: a Collection/franchise row (the other films in the set), release-ordered. - 'More Like This' now uses TMDB recommendations (better curated), similar as fallback. - TV: a 'Next Episode' banner (S/E + name + air date) for continuing shows, and the selected season's overview under the season nav. All in-app (cards drill into library/preview detail). Shell tests updated. --- tests/test_video_side_shell.py | 6 ++ webui/index.html | 12 ++++ webui/static/video/video-detail.js | 110 ++++++++++++++++++++++++----- webui/static/video/video-side.css | 36 +++++++++- 4 files changed, 146 insertions(+), 18 deletions(-) diff --git a/tests/test_video_side_shell.py b/tests/test_video_side_shell.py index 2149f9a6..80472f75 100644 --- a/tests/test_video_side_shell.py +++ b/tests/test_video_side_shell.py @@ -424,6 +424,12 @@ def test_detail_keeps_preview_items_in_app(): # streaming providers. assert "vd-prov--server" in src and "Play on " in src assert "ex.providers_link" in src + # Best-in-class billboard: primary Play CTA, director/creator line, collection + # row, recommendations (with similar fallback), next-episode + season overview. + assert "vd-play-btn" in src + assert "renderCrewLine" in src and "renderNextEpisode" in src + assert "data-vd-collection" in _INDEX and "renderSeasonOverview" in src + assert "ex.recommendations" in src # The old external 'similar' link (themoviedb.org//) is gone — the # only remaining themoviedb.org ref is the TMDB badge logo asset for owned items. assert "www.themoviedb.org/' + (s.kind" not in src diff --git a/webui/index.html b/webui/index.html index 1ee0f652..a5dae16d 100644 --- a/webui/index.html +++ b/webui/index.html @@ -882,6 +882,8 @@

+ + @@ -905,6 +907,7 @@
+
+ + '; } + function renderSeasonOverview() { + var el = q('[data-vd-season-overview]'); + if (!el) return; + var s = seasonByNum(selectedSeason); + var ov = s && s.overview; + el.textContent = ov || ''; + el.hidden = !ov; + } + function renderEpisodes() { + renderSeasonOverview(); var host = q('[data-vd-episodes]'); if (!host) return; var season = seasonByNum(selectedSeason); @@ -521,6 +596,7 @@ if (se && se.episodes) { season.episodes = se.episodes; season.episode_total = se.episodes.length; + if (se.overview) season.overview = se.overview; } if (currentId === sid && selectedSeason === sn) { renderSeasonNav(); renderEpisodes(); } }) diff --git a/webui/static/video/video-side.css b/webui/static/video/video-side.css index 222ff3d1..636bef21 100644 --- a/webui/static/video/video-side.css +++ b/webui/static/video/video-side.css @@ -743,7 +743,8 @@ body[data-side="video"] .dashboard-header-sweep { box-shadow: 0 8px 22px rgba(var(--vd-accent-rgb), 0.55); } .vd-trailer-ic { font-size: 12px; } -.vd-providers-section, .vd-similar-section { margin-top: 40px; } +.vd-providers-section, .vd-similar-section, .vd-collection-section { margin-top: 40px; } +.vd-sim-year { display: block; font-size: 11px; color: rgba(255, 255, 255, 0.45); margin-top: 2px; } .vd-providers { display: flex; flex-wrap: wrap; gap: 14px; } .vd-prov { display: flex; flex-direction: column; align-items: center; gap: 7px; width: 78px; text-align: center; } .vd-prov img, .vd-prov-ph { @@ -1160,3 +1161,36 @@ a.vd-prov:hover img, a.vd-prov:hover .vd-prov-ph { border-color: rgba(var(--vd-a .vsr-grid { grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 16px 14px; } .vp-photo-wrap { width: 150px; height: 150px; } } + +/* ── billboard: Play CTA, crew line, next-episode, season overview ────────── */ +.vd-play-btn { + display: inline-flex; align-items: center; gap: 9px; text-decoration: none; + padding: 11px 22px; border-radius: 10px; font-size: 15px; font-weight: 800; + background: #fff; color: #0b0b0f; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4); + transition: transform 0.18s ease, box-shadow 0.18s ease; +} +.vd-play-btn:hover { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.55); } +.vd-play-logo { height: 17px; width: auto; } +.vd-play-ic { font-size: 14px; } + +.vd-crew-line { font-size: 14px; color: rgba(255, 255, 255, 0.84); margin: 2px 0 14px; + text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6); } +.vd-crew-line-k { color: rgba(255, 255, 255, 0.5); font-weight: 600; margin-right: 4px; } + +.vd-next-ep { + display: inline-flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 2px 0 14px; + font-size: 13.5px; color: rgba(255, 255, 255, 0.9); text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6); +} +.vd-next-ep-badge { + font-size: 11.5px; font-weight: 800; letter-spacing: 0.5px; text-transform: uppercase; + color: rgb(var(--vd-accent-rgb)); padding: 4px 11px; border-radius: 999px; + background: rgba(var(--vd-accent-rgb), 0.16); border: 1px solid rgba(var(--vd-accent-rgb), 0.36); +} +.vd-next-ep-code { font-weight: 800; } +.vd-next-ep-name { color: rgba(255, 255, 255, 0.72); font-style: italic; } +.vd-next-ep-when { color: rgba(255, 255, 255, 0.6); } + +.vd-season-overview { + font-size: 14px; line-height: 1.6; color: rgba(255, 255, 255, 0.7); + max-width: 880px; margin: -8px 0 22px; +}