diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c22f971..dcf3621e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Optimize transcoding session to only work ahead (at max speed) for 2 minutes before throttling to realtime - This should *greatly* reduce cpu/gpu use when joining a channel, particularly with long content +- Allow manually editing (typing) schedule item fixed start time +- Use different control for editing schedule item duration, and allow 24-hour duration + - This is needed if you want a default/fallback alternate schedule to fill the entire day with one schedule item + - The schedule item should have a fixed start time of midnight (00:00) and a duration of 24 hours ## [0.8.1-beta] - 2023-08-07 ### Added diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs index 0bcb5f770..555c04b87 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs @@ -83,6 +83,11 @@ public abstract class ProgramScheduleItemCommandBase return BaseError.New("[PlayoutDuration] is required for playout mode 'duration'"); } + if (item.PlayoutDuration <= TimeSpan.Zero || item.PlayoutDuration > TimeSpan.FromHours(24)) + { + return BaseError.New("[PlayoutDuration] must be between 1 minute and 24 hours"); + } + if (item.DiscardToFillAttempts is null) { return BaseError.New("[DiscardToFillAttempts] is required for playout mode 'duration'"); @@ -251,7 +256,7 @@ public abstract class ProgramScheduleItemCommandBase SmartCollectionId = item.SmartCollectionId, MediaItemId = item.MediaItemId, PlaybackOrder = item.PlaybackOrder, - PlayoutDuration = FixDuration(item.PlayoutDuration.GetValueOrDefault()), + PlayoutDuration = item.PlayoutDuration.GetValueOrDefault(), TailMode = item.TailMode, DiscardToFillAttempts = FixDiscardToFillAttempts( item.PlaybackOrder, @@ -272,9 +277,6 @@ public abstract class ProgramScheduleItemCommandBase _ => throw new NotSupportedException($"Unsupported playout mode {item.PlayoutMode}") }; - private static TimeSpan FixDuration(TimeSpan duration) => - duration >= TimeSpan.FromDays(1) ? duration.Subtract(TimeSpan.FromDays(1)) : duration; - private static TimeSpan? FixStartTime(TimeSpan? startTime) => startTime.HasValue && startTime.Value >= TimeSpan.FromDays(1) ? startTime.Value.Subtract(TimeSpan.FromDays(1)) diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs b/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs index 1fed7d638..6cdd04f53 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs @@ -29,7 +29,7 @@ public class ReplaceProgramScheduleItemsHandler : ProgramScheduleItemCommandBase { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation validation = await Validate(dbContext, request); - return await LanguageExtensions.Apply(validation, ps => PersistItems(dbContext, request, ps)); + return await validation.Apply(ps => PersistItems(dbContext, request, ps)); } private async Task> PersistItems( diff --git a/ErsatzTV/Pages/ScheduleItemsEditor.razor b/ErsatzTV/Pages/ScheduleItemsEditor.razor index 5b49f03d7..fa1840c27 100644 --- a/ErsatzTV/Pages/ScheduleItemsEditor.razor +++ b/ErsatzTV/Pages/ScheduleItemsEditor.razor @@ -100,7 +100,7 @@ Fixed } - + Collection Television Show @@ -221,7 +221,23 @@ Duration - + + + + + + + + (none) Offline diff --git a/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs b/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs index 76ea2506f..6b2bc184f 100644 --- a/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs +++ b/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs @@ -16,6 +16,8 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged private int? _multipleCount; private TimeSpan? _playoutDuration; private TimeSpan? _startTime; + private int _playoutDurationHours; + private int _playoutDurationMinutes; public int Id { get; set; } public int Index { get; set; } @@ -93,7 +95,31 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged public TimeSpan? PlayoutDuration { get => PlayoutMode == PlayoutMode.Duration ? _playoutDuration : null; - set => _playoutDuration = value; + set + { + _playoutDuration = value; + CheckPlayoutDuration(); + } + } + + public int PlayoutDurationHours + { + get => _playoutDurationHours; + set + { + _playoutDuration = TimeSpan.FromHours(value) + TimeSpan.FromMinutes(_playoutDuration?.Minutes ?? 0); + CheckPlayoutDuration(); + } + } + + public int PlayoutDurationMinutes + { + get => _playoutDurationMinutes; + set + { + _playoutDuration = TimeSpan.FromHours(_playoutDuration?.Hours ?? 0) + TimeSpan.FromMinutes(value); + CheckPlayoutDuration(); + } } public TailMode TailMode { get; set; } @@ -115,4 +141,26 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged [CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + + private void CheckPlayoutDuration() + { + _playoutDuration ??= TimeSpan.Zero; + + if (_playoutDuration > TimeSpan.FromHours(24)) + { + _playoutDuration = TimeSpan.FromHours(24); + } + + if (_playoutDuration < TimeSpan.FromMinutes(1)) + { + _playoutDuration = TimeSpan.FromMinutes(1); + } + + _playoutDurationHours = (int)_playoutDuration.Value.TotalHours; + _playoutDurationMinutes = _playoutDuration.Value.Minutes; + + OnPropertyChanged(nameof(PlayoutDuration)); + OnPropertyChanged(nameof(PlayoutDurationHours)); + OnPropertyChanged(nameof(PlayoutDurationMinutes)); + } }