The providers upstream ships in the box: TMDb is a third of one of them, and eight others are not written at all #105

Closed
opened 2026-08-13 22:59:41 +02:00 by Claude · 0 comments
Collaborator

The goal is the providers a stock upstream has — no plugins. That set is larger than it looks, because upstream's MediaBrowser.Providers/Plugins/ directory is compiled into the server rather than loaded as a plugin: TMDb, OMDb, MusicBrainz, AudioDb and StudioImages are all in-box.

What upstream registers at the pinned ref, and what exists here:

Remote metadata

Upstream Here
TmdbMovieProvider + TmdbMovieImageProvider yes — crates/jellyfin-app/src/metadata/tmdb/mod.rs
TmdbSeriesProvider, TmdbSeasonProvider, TmdbEpisodeProvider + their three image providers no
TmdbPersonProvider + TmdbPersonImageProvider portraits come off a film's credits; no person lookup
TmdbBoxSetProvider + TmdbBoxSetImageProvider no
TmdbMissingEpisodeProvider no
OmdbItemProvider, OmdbEpisodeProvider, OmdbImageProvider no
MusicBrainzAlbumProvider, MusicBrainzArtistProvider no
AudioDbAlbumProvider, AudioDbArtistProvider + two image providers no
StudiosImageProvider no

Local metadata

Upstream Here
NFO: movie, video, music video, series, season, episode, album, artist scan/nfo.rs reads the fields a scan can act on
BoxSetXmlProvider, PlaylistXmlProvider no
PlaylistItemsProvider (reads m3u/pls contents) no
Local images (MediaBrowser.LocalMetadata/Images/) scan/images.rs

Generated images

Upstream Here
GenreImageProvider, MusicGenreImageProvider yes — the collage
ArtistImageProvider, CollectionFolderImageProvider, DynamicImageProvider (user view), PlaylistImageProvider, CollectionImageProvider (box set) no
AudioImageProvider, EmbeddedImageProvider, VideoImageProvider no
TrickplayProvider no
ProbeProvider yes — the ingest probe

This is the umbrella. The substantive ones are filed separately: TMDb TV #106,
person and box set #107, OMDb #108, music #109, and the generated images #110. Two notes that belong here:

  • The practical effect today is that TV and music libraries scan to bare rows. A film gets an overview, a cast and artwork; a series gets what its filename and its NFO carry.
  • MILESTONES.md (M11, "Studio images are not on this list") calls StudiosImageProvider a plugin. It is MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs, compiled in-tree, and the reasoning there should be corrected.

Where to start

M10 built the whole machine for exactly this, and a new provider is a caller of
it rather than a new mechanism. Read, in this order:

  • crates/jellyfin-app/src/metadata/mod.rs — Fields, merge, apply, and
    the rank order a source is merged at. The lock set is honoured here.
  • crates/jellyfin-app/src/metadata/tmdb/mod.rs — the one provider that exists, and
    the shape a second one copies: it talks to the MetadataHttp port and nothing
    else, so what it asks and what it answers can both be held still.
  • crates/jellyfin-fetch/ — live.rs and cassette.rs. D27 is the rule: the
    network is recorded once from upstream's own traffic and replayed to both, and
    a recording nothing answers fails rather than falling through to the live
    one.
  • D28 and the enriched snapshot — the rows upstream fetched, committed as the
    answer key. A provider is measured against those rows, not against what looks
    right.

Upstream's are all in-tree under MediaBrowser.Providers/Plugins/ despite the
directory name, plus the local ones in MediaBrowser.XbmcMetadata/ and
MediaBrowser.LocalMetadata/.

How to know it is done

Each provider filed under this umbrella lands with: a cassette recording
regenerated beside the answer key, an offline chain test (what a scan resolved →
the provider → the merge → the row) measured against the row upstream wrote, and
the pictures compared on bytes rather than on dimensions.

One correction belongs with this issue: MILESTONES.md (M11, "Studio images are
not on this list") calls StudiosImageProvider a plugin. It is in-tree at
MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs, and the
paragraph should say what is actually true about it.

House rules

CLAUDE.md is the short version and these are the parts that bite: tests run
under cargo nextest; upstream's source is the checkout in upstream/ at the
pin
(v12.0-rc5, 6d501ba4) and not the clone beside the repository;
generated files are regenerated rather than edited; a comment says what the code
does, not how it got there; and Dn references stay out of code and comments.
ARCHITECTURE.md decides which crate a thing goes in and which dependency edges
are illegal, DECISIONS.md says why a rule exists.

The reference pair is cargo xtask reference up (and reference provision,
which is idempotent); a run is
cargo run -p jellyfin-harness -- replay --upstream URL --ours URL --scenario FILE --out DIR,
with diff-db for the two databases. crates/jellyfin-harness/README.md says
what a diff means and what an ignore entry has to carry — every entry needs a
reason and an expiry milestone, and an adds-only history is the smell the risk
table names.

The goal is the providers a *stock* upstream has — no plugins. That set is larger than it looks, because upstream's `MediaBrowser.Providers/Plugins/` directory is compiled into the server rather than loaded as a plugin: TMDb, OMDb, MusicBrainz, AudioDb and StudioImages are all in-box. What upstream registers at the pinned ref, and what exists here: **Remote metadata** | Upstream | Here | |---|---| | `TmdbMovieProvider` + `TmdbMovieImageProvider` | yes — `crates/jellyfin-app/src/metadata/tmdb/mod.rs` | | `TmdbSeriesProvider`, `TmdbSeasonProvider`, `TmdbEpisodeProvider` + their three image providers | no | | `TmdbPersonProvider` + `TmdbPersonImageProvider` | portraits come off a film's credits; no person lookup | | `TmdbBoxSetProvider` + `TmdbBoxSetImageProvider` | no | | `TmdbMissingEpisodeProvider` | no | | `OmdbItemProvider`, `OmdbEpisodeProvider`, `OmdbImageProvider` | no | | `MusicBrainzAlbumProvider`, `MusicBrainzArtistProvider` | no | | `AudioDbAlbumProvider`, `AudioDbArtistProvider` + two image providers | no | | `StudiosImageProvider` | no | **Local metadata** | Upstream | Here | |---|---| | NFO: movie, video, music video, series, season, episode, album, artist | `scan/nfo.rs` reads the fields a scan can act on | | `BoxSetXmlProvider`, `PlaylistXmlProvider` | no | | `PlaylistItemsProvider` (reads m3u/pls contents) | no | | Local images (`MediaBrowser.LocalMetadata/Images/`) | `scan/images.rs` | **Generated images** | Upstream | Here | |---|---| | `GenreImageProvider`, `MusicGenreImageProvider` | yes — the collage | | `ArtistImageProvider`, `CollectionFolderImageProvider`, `DynamicImageProvider` (user view), `PlaylistImageProvider`, `CollectionImageProvider` (box set) | no | | `AudioImageProvider`, `EmbeddedImageProvider`, `VideoImageProvider` | no | | `TrickplayProvider` | no | | `ProbeProvider` | yes — the ingest probe | This is the umbrella. The substantive ones are filed separately: TMDb TV #106, person and box set #107, OMDb #108, music #109, and the generated images #110. Two notes that belong here: - The practical effect today is that **TV and music libraries scan to bare rows.** A film gets an overview, a cast and artwork; a series gets what its filename and its NFO carry. - `MILESTONES.md` (M11, "Studio images are not on this list") calls `StudiosImageProvider` a plugin. It is `MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs`, compiled in-tree, and the reasoning there should be corrected. --- ## Where to start M10 built the whole machine for exactly this, and a new provider is a caller of it rather than a new mechanism. Read, in this order: - `crates/jellyfin-app/src/metadata/mod.rs` — `Fields`, `merge`, `apply`, and the rank order a source is merged at. The lock set is honoured here. - `crates/jellyfin-app/src/metadata/tmdb/mod.rs` — the one provider that exists, and the shape a second one copies: it talks to the `MetadataHttp` port and nothing else, so what it asks and what it answers can both be held still. - `crates/jellyfin-fetch/` — `live.rs` and `cassette.rs`. D27 is the rule: the network is recorded once from upstream's own traffic and replayed to both, and a recording nothing answers **fails** rather than falling through to the live one. - D28 and the `enriched` snapshot — the rows upstream fetched, committed as the answer key. A provider is measured against those rows, not against what looks right. Upstream's are all in-tree under `MediaBrowser.Providers/Plugins/` despite the directory name, plus the local ones in `MediaBrowser.XbmcMetadata/` and `MediaBrowser.LocalMetadata/`. ## How to know it is done Each provider filed under this umbrella lands with: a cassette recording regenerated beside the answer key, an offline chain test (what a scan resolved → the provider → the merge → the row) measured against the row upstream wrote, and the pictures compared on bytes rather than on dimensions. One correction belongs with this issue: `MILESTONES.md` (M11, "Studio images are not on this list") calls `StudiosImageProvider` a plugin. It is in-tree at `MediaBrowser.Providers/Plugins/StudioImages/StudiosImageProvider.cs`, and the paragraph should say what is actually true about it. ### House rules `CLAUDE.md` is the short version and these are the parts that bite: tests run under `cargo nextest`; upstream's source is the checkout in `upstream/` **at the pin** (`v12.0-rc5`, `6d501ba4`) and not the clone beside the repository; generated files are regenerated rather than edited; a comment says what the code does, not how it got there; and `Dn` references stay out of code and comments. `ARCHITECTURE.md` decides which crate a thing goes in and which dependency edges are illegal, `DECISIONS.md` says why a rule exists. The reference pair is `cargo xtask reference up` (and `reference provision`, which is idempotent); a run is `cargo run -p jellyfin-harness -- replay --upstream URL --ours URL --scenario FILE --out DIR`, with `diff-db` for the two databases. `crates/jellyfin-harness/README.md` says what a diff means and what an ignore entry has to carry — every entry needs a reason and an expiry milestone, and an adds-only history is the smell the risk table names.
alex closed this issue 2026-08-16 21:49:25 +02:00
Sign in to join this conversation.
No description provided.