diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-30 16:13:47 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-30 16:13:47 +0200 |
| commit | 4f460c055e1eeb3383a8363af43df1cddc6dde10 (patch) | |
| tree | 7189039a6416c3a5bef6fed447b10685655df1c4 /packages/meshbay-hub/src | |
| parent | e94260ca018f43260b1b587ea487045a553e6e0a (diff) | |
| download | meshbay-4f460c055e1eeb3383a8363af43df1cddc6dde10.tar.gz | |
revert(hub): drop the poster-grid movie merge (V12)
V12 collapsed movies that TMDB resolved to the same tmdb_id into one
card. With the matcher still imperfect that fuses *different films*:
every numbered entry of a saga whose bare title resolves to the same
base id becomes one card, and two unrelated movies sharing a title do
too (seen live on a 9-film saga and a 2-film pair). A tmdb_id-keyed
merge only works once matching is reliable, which it is not yet.
Movies render one card per file again; VideoDetailModal loses the
`files` prop and the versions list, back to a single Play button;
`.video-version-list` and the `video.versions` key (×10 locales) are
removed. mergedShows (V6) is untouched — the operator's report was
about movies.
docs/mediacenter.md §10.1: V12 un-struck, marked reverted.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018BMLQjqFGCize2KtNBT79v
Diffstat (limited to 'packages/meshbay-hub/src')
12 files changed, 17 insertions, 77 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index d1cbf2b..86258d6 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -186,7 +186,6 @@ export default { 'video.search_apply_hint_movie': 'Gilt nur für diese Datei.', 'video.source_file': 'Datei: {name}', 'video.no_match': 'Keine sichere TMDB-Übereinstimmung — Dateiname wird angezeigt.', - 'video.versions': '{n} Versionen', 'video.rematch_one': 'Neu zuordnen', // Musik diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index 6ddfa91..5d2725c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -184,7 +184,6 @@ export default { 'video.search_apply_hint_movie': 'Applies to this file only.', 'video.source_file': 'File: {name}', 'video.no_match': 'No confident TMDB match — showing the filename.', - 'video.versions': '{n} versions', 'video.rematch_one': 'Re-match', // Music diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index c942869..7080677 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -184,7 +184,6 @@ export default { 'video.search_apply_hint_movie': 'Se aplica solo a este archivo.', 'video.source_file': 'Archivo: {name}', 'video.no_match': 'Sin coincidencia fiable en TMDB — se muestra el nombre del archivo.', - 'video.versions': '{n} versiones', 'video.rematch_one': 'Volver a asociar', // Música diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 8eefb29..eee4292 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -185,7 +185,6 @@ export default { 'video.search_apply_hint_movie': 'Ne s’applique qu’à ce fichier.', 'video.source_file': 'Fichier : {name}', 'video.no_match': 'Aucune correspondance TMDB fiable — nom de fichier affiché.', - 'video.versions': '{n} versions', 'video.rematch_one': 'Relancer la recherche', // Musique diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index 4ee261c..1031fed 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -185,7 +185,6 @@ export default { 'video.search_apply_hint_movie': 'Si applica solo a questo file.', 'video.source_file': 'File: {name}', 'video.no_match': 'Nessuna corrispondenza TMDB affidabile — mostrato il nome del file.', - 'video.versions': '{n} versioni', 'video.rematch_one': 'Riassocia', // Musica diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index e30f5bf..39863b1 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -182,7 +182,6 @@ export default { 'video.search_apply_hint_movie': 'このファイルにのみ適用されます。', 'video.source_file': 'ファイル: {name}', 'video.no_match': '確実なTMDB一致なし — ファイル名を表示しています。', - 'video.versions': '{n} 個のバージョン', 'video.rematch_one': '再マッチ', // 音楽 diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 2f66e00..21d14d7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -186,7 +186,6 @@ export default { 'video.search_apply_hint_movie': 'Alleen van toepassing op dit bestand.', 'video.source_file': 'Bestand: {name}', 'video.no_match': 'Geen betrouwbare TMDB-match — bestandsnaam wordt getoond.', - 'video.versions': '{n} versies', 'video.rematch_one': 'Opnieuw koppelen', // Muziek diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index f809182..d6ca6af 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -193,7 +193,6 @@ export default { 'video.search_apply_hint_movie': 'Dotyczy tylko tego pliku.', 'video.source_file': 'Plik: {name}', 'video.no_match': 'Brak pewnego dopasowania TMDB — pokazano nazwę pliku.', - 'video.versions': 'Wersje: {n}', 'video.rematch_one': 'Dopasuj ponownie', // Muzyka diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index 2e1a9f5..5e6dd42 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -186,7 +186,6 @@ export default { 'video.search_apply_hint_movie': 'Aplica-se somente a este arquivo.', 'video.source_file': 'Arquivo: {name}', 'video.no_match': 'Sem correspondência confiável no TMDB — exibindo o nome do arquivo.', - 'video.versions': '{n} versões', 'video.rematch_one': 'Combinar de novo', // Música diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index 1ec23eb..da97162 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -179,7 +179,6 @@ export default { 'video.search_apply_hint_movie': '仅适用于此文件。', 'video.source_file': '文件:{name}', 'video.no_match': '没有可靠的 TMDB 匹配 — 显示文件名。', - 'video.versions': '{n} 个版本', 'video.rematch_one': '重新匹配', // 音乐 diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index 297ef7f..2523c1a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -2988,7 +2988,6 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } margin-bottom: 4px; } .video-card-unmatched { outline: 1px dashed var(--warn, #d98324); outline-offset: -1px; } -.video-version-list { margin-top: 12px; display: flex; flex-direction: column; gap: 4px; } .video-admin-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 8px 0; } .video-card-flag { display: inline-block; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/video-app.js b/packages/meshbay-hub/src/meshbay_hub/static/video-app.js index 50fbcf6..5222afb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/video-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/video-app.js @@ -484,7 +484,7 @@ function TmdbSearchOverlay({ } function VideoDetailModal({ - title, meta, repEntry, show, files, transportRef, gekRef, onClose, onPlay, isNodeAdmin, + title, meta, repEntry, show, transportRef, gekRef, onClose, onPlay, isNodeAdmin, }) { const confident = Boolean(meta && meta.confidence && meta.tmdb_id); const [searching, setSearching] = useState(false); @@ -568,27 +568,12 @@ function VideoDetailModal({ <${SeasonTabs} seasons=${show.seasons} selected=${selectedSeason} onSelect=${setSelectedSeason} /> `} - ${!show && (!files || files.length <= 1) && html` + ${!show && html` <button class="admin-btn" onClick=${() => onPlay(repEntry)}> <${Icon} name="play" /> ${t('group.play')} ${repEntry.duration ? ` (${formatDuration(repEntry.duration)})` : ''} </button> `} - ${!show && files && files.length > 1 && html` - <div class="video-version-list"> - ${[...files] - .sort((a, b) => (b.height || 0) - (a.height || 0) || (b.size || 0) - (a.size || 0)) - .map((f) => html` - <button class="video-episode-row" key=${f.id} onClick=${() => onPlay(f)}> - <${Icon} name="play" /> - <span class="video-episode-label"> - ${[formatResolution(f.width, f.height), formatDuration(f.duration), - formatSize(f.size)].filter(Boolean).join(' · ')} - </span> - </button> - `)} - </div> - `} ${show && html` <div class="video-season-list"> ${(showMultiSeason ? show.seasons.filter((s) => s.season === selectedSeason) : show.seasons) @@ -683,35 +668,7 @@ function PosterGrid({ movies, shows, transportRef, gekRef, onPreview, tmdbEnable }); }, [shows, metaByGroup]); - // The movie counterpart of mergedShows (§10.1/V12): two files that TMDB - // resolves to the same id — the usual case being one film present at two - // resolutions, or the same rip in two folders — collapse to one card - // whose detail modal lists the versions. Same keying discipline as - // mergedShows: the first constituent's id is stable and unique, so an - // unmerged movie keeps the exact key its card already had and a merge - // updates props instead of remounting (which would throw away an - // already-resolved poster). - const mergedMovies = useMemo(() => { - const byTmdbId = new Map(); - const standalone = []; - for (const e of movies) { - const meta = metaByGroup[`movie:${e.id}`]; - const tmdbId = meta && meta.confidence && meta.tmdb_id; - if (tmdbId) { - if (!byTmdbId.has(tmdbId)) byTmdbId.set(tmdbId, []); - byTmdbId.get(tmdbId).push(e); - } else { - standalone.push([e]); - } - } - return [...byTmdbId.values(), ...standalone].map((files) => ({ - key: `movie:${files[0].id}`, - title: files[0].display_title || files[0].name, - files, - })); - }, [movies, metaByGroup]); - - const openDetail = (title, repEntry, show, files) => setDetail({ title, repEntry, show, files }); + const openDetail = (title, repEntry, show) => setDetail({ title, repEntry, show }); const detailTRef = detail && detail.repEntry._tRef ? detail.repEntry._tRef : transportRef; const detailGRef = detail && detail.repEntry._gRef ? detail.repEntry._gRef : gekRef; const detailMeta = useMediaMeta( @@ -720,27 +677,21 @@ function PosterGrid({ movies, shows, transportRef, gekRef, onPreview, tmdbEnable return html` <div class="video-grid"> - ${mergedMovies.map((m) => { - const repEntry = m.files.find((e) => e.thumb_hash) || m.files[0]; - const subtitle = m.files.length > 1 - ? t('video.versions', { n: m.files.length }) - : formatDuration(repEntry.duration); - // With TMDB off and only one file there is nothing the detail modal - // would add for a movie (no overview, no season list) — straight to - // the player. More than one file always needs the version picker. - const straightToPlayer = !tmdbEnabled && m.files.length === 1; - return html` - <${LazyTile} key=${m.key}> - <${PosterCard} title=${m.title} - subtitle=${subtitle} repEntry=${repEntry} - groupKey=${`movie:${m.files[0].id}`} - onMetaResolved=${handleMetaResolved} + ${movies.map((e) => html` + <${LazyTile} key=${e.id}> + <${PosterCard} title=${e.display_title || e.name} + subtitle=${formatDuration(e.duration)} repEntry=${e} + groupKey=${`movie:${e.id}`} transportRef=${transportRef} gekRef=${gekRef} - onOpen=${() => (straightToPlayer - ? onPreview(repEntry) - : openDetail(m.title, repEntry, null, m.files))} /> + onOpen=${() => (tmdbEnabled + // With TMDB off there is nothing the detail modal would show + // for a movie (no overview, no season list to pick from, + // unlike a show) — so it would just be an extra click in + // front of a Play button. Straight to the player instead. + ? openDetail(e.display_title || e.name, e, null) + : onPreview(e))} /> </${LazyTile}> - `; })} + `)} ${mergedShows.map((s) => { // Prefer an episode that actually has a thumbnail over blindly // episodes[0]: if that specific file's enrichment hasn't produced @@ -772,7 +723,7 @@ function PosterGrid({ movies, shows, transportRef, gekRef, onPreview, tmdbEnable </div> ${detail && html` <${VideoDetailModal} title=${detail.title} meta=${detailMeta} - repEntry=${detail.repEntry} show=${detail.show} files=${detail.files} + repEntry=${detail.repEntry} show=${detail.show} transportRef=${detailTRef} gekRef=${detailGRef} isNodeAdmin=${isNodeAdmin} onClose=${() => setDetail(null)} onPlay=${(entry) => { setDetail(null); onPreview(entry); }} /> |