8c5fb5950d0878f6ddda726f00de3cf566911860
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1e247c4c7d |
feat(audio): align a fetched manifest to the local file before storing
The signature had a producer and a reader but no consumer, so nothing ever fingerprinted anything. `ManifestAligner` runs on the fetch path, before the windows are stored. A local alignment supersedes the server's offset. The server has never seen this file — its offset is a runtime-difference inference at best, while the local comparison is against the media the windows will actually be drawn over. It also needs no round trip, so no signature leaves the instance. This is what jRay's spec already meant by matching being a consumer concern: the server never rewrites a manifest, so one stored manifest serves every trim of the same cut. The offset has two terms and only one is in the server's pseudocode. Both windows are centred on their own file's midpoint, so unequal runtimes start them at different absolute times; a release with 40 s of extra head material recovers 20 s from the slide and 20 s from the anchor difference. Using the slide alone is wrong by half the runtime difference on every shifted release. Degradation, never failure. Signatures off, no manifest signature, media under the window, a `v2:` producer, a missing binary, a decode error — each applies the server's offset rather than refusing, because a signature is an enhancement to cut matching and must never break a fetch. "Un-comparable" and "does not match" are kept distinct, which a test caught: `Compare` returns null for both, and conflating them would report a 90-second extra as content disagreeing with its own manifest. A genuine disagreement is stored anyway — the audio may legitimately differ, a different language track being the obvious case — and surfaced as a caveat that outranks the tier's, since it is the stronger statement. The applied offset, score, slide and the local file's own signature are written beside the truth file: the offset is otherwise unrecoverable once the windows are shifted, and the stored signature lets a later fetch align without decoding again. Provenance is never injected into the truth file, so the bytes served back stay the producer's (JR-004). `docs/audio-alignment.md` documents the mechanism end to end. TRACES: JR-047 | SR-003 |
||
|
|
5152f6f129 |
JR-010: record how truth data was obtained
Three routes now deliver truth data -- a sidecar, a worker push, and a fetched
manifest -- and once stored they were indistinguishable. The truth file records
nothing about how it arrived, so a locally computed sidecar and a loose-tier
manifest from a third-party server looked identical to every reader, despite
making claims of very different strength about the same item.
TruthProvenance records source, server, match tier, applied offset and caveat.
GET /Items/{itemId}/Provenance serves it, and JR-036's loose-tier caveat now
has somewhere to come from.
Two decisions carried in the code rather than assumed:
Provenance is stored BESIDE the truth file, never inside it. Injecting fields
would mean the bytes served back are not the bytes the producer wrote, which is
the property JR-004 turns on. UT-026 pins it by asserting the stored truth JSON
contains no provenance keys.
The applied offset is recorded because it is otherwise unrecoverable. Once
JR-030 shifts every window the timings look native, and nothing else would say
they had been shifted -- which matters when diagnosing an overlay that is
consistently a few seconds out.
A sidecar's provenance is derived rather than stored: it is local, and its
timestamp is the file's own. Precedence resolves through the same rule as
GetTruthAsync, because resolving it twice by different rules is how the two
would drift.
Fourth mutation check: stopping Delete from removing provenance fails UT-027
alone -- a stale record would otherwise outlive its claim and describe data the
next fetch had already replaced.
TRACES: UT-024, UT-025, UT-026, UT-027, UT-028 | JR-010
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|
|
3d210b5bd3 |
Manifest fetch across the configured servers (JR-025 … JR-037)
Satisfies JRay-public-server UR-007. Servers are tried in configured order and the first result clearing the configured tier wins; first-match rather than best-match because querying every server for every item multiplies egress and leaks the library to more parties, and the ordering already encodes which source the admin prefers. Every server is untrusted, including the pre-configured community one, so a fetched manifest is re-validated against the same rules the server applies on upload: envelope version refused if unknown, identifiers format-checked, windows bounds-checked against the *local* file's runtime, belief bounded to [0, 1], control and bidi characters refused in names. Responses are capped while streaming rather than after buffering, since a hostile server can declare any Content-Length it likes. HTTPS is required away from loopback. A failing server is skipped with exponential backoff so one dead server cannot stall a sweep. The audio-tier offset is applied once, at store time, so stored truth is always in the local file's own timebase and no read path needs offset awareness. Windows are shifted, never reshaped — merging adjacent ones would answer "was a face visible" rather than "was the actor present" (SR-002). Also records why there is no `exact` tier, which was missing and led me to re-add one. The file-hash tier is withdrawn on legal grounds: a TMDB id discloses "some copy of this film", but an OpenSubtitles hash discloses "this exact release", which turns a catalogue lookup into a release-identification service and a server's database into a mapping from file fingerprints to the instances holding them. The reason now lives on MatchTier and in SPEC.md §JR-036, `TitleQuery` has no VideoHash property so there is nothing to send, and a test asserts the enum has no Exact member — the spec had still listed `exact` as a configurable tier, which is what made the removal look like an oversight. 42 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> TRACES: JR-025, JR-027, JR-028, JR-029, JR-030, JR-031, JR-036, JR-037 | PR-005, PR-006 |