diff --git a/CHANGELOG.md b/CHANGELOG.md index e32b6882..70a76580 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix MySQL queries that could fail during media server library scans - Fix scanning Jellyfin libraries when library options and/or path infos are not returned from Jellyfin API - Fix error indexing music videos in `File Not Found` state +- Fix bug scheduling duration filler when filler collection contains item with zero duration ### Changed - Upgrade ffmpeg to 6.1, which is now *required* for all installs diff --git a/ErsatzTV.Core/Extensions/MediaItemExtensions.cs b/ErsatzTV.Core/Extensions/MediaItemExtensions.cs index 47ea573b..5f02c792 100644 --- a/ErsatzTV.Core/Extensions/MediaItemExtensions.cs +++ b/ErsatzTV.Core/Extensions/MediaItemExtensions.cs @@ -7,8 +7,9 @@ namespace ErsatzTV.Core.Extensions; public static class MediaItemExtensions { - public static Option GetDuration(this MediaItem mediaItem) => - mediaItem switch + public static Option GetNonZeroDuration(this MediaItem mediaItem) + { + Option maybeDuration = mediaItem switch { Movie m => m.MediaVersions.HeadOrNone().Map(v => v.Duration), Episode e => e.MediaVersions.HeadOrNone().Map(v => v.Duration), @@ -18,6 +19,10 @@ public static class MediaItemExtensions _ => None }; + // zero duration is invalid, so return none in that case + return maybeDuration.Any(duration => duration == TimeSpan.Zero) ? Option.None : maybeDuration; + } + public static MediaVersion GetHeadVersion(this MediaItem mediaItem) => mediaItem switch { diff --git a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs index 366469bb..10595dcb 100644 --- a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs @@ -15,7 +15,7 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu { _sortedMediaItems = mediaItems.OrderBy(identity, new ChronologicalMediaComparer()).ToList(); _lazyMinimumDuration = new Lazy>( - () => _sortedMediaItems.Bind(i => i.GetDuration()).OrderBy(identity).HeadOrNone()); + () => _sortedMediaItems.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); State = new CollectionEnumeratorState { Seed = state.Seed }; diff --git a/ErsatzTV.Core/Scheduling/CustomOrderCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/CustomOrderCollectionEnumerator.cs index ddaef5ba..9b589603 100644 --- a/ErsatzTV.Core/Scheduling/CustomOrderCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/CustomOrderCollectionEnumerator.cs @@ -26,7 +26,7 @@ public class CustomOrderCollectionEnumerator : IMediaCollectionEnumerator .Map(ci => mediaItems.First(mi => mi.Id == ci.MediaItemId)) .ToList(); _lazyMinimumDuration = new Lazy>( - () => _sortedMediaItems.Bind(i => i.GetDuration()).OrderBy(identity).HeadOrNone()); + () => _sortedMediaItems.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); State = new CollectionEnumeratorState { Seed = state.Seed }; while (State.Index < state.Index) diff --git a/ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs index af101446..6cbcd492 100644 --- a/ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/RandomizedMediaCollectionEnumerator.cs @@ -15,7 +15,7 @@ public class RandomizedMediaCollectionEnumerator : IMediaCollectionEnumerator { _mediaItems = mediaItems; _lazyMinimumDuration = - new Lazy>(() => _mediaItems.Bind(i => i.GetDuration()).OrderBy(identity).HeadOrNone()); + new Lazy>(() => _mediaItems.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); _random = new Random(state.Seed); State = new CollectionEnumeratorState { Seed = state.Seed }; diff --git a/ErsatzTV.Core/Scheduling/SeasonEpisodeMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/SeasonEpisodeMediaCollectionEnumerator.cs index 235f4c87..04818c88 100644 --- a/ErsatzTV.Core/Scheduling/SeasonEpisodeMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/SeasonEpisodeMediaCollectionEnumerator.cs @@ -15,7 +15,7 @@ public sealed class SeasonEpisodeMediaCollectionEnumerator : IMediaCollectionEnu { _sortedMediaItems = mediaItems.OrderBy(identity, new SeasonEpisodeMediaComparer()).ToList(); _lazyMinimumDuration = new Lazy>( - () => _sortedMediaItems.Bind(i => i.GetDuration()).OrderBy(identity).HeadOrNone()); + () => _sortedMediaItems.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); State = new CollectionEnumeratorState { Seed = state.Seed }; diff --git a/ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs index 294a2d5b..46081475 100644 --- a/ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs @@ -34,7 +34,7 @@ public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator _random = new Random(state.Seed); _shuffled = Shuffle(_collections, _random); _lazyMinimumDuration = - new Lazy>(() => _shuffled.Bind(i => i.GetDuration()).OrderBy(identity).HeadOrNone()); + new Lazy>(() => _shuffled.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); State = new CollectionEnumeratorState { Seed = state.Seed }; while (State.Index < state.Index) diff --git a/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs index 5113b6ed..48a9a945 100644 --- a/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs @@ -31,7 +31,7 @@ public class ShuffledMediaCollectionEnumerator : IMediaCollectionEnumerator _random = new CloneableRandom(state.Seed); _shuffled = Shuffle(_mediaItems, _random); _lazyMinimumDuration = - new Lazy>(() => _shuffled.Bind(i => i.GetDuration()).OrderBy(identity).HeadOrNone()); + new Lazy>(() => _shuffled.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); State = new CollectionEnumeratorState { Seed = state.Seed }; while (State.Index < state.Index) diff --git a/ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs b/ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs index 09405504..07722bb2 100644 --- a/ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs +++ b/ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs @@ -78,7 +78,7 @@ public class MultiEpisodeShuffleCollectionEnumerator : IMediaCollectionEnumerato _random = new CloneableRandom(state.Seed); _shuffled = Shuffle(_random); _lazyMinimumDuration = - new Lazy>(() => _shuffled.Bind(i => i.GetDuration()).OrderBy(identity).HeadOrNone()); + new Lazy>(() => _shuffled.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); State = new CollectionEnumeratorState { Seed = state.Seed }; while (State.Index < state.Index)