Browse Source

fix: randomize start points with classic playlists (#2948)

* fix: randomize start points with classic playlists

* update dependencies
pull/2949/head
Jason Dove 2 weeks ago committed by GitHub
parent
commit
e5a2a763d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 4
      ErsatzTV.Application/ErsatzTV.Application.csproj
  3. 18
      ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj
  4. 5
      ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs
  5. 18
      ErsatzTV.Core/ErsatzTV.Core.csproj
  6. 1
      ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutEnumerator.cs
  7. 2
      ErsatzTV.Core/Scheduling/Engine/MarathonHelper.cs
  8. 1
      ErsatzTV.Core/Scheduling/Engine/PlaylistHelper.cs
  9. 1
      ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs
  10. 20
      ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs
  11. 1
      ErsatzTV.Core/Scheduling/PlayoutBuilder.cs
  12. 1
      ErsatzTV.Core/Scheduling/YamlScheduling/EnumeratorCache.cs
  13. 8
      ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj
  14. 4
      ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj
  15. 2
      ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj
  16. 6
      ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj
  17. 6
      ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj
  18. 13
      ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs
  19. 64
      ErsatzTV.Scanner.Tests/Core/Metadata/MovieFolderScannerTests.cs
  20. 6
      ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj
  21. 10
      ErsatzTV.Scanner/ErsatzTV.Scanner.csproj
  22. 1
      ErsatzTV.Tests/ErsatzTV.Tests.csproj
  23. 21
      ErsatzTV/ErsatzTV.csproj

2
CHANGELOG.md

@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -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

4
ErsatzTV.Application/ErsatzTV.Application.csproj

@ -14,8 +14,8 @@ @@ -14,8 +14,8 @@
<PackageReference Include="CliWrap" Version="3.10.2" />
<PackageReference Include="Humanizer.Core" Version="3.0.10" />
<PackageReference Include="MediatR" Version="[12.5.0]" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.10" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Serilog.Formatting.Compact.Reader" Version="4.0.0" />
<PackageReference Include="WebMarkupMin.Core" Version="2.21.0" />

18
ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj

@ -9,20 +9,20 @@ @@ -9,20 +9,20 @@
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.10.2" />
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="NSubstitute" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="Serilog" Version="4.3.1" />
<PackageReference Include="Serilog" Version="4.4.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageReference Include="Shouldly" Version="4.3.0" />
<PackageReference Include="Testably.Abstractions.Testing" Version="6.6.0" />
<PackageReference Include="Testably.Abstractions.Testing" Version="7.0.1" />
</ItemGroup>
<ItemGroup>

5
ErsatzTV.Core.Tests/Scheduling/PlaylistEnumeratorTests.cs

@ -62,6 +62,7 @@ public class PlaylistEnumeratorTests @@ -62,6 +62,7 @@ public class PlaylistEnumeratorTests
new CollectionEnumeratorState(),
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: false,
CancellationToken.None);
var items = new List<int>();
@ -130,6 +131,7 @@ public class PlaylistEnumeratorTests @@ -130,6 +131,7 @@ public class PlaylistEnumeratorTests
new CollectionEnumeratorState(),
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: false,
CancellationToken.None);
var items = new List<int>();
@ -200,6 +202,7 @@ public class PlaylistEnumeratorTests @@ -200,6 +202,7 @@ public class PlaylistEnumeratorTests
state,
shufflePlaylistItems: true,
batchSize: Option<int>.None,
randomStartPoint: false,
CancellationToken.None);
var items = new List<int>();
@ -270,6 +273,7 @@ public class PlaylistEnumeratorTests @@ -270,6 +273,7 @@ public class PlaylistEnumeratorTests
state,
shufflePlaylistItems: true,
batchSize: Option<int>.None,
randomStartPoint: false,
CancellationToken.None);
var items = new List<int>();
@ -352,6 +356,7 @@ public class PlaylistEnumeratorTests @@ -352,6 +356,7 @@ public class PlaylistEnumeratorTests
state,
shufflePlaylistItems: true,
batchSize: Option<int>.None,
randomStartPoint: false,
CancellationToken.None);
enumerator.CountForFiller.ShouldBe(7);

18
ErsatzTV.Core/ErsatzTV.Core.csproj

@ -16,19 +16,19 @@ @@ -16,19 +16,19 @@
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="LanguageExt.Transformers" Version="4.4.8" />
<PackageReference Include="MediatR" Version="[12.5.0]" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="NCalcSync" Version="6.4.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="Serilog" Version="4.3.1" />
<PackageReference Include="Serilog" Version="4.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="SkiaSharp" Version="4.150.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="4.150.0" />
<PackageReference Include="System.CommandLine" Version="2.0.9" />
<PackageReference Include="Testably.Abstractions" Version="10.2.0" />
<PackageReference Include="SkiaSharp" Version="4.150.1" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="4.150.1" />
<PackageReference Include="System.CommandLine" Version="2.0.10" />
<PackageReference Include="Testably.Abstractions" Version="10.3.0" />
<PackageReference Include="TimeSpanParserUtil" Version="1.2.0" />
<PackageReference Include="YamlDotNet" Version="18.1.0" />
</ItemGroup>

1
ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutEnumerator.cs

@ -168,6 +168,7 @@ public static class BlockPlayoutEnumerator @@ -168,6 +168,7 @@ public static class BlockPlayoutEnumerator
state,
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: false,
cancellationToken);
DateTime historyTime = currentTime.UtcDateTime;

2
ErsatzTV.Core/Scheduling/Engine/MarathonHelper.cs

@ -66,6 +66,7 @@ public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository @@ -66,6 +66,7 @@ public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository
state,
marathonShuffleGroups,
marathonBatchSize,
randomStartPoint: false,
cancellationToken);
}
@ -136,6 +137,7 @@ public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository @@ -136,6 +137,7 @@ public class MarathonHelper(IMediaCollectionRepository mediaCollectionRepository
state,
shuffleGroups,
batchSize: Option<int>.None,
randomStartPoint: false,
cancellationToken);
return new PlaylistContentResult(

1
ErsatzTV.Core/Scheduling/Engine/PlaylistHelper.cs

@ -58,6 +58,7 @@ public class PlaylistHelper(IMediaCollectionRepository mediaCollectionRepository @@ -58,6 +58,7 @@ public class PlaylistHelper(IMediaCollectionRepository mediaCollectionRepository
state,
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: false,
cancellationToken);
return new PlaylistContentResult(

1
ErsatzTV.Core/Scheduling/Engine/SchedulingEngine.cs

@ -257,6 +257,7 @@ public class SchedulingEngine( @@ -257,6 +257,7 @@ public class SchedulingEngine(
state,
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: false,
CancellationToken.None);
string historyKey = HistoryDetails.KeyForSchedulingContent(key, PlaybackOrder.None);

20
ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs

@ -121,6 +121,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator @@ -121,6 +121,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
CollectionEnumeratorState state,
bool shufflePlaylistItems,
Option<int> batchSize,
bool randomStartPoint,
CancellationToken cancellationToken)
{
var result = new PlaylistEnumerator
@ -131,6 +132,10 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator @@ -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<CollectionKey, IMediaCollectionEnumerator>();
result._allMediaItemIds = [];
@ -166,6 +171,11 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator @@ -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 @@ -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);

1
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

@ -1325,6 +1325,7 @@ public class PlayoutBuilder : IPlayoutBuilder @@ -1325,6 +1325,7 @@ public class PlayoutBuilder : IPlayoutBuilder
state,
marathonShuffleGroups,
batchSize: Option<int>.None,
randomStartPoint,
cancellationToken);
}
}

1
ErsatzTV.Core/Scheduling/YamlScheduling/EnumeratorCache.cs

@ -175,6 +175,7 @@ public class EnumeratorCache(IMediaCollectionRepository mediaCollectionRepositor @@ -175,6 +175,7 @@ public class EnumeratorCache(IMediaCollectionRepository mediaCollectionRepositor
state,
shufflePlaylistItems: false,
batchSize: Option<int>.None,
randomStartPoint: false,
cancellationToken);
}

8
ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj

@ -8,10 +8,10 @@ @@ -8,10 +8,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="NSubstitute" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="Shouldly" Version="4.3.0" />

4
ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj

@ -15,8 +15,8 @@ @@ -15,8 +15,8 @@
<PackageReference Include="Hardware.Info" Version="110.0.0.1" />
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Lennox.NvEncSharp" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
</ItemGroup>

2
ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.15" />
<!-- Force fixed native SQLite to resolve NU1903 (GHSA-2m69-gcr7-jv3q) from the transitive 2.1.10 -->
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.3" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.4" />
</ItemGroup>

6
ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj

@ -9,8 +9,8 @@ @@ -9,8 +9,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="NSubstitute" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Shouldly" Version="4.3.0" />
<PackageReference Include="Testably.Abstractions.Testing" Version="6.6.0" />
<PackageReference Include="Testably.Abstractions.Testing" Version="7.0.1" />
</ItemGroup>
<ItemGroup>

6
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

@ -18,8 +18,8 @@ @@ -18,8 +18,8 @@
<PackageReference Include="EFCore.BulkExtensions" Version="[9.0.2,10)" />
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="9.4.2" />
<PackageReference Include="Humanizer.Core" Version="3.0.10" />
<PackageReference Include="Jint" Version="4.11.0" />
<PackageReference Include="JsonSchema.Net" Version="9.2.2" />
<PackageReference Include="Jint" Version="4.13.0" />
<PackageReference Include="JsonSchema.Net" Version="9.3.0" />
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00018" />
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00018" />
<PackageReference Include="Lucene.Net.QueryParser" Version="4.8.0-beta00018" />
@ -36,7 +36,7 @@ @@ -36,7 +36,7 @@
<PackageReference Include="RichTextKit.Stbear" Version="0.4.167.3" />
<PackageReference Include="Scriban.Signed" Version="7.2.5" />
<PackageReference Include="SixLabors.ImageSharp" Version="[3.1.12,4)" />
<PackageReference Include="SkiaSharp" Version="4.150.0" />
<PackageReference Include="SkiaSharp" Version="4.150.1" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="TimeZoneConverter" Version="7.2.0" />
</ItemGroup>

13
ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs

@ -348,13 +348,13 @@ public class TranscodingTests @@ -348,13 +348,13 @@ public class TranscodingTests
metadataRepository.When(x => x.UpdateStatistics(Arg.Any<MediaItem>(), Arg.Any<MediaVersion>(), Arg.Any<bool>()))
.Do(x =>
{
MediaVersion version = x.Arg<MediaVersion>();
if (version.Streams.Any(s => s.MediaStreamKind == MediaStreamKind.Video && !s.AttachedPic))
MediaVersion? version = x.Arg<MediaVersion>();
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 @@ -520,8 +520,11 @@ public class TranscodingTests
.Do(args =>
{
MediaVersion? version = args.Arg<MediaVersion>();
version.MediaFiles = v.MediaFiles;
v = version;
if (version != null)
{
version.MediaFiles = v.MediaFiles;
v = version;
}
});
var localStatisticsProvider = new LocalStatisticsProvider(

64
ErsatzTV.Scanner.Tests/Core/Metadata/MovieFolderScannerTests.cs

@ -59,7 +59,7 @@ public class MovieFolderScannerTests @@ -59,7 +59,7 @@ public class MovieFolderScannerTests
_movieRepository = Substitute.For<IMovieRepository>();
_movieRepository.GetOrAdd(Arg.Any<LibraryPath>(), Arg.Any<LibraryFolder>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns(args =>
Right<BaseError, MediaItemScanResult<Movie>>(new FakeMovieWithPath(args.Arg<string>()))
Right<BaseError, MediaItemScanResult<Movie>>(new FakeMovieWithPath(args.Arg<string>() ?? string.Empty))
.AsTask());
_movieRepository.FindMoviePaths(Arg.Any<LibraryPath>())
.Returns(new List<string>().AsEnumerable().AsTask());
@ -78,7 +78,7 @@ public class MovieFolderScannerTests @@ -78,7 +78,7 @@ public class MovieFolderScannerTests
_localMetadataProvider.RefreshFallbackMetadata(Arg.Any<Movie>())
.Returns(arg =>
{
((Movie)arg.Arg<MediaItem>()).MovieMetadata = [new MovieMetadata { Artwork = [] }];
(arg.Arg<MediaItem>() as Movie ?? new Movie()).MovieMetadata = [new MovieMetadata { Artwork = [] }];
return Task.FromResult(true);
});
@ -141,10 +141,10 @@ public class MovieFolderScannerTests @@ -141,10 +141,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
}
[Test]
@ -190,10 +190,10 @@ public class MovieFolderScannerTests @@ -190,10 +190,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
}
[Test]
@ -238,10 +238,10 @@ public class MovieFolderScannerTests @@ -238,10 +238,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshSidecarMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath),
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath),
metadataPath);
}
@ -287,10 +287,10 @@ public class MovieFolderScannerTests @@ -287,10 +287,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshSidecarMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath),
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath),
metadataPath);
}
@ -340,10 +340,10 @@ public class MovieFolderScannerTests @@ -340,10 +340,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(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 @@ -394,10 +394,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(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 @@ -448,10 +448,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(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 @@ -502,10 +502,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(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 @@ -556,10 +556,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(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 @@ -610,10 +610,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(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 @@ -664,10 +664,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(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 @@ -805,10 +805,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
}
[Test]
@ -854,10 +854,10 @@ public class MovieFolderScannerTests @@ -854,10 +854,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
}
[Test]
@ -905,10 +905,10 @@ public class MovieFolderScannerTests @@ -905,10 +905,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
}
[Test]
@ -950,10 +950,10 @@ public class MovieFolderScannerTests @@ -950,10 +950,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath,
FFprobePath,
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
await _localMetadataProvider.Received(1).RefreshFallbackMetadata(
Arg.Is<Movie>(i => i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
Arg.Is<Movie>(i => i != null && i.MediaVersions.Head().MediaFiles.Head().Path == moviePath));
}
[Test]

6
ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj

@ -10,8 +10,8 @@ @@ -10,8 +10,8 @@
<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="NSubstitute" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Shouldly" Version="4.3.0" />
<PackageReference Include="Testably.Abstractions.Testing" Version="6.6.0" />
<PackageReference Include="Testably.Abstractions.Testing" Version="7.0.1" />
</ItemGroup>
<ItemGroup>

10
ErsatzTV.Scanner/ErsatzTV.Scanner.csproj

@ -26,16 +26,16 @@ @@ -26,16 +26,16 @@
<PackageReference Include="Humanizer.Core" Version="3.0.10" />
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="MediatR" Version="[12.5.0]" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
<PackageReference Include="Serilog" Version="4.3.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.10" />
<PackageReference Include="Serilog" Version="4.4.0" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="10.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="System.CommandLine" Version="2.0.9" />
<PackageReference Include="System.CommandLine" Version="2.0.10" />
</ItemGroup>
<ItemGroup>

1
ErsatzTV.Tests/ErsatzTV.Tests.csproj

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NuGetAuditMode>direct</NuGetAuditMode>
</PropertyGroup>
<ItemGroup>

21
ErsatzTV/ErsatzTV.csproj

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
<UserSecretsId>bf31217d-f4ec-4520-8cc3-138059044ede</UserSecretsId>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NuGetAuditMode>direct</NuGetAuditMode>
<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
@ -36,30 +37,30 @@ @@ -36,30 +37,30 @@
<PackageReference Include="FluentValidation" Version="12.1.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.1" />
<PackageReference Include="Heron.MudCalendar" Version="4.0.0" />
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
<PackageReference Include="HtmlSanitizer" Version="9.0.967" />
<PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Markdig" Version="1.3.2" />
<PackageReference Include="MediatR.Courier.DependencyInjection" Version="5.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.9" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.10" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.10" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.10" />
<!-- Override transitive Microsoft.OpenApi 2.0.0 (GHSA-v5pm-xwqc-g5wc) from Microsoft.AspNetCore.OpenApi -->
<PackageReference Include="Microsoft.OpenApi" Version="2.7.5" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="10.0.9" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="10.0.10" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.9">
<PackageReference Include="Microsoft.Extensions.ApiDescription.Server" Version="10.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="MudBlazor" Version="9.6.0" />
<PackageReference Include="MudBlazor" Version="9.7.0" />
<PackageReference Include="NaturalSort.Extension" Version="4.4.1" />
<PackageReference Include="Refit.HttpClientFactory" Version="13.1.0" />
<PackageReference Include="Scalar.AspNetCore" Version="2.16.11" />
<PackageReference Include="Serilog" Version="4.3.1" />
<PackageReference Include="Scalar.AspNetCore" Version="2.16.16" />
<PackageReference Include="Serilog" Version="4.4.0" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="10.0.1" />
<PackageReference Include="VueCliMiddleware" Version="6.0.0" />

Loading…
Cancel
Save