diff --git a/CHANGELOG.md b/CHANGELOG.md index b27c598f5..46e9383f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Update Plex movie and other video titles when changed from Plex - The first library scan after updating will act like a deep scan due to adding title to the Plex etag calculation - Future periodic scans will update titles in ETV automatically (deep scans will not be required to update titles) +- Randomize start points on playlist items in classic schedules when setting is enabled + - Playlists ignored this setting in earlier builds ## [26.6.0] - 2026-07-09 ### Added diff --git a/ErsatzTV.Application/ErsatzTV.Application.csproj b/ErsatzTV.Application/ErsatzTV.Application.csproj index 0ac00ea47..133ef6984 100644 --- a/ErsatzTV.Application/ErsatzTV.Application.csproj +++ b/ErsatzTV.Application/ErsatzTV.Application.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj index 9a882a6a1..c40941431 100644 --- a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj +++ b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj @@ -9,20 +9,20 @@ - - - - - - - + + + + + + + - + - + diff --git a/ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs b/ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs index 1d174815b..90f3e3a20 100644 --- a/ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs @@ -62,6 +62,7 @@ public class PlaylistEnumeratorTests new CollectionEnumeratorState(), shufflePlaylistItems: false, batchSize: Option.None, + randomStartPoint: false, CancellationToken.None); var items = new List(); @@ -130,6 +131,7 @@ public class PlaylistEnumeratorTests new CollectionEnumeratorState(), shufflePlaylistItems: false, batchSize: Option.None, + randomStartPoint: false, CancellationToken.None); var items = new List(); @@ -200,6 +202,7 @@ public class PlaylistEnumeratorTests state, shufflePlaylistItems: true, batchSize: Option.None, + randomStartPoint: false, CancellationToken.None); var items = new List(); @@ -270,6 +273,7 @@ public class PlaylistEnumeratorTests state, shufflePlaylistItems: true, batchSize: Option.None, + randomStartPoint: false, CancellationToken.None); var items = new List(); @@ -352,6 +356,7 @@ public class PlaylistEnumeratorTests state, shufflePlaylistItems: true, batchSize: Option.None, + randomStartPoint: false, CancellationToken.None); enumerator.CountForFiller.ShouldBe(7); diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj b/ErsatzTV.Core/ErsatzTV.Core.csproj index 02be50aed..d753c77f6 100644 --- a/ErsatzTV.Core/ErsatzTV.Core.csproj +++ b/ErsatzTV.Core/ErsatzTV.Core.csproj @@ -16,19 +16,19 @@ - - - - + + + + - + - - - - + + + + diff --git a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutEnumerator.cs b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutEnumerator.cs index 2cb096bad..4c6586f42 100644 --- a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutEnumerator.cs @@ -168,6 +168,7 @@ public static class BlockPlayoutEnumerator state, shufflePlaylistItems: false, batchSize: Option.None, + randomStartPoint: false, cancellationToken); DateTime historyTime = currentTime.UtcDateTime; diff --git a/ErsatzTV.Core/Scheduling/Engine/MarathonHelper.cs b/ErsatzTV.Core/Scheduling/Engine/MarathonHelper.cs index e7975327c..f67c5b04b 100644 --- a/ErsatzTV.Core/Scheduling/Engine/MarathonHelper.cs +++ b/ErsatzTV.Core/Scheduling/Engine/MarathonHelper.cs @@ -66,6 +66,7 @@ public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository state, marathonShuffleGroups, marathonBatchSize, + randomStartPoint: false, cancellationToken); } @@ -136,6 +137,7 @@ public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository state, shuffleGroups, batchSize: Option.None, + randomStartPoint: false, cancellationToken); return new PlaylistContentResult( diff --git a/ErsatzTV.Core/Scheduling/Engine/PlaylistHelper.cs b/ErsatzTV.Core/Scheduling/Engine/PlaylistHelper.cs index cb1bfa06a..d7ff4363d 100644 --- a/ErsatzTV.Core/Scheduling/Engine/PlaylistHelper.cs +++ b/ErsatzTV.Core/Scheduling/Engine/PlaylistHelper.cs @@ -58,6 +58,7 @@ public class PlaylistHelper(IMediaCollectionRepository mediaCollectionRepository state, shufflePlaylistItems: false, batchSize: Option.None, + randomStartPoint: false, cancellationToken); return new PlaylistContentResult( diff --git a/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs b/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs index 53837822e..2139f65c9 100644 --- a/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs +++ b/ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs @@ -257,6 +257,7 @@ public class SchedulingEngine( state, shufflePlaylistItems: false, batchSize: Option.None, + randomStartPoint: false, CancellationToken.None); string historyKey = HistoryDetails.KeyForSchedulingContent(key, PlaybackOrder.None); diff --git a/ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs b/ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs index 44706de67..194a99eb3 100644 --- a/ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs @@ -121,6 +121,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator CollectionEnumeratorState state, bool shufflePlaylistItems, Option batchSize, + bool randomStartPoint, CancellationToken cancellationToken) { var result = new PlaylistEnumerator @@ -131,6 +132,10 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator _batchSize = batchSize }; + // random start points only apply to a fresh build with no saved state + var random = new Random(state.Seed); + randomStartPoint = randomStartPoint && state.Index == 0; + // collections should share enumerators var enumeratorMap = new Dictionary(); result._allMediaItemIds = []; @@ -166,6 +171,11 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator switch (playlistItem.PlaybackOrder) { case PlaybackOrder.Chronological: + if (randomStartPoint) + { + initState.Index = random.Next(0, items.Count - 1); + } + enumerator = new ChronologicalMediaCollectionEnumerator(items, initState); break; // TODO: fix multi episode shuffle? @@ -187,18 +197,22 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator CollectionKey.ForPlaylistItem(playlistItem), cancellationToken), initState, - // TODO: fix this - false, + randomStartPoint, cancellationToken); break; case PlaybackOrder.SeasonEpisode: - // TODO: check random start point? + if (randomStartPoint) + { + initState.Index = random.Next(0, items.Count - 1); + } + enumerator = new SeasonEpisodeMediaCollectionEnumerator(items, initState); // season, episode will filter out season 0, so we may get an empty enumerator back if (enumerator.Count == 0) { enumerator = null; } + break; case PlaybackOrder.Random: enumerator = new RandomizedMediaCollectionEnumerator(items, initState); diff --git a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs index 582eb65d1..bfdb642dc 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs @@ -1325,6 +1325,7 @@ public class PlayoutBuilder : IPlayoutBuilder state, marathonShuffleGroups, batchSize: Option.None, + randomStartPoint, cancellationToken); } } diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/EnumeratorCache.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/EnumeratorCache.cs index 9c394253c..af3d9c09e 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/EnumeratorCache.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/EnumeratorCache.cs @@ -175,6 +175,7 @@ public class EnumeratorCache(IMediaCollectionRepository mediaCollectionRepositor state, shufflePlaylistItems: false, batchSize: Option.None, + randomStartPoint: false, cancellationToken); } diff --git a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj index c81e4c005..22d393f1f 100644 --- a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj +++ b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj index e08b73939..8b7a66f11 100644 --- a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj +++ b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj b/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj index 352bd81f8..23fe09aea 100644 --- a/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj +++ b/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj @@ -17,7 +17,7 @@ - + diff --git a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj index ed169e16e..6dd693849 100644 --- a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj +++ b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj @@ -9,8 +9,8 @@ - - + + @@ -22,7 +22,7 @@ all - + diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj index f4cf012a5..3fc8fb95a 100644 --- a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj +++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj @@ -18,8 +18,8 @@ - - + + @@ -36,7 +36,7 @@ - + diff --git a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs index 179a37dfd..082bf04a3 100644 --- a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs @@ -348,13 +348,13 @@ public class TranscodingTests metadataRepository.When(x => x.UpdateStatistics(Arg.Any(), Arg.Any(), Arg.Any())) .Do(x => { - MediaVersion version = x.Arg(); - if (version.Streams.Any(s => s.MediaStreamKind == MediaStreamKind.Video && !s.AttachedPic)) + MediaVersion? version = x.Arg(); + if (version != null && version.Streams.Any(s => s.MediaStreamKind == MediaStreamKind.Video && !s.AttachedPic)) { version.MediaFiles = videoVersion.MediaFiles; videoVersion = version; } - else + else if (version != null) { version.MediaFiles = songVersion.MediaFiles; songVersion = version; @@ -520,8 +520,11 @@ public class TranscodingTests .Do(args => { MediaVersion? version = args.Arg(); - version.MediaFiles = v.MediaFiles; - v = version; + if (version != null) + { + version.MediaFiles = v.MediaFiles; + v = version; + } }); var localStatisticsProvider = new LocalStatisticsProvider( diff --git a/ErsatzTV.Scanner.Tests/Core/Metadata/MovieFolderScannerTests.cs b/ErsatzTV.Scanner.Tests/Core/Metadata/MovieFolderScannerTests.cs index 4a2e37132..610bff242 100644 --- a/ErsatzTV.Scanner.Tests/Core/Metadata/MovieFolderScannerTests.cs +++ b/ErsatzTV.Scanner.Tests/Core/Metadata/MovieFolderScannerTests.cs @@ -59,7 +59,7 @@ public class MovieFolderScannerTests _movieRepository = Substitute.For(); _movieRepository.GetOrAdd(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any()) .Returns(args => - Right>(new FakeMovieWithPath(args.Arg())) + Right>(new FakeMovieWithPath(args.Arg() ?? string.Empty)) .AsTask()); _movieRepository.FindMoviePaths(Arg.Any()) .Returns(new List().AsEnumerable().AsTask()); @@ -78,7 +78,7 @@ public class MovieFolderScannerTests _localMetadataProvider.RefreshFallbackMetadata(Arg.Any()) .Returns(arg => { - ((Movie)arg.Arg()).MovieMetadata = [new MovieMetadata { Artwork = [] }]; + (arg.Arg() as Movie ?? new Movie()).MovieMetadata = [new MovieMetadata { Artwork = [] }]; return Task.FromResult(true); }); @@ -141,10 +141,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); } [Test] @@ -190,10 +190,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); } [Test] @@ -238,10 +238,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshSidecarMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath), + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath), metadataPath); } @@ -287,10 +287,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshSidecarMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath), + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath), metadataPath); } @@ -340,10 +340,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _imageCache.Received(1).CopyArtworkToCache(posterPath, ArtworkKind.Poster); } @@ -394,10 +394,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _imageCache.Received(1).CopyArtworkToCache(posterPath, ArtworkKind.Poster); } @@ -448,10 +448,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _imageCache.Received(1).CopyArtworkToCache(posterPath, ArtworkKind.Poster); } @@ -502,10 +502,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _imageCache.Received(1).CopyArtworkToCache(fanArtPath, ArtworkKind.FanArt); } @@ -556,10 +556,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _imageCache.Received(1).CopyArtworkToCache(fanArtPath, ArtworkKind.FanArt); } @@ -610,10 +610,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _imageCache.Received(1).CopyArtworkToCache(fanArtPath, ArtworkKind.FanArt); } @@ -664,10 +664,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); // Should receive Poster call (as folder.ext is valid for poster) await _imageCache.Received(1).CopyArtworkToCache(folderImagePath, ArtworkKind.Poster); @@ -805,10 +805,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); } [Test] @@ -854,10 +854,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); } [Test] @@ -905,10 +905,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); } [Test] @@ -950,10 +950,10 @@ public class MovieFolderScannerTests await _localStatisticsProvider.Received(1).RefreshStatistics( FFmpegPath, FFprobePath, - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); await _localMetadataProvider.Received(1).RefreshFallbackMetadata( - Arg.Is(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); + Arg.Is(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath)); } [Test] diff --git a/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj b/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj index 073a3f933..0df5a6caa 100644 --- a/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj +++ b/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj @@ -10,8 +10,8 @@ - - + + @@ -23,7 +23,7 @@ all - + diff --git a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj index 8963ce279..128416a88 100644 --- a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj +++ b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj @@ -26,16 +26,16 @@ - - - - + + + + - + diff --git a/ErsatzTV.Tests/ErsatzTV.Tests.csproj b/ErsatzTV.Tests/ErsatzTV.Tests.csproj index 5f8a6b2d2..27e94d915 100644 --- a/ErsatzTV.Tests/ErsatzTV.Tests.csproj +++ b/ErsatzTV.Tests/ErsatzTV.Tests.csproj @@ -4,6 +4,7 @@ net10.0 enable enable + direct diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 0387fd85d..036c721cf 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -13,6 +13,7 @@ bf31217d-f4ec-4520-8cc3-138059044ede latest-Recommended true + direct true en-US @@ -36,30 +37,30 @@ - + - - - - + + + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + +