From 35957b426d9a1eea0fb7787a41a663f383971a14 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Tue, 16 Jun 2026 11:07:53 -0700 Subject: [PATCH] Video: airing shows get BOTH the watchlist eye AND the get/download button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An airing show is ongoing (follow for new episodes) AND has a back-catalog you may be missing (acquire) — so it needs both controls, not one or the other. VideoGet.cardButton() now returns a control GROUP: person -> eye | movie -> get | airing show -> eye + get | ended show -> get Library + watchlist-page cards route through it (the other surfaces already did). New .vcard-ctrls wrapper lays the buttons out top-right; hover-reveal unchanged. (Before, only ended shows showed the download button — which is why airing shows looked like they had no way to acquire missing episodes.) --- webui/static/video/video-get-modal.js | 31 +++++++++++++++------------ webui/static/video/video-library.js | 24 ++++++--------------- webui/static/video/video-side.css | 5 +++++ webui/static/video/video-watchlist.js | 7 ++++-- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/webui/static/video/video-get-modal.js b/webui/static/video/video-get-modal.js index 50b4ea13..738bc96b 100644 --- a/webui/static/video/video-get-modal.js +++ b/webui/static/video/video-get-modal.js @@ -156,27 +156,30 @@ }); }, true); - // Pick the right overlay control for any card: person → eye; movie → get; - // show → eye when airing (or status unknown = still followable), get when - // confirmed ended. One helper so every surface stays consistent. + // The control group for any card, so every surface stays consistent: + // person → eye (watchlist) + // movie → get (download/wishlist) + // airing show → eye + get (follow new episodes AND grab the back catalog) + // ended show → get + // Returns a positioned wrapper holding 0–2 buttons. function cardButton(o) { if (!o || !o.kind) return ''; + var parts = []; if (o.kind === 'person') { - return window.VideoWatchlist - ? VideoWatchlist.btn({ kind: 'person', tmdbId: o.tmdbId, title: o.title, poster: o.poster }) : ''; - } - if (o.kind === 'show') { + if (window.VideoWatchlist) parts.push(VideoWatchlist.btn({ kind: 'person', tmdbId: o.tmdbId, title: o.title, poster: o.poster })); + } else if (o.kind === 'show') { var airing = !o.status ? true : isAiring(o.status); if (airing && o.tmdbId && window.VideoWatchlist) { - return VideoWatchlist.btn({ kind: 'show', tmdbId: o.tmdbId, title: o.title, - poster: o.poster, libraryId: o.libraryId }); + parts.push(VideoWatchlist.btn({ kind: 'show', tmdbId: o.tmdbId, title: o.title, + poster: o.poster, libraryId: o.libraryId })); } - return btn({ kind: 'show', source: o.source || 'tmdb', openId: o.libraryId || o.tmdbId, title: o.title }); + // every show can be acquired (missing/back-catalog episodes) + parts.push(btn({ kind: 'show', source: o.source || 'tmdb', openId: o.libraryId || o.tmdbId, title: o.title })); + } else if (o.kind === 'movie') { + parts.push(btn({ kind: 'movie', source: o.source || 'tmdb', openId: o.libraryId || o.tmdbId, title: o.title })); } - if (o.kind === 'movie') { - return btn({ kind: 'movie', source: o.source || 'tmdb', openId: o.libraryId || o.tmdbId, title: o.title }); - } - return ''; + parts = parts.filter(Boolean); + return parts.length ? '' + parts.join('') + '' : ''; } window.VideoGet = { btn: btn, isAiring: isAiring, open: openModal, cardButton: cardButton }; diff --git a/webui/static/video/video-library.js b/webui/static/video/video-library.js index 196ff4a2..01729a07 100644 --- a/webui/static/video/video-library.js +++ b/webui/static/video/video-library.js @@ -65,23 +65,13 @@ fallback + '\'">' : '
' + fallback + '
'; - // Contextual overlay control, injected inside the positioned poster box: - // airing show -> watchlist eye (monitor for new episodes) - // movie / ended show -> "get" download symbol (opens the detail modal) - var ctrl = ''; - if (kind === 'movie') { - ctrl = window.VideoGet ? VideoGet.btn({ kind: 'movie', source: 'library', openId: it.id, title: it.title }) : ''; - } else if (kind === 'show') { - var airing = !window.VideoGet || VideoGet.isAiring(it.status); - if (airing && it.tmdb_id && window.VideoWatchlist) { - ctrl = VideoWatchlist.btn({ - kind: 'show', tmdbId: it.tmdb_id, title: it.title, - poster: it.has_poster ? ('/api/video/poster/show/' + it.id) : '', libraryId: it.id - }); - } else if (window.VideoGet) { - ctrl = VideoGet.btn({ kind: 'show', source: 'library', openId: it.id, title: it.title }); - } - } + // Overlay control group (eye/get), injected inside the positioned poster + // box: airing show -> eye + get; ended show / movie -> get; (people elsewhere). + var ctrl = window.VideoGet ? VideoGet.cardButton({ + kind: kind, tmdbId: it.tmdb_id, libraryId: it.id, title: it.title, + poster: it.has_poster ? ('/api/video/poster/' + kind + '/' + it.id) : '', + status: it.status, source: 'library' + }) : ''; if (ctrl) img = img.replace(/<\/div>$/, ctrl + ''); var badge = ''; diff --git a/webui/static/video/video-side.css b/webui/static/video/video-side.css index 92802ad9..21e46dc1 100644 --- a/webui/static/video/video-side.css +++ b/webui/static/video/video-side.css @@ -1973,3 +1973,8 @@ body[data-side="video"] #soulsync-toggle { display: none; } additive (no flow change); buttons are absolute top-right within them. */ .vsr-card, .vd-sim-card, .vd-cast-card { position: relative; } .vd-cast-card:hover .vget-btn { opacity: 1; transform: none; } + +/* Card control GROUP (eye + get can co-exist on an airing show). The wrapper is + positioned top-right; the buttons inside flow statically in a little row. */ +.vcard-ctrls { position: absolute; top: 8px; right: 8px; z-index: 4; display: flex; gap: 6px; } +.vcard-ctrls .vwl-btn, .vcard-ctrls .vget-btn { position: static; top: auto; right: auto; } diff --git a/webui/static/video/video-watchlist.js b/webui/static/video/video-watchlist.js index 4994078e..df2ace66 100644 --- a/webui/static/video/video-watchlist.js +++ b/webui/static/video/video-watchlist.js @@ -46,8 +46,11 @@ ? '' : '
' + ph + '
'; - var btn = wlBtn({ kind: kind, tmdbId: it.tmdb_id, title: it.title, - poster: it.poster_url, libraryId: it.library_id }); + var btn = window.VideoGet + ? VideoGet.cardButton({ kind: kind, tmdbId: it.tmdb_id, libraryId: it.library_id, + title: it.title, poster: it.poster_url, status: it.status, + source: it.library_id ? 'library' : 'tmdb' }) + : wlBtn({ kind: kind, tmdbId: it.tmdb_id, title: it.title, poster: it.poster_url, libraryId: it.library_id }); var pill = kind === 'show' ? statusPill(it.status) : ''; var meta = (kind === 'show' && it.episode_count) ? '' + (it.owned_count || 0) + '/' + it.episode_count + ' eps' : '';