diff --git a/CHANGELOG.md b/CHANGELOG.md index 72ec00cda..b6b4fedff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added - Support `(Part #)` name suffixes for multi-part episode grouping +- Support multi-episode files in local libraries + +### Fixed +- Fix search result crashes due to missing season metadata ## [0.0.42-prealpha] - 2021-05-31 ### Added diff --git a/ErsatzTV.Application/MediaCards/Mapper.cs b/ErsatzTV.Application/MediaCards/Mapper.cs index 9c6a701ca..082d038ba 100644 --- a/ErsatzTV.Application/MediaCards/Mapper.cs +++ b/ErsatzTV.Application/MediaCards/Mapper.cs @@ -57,10 +57,7 @@ namespace ErsatzTV.Application.MediaCards em => em.Plot ?? string.Empty, () => string.Empty), isSearchResult - ? GetPoster( - episodeMetadata.Episode.Season.SeasonMetadata.Head(), - maybeJellyfin, - maybeEmby) + ? GetEpisodePoster(episodeMetadata, maybeJellyfin, maybeEmby) : GetThumbnail(episodeMetadata, maybeJellyfin, maybeEmby), episodeMetadata.Directors.Map(d => d.Name).ToList(), episodeMetadata.Writers.Map(w => w.Name).ToList()); @@ -146,6 +143,24 @@ namespace ErsatzTV.Application.MediaCards private static string GetSeasonName(int number) => number == 0 ? "Specials" : $"Season {number}"; + private static string GetEpisodePoster( + EpisodeMetadata episodeMetadata, + Option maybeJellyfin, + Option maybeEmby) + { + Option maybeSeasonMetadata = episodeMetadata.Episode.Season.SeasonMetadata.HeadOrNone(); + return maybeSeasonMetadata.Match( + seasonMetadata => GetPoster(seasonMetadata, maybeJellyfin, maybeEmby), + () => + { + Option maybeShowMetadata = + episodeMetadata.Episode.Season.Show.ShowMetadata.HeadOrNone(); + return maybeShowMetadata.Match( + showMetadata => GetPoster(showMetadata, maybeJellyfin, maybeEmby), + () => string.Empty); + }); + } + private static string GetPoster( Metadata metadata, Option maybeJellyfin, diff --git a/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs b/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs index 51a636857..600ed7d20 100644 --- a/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs +++ b/ErsatzTV.Core.Tests/Fakes/FakeTelevisionRepository.cs @@ -95,7 +95,5 @@ namespace ErsatzTV.Core.Tests.Fakes public Task AddDirector(EpisodeMetadata metadata, Director director) => throw new NotSupportedException(); public Task AddWriter(EpisodeMetadata metadata, Writer writer) => throw new NotSupportedException(); - - public Task AddMetadata(Episode episode, EpisodeMetadata metadata) => throw new NotSupportedException(); } } diff --git a/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs b/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs index 2335b3119..9071f055c 100644 --- a/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs +++ b/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs @@ -176,6 +176,8 @@ namespace ErsatzTV.Core.Metadata private async Task ApplyMetadataUpdate(Episode episode, List episodeMetadata) { + var updated = false; + episode.EpisodeMetadata ??= new List(); var toUpdate = episode.EpisodeMetadata @@ -189,6 +191,7 @@ namespace ErsatzTV.Core.Metadata foreach (EpisodeMetadata metadata in toRemove) { await _televisionRepository.RemoveMetadata(episode, metadata); + updated = true; } foreach (EpisodeMetadata metadata in toAdd) @@ -200,14 +203,14 @@ namespace ErsatzTV.Core.Metadata metadata.Episode = episode; episode.EpisodeMetadata.Add(metadata); - await _metadataRepository.Add(metadata); + updated = await _metadataRepository.Add(metadata) || updated; } foreach (EpisodeMetadata metadata in toUpdate) { Option maybeExisting = episode.EpisodeMetadata.Find(em => em.EpisodeNumber == metadata.EpisodeNumber); - await maybeExisting.Match( + updated = await maybeExisting.Match( async existing => { existing.Outline = metadata.Outline; @@ -229,13 +232,13 @@ namespace ErsatzTV.Core.Metadata ? _fallbackMetadataProvider.GetSortTitle(metadata.Title) : metadata.SortTitle; - bool updated = await UpdateMetadataCollections( + updated = await UpdateMetadataCollections( existing, metadata, (_, _) => Task.FromResult(false), (_, _) => Task.FromResult(false), (_, _) => Task.FromResult(false), - _televisionRepository.AddActor); + _televisionRepository.AddActor) || updated; foreach (Director director in existing.Directors .Filter(d => metadata.Directors.All(d2 => d2.Name != d.Name)).ToList()) @@ -299,10 +302,10 @@ namespace ErsatzTV.Core.Metadata return await _metadataRepository.Update(existing) || updated; }, - () => Task.FromResult(false)); + () => Task.FromResult(updated)) || updated; } - return true; + return updated; } private Task ApplyMetadataUpdate(Movie movie, MovieMetadata metadata) => diff --git a/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs b/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs index 740d00303..6084e14e6 100644 --- a/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs @@ -22,6 +22,7 @@ namespace ErsatzTV.Core.Metadata private readonly ILibraryRepository _libraryRepository; private readonly ILocalFileSystem _localFileSystem; private readonly ILocalMetadataProvider _localMetadataProvider; + private readonly IMetadataRepository _metadataRepository; private readonly ILogger _logger; private readonly IMediator _mediator; private readonly ISearchIndex _searchIndex; @@ -49,6 +50,7 @@ namespace ErsatzTV.Core.Metadata _localFileSystem = localFileSystem; _televisionRepository = televisionRepository; _localMetadataProvider = localMetadataProvider; + _metadataRepository = metadataRepository; _searchIndex = searchIndex; _searchRepository = searchRepository; _libraryRepository = libraryRepository; @@ -169,6 +171,7 @@ namespace ErsatzTV.Core.Metadata { Either maybeSeason = await _televisionRepository .GetOrAddSeason(show, libraryPath.Id, seasonNumber) + .BindT(EnsureMetadataExists) .BindT(season => UpdatePoster(season, seasonFolder)); await maybeSeason.Match( @@ -270,8 +273,28 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> UpdateMetadata( - Episode episode) + private async Task> EnsureMetadataExists(Season season) + { + season.SeasonMetadata ??= new List(); + + if (!season.SeasonMetadata.Any()) + { + var metadata = new SeasonMetadata + { + SeasonId = season.Id, + Season = season, + DateAdded = DateTime.UtcNow, + Guids = new List() + }; + + season.SeasonMetadata.Add(metadata); + await _metadataRepository.Add(metadata); + } + + return season; + } + + private async Task> UpdateMetadata(Episode episode) { try { diff --git a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs index c2b41224b..48a7e83f4 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs @@ -97,6 +97,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories .ThenInclude(e => e.Season) .ThenInclude(s => s.Show) .ThenInclude(s => s.ShowMetadata) + .ThenInclude(sm => sm.Artwork) .OrderBy(em => em.SortTitle) .ToListAsync(); }