Commit Graph
4 Commits
Author SHA1 Message Date
dtourolle 17be9bcc4e feat(audio): v1 signature producer, bit-exact with the pipeline
`AudioSignature` is the DSP — band table, periodic Hann, radix-2 FFT,
band-mean peak, energy class, packing — and `AudioSignatureService` the
decode, running the FFmpeg binary `IMediaEncoder.EncoderPath` names. The
plugin gained no dependency.

The server specification's prose does not determine a byte stream, so the
parameters it leaves open are pinned by the fixture shared with the
extraction repo and restated at the top of `AudioSignature`: double
throughout, whole frames only, periodic Hann, unnormalised FFT, band mean
rather than sum, argmax ties to the lowest index.

`fixtures/audio/` holds the extraction repo's three files byte-identically
and the computed signature equals the recorded vector exactly. The binding
check regenerates the fixture PCM from `make_fixture.py`'s arithmetic and
verifies it against the recorded decode checksums, so it runs on a host
with no codec at all and a decode divergence stays distinguishable from a
DSP one; the two tests that drive real FFmpeg self-skip without a binary.

The workflow named "Test Plugin" until now only compiled one. A test that
is built and never run is not evidence, and a golden vector shared across
two repos exists precisely so CI fails when they drift.

TRACES: JR-042, JR-043 | SR-003
2026-07-31 16:24:11 +02:00
dtourolleandClaude Opus 5 2926740d03 JR-023: correct the missing-dependency logs, and test them
🏗️ Build Plugin / build (push) Successful in 29s
Latest Release / latest-release (push) Successful in 39s
🧪 Test Plugin / test (push) Successful in 26s
Both failure paths still told admins the overlay was "falling back to patching
index.html on disk". JR-021 deleted that fallback, so the message was false --
and it was false in the worst place, since an admin reads it precisely when
debugging a missing overlay and would go hunting for a patch that no longer
exists. They now name the install URL and say the overlay is disabled while
every other feature is unaffected. The not-found case is a Warning, not
Information: a headline feature being off should not sit among startup chatter.

UT-012..015 cover the branch. The File Transformation assembly is genuinely
absent from the test host, so TryRegister exercises its real not-found path
rather than a seam invented for the test. UT-015 pins that a null payload
returns empty rather than throwing -- this callback runs inside another
plugin's request path on every page served, so throwing would break the web
client itself, not just JRay's overlay.

Making those runnable needed the test project to reference Jellyfin.Controller
and Jellyfin.Model without the plugin's ExcludeAssets=runtime. My earlier claim
that DisableTransitiveFrameworkReferences alone sufficed was too narrow: it
drops the demand for the web *framework*, but ILogger and MediaBrowser.Common
live in the excluded assets, so anything past dependency-free logic failed at
run time with FileNotFoundException. Both settings are needed, and the register
now says so.

Second mutation check: downgrading the warning to Information fails UT-013 and
nothing else. Source restored and re-verified.

JR-023 reaches Done for the detection half. The config-page banner stays T4 --
verifiable only against a live server.

TRACES: UT-012, UT-013, UT-014, UT-015 | JR-023

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:18:40 +02:00
dtourolleandClaude Opus 5 cc973fd7c5 Make the test suite runnable without an ASP.NET Core runtime
🏗️ Build Plugin / build (push) Successful in 26s
Latest Release / latest-release (push) Successful in 37s
🧪 Test Plugin / test (push) Successful in 25s
The 11 tests built but could not execute: the plugin framework-references
Microsoft.AspNetCore.App through Jellyfin.Controller, that reference flows into
anything referencing the plugin, and the test host then demanded a web runtime
even for tests that touch no web type. This box has none at any version, and
Arch packages only 8 and 10 -- so "install the runtime" was neither available
here nor a clean answer.

DisableTransitiveFrameworkReferences drops the inherited reference. .NET
resolves assemblies lazily, so pure-logic types load without it. This is a
T1-tier decision rather than a workaround: requiring a web runtime to test
string and rule logic is incidental coupling. T2 -- controllers and
authorisation -- genuinely needs it, and belongs in a second project that keeps
the reference.

All 11 now pass. The suite was also checked to FAIL: removing the
newline-stripping from RemoveInjection fails UT-001 and nothing else, then the
source was restored and re-verified byte-identical. A suite that has only ever
passed is not evidence that it tests anything.

JR-016 and JR-022 therefore reach Done -- implemented and verified by tests
that execute. JR-023 stays In Progress: its detection branch has no test and
its config-page half is T4.

TRACES: JR-016, JR-022 | PR-003, PR-004

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 09:10:40 +02:00
dtourolleandClaude Opus 5 5159692364 Test project: UT-001..011 for JR-016 and JR-022
🏗️ Build Plugin / build (push) Successful in 39s
Latest Release / latest-release (push) Successful in 38s
🧪 Test Plugin / test (push) Successful in 26s
The register defined 46 requirements and zero tests, so every Done in it rested
on inspection. This adds the T1 tier: an xUnit project in the solution,
covering the two units whose logic is pure enough to test without a Jellyfin
host.

JR-022 (UT-001..006) covers the cases where removal could reach too far --
another plugin's injection, and an unmarked look-alike script tag -- because
index.html is a file JRay shares. UT-001 pins the trailing newline to the tag,
without which every install cycle leaves another blank line behind.

JR-016 (UT-007..011) covers specificity resolution, including the prioritised
series inside an ignored genre that motivated the rule, and a Series rule
valued Guid.Empty, which would otherwise swallow every movie in the library.

RemoveInjection is reached through InternalsVisibleTo rather than being made
public: it is factored out for testability, not part of the surface.

The suite BUILDS but does not RUN here. Jellyfin.Controller framework-
references Microsoft.AspNetCore.App, so the test host demands it even for pure
logic, and this machine has no ASP.NET Core runtime at any version --
RollForward cannot substitute for a framework that is absent entirely. Fix is
to install aspnet-runtime, which the CI image needs for the same reason.

So every UT here is recorded as Written, not Passing, and no requirement is
promoted to Done on their strength. A test whose result nobody has seen is not
evidence.

TRACES: UT-001, UT-002, UT-003, UT-004, UT-005, UT-006 | JR-022
TRACES: UT-007, UT-008, UT-009, UT-010, UT-011 | JR-016

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 19:12:24 +02:00