' : '');
+ // lazily fetch the episode's guest stars, then re-render if still selected
+ if (sel && sel._guests === undefined) {
+ sel._guests = null;
+ fetch('/api/video/episode/' + tmdb + '/' + sel.season_number + '/' + sel.episode_number, { headers: { Accept: 'application/json' } })
+ .then(function (r) { return r.ok ? r.json() : null; })
+ .then(function (d) {
+ sel._guests = (d && d.guest_stars) || [];
+ var cur = group.querySelector('.vwsh-epc--sel');
+ if (cur && +cur.getAttribute('data-s') === sel.season_number && +cur.getAttribute('data-e') === sel.episode_number)
+ renderInfoBar(group, tmdb, sel);
+ })
+ .catch(function () { sel._guests = []; });
+ }
+ }
+ // Lazily load the show's synopsis + cast when its orb first expands.
function loadShowInfo(group) {
- if (!group || group.getAttribute('data-vwsh-info-loaded')) return;
- group.setAttribute('data-vwsh-info-loaded', '1');
+ if (!group) return;
var tmdb = parseInt(group.getAttribute('data-vwsh-tmdb'), 10);
+ renderInfoBar(group, tmdb, null); // paint the View-show button immediately
+ if (group.getAttribute('data-vwsh-info-loaded')) return;
+ group.setAttribute('data-vwsh-info-loaded', '1');
var sh = state.showData[tmdb]; if (!sh) return;
var url = sh.library_id != null ? '/api/video/detail/show/' + sh.library_id : '/api/video/tmdb/show/' + tmdb;
fetch(url, { headers: { Accept: 'application/json' } })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (d) {
- var info = group.querySelector('[data-vwsh-info]');
- if (!d || !info) return;
- var cast = (d.cast || []).slice(0, 8).map(function (c) {
- var photo = c.photo
- ? ''
- : '';
- return '';
- }).join('');
- info.innerHTML = (d.overview ? '
' + esc(d.overview) + '
' : '') +
- (cast ? '
' + cast + '
' : '');
+ if (!d) return;
+ state.showInfo[tmdb] = { overview: d.overview || '', cast: d.cast || [] };
+ if (!group.querySelector('.vwsh-epc--sel')) renderInfoBar(group, tmdb, null); // unless an episode is selected
})
.catch(function () { /* best-effort */ });
}
- // A single episode as a rich card: still + title + meta + synopsis. The card
- // links to the show detail (episodes have no page of their own).
+ // A single episode card. Clicking it SELECTS the episode (drives the info bar);
+ // the "View show" button in the info bar is what navigates.
function epCard(sh, se, e) {
- var src = sh.library_id != null ? 'library' : 'tmdb';
- var openId = sh.library_id != null ? sh.library_id : sh.tmdb_id;
var t = e.title || ('Episode ' + e.episode_number);
var st = STATUS[e.status] ? e.status : 'wanted';
var date = fmtDate(e.air_date);
@@ -157,12 +199,11 @@
? ''
: '';
- return '
' +
'' +
@@ -319,14 +360,27 @@
}));
return;
}
+ var epc = e.target.closest('[data-vwsh-ep]');
+ if (epc) { // episode → SELECT it (drives the info bar); click again to deselect
+ var eg = epc.closest('.wl-orb-group');
+ var etmdb = parseInt(epc.getAttribute('data-tmdb'), 10);
+ var eWasSel = epc.classList.contains('vwsh-epc--sel');
+ if (eg) { var es = eg.querySelectorAll('.vwsh-epc'); for (var j = 0; j < es.length; j++) es[j].classList.remove('vwsh-epc--sel'); }
+ if (eWasSel) { renderInfoBar(eg, etmdb, null); return; } // back to show synopsis + cast
+ epc.classList.add('vwsh-epc--sel');
+ renderInfoBar(eg, etmdb, findEpisode(etmdb, parseInt(epc.getAttribute('data-s'), 10), parseInt(epc.getAttribute('data-e'), 10)));
+ return;
+ }
var tile = e.target.closest('[data-vwsh-tile]');
if (tile) { // season → render its episodes full-width below (single-select)
var group = tile.closest('.wl-orb-group');
+ var tmdb = parseInt(tile.getAttribute('data-tmdb'), 10);
var wasSel = tile.classList.contains('vwsh-tile--sel');
if (group) { var all = group.querySelectorAll('.wl-album-tile'); for (var i = 0; i < all.length; i++) all[i].classList.remove('vwsh-tile--sel'); }
+ renderInfoBar(group, tmdb, null); // changing/closing a season resets the info bar to show-level
if (wasSel) { var a = group && group.querySelector('[data-vwsh-ep-area]'); if (a) a.innerHTML = ''; return; }
tile.classList.add('vwsh-tile--sel');
- renderEpisodeArea(group, parseInt(tile.getAttribute('data-tmdb'), 10), parseInt(tile.getAttribute('data-vwsh-season'), 10));
+ renderEpisodeArea(group, tmdb, parseInt(tile.getAttribute('data-vwsh-season'), 10));
return;
}
var orb = e.target.closest('[data-vwsh-orb]');