A series is asked about on every scan, and its id is resolved again first #158

Closed
opened 2026-08-20 19:48:49 +02:00 by Claude · 1 comment
Collaborator

A series is asked about on every scan, and its id is resolved again first.

Found while closing #155, which fixed the same shape for a music folder. After
that fix a rescan of an unchanged enriched library is silent except for three
requests, all of them a series:

GET /3/find/tt0098936?external_source=imdb_id&language=en
GET /3/tv/1437?…&append_to_response=credits,images,external_ids,content_ratings,keywords,videos,episode_groups
GET /3/tv/1920?…&append_to_response=…

Two series in the fixture, each described again, plus one find to resolve an
id. A film is quiet — the same rescan asks nothing about one — so this is the
series path alone.

Two separate causes, and the first is the cheaper one.

The id is resolved before anything is read.
crates/ephyra-app/src/scan/run/remote.rs::identified_series runs for every
Series row in the pass, unconditionally, and its own doc says why it runs
where it does: a season and an episode resolve nothing themselves, so the
series has to carry a TMDb id before anything below it is asked. But it asks
again for a series whose row already carries one from the last scan — the
lookup is built from the item the pass holds rather than from what is filed.

The series is described again. The /3/tv/… pair is the description
itself, and nothing short-circuits it. #155 gave a music folder that
short-circuit: a row a database has answered about — DateLastRefreshed, now
carried on Tracked — is described again only where the pass read the folder
or something under it, which is MetadataService.GetProviders' own shape.
Nothing equivalent exists for a series.

Upstream does not do this. runAllProviders is false past a row's first
refresh unless a change monitor reports one, so a second scan of an unchanged
show asks nobody.

Where to start

identified_series for the first half — what it should read the id off is the
filed row rather than the resolved item.

The second half is the harder one, and #155 is the precedent rather than the
answer: a series is not summarised from what is under it the way an album is,
so what stands in for "nothing changed" is a different question. The gate #155
landed is crates/ephyra-app/src/scan/summary.rs::folders.

How to know it is done

A second scan of an unchanged library with a shows library makes no request at
all. crates/ephyra-app/tests/scan_enriched.rs holds the shape this is
measured in — a_rescan_of_an_unchanged_music_library_asks_nothing_and_rewrites_nothing
counts the hosts a pass asked, and its MUSIC_DATABASES filter is what hides
these three today.

A rescan after an episode is added or a show's document changes still asks.

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
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.

A test that cannot fail is worse than none — check each new assertion against
the mutation it exists to catch.

A series is asked about on every scan, and its id is resolved again first. Found while closing #155, which fixed the same shape for a music folder. After that fix a rescan of an unchanged enriched library is silent except for three requests, all of them a series: ``` GET /3/find/tt0098936?external_source=imdb_id&language=en GET /3/tv/1437?…&append_to_response=credits,images,external_ids,content_ratings,keywords,videos,episode_groups GET /3/tv/1920?…&append_to_response=… ``` Two series in the fixture, each described again, plus one `find` to resolve an id. A film is quiet — the same rescan asks nothing about one — so this is the series path alone. Two separate causes, and the first is the cheaper one. **The id is resolved before anything is read.** `crates/ephyra-app/src/scan/run/remote.rs::identified_series` runs for every `Series` row in the pass, unconditionally, and its own doc says why it runs where it does: a season and an episode resolve nothing themselves, so the series has to carry a `TMDb` id before anything below it is asked. But it asks again for a series whose row already carries one from the last scan — the lookup is built from the item the pass holds rather than from what is filed. **The series is described again.** The `/3/tv/…` pair is the description itself, and nothing short-circuits it. #155 gave a music folder that short-circuit: a row a database has answered about — `DateLastRefreshed`, now carried on `Tracked` — is described again only where the pass read the folder or something under it, which is `MetadataService.GetProviders`' own shape. Nothing equivalent exists for a series. Upstream does not do this. `runAllProviders` is false past a row's first refresh unless a change monitor reports one, so a second scan of an unchanged show asks nobody. ## Where to start `identified_series` for the first half — what it should read the id off is the filed row rather than the resolved item. The second half is the harder one, and #155 is the precedent rather than the answer: a series is not summarised from what is under it the way an album is, so what stands in for "nothing changed" is a different question. The gate #155 landed is `crates/ephyra-app/src/scan/summary.rs::folders`. ## How to know it is done A second scan of an unchanged library with a shows library makes no request at all. `crates/ephyra-app/tests/scan_enriched.rs` holds the shape this is measured in — `a_rescan_of_an_unchanged_music_library_asks_nothing_and_rewrites_nothing` counts the hosts a pass asked, and its `MUSIC_DATABASES` filter is what hides these three today. A rescan after an episode is added or a show's document changes still asks. ### 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** 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. A test that cannot fail is worse than none — check each new assertion against the mutation it exists to catch.
Author
Collaborator

Landed in 3265aed, with review fixes in 22de82e.

Both requests were one function. identified_series ran for every Series
row in the pass, before anything decided what to read — the find to resolve an
id, and the tv/… for the document the other ids come out of. Two series in the
fixture, three requests, on a library where nothing had changed. There was no
second cause: the description the issue attributes to the reading pass was this
function's own second call.

It reads the ids off the filed row now, as upstream does, and asks nothing for a
series already described.

What "already described" takes — the review made this precise, and both
halves matter because getting either wrong strands the episodes rather than
the series, which is quiet:

  • The filed TMDb id has to agree with the one the pass read. Merging the
    filed ids over the pass's own and then skipping meant a folder corrected to
    another [tmdbid-…] kept the old show's Imdb id — learned fills an empty
    slot rather than replacing a full one — and every episode below resolved
    through OMDb by the wrong show. The filed ids go on after the pass's now.
  • There has to be an id beside it. The document is read for the other
    databases' ids, so a row that was identified and never described — pinned by
    hand, or a harvest that failed on the network — still owes that reading.

My first attempt gated on the merged lookup instead of the filed row, and a
series named [tmdbid-1437] in its own folder then skipped the harvest on a
first pass: the Firefly episode lost its TV-14 rating. already_resolved
spells all of it, with the cases as unit tests.

On the test. scan_enriched.rs's rescan now asserts that no host at all is
asked, rather than filtering to MUSIC_DATABASES — which is exactly what hid
these three. And a series whose row names no database is still asked about, so
the short-circuit is what the last pass resolved rather than a stamp that
switches asking off for good.

Adding an episode is unaffected: it lands in the season folder, the series row is
left alone either way, and the episode resolves through the ids already on the
series — which is what upstream does too.

Landed in 3265aed, with review fixes in 22de82e. **Both requests were one function.** `identified_series` ran for every `Series` row in the pass, before anything decided what to read — the `find` to resolve an id, and the `tv/…` for the document the other ids come out of. Two series in the fixture, three requests, on a library where nothing had changed. There was no second cause: the description the issue attributes to the reading pass was this function's own second call. It reads the ids off the filed row now, as upstream does, and asks nothing for a series already described. **What "already described" takes** — the review made this precise, and both halves matter because getting either wrong strands the *episodes* rather than the series, which is quiet: - **The filed `TMDb` id has to agree with the one the pass read.** Merging the filed ids over the pass's own and then skipping meant a folder corrected to another `[tmdbid-…]` kept the old show's `Imdb` id — `learned` fills an empty slot rather than replacing a full one — and every episode below resolved through OMDb by the wrong show. The filed ids go on after the pass's now. - **There has to be an id beside it.** The document is read for the *other* databases' ids, so a row that was identified and never described — pinned by hand, or a harvest that failed on the network — still owes that reading. My first attempt gated on the merged lookup instead of the filed row, and a series named `[tmdbid-1437]` in its own folder then skipped the harvest on a **first** pass: the Firefly episode lost its `TV-14` rating. `already_resolved` spells all of it, with the cases as unit tests. **On the test.** `scan_enriched.rs`'s rescan now asserts that *no host at all* is asked, rather than filtering to `MUSIC_DATABASES` — which is exactly what hid these three. And a series whose row names no database is still asked about, so the short-circuit is what the last pass resolved rather than a stamp that switches asking off for good. Adding an episode is unaffected: it lands in the season folder, the series row is left alone either way, and the episode resolves through the ids already on the series — which is what upstream does too.
Sign in to join this conversation.
No description provided.