feat(config): add playlist season settings and persistent season map
Adds the configuration surface for grouping seasons by TubeArchivist playlist. No behaviour yet; the settings are inert until the providers land. - SortSeasonsByPlaylist toggles the feature. - PlaylistSeasonMap persists playlist to season number associations so numbers stay stable across restarts and TubeArchivist outages. - SeasonFetcherAutoEnabled records the one time library migration. - NumberingScheme gains PlaylistIndex. Both serializers persist enums by name, so appending a member is safe. - Constants gains the reserved Unsorted season (9000). Season 0 is never assigned because Jellyfin force-renames it to "Specials". PlaylistSeasonMap carries [JsonInclude] deliberately. Jellyfin stores plugin configuration as XML, which round-trips a private setter via the ICollection add pattern, but the configuration API endpoint deserializes with System.Text.Json, which ignores non-public setters. Without the attribute the collection is dropped on every configuration page save and BasePlugin.UpdateConfiguration writes the emptied collection back to disk. A public setter is not an alternative: CA2227 is enabled and TreatWarningsAsErrors would fail the build.
This commit is contained in:
parent
72ed3359b2
commit
fb15b8d93d
|
|
@ -14,4 +14,10 @@ public enum NumberingScheme
|
|||
/// YYYYMMDD (e.g. 20250804 for August 4th, 2025).
|
||||
/// </summary>
|
||||
YYYYMMDD,
|
||||
|
||||
/// <summary>
|
||||
/// The video position within its TubeArchivist playlist.
|
||||
/// Falls back to <see cref="Default"/> when the video does not belong to a playlist.
|
||||
/// </summary>
|
||||
PlaylistIndex,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// A persisted association between a TubeArchivist playlist and the Jellyfin season number
|
||||
/// used to represent it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Jellyfin groups episodes into seasons using <c>ParentIndexNumber</c>, which is an integer,
|
||||
/// while TubeArchivist identifies playlists by string id. This entry persists the mapping so
|
||||
/// season numbers stay stable across restarts and metadata refreshes.
|
||||
/// A plain dictionary is not used because Jellyfin's XML configuration serializer does not
|
||||
/// reliably round-trip dictionary types.
|
||||
/// </remarks>
|
||||
public class PlaylistSeasonMapEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PlaylistSeasonMapEntry"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A parameterless constructor is required by the XML serializer used to persist the
|
||||
/// plugin configuration.
|
||||
/// </remarks>
|
||||
public PlaylistSeasonMapEntry()
|
||||
{
|
||||
PlaylistId = string.Empty;
|
||||
PlaylistName = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the TubeArchivist playlist id.
|
||||
/// </summary>
|
||||
public string PlaylistId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Jellyfin season number representing the playlist.
|
||||
/// </summary>
|
||||
public int SeasonNumber { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last known TubeArchivist playlist name.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Stored so seasons keep a usable label when TubeArchivist is unreachable.
|
||||
/// </remarks>
|
||||
public string PlaylistName { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jellyfin.Plugin.TubeArchivistMetadata.Utilities;
|
||||
using MediaBrowser.Model.Plugins;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -47,6 +49,9 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration
|
|||
TAJFProgressTaskInterval = 60;
|
||||
JFTAPlaylistsSyncTaskInterval = 60;
|
||||
TAJFPlaylistsSyncTaskInterval = 60;
|
||||
SortSeasonsByPlaylist = false;
|
||||
SeasonFetcherAutoEnabled = false;
|
||||
PlaylistSeasonMap = new Collection<PlaylistSeasonMapEntry>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -191,6 +196,49 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Configuration
|
|||
/// </summary>
|
||||
public NumberingScheme EpisodeNumberingScheme { get; set; } = NumberingScheme.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to group episodes into seasons by TubeArchivist
|
||||
/// playlist instead of by upload year.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Changing this setting requires a "Refresh metadata" with "Replace all metadata" enabled:
|
||||
/// Jellyfin only overwrites an existing season name when replacing metadata.
|
||||
/// </remarks>
|
||||
public bool SortSeasonsByPlaylist { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the persisted TubeArchivist playlist to Jellyfin season number associations.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Populated automatically; not user editable. Entries are append-only so season numbers
|
||||
/// remain stable once assigned.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="JsonIncludeAttribute"/> is required and must not be removed. Jellyfin persists
|
||||
/// plugin configuration as XML, but the plugin configuration API endpoint deserializes the
|
||||
/// posted body with System.Text.Json, which ignores non-public setters. Without this
|
||||
/// attribute the collection is dropped whenever the configuration page is saved, and
|
||||
/// <c>BasePlugin.UpdateConfiguration</c> then writes the emptied collection back to disk.
|
||||
/// A public setter is not an option because it violates CA2227.
|
||||
/// This is unrelated to the Newtonsoft usage of the TubeArchivist API models.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[JsonInclude]
|
||||
public Collection<PlaylistSeasonMapEntry> PlaylistSeasonMap { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the one time migration which enables this
|
||||
/// plugin's Season metadata fetcher on existing libraries has already run.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Libraries created before the Season provider existed store an empty Season fetcher list,
|
||||
/// which Jellyfin treats as "all disabled" rather than "use the defaults". The migration
|
||||
/// repairs that once; keeping this flag means a fetcher removed by hand afterwards stays
|
||||
/// removed instead of being re-added on every restart.
|
||||
/// </remarks>
|
||||
public bool SeasonFetcherAutoEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the playback progress owners Jellyfin usernames to synchronize data from TubeArchivist.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -19,5 +19,19 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata
|
|||
/// Providers name.
|
||||
/// </summary>
|
||||
public const string ProviderName = "TubeArchivist";
|
||||
|
||||
/// <summary>
|
||||
/// Season number used to group videos which do not belong to any TubeArchivist playlist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Season 0 cannot be used: Jellyfin's <c>SeasonMetadataService.BeforeSaveInternal</c>
|
||||
/// force-renames season 0 to the library's "Specials" display name.
|
||||
/// </remarks>
|
||||
public const int UnsortedSeasonNumber = 9000;
|
||||
|
||||
/// <summary>
|
||||
/// Display name of the season grouping videos which do not belong to any TubeArchivist playlist.
|
||||
/// </summary>
|
||||
public const string UnsortedSeasonName = "Unsorted";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue