@page "/schedules/{Id:int}/items" @using ErsatzTV.Application.Filler @using ErsatzTV.Application.MediaCollections @using ErsatzTV.Application.MediaItems @using ErsatzTV.Application.ProgramSchedules @using ErsatzTV.Application.Search @using ErsatzTV.Application.Watermarks @using ErsatzTV.Core.Domain.Filler @implements IDisposable @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 == null ? string.Empty : DateTime.Today.Add(context.StartTime.Value).ToShortTimeString() : "Dynamic") @context.CollectionName @context.PlayoutMode @if (context.PlayoutMode == PlayoutMode.Multiple && context.MultipleCount.HasValue) { @($" ({context.MultipleCount})") } @if (!_schedule.ShuffleScheduleItems) { } @if (!_schedule.ShuffleScheduleItems) { } Add Schedule Item Save Changes @if (_selectedItem is not null) {
Dynamic @if (!_schedule.ShuffleScheduleItems) { Fixed } Collection Television Show Television Season Artist Multi Collection Smart Collection @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) { Only the first 10 items are shown } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) { Only the first 10 items are shown } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) { Only the first 10 items are shown } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) { Only the first 10 items are shown } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) { Only the first 20 items are shown } @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Artist) { Only the first 10 items are shown } @switch (_selectedItem.CollectionType) { case ProgramScheduleItemCollectionType.MultiCollection: Shuffle Shuffle In Order break; case ProgramScheduleItemCollectionType.Collection: case ProgramScheduleItemCollectionType.SmartCollection: Chronological Random Shuffle Shuffle In Order break; case ProgramScheduleItemCollectionType.TelevisionShow: Chronological Season, Episode Random Shuffle Multi-Episode Shuffle break; default: Chronological Random Shuffle break; } @if (!_schedule.ShuffleScheduleItems) { Flood } One Multiple Duration (none) Ordered Groups Shuffled Groups (none) Offline Filler Normal Filler
@foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.PreRoll)) { @filler.Name } @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.MidRoll)) { @filler.Name } @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.PostRoll)) { @filler.Name } @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.Tail)) { @filler.Name } @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.Fallback)) { @filler.Name } @foreach (WatermarkViewModel watermark in _watermarks) { @watermark.Name } (none) @foreach (LanguageCodeViewModel culture in _availableCultures) { @culture.EnglishName } (none) @foreach (LanguageCodeViewModel culture in _availableCultures) { @culture.EnglishName } None Forced Default Any
}
@code { private readonly CancellationTokenSource _cts = new(); [Parameter] public int Id { get; set; } private ProgramScheduleItemsEditViewModel _schedule = new(); private List _fillerPresets = new(); private List _watermarks = new(); private List _availableCultures = new(); private ProgramScheduleItemEditViewModel _selectedItem; public void Dispose() { _cts.Cancel(); _cts.Dispose(); } protected override async Task OnParametersSetAsync() => await LoadScheduleItems(); private async Task LoadScheduleItems() { // TODO: fix performance _fillerPresets = await Mediator.Send(new GetAllFillerPresets(), _cts.Token) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _watermarks = await Mediator.Send(new GetAllWatermarks(), _cts.Token) .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList()); _availableCultures = await Mediator.Send(new GetAllLanguageCodes(), _cts.Token); string name = string.Empty; var shuffleScheduleItems = false; Option maybeSchedule = await Mediator.Send(new GetProgramScheduleById(Id), _cts.Token); foreach (ProgramScheduleViewModel schedule in maybeSchedule) { name = schedule.Name; shuffleScheduleItems = schedule.ShuffleScheduleItems; } Option> maybeResults = await Mediator.Send(new GetProgramScheduleItems(Id), _cts.Token); foreach (IEnumerable items in maybeResults) { _schedule = new ProgramScheduleItemsEditViewModel { Name = name, ShuffleScheduleItems = shuffleScheduleItems, Items = items.Map(ProjectToEditViewModel).ToList() }; if (_schedule.Items.Count == 1) { _selectedItem = _schedule.Items.Head(); } } } private async Task> SearchCollections(string value) { if (string.IsNullOrWhiteSpace(value)) { return new List(); } return await Mediator.Send(new SearchCollections(value), _cts.Token); } private async Task> SearchMultiCollections(string value) { if (string.IsNullOrWhiteSpace(value)) { return new List(); } return await Mediator.Send(new SearchMultiCollections(value), _cts.Token); } private async Task> SearchSmartCollections(string value) { if (string.IsNullOrWhiteSpace(value)) { return new List(); } return await Mediator.Send(new SearchSmartCollections(value), _cts.Token); } private async Task> SearchTelevisionShows(string value) { if (string.IsNullOrWhiteSpace(value)) { return new List(); } return await Mediator.Send(new SearchTelevisionShows(value), _cts.Token); } private async Task> SearchTelevisionSeasons(string value) { if (string.IsNullOrWhiteSpace(value)) { return new List(); } return await Mediator.Send(new SearchTelevisionSeasons(value), _cts.Token); } private async Task> SearchArtists(string value) { if (string.IsNullOrWhiteSpace(value)) { return new List(); } return await Mediator.Send(new SearchArtists(value), _cts.Token); } 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, FillWithGroupMode = item.FillWithGroupMode, CustomTitle = item.CustomTitle, GuideMode = item.GuideMode, PreRollFiller = item.PreRollFiller, MidRollFiller = item.MidRollFiller, PostRollFiller = item.PostRollFiller, TailFiller = item.TailFiller, FallbackFiller = item.FallbackFiller, Watermark = item.Watermark, PreferredAudioLanguageCode = item.PreferredAudioLanguageCode, PreferredAudioTitle = item.PreferredAudioTitle, PreferredSubtitleLanguageCode = item.PreferredSubtitleLanguageCode, SubtitleMode = item.SubtitleMode }; switch (item) { case ProgramScheduleItemMultipleViewModel multiple: result.MultipleCount = multiple.Count; break; case ProgramScheduleItemDurationViewModel duration: result.PlayoutDuration = duration.PlayoutDuration; result.TailMode = duration.TailMode; result.DiscardToFillAttempts = duration.DiscardToFillAttempts; 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 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.FillWithGroupMode, item.MultipleCount, item.PlayoutDuration, item.TailMode, item.DiscardToFillAttempts, item.CustomTitle, item.GuideMode, item.PreRollFiller?.Id, item.MidRollFiller?.Id, item.PostRollFiller?.Id, item.TailFiller?.Id, item.FallbackFiller?.Id, item.Watermark?.Id, item.PreferredAudioLanguageCode, item.PreferredAudioTitle, item.PreferredSubtitleLanguageCode, item.SubtitleMode)).ToList(); Seq errorMessages = await Mediator.Send(new ReplaceProgramScheduleItems(Id, items), _cts.Token).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")); } }