diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs b/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs index d62e530..c38e96c 100644 --- a/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs +++ b/Jellyfin.Plugin.TubeArchivistMetadata/Providers/EpisodeMetadataProvider.cs @@ -65,8 +65,31 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Providers ImageUrl = video.Channel.ThumbUrl, Type = Data.Enums.PersonKind.Actor, }); + PlaylistAssignment? playlistAssignment = null; + if (Plugin.Instance?.Configuration.SortSeasonsByPlaylist == true) + { + var playlistCache = PlaylistCache.GetInstance(); + playlistAssignment = await playlistCache.GetAssignmentAsync(videoTAId, cancellationToken).ConfigureAwait(true); + + if (playlistAssignment == null) + { + // Only bucket into "Unsorted" when the playlists were actually retrieved. + // If TubeArchivist is unreachable the video falls back to its upload year, + // which Jellyfin can still correct on a later refresh. + if (await playlistCache.HasPlaylistDataAsync(cancellationToken).ConfigureAwait(true)) + { + _logger.LogDebug("{Message}", string.Format(CultureInfo.CurrentCulture, "No TubeArchivist playlist found for video {0}. Grouping it into the {1} season.", videoTAId, Constants.UnsortedSeasonName)); + playlistAssignment = new PlaylistAssignment(string.Empty, Constants.UnsortedSeasonName, Constants.UnsortedSeasonNumber, 0); + } + else + { + _logger.LogWarning("{Message}", string.Format(CultureInfo.CurrentCulture, "TubeArchivist playlists unavailable. Grouping video {0} by upload year.", videoTAId)); + } + } + } + result.HasMetadata = true; - result.Item = video.ToEpisode(); + result.Item = video.ToEpisode(playlistAssignment); result.Item.Path = info.Path; result.Provider = Name; result.People = peopleInfo; diff --git a/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs b/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs index 22b3f44..9c810c6 100644 --- a/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs +++ b/Jellyfin.Plugin.TubeArchivistMetadata/TubeArchivist/Video/Video.cs @@ -9,6 +9,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace Jellyfin.Plugin.TubeArchivistMetadata.TubeArchivist @@ -121,15 +122,38 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.TubeArchivist /// The video equivalent Jellyfin object. public Episode ToEpisode() { + return ToEpisode(null); + } + + /// + /// Converts the TubeArchivist API video to a Jellyfin object, + /// optionally grouping it into the season representing its TubeArchivist playlist. + /// + /// + /// The playlist the video belongs to, or null to group the video by upload year. + /// + /// The video equivalent Jellyfin object. + public Episode ToEpisode(PlaylistAssignment? playlistAssignment) + { + // A null assignment always means "group by upload year". Deciding when an unassigned + // video belongs in the Unsorted season needs to know whether playlist data was actually + // retrieved, so the caller makes that call and passes an Unsorted assignment instead. + var seasonNumber = playlistAssignment?.SeasonNumber ?? Published.Year; + var seasonName = playlistAssignment?.SeasonName ?? Published.Year.ToString(CultureInfo.CurrentCulture); + return new Episode { Name = Title, Overview = Utils.FormatDescription(Description), - SeasonName = Published.Year.ToString(CultureInfo.CurrentCulture), - ParentIndexNumber = Published.Year, + + // Jellyfin overwrites SeasonName from the parent Season entity; it is set here only + // for consistency. Season naming is handled by SeasonMetadataProvider. + SeasonName = seasonName, + ParentIndexNumber = seasonNumber, IndexNumber = Plugin.Instance?.Configuration?.EpisodeNumberingScheme switch { NumberingScheme.YYYYMMDD => (Published.Year * 10000) + (Published.Month * 100) + Published.Day, + NumberingScheme.PlaylistIndex => playlistAssignment?.Index, _ => null }, SeriesName = Channel.Name,