@page "/schedules/{Id:int}/items" @using ErsatzTV.Application.MediaCollections @using ErsatzTV.Application.MediaCollections.Queries @using ErsatzTV.Application.MediaItems @using ErsatzTV.Application.ProgramSchedules @using ErsatzTV.Application.ProgramSchedules.Commands @using ErsatzTV.Application.ProgramSchedules.Queries @using ErsatzTV.Application.Television.Queries @using ErsatzTV.Application.Artists.Queries @inject NavigationManager _navigationManager @inject ILogger _logger @inject ISnackbar _snackbar @inject IMediator _mediator @_schedule.Name Items Start Time Collection Playout Mode @(context.StartType == StartType.Fixed ? context.StartTime?.ToString(@"hh\:mm") ?? string.Empty : "Dynamic") @context.CollectionName @context.PlayoutMode @if (context.PlayoutMode == PlayoutMode.Multiple && context.MultipleCount.HasValue) { @($" ({context.MultipleCount})") } Add Schedule Item Save Changes @if (_selectedItem is not null) {
@foreach (StartType startType in Enum.GetValues()) { @startType } @foreach (ProgramScheduleItemCollectionType collectionType in Enum.GetValues()) { @collectionType } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) { } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) { } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) { } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) { } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) { } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Artist) { } @switch (_selectedItem.CollectionType) { case ProgramScheduleItemCollectionType.MultiCollection: Shuffle Shuffle In Order break; case ProgramScheduleItemCollectionType.Collection: case ProgramScheduleItemCollectionType.SmartCollection: Chronological Random Shuffle Shuffle In Order break; default: Chronological Random Shuffle break; } @foreach (PlayoutMode playoutMode in Enum.GetValues()) { @playoutMode }
Start Type A fixed start type requires a start time, while a dynamic start type means the schedule item will start immediately after the preceding schedule item. Playout Mode
  • One - to play one media item from the collection before advancing to the next schedule item.
  • Multiple - to play a specified count of media items from the collection before advancing to the next schedule item. A count of 0 is a special case that will automatically and always equal the number of items in the collection, so each item from the collection will be played once before advancing.
  • Duration - to play the maximum number of complete media items that will fit in the specified playout duration, before either going offline for the remainder of the playout duration (an offline tail), or immediately advancing to the next schedule item.
  • Flood - to play media items from the collection forever, or until the next schedule item's start time if one exists.
}
@code { [Parameter] public int Id { get; set; } private ProgramScheduleItemsEditViewModel _schedule; private List _mediaCollections; private List _multiCollections; private List _smartCollections; private List _televisionShows; private List _televisionSeasons; private List _artists; private ProgramScheduleItemEditViewModel _selectedItem; protected override Task OnParametersSetAsync() => LoadScheduleItems(); private async Task LoadScheduleItems() { // TODO: fix performance _mediaCollections = await _mediator.Send(new GetAllCollections()) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _multiCollections = await _mediator.Send(new GetAllMultiCollections()) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _smartCollections = await _mediator.Send(new GetAllSmartCollections()) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _televisionShows = await _mediator.Send(new GetAllTelevisionShows()) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _televisionSeasons = await _mediator.Send(new GetAllTelevisionSeasons()) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _artists = await _mediator.Send(new GetAllArtists()) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); string name = string.Empty; Option maybeSchedule = await _mediator.Send(new GetProgramScheduleById(Id)); maybeSchedule.IfSome(vm => name = vm.Name); Option> maybeResults = await _mediator.Send(new GetProgramScheduleItems(Id)); maybeResults.IfSome(items => _schedule = new ProgramScheduleItemsEditViewModel { Name = name, Items = items.Map(ProjectToEditViewModel).ToList() }); } private ProgramScheduleItemEditViewModel ProjectToEditViewModel(ProgramScheduleItemViewModel item) { var result = new ProgramScheduleItemEditViewModel { Id = item.Id, Index = item.Index, StartType = item.StartType, StartTime = item.StartTime, PlayoutMode = item.PlayoutMode, CollectionType = item.CollectionType, Collection = item.Collection, MultiCollection = item.MultiCollection, SmartCollection = item.SmartCollection, MediaItem = item.MediaItem, PlaybackOrder = item.PlaybackOrder, CustomTitle = item.CustomTitle }; switch (item) { case ProgramScheduleItemMultipleViewModel multiple: result.MultipleCount = multiple.Count; break; case ProgramScheduleItemDurationViewModel duration: result.PlayoutDuration = duration.PlayoutDuration; result.OfflineTail = duration.OfflineTail; break; } return result; } private void AddScheduleItem() { var item = new ProgramScheduleItemEditViewModel { Index = _schedule.Items.Map(i => i.Index).DefaultIfEmpty().Max() + 1, StartType = StartType.Dynamic, PlayoutMode = PlayoutMode.One, PlaybackOrder = PlaybackOrder.Shuffle, CollectionType = ProgramScheduleItemCollectionType.Collection }; _schedule.Items.Add(item); _selectedItem = item; } private void RemoveScheduleItem(ProgramScheduleItemEditViewModel item) { _selectedItem = null; _schedule.Items.Remove(item); } private void MoveItemUp(ProgramScheduleItemEditViewModel item) { // swap with lower index ProgramScheduleItemEditViewModel toSwap = _schedule.Items.OrderByDescending(x => x.Index).First(x => x.Index < item.Index); (toSwap.Index, item.Index) = (item.Index, toSwap.Index); } private void MoveItemDown(ProgramScheduleItemEditViewModel item) { // swap with higher index ProgramScheduleItemEditViewModel toSwap = _schedule.Items.OrderBy(x => x.Index).First(x => x.Index > item.Index); (toSwap.Index, item.Index) = (item.Index, toSwap.Index); } private Task> SearchMediaCollections(string value) => _mediaCollections.Filter(c => c.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask(); private Task> SearchMultiCollections(string value) => _multiCollections.Filter(c => c.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask(); private Task> SearchSmartCollections(string value) => _smartCollections.Filter(c => c.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask(); private Task> SearchTelevisionShows(string value) => _televisionShows.Filter(s => s.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask(); private Task> SearchTelevisionSeasons(string value) => _televisionSeasons.Filter(s => s.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask(); private Task> SearchArtists(string value) => _artists.Filter(s => s.Name.Contains(value ?? string.Empty, StringComparison.OrdinalIgnoreCase)).AsTask(); private async Task SaveChanges() { var items = _schedule.Items.Map(item => new ReplaceProgramScheduleItem( item.Index, item.StartType, item.StartTime, item.PlayoutMode, item.CollectionType, item.Collection?.Id, item.MultiCollection?.Id, item.SmartCollection?.Id, item.MediaItem?.MediaItemId, item.PlaybackOrder, item.MultipleCount, item.PlayoutDuration, item.PlayoutMode == PlayoutMode.Duration ? item.OfflineTail.IfNone(false) : null, item.CustomTitle)).ToList(); Seq errorMessages = await _mediator.Send(new ReplaceProgramScheduleItems(Id, items)).Map(e => e.LeftToSeq()); errorMessages.HeadOrNone().Match( error => { _snackbar.Add($"Unexpected error saving schedule: {error.Value}", Severity.Error); _logger.LogError("Unexpected error saving schedule: {Error}", error.Value); }, () => _navigationManager.NavigateTo("/schedules")); } }