Video: airing shows get BOTH the watchlist eye AND the get/download button

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.)
This commit is contained in:
BoulderBadgeDad 2026-06-16 11:07:53 -07:00
parent 638269d870
commit 35957b426d
4 changed files with 34 additions and 33 deletions

View file

@ -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 02 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 ? '<span class="vcard-ctrls">' + parts.join('') + '</span>' : '';
}
window.VideoGet = { btn: btn, isAiring: isAiring, open: openModal, cardButton: cardButton };

View file

@ -65,23 +65,13 @@
fallback + '</div>\'"></div>'
: '<div class="library-artist-image"><div class="library-artist-image-fallback">' + fallback + '</div></div>';
// 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 + '</div>');
var badge = '';

View file

@ -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; }

View file

@ -46,8 +46,11 @@
? '<img class="vwlp-card-img" src="' + esc(it.poster_url) + '" alt="" loading="lazy" ' +
'onload="this.classList.add(\'vwlp-loaded\')" onerror="this.style.display=\'none\'">'
: '<div class="vwlp-card-ph">' + ph + '</div>';
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)
? '<span class="vwlp-card-meta">' + (it.owned_count || 0) + '/' + it.episode_count + ' eps</span>' : '';