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/).
- Update Plex movie and other video titles when changed from Plex - 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 - 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) - 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 ## [26.6.0] - 2026-07-09
### Added ### Added

4
ErsatzTV.Application/ErsatzTV.Application.csproj

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

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

@ -9,20 +9,20 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.10.2" /> <PackageReference Include="CliWrap" Version="3.10.2" />
<PackageReference Include="LanguageExt.Core" Version="4.4.9" /> <PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.9" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="NSubstitute" Version="5.3.0" /> <PackageReference Include="NSubstitute" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.6.1" /> <PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" /> <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.Extensions.Logging" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" /> <PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageReference Include="Shouldly" Version="4.3.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>
<ItemGroup> <ItemGroup>

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

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

18
ErsatzTV.Core/ErsatzTV.Core.csproj

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

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

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

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

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

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

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

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

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

20
ErsatzTV.Core/Scheduling/PlaylistEnumerator.cs

@ -121,6 +121,7 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
CollectionEnumeratorState state, CollectionEnumeratorState state,
bool shufflePlaylistItems, bool shufflePlaylistItems,
Option<int> batchSize, Option<int> batchSize,
bool randomStartPoint,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var result = new PlaylistEnumerator var result = new PlaylistEnumerator
@ -131,6 +132,10 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
_batchSize = batchSize _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 // collections should share enumerators
var enumeratorMap = new Dictionary<CollectionKey, IMediaCollectionEnumerator>(); var enumeratorMap = new Dictionary<CollectionKey, IMediaCollectionEnumerator>();
result._allMediaItemIds = []; result._allMediaItemIds = [];
@ -166,6 +171,11 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
switch (playlistItem.PlaybackOrder) switch (playlistItem.PlaybackOrder)
{ {
case PlaybackOrder.Chronological: case PlaybackOrder.Chronological:
if (randomStartPoint)
{
initState.Index = random.Next(0, items.Count - 1);
}
enumerator = new ChronologicalMediaCollectionEnumerator(items, initState); enumerator = new ChronologicalMediaCollectionEnumerator(items, initState);
break; break;
// TODO: fix multi episode shuffle? // TODO: fix multi episode shuffle?
@ -187,18 +197,22 @@ public class PlaylistEnumerator : IMediaCollectionEnumerator
CollectionKey.ForPlaylistItem(playlistItem), CollectionKey.ForPlaylistItem(playlistItem),
cancellationToken), cancellationToken),
initState, initState,
// TODO: fix this randomStartPoint,
false,
cancellationToken); cancellationToken);
break; break;
case PlaybackOrder.SeasonEpisode: case PlaybackOrder.SeasonEpisode:
// TODO: check random start point? if (randomStartPoint)
{
initState.Index = random.Next(0, items.Count - 1);
}
enumerator = new SeasonEpisodeMediaCollectionEnumerator(items, initState); enumerator = new SeasonEpisodeMediaCollectionEnumerator(items, initState);
// season, episode will filter out season 0, so we may get an empty enumerator back // season, episode will filter out season 0, so we may get an empty enumerator back
if (enumerator.Count == 0) if (enumerator.Count == 0)
{ {
enumerator = null; enumerator = null;
} }
break; break;
case PlaybackOrder.Random: case PlaybackOrder.Random:
enumerator = new RandomizedMediaCollectionEnumerator(items, initState); enumerator = new RandomizedMediaCollectionEnumerator(items, initState);

1
ErsatzTV.Core/Scheduling/PlayoutBuilder.cs

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

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

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

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

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

4
ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj

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

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

@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.15" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" 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 --> <!-- 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> </ItemGroup>

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

@ -9,8 +9,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="NSubstitute" Version="5.3.0" /> <PackageReference Include="NSubstitute" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.6.1" /> <PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" /> <PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.14.0"> <PackageReference Include="NUnit.Analyzers" Version="4.14.0">
@ -22,7 +22,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Shouldly" Version="4.3.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>
<ItemGroup> <ItemGroup>

6
ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj

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

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

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

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

@ -59,7 +59,7 @@ public class MovieFolderScannerTests
_movieRepository = Substitute.For<IMovieRepository>(); _movieRepository = Substitute.For<IMovieRepository>();
_movieRepository.GetOrAdd(Arg.Any<LibraryPath>(), Arg.Any<LibraryFolder>(), Arg.Any<string>(), Arg.Any<CancellationToken>()) _movieRepository.GetOrAdd(Arg.Any<LibraryPath>(), Arg.Any<LibraryFolder>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns(args => .Returns(args =>
Right<BaseError, MediaItemScanResult<Movie>>(new FakeMovieWithPath(args.Arg<string>())) Right<BaseError, MediaItemScanResult<Movie>>(new FakeMovieWithPath(args.Arg<string>() ?? string.Empty))
.AsTask()); .AsTask());
_movieRepository.FindMoviePaths(Arg.Any<LibraryPath>()) _movieRepository.FindMoviePaths(Arg.Any<LibraryPath>())
.Returns(new List<string>().AsEnumerable().AsTask()); .Returns(new List<string>().AsEnumerable().AsTask());
@ -78,7 +78,7 @@ public class MovieFolderScannerTests
_localMetadataProvider.RefreshFallbackMetadata(Arg.Any<Movie>()) _localMetadataProvider.RefreshFallbackMetadata(Arg.Any<Movie>())
.Returns(arg => .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); return Task.FromResult(true);
}); });
@ -141,10 +141,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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] [Test]
@ -190,10 +190,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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] [Test]
@ -238,10 +238,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); metadataPath);
} }
@ -287,10 +287,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); metadataPath);
} }
@ -340,10 +340,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); await _imageCache.Received(1).CopyArtworkToCache(posterPath, ArtworkKind.Poster);
} }
@ -394,10 +394,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); await _imageCache.Received(1).CopyArtworkToCache(posterPath, ArtworkKind.Poster);
} }
@ -448,10 +448,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); await _imageCache.Received(1).CopyArtworkToCache(posterPath, ArtworkKind.Poster);
} }
@ -502,10 +502,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); await _imageCache.Received(1).CopyArtworkToCache(fanArtPath, ArtworkKind.FanArt);
} }
@ -556,10 +556,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); await _imageCache.Received(1).CopyArtworkToCache(fanArtPath, ArtworkKind.FanArt);
} }
@ -610,10 +610,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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); await _imageCache.Received(1).CopyArtworkToCache(fanArtPath, ArtworkKind.FanArt);
} }
@ -664,10 +664,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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) // Should receive Poster call (as folder.ext is valid for poster)
await _imageCache.Received(1).CopyArtworkToCache(folderImagePath, ArtworkKind.Poster); await _imageCache.Received(1).CopyArtworkToCache(folderImagePath, ArtworkKind.Poster);
@ -805,10 +805,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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] [Test]
@ -854,10 +854,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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] [Test]
@ -905,10 +905,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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] [Test]
@ -950,10 +950,10 @@ public class MovieFolderScannerTests
await _localStatisticsProvider.Received(1).RefreshStatistics( await _localStatisticsProvider.Received(1).RefreshStatistics(
FFmpegPath, FFmpegPath,
FFprobePath, 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( 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] [Test]

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

@ -10,8 +10,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.9" /> <PackageReference Include="LanguageExt.Core" Version="4.4.9" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="NSubstitute" Version="5.3.0" /> <PackageReference Include="NSubstitute" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.6.1" /> <PackageReference Include="NUnit" Version="4.6.1" />
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" /> <PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageReference Include="NUnit.Analyzers" Version="4.14.0"> <PackageReference Include="NUnit.Analyzers" Version="4.14.0">
@ -23,7 +23,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Shouldly" Version="4.3.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>
<ItemGroup> <ItemGroup>

10
ErsatzTV.Scanner/ErsatzTV.Scanner.csproj

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

1
ErsatzTV.Tests/ErsatzTV.Tests.csproj

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

21
ErsatzTV/ErsatzTV.csproj

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

Loading…
Cancel
Save