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
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Plugin.JRay.Configuration;
|
||||
|
||||
namespace Jellyfin.Plugin.JRay.Services.Interfaces;
|
||||
|
||||
/// <summary>
|
||||
/// Fetches actor-timeline manifests from the configured public servers.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Satisfies <c>JRay-public-server</c> UR-007: the plugin queries a configurable,
|
||||
/// <b>ordered</b> list of servers, and the first result clearing the configured
|
||||
/// match tier wins.
|
||||
/// </remarks>
|
||||
// TRACES: JR-025 | PR-006
|
||||
public interface IManifestExchangeClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Fetches a movie manifest from the first server that has an acceptable one.
|
||||
/// </summary>
|
||||
/// <param name="servers">The configured servers, in trust order.</param>
|
||||
/// <param name="minimumTier">The lowest cut-match tier that may be stored.</param>
|
||||
/// <param name="query">Identity and cut parameters for the local item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The accepted manifest, or null when no server had one.</returns>
|
||||
Task<ManifestFetchOutcome?> FetchMovieAsync(
|
||||
IReadOnlyList<ManifestServer> servers,
|
||||
MatchTier minimumTier,
|
||||
TitleQuery query,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a single episode manifest.
|
||||
/// </summary>
|
||||
/// <param name="servers">The configured servers, in trust order.</param>
|
||||
/// <param name="minimumTier">The lowest cut-match tier that may be stored.</param>
|
||||
/// <param name="query">Identity and cut parameters for the local item.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The accepted manifest, or null when no server had one.</returns>
|
||||
Task<ManifestFetchOutcome?> FetchEpisodeAsync(
|
||||
IReadOnlyList<ManifestServer> servers,
|
||||
MatchTier minimumTier,
|
||||
TitleQuery query,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Per-server reachability and last error, for the configuration page.
|
||||
/// </summary>
|
||||
/// <param name="servers">The configured servers.</param>
|
||||
/// <returns>One status entry per configured server.</returns>
|
||||
IReadOnlyList<ServerStatus> GetStatus(IReadOnlyList<ManifestServer> servers);
|
||||
}
|
||||
Reference in New Issue
Block a user