summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-30 16:13:47 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-30 16:13:47 +0200
commit4f460c055e1eeb3383a8363af43df1cddc6dde10 (patch)
tree7189039a6416c3a5bef6fed447b10685655df1c4
parente94260ca018f43260b1b587ea487045a553e6e0a (diff)
downloadmeshbay-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
-rw-r--r--docs/mediacenter.md2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/de.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/en.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/es.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/it.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css1
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/video-app.js83
13 files changed, 18 insertions, 78 deletions
diff --git a/docs/mediacenter.md b/docs/mediacenter.md
index 45f57d9..aa1bd03 100644
--- a/docs/mediacenter.md
+++ b/docs/mediacenter.md
@@ -767,7 +767,7 @@ TMDB id, and an unmatched poster gets a badge.
| ~~V9~~ | `_best_match` trusted `results[0]` per query unconditionally | Optional `year`: when the top result is not a confident textual hit (< 0.6) and a year was requested, a different result of that exact release year is preferred. A confident top hit is never overridden |
| ~~V10~~ | `sequel_variants` narrow (trailing digit 2–9, arabic→roman only) | Widened: digit↔Roman both ways, spelled-out indices (one/two…, un/deux…, ordinals), a "Part N" / "Chapitre N" wrapper. Still empty for a trailing word that is not an index or a 4-digit year |
| ~~V11~~ | Extra TMDB calls in the 0.6–0.85 band | When the primary hit is decent (≥ 0.6) and there is nothing more specific to try (no `alternative_title`, no sequel variant), the ladder returns without the extra requests |
-| ~~V12~~ | Movies not merged in the poster grid | `mergedMovies` groups by resolved `tmdb_id`, mirroring `mergedShows`; the detail modal lists the versions (resolution · duration · size), each a Play button. New `video.versions` key ×10. Edge: "Fix match" on a merged movie corrects only the representative file; the other version un-merges and can be corrected on its own |
+| V12 | Movies not merged in the poster grid | Built (`mergedMovies` by resolved `tmdb_id`) then **reverted** (`revert/video-movie-merge`): with a poor matcher it *fuses distinct films* — every numbered entry of a saga that TMDB resolves to the same base id collapses into one card (seen live: a 9-film saga, two unrelated same-title movies). A merge that trusts `tmdb_id` needs the match to be right first; that is the V8–V11 / V9 work, still not fully there. Movies now render one card per file again; the detail modal has a single Play button; `video.versions` key removed. |
| ~~V13~~ | Per-card "re-match this one file" button | `OP_TMDB_REMATCH` / `MNP.TMDB_REMATCH` (MNP 0.13, additive) → `media_cache.drop_tmdb_match` (forgets the match *and* the override marker). Signed like `tmdb_override`. Button next to "Fix match" in the detail modal; `transport.rematchTmdbMatch`; `video.rematch_one` key ×10 |
### 10.2 A movie mis-shelved as a series (2026-08-29)
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); }} />