scheduling bug fixes (#1238)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result.Distinct().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
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
|
||||
|
||||
// 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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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 @@
|
||||
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user