From fd8d464809ddb58818590062e5b5b131a570dbca Mon Sep 17 00:00:00 2001 From: Jason Dove Date: Sat, 27 Feb 2021 16:12:08 -0600 Subject: [PATCH] fix unit tests --- .../Metadata/FallbackMetadataProviderTests.cs | 12 ++--- .../Metadata/MovieFolderScannerTests.cs | 26 ++++++---- .../Interfaces/Images/IImageCache.cs | 2 + .../Metadata/FallbackMetadataProvider.cs | 13 +++-- ErsatzTV.Core/Metadata/LocalFolderScanner.cs | 24 ++------- .../Metadata/LocalMetadataProvider.cs | 49 +++++++------------ ErsatzTV.Infrastructure/Images/ImageCache.cs | 24 +++++++++ 7 files changed, 75 insertions(+), 75 deletions(-) diff --git a/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs b/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs index 937357aa5..1359cb232 100644 --- a/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs +++ b/ErsatzTV.Core.Tests/Metadata/FallbackMetadataProviderTests.cs @@ -1,4 +1,5 @@ -using ErsatzTV.Core.Domain; +using System; +using ErsatzTV.Core.Domain; using ErsatzTV.Core.Metadata; using FluentAssertions; using NUnit.Framework; @@ -37,14 +38,13 @@ namespace ErsatzTV.Core.Tests.Metadata 2)] public void GetFallbackMetadata_ShouldHandleVariousFormats(string path, string title, int season, int episode) { - EpisodeMetadata metadata = FallbackMetadataProvider.GetFallbackMetadata( - new Episode - { Path = path, LibraryPath = new LibraryPath() }); + (EpisodeMetadata metadata, int episodeNumber) = FallbackMetadataProvider.GetFallbackMetadata( + new Episode { Path = path, LibraryPath = new LibraryPath() }); metadata.Title.Should().Be(title); + // TODO: how can we test season number? do we need to? // metadata.Season.Should().Be(season); - // metadata.Episode.Should().Be(episode); - // TODO: how do we test season and episode number? + episodeNumber.Should().Be(episode); } } } diff --git a/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs b/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs index 513abc9e8..42d043e11 100644 --- a/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs +++ b/ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Threading.Tasks; @@ -42,12 +43,20 @@ namespace ErsatzTV.Core.Tests.Metadata _movieRepository = new Mock(); _movieRepository.Setup(x => x.GetOrAdd(It.IsAny(), It.IsAny())) .Returns( - (LibraryPath _, string path) => - Right(new Movie { Path = path }).AsTask()); + (LibraryPath _, string path) => Right(new Movie { Path = path }).AsTask()); _localStatisticsProvider = new Mock(); _localMetadataProvider = new Mock(); + // fallback metadata adds metadata to a movie, so we need to replicate that here + _localMetadataProvider.Setup(x => x.RefreshFallbackMetadata(It.IsAny())) + .Returns( + (MediaItem mediaItem) => + { + ((Movie) mediaItem).MovieMetadata = new List { new() }; + return Unit.Default.AsTask(); + }); + _imageCache = new Mock(); _imageCache.Setup( x => x.ResizeAndSaveImage(FakeLocalFileSystem.TestBytes, It.IsAny(), It.IsAny())) @@ -186,7 +195,7 @@ namespace ErsatzTV.Core.Tests.Metadata MovieFolderScanner service = GetService( new FakeFileEntry(moviePath), - new FakeFileEntry(posterPath) + new FakeFileEntry(posterPath) { LastWriteTime = DateTime.Now } ); var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; @@ -206,7 +215,7 @@ namespace ErsatzTV.Core.Tests.Metadata Times.Once); _imageCache.Verify( - x => x.ResizeAndSaveImage(FakeLocalFileSystem.TestBytes, It.IsAny(), It.IsAny()), + x => x.CopyArtworkToCache(posterPath, ArtworkKind.Poster), Times.Once); } @@ -227,7 +236,7 @@ namespace ErsatzTV.Core.Tests.Metadata MovieFolderScanner service = GetService( new FakeFileEntry(moviePath), - new FakeFileEntry(posterPath) + new FakeFileEntry(posterPath) { LastWriteTime = DateTime.Now } ); var libraryPath = new LibraryPath { Id = 1, Path = FakeRoot }; @@ -247,7 +256,7 @@ namespace ErsatzTV.Core.Tests.Metadata Times.Once); _imageCache.Verify( - x => x.ResizeAndSaveImage(FakeLocalFileSystem.TestBytes, It.IsAny(), It.IsAny()), + x => x.CopyArtworkToCache(posterPath, ArtworkKind.Poster), Times.Once); } @@ -354,9 +363,6 @@ namespace ErsatzTV.Core.Tests.Metadata } private MovieFolderScanner GetService(params FakeFileEntry[] files) => - // var mockImageCache = new Mock(); - // mockImageCache.Setup(i => i.ResizeAndSaveImage(It.IsAny(), It.IsAny(), It.IsAny())) - // .Returns(Right("image").AsTask()); new( new FakeLocalFileSystem(new List(files)), _movieRepository.Object, diff --git a/ErsatzTV.Core/Interfaces/Images/IImageCache.cs b/ErsatzTV.Core/Interfaces/Images/IImageCache.cs index 1d2d6d176..513967f29 100644 --- a/ErsatzTV.Core/Interfaces/Images/IImageCache.cs +++ b/ErsatzTV.Core/Interfaces/Images/IImageCache.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using ErsatzTV.Core.Domain; using LanguageExt; namespace ErsatzTV.Core.Interfaces.Images @@ -8,5 +9,6 @@ namespace ErsatzTV.Core.Interfaces.Images Task> ResizeImage(byte[] imageBuffer, int height); Task> ResizeAndSaveImage(byte[] imageBuffer, int? height, int? width); Task> SaveImage(byte[] imageBuffer); + string CopyArtworkToCache(string path, ArtworkKind artworkKind); } } diff --git a/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs b/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs index 7e76905a2..f7dfc1b0c 100644 --- a/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs +++ b/ErsatzTV.Core/Metadata/FallbackMetadataProvider.cs @@ -3,6 +3,7 @@ using System.IO; using System.Text.RegularExpressions; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Metadata; +using static LanguageExt.Prelude; namespace ErsatzTV.Core.Metadata { @@ -16,12 +17,12 @@ namespace ErsatzTV.Core.Metadata return GetTelevisionShowMetadata(fileName, metadata); } - public static EpisodeMetadata GetFallbackMetadata(Episode episode) + public static Tuple GetFallbackMetadata(Episode episode) { string fileName = Path.GetFileName(episode.Path); var metadata = new EpisodeMetadata { MetadataKind = MetadataKind.Fallback, Title = fileName ?? episode.Path }; - return fileName != null ? GetEpisodeMetadata(fileName, metadata) : metadata; + return fileName != null ? GetEpisodeMetadata(fileName, metadata) : Tuple(metadata, 0); } public static MovieMetadata GetFallbackMetadata(Movie movie) @@ -32,7 +33,7 @@ namespace ErsatzTV.Core.Metadata return fileName != null ? GetMovieMetadata(fileName, metadata) : metadata; } - private static EpisodeMetadata GetEpisodeMetadata(string fileName, EpisodeMetadata metadata) + private static Tuple GetEpisodeMetadata(string fileName, EpisodeMetadata metadata) { try { @@ -41,9 +42,7 @@ namespace ErsatzTV.Core.Metadata if (match.Success) { metadata.Title = match.Groups[1].Value; - // TODO: set episode number? - // metadata.Season = int.Parse(match.Groups[2].Value); - // metadata.Episode = int.Parse(match.Groups[3].Value); + return Tuple(metadata, int.Parse(match.Groups[3].Value)); } } catch (Exception) @@ -51,7 +50,7 @@ namespace ErsatzTV.Core.Metadata // ignored } - return metadata; + return Tuple(metadata, 0); } private static MovieMetadata GetMovieMetadata(string fileName, MovieMetadata metadata) diff --git a/ErsatzTV.Core/Metadata/LocalFolderScanner.cs b/ErsatzTV.Core/Metadata/LocalFolderScanner.cs index 450305df1..de1f2b27b 100644 --- a/ErsatzTV.Core/Metadata/LocalFolderScanner.cs +++ b/ErsatzTV.Core/Metadata/LocalFolderScanner.cs @@ -3,13 +3,13 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; -using System.Text; using System.Threading.Tasks; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Images; using ErsatzTV.Core.Interfaces.Metadata; using LanguageExt; using Microsoft.Extensions.Logging; +using static LanguageExt.Prelude; namespace ErsatzTV.Core.Metadata { @@ -91,7 +91,7 @@ namespace ErsatzTV.Core.Metadata metadata.Artwork ??= new List(); Option maybePoster = - metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == artworkKind); + Optional(metadata.Artwork).Flatten().FirstOrDefault(a => a.ArtworkKind == artworkKind); bool shouldRefresh = maybePoster.Match( artwork => artwork.DateUpdated < lastWriteTime, @@ -100,7 +100,7 @@ namespace ErsatzTV.Core.Metadata if (shouldRefresh) { _logger.LogDebug("Refreshing {Attribute} from {Path}", artworkKind, artworkFile); - string cacheName = CopyArtworkToCache(artworkFile, artworkKind); + string cacheName = _imageCache.CopyArtworkToCache(artworkFile, artworkKind); maybePoster.Match( artwork => @@ -126,23 +126,5 @@ namespace ErsatzTV.Core.Metadata return false; } - - private string CopyArtworkToCache(string path, ArtworkKind artworkKind) - { - var filenameKey = $"{path}:{_localFileSystem.GetLastWriteTime(path).ToFileTimeUtc()}"; - byte[] hash = Crypto.ComputeHash(Encoding.UTF8.GetBytes(filenameKey)); - string hex = BitConverter.ToString(hash).Replace("-", string.Empty); - string subfolder = hex.Substring(0, 2); - string baseFolder = artworkKind switch - { - ArtworkKind.Poster => Path.Combine(FileSystemLayout.PosterCacheFolder, subfolder), - ArtworkKind.Thumbnail => Path.Combine(FileSystemLayout.ThumbnailCacheFolder, subfolder), - _ => FileSystemLayout.ImageCacheFolder - }; - string target = Path.Combine(baseFolder, hex); - _localFileSystem.CopyFile(path, target); - - return hex; - } } } diff --git a/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs b/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs index b6e8f09ea..cd99a9f70 100644 --- a/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs +++ b/ErsatzTV.Core/Metadata/LocalMetadataProvider.cs @@ -83,8 +83,10 @@ namespace ErsatzTV.Core.Metadata ApplyMetadataUpdate(televisionShow, _fallbackMetadataProvider.GetFallbackMetadataForShow(showFolder)) .ToUnit(); - private async Task ApplyMetadataUpdate(Episode episode, EpisodeMetadata metadata) + private async Task ApplyMetadataUpdate(Episode episode, Tuple metadataEpisodeNumber) { + (EpisodeMetadata metadata, int episodeNumber) = metadataEpisodeNumber; + episode.EpisodeNumber = episodeNumber; Optional(episode.EpisodeMetadata).Flatten().HeadOrNone().Match( existing => { @@ -169,16 +171,10 @@ namespace ErsatzTV.Core.Metadata return None; } - // if (!(mediaItem.Source is LocalMediaSource)) - // { - // _logger.LogDebug("Media source {Name} is not a local media source", mediaItem.Source.Name); - // return None; - // } - return await LoadMovieMetadata(mediaItem, nfoFileName); } - private async Task> LoadMetadata(Episode mediaItem, string nfoFileName) + private async Task>> LoadMetadata(Episode mediaItem, string nfoFileName) { if (nfoFileName == null || !File.Exists(nfoFileName)) { @@ -186,18 +182,10 @@ namespace ErsatzTV.Core.Metadata return None; } - // if (!(mediaItem.Source is LocalMediaSource)) - // { - // _logger.LogDebug("Media source {Name} is not a local media source", mediaItem.Source.Name); - // return None; - // } - return await LoadEpisodeMetadata(mediaItem, nfoFileName); } - private async Task> LoadMetadata( - Show televisionShow, - string nfoFileName) + private async Task> LoadMetadata(Show televisionShow, string nfoFileName) { if (nfoFileName == null || !File.Exists(nfoFileName)) { @@ -235,32 +223,31 @@ namespace ErsatzTV.Core.Metadata } } - private async Task> LoadEpisodeMetadata( - Episode mediaItem, - string nfoFileName) + private async Task>> LoadEpisodeMetadata(Episode episode, string nfoFileName) { try { await using FileStream fileStream = File.Open(nfoFileName, FileMode.Open, FileAccess.Read); Option maybeNfo = EpisodeSerializer.Deserialize(fileStream) as TvShowEpisodeNfo; - return maybeNfo.Match>( - nfo => new EpisodeMetadata + return maybeNfo.Match>>( + nfo => { - MetadataKind = MetadataKind.Sidecar, - DateUpdated = File.GetLastWriteTimeUtc(nfoFileName), - Title = nfo.Title, - ReleaseDate = GetAired(nfo.Aired), - // Episode = nfo.Episode, - // Season = nfo.Season, - // TODO: save episode number somewhere? - Plot = nfo.Plot + var metadata = new EpisodeMetadata + { + MetadataKind = MetadataKind.Sidecar, + DateUpdated = File.GetLastWriteTimeUtc(nfoFileName), + Title = nfo.Title, + ReleaseDate = GetAired(nfo.Aired), + Plot = nfo.Plot + }; + return Tuple(metadata, nfo.Episode); }, None); } catch (Exception ex) { _logger.LogDebug(ex, "Failed to read TV episode nfo metadata from {Path}", nfoFileName); - return FallbackMetadataProvider.GetFallbackMetadata(mediaItem); + return FallbackMetadataProvider.GetFallbackMetadata(episode); } } diff --git a/ErsatzTV.Infrastructure/Images/ImageCache.cs b/ErsatzTV.Infrastructure/Images/ImageCache.cs index 5ad2332a8..c670adee9 100644 --- a/ErsatzTV.Infrastructure/Images/ImageCache.cs +++ b/ErsatzTV.Infrastructure/Images/ImageCache.cs @@ -1,9 +1,12 @@ using System; using System.IO; using System.Security.Cryptography; +using System.Text; using System.Threading.Tasks; using ErsatzTV.Core; +using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Images; +using ErsatzTV.Core.Interfaces.Metadata; using LanguageExt; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Jpeg; @@ -14,9 +17,12 @@ namespace ErsatzTV.Infrastructure.Images public class ImageCache : IImageCache { private static readonly SHA1CryptoServiceProvider Crypto; + private readonly ILocalFileSystem _localFileSystem; static ImageCache() => Crypto = new SHA1CryptoServiceProvider(); + public ImageCache(ILocalFileSystem localFileSystem) => _localFileSystem = localFileSystem; + public async Task> ResizeImage(byte[] imageBuffer, int height) { await using var inStream = new MemoryStream(imageBuffer); @@ -81,5 +87,23 @@ namespace ErsatzTV.Infrastructure.Images return BaseError.New(ex.Message); } } + + public string CopyArtworkToCache(string path, ArtworkKind artworkKind) + { + var filenameKey = $"{path}:{_localFileSystem.GetLastWriteTime(path).ToFileTimeUtc()}"; + byte[] hash = Crypto.ComputeHash(Encoding.UTF8.GetBytes(filenameKey)); + string hex = BitConverter.ToString(hash).Replace("-", string.Empty); + string subfolder = hex.Substring(0, 2); + string baseFolder = artworkKind switch + { + ArtworkKind.Poster => Path.Combine(FileSystemLayout.PosterCacheFolder, subfolder), + ArtworkKind.Thumbnail => Path.Combine(FileSystemLayout.ThumbnailCacheFolder, subfolder), + _ => FileSystemLayout.ImageCacheFolder + }; + string target = Path.Combine(baseFolder, hex); + _localFileSystem.CopyFile(path, target); + + return hex; + } } }