diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/transport.js | 53 |
1 files changed, 31 insertions, 22 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js index 065ccc0..7535594 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js @@ -498,13 +498,17 @@ class MeshBayTransport { /** * TMDB metadata for one file (Videos app, docs/mediacenter.md §5.4). - * `path` is root+relpath, exactly what index_sync/index_delta already - * gave this browser — never a raw filesystem path constructed here. + * Keyed by the entry's own `id` (its content hash) — never a path: a + * path names the *folder* a file is in (indexer.py's `_virtual_dir`), so + * two files sharing a folder (any multi-episode season) would resolve to + * whichever entry the node's index happened to return first (found live + * via the Music app's identical bug, 2026-08-25 — see webrtc_server.py's + * `_do_media_meta_request`). * `confidence: 0` (no tmdb_id, no fields) means no confident match — * the caller falls back to a thumbnail-only card (§4.1), not an error. */ - async fetchMediaMeta(path) { - const msg = await this._sendAndWait({ type: 'media_meta_req', v: '0.5', path }); + async fetchMediaMeta(fileId) { + const msg = await this._sendAndWait({ type: 'media_meta_req', v: '0.6', file_id: fileId }); if (msg.type === 'error') throw new Error(msg.detail); return msg; } @@ -550,15 +554,16 @@ class MeshBayTransport { * override would let any member vandalize another show's metadata. * Applies to every file sharing the representative one's display_title, * not just the file the operator happened to be looking at (webrtc_ - * server.py's _admin_exec_tmdb_override). + * server.py's _admin_exec_tmdb_override). Keyed by `fileId`, not a path + * — same reasoning as fetchMediaMeta above. */ - async overrideTmdbMatch(path, tmdbId, mediaType, signFn) { + async overrideTmdbMatch(fileId, tmdbId, mediaType, signFn) { const msg = await this._sendAndWait({ - type: 'tmdb_override', v: '0.6', path, tmdb_id: tmdbId, media_type: mediaType, + type: 'tmdb_override', v: '0.7', file_id: fileId, tmdb_id: tmdbId, media_type: mediaType, }); if (msg.type === 'error') throw new Error(msg.detail); if (msg.type === 'admin_challenge') { - const subject = `path=${path},tmdb_id=${tmdbId},media_type=${mediaType}`; + const subject = `file_id=${fileId},tmdb_id=${tmdbId},media_type=${mediaType}`; return this._authorizeAdminOp(msg, 'tmdb_override', subject, signFn); } return msg; @@ -668,15 +673,19 @@ class MeshBayTransport { } /** - * MusicBrainz metadata for one track's path (Music app, docs/musicbay.md - * §4.3) — same shape as fetchMediaMeta, minus a season/episode concept: + * MusicBrainz metadata for one track (Music app, docs/musicbay.md §4.3) + * — same shape as fetchMediaMeta, minus a season/episode concept: * album-level (release), resolved from the track's own artist/album - * fields already in the index. `confidence: 0` means no confident match - * (or MusicBrainz off for this group, or nothing configured) — the caller - * falls back to the embedded/no cover it already had, not an error. + * fields already in the index. Keyed by the track's own `id` (content + * hash), not a path — a path names the *folder* a track is in, and an + * album is one folder with many tracks in it; three unrelated albums + * shared one folder's track's cover before this fix (found live, + * 2026-08-25). `confidence: 0` means no confident match (or MusicBrainz + * off for this group, or nothing configured) — the caller falls back to + * the embedded/no cover it already had, not an error. */ - async fetchMusicMeta(path) { - const msg = await this._sendAndWait({ type: 'music_meta_req', v: '0.8', path }); + async fetchMusicMeta(fileId) { + const msg = await this._sendAndWait({ type: 'music_meta_req', v: '0.9', file_id: fileId }); if (msg.type === 'error') throw new Error(msg.detail); return msg; } @@ -1467,10 +1476,10 @@ class MeshBayTransport { _key: obj.type === 'file_req' ? `chunk:${obj.file_id}:${obj.chunk_index}` : obj.type === 'ping' ? `ping:${obj.token}` - : obj.type === 'media_meta_req' ? `media_meta:${obj.path}` + : obj.type === 'media_meta_req' ? `media_meta:${obj.file_id}` // Same reordering hazard as media_meta_req: an album grid fires // one music_meta_req per visible tile, several at a time. - : obj.type === 'music_meta_req' ? `music_meta:${obj.path}` + : obj.type === 'music_meta_req' ? `music_meta:${obj.file_id}` // Same reordering hazard as media_meta_req: a season-tab bar or a // search box can have more than one of these in flight at once. : obj.type === 'season_meta_req' ? `season_meta:${obj.tmdb_id}:${obj.season}` @@ -1652,7 +1661,7 @@ class MeshBayTransport { // member_upload_ack/apps_enabled_ack above. if (msg.type === 'tmdb_override_ack' && this._onTmdbOverride) { this._onTmdbOverride({ - path: msg.path || '', tmdbId: msg.tmdb_id || '', mediaType: msg.media_type || '', + fileId: msg.file_id || '', tmdbId: msg.tmdb_id || '', mediaType: msg.media_type || '', }); } @@ -1774,21 +1783,21 @@ class MeshBayTransport { } if (msg.type === 'media_meta_resp') { - const key = `media_meta:${msg.path}`; + const key = `media_meta:${msg.file_id}`; for (const [, handler] of this._pending) { if (handler._key === key) { handler.resolve(msg); return; } } - // Nobody asked for this path any more (tile scrolled out and a fresh + // Nobody asked for this file any more (tile scrolled out and a fresh // request superseded it, most likely) — must not fall through to the // oldest pending request, which would hand a different tile's promise - // a TMDB result for a path it never asked about. + // a TMDB result for a file it never asked about. return; } // Same reasoning as media_meta_resp: keyed, not arrival-order, and // "nobody's waiting any more" must not fall through either. if (msg.type === 'music_meta_resp') { - const key = `music_meta:${msg.path}`; + const key = `music_meta:${msg.file_id}`; for (const [, handler] of this._pending) { if (handler._key === key) { handler.resolve(msg); return; } } |