Files
jRay/Jellyfin.Plugin.JRay/Configuration/PluginConfiguration.cs
T
dtourolleandClaude Opus 5 3b24fe1b3c Requirements register, spec rewrite, and TRACES tags
jRay had no requirement IDs, so nothing in this repo could be traced to and
the CI gate had no denominator to read. The other two components had already
moved to registers; this brings the plugin level with them.

Adds docs/requirements.md with 46 permanent JR-nnn IDs, each carrying a parent
requirement, priority, status and verification tier, plus a per-requirement
verification plan. JR is flat rather than split by theme: the plugin is one
deployable with one audience, and JRay-public-server already ships UR/DR, so a
second repo using those prefixes would make UR-007 ambiguous across registers.

Rewrites SPEC.md as requirements prose with Current:/Gap: on every one. It had
drifted into a format-plus-API reference that documented schema_version 1 while
owning a format whose v2 shape was specified only in the other two repos, said
nothing about SR-002's scene-scoped semantics, and carried the manifest
exchange as a "planned" aside while its configuration classes were already
implemented. Plugin-side exchange obligations move here from the server's
spec, where they were an ownership inversion.

Adds JR-038..041 for PR-005, which had no software row in any repo -- it was
held structurally by SR-004 and GR-005 both being prohibitions, and a goal
preserved only by prohibitions is the kind that erodes unnoticed. jRay is the
component that actually opens a socket.

Tags 18 units with the requirements they satisfy. Tags name what the code
satisfies, so FileTransformationRegistration is not tagged JR-021: that
requirement is a prohibition and was still violated elsewhere when this was
written.

Vendors jray-project as a submodule for the system spec and shared gate.

TRACES: JR-001, JR-004, JR-005, JR-007, JR-008, JR-009, JR-010, JR-011
TRACES: JR-012, JR-013, JR-014, JR-015, JR-016, JR-017, JR-018, JR-019
TRACES: JR-020, JR-024, JR-025, JR-036, JR-038

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 18:57:48 +02:00

138 lines
5.5 KiB
C#

using System.Collections.ObjectModel;
using MediaBrowser.Model.Plugins;
namespace Jellyfin.Plugin.JRay.Configuration;
/// <summary>
/// Plugin configuration.
/// </summary>
/// <remarks>
/// Every manifest-exchange switch here defaults to <b>off</b>, including the
/// pre-configured community server, so no traffic leaves an installation until
/// an admin acts. Fetching and contributing each reveal to a server operator
/// that some instance holds a given title; that is inherent to the exchange, so
/// the defaults bound the exposure rather than pretending to remove it.
/// </remarks>
// TRACES: JR-036, JR-038 | PR-005
public class PluginConfiguration : BasePluginConfiguration
{
/// <summary>
/// The community manifest exchange. Shipped pre-configured but
/// <b>disabled</b>, so no traffic leaves an installation until an admin opts
/// in (public server specification, §9).
/// </summary>
public const string CommunityServerUrl = "https://jray.tourolle.paris";
/// <summary>
/// Display name for <see cref="CommunityServerUrl"/>.
/// </summary>
public const string CommunityServerName = "JRay Community";
/// <summary>
/// Initializes a new instance of the <see cref="PluginConfiguration"/> class.
/// </summary>
public PluginConfiguration()
{
TruthFileSuffix = ".jray.json";
CacheDurationMinutes = 60;
EnableOverlay = true;
// Manifest sharing is a network egress feature, so every part of it is
// off by default (public server specification, §9 "Configuration").
EnableManifestSharing = false;
ContributeManifests = false;
ComputeAudioSignatures = false;
MinimumMatchTier = MatchTier.Runtime;
// Pre-configured but disabled: the admin opts in by enabling it, rather
// than by having to discover and type a URL.
Servers.Add(new ManifestServer
{
Url = CommunityServerUrl,
Name = CommunityServerName,
Enabled = false,
AllowContribute = false,
TrustLevel = ServerTrustLevel.FetchOnly,
});
}
/// <summary>
/// Gets or sets the filename suffix used to find a scene-actor-extraction
/// "truth" file for a media item. The plugin looks for a file named
/// "&lt;media file basename&gt;&lt;TruthFileSuffix&gt;" next to the media file,
/// e.g. "Movie.mkv" -&gt; "Movie.jray.json".
/// </summary>
public string TruthFileSuffix { get; set; }
/// <summary>
/// Gets or sets how long (in minutes) a loaded truth file is cached in
/// memory before being re-read from disk.
/// </summary>
public int CacheDurationMinutes { get; set; }
/// <summary>
/// Gets or sets a value indicating whether JRay should inject its
/// pause-overlay script into the web client's index.html. When disabled,
/// any previously injected script is removed.
/// </summary>
public bool EnableOverlay { get; set; }
/// <summary>
/// Gets or sets a value indicating whether JRay may fetch actor-timeline
/// manifests from the configured servers. Off by default — this is a network
/// egress feature and must be opt-in.
/// </summary>
/// <remarks>
/// Fetching reveals to a server operator that some instance holds a given
/// title. That is inherent to the exchange, and each configured server
/// multiplies the exposure, which the configuration page states plainly.
/// </remarks>
public bool EnableManifestSharing { get; set; }
/// <summary>
/// Gets or sets a value indicating whether locally generated manifests may be
/// contributed back. A separate opt-in from downloading, and off by default.
/// </summary>
/// <remarks>
/// Contribution additionally requires <see cref="ManifestServer.AllowContribute"/>
/// on the specific server and a token for it. Uploads are never fanned out to
/// every configured server.
/// </remarks>
public bool ContributeManifests { get; set; }
/// <summary>
/// Gets or sets the minimum cut-match tier a fetched manifest must reach
/// before it is stored.
/// </summary>
/// <remarks>
/// Defaults to <see cref="MatchTier.Runtime"/>. <see cref="MatchTier.Loose"/>
/// admits manifests whose runtime differs by up to 30s, which may be a
/// different trim of the same cut — usable, but it should be surfaced as a
/// caveat rather than applied silently.
/// </remarks>
public MatchTier MinimumMatchTier { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the plugin computes audio
/// signatures for library items, enabling content-based cut matching and
/// identification of files whose providence is unknown.
/// </summary>
/// <remarks>
/// Off by default. Uses the FFmpeg binary Jellyfin already ships (via
/// <c>IMediaEncoder.EncoderPath</c>), so there is no extra dependency, but it
/// costs roughly a second or two of I/O per item and is therefore opt-in.
/// </remarks>
public bool ComputeAudioSignatures { get; set; }
/// <summary>
/// Gets the ordered list of manifest servers.
/// </summary>
/// <remarks>
/// Order is the user's trust ranking: for a fetch, servers are tried in order
/// and the first result clearing <see cref="MinimumMatchTier"/> wins. For a
/// series, first-match applies per <i>episode</i>, so a later server is
/// queried only for the episodes earlier ones lacked.
/// </remarks>
public Collection<ManifestServer> Servers { get; } = new();
}