diff --git a/CHANGELOG.md b/CHANGELOG.md index 42b3959dd..a39418748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,9 +26,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix tray icon shortcut to open logs folder on Windows - Unlock playout when playout build fails - Ignore errors deleting old HLS segments; this should improve stream reliability +- Update show year when changed within Plex ### Changed - Upgrade from .NET 7 to .NET 8 +- In schedule items, disambiguate seasons from shows with the same title by including show year + - Old format: `Show Title (Season Number)` + - New format: `Show Title (Show Year) - Season Number` ## [0.8.4-beta] - 2023-12-02 ### Fixed diff --git a/ErsatzTV.Application/MediaItems/Mapper.cs b/ErsatzTV.Application/MediaItems/Mapper.cs index bc53843ee..f087ce0f7 100644 --- a/ErsatzTV.Application/MediaItems/Mapper.cs +++ b/ErsatzTV.Application/MediaItems/Mapper.cs @@ -1,4 +1,5 @@ -using ErsatzTV.Core.Domain; +using System.Globalization; +using ErsatzTV.Core.Domain; namespace ErsatzTV.Application.MediaItems; @@ -8,13 +9,27 @@ internal static class Mapper new(show.Id, show.ShowMetadata.HeadOrNone().Map(sm => $"{sm?.Title} ({sm?.Year})").IfNone("???")); internal static NamedMediaItemViewModel ProjectToViewModel(Season season) => - new(season.Id, $"{ShowTitle(season)} ({SeasonDescription(season)})"); + new(season.Id, $"{ShowTitle(season)} - {SeasonDescription(season)}"); internal static NamedMediaItemViewModel ProjectToViewModel(Artist artist) => new(artist.Id, artist.ArtistMetadata.HeadOrNone().Match(am => am.Title, () => "???")); - private static string ShowTitle(Season season) => - season.Show.ShowMetadata.HeadOrNone().Map(sm => sm.Title).IfNone("???"); + private static string ShowTitle(Season season) + { + var title = "???"; + var year = "???"; + + foreach (ShowMetadata show in season.Show.ShowMetadata.HeadOrNone()) + { + title = show.Title; + foreach (int y in Optional(show.Year)) + { + year = y.ToString(CultureInfo.InvariantCulture); + } + } + + return $"{title} ({year})"; + } private static string SeasonDescription(Season season) => season.SeasonNumber == 0 ? "Specials" : $"Season {season.SeasonNumber}"; diff --git a/ErsatzTV.Application/Search/Queries/SearchTelevisionSeasonsHandler.cs b/ErsatzTV.Application/Search/Queries/SearchTelevisionSeasonsHandler.cs index 024104a98..d854fb4e1 100644 --- a/ErsatzTV.Application/Search/Queries/SearchTelevisionSeasonsHandler.cs +++ b/ErsatzTV.Application/Search/Queries/SearchTelevisionSeasonsHandler.cs @@ -1,3 +1,4 @@ +using System.Globalization; using ErsatzTV.Application.MediaItems; using ErsatzTV.Core.Domain; using ErsatzTV.Infrastructure.Data; @@ -25,21 +26,25 @@ public class SearchTelevisionSeasonsHandler : IRequestHandler list.Map(ToNamedMediaItem).ToList()); } - private static NamedMediaItemViewModel ToNamedMediaItem(TelevisionSeason season) => new( - season.Id, - $"{ShowTitle(season)} ({SeasonTitle(season)})"); + private static NamedMediaItemViewModel ToNamedMediaItem(TelevisionSeason season) => + new(season.Id, $"{ShowTitle(season)} - {SeasonTitle(season)}"); - private static string ShowTitle(TelevisionSeason season) => $"{season.Title ?? "???"}"; + private static string ShowTitle(TelevisionSeason season) + { + string title = season.Title ?? "???"; + string year = season.Year.HasValue ? season.Year.Value.ToString(CultureInfo.InvariantCulture) : "???"; + return $"{title} ({year})"; + } private static string SeasonTitle(TelevisionSeason season) => season.SeasonNumber == 0 ? "Specials" : $"Season {season.SeasonNumber}"; - public record TelevisionSeason(int Id, string Title, int SeasonNumber); + public record TelevisionSeason(int Id, string Title, int? Year, int SeasonNumber); } diff --git a/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs index 3df27f7b9..63cf7a0c4 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/ITelevisionRepository.cs @@ -41,4 +41,5 @@ public interface ITelevisionRepository Task UpdateTitles(EpisodeMetadata metadata, string title, string sortTitle); Task UpdateOutline(EpisodeMetadata metadata, string outline); Task UpdatePlot(EpisodeMetadata metadata, string plot); + Task UpdateYear(ShowMetadata metadata, int? year); } diff --git a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs index 47dae55fb..af60bc134 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs @@ -525,6 +525,14 @@ public class TelevisionRepository : ITelevisionRepository new { Plot = plot, MetadataId = metadata.Id }).Map(result => result > 0); } + public async Task UpdateYear(ShowMetadata metadata, int? year) + { + await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); + return await dbContext.Connection.ExecuteAsync( + "UPDATE ShowMetadata SET Year = @Year WHERE Id = @MetadataId", + new { Year = year, MetadataId = metadata.Id }).Map(result => result > 0); + } + public async Task> GetShowItems(int showId) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); diff --git a/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs b/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs index 8fcf4a841..b59059e5f 100644 --- a/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs @@ -441,6 +441,14 @@ public class PlexTelevisionLibraryScanner : result.IsUpdated = true; } + if (existingMetadata.Year != fullMetadata.Year) + { + if (await _televisionRepository.UpdateYear(existingMetadata, fullMetadata.Year)) + { + result.IsUpdated = true; + } + } + if (result.IsUpdated) { await _metadataRepository.MarkAsUpdated(existingMetadata, fullMetadata.DateUpdated);