Browse Source

scheduling bug fixes (#1238)

pull/1239/head
Jason Dove 3 years ago committed by GitHub
parent
commit
a684dcced9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      CHANGELOG.md
  2. 34
      ErsatzTV.Core/Scheduling/GroupedMediaItem.cs
  3. 40
      ErsatzTV.Core/Scheduling/MediaItemsForCollection.cs
  4. 6
      ErsatzTV.Core/Scheduling/MultiCollectionGrouper.cs
  5. 2
      ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs
  6. 2
      ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs
  7. 10
      ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs
  8. 2
      ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs
  9. 3
      ErsatzTV/Pages/Playouts.razor

7
CHANGELOG.md

@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -6,6 +6,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Fixed
- Limit `HLS Direct` streams to realtime speed
- Fix `Reset Playout` button to use worker thread instead of UI thread
- This fixes potential UI hangs and database concurrency bugs
### Changed
- Remove duplicate items from smart collections before scheduling
- i.e. shows no longer need to be filtered out if search results also include episodes
- Certain multi-collection scenarios may still include duplicates across multiple collections
## [0.7.7-beta] - 2023-04-07
### Added

34
ErsatzTV.Core/Scheduling/GroupedMediaItem.cs

@ -4,7 +4,7 @@ using ErsatzTV.Core.Domain; @@ -4,7 +4,7 @@ using ErsatzTV.Core.Domain;
namespace ErsatzTV.Core.Scheduling;
[DebuggerDisplay("{" + nameof(First) + "}")]
public class GroupedMediaItem
public class GroupedMediaItem : IEquatable<GroupedMediaItem>
{
public GroupedMediaItem()
{
@ -16,10 +16,36 @@ public class GroupedMediaItem @@ -16,10 +16,36 @@ public class GroupedMediaItem
Additional = additional ?? new List<MediaItem>();
}
public MediaItem First { get; set; }
public List<MediaItem> Additional { get; set; }
public MediaItem First { get; init; }
public List<MediaItem> Additional { get; protected init; }
public static IList<MediaItem> FlattenGroups(GroupedMediaItem[] copy, int mediaItemCount)
public bool Equals(GroupedMediaItem other) =>
Equals(First.Id, other?.First.Id) && Equals(Additional?.Count, other?.Additional?.Count) &&
Equals(Additional?.Map(x => x.Id), other?.Additional?.Map(x => x.Id));
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((GroupedMediaItem)obj);
}
public override int GetHashCode() => HashCode.Combine(First.Id, Additional?.Map(x => x.Id));
public static IList<MediaItem> FlattenGroups(IEnumerable<GroupedMediaItem> copy, int mediaItemCount)
{
var result = new MediaItem[mediaItemCount];
var i = 0;

40
ErsatzTV.Core/Scheduling/MediaItemsForCollection.cs

@ -11,36 +11,34 @@ public static class MediaItemsForCollection @@ -11,36 +11,34 @@ public static class MediaItemsForCollection
IArtistRepository artistRepository,
CollectionKey collectionKey)
{
var result = new List<MediaItem>();
switch (collectionKey.CollectionType)
{
case ProgramScheduleItemCollectionType.Collection:
List<MediaItem> collectionItems =
await mediaCollectionRepository.GetItems(collectionKey.CollectionId ?? 0);
return collectionItems;
result.AddRange(await mediaCollectionRepository.GetItems(collectionKey.CollectionId ?? 0));
break;
case ProgramScheduleItemCollectionType.TelevisionShow:
List<Episode> showItems =
await televisionRepository.GetShowItems(collectionKey.MediaItemId ?? 0);
return showItems.Cast<MediaItem>().ToList();
result.AddRange(await televisionRepository.GetShowItems(collectionKey.MediaItemId ?? 0));
break;
case ProgramScheduleItemCollectionType.TelevisionSeason:
List<Episode> seasonItems =
await televisionRepository.GetSeasonItems(collectionKey.MediaItemId ?? 0);
return seasonItems.Cast<MediaItem>().ToList();
result.AddRange(await televisionRepository.GetSeasonItems(collectionKey.MediaItemId ?? 0));
break;
case ProgramScheduleItemCollectionType.Artist:
List<MusicVideo> artistItems =
await artistRepository.GetArtistItems(collectionKey.MediaItemId ?? 0);
return artistItems.Cast<MediaItem>().ToList();
result.AddRange(await artistRepository.GetArtistItems(collectionKey.MediaItemId ?? 0));
break;
case ProgramScheduleItemCollectionType.MultiCollection:
List<MediaItem> multiCollectionItems =
await mediaCollectionRepository.GetMultiCollectionItems(
collectionKey.MultiCollectionId ?? 0);
return multiCollectionItems;
result.AddRange(
await mediaCollectionRepository.GetMultiCollectionItems(collectionKey.MultiCollectionId ?? 0));
break;
case ProgramScheduleItemCollectionType.SmartCollection:
List<MediaItem> smartCollectionItems =
await mediaCollectionRepository.GetSmartCollectionItems(
collectionKey.SmartCollectionId ?? 0);
return smartCollectionItems;
result.AddRange(
await mediaCollectionRepository.GetSmartCollectionItems(collectionKey.SmartCollectionId ?? 0));
break;
default:
return new List<MediaItem>();
throw new ArgumentOutOfRangeException();
}
return result.DistinctBy(x => x.Id).ToList();
}
}

6
ErsatzTV.Core/Scheduling/MultiCollectionGrouper.cs

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
namespace ErsatzTV.Core.Scheduling;
public class MultiCollectionGrouper
public static class MultiCollectionGrouper
{
public static List<GroupedMediaItem> GroupMediaItems(IList<CollectionWithItems> collections)
public static List<GroupedMediaItem> GroupMediaItems(IEnumerable<CollectionWithItems> collections)
{
var result = new List<GroupedMediaItem>();
@ -18,6 +18,6 @@ public class MultiCollectionGrouper @@ -18,6 +18,6 @@ public class MultiCollectionGrouper
}
}
return result;
return result.Distinct().ToList();
}
}

2
ErsatzTV.Core/Scheduling/ShuffleInOrderCollectionEnumerator.cs

@ -52,7 +52,7 @@ public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator @@ -52,7 +52,7 @@ public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator
State.Seed = _random.Next();
_random = new Random(State.Seed);
_shuffled = Shuffle(_collections, _random);
} while (_collections.Count > 1 && Current == tail);
} while (_collections.Count > 1 && Current.Map(x => x.Id) == tail.Map(x => x.Id));
}
else
{

2
ErsatzTV.Core/Scheduling/ShuffledMediaCollectionEnumerator.cs

@ -49,7 +49,7 @@ public class ShuffledMediaCollectionEnumerator : IMediaCollectionEnumerator @@ -49,7 +49,7 @@ public class ShuffledMediaCollectionEnumerator : IMediaCollectionEnumerator
State.Seed = _random.Next();
_random = new CloneableRandom(State.Seed);
_shuffled = Shuffle(_mediaItems, _random);
} while (_mediaItems.Count > 1 && Current == tail);
} while (_mediaItems.Count > 1 && Current.Map(x => x.Id) == tail.Map(x => x.Id));
}
else
{

10
ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs

@ -86,7 +86,7 @@ public class MediaCollectionRepository : IMediaCollectionRepository @@ -86,7 +86,7 @@ public class MediaCollectionRepository : IMediaCollectionRepository
}
}
return result.Distinct().ToList();
return result.DistinctBy(x => x.Id).ToList();
}
public async Task<List<MediaItem>> GetSmartCollectionItems(int id)
@ -150,7 +150,7 @@ public class MediaCollectionRepository : IMediaCollectionRepository @@ -150,7 +150,7 @@ public class MediaCollectionRepository : IMediaCollectionRepository
result.AddRange(await GetSongItems(dbContext, songIds));
}
return result;
return result.DistinctBy(x => x.Id).ToList();
}
public async Task<List<CollectionWithItems>> GetMultiCollectionCollections(int id)
@ -221,14 +221,14 @@ public class MediaCollectionRepository : IMediaCollectionRepository @@ -221,14 +221,14 @@ public class MediaCollectionRepository : IMediaCollectionRepository
// remove duplicate items from ungrouped collections
var toRemoveFrom = result.Filter(c => !c.ScheduleAsGroup).ToList();
var toRemove = result.Filter(c => c.ScheduleAsGroup)
var scheduleAsGroupItemIds = result.Filter(c => c.ScheduleAsGroup)
.SelectMany(c => c.MediaItems.Map(i => i.Id))
.Distinct()
.ToList();
.ToHashSet();
foreach (CollectionWithItems collection in toRemoveFrom)
{
collection.MediaItems.RemoveAll(mi => toRemove.Contains(mi.Id));
collection.MediaItems.RemoveAll(mi => scheduleAsGroupItemIds.Contains(mi.Id));
}
return result;

2
ErsatzTV.Infrastructure/Scheduling/MultiEpisodeShuffleCollectionEnumerator.cs

@ -93,7 +93,7 @@ public class MultiEpisodeShuffleCollectionEnumerator : IMediaCollectionEnumerato @@ -93,7 +93,7 @@ public class MultiEpisodeShuffleCollectionEnumerator : IMediaCollectionEnumerato
State.Seed = _random.Next();
_random = new CloneableRandom(State.Seed);
_shuffled = Shuffle(_random);
} while (_mediaItemCount > 1 && Current == tail);
} while (_mediaItemCount > 1 && Current.Map(x => x.Id) == tail.Map(x => x.Id));
}
else
{

3
ErsatzTV/Pages/Playouts.razor

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@inject ChannelWriter<IBackgroundServiceRequest> WorkerChannel;
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
<MudButton Variant="Variant.Filled" Color="Color.Primary" Link="playouts/add">
@ -176,7 +177,7 @@ @@ -176,7 +177,7 @@
private async Task ResetPlayout(PlayoutNameViewModel playout)
{
await _mediator.Send(new BuildPlayout(playout.PlayoutId, PlayoutBuildMode.Reset), _cts.Token);
await WorkerChannel.WriteAsync(new BuildPlayout(playout.PlayoutId, PlayoutBuildMode.Reset), _cts.Token);
if (_table != null)
{
await _table.ReloadServerData();

Loading…
Cancel
Save