From 5d081ceeff2c33729a02aaec63cdabc5be98752e Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Sat, 16 Aug 2025 09:44:48 -0500 Subject: [PATCH] fix editorconfig and run code cleanup (#2324) * fix formatting rules * reformat ersatztv * reformat ersatztv.application * reformat ersatztv.core * refactor ersatztv.core.tests * reformat ersatztv.ffmpeg * reformat ersatztv.ffmpeg.tests * reformat ersatztv.infrastructure * cleanup infra mysql * cleanup infra sqlite * cleanup infra tests * cleanup ersatztv.scanner * cleanup ersatztv.scanner.tests * sln cleanup * update dependencies --- .config/dotnet-tools.json | 2 +- .editorconfig | 38 +- .../Queries/GetChannelPlaylistHandler.cs | 2 +- .../Commands/CallEmbyShowScannerHandler.cs | 18 +- .../UpdateEmbyLibraryPreferencesHandler.cs | 2 +- .../ErsatzTV.Application.csproj | 2 +- .../Commands/UpdateFFmpegSettingsHandler.cs | 2 +- .../Commands/CreateFillerPresetHandler.cs | 3 +- .../Commands/UpdateFillerPresetHandler.cs | 3 +- .../RefreshGraphicsElementsHandler.cs | 12 +- .../Graphics/GraphicsElementViewModel.cs | 2 +- ErsatzTV.Application/Graphics/Mapper.cs | 6 +- .../Queries/GetAllGraphicsElements.cs | 2 +- .../Queries/GetAllGraphicsElementsHandler.cs | 2 +- .../CallJellyfinShowScannerHandler.cs | 18 +- ...UpdateJellyfinLibraryPreferencesHandler.cs | 2 +- .../Commands/ILocalLibraryRequest.cs | 2 +- .../Commands/QueueShowScanByLibraryId.cs | 2 +- .../QueueShowScanByLibraryIdHandler.cs | 16 +- .../Commands/UpdateLocalLibraryHandler.cs | 2 +- .../Commands/PreviewPlaylistPlayoutHandler.cs | 6 +- .../Queries/GetPlaylistItemsHandler.cs | 2 +- .../MediaItems/Queries/GetRemoteStreamById.cs | 2 - .../Playouts/Commands/BuildPlayoutHandler.cs | 55 +- .../Commands/CreateBlockPlayoutHandler.cs | 1 + .../Commands/CreateYamlPlayoutHandler.cs | 1 + ErsatzTV.Application/Playouts/Mapper.cs | 3 +- .../CheckForOverlappingPlayoutItemsHandler.cs | 14 +- .../Commands/CallPlexShowScannerHandler.cs | 18 +- .../UpdatePlexLibraryPreferencesHandler.cs | 2 +- .../ProgramScheduleItemCommandBase.cs | 2 +- .../ProgramSchedules/Mapper.cs | 12 +- .../Scheduling/BlockViewModel.cs | 8 +- .../Commands/CreateDecoGroupHandler.cs | 2 +- .../CreateDecoTemplateGroupHandler.cs | 4 +- .../Commands/CreateTemplateGroupHandler.cs | 4 +- .../Scheduling/Commands/UpdateDecoHandler.cs | 9 +- ErsatzTV.Application/Scheduling/Mapper.cs | 15 +- .../Queries/GetBlockItemsHandler.cs | 2 +- .../Queries/GetDecoTemplateTreeHandler.cs | 1 - .../Queries/GetTemplateTreeHandler.cs | 1 - .../Queries/QuerySearchIndexRemoteStreams.cs | 3 +- .../Commands/StartFFmpegSessionHandler.cs | 2 +- .../Streaming/HlsSessionWorker.cs | 29 +- .../Streaming/HlsSessionWorkerV2.cs | 13 +- ErsatzTV.Application/Streaming/PtsTime.cs | 1 + .../Queries/GetLastPtsTimeHandler.cs | 5 +- ...layoutItemProcessByChannelNumberHandler.cs | 29 +- .../ExtractEmbeddedSubtitlesHandler.cs | 4 +- .../Queries/GetTelevisionShowByIdHandler.cs | 2 +- .../PrepareTroubleshootingPlaybackHandler.cs | 51 +- .../StartTroubleshootingPlaybackHandler.cs | 19 +- .../Queries/GetTroubleshootingInfoHandler.cs | 10 +- .../ErsatzTV.Core.Tests.csproj | 2 +- .../FFmpeg/CustomStreamSelectorTests.cs | 138 +- .../ClassicScheduling/ContinuePlayoutTests.cs | 117 +- .../GetStartTimeAfterTests.cs | 4 +- .../ClassicScheduling/NewPlayoutTests.cs | 222 +++- .../PlayoutBuilderTestBase.cs | 26 +- .../ClassicScheduling/RefreshPlayoutTests.cs | 20 +- .../ClassicScheduling/ResetPlayoutTests.cs | 22 +- .../Scheduling/FillerExpressionTests.cs | 3 +- .../MultiPartEpisodeGrouperTests.cs | 6 +- .../Scheduling/ScheduleIntegrationTests.cs | 37 +- ErsatzTV.Core/Domain/GraphicsElement.cs | 2 +- ErsatzTV.Core/Domain/GraphicsElementKind.cs | 2 +- ErsatzTV.Core/ErsatzTV.Core.csproj | 2 +- .../FFmpeg/FFmpegComplexFilterBuilder.cs | 2 +- .../FFmpeg/FFmpegLibraryProcessService.cs | 31 +- .../FFmpegPlaybackSettingsCalculator.cs | 2 +- ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs | 5 +- .../Graphics/ImageGraphicsElement.cs | 7 +- ErsatzTV.Core/Health/HealthCheckResult.cs | 7 +- .../Interfaces/FFmpeg/PlayoutItemResult.cs | 2 +- .../Repositories/IPlexTelevisionRepository.cs | 2 +- .../Repositories/ITemplateDataRepository.cs | 4 +- .../Interfaces/Scheduling/IPlayoutBuilder.cs | 4 +- .../Scheduling/IPlayoutTimeShifter.cs | 2 +- .../Interfaces/Search/ISearchIndex.cs | 2 +- .../Streaming/GraphicsEngineContext.cs | 4 +- .../Interfaces/Streaming/IGraphicsEngine.cs | 2 +- ErsatzTV.Core/PathUtils.cs | 3 +- .../BlockScheduling/BlockPlayoutBuilder.cs | 12 +- .../BlockPlayoutChangeDetection.cs | 3 +- .../BlockPlayoutFillerBuilder.cs | 2 +- .../BlockScheduling/EffectiveBlock.cs | 2 +- .../ChronologicalMediaCollectionEnumerator.cs | 32 +- ErsatzTV.Core/Scheduling/FillerExpression.cs | 5 +- .../Scheduling/PlayoutBuildResult.cs | 12 +- ErsatzTV.Core/Scheduling/PlayoutBuilder.cs | 25 +- .../Scheduling/PlayoutModeSchedulerBase.cs | 14 +- .../PlayoutModeSchedulerDuration.cs | 5 +- .../Scheduling/PlayoutModeSchedulerFlood.cs | 3 +- .../PlayoutModeSchedulerMultiple.cs | 5 +- .../Scheduling/PlayoutModeSchedulerOne.cs | 3 +- .../Scheduling/PlayoutReferenceData.cs | 2 +- .../Handlers/YamlPlayoutContentHandler.cs | 6 +- .../Handlers/YamlPlayoutDurationHandler.cs | 2 +- .../Handlers/YamlPlayoutGraphicsOffHandler.cs | 6 +- .../Handlers/YamlPlayoutGraphicsOnHandler.cs | 6 +- .../Handlers/YamlPlayoutMidRollHandler.cs | 3 +- .../Handlers/YamlPlayoutRewindHandler.cs | 2 +- .../Handlers/YamlPlayoutWatermarkHandler.cs | 6 +- .../Models/YamlPlayoutRewindInstruction.cs | 2 +- .../YamlScheduling/YamlPlayoutBuilder.cs | 54 +- .../YamlScheduling/YamlPlayoutContext.cs | 10 +- .../TroubleshootingNotifier.cs | 17 +- .../ErsatzTV.FFmpeg.Tests.csproj | 2 +- .../Capabilities/FFmpegKnownEncoder.cs | 5 +- ErsatzTV.FFmpeg/Capabilities/FourCC.cs | 10 +- .../HardwareCapabilitiesFactory.cs | 7 +- .../Capabilities/IHardwareCapabilities.cs | 6 +- .../VideoToolbox/VideoToolboxUtil.cs | 8 +- .../VideoToolboxHardwareCapabilities.cs | 21 +- .../Environment/CudaVisibleDevicesVariable.cs | 2 +- ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj | 2 +- ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs | 5 +- ErsatzTV.FFmpeg/Filter/ComplexFilter.cs | 2 +- .../Filter/OverlayGraphicsEngineFilter.cs | 3 +- ErsatzTV.FFmpeg/InputFile.cs | 5 +- .../InputOption/DoNotIgnoreLoopInputOption.cs | 2 +- .../OutputFormat/OutputFormatHls.cs | 2 +- .../OutputOption/TimeLimitOutputOption.cs | 2 +- .../Pipeline/NvidiaPipelineBuilder.cs | 9 +- .../Pipeline/PipelineBuilderBase.cs | 4 +- .../Pipeline/QsvPipelineBuilder.cs | 14 +- .../Pipeline/SoftwarePipelineBuilder.cs | 4 +- .../Pipeline/VaapiPipelineBuilder.cs | 4 +- .../ErsatzTV.Infrastructure.MySql.csproj | 12 +- .../ErsatzTV.Infrastructure.Sqlite.csproj | 13 +- .../ErsatzTV.Infrastructure.Tests.csproj | 2 +- .../GraphicsElementConfiguration.cs | 2 +- .../Repositories/EmbyTelevisionRepository.cs | 2 +- .../Data/Repositories/ImageRepository.cs | 8 +- .../JellyfinTelevisionRepository.cs | 2 +- .../Repositories/MediaCollectionRepository.cs | 4 +- .../Data/Repositories/MetadataRepository.cs | 2 +- .../Data/Repositories/MovieRepository.cs | 8 +- .../Data/Repositories/MusicVideoRepository.cs | 8 +- .../Data/Repositories/OtherVideoRepository.cs | 8 +- .../Repositories/PlexTelevisionRepository.cs | 2 +- .../Repositories/RemoteStreamRepository.cs | 8 +- .../Data/Repositories/SongRepository.cs | 8 +- .../Data/Repositories/TelevisionRepository.cs | 8 +- .../Repositories/TemplateDataRepository.cs | 45 +- ErsatzTV.Infrastructure/Emby/EmbyApiClient.cs | 100 +- ErsatzTV.Infrastructure/Emby/IEmbyApi.cs | 20 +- .../Emby/Models/EmbySearchHintsResponse.cs | 2 +- ErsatzTV.Infrastructure/Epg/EpgReader.cs | 14 +- .../ErsatzTV.Infrastructure.csproj | 4 +- ErsatzTV.Infrastructure/GitHub/IGitHubApi.cs | 4 +- .../Health/Checks/BaseHealthCheck.cs | 2 +- .../Health/Checks/ErrorReportsHealthCheck.cs | 4 +- .../Health/Checks/FFmpegVersionHealthCheck.cs | 14 +- .../Health/Checks/UnifiedDockerHealthCheck.cs | 3 +- ErsatzTV.Infrastructure/Images/ImageCache.cs | 2 +- .../Jellyfin/IJellyfinApi.cs | 22 +- .../Jellyfin/JellyfinApiClient.cs | 102 +- .../Models/JellyfinSearchHintsResponse.cs | 2 +- .../Metadata/LocalStatisticsProvider.cs | 13 +- .../Plex/IPlexServerApi.cs | 38 +- .../Plex/Models/PlexHubResponse.cs | 2 +- .../Plex/PlexServerApiClient.cs | 68 +- .../Plex/PlexTvApiClient.cs | 2 +- .../Scheduling/PlayoutTimeShifter.cs | 4 +- .../Scheduling/YamlScheduleValidator.cs | 26 +- .../Search/LowercaseKeywordAnalyzer.cs | 2 +- .../Graphics/Fonts/CustomFontMapper.cs | 12 +- .../Graphics/Fonts/GraphicsEngineFonts.cs | 8 +- .../Streaming/Graphics/GraphicsElement.cs | 21 +- .../Streaming/Graphics/GraphicsEngine.cs | 51 +- .../Streaming/Graphics/IGraphicsElement.cs | 2 +- .../Streaming/Graphics/Image/ImageElement.cs | 8 +- .../Graphics/Image/ImageElementBase.cs | 40 +- .../Graphics/Image/WatermarkElement.cs | 15 +- .../Graphics/OpacityExpressionHelper.cs | 20 +- .../Graphics/PreparedElementImage.cs | 2 +- .../Graphics/Subtitle/SubtitleElement.cs | 69 +- .../Graphics/Text/TemplateFunctions.cs | 4 +- .../Streaming/Graphics/Text/TextElement.cs | 63 +- .../Core/FFmpeg/TranscodingTests.cs | 8 +- .../ErsatzTV.Scanner.Tests.csproj | 2 +- .../Emby/Commands/SynchronizeEmbyShowById.cs | 2 +- .../SynchronizeEmbyShowByIdHandler.cs | 7 +- .../Commands/SynchronizeJellyfinShowById.cs | 2 +- .../SynchronizeJellyfinShowByIdHandler.cs | 13 +- .../Commands/ScanLocalLibraryHandler.cs | 2 +- .../Plex/Commands/SynchronizePlexShowById.cs | 2 +- .../SynchronizePlexShowByIdHandler.cs | 7 +- .../Core/Emby/EmbyMovieLibraryScanner.cs | 2 +- .../Core/Emby/EmbyTelevisionLibraryScanner.cs | 108 +- .../Metadata/ILocalChaptersProvider.cs | 2 +- .../Jellyfin/JellyfinMovieLibraryScanner.cs | 2 +- .../JellyfinTelevisionLibraryScanner.cs | 108 +- .../Core/Metadata/LocalChaptersProvider.cs | 41 +- .../Core/Metadata/LocalMetadataProvider.cs | 22 +- .../MediaServerMovieLibraryScanner.cs | 4 +- .../MediaServerOtherVideoLibraryScanner.cs | 8 +- .../MediaServerTelevisionLibraryScanner.cs | 24 +- .../Core/Metadata/MovieFolderScanner.cs | 2 +- .../Core/Metadata/MusicVideoFolderScanner.cs | 2 +- .../Core/Metadata/Nfo/NfoReader.cs | 30 +- .../Core/Metadata/Nfo/OtherVideoNfoReader.cs | 3 +- .../Core/Metadata/OtherVideoFolderScanner.cs | 2 +- .../Metadata/RemoteStreamFolderScanner.cs | 13 +- .../Core/Metadata/TelevisionFolderScanner.cs | 2 +- .../Core/Plex/PlexOtherVideoLibraryScanner.cs | 2 +- .../Core/Plex/PlexTelevisionLibraryScanner.cs | 10 +- ErsatzTV.Scanner/ErsatzTV.Scanner.csproj | 4 +- ErsatzTV.Scanner/Program.cs | 2 - ErsatzTV.Scanner/Worker.cs | 6 +- ErsatzTV/App.razor | 2 +- .../Controllers/Api/LibrariesController.cs | 9 +- .../Controllers/Api/TroubleshootController.cs | 17 +- ErsatzTV/Controllers/InternalController.cs | 24 +- ErsatzTV/Controllers/IptvController.cs | 4 +- ErsatzTV/ErsatzTV.csproj | 2 +- ErsatzTV/Pages/Artist.razor | 54 +- ErsatzTV/Pages/ArtistList.razor | 2 +- ErsatzTV/Pages/BlockEditor.razor | 587 ++++---- ErsatzTV/Pages/BlockPlayoutEditor.razor | 7 +- ErsatzTV/Pages/Blocks.razor | 2 +- ErsatzTV/Pages/ChannelEditor.razor | 500 +++---- ErsatzTV/Pages/Channels.razor | 2 +- ErsatzTV/Pages/CollectionEditor.razor | 2 +- ErsatzTV/Pages/CollectionItems.razor | 652 ++++----- ErsatzTV/Pages/Collections.razor | 2 +- ErsatzTV/Pages/DecoEditor.razor | 592 ++++----- ErsatzTV/Pages/DecoTemplateEditor.razor | 6 +- ErsatzTV/Pages/DecoTemplates.razor | 2 +- ErsatzTV/Pages/Decos.razor | 2 +- ErsatzTV/Pages/EmbyLibrariesEditor.razor | 2 +- ErsatzTV/Pages/EmbyMediaSourceEditor.razor | 2 +- ErsatzTV/Pages/EmbyMediaSources.razor | 2 +- .../Pages/EmbyPathReplacementsEditor.razor | 2 +- ErsatzTV/Pages/EpisodeList.razor | 2 +- ErsatzTV/Pages/FFmpeg.razor | 2 +- ErsatzTV/Pages/FFmpegEditor.razor | 2 +- ErsatzTV/Pages/FillerPresetEditor.razor | 46 +- ErsatzTV/Pages/FillerPresets.razor | 2 +- ErsatzTV/Pages/ImageBrowser.razor | 2 +- ErsatzTV/Pages/ImageList.razor | 2 +- ErsatzTV/Pages/Index.razor | 2 +- ErsatzTV/Pages/JellyfinLibrariesEditor.razor | 2 +- .../Pages/JellyfinMediaSourceEditor.razor | 2 +- ErsatzTV/Pages/JellyfinMediaSources.razor | 2 +- .../JellyfinPathReplacementsEditor.razor | 2 +- ErsatzTV/Pages/Libraries.razor | 2 +- ErsatzTV/Pages/LocalLibraries.razor | 2 +- ErsatzTV/Pages/LocalLibraryEditor.razor | 7 +- ErsatzTV/Pages/Logs.razor | 4 +- ErsatzTV/Pages/Movie.razor | 9 +- ErsatzTV/Pages/MovieList.razor | 2 +- ErsatzTV/Pages/MultiCollectionEditor.razor | 2 +- ErsatzTV/Pages/MusicVideoList.razor | 2 +- ErsatzTV/Pages/OtherVideoList.razor | 2 +- ErsatzTV/Pages/PlaybackTroubleshooting.razor | 33 +- ErsatzTV/Pages/PlaylistEditor.razor | 615 ++++----- ErsatzTV/Pages/Playlists.razor | 2 +- .../PlayoutAlternateSchedulesEditor.razor | 2 +- ErsatzTV/Pages/PlayoutEditor.razor | 2 +- ErsatzTV/Pages/PlayoutTemplatesEditor.razor | 2 +- ErsatzTV/Pages/Playouts.razor | 4 +- ErsatzTV/Pages/PlexLibrariesEditor.razor | 2 +- ErsatzTV/Pages/PlexMediaSources.razor | 2 +- .../Pages/PlexPathReplacementsEditor.razor | 2 +- ErsatzTV/Pages/RemoteStreamList.razor | 2 +- ErsatzTV/Pages/ScheduleEditor.razor | 2 +- ErsatzTV/Pages/ScheduleItemsEditor.razor | 1179 +++++++++-------- ErsatzTV/Pages/Schedules.razor | 2 +- ErsatzTV/Pages/Search.razor | 784 +++++------ ErsatzTV/Pages/Settings/FFmpegSettings.razor | 2 +- ErsatzTV/Pages/Settings/HDHRSettings.razor | 2 +- ErsatzTV/Pages/Settings/LoggingSettings.razor | 2 +- ErsatzTV/Pages/Settings/PlayoutSettings.razor | 2 +- ErsatzTV/Pages/Settings/ScannerSettings.razor | 2 +- ErsatzTV/Pages/Settings/XMLTVSettings.razor | 2 +- ErsatzTV/Pages/SmartCollectionEditor.razor | 5 +- ErsatzTV/Pages/SongList.razor | 2 +- ErsatzTV/Pages/TelevisionEpisodeList.razor | 12 +- ErsatzTV/Pages/TelevisionSeasonList.razor | 9 +- .../Pages/TelevisionSeasonSearchResults.razor | 2 +- ErsatzTV/Pages/TelevisionShowList.razor | 2 +- ErsatzTV/Pages/TemplateEditor.razor | 6 +- ErsatzTV/Pages/Templates.razor | 2 +- ErsatzTV/Pages/TraktListEditor.razor | 4 +- ErsatzTV/Pages/TraktLists.razor | 2 +- ErsatzTV/Pages/Trash.razor | 758 +++++------ ErsatzTV/Pages/Troubleshooting.razor | 2 +- ErsatzTV/Pages/WatermarkEditor.razor | 6 +- ErsatzTV/Pages/Watermarks.razor | 2 +- ErsatzTV/Pages/YamlPlayoutEditor.razor | 7 +- ErsatzTV/Pages/YamlValidator.razor | 13 +- ErsatzTV/Pages/_Host.cshtml | 2 +- ErsatzTV/Program.cs | 8 +- .../Shared/AddCustomResolutionDialog.razor | 2 +- ErsatzTV/Shared/AddToCollectionDialog.razor | 2 +- ErsatzTV/Shared/AddToPlaylistDialog.razor | 2 +- ErsatzTV/Shared/AddToScheduleDialog.razor | 2 +- ErsatzTV/Shared/AddTraktListDialog.razor | 2 +- ErsatzTV/Shared/ChannelPreviewDialog.razor | 2 +- ErsatzTV/Shared/CopyFFmpegProfileDialog.razor | 2 +- ErsatzTV/Shared/CopyScheduleDialog.razor | 2 +- ErsatzTV/Shared/CopyWatermarkDialog.razor | 2 +- ErsatzTV/Shared/DeleteDialog.razor | 2 +- .../Shared/DeleteFromDatabaseDialog.razor | 2 +- .../DisconnectRemoteMediaSourceDialog.razor | 2 +- .../Shared/EditExternalJsonFileDialog.razor | 2 +- .../EditImageFolderDurationDialog.razor | 2 +- ErsatzTV/Shared/FragmentLetterAnchor.razor | 2 +- ErsatzTV/Shared/LetterBar.razor | 2 +- ErsatzTV/Shared/MainLayout.razor | 6 +- ErsatzTV/Shared/MarkdownView.razor | 2 +- ErsatzTV/Shared/MediaCard.razor | 9 +- ErsatzTV/Shared/MediaCardPager.razor | 4 +- ErsatzTV/Shared/MediaItemInfoDialog.razor | 2 +- .../Shared/MoveLocalLibraryPathDialog.razor | 2 +- ErsatzTV/Shared/RemoteMediaSourceEditor.razor | 2 +- .../RemoteMediaSourceLibrariesEditor.razor | 2 +- ...oteMediaSourcePathReplacementsEditor.razor | 2 +- ErsatzTV/Shared/RemoteMediaSources.razor | 2 +- .../Shared/RemoveFromCollectionDialog.razor | 2 +- .../Shared/SaveAsSmartCollectionDialog.razor | 2 +- ErsatzTV/Shared/SchedulePlayoutReset.razor | 2 +- ErsatzTV/Shared/SignOutOfPlexDialog.razor | 2 +- ErsatzTV/Shared/_Favicons.cshtml | 2 +- .../ChannelEditViewModelValidator.cs | 4 +- ...ogramScheduleItemEditViewModelValidator.cs | 4 +- .../ProgramScheduleItemEditViewModel.cs | 2 +- ErsatzTV/_Imports.razor | 2 +- ErsatzTV/wwwroot/browserconfig.xml | 2 +- 331 files changed, 4828 insertions(+), 4191 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 3b7ed03ca..17b144868 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2025.1.4", + "version": "2025.2.0", "commands": [ "jb" ], diff --git a/.editorconfig b/.editorconfig index 6f357a3e6..e9840bf15 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,28 +1,11 @@ - [*] charset=utf-8 end_of_line=lf trim_trailing_whitespace=true -insert_final_newline=false +insert_final_newline=true indent_style=space indent_size=4 -[*.json] -ij_json_array_wrapping = normal -ij_json_keep_blank_lines_in_code = 0 -ij_json_keep_indents_on_empty_lines = false -ij_json_keep_line_breaks = true -ij_json_keep_trailing_comma = false -ij_json_object_wrapping = normal -ij_json_property_alignment = do_not_align -ij_json_space_after_colon = true -ij_json_space_after_comma = true -ij_json_space_before_colon = false -ij_json_space_before_comma = false -ij_json_spaces_within_braces = true -ij_json_spaces_within_brackets = true -ij_json_wrap_long_lines = false - # Microsoft .NET properties csharp_new_line_before_members_in_object_initializers=false csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion @@ -58,6 +41,8 @@ resharper_braces_for_for=required resharper_braces_for_foreach=required resharper_braces_for_ifelse=required resharper_braces_for_while=required +resharper_csharp_arguments_literal=positional +resharper_csharp_arguments_named=positional resharper_csharp_insert_final_newline=true resharper_csharp_max_attribute_length_for_same_line=0 resharper_csharp_place_accessorholder_attribute_on_same_line=never @@ -100,7 +85,22 @@ tab_width=4 indent_style = space indent_size = 2 +[*.json] +ij_json_array_wrapping = normal +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_keep_trailing_comma = false +ij_json_object_wrapping = normal +ij_json_property_alignment = do_not_align +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = false +ij_json_space_before_comma = false +ij_json_spaces_within_braces = true +ij_json_spaces_within_brackets = true +ij_json_wrap_long_lines = false [*.cs] # disable CA1848: Use the LoggerMessage delegates` -dotnet_diagnostic.ca1848.severity = none \ No newline at end of file +dotnet_diagnostic.ca1848.severity = none diff --git a/ErsatzTV.Application/Channels/Queries/GetChannelPlaylistHandler.cs b/ErsatzTV.Application/Channels/Queries/GetChannelPlaylistHandler.cs index b15ff3587..d4132aa19 100644 --- a/ErsatzTV.Application/Channels/Queries/GetChannelPlaylistHandler.cs +++ b/ErsatzTV.Application/Channels/Queries/GetChannelPlaylistHandler.cs @@ -27,7 +27,7 @@ public class GetChannelPlaylistHandler : IRequestHandler(); foreach (Channel channel in channels) { - if (channel.IsEnabled == false) + if (!channel.IsEnabled) { continue; } diff --git a/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs b/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs index 4de43e3ba..ae616ca0d 100644 --- a/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs @@ -1,3 +1,5 @@ +using System.Globalization; +using System.Threading.Channels; using ErsatzTV.Application.Libraries; using ErsatzTV.Core; using ErsatzTV.Core.Errors; @@ -5,8 +7,6 @@ using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.FFmpeg.Runtime; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -using System.Globalization; -using System.Threading.Channels; namespace ErsatzTV.Application.Emby; @@ -67,16 +67,12 @@ public class CallEmbyShowScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - SynchronizeEmbyShowById request) - { - return Task.FromResult(DateTimeOffset.MinValue); - } + SynchronizeEmbyShowById request) => + Task.FromResult(DateTimeOffset.MinValue); protected override bool ScanIsRequired( DateTimeOffset lastScan, int libraryRefreshInterval, - SynchronizeEmbyShowById request) - { - return true; - } -} \ No newline at end of file + SynchronizeEmbyShowById request) => + true; +} diff --git a/ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferencesHandler.cs b/ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferencesHandler.cs index 986a30aeb..61fb721d3 100644 --- a/ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferencesHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/UpdateEmbyLibraryPreferencesHandler.cs @@ -23,7 +23,7 @@ public class UpdateEmbyLibraryPreferences request, CancellationToken cancellationToken) { - var toDisable = request.Preferences.Filter(p => p.ShouldSyncItems == false).Map(p => p.Id).ToList(); + var toDisable = request.Preferences.Filter(p => !p.ShouldSyncItems).Map(p => p.Id).ToList(); List ids = await _mediaSourceRepository.DisableEmbyLibrarySync(toDisable); await _searchIndex.RemoveItems(ids); _searchIndex.Commit(); diff --git a/ErsatzTV.Application/ErsatzTV.Application.csproj b/ErsatzTV.Application/ErsatzTV.Application.csproj index e30aa24d1..d9a4f7a1c 100644 --- a/ErsatzTV.Application/ErsatzTV.Application.csproj +++ b/ErsatzTV.Application/ErsatzTV.Application.csproj @@ -30,4 +30,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs index 69f604757..1d5a30d68 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs @@ -97,7 +97,7 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler allExisting = await dbContext.GraphicsElements .ToListAsync(cancellationToken); - foreach (var existing in allExisting.Where(e => !localFileSystem.FileExists(e.Path))) + foreach (GraphicsElement existing in allExisting.Where(e => !localFileSystem.FileExists(e.Path))) { logger.LogWarning( "Removing graphics element that references non-existing file {File}", @@ -35,7 +35,7 @@ public class RefreshGraphicsElementsHandler( .Where(f => allExisting.All(e => e.Path != f)) .ToList(); - foreach (var path in newTextPaths) + foreach (string path in newTextPaths) { logger.LogDebug("Adding new graphics element from file {File}", path); @@ -53,7 +53,7 @@ public class RefreshGraphicsElementsHandler( .Where(f => allExisting.All(e => e.Path != f)) .ToList(); - foreach (var path in newImagePaths) + foreach (string path in newImagePaths) { logger.LogDebug("Adding new graphics element from file {File}", path); @@ -71,7 +71,7 @@ public class RefreshGraphicsElementsHandler( .Where(f => allExisting.All(e => e.Path != f)) .ToList(); - foreach (var path in newSubtitlePaths) + foreach (string path in newSubtitlePaths) { logger.LogDebug("Adding new graphics element from file {File}", path); @@ -86,4 +86,4 @@ public class RefreshGraphicsElementsHandler( await dbContext.SaveChangesAsync(cancellationToken); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Application/Graphics/GraphicsElementViewModel.cs b/ErsatzTV.Application/Graphics/GraphicsElementViewModel.cs index 2986ecfc5..8f842f983 100644 --- a/ErsatzTV.Application/Graphics/GraphicsElementViewModel.cs +++ b/ErsatzTV.Application/Graphics/GraphicsElementViewModel.cs @@ -1,3 +1,3 @@ namespace ErsatzTV.Application.Graphics; -public record GraphicsElementViewModel(int Id, string Name); \ No newline at end of file +public record GraphicsElementViewModel(int Id, string Name); diff --git a/ErsatzTV.Application/Graphics/Mapper.cs b/ErsatzTV.Application/Graphics/Mapper.cs index af142c66b..462e39cbc 100644 --- a/ErsatzTV.Application/Graphics/Mapper.cs +++ b/ErsatzTV.Application/Graphics/Mapper.cs @@ -6,13 +6,13 @@ public static class Mapper { public static GraphicsElementViewModel ProjectToViewModel(GraphicsElement graphicsElement) { - var fileName = Path.GetFileName(graphicsElement.Path); + string fileName = Path.GetFileName(graphicsElement.Path); return graphicsElement.Kind switch { GraphicsElementKind.Text => new GraphicsElementViewModel(graphicsElement.Id, $"text/{fileName}"), GraphicsElementKind.Image => new GraphicsElementViewModel(graphicsElement.Id, $"image/{fileName}"), - GraphicsElementKind.Subtitle => new GraphicsElementViewModel(graphicsElement.Id, $"subtitle/{fileName}"), + GraphicsElementKind.Subtitle => new GraphicsElementViewModel(graphicsElement.Id, $"subtitle/{fileName}"), _ => new GraphicsElementViewModel(graphicsElement.Id, graphicsElement.Path) }; } -} \ No newline at end of file +} diff --git a/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElements.cs b/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElements.cs index e5986cd4f..b0bfc4af7 100644 --- a/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElements.cs +++ b/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElements.cs @@ -1,3 +1,3 @@ namespace ErsatzTV.Application.Graphics; -public record GetAllGraphicsElements : IRequest>; \ No newline at end of file +public record GetAllGraphicsElements : IRequest>; diff --git a/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElementsHandler.cs b/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElementsHandler.cs index 793192039..69978a229 100644 --- a/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElementsHandler.cs +++ b/ErsatzTV.Application/Graphics/Queries/GetAllGraphicsElementsHandler.cs @@ -16,4 +16,4 @@ public class GetAllGraphicsElementsHandler(IDbContextFactory dbContex .ToListAsync(cancellationToken) .Map(list => list.Map(ProjectToViewModel).ToList()); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs index 3ce6ff3d4..d4132cc6b 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs @@ -1,3 +1,5 @@ +using System.Globalization; +using System.Threading.Channels; using ErsatzTV.Application.Libraries; using ErsatzTV.Core; using ErsatzTV.Core.Errors; @@ -5,8 +7,6 @@ using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.FFmpeg.Runtime; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -using System.Globalization; -using System.Threading.Channels; namespace ErsatzTV.Application.Jellyfin; @@ -67,16 +67,12 @@ public class CallJellyfinShowScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - SynchronizeJellyfinShowById request) - { - return Task.FromResult(DateTimeOffset.MinValue); - } + SynchronizeJellyfinShowById request) => + Task.FromResult(DateTimeOffset.MinValue); protected override bool ScanIsRequired( DateTimeOffset lastScan, int libraryRefreshInterval, - SynchronizeJellyfinShowById request) - { - return true; - } -} \ No newline at end of file + SynchronizeJellyfinShowById request) => + true; +} diff --git a/ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferencesHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferencesHandler.cs index d6c2fa7cb..fe861c616 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferencesHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/UpdateJellyfinLibraryPreferencesHandler.cs @@ -23,7 +23,7 @@ public class UpdateJellyfinLibraryPreferences request, CancellationToken cancellationToken) { - var toDisable = request.Preferences.Filter(p => p.ShouldSyncItems == false).Map(p => p.Id).ToList(); + var toDisable = request.Preferences.Filter(p => !p.ShouldSyncItems).Map(p => p.Id).ToList(); List ids = await _mediaSourceRepository.DisableJellyfinLibrarySync(toDisable); await _searchIndex.RemoveItems(ids); _searchIndex.Commit(); diff --git a/ErsatzTV.Application/Libraries/Commands/ILocalLibraryRequest.cs b/ErsatzTV.Application/Libraries/Commands/ILocalLibraryRequest.cs index 698f643d4..862dbf0e5 100644 --- a/ErsatzTV.Application/Libraries/Commands/ILocalLibraryRequest.cs +++ b/ErsatzTV.Application/Libraries/Commands/ILocalLibraryRequest.cs @@ -2,5 +2,5 @@ public interface ILocalLibraryRequest { - public string Name { get; } + string Name { get; } } diff --git a/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryId.cs b/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryId.cs index d75ad7d5e..e4b1bf194 100644 --- a/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryId.cs +++ b/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryId.cs @@ -1,3 +1,3 @@ namespace ErsatzTV.Application.Libraries; -public record QueueShowScanByLibraryId(int LibraryId, int ShowId, string ShowTitle, bool DeepScan) : IRequest; \ No newline at end of file +public record QueueShowScanByLibraryId(int LibraryId, int ShowId, string ShowTitle, bool DeepScan) : IRequest; diff --git a/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs b/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs index 0b578bb4f..989247c12 100644 --- a/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs @@ -1,6 +1,7 @@ using ErsatzTV.Application.Emby; using ErsatzTV.Application.Jellyfin; using ErsatzTV.Application.Plex; +using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Locking; using ErsatzTV.Infrastructure.Data; @@ -48,25 +49,28 @@ public class QueueShowScanByLibraryIdHandler( return false; } - logger.LogDebug("Queued show scan for library id {Id}, show: {ShowTitle}, deepScan: {DeepScan}", - library.Id, request.ShowTitle, request.DeepScan); + logger.LogDebug( + "Queued show scan for library id {Id}, show: {ShowTitle}, deepScan: {DeepScan}", + library.Id, + request.ShowTitle, + request.DeepScan); try { switch (library) { case PlexLibrary: - var plexResult = await mediator.Send( + Either plexResult = await mediator.Send( new SynchronizePlexShowById(library.Id, request.ShowId, request.DeepScan), cancellationToken); return plexResult.IsRight; case JellyfinLibrary: - var jellyfinResult = await mediator.Send( + Either jellyfinResult = await mediator.Send( new SynchronizeJellyfinShowById(library.Id, request.ShowId, request.DeepScan), cancellationToken); return jellyfinResult.IsRight; case EmbyLibrary: - var embyResult = await mediator.Send( + Either embyResult = await mediator.Send( new SynchronizeEmbyShowById(library.Id, request.ShowId, request.DeepScan), cancellationToken); return embyResult.IsRight; @@ -87,4 +91,4 @@ public class QueueShowScanByLibraryIdHandler( return false; } -} \ No newline at end of file +} diff --git a/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs b/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs index 807101377..bc6833960 100644 --- a/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs @@ -55,7 +55,7 @@ public class UpdateLocalLibraryHandler : LocalLibraryHandlerBase, var toRemoveIds = toRemove.Map(lp => lp.Id).ToHashSet(); - int changeCount = 0; + var changeCount = 0; // save item ids first; will need to remove from search index List itemsToRemove = await dbContext.MediaItems diff --git a/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs index d10ffbf48..37ca44a96 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs @@ -55,7 +55,11 @@ public class PreviewPlaylistPlayoutHandler( // TODO: make an explicit method to preview, this is ugly playoutBuilder.TrimStart = false; playoutBuilder.DebugPlaylist = playout.ProgramSchedule.Items[0].Playlist; - var result = await playoutBuilder.Build(playout, referenceData, PlayoutBuildMode.Reset, cancellationToken); + PlayoutBuildResult result = await playoutBuilder.Build( + playout, + referenceData, + PlayoutBuildMode.Reset, + cancellationToken); var maxItems = 0; Dictionary> map = diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistItemsHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistItemsHandler.cs index a406bd5fb..995777683 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistItemsHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistItemsHandler.cs @@ -57,7 +57,7 @@ public class GetPlaylistItemsHandler(IDbContextFactory dbContextFacto .ThenInclude(mm => mm.Artwork) .ToListAsync(cancellationToken); - if (allItems.All(bi => bi.IncludeInProgramGuide == false)) + if (allItems.All(bi => !bi.IncludeInProgramGuide)) { foreach (PlaylistItem bi in allItems) { diff --git a/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamById.cs b/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamById.cs index 0ca058e7c..1e82df40a 100644 --- a/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamById.cs +++ b/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamById.cs @@ -1,5 +1,3 @@ -using ErsatzTV.Core; - namespace ErsatzTV.Application.MediaItems; public record GetRemoteStreamById(int RemoteStreamId) : IRequest>; diff --git a/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs index 871d29f5b..4d2178583 100644 --- a/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/BuildPlayoutHandler.cs @@ -13,6 +13,7 @@ using ErsatzTV.Core.Scheduling; using ErsatzTV.Infrastructure.Data; using ErsatzTV.Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; +using Channel = ErsatzTV.Core.Domain.Channel; namespace ErsatzTV.Application.Playouts; @@ -23,10 +24,10 @@ public class BuildPlayoutHandler : IRequestHandler _dbContextFactory; private readonly IEntityLocker _entityLocker; - private readonly IPlayoutTimeShifter _playoutTimeShifter; private readonly IExternalJsonPlayoutBuilder _externalJsonPlayoutBuilder; private readonly IFFmpegSegmenterService _ffmpegSegmenterService; private readonly IPlayoutBuilder _playoutBuilder; + private readonly IPlayoutTimeShifter _playoutTimeShifter; private readonly ChannelWriter _workerChannel; private readonly IYamlPlayoutBuilder _yamlPlayoutBuilder; @@ -78,9 +79,9 @@ public class BuildPlayoutHandler : IRequestHandler pi.PlayoutId == playout.Id) @@ -145,7 +154,7 @@ public class BuildPlayoutHandler : IRequestHandler pi.PlayoutId == playout.Id) @@ -163,8 +172,10 @@ public class BuildPlayoutHandler : IRequestHandler 0) { changeCount += 1; - bool anyWatermarks = result.AddedItems.Any(i => i.PlayoutItemWatermarks is not null && i.PlayoutItemWatermarks.Count > 0); - bool anyGraphicsElements = result.AddedItems.Any(i => i.PlayoutItemGraphicsElements is not null && i.PlayoutItemGraphicsElements.Count > 0); + bool anyWatermarks = result.AddedItems.Any(i => + i.PlayoutItemWatermarks is not null && i.PlayoutItemWatermarks.Count > 0); + bool anyGraphicsElements = result.AddedItems.Any(i => + i.PlayoutItemGraphicsElements is not null && i.PlayoutItemGraphicsElements.Count > 0); if (anyWatermarks || anyGraphicsElements) { // need to use slow ef core to also insert watermarks and graphics elements properly @@ -254,11 +265,11 @@ public class BuildPlayoutHandler : IRequestHandler maybePlayout = await dbContext.Playouts .Include(p => p.Anchor) .SelectOneAsync(p => p.Id, p => p.Id == buildPlayout.PlayoutId); - foreach (var playout in maybePlayout) + foreach (Playout playout in maybePlayout) { switch (playout.ProgramSchedulePlayoutType) { @@ -267,7 +278,7 @@ public class BuildPlayoutHandler : IRequestHandler p.FillGroupIndices) .LoadAsync(); - foreach (var fillGroupIndex in playout.FillGroupIndices) + foreach (PlayoutScheduleItemFillGroupIndex fillGroupIndex in playout.FillGroupIndices) { await dbContext.Entry(fillGroupIndex) .Reference(fgi => fgi.EnumeratorState) @@ -278,7 +289,7 @@ public class BuildPlayoutHandler : IRequestHandler p.ProgramScheduleAnchors) .LoadAsync(); - foreach (var anchor in playout.ProgramScheduleAnchors) + foreach (PlayoutProgramScheduleAnchor anchor in playout.ProgramScheduleAnchors) { await dbContext.Entry(anchor) .Reference(a => a.EnumeratorState) @@ -297,12 +308,12 @@ public class BuildPlayoutHandler : IRequestHandler c.Playouts.Any(p => p.Id == playoutId)) .FirstOrDefaultAsync(); - var deco = Option.None; + Option deco = Option.None; List existingItems = []; List playoutTemplates = []; @@ -332,7 +343,7 @@ public class BuildPlayoutHandler : IRequestHandler ps.Playouts.Any(p => p.Id == playoutId)) .Include(ps => ps.Items) @@ -354,7 +365,7 @@ public class BuildPlayoutHandler : IRequestHandler psi.FallbackFiller) .FirstOrDefaultAsync(); - var programScheduleAlternates = await dbContext.ProgramScheduleAlternates + List programScheduleAlternates = await dbContext.ProgramScheduleAlternates .AsNoTracking() .Where(pt => pt.PlayoutId == playoutId) .Include(a => a.ProgramSchedule) @@ -384,7 +395,7 @@ public class BuildPlayoutHandler : IRequestHandler psi.FallbackFiller) .ToListAsync(); - var playoutHistory = await dbContext.PlayoutHistory + List playoutHistory = await dbContext.PlayoutHistory .AsNoTracking() .Where(h => h.PlayoutId == playoutId) .ToListAsync(); diff --git a/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs index 84089a8ea..08806ff61 100644 --- a/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs @@ -33,6 +33,7 @@ public class CreateBlockPlayoutHandler( { await channel.WriteAsync(new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false)); } + await channel.WriteAsync(new RefreshChannelList()); return new CreatePlayoutResponse(playout.Id); } diff --git a/ErsatzTV.Application/Playouts/Commands/CreateYamlPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/CreateYamlPlayoutHandler.cs index 64081202f..c4091750d 100644 --- a/ErsatzTV.Application/Playouts/Commands/CreateYamlPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/CreateYamlPlayoutHandler.cs @@ -46,6 +46,7 @@ public class CreateYamlPlayoutHandler { await _channel.WriteAsync(new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false)); } + await _channel.WriteAsync(new RefreshChannelList()); return new CreatePlayoutResponse(playout.Id); } diff --git a/ErsatzTV.Application/Playouts/Mapper.cs b/ErsatzTV.Application/Playouts/Mapper.cs index d5e930397..fc81e7a95 100644 --- a/ErsatzTV.Application/Playouts/Mapper.cs +++ b/ErsatzTV.Application/Playouts/Mapper.cs @@ -76,7 +76,8 @@ internal static class Mapper case Image i: return i.ImageMetadata.HeadOrNone().Map(im => im.Title ?? string.Empty).IfNone("[unknown image]"); case RemoteStream rs: - return rs.RemoteStreamMetadata.HeadOrNone().Map(im => im.Title ?? string.Empty).IfNone("[unknown remote stream]"); + return rs.RemoteStreamMetadata.HeadOrNone().Map(im => im.Title ?? string.Empty) + .IfNone("[unknown remote stream]"); default: return string.Empty; } diff --git a/ErsatzTV.Application/Playouts/Queries/CheckForOverlappingPlayoutItemsHandler.cs b/ErsatzTV.Application/Playouts/Queries/CheckForOverlappingPlayoutItemsHandler.cs index 746c1becb..d995f5c66 100644 --- a/ErsatzTV.Application/Playouts/Queries/CheckForOverlappingPlayoutItemsHandler.cs +++ b/ErsatzTV.Application/Playouts/Queries/CheckForOverlappingPlayoutItemsHandler.cs @@ -1,3 +1,4 @@ +using ErsatzTV.Core.Domain; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -18,22 +19,21 @@ public class CheckForOverlappingPlayoutItemsHandler( .AnyAsync( a => dbContext.PlayoutItems .Where(b => b.PlayoutId == a.PlayoutId) - .Any( - b => - a.Id < b.Id && - a.Start < b.Finish && - a.Finish > b.Start), + .Any(b => + a.Id < b.Id && + a.Start < b.Finish && + a.Finish > b.Start), cancellationToken); if (hasConflict) { - var maybeChannel = await dbContext.Channels + Option maybeChannel = await dbContext.Channels .AsNoTracking() .Where(c => c.Playouts.Any(p => p.Id == request.PlayoutId)) .FirstOrDefaultAsync(cancellationToken) .Map(Optional); - foreach (var channel in maybeChannel) + foreach (Channel channel in maybeChannel) { logger.LogWarning( "Playout for channel {ChannelName} has overlapping playout items; this may be a bug.", diff --git a/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs b/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs index aeffc026e..720a432f2 100644 --- a/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs @@ -1,3 +1,5 @@ +using System.Globalization; +using System.Threading.Channels; using ErsatzTV.Application.Libraries; using ErsatzTV.Core; using ErsatzTV.Core.Errors; @@ -5,8 +7,6 @@ using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.FFmpeg.Runtime; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; -using System.Globalization; -using System.Threading.Channels; namespace ErsatzTV.Application.Plex; @@ -67,16 +67,12 @@ public class CallPlexShowScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - SynchronizePlexShowById request) - { - return Task.FromResult(DateTimeOffset.MinValue); - } + SynchronizePlexShowById request) => + Task.FromResult(DateTimeOffset.MinValue); protected override bool ScanIsRequired( DateTimeOffset lastScan, int libraryRefreshInterval, - SynchronizePlexShowById request) - { - return true; - } -} \ No newline at end of file + SynchronizePlexShowById request) => + true; +} diff --git a/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs b/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs index 23d497f43..6d3016e49 100644 --- a/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/UpdatePlexLibraryPreferencesHandler.cs @@ -23,7 +23,7 @@ public class UpdatePlexLibraryPreferences request, CancellationToken cancellationToken) { - var toDisable = request.Preferences.Filter(p => p.ShouldSyncItems == false).Map(p => p.Id).ToList(); + var toDisable = request.Preferences.Filter(p => !p.ShouldSyncItems).Map(p => p.Id).ToList(); List ids = await _mediaSourceRepository.DisablePlexLibrarySync(toDisable); await _searchIndex.RemoveItems(ids); _searchIndex.Commit(); diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs index 68517a795..157e4d486 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs @@ -302,7 +302,7 @@ public abstract class ProgramScheduleItemCommandBase _ => throw new NotSupportedException($"Unsupported playout mode {item.PlayoutMode}") }; - foreach (var watermarkId in item.WatermarkIds) + foreach (int watermarkId in item.WatermarkIds) { result.ProgramScheduleItemWatermarks ??= []; result.ProgramScheduleItemWatermarks.Add( diff --git a/ErsatzTV.Application/ProgramSchedules/Mapper.cs b/ErsatzTV.Application/ProgramSchedules/Mapper.cs index 6ddefa0f0..632e6f5d1 100644 --- a/ErsatzTV.Application/ProgramSchedules/Mapper.cs +++ b/ErsatzTV.Application/ProgramSchedules/Mapper.cs @@ -66,7 +66,8 @@ internal static class Mapper duration.FallbackFiller != null ? Filler.Mapper.ProjectToViewModel(duration.FallbackFiller) : null, - duration.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(), + duration.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)) + .ToList(), duration.PreferredAudioLanguageCode, duration.PreferredAudioTitle, duration.PreferredSubtitleLanguageCode, @@ -117,7 +118,8 @@ internal static class Mapper flood.FallbackFiller != null ? Filler.Mapper.ProjectToViewModel(flood.FallbackFiller) : null, - flood.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(), + flood.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)) + .ToList(), flood.PreferredAudioLanguageCode, flood.PreferredAudioTitle, flood.PreferredSubtitleLanguageCode, @@ -170,7 +172,8 @@ internal static class Mapper multiple.FallbackFiller != null ? Filler.Mapper.ProjectToViewModel(multiple.FallbackFiller) : null, - multiple.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(), + multiple.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)) + .ToList(), multiple.PreferredAudioLanguageCode, multiple.PreferredAudioTitle, multiple.PreferredSubtitleLanguageCode, @@ -221,7 +224,8 @@ internal static class Mapper one.FallbackFiller != null ? Filler.Mapper.ProjectToViewModel(one.FallbackFiller) : null, - one.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)).ToList(), + one.ProgramScheduleItemWatermarks.Map(wm => Watermarks.Mapper.ProjectToViewModel(wm.Watermark)) + .ToList(), one.PreferredAudioLanguageCode, one.PreferredAudioTitle, one.PreferredSubtitleLanguageCode, diff --git a/ErsatzTV.Application/Scheduling/BlockViewModel.cs b/ErsatzTV.Application/Scheduling/BlockViewModel.cs index 3e0a216a5..d54a36213 100644 --- a/ErsatzTV.Application/Scheduling/BlockViewModel.cs +++ b/ErsatzTV.Application/Scheduling/BlockViewModel.cs @@ -2,4 +2,10 @@ using ErsatzTV.Core.Domain.Scheduling; namespace ErsatzTV.Application.Scheduling; -public record BlockViewModel(int Id, int GroupId, string GroupName, string Name, int Minutes, BlockStopScheduling StopScheduling); +public record BlockViewModel( + int Id, + int GroupId, + string GroupName, + string Name, + int Minutes, + BlockStopScheduling StopScheduling); diff --git a/ErsatzTV.Application/Scheduling/Commands/CreateDecoGroupHandler.cs b/ErsatzTV.Application/Scheduling/Commands/CreateDecoGroupHandler.cs index d6c82e51c..a065df63b 100644 --- a/ErsatzTV.Application/Scheduling/Commands/CreateDecoGroupHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/CreateDecoGroupHandler.cs @@ -43,4 +43,4 @@ public class CreateDecoGroupHandler(IDbContextFactory dbContextFactor return (result1, result2).Apply((_, _) => createDecoGroup.Name); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Application/Scheduling/Commands/CreateDecoTemplateGroupHandler.cs b/ErsatzTV.Application/Scheduling/Commands/CreateDecoTemplateGroupHandler.cs index d3a5388d3..dbcb7fe41 100644 --- a/ErsatzTV.Application/Scheduling/Commands/CreateDecoTemplateGroupHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/CreateDecoTemplateGroupHandler.cs @@ -26,7 +26,9 @@ public class CreateDecoTemplateGroupHandler(IDbContextFactory dbConte return Mapper.ProjectToViewModel(decoDecoTemplateGroup); } - private static Task> Validate(TvContext dbContext, CreateDecoTemplateGroup request) => + private static Task> Validate( + TvContext dbContext, + CreateDecoTemplateGroup request) => ValidateName(dbContext, request).MapT(name => new DecoTemplateGroup { Name = name, DecoTemplates = [] }); private static async Task> ValidateName( diff --git a/ErsatzTV.Application/Scheduling/Commands/CreateTemplateGroupHandler.cs b/ErsatzTV.Application/Scheduling/Commands/CreateTemplateGroupHandler.cs index c09f2b271..f0d0ed1b9 100644 --- a/ErsatzTV.Application/Scheduling/Commands/CreateTemplateGroupHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/CreateTemplateGroupHandler.cs @@ -26,7 +26,9 @@ public class CreateTemplateGroupHandler(IDbContextFactory dbContextFa return Mapper.ProjectToViewModel(templateGroup); } - private static Task> Validate(TvContext dbContext, CreateTemplateGroup request) => + private static Task> Validate( + TvContext dbContext, + CreateTemplateGroup request) => ValidateName(dbContext, request).MapT(name => new TemplateGroup { Name = name, Templates = [] }); private static async Task> ValidateName( diff --git a/ErsatzTV.Application/Scheduling/Commands/UpdateDecoHandler.cs b/ErsatzTV.Application/Scheduling/Commands/UpdateDecoHandler.cs index 53d1658f1..00eb1ea3e 100644 --- a/ErsatzTV.Application/Scheduling/Commands/UpdateDecoHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/UpdateDecoHandler.cs @@ -32,10 +32,12 @@ public class UpdateDecoHandler(IDbContextFactory dbContextFactory) if (request.WatermarkMode is DecoMode.Override) { // this is different than schedule item/playout item because we have to merge watermark ids - var toAdd = request.WatermarkIds.Where(id => existing.DecoWatermarks.All(wm => wm.WatermarkId != id)); - var toRemove = existing.DecoWatermarks.Where(wm => !request.WatermarkIds.Contains(wm.WatermarkId)); + IEnumerable toAdd = + request.WatermarkIds.Where(id => existing.DecoWatermarks.All(wm => wm.WatermarkId != id)); + IEnumerable toRemove = + existing.DecoWatermarks.Where(wm => !request.WatermarkIds.Contains(wm.WatermarkId)); existing.DecoWatermarks.RemoveAll(toRemove.Contains); - foreach (var watermarkId in toAdd) + foreach (int watermarkId in toAdd) { existing.DecoWatermarks.Add( new DecoWatermark @@ -75,6 +77,7 @@ public class UpdateDecoHandler(IDbContextFactory dbContextFactory) break; } } + existing.DefaultFillerTrimToFit = request.DefaultFillerTrimToFit; // dead air fallback diff --git a/ErsatzTV.Application/Scheduling/Mapper.cs b/ErsatzTV.Application/Scheduling/Mapper.cs index ac2ebe942..e667d55f7 100644 --- a/ErsatzTV.Application/Scheduling/Mapper.cs +++ b/ErsatzTV.Application/Scheduling/Mapper.cs @@ -9,9 +9,10 @@ internal static class Mapper internal static TreeViewModel ProjectToViewModel(List decoTemplateGroups) => new( decoTemplateGroups.OrderBy(dtg => dtg.Name).Map(dtg => new TreeGroupViewModel( - dtg.Id, - dtg.Name, - dtg.DecoTemplates.OrderBy(dt => dt.Name).Map(dt => new TreeItemViewModel(dt.Id, dt.Name)).ToList())).ToList()); + dtg.Id, + dtg.Name, + dtg.DecoTemplates.OrderBy(dt => dt.Name).Map(dt => new TreeItemViewModel(dt.Id, dt.Name)).ToList())) + .ToList()); internal static TreeViewModel ProjectToViewModel(List decoGroups) => new( @@ -30,9 +31,11 @@ internal static class Mapper internal static BlockTreeViewModel ProjectToViewModel(List blockGroups) => new( blockGroups.OrderBy(bg => bg.Name).Map(bg => new BlockTreeBlockGroupViewModel( - bg.Id, - bg.Name, - bg.Blocks.OrderBy(b => b.Name).Map(b => new BlockTreeBlockViewModel(b.Id, b.Name, b.Minutes)).ToList())).ToList()); + bg.Id, + bg.Name, + bg.Blocks.OrderBy(b => b.Name).Map(b => new BlockTreeBlockViewModel(b.Id, b.Name, b.Minutes)) + .ToList())) + .ToList()); internal static BlockGroupViewModel ProjectToViewModel(BlockGroup blockGroup) => new(blockGroup.Id, blockGroup.Name); diff --git a/ErsatzTV.Application/Scheduling/Queries/GetBlockItemsHandler.cs b/ErsatzTV.Application/Scheduling/Queries/GetBlockItemsHandler.cs index dcfb5093a..b56dc932d 100644 --- a/ErsatzTV.Application/Scheduling/Queries/GetBlockItemsHandler.cs +++ b/ErsatzTV.Application/Scheduling/Queries/GetBlockItemsHandler.cs @@ -33,7 +33,7 @@ public class GetBlockItemsHandler(IDbContextFactory dbContextFactory) .ThenInclude(am => am.Artwork) .ToListAsync(cancellationToken); - if (allItems.All(bi => bi.IncludeInProgramGuide == false)) + if (allItems.All(bi => !bi.IncludeInProgramGuide)) { foreach (BlockItem bi in allItems) { diff --git a/ErsatzTV.Application/Scheduling/Queries/GetDecoTemplateTreeHandler.cs b/ErsatzTV.Application/Scheduling/Queries/GetDecoTemplateTreeHandler.cs index 990a09e2e..920f87c30 100644 --- a/ErsatzTV.Application/Scheduling/Queries/GetDecoTemplateTreeHandler.cs +++ b/ErsatzTV.Application/Scheduling/Queries/GetDecoTemplateTreeHandler.cs @@ -1,5 +1,4 @@ using ErsatzTV.Application.Tree; -using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain.Scheduling; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; diff --git a/ErsatzTV.Application/Scheduling/Queries/GetTemplateTreeHandler.cs b/ErsatzTV.Application/Scheduling/Queries/GetTemplateTreeHandler.cs index fd0dd253d..3cd57563c 100644 --- a/ErsatzTV.Application/Scheduling/Queries/GetTemplateTreeHandler.cs +++ b/ErsatzTV.Application/Scheduling/Queries/GetTemplateTreeHandler.cs @@ -1,5 +1,4 @@ using ErsatzTV.Application.Tree; -using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain.Scheduling; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; diff --git a/ErsatzTV.Application/Search/Queries/QuerySearchIndexRemoteStreams.cs b/ErsatzTV.Application/Search/Queries/QuerySearchIndexRemoteStreams.cs index a9aa92d22..1a611c2de 100644 --- a/ErsatzTV.Application/Search/Queries/QuerySearchIndexRemoteStreams.cs +++ b/ErsatzTV.Application/Search/Queries/QuerySearchIndexRemoteStreams.cs @@ -2,4 +2,5 @@ namespace ErsatzTV.Application.Search; -public record QuerySearchIndexRemoteStreams(string Query, int PageNumber, int PageSize) : IRequest; +public record QuerySearchIndexRemoteStreams(string Query, int PageNumber, int PageSize) + : IRequest; diff --git a/ErsatzTV.Application/Streaming/Commands/StartFFmpegSessionHandler.cs b/ErsatzTV.Application/Streaming/Commands/StartFFmpegSessionHandler.cs index 588393a96..c3e7cfb2c 100644 --- a/ErsatzTV.Application/Streaming/Commands/StartFFmpegSessionHandler.cs +++ b/ErsatzTV.Application/Streaming/Commands/StartFFmpegSessionHandler.cs @@ -21,8 +21,8 @@ public class StartFFmpegSessionHandler : IRequestHandler _logger; @@ -36,6 +36,7 @@ public class HlsSessionWorker : IHlsSessionWorker private readonly Option _targetFramerate; private CancellationTokenSource _cancellationTokenSource; private string _channelNumber; + private DateTimeOffset _channelStart; private bool _disposedValue; private bool _hasWrittenSegments; private DateTimeOffset _lastAccess; @@ -44,7 +45,6 @@ public class HlsSessionWorker : IHlsSessionWorker private HlsSessionState _state; private Timer _timer; private DateTimeOffset _transcodedUntil; - private DateTimeOffset _channelStart; public HlsSessionWorker( IServiceScopeFactory serviceScopeFactory, @@ -152,7 +152,10 @@ public class HlsSessionWorker : IHlsSessionWorker GC.SuppressFinalize(this); } - public async Task Run(string channelNumber, Option idleTimeout, CancellationToken incomingCancellationToken) + public async Task Run( + string channelNumber, + Option idleTimeout, + CancellationToken incomingCancellationToken) { _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(incomingCancellationToken); @@ -160,7 +163,7 @@ public class HlsSessionWorker : IHlsSessionWorker { _channelNumber = channelNumber; - foreach (var timeout in idleTimeout) + foreach (TimeSpan timeout in idleTimeout) { lock (_sync) { @@ -181,14 +184,14 @@ public class HlsSessionWorker : IHlsSessionWorker Touch(); _transcodedUntil = DateTimeOffset.Now; PlaylistStart = _transcodedUntil; - _channelStart = _transcodedUntil; + _channelStart = _transcodedUntil; - var maybePlayoutId = await _mediator.Send( + Option maybePlayoutId = await _mediator.Send( new GetPlayoutIdByChannelNumber(_channelNumber), cancellationToken); // time shift on-demand playout if needed - foreach (var playoutId in maybePlayoutId) + foreach (int playoutId in maybePlayoutId) { await _mediator.Send( new TimeShiftOnDemandPlayout(playoutId, _transcodedUntil, true), @@ -205,7 +208,7 @@ public class HlsSessionWorker : IHlsSessionWorker while (!cancellationToken.IsCancellationRequested) { - foreach (var timeout in idleTimeout) + foreach (TimeSpan timeout in idleTimeout) { if (DateTimeOffset.Now - _lastAccess > timeout) { @@ -461,7 +464,7 @@ public class HlsSessionWorker : IHlsSessionWorker { await TrimAndDelete(cancellationToken); - var maybePipe = Option.None; + Option maybePipe = Option.None; var stdErrBuffer = new StringBuilder(); Command process = processModel.Process; @@ -472,8 +475,8 @@ public class HlsSessionWorker : IHlsSessionWorker { using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - var processWithPipe = process; - foreach (var graphicsEngineContext in processModel.GraphicsEngineContext) + Command processWithPipe = process; + foreach (GraphicsEngineContext graphicsEngineContext in processModel.GraphicsEngineContext) { var pipe = new Pipe(); maybePipe = pipe; @@ -505,7 +508,7 @@ public class HlsSessionWorker : IHlsSessionWorker await linkedCts.CancelAsync(); // detect the non-zero exit code and transcode the ffmpeg error message instead - string errorMessage = stdErrBuffer.ToString(); + var errorMessage = stdErrBuffer.ToString(); if (string.IsNullOrWhiteSpace(errorMessage)) { errorMessage = $"Unknown FFMPEG error; exit code {commandResult.ExitCode}"; @@ -563,7 +566,7 @@ public class HlsSessionWorker : IHlsSessionWorker } finally { - foreach (var pipe in maybePipe) + foreach (Pipe pipe in maybePipe) { await pipe.Writer.CompleteAsync(); } diff --git a/ErsatzTV.Application/Streaming/HlsSessionWorkerV2.cs b/ErsatzTV.Application/Streaming/HlsSessionWorkerV2.cs index 2f2a9747d..5d6ab17d2 100644 --- a/ErsatzTV.Application/Streaming/HlsSessionWorkerV2.cs +++ b/ErsatzTV.Application/Streaming/HlsSessionWorkerV2.cs @@ -28,6 +28,7 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker private readonly Option _targetFramerate; private CancellationTokenSource _cancellationTokenSource; private string _channelNumber; + private DateTimeOffset _channelStart; private bool _disposedValue; private DateTimeOffset _lastAccess; private Option _lastProcessModel; @@ -35,7 +36,6 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker private HlsSessionState _state; private Timer _timer; private DateTimeOffset _transcodedUntil; - private DateTimeOffset _channelStart; public HlsSessionWorkerV2( IServiceScopeFactory serviceScopeFactory, @@ -99,7 +99,10 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker GC.SuppressFinalize(this); } - public async Task Run(string channelNumber, Option idleTimeout, CancellationToken incomingCancellationToken) + public async Task Run( + string channelNumber, + Option idleTimeout, + CancellationToken incomingCancellationToken) { _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(incomingCancellationToken); @@ -107,7 +110,7 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker { _channelNumber = channelNumber; - foreach (var timeout in idleTimeout) + foreach (TimeSpan timeout in idleTimeout) { lock (_sync) { @@ -130,12 +133,12 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker PlaylistStart = _transcodedUntil; _channelStart = _transcodedUntil; - var maybePlayoutId = await _mediator.Send( + Option maybePlayoutId = await _mediator.Send( new GetPlayoutIdByChannelNumber(_channelNumber), cancellationToken); // time shift on-demand playout if needed - foreach (var playoutId in maybePlayoutId) + foreach (int playoutId in maybePlayoutId) { await _mediator.Send( new TimeShiftOnDemandPlayout(playoutId, _transcodedUntil, true), diff --git a/ErsatzTV.Application/Streaming/PtsTime.cs b/ErsatzTV.Application/Streaming/PtsTime.cs index 631d756e8..127c8154c 100644 --- a/ErsatzTV.Application/Streaming/PtsTime.cs +++ b/ErsatzTV.Application/Streaming/PtsTime.cs @@ -14,6 +14,7 @@ public record PtsTime(long Value) { ptsTime += duration; } + return new PtsTime(ptsTime); } } diff --git a/ErsatzTV.Application/Streaming/Queries/GetLastPtsTimeHandler.cs b/ErsatzTV.Application/Streaming/Queries/GetLastPtsTimeHandler.cs index d2745ff11..0655e3cf3 100644 --- a/ErsatzTV.Application/Streaming/Queries/GetLastPtsTimeHandler.cs +++ b/ErsatzTV.Application/Streaming/Queries/GetLastPtsTimeHandler.cs @@ -60,7 +60,10 @@ public class GetLastPtsTimeHandler : IRequestHandler> GetPts(RequestParameters parameters, FileInfo segment, CancellationToken cancellationToken) + private async Task> GetPts( + RequestParameters parameters, + FileInfo segment, + CancellationToken cancellationToken) { string[] argumentList = { diff --git a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs index 97fd59acb..9c03e6e87 100644 --- a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs +++ b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs @@ -290,18 +290,19 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< disableWatermarks = false; playoutItemWatermarks.Clear(); - playoutItemWatermarks.Add(new ChannelWatermark - { - Mode = ChannelWatermarkMode.Permanent, - Size = WatermarkSize.Scaled, - WidthPercent = 100, - HorizontalMarginPercent = 0, - VerticalMarginPercent = 0, - Opacity = 100, - Location = WatermarkLocation.TopLeft, - ImageSource = ChannelWatermarkImageSource.Resource, - Image = image - }); + playoutItemWatermarks.Add( + new ChannelWatermark + { + Mode = ChannelWatermarkMode.Permanent, + Size = WatermarkSize.Scaled, + WidthPercent = 100, + HorizontalMarginPercent = 0, + VerticalMarginPercent = 0, + Opacity = 100, + Location = WatermarkLocation.TopLeft, + ImageSource = ChannelWatermarkImageSource.Resource, + Image = image + }); } } @@ -355,7 +356,9 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< channel.FFmpegProfile.VaapiDevice, Optional(channel.FFmpegProfile.QsvExtraHardwareFrames), hlsRealtime: request.HlsRealtime, - playoutItemWithPath.PlayoutItem.MediaItem is RemoteStream { IsLive: true } ? StreamInputKind.Live : StreamInputKind.Vod, + playoutItemWithPath.PlayoutItem.MediaItem is RemoteStream { IsLive: true } + ? StreamInputKind.Live + : StreamInputKind.Vod, playoutItemWithPath.PlayoutItem.FillerKind, inPoint, outPoint, diff --git a/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs b/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs index 6932a2912..bd309546b 100644 --- a/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs +++ b/ErsatzTV.Application/Subtitles/Commands/ExtractEmbeddedSubtitlesHandler.cs @@ -280,7 +280,7 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler s.SubtitleKind == SubtitleKind.Embedded) .Filter(s => s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" && s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs") - .Filter(s => s.IsExtracted == false || string.IsNullOrWhiteSpace(s.Path) || + .Filter(s => !s.IsExtracted || string.IsNullOrWhiteSpace(s.Path) || FileDoesntExist(mediaItem.Id, s)); // find cache paths for each subtitle @@ -347,7 +347,7 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler> { - private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IDbContextFactory _dbContextFactory; + private readonly IMediaSourceRepository _mediaSourceRepository; private readonly ISearchRepository _searchRepository; public GetTelevisionShowByIdHandler( diff --git a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs index b7b2d38e3..2f1b0a7d2 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs @@ -34,12 +34,16 @@ public class PrepareTroubleshootingPlaybackHandler( ILogger logger) : IRequestHandler> { - public async Task> Handle(PrepareTroubleshootingPlayback request, CancellationToken cancellationToken) + public async Task> Handle( + PrepareTroubleshootingPlayback request, + CancellationToken cancellationToken) { try { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation> validation = await Validate(dbContext, request); + Validation> validation = await Validate( + dbContext, + request); return await validation.Match( tuple => GetProcess(dbContext, request, tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4), error => Task.FromResult>(error.Join())); @@ -96,8 +100,8 @@ public class PrepareTroubleshootingPlaybackHandler( List watermarks = []; if (request.WatermarkIds.Count > 0) { - var channelWatermarks = await dbContext.ChannelWatermarks - .Where(w => request.WatermarkIds.Contains(w.Id)) + List channelWatermarks = await dbContext.ChannelWatermarks + .Where(w => request.WatermarkIds.Contains(w.Id)) .ToListAsync(); watermarks.AddRange(channelWatermarks); @@ -126,18 +130,19 @@ public class PrepareTroubleshootingPlaybackHandler( string image = is43 ? "song_progress_overlay_43.png" : "song_progress_overlay.png"; watermarks.Clear(); - watermarks.Add(new ChannelWatermark - { - Mode = ChannelWatermarkMode.Permanent, - Size = WatermarkSize.Scaled, - WidthPercent = 100, - HorizontalMarginPercent = 0, - VerticalMarginPercent = 0, - Opacity = 100, - Location = WatermarkLocation.TopLeft, - ImageSource = ChannelWatermarkImageSource.Resource, - Image = image - }); + watermarks.Add( + new ChannelWatermark + { + Mode = ChannelWatermarkMode.Permanent, + Size = WatermarkSize.Scaled, + WidthPercent = 100, + HorizontalMarginPercent = 0, + VerticalMarginPercent = 0, + Opacity = 100, + Location = WatermarkLocation.TopLeft, + ImageSource = ChannelWatermarkImageSource.Resource, + Image = image + }); } } @@ -156,7 +161,7 @@ public class PrepareTroubleshootingPlaybackHandler( TimeSpan outPoint = duration; if (!hlsRealtime) { - foreach (var seekSeconds in request.SeekSeconds) + foreach (int seekSeconds in request.SeekSeconds) { inPoint = TimeSpan.FromSeconds(seekSeconds); if (inPoint > version.Duration) @@ -173,7 +178,7 @@ public class PrepareTroubleshootingPlaybackHandler( } } - var graphicsElements = await dbContext.GraphicsElements + List graphicsElements = await dbContext.GraphicsElements .Where(ge => request.GraphicsElementIds.Contains(ge.Id)) .ToListAsync(); @@ -216,7 +221,9 @@ public class PrepareTroubleshootingPlaybackHandler( return playoutItemResult; } - private static async Task> GetSelectedSubtitle(MediaItem mediaItem, PrepareTroubleshootingPlayback request) + private static async Task> GetSelectedSubtitle( + MediaItem mediaItem, + PrepareTroubleshootingPlayback request) { if (request.SubtitleId is not null) { @@ -268,9 +275,8 @@ public class PrepareTroubleshootingPlaybackHandler( private static async Task> MediaItemMustExist( TvContext dbContext, - PrepareTroubleshootingPlayback request) - { - return await dbContext.MediaItems + PrepareTroubleshootingPlayback request) => + await dbContext.MediaItems .AsNoTracking() .Include(mi => (mi as Episode).EpisodeMetadata) .ThenInclude(em => em.Subtitles) @@ -325,7 +331,6 @@ public class PrepareTroubleshootingPlaybackHandler( .Include(mi => (mi as RemoteStream).RemoteStreamMetadata) .SelectOneAsync(mi => mi.Id, mi => mi.Id == request.MediaItemId) .Map(o => o.ToValidation(new UnableToLocatePlayoutItem())); - } private static Task> FFmpegPathMustExist(TvContext dbContext) => dbContext.ConfigElements.GetValue(ConfigElementKey.FFmpegPath) diff --git a/ErsatzTV.Application/Troubleshooting/Commands/StartTroubleshootingPlaybackHandler.cs b/ErsatzTV.Application/Troubleshooting/Commands/StartTroubleshootingPlaybackHandler.cs index f60bd8c70..59cc54353 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/StartTroubleshootingPlaybackHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/StartTroubleshootingPlaybackHandler.cs @@ -70,8 +70,8 @@ public class StartTroubleshootingPlaybackHandler( cancellationToken); } - if (hwAccel is HardwareAccelerationKind.Vaapi || (hwAccel is HardwareAccelerationKind.Qsv && - runtimeInfo.IsOSPlatform(OSPlatform.Linux))) + if (hwAccel is HardwareAccelerationKind.Vaapi || hwAccel is HardwareAccelerationKind.Qsv && + runtimeInfo.IsOSPlatform(OSPlatform.Linux)) { await File.WriteAllTextAsync( Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "capabilities_vaapi.txt"), @@ -87,20 +87,23 @@ public class StartTroubleshootingPlaybackHandler( cancellationToken); } - logger.LogDebug("ffmpeg troubleshooting arguments {FFmpegArguments}", request.PlayoutItemResult.Process.Arguments); + logger.LogDebug( + "ffmpeg troubleshooting arguments {FFmpegArguments}", + request.PlayoutItemResult.Process.Arguments); - var maybePipe = Option.None; + Option maybePipe = Option.None; try { using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - var processWithPipe = request.PlayoutItemResult.Process; - foreach (var graphicsEngineContext in request.PlayoutItemResult.GraphicsEngineContext) + Command processWithPipe = request.PlayoutItemResult.Process; + foreach (GraphicsEngineContext graphicsEngineContext in request.PlayoutItemResult.GraphicsEngineContext) { var pipe = new Pipe(); maybePipe = pipe; - processWithPipe = processWithPipe.WithStandardInputPipe(PipeSource.FromStream(pipe.Reader.AsStream())); + processWithPipe = + processWithPipe.WithStandardInputPipe(PipeSource.FromStream(pipe.Reader.AsStream())); // fire and forget graphics engine task _ = graphicsEngine.Run( @@ -136,7 +139,7 @@ public class StartTroubleshootingPlaybackHandler( } finally { - foreach (var pipe in maybePipe) + foreach (Pipe pipe in maybePipe) { await pipe.Writer.CompleteAsync(); } diff --git a/ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs b/ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs index 666d19e98..9e951e908 100644 --- a/ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Queries/GetTroubleshootingInfoHandler.cs @@ -135,23 +135,25 @@ public class GetTroubleshootingInfoHandler : IRequestHandler decoders = _hardwareCapabilitiesFactory.GetVideoToolboxDecoders(); videoToolboxCapabilities.AppendLine("VideoToolbox Decoders: "); videoToolboxCapabilities.AppendLine(); - foreach (var decoder in decoders) + foreach (string decoder in decoders) { videoToolboxCapabilities.AppendLine(CultureInfo.InvariantCulture, $"\t{decoder}"); } + videoToolboxCapabilities.AppendLine(); videoToolboxCapabilities.AppendLine(); - var encoders = _hardwareCapabilitiesFactory.GetVideoToolboxEncoders(); + List encoders = _hardwareCapabilitiesFactory.GetVideoToolboxEncoders(); videoToolboxCapabilities.AppendLine("VideoToolbox Encoders: "); videoToolboxCapabilities.AppendLine(); - foreach (var encoder in encoders) + foreach (string encoder in encoders) { videoToolboxCapabilities.AppendLine(CultureInfo.InvariantCulture, $"\t{encoder}"); } + videoToolboxCapabilities.AppendLine(); videoToolboxCapabilities.AppendLine(); } diff --git a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj index 2d2aa0688..48d544a65 100644 --- a/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj +++ b/ErsatzTV.Core.Tests/ErsatzTV.Core.Tests.csproj @@ -35,4 +35,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.Core.Tests/FFmpeg/CustomStreamSelectorTests.cs b/ErsatzTV.Core.Tests/FFmpeg/CustomStreamSelectorTests.cs index e9ddec3f7..db1a43a82 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/CustomStreamSelectorTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/CustomStreamSelectorTests.cs @@ -58,7 +58,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -83,7 +87,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -108,7 +116,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -139,7 +151,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -165,7 +181,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -192,7 +212,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -219,7 +243,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeFalse(); } @@ -241,7 +269,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -269,7 +301,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -303,7 +339,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -334,7 +374,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -367,7 +411,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -481,7 +529,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -509,7 +561,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -539,7 +595,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -569,7 +629,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -596,7 +660,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -623,7 +691,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -650,7 +722,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -677,7 +753,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -703,7 +783,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeTrue(); @@ -730,7 +814,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.Subtitle.IsSome.ShouldBeTrue(); @@ -758,7 +846,11 @@ public class CustomStreamSelectorTests new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = yaml }]), new NullLogger()); - StreamSelectorResult result = await streamSelector.SelectStreams(_channel, DateTimeOffset.Now, _audioVersion, _subtitles); + StreamSelectorResult result = await streamSelector.SelectStreams( + _channel, + DateTimeOffset.Now, + _audioVersion, + _subtitles); result.AudioStream.IsSome.ShouldBeFalse(); result.Subtitle.IsSome.ShouldBeFalse(); diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs index c7453ac6e..dc0547631 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ContinuePlayoutTests.cs @@ -28,7 +28,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().MediaItemId.ShouldBe(1); @@ -73,7 +80,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().MediaItemId.ShouldBe(1); @@ -119,7 +133,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromDays(1); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(4); result.AddedItems.Map(i => i.MediaItemId).ToList().ShouldBe([1, 2, 1, 2]); @@ -156,7 +177,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start2 = HoursAfterMidnight(1); DateTimeOffset finish2 = start2 + TimeSpan.FromDays(1); - result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Continue, start2, finish2, CancellationToken); + result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Continue, + start2, + finish2, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems[0].StartOffset.ShouldBe(finish); @@ -171,7 +199,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start3 = HoursAfterMidnight(2); DateTimeOffset finish3 = start3 + TimeSpan.FromDays(1); - result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Continue, start3, finish3, CancellationToken); + result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Continue, + start3, + finish3, + CancellationToken); result.AddedItems.Count.ShouldBe(0); @@ -196,8 +231,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); playout.ProgramScheduleAnchors.Count.ShouldBe(1); @@ -235,11 +276,19 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase mediaItems.Add(TestMovie(i, TimeSpan.FromMinutes(55), DateTime.Today.AddHours(i))); } - (PlayoutBuilder builder, Playout playout, PlayoutReferenceData referenceData) = TestDataFloodForItems(mediaItems, PlaybackOrder.Shuffle); + (PlayoutBuilder builder, Playout playout, PlayoutReferenceData referenceData) = + TestDataFloodForItems(mediaItems, PlaybackOrder.Shuffle); DateTimeOffset start = HoursAfterMidnight(0).AddSeconds(5); DateTimeOffset finish = start + TimeSpan.FromDays(2); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(53); playout.ProgramScheduleAnchors.Count.ShouldBe(2); @@ -301,7 +350,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); playout.ProgramScheduleAnchors.Count.ShouldBe(2); @@ -346,7 +402,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0).AddSeconds(5); DateTimeOffset finish = start + TimeSpan.FromDays(2); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(53); playout.ProgramScheduleAnchors.Count.ShouldBe(4); @@ -482,8 +545,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(32); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Continue, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Continue, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(5); @@ -595,8 +664,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(5); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Continue, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Continue, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(4); @@ -707,8 +782,14 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase localFileSystem, Logger); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Continue, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Continue, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(5); @@ -728,4 +809,4 @@ public class ContinuePlayoutTests : PlayoutBuilderTestBase playout.Anchor.DurationFinish.ShouldBeNull(); playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(6)); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/GetStartTimeAfterTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/GetStartTimeAfterTests.cs index 177c5af4e..a6f0ce815 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/GetStartTimeAfterTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/GetStartTimeAfterTests.cs @@ -1,7 +1,7 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Scheduling; -using Shouldly; using NUnit.Framework; +using Shouldly; namespace ErsatzTV.Core.Tests.Scheduling.ClassicScheduling; @@ -30,4 +30,4 @@ public class GetStartTimeAfterTests result.ShouldBe(DateTimeOffset.Parse("2025-11-02T02:00:00-06:00")); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs index b9b1b91af..1c44ebed4 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/NewPlayoutTests.cs @@ -46,8 +46,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().MediaItemId.ShouldBe(2); @@ -102,8 +108,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().MediaItemId.ShouldBe(2); @@ -158,8 +170,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().MediaItemId.ShouldBe(2); @@ -189,8 +207,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().StartOffset.ShouldBe(start); @@ -219,8 +243,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().StartOffset.ShouldBe(start); @@ -242,8 +272,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().StartOffset.ShouldBe(start); @@ -266,8 +302,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(1); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(2); result.AddedItems[0].StartOffset.ShouldBe(midnight); @@ -291,8 +333,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(4); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(4); result.AddedItems[0].StartOffset.ShouldBe(start); @@ -321,8 +369,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().MediaItemId.ShouldBe(1); @@ -367,8 +421,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(1); result.AddedItems.Head().MediaItemId.ShouldBe(1); @@ -482,8 +542,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(5); result.AddedItems[0].StartOffset.ShouldBe(start); @@ -582,8 +648,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(30); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(28); result.AddedItems[0].StartOffset.ShouldBe(start); @@ -733,8 +805,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(7); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); @@ -843,8 +921,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(7); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); @@ -952,8 +1036,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(24); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); @@ -1062,8 +1152,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(7); @@ -1177,8 +1273,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); @@ -1292,8 +1394,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(5); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(5); @@ -1416,8 +1524,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(12); @@ -1533,8 +1647,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(1); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(2); @@ -1616,8 +1736,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); @@ -1654,8 +1780,14 @@ public class NewPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromDays(2); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(8); result.AddedItems[0].MediaItemId.ShouldBe(1); @@ -1692,4 +1824,4 @@ public class NewPlayoutTests : PlayoutBuilderTestBase playout.Anchor.NextStartOffset.ShouldBe(start + TimeSpan.FromHours(48)); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/PlayoutBuilderTestBase.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/PlayoutBuilderTestBase.cs index 832e69bb9..39f285550 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/PlayoutBuilderTestBase.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/PlayoutBuilderTestBase.cs @@ -16,9 +16,6 @@ namespace ErsatzTV.Core.Tests.Scheduling.ClassicScheduling; public abstract class PlayoutBuilderTestBase { - [SetUp] - public void SetUp() => CancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token; - protected readonly ILogger Logger; protected CancellationToken CancellationToken; @@ -35,6 +32,9 @@ public abstract class PlayoutBuilderTestBase Logger = loggerFactory.CreateLogger(); } + [SetUp] + public void SetUp() => CancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token; + protected static DateTimeOffset HoursAfterMidnight(int hours) { DateTimeOffset now = DateTimeOffset.Now; @@ -88,7 +88,14 @@ public abstract class PlayoutBuilderTestBase FillGroupIndices = [] }; - var referenceData = new PlayoutReferenceData(playout.Channel, Option.None, [], [], playout.ProgramSchedule, [], []); + var referenceData = new PlayoutReferenceData( + playout.Channel, + Option.None, + [], + [], + playout.ProgramSchedule, + [], + []); return new TestData(builder, playout, referenceData); } @@ -194,10 +201,17 @@ public abstract class PlayoutBuilderTestBase FillGroupIndices = [] }; - var referenceData = new PlayoutReferenceData(playout.Channel, Option.None, [], [], playout.ProgramSchedule, [], []); + var referenceData = new PlayoutReferenceData( + playout.Channel, + Option.None, + [], + [], + playout.ProgramSchedule, + [], + []); return new TestData(builder, playout, referenceData); } protected record TestData(PlayoutBuilder Builder, Playout Playout, PlayoutReferenceData ReferenceData); -} \ No newline at end of file +} diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs index 295d519dd..d9b9d2ff6 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/RefreshPlayoutTests.cs @@ -86,7 +86,14 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase Playout = playout }); - var referenceData = new PlayoutReferenceData(playout.Channel, Option.None, [], [], playout.ProgramSchedule, [], []); + var referenceData = new PlayoutReferenceData( + playout.Channel, + Option.None, + [], + [], + playout.ProgramSchedule, + [], + []); IConfigElementRepository configRepo = Substitute.For(); var televisionRepo = new FakeTelevisionRepository(); @@ -106,7 +113,14 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(24); DateTimeOffset finish = start + TimeSpan.FromDays(1); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Refresh, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Refresh, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(4); result.AddedItems[0].MediaItemId.ShouldBe(2); @@ -124,4 +138,4 @@ public class RefreshPlayoutTests : PlayoutBuilderTestBase playout.Anchor.NextStartOffset.ShouldBe(HoursAfterMidnight(48)); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs index b4dfd8917..733441dbd 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/ResetPlayoutTests.cs @@ -26,8 +26,14 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start = HoursAfterMidnight(0); DateTimeOffset finish = start + TimeSpan.FromHours(6); - PlayoutBuildResult result = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start, finish, CancellationToken); + PlayoutBuildResult result = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + CancellationToken); result.AddedItems.Count.ShouldBe(6); playout.Anchor.NextStartOffset.ShouldBe(finish); @@ -40,8 +46,14 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase DateTimeOffset start2 = HoursAfterMidnight(0); DateTimeOffset finish2 = start2 + TimeSpan.FromHours(6); - PlayoutBuildResult result2 = await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, - PlayoutBuildMode.Reset, start2, finish2, CancellationToken); + PlayoutBuildResult result2 = await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start2, + finish2, + CancellationToken); result2.AddedItems.Count.ShouldBe(6); playout.Anchor.NextStartOffset.ShouldBe(finish); @@ -53,4 +65,4 @@ public class ResetPlayoutTests : PlayoutBuilderTestBase firstSeedValue.ShouldNotBe(secondSeedValue); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core.Tests/Scheduling/FillerExpressionTests.cs b/ErsatzTV.Core.Tests/Scheduling/FillerExpressionTests.cs index 83d009620..4f8419dfe 100644 --- a/ErsatzTV.Core.Tests/Scheduling/FillerExpressionTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/FillerExpressionTests.cs @@ -64,7 +64,8 @@ public class FillerExpressionTests var fillerPreset = new FillerPreset { FillerKind = FillerKind.MidRoll, - Expression = "(total_progress >= 0.2 and matched_points = 0) or (total_progress >= 0.6 and matched_points = 1)" + Expression = + "(total_progress >= 0.2 and matched_points = 0) or (total_progress >= 0.6 and matched_points = 1)" }; List result = FillerExpression.FilterChapters(fillerPreset.Expression, chapters, playoutItem); diff --git a/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs b/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs index b4c5be94b..65bb72048 100644 --- a/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/MultiPartEpisodeGrouperTests.cs @@ -38,7 +38,6 @@ public class MultiPartEpisodeGrouperTests [TestCase("Episode 1 Part One", "Episode 2 (II)", "Episode 3")] [TestCase("Episode 1 (Part One)", "Episode 2 (II)", "Episode 3")] [TestCase("Episode 1 (1)", "Episode 2 (Part 2)", "Episode 3")] - public void MixedNaming_Group(string one, string two, string three) { var mediaItems = new List @@ -58,7 +57,10 @@ public class MultiPartEpisodeGrouperTests [Test] [TestCase("The Meddlers (Part One)", "The Meddlers (Part Two)", "The Meddlers (Part Three)")] [TestCase("The Slaves of Jedikiah, Part 1", "The Slaves of Jedikiah, Part 2", "The Slaves of Jedikiah, Part 3")] - [TestCase("An Unearthly Child: An Unearthly Child (1)", "An Unearthly Child: The Cave of Skulls (2)", "An Unearthly Child: The Forest of Fear (3)")] + [TestCase( + "An Unearthly Child: An Unearthly Child (1)", + "An Unearthly Child: The Cave of Skulls (2)", + "An Unearthly Child: The Forest of Fear (3)")] [TestCase("The Savages (1)", "The Savages (2)", "The Savages (3)")] public void All_Grouped(string one, string two, string three) { diff --git a/ErsatzTV.Core.Tests/Scheduling/ScheduleIntegrationTests.cs b/ErsatzTV.Core.Tests/Scheduling/ScheduleIntegrationTests.cs index 8be6c49da..41c28aab5 100644 --- a/ErsatzTV.Core.Tests/Scheduling/ScheduleIntegrationTests.cs +++ b/ErsatzTV.Core.Tests/Scheduling/ScheduleIntegrationTests.cs @@ -130,9 +130,19 @@ public class ScheduleIntegrationTests Option maybePlayout = await GetPlayout(context, PLAYOUT_ID); Playout playout = maybePlayout.ValueUnsafe(); - var referenceData = await GetReferenceData(context, PLAYOUT_ID, ProgramSchedulePlayoutType.Classic); + PlayoutReferenceData referenceData = await GetReferenceData( + context, + PLAYOUT_ID, + ProgramSchedulePlayoutType.Classic); - await builder.Build(playout, referenceData, PlayoutBuildResult.Empty, PlayoutBuildMode.Reset, start, finish, _cancellationToken); + await builder.Build( + playout, + referenceData, + PlayoutBuildResult.Empty, + PlayoutBuildMode.Reset, + start, + finish, + _cancellationToken); // TODO: would need to apply changes from build result await context.SaveChangesAsync(_cancellationToken); @@ -144,7 +154,10 @@ public class ScheduleIntegrationTests Option maybePlayout = await GetPlayout(context, PLAYOUT_ID); Playout playout = maybePlayout.ValueUnsafe(); - var referenceData = await GetReferenceData(context, PLAYOUT_ID, ProgramSchedulePlayoutType.Classic); + PlayoutReferenceData referenceData = await GetReferenceData( + context, + PLAYOUT_ID, + ProgramSchedulePlayoutType.Classic); await builder.Build( playout, @@ -165,7 +178,10 @@ public class ScheduleIntegrationTests Option maybePlayout = await GetPlayout(context, PLAYOUT_ID); Playout playout = maybePlayout.ValueUnsafe(); - var referenceData = await GetReferenceData(context, PLAYOUT_ID, ProgramSchedulePlayoutType.Classic); + PlayoutReferenceData referenceData = await GetReferenceData( + context, + PLAYOUT_ID, + ProgramSchedulePlayoutType.Classic); await builder.Build( playout, @@ -310,7 +326,10 @@ public class ScheduleIntegrationTests Option maybePlayout = await GetPlayout(context, playoutId); Playout playout = maybePlayout.ValueUnsafe(); - var referenceData = await GetReferenceData(context, playoutId, ProgramSchedulePlayoutType.Classic); + PlayoutReferenceData referenceData = await GetReferenceData( + context, + playoutId, + ProgramSchedulePlayoutType.Classic); await builder.Build( playout, @@ -380,7 +399,7 @@ public class ScheduleIntegrationTests int playoutId, ProgramSchedulePlayoutType playoutType) { - var channel = await dbContext.Channels + Channel channel = await dbContext.Channels .AsNoTracking() .Where(c => c.Playouts.Any(p => p.Id == playoutId)) .FirstOrDefaultAsync(); @@ -408,7 +427,7 @@ public class ScheduleIntegrationTests .ToListAsync(); } - var programSchedule = await dbContext.ProgramSchedules + ProgramSchedule programSchedule = await dbContext.ProgramSchedules .AsNoTracking() .Where(ps => ps.Playouts.Any(p => p.Id == playoutId)) .Include(ps => ps.Items) @@ -430,7 +449,7 @@ public class ScheduleIntegrationTests .ThenInclude(psi => psi.FallbackFiller) .FirstOrDefaultAsync(); - var programScheduleAlternates = await dbContext.ProgramScheduleAlternates + List programScheduleAlternates = await dbContext.ProgramScheduleAlternates .AsNoTracking() .Where(pt => pt.PlayoutId == playoutId) .Include(a => a.ProgramSchedule) @@ -460,7 +479,7 @@ public class ScheduleIntegrationTests .ThenInclude(psi => psi.FallbackFiller) .ToListAsync(); - var playoutHistory = await dbContext.PlayoutHistory + List playoutHistory = await dbContext.PlayoutHistory .AsNoTracking() .Where(h => h.PlayoutId == playoutId) .ToListAsync(); diff --git a/ErsatzTV.Core/Domain/GraphicsElement.cs b/ErsatzTV.Core/Domain/GraphicsElement.cs index c4922cb94..44c621d9d 100644 --- a/ErsatzTV.Core/Domain/GraphicsElement.cs +++ b/ErsatzTV.Core/Domain/GraphicsElement.cs @@ -7,4 +7,4 @@ public class GraphicsElement public GraphicsElementKind Kind { get; set; } public List PlayoutItems { get; set; } public List PlayoutItemGraphicsElements { get; set; } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/Domain/GraphicsElementKind.cs b/ErsatzTV.Core/Domain/GraphicsElementKind.cs index d7b9b6e40..fb8cf70d0 100644 --- a/ErsatzTV.Core/Domain/GraphicsElementKind.cs +++ b/ErsatzTV.Core/Domain/GraphicsElementKind.cs @@ -5,4 +5,4 @@ public enum GraphicsElementKind Image = 0, Text = 1, Subtitle = 2 -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/ErsatzTV.Core.csproj b/ErsatzTV.Core/ErsatzTV.Core.csproj index f76dc2283..2c2947b32 100644 --- a/ErsatzTV.Core/ErsatzTV.Core.csproj +++ b/ErsatzTV.Core/ErsatzTV.Core.csproj @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs b/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs index 77883e881..b779f7887 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs @@ -199,7 +199,7 @@ public class FFmpegComplexFilterBuilder } }); - if (scaleOrPad && _boxBlur == false) + if (scaleOrPad && !_boxBlur) { videoFilterQueue.Add("setsar=1"); } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs index b012788ba..ffd748c56 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs @@ -216,7 +216,10 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService videoPath != audioPath, // still image when paths are different videoVersion.VideoScanKind == VideoScanKind.Progressive ? ScanKind.Progressive : ScanKind.Interlaced); - var videoInputFile = new VideoInputFile(videoPath, new List { ffmpegVideoStream }, streamInputKind); + var videoInputFile = new VideoInputFile( + videoPath, + new List { ffmpegVideoStream }, + streamInputKind); Option audioInputFile = maybeAudioStream.Map(audioStream => { @@ -347,7 +350,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService None, None); - foreach (var watermark in options.Watermark) + foreach (ChannelWatermark watermark in options.Watermark) { // don't allow duplicates watermarks.TryAdd(watermark.Id, new WatermarkElementContext(options)); @@ -355,7 +358,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService } // load all playout item watermarks - foreach (var playoutItemWatermark in playoutItemWatermarks) + foreach (ChannelWatermark playoutItemWatermark in playoutItemWatermarks) { WatermarkOptions options = await _ffmpegProcessService.GetWatermarkOptions( ffprobePath, @@ -366,7 +369,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService None, None); - foreach (var watermark in options.Watermark) + foreach (ChannelWatermark watermark in options.Watermark) { // don't allow duplicates watermarks.TryAdd(watermark.Id, new WatermarkElementContext(options)); @@ -448,13 +451,13 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playbackSettings.VideoTrackTimeScale, playbackSettings.Deinterlace); - foreach (var playoutItemGraphicsElement in graphicsElements) + foreach (PlayoutItemGraphicsElement playoutItemGraphicsElement in graphicsElements) { switch (playoutItemGraphicsElement.GraphicsElement.Kind) { case GraphicsElementKind.Text: { - var maybeElement = + Option maybeElement = await TextGraphicsElement.FromFile(playoutItemGraphicsElement.GraphicsElement.Path); if (maybeElement.IsNone) { @@ -463,7 +466,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playoutItemGraphicsElement.GraphicsElement.Path); } - foreach (var element in maybeElement) + foreach (TextGraphicsElement element in maybeElement) { var variables = new Dictionary(); if (!string.IsNullOrWhiteSpace(playoutItemGraphicsElement.Variables)) @@ -479,7 +482,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService } case GraphicsElementKind.Image: { - var maybeElement = + Option maybeElement = await ImageGraphicsElement.FromFile(playoutItemGraphicsElement.GraphicsElement.Path); if (maybeElement.IsNone) { @@ -488,7 +491,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playoutItemGraphicsElement.GraphicsElement.Path); } - foreach (var element in maybeElement) + foreach (ImageGraphicsElement element in maybeElement) { graphicsElementContexts.Add(new ImageElementContext(element)); } @@ -497,7 +500,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService } case GraphicsElementKind.Subtitle: { - var maybeElement = + Option maybeElement = await SubtitlesGraphicsElement.FromFile(playoutItemGraphicsElement.GraphicsElement.Path); if (maybeElement.IsNone) { @@ -506,7 +509,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playoutItemGraphicsElement.GraphicsElement.Path); } - foreach (var element in maybeElement) + foreach (SubtitlesGraphicsElement element in maybeElement) { var variables = new Dictionary(); if (!string.IsNullOrWhiteSpace(playoutItemGraphicsElement.Variables)) @@ -540,8 +543,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService new Resolution { Width = desiredState.ScaledSize.Width, Height = desiredState.ScaledSize.Height }, channel.FFmpegProfile.Resolution, await playbackSettings.FrameRate.IfNoneAsync(24), - ChannelStartTime: channelStartTime, - ContentStartTime: start, + channelStartTime, + start, await playbackSettings.StreamSeek.IfNoneAsync(TimeSpan.Zero), finish - now); } @@ -592,7 +595,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService pipelineAction?.Invoke(pipeline); - var command = GetCommand( + Command command = GetCommand( ffmpegPath, videoInputFile, audioInputFile, diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs index 5a4b84b68..5e02b9c9d 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs @@ -114,7 +114,7 @@ public static class FFmpegPlaybackSettingsCalculator result.VideoTrackTimeScale = 90000; - foreach (MediaStream stream in videoStream.Where(s => s.AttachedPic == false)) + foreach (MediaStream stream in videoStream.Where(s => !s.AttachedPic)) { result.VideoFormat = ffmpegProfile.VideoFormat; result.VideoBitrate = ffmpegProfile.VideoBitrate; diff --git a/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs b/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs index 6ec46ca00..b2a7979c0 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs @@ -153,10 +153,11 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector candidateSubtitles = candidateSubtitles.Filter(s => s.SubtitleKind is not SubtitleKind.Embedded).ToList(); } - foreach (Subtitle subtitle in candidateSubtitles.Filter(s => s.SubtitleKind is SubtitleKind.Embedded && !s.IsImage) + foreach (Subtitle subtitle in candidateSubtitles + .Filter(s => s.SubtitleKind is SubtitleKind.Embedded && !s.IsImage) .ToList()) { - if (subtitle.IsExtracted == false) + if (!subtitle.IsExtracted) { _logger.LogDebug( "Ignoring embedded subtitle with index {Index} that has not been extracted", diff --git a/ErsatzTV.Core/Graphics/ImageGraphicsElement.cs b/ErsatzTV.Core/Graphics/ImageGraphicsElement.cs index 75bc7989b..9cb45b08f 100644 --- a/ErsatzTV.Core/Graphics/ImageGraphicsElement.cs +++ b/ErsatzTV.Core/Graphics/ImageGraphicsElement.cs @@ -15,19 +15,24 @@ public class ImageGraphicsElement public string OpacityExpression { get; set; } public WatermarkLocation Location { get; set; } + [YamlMember(Alias = "horizontal_margin_percent", ApplyNamingConventions = false)] public double? HorizontalMarginPercent { get; set; } + [YamlMember(Alias = "vertical_margin_percent", ApplyNamingConventions = false)] public double? VerticalMarginPercent { get; set; } [YamlMember(Alias = "location_x", ApplyNamingConventions = false)] public double? LocationX { get; set; } + [YamlMember(Alias = "location_y", ApplyNamingConventions = false)] public double? LocationY { get; set; } + [YamlMember(Alias = "z_index", ApplyNamingConventions = false)] public int? ZIndex { get; set; } public bool Scale { get; set; } + [YamlMember(Alias = "scale_width_percent", ApplyNamingConventions = false)] public double? ScaleWidthPercent { get; set; } @@ -54,4 +59,4 @@ public class ImageGraphicsElement return Option.None; } } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/Health/HealthCheckResult.cs b/ErsatzTV.Core/Health/HealthCheckResult.cs index 1227411a2..3b1d26581 100644 --- a/ErsatzTV.Core/Health/HealthCheckResult.cs +++ b/ErsatzTV.Core/Health/HealthCheckResult.cs @@ -1,3 +1,8 @@ namespace ErsatzTV.Core.Health; -public record HealthCheckResult(string Title, HealthCheckStatus Status, string Message, string BriefMessage, Option Link); +public record HealthCheckResult( + string Title, + HealthCheckStatus Status, + string Message, + string BriefMessage, + Option Link); diff --git a/ErsatzTV.Core/Interfaces/FFmpeg/PlayoutItemResult.cs b/ErsatzTV.Core/Interfaces/FFmpeg/PlayoutItemResult.cs index 9b3673d71..3eb1c7666 100644 --- a/ErsatzTV.Core/Interfaces/FFmpeg/PlayoutItemResult.cs +++ b/ErsatzTV.Core/Interfaces/FFmpeg/PlayoutItemResult.cs @@ -3,4 +3,4 @@ using ErsatzTV.Core.Interfaces.Streaming; namespace ErsatzTV.Core.Interfaces.FFmpeg; -public record PlayoutItemResult(Command Process, Option GraphicsEngineContext); \ No newline at end of file +public record PlayoutItemResult(Command Process, Option GraphicsEngineContext); diff --git a/ErsatzTV.Core/Interfaces/Repositories/IPlexTelevisionRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/IPlexTelevisionRepository.cs index 9b26b5be8..05e60bb2f 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/IPlexTelevisionRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/IPlexTelevisionRepository.cs @@ -14,4 +14,4 @@ public interface IPlexTelevisionRepository : IMediaServerTelevisionRepository Existing, Option Added); -public record PlexShowTitleKeyResult(string Title, string Key); \ No newline at end of file +public record PlexShowTitleKeyResult(string Title, string Key); diff --git a/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs b/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs index 05f05549f..8b9d3eba6 100644 --- a/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs +++ b/ErsatzTV.Core/Interfaces/Repositories/ITemplateDataRepository.cs @@ -4,9 +4,9 @@ namespace ErsatzTV.Core.Interfaces.Repositories; public interface ITemplateDataRepository { - public Task>> GetMediaItemTemplateData(MediaItem mediaItem); + Task>> GetMediaItemTemplateData(MediaItem mediaItem); - public Task>> GetEpgTemplateData( + Task>> GetEpgTemplateData( string channelNumber, DateTimeOffset time, int count); diff --git a/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs b/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs index 22acd898f..8d4a7c3c6 100644 --- a/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs +++ b/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutBuilder.cs @@ -5,8 +5,8 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; public interface IPlayoutBuilder { - public bool TrimStart { get; set; } - public Playlist DebugPlaylist { get; set; } + bool TrimStart { get; set; } + Playlist DebugPlaylist { get; set; } Task Build( Playout playout, diff --git a/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutTimeShifter.cs b/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutTimeShifter.cs index 30918eb63..21bc013c1 100644 --- a/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutTimeShifter.cs +++ b/ErsatzTV.Core/Interfaces/Scheduling/IPlayoutTimeShifter.cs @@ -2,5 +2,5 @@ namespace ErsatzTV.Core.Interfaces.Scheduling; public interface IPlayoutTimeShifter { - public Task TimeShift(int playoutId, DateTimeOffset now, bool force); + Task TimeShift(int playoutId, DateTimeOffset now, bool force); } diff --git a/ErsatzTV.Core/Interfaces/Search/ISearchIndex.cs b/ErsatzTV.Core/Interfaces/Search/ISearchIndex.cs index c80dad19d..7c4616b10 100644 --- a/ErsatzTV.Core/Interfaces/Search/ISearchIndex.cs +++ b/ErsatzTV.Core/Interfaces/Search/ISearchIndex.cs @@ -9,7 +9,7 @@ namespace ErsatzTV.Core.Interfaces.Search; public interface ISearchIndex : IDisposable { - public int Version { get; } + int Version { get; } Task IndexExists(); Task Initialize(ILocalFileSystem localFileSystem, IConfigElementRepository configElementRepository); Task Rebuild(ICachingSearchRepository searchRepository, IFallbackMetadataProvider fallbackMetadataProvider); diff --git a/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs b/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs index 14f61d65a..c6719fcc2 100644 --- a/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs +++ b/ErsatzTV.Core/Interfaces/Streaming/GraphicsEngineContext.cs @@ -28,7 +28,9 @@ public record TextElementDataContext(TextGraphicsElement TextElement, Dictionary public record ImageElementContext(ImageGraphicsElement ImageElement) : GraphicsElementContext; -public record SubtitleElementDataContext(SubtitlesGraphicsElement SubtitlesElement, Dictionary Variables) +public record SubtitleElementDataContext( + SubtitlesGraphicsElement SubtitlesElement, + Dictionary Variables) : GraphicsElementContext, ITemplateDataContext { public int EpgEntries => SubtitlesElement.EpgEntries; diff --git a/ErsatzTV.Core/Interfaces/Streaming/IGraphicsEngine.cs b/ErsatzTV.Core/Interfaces/Streaming/IGraphicsEngine.cs index e243acdd0..14ef07cde 100644 --- a/ErsatzTV.Core/Interfaces/Streaming/IGraphicsEngine.cs +++ b/ErsatzTV.Core/Interfaces/Streaming/IGraphicsEngine.cs @@ -5,4 +5,4 @@ namespace ErsatzTV.Core.Interfaces.Streaming; public interface IGraphicsEngine { Task Run(GraphicsEngineContext context, PipeWriter pipeWriter, CancellationToken cancellationToken); -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/PathUtils.cs b/ErsatzTV.Core/PathUtils.cs index 1d49a4893..a55e0663e 100644 --- a/ErsatzTV.Core/PathUtils.cs +++ b/ErsatzTV.Core/PathUtils.cs @@ -14,6 +14,7 @@ public static class PathUtils { builder.Append(b.ToString("x2", CultureInfo.InvariantCulture)); } + return builder.ToString(); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs index 123e85654..493cae277 100644 --- a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutBuilder.cs @@ -33,7 +33,7 @@ public class BlockPlayoutBuilder( PlayoutBuildMode mode, CancellationToken cancellationToken) { - var result = PlayoutBuildResult.Empty; + PlayoutBuildResult result = PlayoutBuildResult.Empty; logger.LogDebug( "Building block playout {PlayoutId} for channel {ChannelNumber} - {ChannelName}", @@ -72,7 +72,8 @@ public class BlockPlayoutBuilder( BlockPlayoutChangeDetection.GetPlayoutItemToBlockKeyMap(referenceData); // remove items without a block key (shouldn't happen often, just upgrades) - foreach (var item in referenceData.ExistingItems.Where(i => i.FillerKind is not FillerKind.DecoDefault && !itemBlockKeys.ContainsKey(i))) + foreach (PlayoutItem item in referenceData.ExistingItems.Where(i => + i.FillerKind is not FillerKind.DecoDefault && !itemBlockKeys.ContainsKey(i))) { result.ItemsToRemove.Add(item.Id); } @@ -327,9 +328,12 @@ public class BlockPlayoutBuilder( return $"{showTitle}s{e.Season.SeasonNumber:00}{numbersString} - {titlesString}"; } - private static PlayoutBuildResult CleanUpHistory(PlayoutReferenceData referenceData, DateTimeOffset start, PlayoutBuildResult result) + private static PlayoutBuildResult CleanUpHistory( + PlayoutReferenceData referenceData, + DateTimeOffset start, + PlayoutBuildResult result) { - var allItemsToDelete = referenceData.PlayoutHistory + IEnumerable allItemsToDelete = referenceData.PlayoutHistory .Append(result.AddedHistory) .GroupBy(h => (h.BlockId, h.Key)) .SelectMany(group => group diff --git a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutChangeDetection.cs b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutChangeDetection.cs index defebe13a..04af9cb57 100644 --- a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutChangeDetection.cs +++ b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutChangeDetection.cs @@ -46,7 +46,8 @@ internal static class BlockPlayoutChangeDetection { foreach (PlayoutItem playoutItem in playoutItems) { - if (!itemBlockKeys.TryGetValue(playoutItem, out var blockKey) || effectiveBlock.Block.Id != blockKey.b) + if (!itemBlockKeys.TryGetValue(playoutItem, out BlockKey blockKey) || + effectiveBlock.Block.Id != blockKey.b) { continue; } diff --git a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs index becc31f7c..628f728a5 100644 --- a/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs +++ b/ErsatzTV.Core/Scheduling/BlockScheduling/BlockPlayoutFillerBuilder.cs @@ -37,7 +37,7 @@ public class BlockPlayoutFillerBuilder( { // remove all playout items with type filler // except block items that are hidden from the guide (guide mode) - foreach (var item in filteredExistingItems) + foreach (PlayoutItem item in filteredExistingItems) { if (item.FillerKind is FillerKind.None or FillerKind.GuideMode) { diff --git a/ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs b/ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs index 0726e50c1..7d432190f 100644 --- a/ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs +++ b/ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs @@ -63,7 +63,7 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St private static EffectiveBlock NormalizeGuideMode(EffectiveBlock effectiveBlock) { if (effectiveBlock.Block.Items is not null && - effectiveBlock.Block.Items.All(bi => bi.IncludeInProgramGuide == false)) + effectiveBlock.Block.Items.All(bi => !bi.IncludeInProgramGuide)) { foreach (BlockItem blockItem in effectiveBlock.Block.Items) { diff --git a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs index 9e6f15959..564da602e 100644 --- a/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs +++ b/ErsatzTV.Core/Scheduling/ChronologicalMediaCollectionEnumerator.cs @@ -6,9 +6,9 @@ namespace ErsatzTV.Core.Scheduling; public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnumerator { + private readonly Lazy> _lazyMediaItemGroupSize; private readonly Lazy> _lazyMinimumDuration; private readonly List _sortedMediaItems; - private readonly Lazy> _lazyMediaItemGroupSize; public ChronologicalMediaCollectionEnumerator( IEnumerable mediaItems, @@ -36,6 +36,21 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu } } + public void ResetState(CollectionEnumeratorState state) => + // seed doesn't matter in chronological + State.Index = state.Index; + + public CollectionEnumeratorState State { get; } + + public Option Current => _sortedMediaItems.Count != 0 ? _sortedMediaItems[State.Index] : None; + public Option CurrentIncludeInProgramGuide { get; } + + public void MoveNext() => State.Index = (State.Index + 1) % _sortedMediaItems.Count; + + public Option MinimumDuration => _lazyMinimumDuration.Value; + + public int Count => _sortedMediaItems.Count; + private Dictionary CalculateMediaItemGroupSizes() { var result = new Dictionary(); @@ -54,21 +69,6 @@ public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnu return result; } - public void ResetState(CollectionEnumeratorState state) => - // seed doesn't matter in chronological - State.Index = state.Index; - - public CollectionEnumeratorState State { get; } - - public Option Current => _sortedMediaItems.Count != 0 ? _sortedMediaItems[State.Index] : None; - public Option CurrentIncludeInProgramGuide { get; } - - public void MoveNext() => State.Index = (State.Index + 1) % _sortedMediaItems.Count; - - public Option MinimumDuration => _lazyMinimumDuration.Value; - - public int Count => _sortedMediaItems.Count; - public int GroupSizeForMediaItem(MediaItem mediaItem) => _lazyMediaItemGroupSize.Value.GetValueOrDefault(mediaItem.Id, 1); } diff --git a/ErsatzTV.Core/Scheduling/FillerExpression.cs b/ErsatzTV.Core/Scheduling/FillerExpression.cs index 73a0ae274..afa3ee1ed 100644 --- a/ErsatzTV.Core/Scheduling/FillerExpression.cs +++ b/ErsatzTV.Core/Scheduling/FillerExpression.cs @@ -5,7 +5,10 @@ namespace ErsatzTV.Core.Scheduling; public static class FillerExpression { - public static List FilterChapters(string fillerExpression, List effectiveChapters, PlayoutItem playoutItem) + public static List FilterChapters( + string fillerExpression, + List effectiveChapters, + PlayoutItem playoutItem) { if (effectiveChapters.Count == 0 || string.IsNullOrWhiteSpace(fillerExpression)) { diff --git a/ErsatzTV.Core/Scheduling/PlayoutBuildResult.cs b/ErsatzTV.Core/Scheduling/PlayoutBuildResult.cs index ff1e58da9..d940e0983 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutBuildResult.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutBuildResult.cs @@ -14,5 +14,13 @@ public record PlayoutBuildResult( Option TimeShiftTo) { public static PlayoutBuildResult Empty => - new(false, Option.None, Option.None, [], [], [], [], Option.None); -} \ No newline at end of file + new( + false, + Option.None, + Option.None, + [], + [], + [], + [], + Option.None); +} diff --git a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs index 574d957be..391ca9d22 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutBuilder.cs @@ -65,7 +65,7 @@ public class PlayoutBuilder : IPlayoutBuilder PlayoutBuildMode mode, CancellationToken cancellationToken) { - var result = PlayoutBuildResult.Empty; + PlayoutBuildResult result = PlayoutBuildResult.Empty; if (playout.ProgramSchedulePlayoutType is not ProgramSchedulePlayoutType.Classic) { @@ -117,7 +117,13 @@ public class PlayoutBuilder : IPlayoutBuilder { foreach (PlayoutParameters parameters in await Validate(playout, referenceData)) { - result = await Build(playout, referenceData, result, mode, parameters with { Start = start, Finish = finish }, cancellationToken); + result = await Build( + playout, + referenceData, + result, + mode, + parameters with { Start = start, Finish = finish }, + cancellationToken); } return result; @@ -443,7 +449,7 @@ public class PlayoutBuilder : IPlayoutBuilder { // check for future items that aren't grouped inside range var futureItems = result.AddedItems.Filter(i => i.StartOffset > trimAfter).ToList(); - var futureItemCount = futureItems.Count(futureItem => + int futureItemCount = futureItems.Count(futureItem => result.AddedItems.All(i => i == futureItem || i.GuideGroup != futureItem.GuideGroup)); // it feels hacky to have to clean up a playlist like this, @@ -808,7 +814,8 @@ public class PlayoutBuilder : IPlayoutBuilder playoutBuilderState.CurrentTime); // if we ended in a different alternate schedule, fix the anchor data - if (playoutBuilderState.CurrentTime > playoutFinish && activeScheduleAtAnchor.Id != activeSchedule.Id && activeScheduleAtAnchor.Items.Count > 0) + if (playoutBuilderState.CurrentTime > playoutFinish && activeScheduleAtAnchor.Id != activeSchedule.Id && + activeScheduleAtAnchor.Items.Count > 0) { PlayoutBuilderState cleanState = playoutBuilderState with { @@ -875,7 +882,8 @@ public class PlayoutBuilder : IPlayoutBuilder private async Task>> GetCollectionMediaItems(PlayoutReferenceData referenceData) { - IEnumerable>> collectionKeys = GetAllCollectionKeys(referenceData); + IEnumerable>> collectionKeys = + GetAllCollectionKeys(referenceData); IEnumerable>>> tasks = collectionKeys.Select(async key => { @@ -886,14 +894,13 @@ public class PlayoutBuilder : IPlayoutBuilder return Map.createRange(await Task.WhenAll(tasks)); } - private static IEnumerable>> GetAllCollectionKeys(PlayoutReferenceData referenceData) - { - return referenceData.ProgramSchedule.Items + private static IEnumerable>> GetAllCollectionKeys( + PlayoutReferenceData referenceData) => + referenceData.ProgramSchedule.Items .Append(referenceData.ProgramScheduleAlternates.Bind(psa => psa.ProgramSchedule.Items)) .DistinctBy(item => item.Id) .SelectMany(CollectionKeysForItem) .DistinctBy(kvp => kvp.Key); - } private async Task> FetchMediaItemsForKeyAsync( CollectionKey collectionKey, diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs index 1685d853b..2ece67e52 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerBase.cs @@ -286,7 +286,7 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe // missing pad-to-nearest-minute value is invalid; use no filler FillerPreset invalidPadFiller = allFiller - .FirstOrDefault(f => f.FillerMode == FillerMode.Pad && f.PadToNearestMinute.HasValue == false); + .FirstOrDefault(f => f.FillerMode == FillerMode.Pad && !f.PadToNearestMinute.HasValue); if (invalidPadFiller is not null) { Logger.LogError( @@ -340,7 +340,8 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe // convert playlist filler if (allFiller.Any(f => f.CollectionType is ProgramScheduleItemCollectionType.Playlist)) { - var toRemove = allFiller.Filter(f => f.CollectionType is ProgramScheduleItemCollectionType.Playlist).ToList(); + var toRemove = allFiller.Filter(f => f.CollectionType is ProgramScheduleItemCollectionType.Playlist) + .ToList(); allFiller.RemoveAll(toRemove.Contains); foreach (FillerPreset playlistFiller in toRemove) @@ -367,7 +368,8 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe }; // if filler count is 2, we need to schedule 2 * (number of items in one full playlist iteration) - var fillerEnumerator = enumerators[CollectionKey.ForFillerPreset(playlistFiller)]; + IMediaCollectionEnumerator fillerEnumerator = + enumerators[CollectionKey.ForFillerPreset(playlistFiller)]; if (fillerEnumerator is PlaylistEnumerator playlistEnumerator) { clone.Count *= playlistEnumerator.CountForFiller; @@ -428,7 +430,10 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe foreach (FillerPreset filler in allFiller.Filter(f => f.FillerKind == FillerKind.MidRoll && f.FillerMode != FillerMode.Pad)) { - List filteredChapters = FillerExpression.FilterChapters(filler.Expression, effectiveChapters, playoutItem); + List filteredChapters = FillerExpression.FilterChapters( + filler.Expression, + effectiveChapters, + playoutItem); if (filteredChapters.Count <= 1) { result.Add(playoutItem); @@ -797,7 +802,6 @@ public abstract class PlayoutModeSchedulerBase : IPlayoutModeScheduler whe if (remainingToFill - itemDuration >= TimeSpan.Zero) { - var playoutItem = new PlayoutItem { PlayoutId = playoutBuilderState.PlayoutId, diff --git a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs index f9199198d..b49e233a1 100644 --- a/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs +++ b/ErsatzTV.Core/Scheduling/PlayoutModeSchedulerDuration.cs @@ -55,7 +55,7 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase= nextState.DurationFinish.IfNone(SystemTime.MaxValueUtc) || // don't start if the first item will already be after the hard stop - (playoutItems.Count == 0 && itemStartTime >= hardStop)) + playoutItems.Count == 0 && itemStartTime >= hardStop) { nextState = nextState with { CurrentTime = hardStop }; break; @@ -165,7 +165,8 @@ public class PlayoutModeSchedulerDuration : PlayoutModeSchedulerBase PlayoutTemplates, ProgramSchedule ProgramSchedule, List ProgramScheduleAlternates, - List PlayoutHistory); \ No newline at end of file + List PlayoutHistory); diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutContentHandler.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutContentHandler.cs index 8eb8c76d2..fbf39ee9b 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutContentHandler.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutContentHandler.cs @@ -185,9 +185,9 @@ public abstract class YamlPlayoutContentHandler(EnumeratorCache enumeratorCache) } else { - foreach (var midRollSequence in maybeMidRollSequence) + foreach (YamlPlayoutContext.MidRollSequence midRollSequence in maybeMidRollSequence) { - var filteredChapters = FillerExpression.FilterChapters( + List filteredChapters = FillerExpression.FilterChapters( midRollSequence.Expression, itemChapters, playoutItem); @@ -201,7 +201,7 @@ public abstract class YamlPlayoutContentHandler(EnumeratorCache enumeratorCache) { for (var j = 0; j < filteredChapters.Count; j++) { - var nextItem = playoutItem.ForChapter(filteredChapters[j]); + PlayoutItem nextItem = playoutItem.ForChapter(filteredChapters[j]); nextItem.Start = context.CurrentTime.UtcDateTime; nextItem.Finish = context.CurrentTime.UtcDateTime + (nextItem.OutPoint - nextItem.InPoint); diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs index aebad8c54..ae911318f 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutDurationHandler.cs @@ -29,7 +29,7 @@ public class YamlPlayoutDurationHandler(EnumeratorCache enumeratorCache) : YamlP return false; } - if (duration.StopBeforeEnd == false && duration.OfflineTail) + if (!duration.StopBeforeEnd && duration.OfflineTail) { logger.LogError("offline_tail must be false when stop_before_end is false"); return false; diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOffHandler.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOffHandler.cs index 00ed7ca3b..283a0cd94 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOffHandler.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOffHandler.cs @@ -30,7 +30,7 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl } else { - foreach (var ge in await GetGraphicsElementByPath(graphicsOff.GraphicsOff)) + foreach (GraphicsElement ge in await GetGraphicsElementByPath(graphicsOff.GraphicsOff)) { context.RemoveGraphicsElement(ge.Id); } @@ -41,7 +41,7 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl private async Task> GetGraphicsElementByPath(string path) { - if (_graphicsElementCache.TryGetValue(path, out var cachedGraphicsElement)) + if (_graphicsElementCache.TryGetValue(path, out Option cachedGraphicsElement)) { foreach (GraphicsElement graphicsElement in cachedGraphicsElement) { @@ -61,4 +61,4 @@ public class YamlPlayoutGraphicsOffHandler(IGraphicsElementRepository graphicsEl return Option.None; } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOnHandler.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOnHandler.cs index dacd40dd3..c4ef818fa 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOnHandler.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutGraphicsOnHandler.cs @@ -35,7 +35,7 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle return false; } - foreach (var ge in await GetGraphicsElementByPath(graphicsOn.GraphicsOn)) + foreach (GraphicsElement ge in await GetGraphicsElementByPath(graphicsOn.GraphicsOn)) { string variables = null; if (graphicsOn.Variables.Count > 0) @@ -51,7 +51,7 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle private async Task> GetGraphicsElementByPath(string path) { - if (_graphicsElementCache.TryGetValue(path, out var cachedGraphicsElement)) + if (_graphicsElementCache.TryGetValue(path, out Option cachedGraphicsElement)) { foreach (GraphicsElement graphicsElement in cachedGraphicsElement) { @@ -71,4 +71,4 @@ public class YamlPlayoutGraphicsOnHandler(IGraphicsElementRepository graphicsEle return Option.None; } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutMidRollHandler.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutMidRollHandler.cs index 3e1c48fa1..1839de80b 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutMidRollHandler.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutMidRollHandler.cs @@ -20,7 +20,8 @@ public class YamlPlayoutMidRollHandler : IYamlPlayoutHandler return Task.FromResult(false); } - if (midRoll.MidRoll && !string.IsNullOrWhiteSpace(midRoll.Sequence) && !string.IsNullOrWhiteSpace(midRoll.Expression)) + if (midRoll.MidRoll && !string.IsNullOrWhiteSpace(midRoll.Sequence) && + !string.IsNullOrWhiteSpace(midRoll.Expression)) { context.SetMidRollSequence(new YamlPlayoutContext.MidRollSequence(midRoll.Sequence, midRoll.Expression)); } diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutRewindHandler.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutRewindHandler.cs index b93e50080..d15ad8797 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutRewindHandler.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutRewindHandler.cs @@ -27,4 +27,4 @@ public class YamlPlayoutRewindHandler : IYamlPlayoutHandler return Task.FromResult(true); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutWatermarkHandler.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutWatermarkHandler.cs index dd04b84e8..17796bcc1 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutWatermarkHandler.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutWatermarkHandler.cs @@ -26,7 +26,7 @@ public class YamlPlayoutWatermarkHandler(IChannelRepository channelRepository) : if (watermark.Watermark && !string.IsNullOrWhiteSpace(watermark.Name)) { - foreach (var wm in await GetChannelWatermarkByName(watermark.Name)) + foreach (ChannelWatermark wm in await GetChannelWatermarkByName(watermark.Name)) { context.SetChannelWatermarkId(wm.Id); } @@ -35,7 +35,7 @@ public class YamlPlayoutWatermarkHandler(IChannelRepository channelRepository) : { if (!string.IsNullOrWhiteSpace(watermark.Name)) { - foreach (var wm in await GetChannelWatermarkByName(watermark.Name)) + foreach (ChannelWatermark wm in await GetChannelWatermarkByName(watermark.Name)) { context.RemoveChannelWatermarkId(wm.Id); } @@ -51,7 +51,7 @@ public class YamlPlayoutWatermarkHandler(IChannelRepository channelRepository) : private async Task> GetChannelWatermarkByName(string name) { - if (_watermarkCache.TryGetValue(name, out var cachedWatermark)) + if (_watermarkCache.TryGetValue(name, out Option cachedWatermark)) { foreach (ChannelWatermark channelWatermark in cachedWatermark) { diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutRewindInstruction.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutRewindInstruction.cs index 2f49d3498..104a3f00d 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutRewindInstruction.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/Models/YamlPlayoutRewindInstruction.cs @@ -3,4 +3,4 @@ namespace ErsatzTV.Core.Scheduling.YamlScheduling.Models; public class YamlPlayoutRewindInstruction : YamlPlayoutInstruction { public string Rewind { get; set; } -} \ No newline at end of file +} diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs index f04dc7923..c390b5973 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutBuilder.cs @@ -30,7 +30,7 @@ public class YamlPlayoutBuilder( PlayoutBuildMode mode, CancellationToken cancellationToken) { - var result = PlayoutBuildResult.Empty; + PlayoutBuildResult result = PlayoutBuildResult.Empty; if (!localFileSystem.FileExists(playout.TemplateFile)) { @@ -39,7 +39,7 @@ public class YamlPlayoutBuilder( } Option maybePlayoutDefinition = - await LoadYamlDefinition(playout.TemplateFile, isImport: false, cancellationToken); + await LoadYamlDefinition(playout.TemplateFile, false, cancellationToken); if (maybePlayoutDefinition.IsNone) { logger.LogWarning("YAML playout file {File} is invalid; aborting.", playout.TemplateFile); @@ -49,11 +49,11 @@ public class YamlPlayoutBuilder( // using ValueUnsafe to avoid nesting YamlPlayoutDefinition playoutDefinition = maybePlayoutDefinition.ValueUnsafe(); - foreach (var import in playoutDefinition.Import) + foreach (string import in playoutDefinition.Import) { try { - var path = import; + string path = import; if (!File.Exists(import)) { path = Path.Combine( @@ -66,16 +66,23 @@ public class YamlPlayoutBuilder( } } - var maybeImportedDefinition = await LoadYamlDefinition(path, isImport: true, cancellationToken); - foreach (var importedDefinition in maybeImportedDefinition) + Option maybeImportedDefinition = + await LoadYamlDefinition(path, true, cancellationToken); + foreach (YamlPlayoutDefinition importedDefinition in maybeImportedDefinition) { - var contentToAdd = importedDefinition.Content - .Where(c => playoutDefinition.Content.All(c2 => !string.Equals(c2.Key, c.Key, StringComparison.OrdinalIgnoreCase))); + IEnumerable contentToAdd = importedDefinition.Content + .Where(c => playoutDefinition.Content.All(c2 => !string.Equals( + c2.Key, + c.Key, + StringComparison.OrdinalIgnoreCase))); playoutDefinition.Content.AddRange(contentToAdd); - var sequencesToAdd = importedDefinition.Sequence - .Where(s => playoutDefinition.Sequence.All(s2 => !string.Equals(s2.Key, s.Key, StringComparison.OrdinalIgnoreCase))); + IEnumerable sequencesToAdd = importedDefinition.Sequence + .Where(s => playoutDefinition.Sequence.All(s2 => !string.Equals( + s2.Key, + s.Key, + StringComparison.OrdinalIgnoreCase))); playoutDefinition.Sequence.AddRange(sequencesToAdd); } @@ -236,13 +243,16 @@ public class YamlPlayoutBuilder( continue; - async Task ExecuteSequenceLocal(string sequence) => await ExecuteSequence( - handlers, - enumeratorCache, - mode, - context, - sequence, - cancellationToken); + async Task ExecuteSequenceLocal(string sequence) + { + await ExecuteSequence( + handlers, + enumeratorCache, + mode, + context, + sequence, + cancellationToken); + } } if (!instruction.ChangesIndex) @@ -303,7 +313,13 @@ public class YamlPlayoutBuilder( foreach (IYamlPlayoutHandler handler in maybeHandler) { - if (!await handler.Handle(context, instruction, mode, _ => Task.CompletedTask, logger, cancellationToken)) + if (!await handler.Handle( + context, + instruction, + mode, + _ => Task.CompletedTask, + logger, + cancellationToken)) { logger.LogInformation("YAML playout instruction handler failed"); } @@ -432,7 +448,7 @@ public class YamlPlayoutBuilder( try { string yaml = await File.ReadAllTextAsync(fileName, cancellationToken); - if (await yamlScheduleValidator.ValidateSchedule(yaml, isImport) == false) + if (!await yamlScheduleValidator.ValidateSchedule(yaml, isImport)) { return Option.None; } diff --git a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs index c697b6a17..a9749bb62 100644 --- a/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs +++ b/ErsatzTV.Core/Scheduling/YamlScheduling/YamlPlayoutContext.cs @@ -13,16 +13,17 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio NullValueHandling = NullValueHandling.Ignore }; - private readonly System.Collections.Generic.HashSet _visitedInstructions = []; - private readonly Stack _fillerKind = new(); private readonly System.Collections.Generic.HashSet _channelWatermarkIds = []; + private readonly Stack _fillerKind = new(); private readonly Dictionary _graphicsElements = []; + + private readonly System.Collections.Generic.HashSet _visitedInstructions = []; private int _guideGroup = guideGroup; private bool _guideGroupLocked; private int _instructionIndex; - private Option _preRollSequence; - private Option _postRollSequence; private Option _midRollSequence; + private Option _postRollSequence; + private Option _preRollSequence; public Playout Playout { get; } = playout; @@ -112,6 +113,7 @@ public class YamlPlayoutContext(Playout playout, YamlPlayoutDefinition definitio public void PushFillerKind(FillerKind fillerKind) => _fillerKind.Push(fillerKind); public void PopFillerKind() => _fillerKind.Pop(); + public Option GetFillerKind() => _fillerKind.TryPeek(out FillerKind fillerKind) ? fillerKind : Option.None; diff --git a/ErsatzTV.Core/Troubleshooting/TroubleshootingNotifier.cs b/ErsatzTV.Core/Troubleshooting/TroubleshootingNotifier.cs index d7999dd34..4f4121d70 100644 --- a/ErsatzTV.Core/Troubleshooting/TroubleshootingNotifier.cs +++ b/ErsatzTV.Core/Troubleshooting/TroubleshootingNotifier.cs @@ -7,18 +7,9 @@ public class TroubleshootingNotifier : ITroubleshootingNotifier { private readonly ConcurrentDictionary _failedSessions = new(); - public bool IsFailed(Guid sessionId) - { - return _failedSessions.TryGetValue(sessionId, out _); - } + public bool IsFailed(Guid sessionId) => _failedSessions.TryGetValue(sessionId, out _); - public void NotifyFailed(Guid sessionId) - { - _failedSessions[sessionId] = true; - } + public void NotifyFailed(Guid sessionId) => _failedSessions[sessionId] = true; - public void RemoveSession(Guid sessionId) - { - _failedSessions.TryRemove(sessionId, out _); - } -} \ No newline at end of file + public void RemoveSession(Guid sessionId) => _failedSessions.TryRemove(sessionId, out _); +} diff --git a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj index d648a1701..12db19c0e 100644 --- a/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj +++ b/ErsatzTV.FFmpeg.Tests/ErsatzTV.FFmpeg.Tests.csproj @@ -26,4 +26,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.FFmpeg/Capabilities/FFmpegKnownEncoder.cs b/ErsatzTV.FFmpeg/Capabilities/FFmpegKnownEncoder.cs index 9a574b3da..3ef1b9804 100644 --- a/ErsatzTV.FFmpeg/Capabilities/FFmpegKnownEncoder.cs +++ b/ErsatzTV.FFmpeg/Capabilities/FFmpegKnownEncoder.cs @@ -2,13 +2,12 @@ namespace ErsatzTV.FFmpeg.Capabilities; public record FFmpegKnownEncoder { + public static readonly FFmpegKnownEncoder H264VideoToolbox = new("h264_videotoolbox"); + public static readonly FFmpegKnownEncoder HevcVideoToolbox = new("hevc_videotoolbox"); private FFmpegKnownEncoder(string Name) => this.Name = Name; public string Name { get; } - public static readonly FFmpegKnownEncoder H264VideoToolbox = new("h264_videotoolbox"); - public static readonly FFmpegKnownEncoder HevcVideoToolbox = new("hevc_videotoolbox"); - // only list the encoders that we actually check for public static IList AllEncoders => [ diff --git a/ErsatzTV.FFmpeg/Capabilities/FourCC.cs b/ErsatzTV.FFmpeg/Capabilities/FourCC.cs index 1e4dcdfd1..f032bdbe7 100644 --- a/ErsatzTV.FFmpeg/Capabilities/FourCC.cs +++ b/ErsatzTV.FFmpeg/Capabilities/FourCC.cs @@ -2,14 +2,14 @@ namespace ErsatzTV.FFmpeg.Capabilities; public static class FourCC { + public const string H264 = "avc1"; + public const string Hevc = "hvc1"; + public const string Vp9 = "vp90"; + public static readonly List AllVideoToolbox = [ H264, Hevc, Vp9 ]; - - public const string H264 = "avc1"; - public const string Hevc = "hvc1"; - public const string Vp9 = "vp90"; -} \ No newline at end of file +} diff --git a/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs b/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs index d03a6a456..1ea672ffa 100644 --- a/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs +++ b/ErsatzTV.FFmpeg/Capabilities/HardwareCapabilitiesFactory.cs @@ -265,7 +265,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory { var result = new List(); - foreach (var fourCC in FourCC.AllVideoToolbox) + foreach (string fourCC in FourCC.AllVideoToolbox) { if (VideoToolboxUtil.IsHardwareDecoderSupported(fourCC, _logger)) { @@ -276,10 +276,7 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory return result; } - public List GetVideoToolboxEncoders() - { - return VideoToolboxUtil.GetAvailableEncoders(_logger); - } + public List GetVideoToolboxEncoders() => VideoToolboxUtil.GetAvailableEncoders(_logger); private async Task> GetFFmpegCapabilities( string ffmpegPath, diff --git a/ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilities.cs b/ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilities.cs index 7e513d1bc..fe95e769c 100644 --- a/ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilities.cs +++ b/ErsatzTV.FFmpeg/Capabilities/IHardwareCapabilities.cs @@ -4,18 +4,18 @@ namespace ErsatzTV.FFmpeg.Capabilities; public interface IHardwareCapabilities { - public FFmpegCapability CanDecode( + FFmpegCapability CanDecode( string videoFormat, Option videoProfile, Option maybePixelFormat, bool isHdr); - public FFmpegCapability CanEncode( + FFmpegCapability CanEncode( string videoFormat, Option videoProfile, Option maybePixelFormat); - public Option GetRateControlMode( + Option GetRateControlMode( string videoFormat, Option maybePixelFormat); } diff --git a/ErsatzTV.FFmpeg/Capabilities/VideoToolbox/VideoToolboxUtil.cs b/ErsatzTV.FFmpeg/Capabilities/VideoToolbox/VideoToolboxUtil.cs index f66aebb4f..308d552d0 100644 --- a/ErsatzTV.FFmpeg/Capabilities/VideoToolbox/VideoToolboxUtil.cs +++ b/ErsatzTV.FFmpeg/Capabilities/VideoToolbox/VideoToolboxUtil.cs @@ -8,7 +8,7 @@ internal static partial class VideoToolboxUtil { private const string CoreFoundation = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"; private const string VideoToolbox = "/System/Library/Frameworks/VideoToolbox.framework/VideoToolbox"; - private const string LibSystem = "/usr/lib/libSystem.dylib"; + private const string LibSystem = "/usr/lib/libSystem.dylib"; [LibraryImport(CoreFoundation)] private static partial long CFArrayGetCount(IntPtr array); @@ -89,7 +89,7 @@ internal static partial class VideoToolboxUtil } long maxSize = length * 4 + 1; - byte[] buffer = new byte[maxSize]; + var buffer = new byte[maxSize]; if (CFStringGetCString(cfString, buffer, maxSize, kCFStringEncodingUTF8)) { int terminator = Array.IndexOf(buffer, (byte)0); @@ -107,7 +107,7 @@ internal static partial class VideoToolboxUtil throw new ArgumentException("FourCC must be 4 characters long.", nameof(fourCC)); } - return ((uint)fourCC[0] << 24) | ((uint)fourCC[1] << 16) | ((uint)fourCC[2] << 8) | (uint)fourCC[3]; + return ((uint)fourCC[0] << 24) | ((uint)fourCC[1] << 16) | ((uint)fourCC[2] << 8) | fourCC[3]; } internal static List GetAvailableEncoders(ILogger logger) @@ -138,7 +138,7 @@ internal static partial class VideoToolboxUtil } var count = (int)CFArrayGetCount(encoderList); - for (int i = 0; i < count; i++) + for (var i = 0; i < count; i++) { IntPtr encoderDict = CFArrayGetValueAtIndex(encoderList, i); if (encoderDict == IntPtr.Zero) diff --git a/ErsatzTV.FFmpeg/Capabilities/VideoToolboxHardwareCapabilities.cs b/ErsatzTV.FFmpeg/Capabilities/VideoToolboxHardwareCapabilities.cs index 0b92d36e6..cc778ddd3 100644 --- a/ErsatzTV.FFmpeg/Capabilities/VideoToolboxHardwareCapabilities.cs +++ b/ErsatzTV.FFmpeg/Capabilities/VideoToolboxHardwareCapabilities.cs @@ -8,8 +8,8 @@ namespace ErsatzTV.FFmpeg.Capabilities; public class VideoToolboxHardwareCapabilities : IHardwareCapabilities { - private static readonly ConcurrentDictionary Encoders = new (); - private static readonly ConcurrentDictionary Decoders = new (); + private static readonly ConcurrentDictionary Encoders = new(); + private static readonly ConcurrentDictionary Decoders = new(); private readonly IFFmpegCapabilities _ffmpegCapabilities; private readonly ILogger _logger; @@ -20,7 +20,11 @@ public class VideoToolboxHardwareCapabilities : IHardwareCapabilities _logger = logger; } - public FFmpegCapability CanDecode(string videoFormat, Option videoProfile, Option maybePixelFormat, bool isHdr) + public FFmpegCapability CanDecode( + string videoFormat, + Option videoProfile, + Option maybePixelFormat, + bool isHdr) { if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && Decoders.IsEmpty) { @@ -50,15 +54,18 @@ public class VideoToolboxHardwareCapabilities : IHardwareCapabilities }; } - public FFmpegCapability CanEncode(string videoFormat, Option videoProfile, Option maybePixelFormat) + public FFmpegCapability CanEncode( + string videoFormat, + Option videoProfile, + Option maybePixelFormat) { if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && Encoders.IsEmpty) { - var encoderList = VideoToolboxUtil.GetAvailableEncoders(_logger); + List encoderList = VideoToolboxUtil.GetAvailableEncoders(_logger); _logger.LogDebug("VideoToolbox reports {Count} encoders", encoderList.Count); // we only really care about h264 and hevc hardware encoders - foreach (var encoder in encoderList) + foreach (string encoder in encoderList) { if (encoder.Contains("HEVC (HW)", StringComparison.OrdinalIgnoreCase)) { @@ -94,4 +101,4 @@ public class VideoToolboxHardwareCapabilities : IHardwareCapabilities public Option GetRateControlMode(string videoFormat, Option maybePixelFormat) => Option.None; -} \ No newline at end of file +} diff --git a/ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs b/ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs index 07c13e6e9..0044a27c3 100644 --- a/ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs +++ b/ErsatzTV.FFmpeg/Environment/CudaVisibleDevicesVariable.cs @@ -2,7 +2,7 @@ public class CudaVisibleDevicesVariable(string visibleDevices) : IPipelineStep { - public EnvironmentVariable[] EnvironmentVariables => [ new("CUDA_VISIBLE_DEVICES", visibleDevices) ]; + public EnvironmentVariable[] EnvironmentVariables => [new("CUDA_VISIBLE_DEVICES", visibleDevices)]; public string[] GlobalOptions => []; public string[] InputOptions(InputFile inputFile) => []; public string[] FilterOptions => []; diff --git a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj index e80248e19..a5453f334 100644 --- a/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj +++ b/ErsatzTV.FFmpeg/ErsatzTV.FFmpeg.csproj @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs b/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs index e3a60a29a..bc3f66b33 100644 --- a/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/ColorspaceFilter.cs @@ -7,8 +7,8 @@ public class ColorspaceFilter : BaseFilter private readonly FrameState _currentState; private readonly IPixelFormat _desiredPixelFormat; private readonly bool _forceInputOverrides; - private readonly VideoStream _videoStream; private readonly bool _isQsv; + private readonly VideoStream _videoStream; public ColorspaceFilter( FrameState currentState, @@ -106,7 +106,8 @@ public class ColorspaceFilter : BaseFilter string colorspace = _desiredPixelFormat.BitDepth switch { - _ when cp.IsUnknown && _isQsv => $"{hwdownload}setparams=range=tv:colorspace=bt709:color_trc=bt709:color_primaries=bt709", + _ when cp.IsUnknown && _isQsv => + $"{hwdownload}setparams=range=tv:colorspace=bt709:color_trc=bt709:color_primaries=bt709", _ when cp.IsUnknown => "setparams=range=tv:colorspace=bt709:color_trc=bt709:color_primaries=bt709", 10 when !cp.IsUnknown => $"{hwdownload}colorspace={inputOverrides}all=bt709:format=yuv420p10", diff --git a/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs b/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs index 561c5df52..78a507287 100644 --- a/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/ComplexFilter.cs @@ -6,8 +6,8 @@ namespace ErsatzTV.FFmpeg.Filter; public class ComplexFilter : IPipelineStep { private readonly Option _maybeAudioInputFile; - private readonly Option _maybeSubtitleInputFile; private readonly Option _maybeGraphicsEngineInput; + private readonly Option _maybeSubtitleInputFile; private readonly Option _maybeVideoInputFile; private readonly Option _maybeWatermarkInputFile; private readonly PipelineContext _pipelineContext; diff --git a/ErsatzTV.FFmpeg/Filter/OverlayGraphicsEngineFilter.cs b/ErsatzTV.FFmpeg/Filter/OverlayGraphicsEngineFilter.cs index 746ffbae4..7e12ee20f 100644 --- a/ErsatzTV.FFmpeg/Filter/OverlayGraphicsEngineFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/OverlayGraphicsEngineFilter.cs @@ -4,7 +4,8 @@ namespace ErsatzTV.FFmpeg.Filter; public class OverlayGraphicsEngineFilter(IPixelFormat outputPixelFormat) : BaseFilter { - public override string Filter => $"overlay=format={(outputPixelFormat.BitDepth == 10 ? '1' : '0')}:alpha=premultiplied"; + public override string Filter => + $"overlay=format={(outputPixelFormat.BitDepth == 10 ? '1' : '0')}:alpha=premultiplied"; public override FrameState NextState(FrameState currentState) => currentState with { FrameDataLocation = FrameDataLocation.Software }; diff --git a/ErsatzTV.FFmpeg/InputFile.cs b/ErsatzTV.FFmpeg/InputFile.cs index b41693d97..b97fd1b1f 100644 --- a/ErsatzTV.FFmpeg/InputFile.cs +++ b/ErsatzTV.FFmpeg/InputFile.cs @@ -61,7 +61,10 @@ public record NullAudioInputFile : AudioInputFile public void Deconstruct(out AudioState DesiredState) => DesiredState = this.DesiredState; } -public record VideoInputFile(string Path, IList VideoStreams, StreamInputKind StreamInputKind = StreamInputKind.Vod) : InputFile( +public record VideoInputFile( + string Path, + IList VideoStreams, + StreamInputKind StreamInputKind = StreamInputKind.Vod) : InputFile( Path, VideoStreams.Cast().ToList()) { diff --git a/ErsatzTV.FFmpeg/InputOption/DoNotIgnoreLoopInputOption.cs b/ErsatzTV.FFmpeg/InputOption/DoNotIgnoreLoopInputOption.cs index 4a2243b6f..4a5751071 100644 --- a/ErsatzTV.FFmpeg/InputOption/DoNotIgnoreLoopInputOption.cs +++ b/ErsatzTV.FFmpeg/InputOption/DoNotIgnoreLoopInputOption.cs @@ -15,7 +15,7 @@ public class DoNotIgnoreLoopInputOption : IInputOption public bool AppliesTo(AudioInputFile audioInputFile) => false; - public bool AppliesTo(VideoInputFile videoInputFile) => videoInputFile.VideoStreams.All(s => s.StillImage == false); + public bool AppliesTo(VideoInputFile videoInputFile) => videoInputFile.VideoStreams.All(s => !s.StillImage); public bool AppliesTo(ConcatInputFile concatInputFile) => false; diff --git a/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs b/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs index ebd04b288..ddedb438b 100644 --- a/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs +++ b/ErsatzTV.FFmpeg/OutputFormat/OutputFormatHls.cs @@ -8,9 +8,9 @@ public class OutputFormatHls : IPipelineStep private readonly FrameState _desiredState; private readonly bool _isFirstTranscode; + private readonly bool _isTroubleshooting; private readonly Option _mediaFrameRate; private readonly bool _oneSecondGop; - private readonly bool _isTroubleshooting; private readonly string _playlistPath; private readonly string _segmentTemplate; diff --git a/ErsatzTV.FFmpeg/OutputOption/TimeLimitOutputOption.cs b/ErsatzTV.FFmpeg/OutputOption/TimeLimitOutputOption.cs index 0ada27275..d8c69d062 100644 --- a/ErsatzTV.FFmpeg/OutputOption/TimeLimitOutputOption.cs +++ b/ErsatzTV.FFmpeg/OutputOption/TimeLimitOutputOption.cs @@ -8,6 +8,6 @@ public class TimeLimitOutputOption(TimeSpan finish) : IPipelineStep public string[] GlobalOptions => []; public string[] InputOptions(InputFile inputFile) => []; public string[] FilterOptions => []; - public string[] OutputOptions => ["-t", $"{((int)finish.TotalHours):00}:{finish:mm}:{finish:ss\\.fffffff}"]; + public string[] OutputOptions => ["-t", $"{(int)finish.TotalHours:00}:{finish:mm}:{finish:ss\\.fffffff}"]; public FrameState NextState(FrameState currentState) => currentState; } diff --git a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs index b3d67c69d..7ea99c9fc 100644 --- a/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs @@ -209,7 +209,8 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder currentState = SetCrop(videoInputFile, desiredState, currentState); SetStillImageLoop(videoInputFile, videoStream, ffmpegState, desiredState, pipelineSteps); - if (currentState.BitDepth == 8 && context.HasSubtitleOverlay || context.HasWatermark || context.HasGraphicsEngine) + if (currentState.BitDepth == 8 && context.HasSubtitleOverlay || context.HasWatermark || + context.HasGraphicsEngine) { Option desiredPixelFormat = Some((IPixelFormat)new PixelFormatYuv420P()); @@ -244,7 +245,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder // need to upload for any sort of overlay if (currentState.FrameDataLocation == FrameDataLocation.Software && - currentState.BitDepth == 8 && context.HasSubtitleText == false + currentState.BitDepth == 8 && !context.HasSubtitleText && (context.HasSubtitleOverlay || context.HasWatermark || context.HasGraphicsEngine)) { var hardwareUpload = new HardwareUploadCudaFilter(currentState); @@ -462,7 +463,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder foreach (VideoStream watermarkStream in watermark.VideoStreams) { - if (watermarkStream.StillImage == false) + if (!watermarkStream.StillImage) { watermark.AddOption(new DoNotIgnoreLoopInputOption()); } @@ -650,7 +651,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder FrameState currentState, List graphicsEngineOverlayFilterSteps) { - foreach (var graphicsEngine in graphicsEngineInput) + foreach (GraphicsEngineInput graphicsEngine in graphicsEngineInput) { graphicsEngine.FilterSteps.Add(new PixelFormatFilter(new PixelFormatYuva420P())); diff --git a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs index da75e486a..1a8edd561 100644 --- a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs +++ b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs @@ -19,9 +19,9 @@ public abstract class PipelineBuilderBase : IPipelineBuilder { private readonly Option _audioInputFile; private readonly Option _concatInputFile; - private readonly Option _graphicsEngineInput; private readonly IFFmpegCapabilities _ffmpegCapabilities; private readonly string _fontsFolder; + private readonly Option _graphicsEngineInput; private readonly HardwareAccelerationMode _hardwareAccelerationMode; private readonly ILogger _logger; private readonly string _reportsFolder; @@ -93,7 +93,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder new StreamSeekFilterOption(seek), new EncoderCopySubtitle(), outputFormat, - new PipeProtocol(), + new PipeProtocol() }; return new FFmpegPipeline(pipelineSteps, false); diff --git a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs index 10fc8c45f..adf7bd624 100644 --- a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs @@ -291,7 +291,8 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder IPixelFormat formatForDownload = pixelFormat; bool usesVppQsv = - videoInputFile.FilterSteps.Any(f => f is QsvFormatFilter or ScaleQsvFilter or DeinterlaceQsvFilter or TonemapQsvFilter); + videoInputFile.FilterSteps.Any(f => + f is QsvFormatFilter or ScaleQsvFilter or DeinterlaceQsvFilter or TonemapQsvFilter); // if we have no filters, check whether we need to convert pixel format // since qsv doesn't seem to like doing that at the encoder @@ -432,7 +433,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder foreach (VideoStream watermarkStream in watermark.VideoStreams) { - if (watermarkStream.StillImage == false) + if (!watermarkStream.StillImage) { watermark.AddOption(new DoNotIgnoreLoopInputOption()); } @@ -560,7 +561,7 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder FrameState desiredState, List graphicsEngineOverlayFilterSteps) { - foreach (var _ in graphicsEngineInput) + foreach (GraphicsEngineInput _ in graphicsEngineInput) { foreach (IPixelFormat desiredPixelFormat in desiredState.PixelFormat) { @@ -608,12 +609,13 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder { DecoderHardwareAccelerationMode: HardwareAccelerationMode.None, EncoderHardwareAccelerationMode: HardwareAccelerationMode.None - } && context is { HasGraphicsEngine: false, HasWatermark: false, HasSubtitleOverlay: false, ShouldDeinterlace: false }; + } && context is + { HasGraphicsEngine: false, HasWatermark: false, HasSubtitleOverlay: false, ShouldDeinterlace: false }; // auto_scale filter seems to muck up 10-bit software decode => hardware scale, so use software scale in that case useSoftwareFilter = useSoftwareFilter || - (ffmpegState is { DecoderHardwareAccelerationMode: HardwareAccelerationMode.None } && - OperatingSystem.IsWindows() && currentState.BitDepth == 10); + ffmpegState is { DecoderHardwareAccelerationMode: HardwareAccelerationMode.None } && + OperatingSystem.IsWindows() && currentState.BitDepth == 10; if (currentState.ScaledSize != desiredState.ScaledSize && useSoftwareFilter) { diff --git a/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs index 9bdaa710f..f40fb3f60 100644 --- a/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs @@ -217,7 +217,7 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase foreach (VideoStream watermarkStream in watermark.VideoStreams) { - if (watermarkStream.StillImage == false) + if (!watermarkStream.StillImage) { watermark.AddOption(new DoNotIgnoreLoopInputOption()); } @@ -315,7 +315,7 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase FrameState desiredState, List graphicsEngineOverlayFilterSteps) { - foreach (var _ in graphicsEngineInput) + foreach (GraphicsEngineInput _ in graphicsEngineInput) { foreach (IPixelFormat desiredPixelFormat in desiredState.PixelFormat) { diff --git a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs index a903ae42e..f592c6f55 100644 --- a/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs @@ -388,7 +388,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder foreach (VideoStream watermarkStream in watermark.VideoStreams) { - if (watermarkStream.StillImage == false) + if (!watermarkStream.StillImage) { watermark.AddOption(new DoNotIgnoreLoopInputOption()); } @@ -542,7 +542,7 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder FrameState desiredState, List graphicsEngineOverlayFilterSteps) { - foreach (var graphicsEngine in graphicsEngineInput) + foreach (GraphicsEngineInput graphicsEngine in graphicsEngineInput) { foreach (IPixelFormat desiredPixelFormat in desiredState.PixelFormat) { diff --git a/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj b/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj index 828d57e7c..61d403b8e 100644 --- a/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj +++ b/ErsatzTV.Infrastructure.MySql/ErsatzTV.Infrastructure.MySql.csproj @@ -7,18 +7,18 @@ - + - - + + - - - + + + diff --git a/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj b/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj index 3306b2770..b947471bb 100644 --- a/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj +++ b/ErsatzTV.Infrastructure.Sqlite/ErsatzTV.Infrastructure.Sqlite.csproj @@ -7,17 +7,16 @@ - - + + - - - - + + + + - diff --git a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj index df6b6476e..2d105aa4f 100644 --- a/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj +++ b/ErsatzTV.Infrastructure.Tests/ErsatzTV.Infrastructure.Tests.csproj @@ -28,4 +28,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.Infrastructure/Data/Configurations/GraphicsElementConfiguration.cs b/ErsatzTV.Infrastructure/Data/Configurations/GraphicsElementConfiguration.cs index 2097e3825..a58c99d1b 100644 --- a/ErsatzTV.Infrastructure/Data/Configurations/GraphicsElementConfiguration.cs +++ b/ErsatzTV.Infrastructure/Data/Configurations/GraphicsElementConfiguration.cs @@ -7,4 +7,4 @@ namespace ErsatzTV.Infrastructure.Data.Configurations; public class GraphicsElementConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) => builder.ToTable("GraphicsElement"); -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Data/Repositories/EmbyTelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/EmbyTelevisionRepository.cs index da86f7f14..d3b24183a 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/EmbyTelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/EmbyTelevisionRepository.cs @@ -431,7 +431,7 @@ public class EmbyTelevisionRepository : IEmbyTelevisionRepository .FirstOrDefaultAsync() .Map(Optional); - foreach (var show in maybeShow) + foreach (EmbyShow show in maybeShow) { return new EmbyShowTitleItemIdResult( await show.ShowMetadata.HeadOrNone().Map(sm => sm.Title).IfNoneAsync("Unknown Show"), diff --git a/ErsatzTV.Infrastructure/Data/Repositories/ImageRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/ImageRepository.cs index e9ecdab22..54b744314 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/ImageRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/ImageRepository.cs @@ -142,7 +142,13 @@ public class ImageRepository : IImageRepository [ new MediaVersion { - MediaFiles = [new MediaFile { Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId }], + MediaFiles = + [ + new MediaFile + { + Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId + } + ], Streams = [] } ], diff --git a/ErsatzTV.Infrastructure/Data/Repositories/JellyfinTelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/JellyfinTelevisionRepository.cs index 08cc90c07..39f60a47e 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/JellyfinTelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/JellyfinTelevisionRepository.cs @@ -435,7 +435,7 @@ public class JellyfinTelevisionRepository : IJellyfinTelevisionRepository .FirstOrDefaultAsync() .Map(Optional); - foreach (var show in maybeShow) + foreach (JellyfinShow show in maybeShow) { return new JellyfinShowTitleItemIdResult( await show.ShowMetadata.HeadOrNone().Map(sm => sm.Title).IfNoneAsync("Unknown Show"), diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs index 8da40c829..9759efd52 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MediaCollectionRepository.cs @@ -1223,7 +1223,9 @@ public class MediaCollectionRepository : IMediaCollectionRepository return await GetRemoteStreamItems(dbContext, ids); } - private static Task> GetRemoteStreamItems(TvContext dbContext, IEnumerable remoteStreamIds) => + private static Task> GetRemoteStreamItems( + TvContext dbContext, + IEnumerable remoteStreamIds) => dbContext.RemoteStreams .AsNoTracking() .Include(m => m.RemoteStreamMetadata) diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MetadataRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MetadataRepository.cs index af3b91b8a..9c207dcc9 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MetadataRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MetadataRepository.cs @@ -541,7 +541,7 @@ public class MetadataRepository : IMetadataRepository { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); - var maybeExisting = await dbContext.MediaVersions + Option maybeExisting = await dbContext.MediaVersions .Include(mv => mv.Chapters) .SelectOneAsync(mv => mv.Id, mv => mv.Id == version.Id); diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs index 2b541765d..407657d05 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs @@ -252,7 +252,13 @@ public class MovieRepository : IMovieRepository [ new MediaVersion { - MediaFiles = [new MediaFile { Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId }], + MediaFiles = + [ + new MediaFile + { + Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId + } + ], Streams = [] } ], diff --git a/ErsatzTV.Infrastructure/Data/Repositories/MusicVideoRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/MusicVideoRepository.cs index 99eaf8e83..7cfc9c660 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/MusicVideoRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/MusicVideoRepository.cs @@ -233,7 +233,13 @@ public class MusicVideoRepository : IMusicVideoRepository [ new MediaVersion { - MediaFiles = [new MediaFile { Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId }], + MediaFiles = + [ + new MediaFile + { + Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId + } + ], Streams = [] } ], diff --git a/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs index ce5eeda39..6fa02aae7 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/OtherVideoRepository.cs @@ -207,7 +207,13 @@ public class OtherVideoRepository : IOtherVideoRepository [ new MediaVersion { - MediaFiles = [new MediaFile { Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId }], + MediaFiles = + [ + new MediaFile + { + Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId + } + ], Streams = [] } ], diff --git a/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs index 08559d992..eea1a45b5 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/PlexTelevisionRepository.cs @@ -504,7 +504,7 @@ public class PlexTelevisionRepository : IPlexTelevisionRepository .FirstOrDefaultAsync() .Map(Optional); - foreach (var show in maybeShow) + foreach (PlexShow show in maybeShow) { return new PlexShowTitleKeyResult( await show.ShowMetadata.HeadOrNone().Map(sm => sm.Title).IfNoneAsync("Unknown Show"), diff --git a/ErsatzTV.Infrastructure/Data/Repositories/RemoteStreamRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/RemoteStreamRepository.cs index 5edc8ee10..cbbd3cdf6 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/RemoteStreamRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/RemoteStreamRepository.cs @@ -149,7 +149,13 @@ public class RemoteStreamRepository( [ new MediaVersion { - MediaFiles = [new MediaFile { Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId }], + MediaFiles = + [ + new MediaFile + { + Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId + } + ], Streams = [] } ], diff --git a/ErsatzTV.Infrastructure/Data/Repositories/SongRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/SongRepository.cs index 89761d9b1..0885a9f1c 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/SongRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/SongRepository.cs @@ -134,7 +134,13 @@ public class SongRepository : ISongRepository [ new MediaVersion { - MediaFiles = [new MediaFile { Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId }], + MediaFiles = + [ + new MediaFile + { + Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId + } + ], Streams = [] } ], diff --git a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs index 23ca2a1ff..a219238f7 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/TelevisionRepository.cs @@ -785,7 +785,13 @@ public class TelevisionRepository : ITelevisionRepository [ new MediaVersion { - MediaFiles = [new MediaFile { Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId }], + MediaFiles = + [ + new MediaFile + { + Path = path, PathHash = PathUtils.GetPathHash(path), LibraryFolderId = libraryFolderId + } + ], Streams = [] } ], diff --git a/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs b/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs index 3d8c28c95..f3946c24b 100644 --- a/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs +++ b/ErsatzTV.Infrastructure/Data/Repositories/TemplateDataRepository.cs @@ -6,6 +6,7 @@ using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Metadata; using ErsatzTV.Infrastructure.Epg; +using ErsatzTV.Infrastructure.Epg.Models; using ErsatzTV.Infrastructure.Extensions; using Microsoft.EntityFrameworkCore; @@ -33,11 +34,11 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext string targetFile = Path.Combine(FileSystemLayout.ChannelGuideCacheFolder, $"{channelNumber}.xml"); if (localFileSystem.FileExists(targetFile)) { - await using var stream = File.OpenRead(targetFile); - var xmlProgrammes = EpgReader.FindProgrammesAt(stream, time, count); + await using FileStream stream = File.OpenRead(targetFile); + List xmlProgrammes = EpgReader.FindProgrammesAt(stream, time, count); var result = new List(); - foreach (var epgProgramme in xmlProgrammes) + foreach (EpgProgramme epgProgramme in xmlProgrammes) { var data = new EpgProgrammeTemplateData { @@ -54,7 +55,7 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext EpgReader.XmlTvDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, - out var start)) + out DateTimeOffset start)) { data.Start = start; } @@ -64,7 +65,7 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext EpgReader.XmlTvDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, - out var stop)) + out DateTimeOffset stop)) { data.Stop = stop; } @@ -102,9 +103,9 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext .ThenInclude(mm => mm.Genres) .SelectOneAsync(m => m.Id, m => m.Id == movieId); - foreach (var movie in maybeMovie) + foreach (Movie movie in maybeMovie) { - foreach (var metadata in movie.MovieMetadata.HeadOrNone()) + foreach (MovieMetadata metadata in movie.MovieMetadata.HeadOrNone()) { return new Dictionary { @@ -112,7 +113,8 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext [MediaItemTemplateDataKey.Plot] = metadata.Plot, [MediaItemTemplateDataKey.ReleaseDate] = metadata.ReleaseDate, [MediaItemTemplateDataKey.Studios] = (metadata.Studios ?? []).Map(s => s.Name).OrderBy(identity), - [MediaItemTemplateDataKey.Directors] = (metadata.Directors ?? []).Map(d => d.Name).OrderBy(identity), + [MediaItemTemplateDataKey.Directors] = + (metadata.Directors ?? []).Map(d => d.Name).OrderBy(identity), [MediaItemTemplateDataKey.Genres] = (metadata.Genres ?? []).Map(g => g.Name).OrderBy(identity), [MediaItemTemplateDataKey.Duration] = movie.GetHeadVersion().Duration }; @@ -142,27 +144,31 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext var result = new Dictionary(); - foreach (var episode in maybeEpisode) + foreach (Episode episode in maybeEpisode) { - foreach (var showMetadata in Optional(episode.Season?.Show?.ShowMetadata.HeadOrNone()).Flatten()) + foreach (ShowMetadata showMetadata in Optional(episode.Season?.Show?.ShowMetadata.HeadOrNone()).Flatten()) { result.Add(MediaItemTemplateDataKey.ShowTitle, showMetadata.Title); result.Add(MediaItemTemplateDataKey.ShowYear, showMetadata.Year); result.Add(MediaItemTemplateDataKey.ShowContentRating, showMetadata.ContentRating); - result.Add(MediaItemTemplateDataKey.ShowGenres, + result.Add( + MediaItemTemplateDataKey.ShowGenres, (showMetadata.Genres ?? []).Map(s => s.Name).OrderBy(identity)); } - foreach (var metadata in episode.EpisodeMetadata.HeadOrNone()) + foreach (EpisodeMetadata metadata in episode.EpisodeMetadata.HeadOrNone()) { result.Add(MediaItemTemplateDataKey.Title, metadata.Title); result.Add(MediaItemTemplateDataKey.Plot, metadata.Plot); result.Add(MediaItemTemplateDataKey.ReleaseDate, metadata.ReleaseDate); - result.Add(MediaItemTemplateDataKey.Studios, + result.Add( + MediaItemTemplateDataKey.Studios, (metadata.Studios ?? []).Map(s => s.Name).OrderBy(identity)); - result.Add(MediaItemTemplateDataKey.Directors, + result.Add( + MediaItemTemplateDataKey.Directors, (metadata.Directors ?? []).Map(s => s.Name).OrderBy(identity)); - result.Add(MediaItemTemplateDataKey.Genres, + result.Add( + MediaItemTemplateDataKey.Genres, (metadata.Genres ?? []).Map(s => s.Name).OrderBy(identity)); result.Add(MediaItemTemplateDataKey.Duration, episode.GetHeadVersion().Duration); } @@ -192,9 +198,9 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext .ThenInclude(mvm => mvm.Genres) .SelectOneAsync(mv => mv.Id, mv => mv.Id == musicVideoId); - foreach (var musicVideo in maybeMusicVideo) + foreach (MusicVideo musicVideo in maybeMusicVideo) { - foreach (var metadata in musicVideo.MusicVideoMetadata.HeadOrNone()) + foreach (MusicVideoMetadata metadata in musicVideo.MusicVideoMetadata.HeadOrNone()) { string artist = string.Empty; foreach (ArtistMetadata artistMetadata in Optional(musicVideo.Artist?.ArtistMetadata).Flatten()) @@ -212,7 +218,8 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext [MediaItemTemplateDataKey.Artists] = (metadata.Artists ?? []).Map(a => a.Name).OrderBy(identity), [MediaItemTemplateDataKey.Artist] = artist, [MediaItemTemplateDataKey.Studios] = (metadata.Studios ?? []).Map(s => s.Name).OrderBy(identity), - [MediaItemTemplateDataKey.Directors] = (metadata.Directors ?? []).Map(d => d.Name).OrderBy(identity), + [MediaItemTemplateDataKey.Directors] = + (metadata.Directors ?? []).Map(d => d.Name).OrderBy(identity), [MediaItemTemplateDataKey.Genres] = (metadata.Genres ?? []).Map(g => g.Name).OrderBy(identity), [MediaItemTemplateDataKey.Duration] = musicVideo.GetHeadVersion().Duration }; @@ -221,4 +228,4 @@ public class TemplateDataRepository(ILocalFileSystem localFileSystem, IDbContext return Option>.None; } -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Emby/EmbyApiClient.cs b/ErsatzTV.Infrastructure/Emby/EmbyApiClient.cs index 3a65ee4f1..6e54771ef 100644 --- a/ErsatzTV.Infrastructure/Emby/EmbyApiClient.cs +++ b/ErsatzTV.Infrastructure/Emby/EmbyApiClient.cs @@ -198,7 +198,7 @@ public class EmbyApiClient : IEmbyApiClient IEmbyApi service = RestService.For(address); EmbyLibraryItemsResponse itemsResponse = await service.GetShowLibraryItems( apiKey, - parentId: library.ItemId, + library.ItemId, recursive: false, startIndex: 0, limit: 1, @@ -218,6 +218,55 @@ public class EmbyApiClient : IEmbyApiClient } } + public async Task>> SearchShowsByTitle( + string address, + string apiKey, + EmbyLibrary library, + string showTitle) + { + try + { + IEmbyApi service = RestService.For(address); + EmbySearchHintsResponse searchResponse = await service.SearchHints( + apiKey, + showTitle, + "Series", + library.ItemId); + + var shows = new List(); + + foreach (EmbySearchHintResponse hint in searchResponse.SearchHints) + { + if (hint.Type == "Series" && + string.Equals(hint.Name, showTitle, StringComparison.OrdinalIgnoreCase)) + { + EmbyLibraryItemsResponse detailResponse = await service.GetShowLibraryItems( + apiKey, + hint.Id, + recursive: false, + startIndex: 0, + limit: 1); + + foreach (EmbyLibraryItemResponse item in detailResponse.Items) + { + Option maybeShow = ProjectToShow(item); + foreach (EmbyShow show in maybeShow) + { + shows.Add(show); + } + } + } + } + + return shows; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error searching Emby shows by title"); + return BaseError.New(ex.Message); + } + } + private static async IAsyncEnumerable> GetPagedLibraryContents( string address, Option maybeLibrary, @@ -924,53 +973,4 @@ public class EmbyApiClient : IEmbyApiClient return version; }); } - - public async Task>> SearchShowsByTitle( - string address, - string apiKey, - EmbyLibrary library, - string showTitle) - { - try - { - IEmbyApi service = RestService.For(address); - EmbySearchHintsResponse searchResponse = await service.SearchHints( - apiKey, - showTitle, - "Series", - library.ItemId); - - var shows = new List(); - - foreach (EmbySearchHintResponse hint in searchResponse.SearchHints) - { - if (hint.Type == "Series" && - string.Equals(hint.Name, showTitle, StringComparison.OrdinalIgnoreCase)) - { - EmbyLibraryItemsResponse detailResponse = await service.GetShowLibraryItems( - apiKey, - hint.Id, - recursive: false, - startIndex: 0, - limit: 1); - - foreach (EmbyLibraryItemResponse item in detailResponse.Items) - { - Option maybeShow = ProjectToShow(item); - foreach (EmbyShow show in maybeShow) - { - shows.Add(show); - } - } - } - } - - return shows; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error searching Emby shows by title"); - return BaseError.New(ex.Message); - } - } } diff --git a/ErsatzTV.Infrastructure/Emby/IEmbyApi.cs b/ErsatzTV.Infrastructure/Emby/IEmbyApi.cs index a452791a5..34b129866 100644 --- a/ErsatzTV.Infrastructure/Emby/IEmbyApi.cs +++ b/ErsatzTV.Infrastructure/Emby/IEmbyApi.cs @@ -7,18 +7,18 @@ namespace ErsatzTV.Infrastructure.Emby; public interface IEmbyApi { [Get("/System/Info")] - public Task GetSystemInformation( + Task GetSystemInformation( [Header("X-Emby-Token")] string apiKey, CancellationToken cancellationToken); [Get("/Library/VirtualFolders")] - public Task> GetLibraries( + Task> GetLibraries( [Header("X-Emby-Token")] string apiKey); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetMovieLibraryItems( + Task GetMovieLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -36,7 +36,7 @@ public interface IEmbyApi int limit = 0); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetShowLibraryItems( + Task GetShowLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -56,7 +56,7 @@ public interface IEmbyApi string ids = null); [Get("/Shows/{parentId}/Seasons?sortOrder=Ascending&sortBy=SortName")] - public Task GetSeasonLibraryItems( + Task GetSeasonLibraryItems( [Header("X-Emby-Token")] string apiKey, string parentId, @@ -68,7 +68,7 @@ public interface IEmbyApi int limit = 0); [Get("/Shows/{showId}/Episodes?sortOrder=Ascending&sortBy=SortName")] - public Task GetEpisodeLibraryItems( + Task GetEpisodeLibraryItems( [Header("X-Emby-Token")] string apiKey, string showId, @@ -83,7 +83,7 @@ public interface IEmbyApi int limit = 0); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetCollectionLibraryItems( + Task GetCollectionLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -100,7 +100,7 @@ public interface IEmbyApi int limit = 0); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetCollectionItems( + Task GetCollectionItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -120,13 +120,13 @@ public interface IEmbyApi [Get("/Items/{itemId}/PlaybackInfo")] - public Task GetPlaybackInfo( + Task GetPlaybackInfo( [Header("X-Emby-Token")] string apiKey, string itemId); [Get("/Search/Hints")] - public Task SearchHints( + Task SearchHints( [Header("X-Emby-Token")] string apiKey, [Query] diff --git a/ErsatzTV.Infrastructure/Emby/Models/EmbySearchHintsResponse.cs b/ErsatzTV.Infrastructure/Emby/Models/EmbySearchHintsResponse.cs index aecc38fdb..dbfc3a9dc 100644 --- a/ErsatzTV.Infrastructure/Emby/Models/EmbySearchHintsResponse.cs +++ b/ErsatzTV.Infrastructure/Emby/Models/EmbySearchHintsResponse.cs @@ -17,4 +17,4 @@ public class EmbySearchHintResponse public string Overview { get; set; } public EmbyImageTagsResponse ImageTags { get; set; } = new(); public List BackdropImageTags { get; set; } = []; -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Epg/EpgReader.cs b/ErsatzTV.Infrastructure/Epg/EpgReader.cs index 9bd930188..05d6759e6 100644 --- a/ErsatzTV.Infrastructure/Epg/EpgReader.cs +++ b/ErsatzTV.Infrastructure/Epg/EpgReader.cs @@ -32,8 +32,9 @@ public static class EpgReader if (foundCurrent) { - using var subtreeReader = reader.ReadSubtree(); - var maybeSubtreeProgramme = Optional(serializer.Deserialize(subtreeReader) as EpgProgramme); + using XmlReader subtreeReader = reader.ReadSubtree(); + Option maybeSubtreeProgramme = + Optional(serializer.Deserialize(subtreeReader) as EpgProgramme); result.AddRange(maybeSubtreeProgramme); if (maybeSubtreeProgramme.IsNone) { @@ -57,18 +58,19 @@ public static class EpgReader XmlTvDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, - out var start) && + out DateTimeOffset start) && DateTimeOffset.TryParseExact( stopStr, XmlTvDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, - out var stop)) + out DateTimeOffset stop)) { if (start <= targetTime && targetTime < stop) { - using var subtreeReader = reader.ReadSubtree(); - var maybeCurrentProgramme = Optional(serializer.Deserialize(subtreeReader) as EpgProgramme); + using XmlReader subtreeReader = reader.ReadSubtree(); + Option maybeCurrentProgramme = + Optional(serializer.Deserialize(subtreeReader) as EpgProgramme); result.AddRange(maybeCurrentProgramme); if (maybeCurrentProgramme.IsNone) { diff --git a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj index af5b0d1e2..342d0451c 100644 --- a/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj +++ b/ErsatzTV.Infrastructure/ErsatzTV.Infrastructure.csproj @@ -14,7 +14,7 @@ - + @@ -51,4 +51,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.Infrastructure/GitHub/IGitHubApi.cs b/ErsatzTV.Infrastructure/GitHub/IGitHubApi.cs index 07425d7e0..9e7cf9cb9 100644 --- a/ErsatzTV.Infrastructure/GitHub/IGitHubApi.cs +++ b/ErsatzTV.Infrastructure/GitHub/IGitHubApi.cs @@ -7,8 +7,8 @@ namespace ErsatzTV.Infrastructure.GitHub; public interface IGitHubApi { [Get("/repos/ErsatzTV/ErsatzTV/releases")] - public Task> GetReleases(CancellationToken cancellationToken); + Task> GetReleases(CancellationToken cancellationToken); [Get("/repos/ErsatzTV/ErsatzTV/releases/tags/{tag}")] - public Task GetTag(string tag, CancellationToken cancellationToken); + Task GetTag(string tag, CancellationToken cancellationToken); } diff --git a/ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs b/ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs index 544f4fdc6..c9ec582d3 100644 --- a/ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs +++ b/ErsatzTV.Infrastructure/Health/Checks/BaseHealthCheck.cs @@ -16,7 +16,7 @@ public abstract class BaseHealthCheck new(Title, HealthCheckStatus.NotApplicable, string.Empty, string.Empty, None); protected HealthCheckResult OkResult() => - new(Title, HealthCheckStatus.Pass, string.Empty, string.Empty, None); + new(Title, HealthCheckStatus.Pass, string.Empty, string.Empty, None); protected HealthCheckResult FailResult(string message, string briefMessage) => new(Title, HealthCheckStatus.Fail, message, briefMessage, None); diff --git a/ErsatzTV.Infrastructure/Health/Checks/ErrorReportsHealthCheck.cs b/ErsatzTV.Infrastructure/Health/Checks/ErrorReportsHealthCheck.cs index 3fe74bf28..a093fdcd1 100644 --- a/ErsatzTV.Infrastructure/Health/Checks/ErrorReportsHealthCheck.cs +++ b/ErsatzTV.Infrastructure/Health/Checks/ErrorReportsHealthCheck.cs @@ -25,7 +25,9 @@ public class ErrorReportsHealthCheck : BaseHealthCheck, IErrorReportsHealthCheck .AsTask(); } - return InfoResult("Automated error reporting is disabled. Please enable to support bug fixing efforts!", "Automated error reporting is disabled") + return InfoResult( + "Automated error reporting is disabled. Please enable to support bug fixing efforts!", + "Automated error reporting is disabled") .AsTask(); } } diff --git a/ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs b/ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs index b8d38af46..117eb49ee 100644 --- a/ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs +++ b/ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs @@ -61,7 +61,10 @@ public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthChe Option maybeVersion = await GetVersion(ffprobePath.Value, cancellationToken); if (maybeVersion.IsNone) { - return WarningResult("Unable to determine ffprobe version", "Unable to determine ffprobe version", link); + return WarningResult( + "Unable to determine ffprobe version", + "Unable to determine ffprobe version", + link); } foreach (string version in maybeVersion) @@ -83,7 +86,10 @@ public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthChe version.StartsWith("5.", StringComparison.OrdinalIgnoreCase) || version.StartsWith("6.", StringComparison.OrdinalIgnoreCase)) { - return FailResult($"{app} version {version} is too old; please install 7.1.1!", $"{app} version is too old", link); + return FailResult( + $"{app} version {version} is too old; please install 7.1.1!", + $"{app} version is too old", + link); } if (!version.StartsWith("7.1.1", StringComparison.OrdinalIgnoreCase) && @@ -92,7 +98,9 @@ public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthChe version != BundledVersionVaapi) { return WarningResult( - $"{app} version {version} is unexpected and may have problems; please install 7.1.1!", $"{app} version is unexpected", link); + $"{app} version {version} is unexpected and may have problems; please install 7.1.1!", + $"{app} version is unexpected", + link); } return None; diff --git a/ErsatzTV.Infrastructure/Health/Checks/UnifiedDockerHealthCheck.cs b/ErsatzTV.Infrastructure/Health/Checks/UnifiedDockerHealthCheck.cs index bf8cb7c7d..935fa8faa 100644 --- a/ErsatzTV.Infrastructure/Health/Checks/UnifiedDockerHealthCheck.cs +++ b/ErsatzTV.Infrastructure/Health/Checks/UnifiedDockerHealthCheck.cs @@ -14,7 +14,8 @@ public class UnifiedDockerHealthCheck : BaseHealthCheck, IUnifiedDockerHealthChe public Task Check(CancellationToken cancellationToken) { - if (InfoVersion.Contains("docker-vaapi") || InfoVersion.Contains("docker-nvidia") || InfoVersion.Contains("docker-arm")) + if (InfoVersion.Contains("docker-vaapi") || InfoVersion.Contains("docker-nvidia") || + InfoVersion.Contains("docker-arm")) { return WarningResult( "VAAPI, NVIDIA, and ARM docker tag suffixes are deprecated; please remove `-vaapi`, `-nvidia`, `-arm64` or `-arm` and pull the default image.", diff --git a/ErsatzTV.Infrastructure/Images/ImageCache.cs b/ErsatzTV.Infrastructure/Images/ImageCache.cs index 0d6a8a698..db5bee074 100644 --- a/ErsatzTV.Infrastructure/Images/ImageCache.cs +++ b/ErsatzTV.Infrastructure/Images/ImageCache.cs @@ -193,4 +193,4 @@ public class ImageCache : IImageCache return await md5.ComputeHashAsync(fs); } } -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Jellyfin/IJellyfinApi.cs b/ErsatzTV.Infrastructure/Jellyfin/IJellyfinApi.cs index 0623d6ceb..f82c3bb2f 100644 --- a/ErsatzTV.Infrastructure/Jellyfin/IJellyfinApi.cs +++ b/ErsatzTV.Infrastructure/Jellyfin/IJellyfinApi.cs @@ -7,23 +7,23 @@ namespace ErsatzTV.Infrastructure.Jellyfin; public interface IJellyfinApi { [Get("/System/Info")] - public Task GetSystemInformation( + Task GetSystemInformation( [Header("X-Emby-Token")] string apiKey, CancellationToken cancellationToken); [Get("/Users")] - public Task> GetUsers( + Task> GetUsers( [Header("X-Emby-Token")] string apiKey); [Get("/Library/VirtualFolders")] - public Task> GetLibraries( + Task> GetLibraries( [Header("X-Emby-Token")] string apiKey); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetMovieLibraryItems( + Task GetMovieLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -43,7 +43,7 @@ public interface IJellyfinApi int limit = 0); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetShowLibraryItems( + Task GetShowLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -63,7 +63,7 @@ public interface IJellyfinApi string ids = null); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetSeasonLibraryItems( + Task GetSeasonLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -80,7 +80,7 @@ public interface IJellyfinApi int limit = 0); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetEpisodeLibraryItems( + Task GetEpisodeLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -97,7 +97,7 @@ public interface IJellyfinApi int limit = 0); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetCollectionLibraryItems( + Task GetCollectionLibraryItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -114,7 +114,7 @@ public interface IJellyfinApi int limit = 0); [Get("/Items?sortOrder=Ascending&sortBy=SortName")] - public Task GetCollectionItems( + Task GetCollectionItems( [Header("X-Emby-Token")] string apiKey, [Query] @@ -131,13 +131,13 @@ public interface IJellyfinApi int limit = 0); [Get("/Items/{itemId}/PlaybackInfo")] - public Task GetPlaybackInfo( + Task GetPlaybackInfo( [Header("X-Emby-Token")] string apiKey, string itemId); [Get("/Search/Hints")] - public Task SearchHints( + Task SearchHints( [Header("X-Emby-Token")] string apiKey, [Query] diff --git a/ErsatzTV.Infrastructure/Jellyfin/JellyfinApiClient.cs b/ErsatzTV.Infrastructure/Jellyfin/JellyfinApiClient.cs index d740afa9f..52f8034ca 100644 --- a/ErsatzTV.Infrastructure/Jellyfin/JellyfinApiClient.cs +++ b/ErsatzTV.Infrastructure/Jellyfin/JellyfinApiClient.cs @@ -232,6 +232,55 @@ public class JellyfinApiClient : IJellyfinApiClient } } + public async Task>> SearchShowsByTitle( + string address, + string apiKey, + JellyfinLibrary library, + string showTitle) + { + try + { + IJellyfinApi service = RestService.For(address); + JellyfinSearchHintsResponse searchResponse = await service.SearchHints( + apiKey, + showTitle, + "Series", + library.ItemId); + + var shows = new List(); + + foreach (JellyfinSearchHintResponse hint in searchResponse.SearchHints) + { + if (hint.Type == "Series" && + string.Equals(hint.Name, showTitle, StringComparison.OrdinalIgnoreCase)) + { + JellyfinLibraryItemsResponse detailResponse = await service.GetShowLibraryItems( + apiKey, + hint.Id, + recursive: false, + startIndex: 0, + limit: 1); + + foreach (JellyfinLibraryItemResponse item in detailResponse.Items) + { + Option maybeShow = ProjectToShow(item); + foreach (JellyfinShow show in maybeShow) + { + shows.Add(show); + } + } + } + } + + return shows; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error searching Jellyfin shows by title"); + return BaseError.New(ex.Message); + } + } + private static async IAsyncEnumerable> GetPagedLibraryItems( string address, Option maybeLibrary, @@ -894,8 +943,8 @@ public class JellyfinApiClient : IJellyfinApiClient double resolutionRatio = width / (double)height; string[] split = videoStream.AspectRatio.Split(":"); - if (double.TryParse(split[0], NumberStyles.Any, CultureInfo.InvariantCulture, out var num) && - double.TryParse(split[1], NumberStyles.Any, CultureInfo.InvariantCulture, out var den) && + if (double.TryParse(split[0], NumberStyles.Any, CultureInfo.InvariantCulture, out double num) && + double.TryParse(split[1], NumberStyles.Any, CultureInfo.InvariantCulture, out double den) && den != 0) { double displayRatio = num / den; @@ -993,53 +1042,4 @@ public class JellyfinApiClient : IJellyfinApiClient return version; }); } - - public async Task>> SearchShowsByTitle( - string address, - string apiKey, - JellyfinLibrary library, - string showTitle) - { - try - { - IJellyfinApi service = RestService.For(address); - JellyfinSearchHintsResponse searchResponse = await service.SearchHints( - apiKey, - showTitle, - "Series", - library.ItemId); - - var shows = new List(); - - foreach (JellyfinSearchHintResponse hint in searchResponse.SearchHints) - { - if (hint.Type == "Series" && - string.Equals(hint.Name, showTitle, StringComparison.OrdinalIgnoreCase)) - { - JellyfinLibraryItemsResponse detailResponse = await service.GetShowLibraryItems( - apiKey, - hint.Id, - recursive: false, - startIndex: 0, - limit: 1); - - foreach (JellyfinLibraryItemResponse item in detailResponse.Items) - { - Option maybeShow = ProjectToShow(item); - foreach (JellyfinShow show in maybeShow) - { - shows.Add(show); - } - } - } - } - - return shows; - } - catch (Exception ex) - { - _logger.LogError(ex, "Error searching Jellyfin shows by title"); - return BaseError.New(ex.Message); - } - } } diff --git a/ErsatzTV.Infrastructure/Jellyfin/Models/JellyfinSearchHintsResponse.cs b/ErsatzTV.Infrastructure/Jellyfin/Models/JellyfinSearchHintsResponse.cs index bd1bc4150..c0ed0bed1 100644 --- a/ErsatzTV.Infrastructure/Jellyfin/Models/JellyfinSearchHintsResponse.cs +++ b/ErsatzTV.Infrastructure/Jellyfin/Models/JellyfinSearchHintsResponse.cs @@ -17,4 +17,4 @@ public class JellyfinSearchHintResponse public string Overview { get; set; } public JellyfinImageTagsResponse ImageTags { get; set; } = new(); public List BackdropImageTags { get; set; } = []; -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Metadata/LocalStatisticsProvider.cs b/ErsatzTV.Infrastructure/Metadata/LocalStatisticsProvider.cs index d36e949d1..3bd33e21b 100644 --- a/ErsatzTV.Infrastructure/Metadata/LocalStatisticsProvider.cs +++ b/ErsatzTV.Infrastructure/Metadata/LocalStatisticsProvider.cs @@ -568,7 +568,18 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider [property: JsonProperty(PropertyName = "variant_bitrate")] string variantBitrate) { - public static readonly FFprobeTags Empty = new(null, null, null, null, null, null, null, null, null, null, null); + public static readonly FFprobeTags Empty = new( + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null); } // ReSharper restore InconsistentNaming } diff --git a/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs b/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs index 9cf8d0f13..181b95e58 100644 --- a/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs +++ b/ErsatzTV.Infrastructure/Plex/IPlexServerApi.cs @@ -7,27 +7,27 @@ public interface IPlexServerApi { [Get("/")] [Headers("Accept: text/xml")] - public Task Ping( + Task Ping( [Query] [AliasAs("X-Plex-Token")] string token, CancellationToken cancellationToken); [Get("/library/sections")] [Headers("Accept: application/json")] - public Task>> GetLibraries( + Task>> GetLibraries( [Query] [AliasAs("X-Plex-Token")] string token); [Get("/library/sections/{key}/all?X-Plex-Container-Start=0&X-Plex-Container-Size=0")] [Headers("Accept: text/xml")] - public Task GetLibrarySection( + Task GetLibrarySection( string key, [Query] [AliasAs("X-Plex-Token")] string token); [Get("/library/sections/{key}/all")] [Headers("Accept: application/json")] - public Task>> + Task>> GetLibrarySectionContents( string key, [Query] [AliasAs("X-Plex-Container-Start")] @@ -39,13 +39,13 @@ public interface IPlexServerApi [Get("/library/all?type=18&X-Plex-Container-Start=0&X-Plex-Container-Size=0")] [Headers("Accept: text/xml")] - public Task GetCollectionCount( + Task GetCollectionCount( [Query] [AliasAs("X-Plex-Token")] string token); [Get("/library/all?type=18")] [Headers("Accept: application/json")] - public Task>> + Task>> GetCollections( [Query] [AliasAs("X-Plex-Container-Start")] int skip, @@ -56,14 +56,14 @@ public interface IPlexServerApi [Get("/library/collections/{key}/children?X-Plex-Container-Start=0&X-Plex-Container-Size=0")] [Headers("Accept: text/xml")] - public Task GetCollectionItemsCount( + Task GetCollectionItemsCount( string key, [Query] [AliasAs("X-Plex-Token")] string token); [Get("/library/collections/{key}/children")] [Headers("Accept: application/json")] - public Task>> + Task>> GetCollectionItems( string key, [Query] [AliasAs("X-Plex-Container-Start")] @@ -75,14 +75,14 @@ public interface IPlexServerApi [Get("/library/tags?type={type}&X-Plex-Container-Start=0&X-Plex-Container-Size=0")] [Headers("Accept: text/xml")] - public Task GetTagsCount( + Task GetTagsCount( int type, [Query] [AliasAs("X-Plex-Token")] string token); [Get("/library/tags?type={type}")] [Headers("Accept: application/json")] - public Task>> + Task>> GetTags( int type, [Query] [AliasAs("X-Plex-Container-Start")] @@ -94,7 +94,7 @@ public interface IPlexServerApi [Get("/library/sections/{key}/all?X-Plex-Container-Start=0&X-Plex-Container-Size=0")] [Headers("Accept: text/xml")] - public Task CountTagContents( + Task CountTagContents( string key, [Query] [AliasAs("X-Plex-Token")] string token, @@ -103,7 +103,7 @@ public interface IPlexServerApi [Get("/library/sections/{key}/all")] [Headers("Accept: application/json")] - public Task>> + Task>> GetTagContents( string key, [Query] [AliasAs("X-Plex-Container-Start")] @@ -117,7 +117,7 @@ public interface IPlexServerApi [Get("/library/metadata/{key}?includeChapters=1")] [Headers("Accept: text/xml")] - public Task + Task GetVideoMetadata( string key, [Query] [AliasAs("X-Plex-Token")] @@ -125,7 +125,7 @@ public interface IPlexServerApi [Get("/library/metadata/{key}")] [Headers("Accept: text/xml")] - public Task + Task GetDirectoryMetadata( string key, [Query] [AliasAs("X-Plex-Token")] @@ -133,14 +133,14 @@ public interface IPlexServerApi [Get("/library/metadata/{key}/children?X-Plex-Container-Start=0&X-Plex-Container-Size=0")] [Headers("Accept: text/xml")] - public Task CountShowChildren( + Task CountShowChildren( string key, [Query] [AliasAs("X-Plex-Token")] string token); [Get("/library/metadata/{key}/children")] [Headers("Accept: text/xml")] - public Task + Task GetShowChildren( string key, [Query] [AliasAs("X-Plex-Container-Start")] @@ -152,14 +152,14 @@ public interface IPlexServerApi [Get("/library/metadata/{key}/children?X-Plex-Container-Start=0&X-Plex-Container-Size=0")] [Headers("Accept: text/xml")] - public Task CountSeasonChildren( + Task CountSeasonChildren( string key, [Query] [AliasAs("X-Plex-Token")] string token); [Get("/library/metadata/{key}/children")] [Headers("Accept: text/xml")] - public Task + Task GetSeasonChildren( string key, [Query] [AliasAs("X-Plex-Container-Start")] @@ -171,7 +171,7 @@ public interface IPlexServerApi [Get("/hubs/search")] [Headers("Accept: application/json")] - public Task>> + Task>> Search( [Query] [AliasAs("query")] string searchTerm, diff --git a/ErsatzTV.Infrastructure/Plex/Models/PlexHubResponse.cs b/ErsatzTV.Infrastructure/Plex/Models/PlexHubResponse.cs index 1760afbee..a9b94e501 100644 --- a/ErsatzTV.Infrastructure/Plex/Models/PlexHubResponse.cs +++ b/ErsatzTV.Infrastructure/Plex/Models/PlexHubResponse.cs @@ -12,4 +12,4 @@ public class PlexHubResponse public string Title { get; set; } public string Type { get; set; } public List Metadata { get; set; } = []; -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs index 4140b33b3..8314f9159 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexServerApiClient.cs @@ -394,37 +394,6 @@ public class PlexServerApiClient : IPlexServerApiClient } } - private static async IAsyncEnumerable> GetPagedLibraryContents( - PlexConnection connection, - Func> countItems, - Func>> getItems) - { - IPlexServerApi xmlService = XmlServiceFor(connection.Uri); - - int size = await countItems(xmlService).Map(r => r.TotalSize); - if (size == 0) - { - yield break; - } - - const int PAGE_SIZE = 10; - - IPlexServerApi jsonService = RestService.For(connection.Uri); - int pages = (size - 1) / PAGE_SIZE + 1; - - for (var i = 0; i < pages; i++) - { - int skip = i * PAGE_SIZE; - - Task> result = getItems(xmlService, jsonService, skip, PAGE_SIZE); - - foreach (TItem item in await result) - { - yield return new Tuple(item, size); - } - } - } - public async Task>> GetSingleShow( PlexLibrary library, string showKey, @@ -464,9 +433,11 @@ public class PlexServerApiClient : IPlexServerApiClient foreach (PlexHubResponse hub in searchResponse.MediaContainer.Hub) { if (hub.Type != "show") + { continue; + } - string fullKey = $"/library/sections/{library.Key}"; + var fullKey = $"/library/sections/{library.Key}"; foreach (PlexMetadataResponse metadata in hub.Metadata.Where(m => m.LibrarySectionKey == fullKey)) { @@ -486,6 +457,37 @@ public class PlexServerApiClient : IPlexServerApiClient } } + private static async IAsyncEnumerable> GetPagedLibraryContents( + PlexConnection connection, + Func> countItems, + Func>> getItems) + { + IPlexServerApi xmlService = XmlServiceFor(connection.Uri); + + int size = await countItems(xmlService).Map(r => r.TotalSize); + if (size == 0) + { + yield break; + } + + const int PAGE_SIZE = 10; + + IPlexServerApi jsonService = RestService.For(connection.Uri); + int pages = (size - 1) / PAGE_SIZE + 1; + + for (var i = 0; i < pages; i++) + { + int skip = i * PAGE_SIZE; + + Task> result = getItems(xmlService, jsonService, skip, PAGE_SIZE); + + foreach (TItem item in await result) + { + yield return new Tuple(item, size); + } + } + } + private static IPlexServerApi XmlServiceFor(string uri, TimeSpan? timeout = null) { var overrides = new XmlAttributeOverrides(); @@ -555,7 +557,7 @@ public class PlexServerApiClient : IPlexServerApiClient // skip collections in libraries that are not synchronized if (plexMediaSource.Libraries.OfType().Any(l => l.Key == item.LibrarySectionId.ToString(CultureInfo.InvariantCulture) && - l.ShouldSyncItems == false)) + !l.ShouldSyncItems)) { return Option.None; } diff --git a/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs b/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs index f12bcb080..05e34bbdd 100644 --- a/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs +++ b/ErsatzTV.Infrastructure/Plex/PlexTvApiClient.cs @@ -41,7 +41,7 @@ public class PlexTvApiClient : IPlexTvApiClient clientIdentifier, token.AuthToken); - var allServers = httpResources.Filter(resource => resource.HttpsRequired == false) + var allServers = httpResources.Filter(resource => !resource.HttpsRequired) .Append(httpsResources.Filter(resource => resource.HttpsRequired)) .Filter(r => r.Provides.Split(",").Any(p => p == "server")) .ToList(); diff --git a/ErsatzTV.Infrastructure/Scheduling/PlayoutTimeShifter.cs b/ErsatzTV.Infrastructure/Scheduling/PlayoutTimeShifter.cs index 6649c010f..1961d1fe9 100644 --- a/ErsatzTV.Infrastructure/Scheduling/PlayoutTimeShifter.cs +++ b/ErsatzTV.Infrastructure/Scheduling/PlayoutTimeShifter.cs @@ -25,7 +25,7 @@ public class PlayoutTimeShifter( .SelectOneAsync(p => p.Id, p => p.Id == playoutId) .MapT(p => p.Channel); - foreach (var channel in maybeChannel.Where(c => c.PlayoutMode is ChannelPlayoutMode.OnDemand)) + foreach (Channel channel in maybeChannel.Where(c => c.PlayoutMode is ChannelPlayoutMode.OnDemand)) { Option maybePlayout = await dbContext.Playouts .Include(p => p.Channel) @@ -35,7 +35,7 @@ public class PlayoutTimeShifter( .Include(p => p.PlayoutHistory) .SelectOneAsync(p => p.ChannelId, p => p.ChannelId == channel.Id); - foreach (var playout in maybePlayout) + foreach (Playout playout in maybePlayout) { if (playout.Channel.PlayoutMode is not ChannelPlayoutMode.OnDemand) { diff --git a/ErsatzTV.Infrastructure/Scheduling/YamlScheduleValidator.cs b/ErsatzTV.Infrastructure/Scheduling/YamlScheduleValidator.cs index 59ff86d0a..29b9ca351 100644 --- a/ErsatzTV.Infrastructure/Scheduling/YamlScheduleValidator.cs +++ b/ErsatzTV.Infrastructure/Scheduling/YamlScheduleValidator.cs @@ -17,7 +17,8 @@ public class YamlScheduleValidator(ILogger logger) : IYam { try { - string schemaFileName = Path.Combine(FileSystemLayout.ResourcesCacheFolder, + string schemaFileName = Path.Combine( + FileSystemLayout.ResourcesCacheFolder, isImport ? "yaml-playout-import.schema.json" : "yaml-playout.schema.json"); using StreamReader sr = File.OpenText(schemaFileName); await using var reader = new JsonTextReader(sr); @@ -50,8 +51,8 @@ public class YamlScheduleValidator(ILogger logger) : IYam var yamlStream = new YamlStream(); yamlStream.Load(textReader); var schedule = JObject.Parse(Convert(yamlStream)); - var formatted = JsonConvert.SerializeObject(schedule, Formatting.Indented); - var lines = formatted.Split('\n'); + string formatted = JsonConvert.SerializeObject(schedule, Formatting.Indented); + string[] lines = formatted.Split('\n'); return string.Join('\n', lines.Select((line, index) => $"{index + 1,4}: {line}")); } @@ -59,7 +60,8 @@ public class YamlScheduleValidator(ILogger logger) : IYam { try { - string schemaFileName = Path.Combine(FileSystemLayout.ResourcesCacheFolder, + string schemaFileName = Path.Combine( + FileSystemLayout.ResourcesCacheFolder, isImport ? "yaml-playout-import.schema.json" : "yaml-playout.schema.json"); using StreamReader sr = File.OpenText(schemaFileName); await using var reader = new JsonTextReader(sr); @@ -87,14 +89,14 @@ public class YamlScheduleValidator(ILogger logger) : IYam private sealed class YamlToJsonVisitor : IYamlVisitor { - private readonly JsonSerializerOptions _options = new() { WriteIndented = false, }; + private readonly JsonSerializerOptions _options = new() { WriteIndented = false }; private object _currentValue; public string JsonString => JsonSerializer.Serialize(_currentValue, _options); public void Visit(YamlScalarNode scalar) { - var value = scalar.Value; + string value = scalar.Value; if (string.IsNullOrEmpty(value)) { @@ -115,13 +117,13 @@ public class YamlScheduleValidator(ILogger logger) : IYam return; } - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intResult)) + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int intResult)) { _currentValue = intResult; return; } - if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var doubleResult)) + if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out double doubleResult)) { _currentValue = doubleResult; return; @@ -133,7 +135,7 @@ public class YamlScheduleValidator(ILogger logger) : IYam public void Visit(YamlSequenceNode sequence) { var array = new List(); - foreach (var node in sequence.Children) + foreach (YamlNode node in sequence.Children) { node.Accept(this); array.Add(_currentValue); @@ -145,12 +147,12 @@ public class YamlScheduleValidator(ILogger logger) : IYam public void Visit(YamlMappingNode mapping) { Dictionary dict = new(StringComparer.OrdinalIgnoreCase); - foreach (var entry in mapping.Children) + foreach (KeyValuePair entry in mapping.Children) { - var key = entry.Key switch + string key = entry.Key switch { YamlScalarNode scalar => scalar.Value, - _ => entry.Key.ToString(), + _ => entry.Key.ToString() }; entry.Value.Accept(this); diff --git a/ErsatzTV.Infrastructure/Search/LowercaseKeywordAnalyzer.cs b/ErsatzTV.Infrastructure/Search/LowercaseKeywordAnalyzer.cs index 5a4a076da..51e577b54 100644 --- a/ErsatzTV.Infrastructure/Search/LowercaseKeywordAnalyzer.cs +++ b/ErsatzTV.Infrastructure/Search/LowercaseKeywordAnalyzer.cs @@ -12,4 +12,4 @@ public sealed class LowercaseKeywordAnalyzer(LuceneVersion matchVersion) : Analy TokenStream result = new LowerCaseFilter(matchVersion, tokenizer); return new TokenStreamComponents(tokenizer, result); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/CustomFontMapper.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/CustomFontMapper.cs index ebe7e5a30..c0f9f3ebf 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/CustomFontMapper.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/CustomFontMapper.cs @@ -10,16 +10,16 @@ public sealed class CustomFontMapper(ILogger logger) : FontMap public void LoadPrivateFont(Stream stream, string familyName) { - var typeface = SKTypeface.FromStream(stream) ?? - throw new ArgumentException("Cannot load font from stream", nameof(stream)); - var qualifiedName = familyName ?? typeface.FamilyName; + SKTypeface typeface = SKTypeface.FromStream(stream) ?? + throw new ArgumentException("Cannot load font from stream", nameof(stream)); + string qualifiedName = familyName ?? typeface.FamilyName; if (typeface.FontSlant != SKFontStyleSlant.Upright) { qualifiedName += "-Italic"; } - if (!_customFonts.TryGetValue(qualifiedName, out var listFonts)) + if (!_customFonts.TryGetValue(qualifiedName, out List listFonts)) { listFonts = []; _customFonts[qualifiedName] = listFonts; @@ -30,7 +30,7 @@ public sealed class CustomFontMapper(ILogger logger) : FontMap public override SKTypeface TypefaceFromStyle(IStyle style, bool ignoreFontVariants) { - var qualifiedName = style.FontFamily; + string qualifiedName = style.FontFamily; if (style.FontItalic) { qualifiedName += "-Italic"; @@ -45,4 +45,4 @@ public sealed class CustomFontMapper(ILogger logger) : FontMap return base.TypefaceFromStyle(style, ignoreFontVariants); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/GraphicsEngineFonts.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/GraphicsEngineFonts.cs index 3fe6daf65..099fcc6db 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/GraphicsEngineFonts.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Fonts/GraphicsEngineFonts.cs @@ -7,9 +7,11 @@ public class GraphicsEngineFonts(CustomFontMapper mapper) private static readonly System.Collections.Generic.HashSet LoadedFontFiles = new(StringComparer.OrdinalIgnoreCase); + public FontMapper Mapper => mapper; + public void LoadFonts(string fontsFolder) { - foreach (var file in Directory.EnumerateFiles(fontsFolder, "*.*", SearchOption.AllDirectories)) + foreach (string file in Directory.EnumerateFiles(fontsFolder, "*.*", SearchOption.AllDirectories)) { if (!file.EndsWith(".ttf", StringComparison.OrdinalIgnoreCase) && !file.EndsWith(".otf", StringComparison.OrdinalIgnoreCase)) @@ -22,10 +24,8 @@ public class GraphicsEngineFonts(CustomFontMapper mapper) continue; } - using var stream = File.OpenRead(file); + using FileStream stream = File.OpenRead(file); mapper.LoadPrivateFont(stream, null); } } - - public FontMapper Mapper => mapper; } diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsElement.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsElement.cs index 7de1b2027..d32ec1767 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsElement.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsElement.cs @@ -10,10 +10,17 @@ public abstract class GraphicsElement : IGraphicsElement public bool IsFailed { get; set; } - public abstract Task InitializeAsync(Resolution squarePixelFrameSize, Resolution frameSize, int frameRate, + public abstract Task InitializeAsync( + Resolution squarePixelFrameSize, + Resolution frameSize, + int frameRate, CancellationToken cancellationToken); - public abstract ValueTask> PrepareImage(TimeSpan timeOfDay, TimeSpan contentTime, TimeSpan contentTotalTime, TimeSpan channelTime, + public abstract ValueTask> PrepareImage( + TimeSpan timeOfDay, + TimeSpan contentTime, + TimeSpan contentTotalTime, + TimeSpan channelTime, CancellationToken cancellationToken); protected static SKPointI CalculatePosition( @@ -23,9 +30,8 @@ public abstract class GraphicsElement : IGraphicsElement int imageWidth, int imageHeight, int horizontalMargin, - int verticalMargin) - { - return location switch + int verticalMargin) => + location switch { WatermarkLocation.BottomLeft => new SKPointI(horizontalMargin, frameHeight - imageHeight - verticalMargin), WatermarkLocation.TopLeft => new SKPointI(horizontalMargin, verticalMargin), @@ -40,7 +46,6 @@ public abstract class GraphicsElement : IGraphicsElement WatermarkLocation.LeftMiddle => new SKPointI(horizontalMargin, (frameHeight - imageHeight) / 2), _ => new SKPointI( frameWidth - imageWidth - horizontalMargin, - frameHeight - imageHeight - verticalMargin), + frameHeight - imageHeight - verticalMargin) }; - } -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsEngine.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsEngine.cs index cfe068e32..12c450ee8 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsEngine.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/GraphicsEngine.cs @@ -31,11 +31,11 @@ public class GraphicsEngine( templateVariables[MediaItemTemplateDataKey.StreamSeek] = context.Seek; // media item variables - var maybeTemplateData = + Option> maybeTemplateData = await templateDataRepository.GetMediaItemTemplateData(context.MediaItem); - foreach (var templateData in maybeTemplateData) + foreach (Dictionary templateData in maybeTemplateData) { - foreach (var variable in templateData) + foreach (KeyValuePair variable in templateData) { templateVariables.Add(variable.Key, variable.Value); } @@ -43,12 +43,12 @@ public class GraphicsEngine( // epg variables int maxEpg = context.Elements.OfType().Max(c => c.EpgEntries); - var startTime = context.ContentStartTime + context.Seek; - var maybeEpgData = + DateTimeOffset startTime = context.ContentStartTime + context.Seek; + Option> maybeEpgData = await templateDataRepository.GetEpgTemplateData(context.ChannelNumber, startTime, maxEpg); - foreach (var templateData in maybeEpgData) + foreach (Dictionary templateData in maybeEpgData) { - foreach (var variable in templateData) + foreach (KeyValuePair variable in templateData) { templateVariables.Add(variable.Key, variable.Value); } @@ -56,7 +56,7 @@ public class GraphicsEngine( } var elements = new List(); - foreach (var element in context.Elements) + foreach (GraphicsElementContext element in context.Elements) { switch (element) { @@ -66,6 +66,7 @@ public class GraphicsEngine( { elements.Add(watermark); } + break; case ImageElementContext imageElementContext: @@ -75,7 +76,7 @@ public class GraphicsEngine( case TextElementDataContext textElementContext: { var variables = templateVariables.ToDictionary(); - foreach (var variable in textElementContext.Variables) + foreach (KeyValuePair variable in textElementContext.Variables) { variables.Add(variable.Key, variable.Value); } @@ -94,7 +95,7 @@ public class GraphicsEngine( case SubtitleElementDataContext subtitleElementContext: { var variables = templateVariables.ToDictionary(); - foreach (var variable in subtitleElementContext.Variables) + foreach (KeyValuePair variable in subtitleElementContext.Variables) { variables.Add(variable.Key, variable.Value); } @@ -113,8 +114,13 @@ public class GraphicsEngine( } // initialize all elements - await Task.WhenAll(elements.Select(e => - e.InitializeAsync(context.SquarePixelFrameSize, context.FrameSize, context.FrameRate, cancellationToken))); + await Task.WhenAll( + elements.Select(e => + e.InitializeAsync( + context.SquarePixelFrameSize, + context.FrameSize, + context.FrameRate, + cancellationToken))); long frameCount = 0; var totalFrames = (long)(context.Duration.TotalSeconds * context.FrameRate); @@ -128,7 +134,7 @@ public class GraphicsEngine( try { // `content_total_seconds` - the total number of seconds in the content - var contentTotalTime = context.Seek + context.Duration; + TimeSpan contentTotalTime = context.Seek + context.Duration; while (!cancellationToken.IsCancellationRequested && frameCount < totalFrames) { @@ -137,24 +143,24 @@ public class GraphicsEngine( var streamTime = TimeSpan.FromSeconds(streamTimeSeconds); // `content_seconds` - the total number of seconds the frame is into the content - var contentTime = context.Seek + streamTime; + TimeSpan contentTime = context.Seek + streamTime; // `time_of_day_seconds` - the total number of seconds the frame is since midnight - var frameTime = context.ContentStartTime + contentTime; + DateTimeOffset frameTime = context.ContentStartTime + contentTime; // `channel_seconds` - the total number of seconds the frame is from when the channel started/activated - var channelTime = frameTime - context.ChannelStartTime; + TimeSpan channelTime = frameTime - context.ChannelStartTime; using var canvas = new SKCanvas(outputBitmap); canvas.Clear(SKColors.Transparent); // prepare images outside mutate to allow async image generation var preparedElementImages = new List(); - foreach (var element in elements.Where(e => !e.IsFailed).OrderBy(e => e.ZIndex)) + foreach (IGraphicsElement element in elements.Where(e => !e.IsFailed).OrderBy(e => e.ZIndex)) { try { - var maybePreparedImage = await element.PrepareImage( + Option maybePreparedImage = await element.PrepareImage( frameTime.TimeOfDay, contentTime, contentTotalTime, @@ -166,7 +172,8 @@ public class GraphicsEngine( catch (Exception ex) { element.IsFailed = true; - logger.LogWarning(ex, + logger.LogWarning( + ex, "Failed to draw graphics element of type {Type}; will disable for this content", element.GetType().Name); } @@ -175,7 +182,7 @@ public class GraphicsEngine( // draw each element using (var paint = new SKPaint()) { - foreach (var preparedImage in preparedElementImages) + foreach (PreparedElementImage preparedImage in preparedElementImages) { using (var colorFilter = SKColorFilter.CreateBlendMode( SKColors.White.WithAlpha((byte)(preparedImage.Opacity * 255)), @@ -199,7 +206,7 @@ public class GraphicsEngine( int frameBufferSize = context.FrameSize.Width * context.FrameSize.Height * 4; using (SKPixmap pixmap = outputBitmap.PeekPixels()) { - var memory = pipeWriter.GetMemory(frameBufferSize); + Memory memory = pipeWriter.GetMemory(frameBufferSize); pixmap.GetPixelSpan().CopyTo(memory.Span); } @@ -217,7 +224,7 @@ public class GraphicsEngine( { await pipeWriter.CompleteAsync(); - foreach (var element in elements.OfType()) + foreach (IDisposable element in elements.OfType()) { element.Dispose(); } diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/IGraphicsElement.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/IGraphicsElement.cs index 482b4bdce..c7f619f86 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/IGraphicsElement.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/IGraphicsElement.cs @@ -20,4 +20,4 @@ public interface IGraphicsElement TimeSpan contentTotalTime, TimeSpan channelTime, CancellationToken cancellationToken); -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElement.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElement.cs index 4278b5e0d..a21024d90 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElement.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElement.cs @@ -32,7 +32,7 @@ public class ImageElement(ImageGraphicsElement imageGraphicsElement, ILogger log ZIndex = imageGraphicsElement.ZIndex ?? 0; - foreach (var expression in _maybeOpacityExpression) + foreach (Expression expression in _maybeOpacityExpression) { expression.EvaluateFunction += OpacityExpressionHelper.EvaluateFunction; } @@ -46,7 +46,7 @@ public class ImageElement(ImageGraphicsElement imageGraphicsElement, ILogger log imageGraphicsElement.ScaleWidthPercent, imageGraphicsElement.HorizontalMarginPercent, imageGraphicsElement.VerticalMarginPercent, - placeWithinSourceContent: false, + false, cancellationToken); } catch (Exception ex) @@ -64,7 +64,7 @@ public class ImageElement(ImageGraphicsElement imageGraphicsElement, ILogger log CancellationToken cancellationToken) { float opacity = _opacity; - foreach (var expression in _maybeOpacityExpression) + foreach (Expression expression in _maybeOpacityExpression) { opacity = OpacityExpressionHelper.GetOpacity( expression, @@ -82,4 +82,4 @@ public class ImageElement(ImageGraphicsElement imageGraphicsElement, ILogger log SKBitmap frameForTimestamp = GetFrameForTimestamp(contentTime); return ValueTask.FromResult(Optional(new PreparedElementImage(frameForTimestamp, Location, opacity, false))); } -} \ No newline at end of file +} diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs index e4e75fb2d..aacea1c1a 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/ImageElementBase.cs @@ -2,9 +2,11 @@ using System.Runtime.InteropServices; using ErsatzTV.Core.Domain; using ErsatzTV.FFmpeg.State; using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Webp; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SkiaSharp; @@ -14,14 +16,21 @@ namespace ErsatzTV.Infrastructure.Streaming.Graphics; public abstract class ImageElementBase : GraphicsElement, IDisposable { - private readonly List _scaledFrames = []; private readonly List _frameDelays = []; + private readonly List _scaledFrames = []; + private double _animatedDurationSeconds; private Image _sourceImage; - private double _animatedDurationSeconds; protected SKPointI Location { get; private set; } + public virtual void Dispose() + { + GC.SuppressFinalize(this); + _sourceImage?.Dispose(); + _scaledFrames?.ForEach(f => f.Dispose()); + } + protected async Task LoadImage( Resolution squarePixelFrameSize, Resolution frameSize, @@ -34,8 +43,8 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable bool placeWithinSourceContent, CancellationToken cancellationToken) { - bool isRemoteUri = Uri.TryCreate(image, UriKind.Absolute, out var uriResult) - && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); + bool isRemoteUri = Uri.TryCreate(image, UriKind.Absolute, out Uri uriResult) + && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); if (isRemoteUri) { @@ -76,13 +85,13 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable _animatedDurationSeconds = 0; - for (int i = 0; i < _sourceImage.Frames.Count; i++) + for (var i = 0; i < _sourceImage.Frames.Count; i++) { Image frame = _sourceImage.Frames.CloneFrame(i); frame.Mutate(ctx => ctx.Resize(scaledWidth, scaledHeight)); _scaledFrames.Add(ToSkiaBitmap(frame)); - var frameDelay = GetFrameDelaySeconds(_sourceImage, i); + double frameDelay = GetFrameDelaySeconds(_sourceImage, i); _animatedDurationSeconds += frameDelay; _frameDelays.Add(frameDelay); } @@ -117,27 +126,27 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable protected static double GetFrameDelaySeconds(Image image, int frameIndex) { - var format = image.Metadata.DecodedImageFormat; - var frameMeta = image.Frames[frameIndex].Metadata; + IImageFormat format = image.Metadata.DecodedImageFormat; + ImageFrameMetadata frameMeta = image.Frames[frameIndex].Metadata; if (format == GifFormat.Instance) { // GIF frame delay is in hundredths of a second - var gifMeta = frameMeta.GetFormatMetadata(GifFormat.Instance); + GifFrameMetadata gifMeta = frameMeta.GetFormatMetadata(GifFormat.Instance); return gifMeta.FrameDelay / 100.0; } if (format == PngFormat.Instance) { // PNG animated frame delay is in seconds (as double) - var pngMeta = frameMeta.GetFormatMetadata(PngFormat.Instance); + PngFrameMetadata pngMeta = frameMeta.GetFormatMetadata(PngFormat.Instance); return pngMeta.FrameDelay.ToDouble(); } if (format == WebpFormat.Instance) { // WEBP animated frame delay is in milliseconds - var webpMeta = frameMeta.GetFormatMetadata(WebpFormat.Instance); + WebpFrameMetadata webpMeta = frameMeta.GetFormatMetadata(WebpFormat.Instance); return webpMeta.FrameDelay / 1000.0; } @@ -155,7 +164,7 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable double currentTime = timestamp.TotalSeconds % _animatedDurationSeconds; double frameTime = 0; - for (int i = 0; i < _sourceImage.Frames.Count; i++) + for (var i = 0; i < _sourceImage.Frames.Count; i++) { frameTime += _frameDelays[i]; if (currentTime <= frameTime) @@ -198,11 +207,4 @@ public abstract class ImageElementBase : GraphicsElement, IDisposable } private sealed record WatermarkMargins(int HorizontalMargin, int VerticalMargin); - - public virtual void Dispose() - { - GC.SuppressFinalize(this); - _sourceImage?.Dispose(); - _scaledFrames?.ForEach(f => f.Dispose()); - } } diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/WatermarkElement.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/WatermarkElement.cs index 2df0608dc..8816e66f5 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Image/WatermarkElement.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Image/WatermarkElement.cs @@ -9,8 +9,8 @@ namespace ErsatzTV.Infrastructure.Streaming.Graphics; public class WatermarkElement : ImageElementBase { - private readonly ILogger _logger; private readonly string _imagePath; + private readonly ILogger _logger; private readonly ChannelWatermark _watermark; private Option _maybeOpacityExpression; @@ -20,12 +20,12 @@ public class WatermarkElement : ImageElementBase { _logger = logger; // TODO: better model coming in here? - foreach (var imagePath in watermarkOptions.ImagePath) + foreach (string imagePath in watermarkOptions.ImagePath) { _imagePath = imagePath; } - foreach (var watermark in watermarkOptions.Watermark) + foreach (ChannelWatermark watermark in watermarkOptions.Watermark) { _watermark = watermark; ZIndex = watermark.ZIndex; @@ -44,7 +44,7 @@ public class WatermarkElement : ImageElementBase { if (_watermark.Mode is ChannelWatermarkMode.Intermittent) { - string expressionString = $@" + var expressionString = $@" if(time_of_day_seconds % {_watermark.FrequencyMinutes * 60} < 1, (time_of_day_seconds % {_watermark.FrequencyMinutes * 60}), if(time_of_day_seconds % {_watermark.FrequencyMinutes * 60} < {1 + _watermark.DurationSeconds}, @@ -57,7 +57,8 @@ public class WatermarkElement : ImageElementBase )"; _maybeOpacityExpression = new Expression(expressionString); } - else if (_watermark.Mode is ChannelWatermarkMode.OpacityExpression && !string.IsNullOrWhiteSpace(_watermark.OpacityExpression)) + else if (_watermark.Mode is ChannelWatermarkMode.OpacityExpression && + !string.IsNullOrWhiteSpace(_watermark.OpacityExpression)) { _maybeOpacityExpression = new Expression(_watermark.OpacityExpression); } @@ -66,7 +67,7 @@ public class WatermarkElement : ImageElementBase _opacity = _watermark.Opacity / 100.0f; } - foreach (var expression in _maybeOpacityExpression) + foreach (Expression expression in _maybeOpacityExpression) { expression.EvaluateFunction += OpacityExpressionHelper.EvaluateFunction; } @@ -98,7 +99,7 @@ public class WatermarkElement : ImageElementBase CancellationToken cancellationToken) { float opacity = _opacity; - foreach (var expression in _maybeOpacityExpression) + foreach (Expression expression in _maybeOpacityExpression) { opacity = OpacityExpressionHelper.GetOpacity( expression, diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/OpacityExpressionHelper.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/OpacityExpressionHelper.cs index 6ec8d7d79..93a82ea3f 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/OpacityExpressionHelper.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/OpacityExpressionHelper.cs @@ -17,11 +17,11 @@ public static class OpacityExpressionHelper throw new ArgumentException("LinearFadePoints() requires 5 arguments."); } - double time = Convert.ToDouble(args.Parameters[0].Evaluate(), CultureInfo.CurrentCulture); - double start = Convert.ToDouble(args.Parameters[1].Evaluate(), CultureInfo.CurrentCulture); - double peakStart = Convert.ToDouble(args.Parameters[2].Evaluate(), CultureInfo.CurrentCulture); - double peakEnd = Convert.ToDouble(args.Parameters[3].Evaluate(), CultureInfo.CurrentCulture); - double end = Convert.ToDouble(args.Parameters[4].Evaluate(), CultureInfo.CurrentCulture); + var time = Convert.ToDouble(args.Parameters[0].Evaluate(), CultureInfo.CurrentCulture); + var start = Convert.ToDouble(args.Parameters[1].Evaluate(), CultureInfo.CurrentCulture); + var peakStart = Convert.ToDouble(args.Parameters[2].Evaluate(), CultureInfo.CurrentCulture); + var peakEnd = Convert.ToDouble(args.Parameters[3].Evaluate(), CultureInfo.CurrentCulture); + var end = Convert.ToDouble(args.Parameters[4].Evaluate(), CultureInfo.CurrentCulture); args.Result = LinearFadePoints(time, start, peakStart, peakEnd, end); break; @@ -33,10 +33,10 @@ public static class OpacityExpressionHelper throw new ArgumentException("LinearFadeDuration() requires 4 arguments."); } - double time = Convert.ToDouble(args.Parameters[0].Evaluate(), CultureInfo.CurrentCulture); - double start = Convert.ToDouble(args.Parameters[1].Evaluate(), CultureInfo.CurrentCulture); - double fadeSeconds = Convert.ToDouble(args.Parameters[2].Evaluate(), CultureInfo.CurrentCulture); - double peakSeconds = Convert.ToDouble(args.Parameters[3].Evaluate(), CultureInfo.CurrentCulture); + var time = Convert.ToDouble(args.Parameters[0].Evaluate(), CultureInfo.CurrentCulture); + var start = Convert.ToDouble(args.Parameters[1].Evaluate(), CultureInfo.CurrentCulture); + var fadeSeconds = Convert.ToDouble(args.Parameters[2].Evaluate(), CultureInfo.CurrentCulture); + var peakSeconds = Convert.ToDouble(args.Parameters[3].Evaluate(), CultureInfo.CurrentCulture); args.Result = LinearFadeDuration(time, start, fadeSeconds, peakSeconds); break; @@ -73,7 +73,7 @@ public static class OpacityExpressionHelper if (fadeSeconds <= 0) { double noFadeEnd = start + peakSeconds; - return (time >= start && time < noFadeEnd) ? 1.0 : 0.0; + return time >= start && time < noFadeEnd ? 1.0 : 0.0; } double peakStart = start + fadeSeconds; diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/PreparedElementImage.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/PreparedElementImage.cs index 1104d982b..01eb64f33 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/PreparedElementImage.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/PreparedElementImage.cs @@ -2,4 +2,4 @@ using SkiaSharp; namespace ErsatzTV.Infrastructure.Streaming.Graphics; -public record PreparedElementImage(SKBitmap Image, SKPointI Point, float Opacity, bool Dispose); \ No newline at end of file +public record PreparedElementImage(SKBitmap Image, SKPointI Point, float Opacity, bool Dispose); diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Subtitle/SubtitleElement.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Subtitle/SubtitleElement.cs index da750e702..244b46cd3 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Subtitle/SubtitleElement.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Subtitle/SubtitleElement.cs @@ -21,12 +21,35 @@ public class SubtitleElement( ILogger logger) : GraphicsElement, IDisposable { - private CommandTask _commandTask; private CancellationTokenSource _cancellationTokenSource; - private PipeReader _pipeReader; - private SKBitmap _videoFrame; + private CommandTask _commandTask; private int _frameSize; + private PipeReader _pipeReader; private SKPointI _point; + private SKBitmap _videoFrame; + + public void Dispose() + { + GC.SuppressFinalize(this); + + _pipeReader?.Complete(); + + _cancellationTokenSource?.Cancel(); + try + { +#pragma warning disable VSTHRD002 + _commandTask?.Task.Wait(); +#pragma warning restore VSTHRD002 + } + catch (Exception) + { + // do nothing + } + + _cancellationTokenSource?.Dispose(); + + _videoFrame?.Dispose(); + } public override async Task InitializeAsync( Resolution squarePixelFrameSize, @@ -46,7 +69,7 @@ public class SubtitleElement( // subtitles contain their own positioning info _point = SKPointI.Empty; - var subtitleTemplateFile = tempFilePool.GetNextTempFile(TempFileCategory.Subtitle); + string subtitleTemplateFile = tempFilePool.GetNextTempFile(TempFileCategory.Subtitle); var scriptObject = new ScriptObject(); scriptObject.Import(variables, renamer: member => member.Name); @@ -55,11 +78,11 @@ public class SubtitleElement( var context = new TemplateContext { MemberRenamer = member => member.Name }; context.PushGlobal(scriptObject); - var inputText = await File.ReadAllTextAsync(subtitlesElement.Template, cancellationToken); + string inputText = await File.ReadAllTextAsync(subtitlesElement.Template, cancellationToken); string textToRender = await Template.Parse(inputText).RenderAsync(context); await File.WriteAllTextAsync(subtitleTemplateFile, textToRender, cancellationToken); - var subtitleFile = Path.GetFileName(subtitleTemplateFile); + string subtitleFile = Path.GetFileName(subtitleTemplateFile); List arguments = [ "-f", "lavfi", @@ -77,7 +100,9 @@ public class SubtitleElement( .WithStandardOutputPipe(PipeTarget.ToStream(pipe.Writer.AsStream())); _cancellationTokenSource = new CancellationTokenSource(); - var linkedToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token); + var linkedToken = CancellationTokenSource.CreateLinkedTokenSource( + cancellationToken, + _cancellationTokenSource.Token); _commandTask = command.ExecuteAsync(linkedToken.Token); } @@ -98,15 +123,15 @@ public class SubtitleElement( while (true) { ReadResult readResult = await _pipeReader.ReadAsync(cancellationToken); - var buffer = readResult.Buffer; - var consumed = buffer.Start; - var examined = buffer.End; + ReadOnlySequence buffer = readResult.Buffer; + SequencePosition consumed = buffer.Start; + SequencePosition examined = buffer.End; try { if (buffer.Length >= _frameSize) { - var sequence = buffer.Slice(0, _frameSize); + ReadOnlySequence sequence = buffer.Slice(0, _frameSize); using (SKPixmap pixmap = _videoFrame.PeekPixels()) { @@ -133,26 +158,4 @@ public class SubtitleElement( } } } - - public void Dispose() - { - GC.SuppressFinalize(this); - - _pipeReader?.Complete(); - - _cancellationTokenSource?.Cancel(); - try - { -#pragma warning disable VSTHRD002 - _commandTask?.Task.Wait(); -#pragma warning restore VSTHRD002 - } - catch (Exception) - { - // do nothing - } - _cancellationTokenSource?.Dispose(); - - _videoFrame?.Dispose(); - } } diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TemplateFunctions.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TemplateFunctions.cs index 6ed31b7dd..3aec3d04e 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TemplateFunctions.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TemplateFunctions.cs @@ -10,7 +10,7 @@ public class TemplateFunctions(ILogger logger) { try { - var tz = TZConvert.GetTimeZoneInfo(timeZoneId); + TimeZoneInfo tz = TZConvert.GetTimeZoneInfo(timeZoneId); return TimeZoneInfo.ConvertTime(dateTimeOffset, tz); } catch (TimeZoneNotFoundException ex) @@ -24,7 +24,7 @@ public class TemplateFunctions(ILogger logger) { try { - var tz = TZConvert.GetTimeZoneInfo(timeZoneId); + TimeZoneInfo tz = TZConvert.GetTimeZoneInfo(timeZoneId); dateTimeOffset = TimeZoneInfo.ConvertTime(dateTimeOffset, tz); } catch (TimeZoneNotFoundException ex) diff --git a/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TextElement.cs b/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TextElement.cs index f1e8d026c..cd2cbd6a9 100644 --- a/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TextElement.cs +++ b/ErsatzTV.Infrastructure/Streaming/Graphics/Text/TextElement.cs @@ -3,11 +3,10 @@ using ErsatzTV.Core.Domain; using ErsatzTV.Core.Graphics; using Microsoft.Extensions.Logging; using NCalc; -using Topten.RichTextKit; using Scriban; using Scriban.Runtime; using SkiaSharp; -using RichTextKit=Topten.RichTextKit; +using RichTextKit = Topten.RichTextKit; namespace ErsatzTV.Infrastructure.Streaming.Graphics; @@ -20,11 +19,19 @@ public partial class TextElement( : GraphicsElement, IDisposable { private static readonly Regex StylePattern = StyleRegex(); + private SKBitmap _image; + private SKPointI _location; private Option _maybeOpacityExpression; private float _opacity; - private SKBitmap _image; - private SKPointI _location; + + public void Dispose() + { + GC.SuppressFinalize(this); + + _image?.Dispose(); + _image = null; + } public override async Task InitializeAsync( Resolution squarePixelFrameSize, @@ -70,18 +77,20 @@ public partial class TextElement( context.PushGlobal(scriptObject); string textToRender = await Template.Parse(textElement.Text).RenderAsync(context); - var textBlock = BuildTextBlock(textToRender); + RichTextKit.TextBlock textBlock = BuildTextBlock(textToRender); - _image = new SKBitmap((int)Math.Ceiling(textBlock.MeasuredWidth), (int)Math.Ceiling(textBlock.MeasuredHeight)); + _image = new SKBitmap( + (int)Math.Ceiling(textBlock.MeasuredWidth), + (int)Math.Ceiling(textBlock.MeasuredHeight)); using (var canvas = new SKCanvas(_image)) { canvas.Clear(SKColors.Transparent); textBlock.Paint(canvas, new SKPoint(0, 0)); } - int horizontalMargin = + var horizontalMargin = (int)Math.Round((textElement.HorizontalMarginPercent ?? 0) / 100.0 * frameSize.Width); - int verticalMargin = (int)Math.Round((textElement.VerticalMarginPercent ?? 0) / 100.0 * frameSize.Height); + var verticalMargin = (int)Math.Round((textElement.VerticalMarginPercent ?? 0) / 100.0 * frameSize.Height); _location = CalculatePosition( textElement.Location, @@ -107,7 +116,7 @@ public partial class TextElement( CancellationToken cancellationToken) { float opacity = _opacity; - foreach (var expression in _maybeOpacityExpression) + foreach (Expression expression in _maybeOpacityExpression) { opacity = OpacityExpressionHelper.GetOpacity( expression, @@ -122,21 +131,13 @@ public partial class TextElement( : new ValueTask>(new PreparedElementImage(_image, _location, opacity, false)); } - public void Dispose() - { - GC.SuppressFinalize(this); - - _image?.Dispose(); - _image = null; - } - - private TextBlock BuildTextBlock(string textToRender) + private RichTextKit.TextBlock BuildTextBlock(string textToRender) { - var textBlock = new TextBlock { FontMapper = graphicsEngineFonts.Mapper }; + var textBlock = new RichTextKit.TextBlock { FontMapper = graphicsEngineFonts.Mapper }; (Dictionary styles, RichTextKit.Style baseStyle) = BuildTextStyles(); - int lastIndex = 0; + var lastIndex = 0; foreach (Match match in StylePattern.Matches(textToRender)) { // unstyled text before match @@ -145,10 +146,10 @@ public partial class TextElement( textBlock.AddText(textToRender.AsSpan(lastIndex, match.Index - lastIndex), baseStyle); } - var styleName = match.Groups[1].Value; - var innerText = match.Groups[2].Value; + string styleName = match.Groups[1].Value; + string innerText = match.Groups[2].Value; - if (styles.TryGetValue(styleName, out var style)) + if (styles.TryGetValue(styleName, out RichTextKit.Style style)) { textBlock.AddText(innerText, style); } @@ -173,17 +174,17 @@ public partial class TextElement( { var styles = new Dictionary(); - var baseStyleDef = textElement.Styles.Find(s => s.Name == textElement.BaseStyle); + StyleDefinition baseStyleDef = textElement.Styles.Find(s => s.Name == textElement.BaseStyle); if (baseStyleDef == null) { throw new InvalidOperationException( $"The specified base_style '{textElement.BaseStyle}' was not found in the styles list."); } - foreach (var s in textElement.Styles) + foreach (StyleDefinition s in textElement.Styles) { // start with base and merge in additional settings - var finalStyle = RichTextStyleFromDef(baseStyleDef); + RichTextKit.Style finalStyle = RichTextStyleFromDef(baseStyleDef); finalStyle.FontFamily = s.FontFamily ?? finalStyle.FontFamily; finalStyle.FontItalic = s.FontItalic ?? finalStyle.FontItalic; @@ -191,7 +192,7 @@ public partial class TextElement( finalStyle.FontWeight = s.FontWeight ?? finalStyle.FontWeight; finalStyle.LetterSpacing = s.LetterSpacing ?? finalStyle.LetterSpacing; - if (s.TextColor != null && SKColor.TryParse(s.TextColor, out var parsedColor)) + if (s.TextColor != null && SKColor.TryParse(s.TextColor, out SKColor parsedColor)) { finalStyle.TextColor = parsedColor; } @@ -207,20 +208,20 @@ public partial class TextElement( { FontFamily = def.FontFamily, FontItalic = def.FontItalic ?? false, - TextColor = SKColor.TryParse(def.TextColor, out var color) ? color : SKColors.White + TextColor = SKColor.TryParse(def.TextColor, out SKColor color) ? color : SKColors.White }; - foreach (var fontSize in Optional(def.FontSize)) + foreach (float fontSize in Optional(def.FontSize)) { style.FontSize = fontSize; } - foreach (var fontWeight in Optional(def.FontWeight)) + foreach (int fontWeight in Optional(def.FontWeight)) { style.FontWeight = fontWeight; } - foreach (var letterSpacing in Optional(def.LetterSpacing)) + foreach (float letterSpacing in Optional(def.LetterSpacing)) { style.LetterSpacing = letterSpacing; } diff --git a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs index 768f304c0..e14e31d7e 100644 --- a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs @@ -162,7 +162,7 @@ public class TranscodingTests //new InputFormat("libx265", "yuv420p"), new("libx265", "yuv420p10le"), // - new("mpeg4", "yuv420p"), + new("mpeg4", "yuv420p") // // new("libvpx-vp9", "yuv420p"), // new("libvpx-vp9", "yuv420p10le"), @@ -328,7 +328,7 @@ public class TranscodingTests .Do(x => { MediaVersion version = x.Arg(); - if (version.Streams.Any(s => s.MediaStreamKind == MediaStreamKind.Video && s.AttachedPic == false)) + if (version.Streams.Any(s => s.MediaStreamKind == MediaStreamKind.Video && !s.AttachedPic)) { version.MediaFiles = videoVersion.MediaFiles; videoVersion = version; @@ -1088,5 +1088,7 @@ public class TranscodingTests } private static string ExecutableName(string baseName) => - OperatingSystem.IsWindows() ? $"{baseName}.exe" : $"/home/jason/Downloads/ffmpeg/ffmpeg-n7.1.1-56-gc2184b65d2-linux64-gpl-7.1/bin/{baseName}"; + OperatingSystem.IsWindows() + ? $"{baseName}.exe" + : $"/home/jason/Downloads/ffmpeg/ffmpeg-n7.1.1-56-gc2184b65d2-linux64-gpl-7.1/bin/{baseName}"; } diff --git a/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj b/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj index 70673e90e..5717300b7 100644 --- a/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj +++ b/ErsatzTV.Scanner.Tests/ErsatzTV.Scanner.Tests.csproj @@ -59,4 +59,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowById.cs b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowById.cs index 4e8b63d40..2b4389e73 100644 --- a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowById.cs +++ b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowById.cs @@ -3,4 +3,4 @@ using ErsatzTV.Core; namespace ErsatzTV.Scanner.Application.Emby; public record SynchronizeEmbyShowById(int EmbyLibraryId, int ShowId, bool DeepScan) - : IRequest>; \ No newline at end of file + : IRequest>; diff --git a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowByIdHandler.cs b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowByIdHandler.cs index 96d0f8662..b76bd4718 100644 --- a/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowByIdHandler.cs +++ b/ErsatzTV.Scanner/Application/Emby/Commands/SynchronizeEmbyShowByIdHandler.cs @@ -11,9 +11,9 @@ public class SynchronizeEmbyShowByIdHandler : IRequestHandler _logger; private readonly IMediaSourceRepository _mediaSourceRepository; - private readonly IEmbyTelevisionRepository _embyTelevisionRepository; public SynchronizeEmbyShowByIdHandler( IMediaSourceRepository mediaSourceRepository, @@ -119,7 +119,8 @@ public class SynchronizeEmbyShowByIdHandler : IRequestHandler> EmbyShowMustExist( SynchronizeEmbyShowById request) => _embyTelevisionRepository.GetShowTitleItemId(request.EmbyLibraryId, request.ShowId) - .Map(v => v.ToValidation($"Jellyfin show {request.ShowId} does not exist in library {request.EmbyLibraryId}.")); + .Map(v => v.ToValidation( + $"Jellyfin show {request.ShowId} does not exist in library {request.EmbyLibraryId}.")); private record RequestParameters( ConnectionParameters ConnectionParameters, @@ -132,4 +133,4 @@ public class SynchronizeEmbyShowByIdHandler : IRequestHandler>; \ No newline at end of file + : IRequest>; diff --git a/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinShowByIdHandler.cs b/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinShowByIdHandler.cs index e270611ad..d2d575364 100644 --- a/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinShowByIdHandler.cs +++ b/ErsatzTV.Scanner/Application/Jellyfin/Commands/SynchronizeJellyfinShowByIdHandler.cs @@ -7,13 +7,14 @@ using Microsoft.Extensions.Logging; namespace ErsatzTV.Scanner.Application.Jellyfin; -public class SynchronizeJellyfinShowByIdHandler : IRequestHandler> +public class + SynchronizeJellyfinShowByIdHandler : IRequestHandler> { private readonly IJellyfinSecretStore _jellyfinSecretStore; private readonly IJellyfinTelevisionLibraryScanner _jellyfinTelevisionLibraryScanner; + private readonly IJellyfinTelevisionRepository _jellyfinTelevisionRepository; private readonly ILogger _logger; private readonly IMediaSourceRepository _mediaSourceRepository; - private readonly IJellyfinTelevisionRepository _jellyfinTelevisionRepository; public SynchronizeJellyfinShowByIdHandler( IMediaSourceRepository mediaSourceRepository, @@ -71,7 +72,8 @@ public class SynchronizeJellyfinShowByIdHandler : IRequestHandler> Validate(SynchronizeJellyfinShowById request) => - (await ValidateConnection(request), await JellyfinLibraryMustExist(request), await JellyfinShowMustExist(request)) + (await ValidateConnection(request), await JellyfinLibraryMustExist(request), + await JellyfinShowMustExist(request)) .Apply((connectionParameters, jellyfinLibrary, showTitleItemId) => new RequestParameters( connectionParameters, @@ -119,7 +121,8 @@ public class SynchronizeJellyfinShowByIdHandler : IRequestHandler> JellyfinShowMustExist( SynchronizeJellyfinShowById request) => _jellyfinTelevisionRepository.GetShowTitleItemId(request.JellyfinLibraryId, request.ShowId) - .Map(v => v.ToValidation($"Jellyfin show {request.ShowId} does not exist in library {request.JellyfinLibraryId}.")); + .Map(v => v.ToValidation( + $"Jellyfin show {request.ShowId} does not exist in library {request.JellyfinLibraryId}.")); private record RequestParameters( ConnectionParameters ConnectionParameters, @@ -132,4 +135,4 @@ public class SynchronizeJellyfinShowByIdHandler : IRequestHandler _logger; private readonly IMediator _mediator; private readonly IMovieFolderScanner _movieFolderScanner; private readonly IMusicVideoFolderScanner _musicVideoFolderScanner; private readonly IOtherVideoFolderScanner _otherVideoFolderScanner; + private readonly IRemoteStreamFolderScanner _remoteStreamFolderScanner; private readonly ISongFolderScanner _songFolderScanner; private readonly ITelevisionFolderScanner _televisionFolderScanner; diff --git a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowById.cs b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowById.cs index 0e260ff0f..f791c3a7a 100644 --- a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowById.cs +++ b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowById.cs @@ -3,4 +3,4 @@ using ErsatzTV.Core; namespace ErsatzTV.Scanner.Application.Plex; public record SynchronizePlexShowById(int PlexLibraryId, int ShowId, bool DeepScan) - : IRequest>; \ No newline at end of file + : IRequest>; diff --git a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowByIdHandler.cs b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowByIdHandler.cs index 7eb9cebc1..d1a053d88 100644 --- a/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowByIdHandler.cs +++ b/ErsatzTV.Scanner/Application/Plex/Commands/SynchronizePlexShowByIdHandler.cs @@ -10,10 +10,10 @@ namespace ErsatzTV.Scanner.Application.Plex; public class SynchronizePlexShowByIdHandler : IRequestHandler> { private readonly ILogger _logger; - private readonly IPlexTelevisionRepository _plexTelevisionRepository; private readonly IMediaSourceRepository _mediaSourceRepository; private readonly IPlexSecretStore _plexSecretStore; private readonly IPlexTelevisionLibraryScanner _plexTelevisionLibraryScanner; + private readonly IPlexTelevisionRepository _plexTelevisionRepository; public SynchronizePlexShowByIdHandler( IPlexTelevisionRepository plexTelevisionRepository, @@ -119,7 +119,8 @@ public class SynchronizePlexShowByIdHandler : IRequestHandler> PlexShowMustExist( SynchronizePlexShowById request) => _plexTelevisionRepository.GetShowTitleKey(request.PlexLibraryId, request.ShowId) - .Map(v => v.ToValidation($"Plex show {request.ShowId} does not exist in library {request.PlexLibraryId}.")); + .Map(v => v.ToValidation( + $"Plex show {request.ShowId} does not exist in library {request.PlexLibraryId}.")); private record RequestParameters( ConnectionParameters ConnectionParameters, @@ -132,4 +133,4 @@ public class SynchronizePlexShowByIdHandler : IRequestHandler> ScanSingleShow( + string address, + string apiKey, + EmbyLibrary library, + string showId, + string showTitle, + bool deepScan, + CancellationToken cancellationToken) + { + List pathReplacements = + await _mediaSourceRepository.GetEmbyPathReplacements(library.MediaSourceId); + + string GetLocalPath(EmbyEpisode episode) + { + return _pathReplacementService.GetReplacementEmbyPath( + pathReplacements, + episode.GetHeadVersion().MediaFiles.Head().Path, + false); + } + + // Search for the specific show + Either> searchResult = await _embyApiClient.GetSingleShow( + address, + apiKey, + library, + showId); + + return await searchResult.Match( + async maybeShow => + { + foreach (EmbyShow show in maybeShow) + { + _logger.LogInformation( + "Found show '{ShowTitle}' with id {ShowId}, starting targeted scan", + showTitle, + show.ItemId); + + return await ScanSingleShowInternal( + _televisionRepository, + new EmbyConnectionParameters(address, apiKey), + library, + show, + GetLocalPath, + deepScan, + cancellationToken); + } + + _logger.LogWarning("No show found with id {ShowId} in library {LibraryName}", showId, library.Name); + + return Right(Unit.Default); + }, + error => Task.FromResult>(error)); + } + protected override IAsyncEnumerable> GetShowLibraryItems( EmbyConnectionParameters connectionParameters, EmbyLibrary library) => @@ -186,58 +240,6 @@ public class EmbyTelevisionLibraryScanner : MediaServerTelevisionLibraryScanner< EpisodeMetadata fullMetadata) => Task.FromResult>>(result); - public async Task> ScanSingleShow( - string address, - string apiKey, - EmbyLibrary library, - string showId, - string showTitle, - bool deepScan, - CancellationToken cancellationToken) - { - List pathReplacements = - await _mediaSourceRepository.GetEmbyPathReplacements(library.MediaSourceId); - - string GetLocalPath(EmbyEpisode episode) - { - return _pathReplacementService.GetReplacementEmbyPath( - pathReplacements, - episode.GetHeadVersion().MediaFiles.Head().Path, - false); - } - - // Search for the specific show - Either> searchResult = await _embyApiClient.GetSingleShow( - address, - apiKey, - library, - showId); - - return await searchResult.Match( - async maybeShow => - { - foreach (var show in maybeShow) - { - _logger.LogInformation("Found show '{ShowTitle}' with id {ShowId}, starting targeted scan", - showTitle, show.ItemId); - - return await ScanSingleShowInternal( - _televisionRepository, - new EmbyConnectionParameters(address, apiKey), - library, - show, - GetLocalPath, - deepScan, - cancellationToken); - } - - _logger.LogWarning("No show found with id {ShowId} in library {LibraryName}", showId, library.Name); - - return Right(Unit.Default); - }, - error => Task.FromResult>(error)); - } - private async Task> ScanSingleShowInternal( IEmbyTelevisionRepository televisionRepository, EmbyConnectionParameters connectionParameters, diff --git a/ErsatzTV.Scanner/Core/Interfaces/Metadata/ILocalChaptersProvider.cs b/ErsatzTV.Scanner/Core/Interfaces/Metadata/ILocalChaptersProvider.cs index 9cd4ece38..487e1e780 100644 --- a/ErsatzTV.Scanner/Core/Interfaces/Metadata/ILocalChaptersProvider.cs +++ b/ErsatzTV.Scanner/Core/Interfaces/Metadata/ILocalChaptersProvider.cs @@ -5,4 +5,4 @@ namespace ErsatzTV.Scanner.Core.Interfaces.Metadata; public interface ILocalChaptersProvider : IDisposable { Task UpdateChapters(MediaItem mediaItem, Option localPath); -} \ No newline at end of file +} diff --git a/ErsatzTV.Scanner/Core/Jellyfin/JellyfinMovieLibraryScanner.cs b/ErsatzTV.Scanner/Core/Jellyfin/JellyfinMovieLibraryScanner.cs index 366b98483..99f528e9f 100644 --- a/ErsatzTV.Scanner/Core/Jellyfin/JellyfinMovieLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Jellyfin/JellyfinMovieLibraryScanner.cs @@ -4,9 +4,9 @@ using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Jellyfin; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; -using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Core.Jellyfin; using ErsatzTV.Core.Metadata; +using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Scanner.Core.Metadata; using Microsoft.Extensions.Logging; diff --git a/ErsatzTV.Scanner/Core/Jellyfin/JellyfinTelevisionLibraryScanner.cs b/ErsatzTV.Scanner/Core/Jellyfin/JellyfinTelevisionLibraryScanner.cs index 9a70ae928..28745056c 100644 --- a/ErsatzTV.Scanner/Core/Jellyfin/JellyfinTelevisionLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Jellyfin/JellyfinTelevisionLibraryScanner.cs @@ -5,9 +5,9 @@ using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Jellyfin; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; -using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Core.Jellyfin; using ErsatzTV.Core.Metadata; +using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Scanner.Core.Metadata; using Microsoft.Extensions.Logging; @@ -77,6 +77,60 @@ public class JellyfinTelevisionLibraryScanner : MediaServerTelevisionLibraryScan cancellationToken); } + public async Task> ScanSingleShow( + string address, + string apiKey, + JellyfinLibrary library, + string showId, + string showTitle, + bool deepScan, + CancellationToken cancellationToken) + { + List pathReplacements = + await _mediaSourceRepository.GetJellyfinPathReplacements(library.MediaSourceId); + + string GetLocalPath(JellyfinEpisode episode) + { + return _pathReplacementService.GetReplacementJellyfinPath( + pathReplacements, + episode.GetHeadVersion().MediaFiles.Head().Path, + false); + } + + // Search for the specific show + Either> searchResult = await _jellyfinApiClient.GetSingleShow( + address, + apiKey, + library, + showId); + + return await searchResult.Match( + async maybeShow => + { + foreach (JellyfinShow show in maybeShow) + { + _logger.LogInformation( + "Found show '{ShowTitle}' with id {ShowId}, starting targeted scan", + showTitle, + show.ItemId); + + return await ScanSingleShowInternal( + _televisionRepository, + new JellyfinConnectionParameters(address, apiKey, library.MediaSourceId), + library, + show, + GetLocalPath, + deepScan, + cancellationToken); + } + + _logger.LogWarning("No show found with id {ShowId} in library {LibraryName}", showId, library.Name); + + return Right(Unit.Default); + }, + error => Task.FromResult>(error)); + } + protected override IAsyncEnumerable> GetShowLibraryItems( JellyfinConnectionParameters connectionParameters, JellyfinLibrary library) => @@ -185,58 +239,6 @@ public class JellyfinTelevisionLibraryScanner : MediaServerTelevisionLibraryScan EpisodeMetadata fullMetadata) => Task.FromResult>>(result); - public async Task> ScanSingleShow( - string address, - string apiKey, - JellyfinLibrary library, - string showId, - string showTitle, - bool deepScan, - CancellationToken cancellationToken) - { - List pathReplacements = - await _mediaSourceRepository.GetJellyfinPathReplacements(library.MediaSourceId); - - string GetLocalPath(JellyfinEpisode episode) - { - return _pathReplacementService.GetReplacementJellyfinPath( - pathReplacements, - episode.GetHeadVersion().MediaFiles.Head().Path, - false); - } - - // Search for the specific show - Either> searchResult = await _jellyfinApiClient.GetSingleShow( - address, - apiKey, - library, - showId); - - return await searchResult.Match( - async maybeShow => - { - foreach (var show in maybeShow) - { - _logger.LogInformation("Found show '{ShowTitle}' with id {ShowId}, starting targeted scan", - showTitle, show.ItemId); - - return await ScanSingleShowInternal( - _televisionRepository, - new JellyfinConnectionParameters(address, apiKey, library.MediaSourceId), - library, - show, - GetLocalPath, - deepScan, - cancellationToken); - } - - _logger.LogWarning("No show found with id {ShowId} in library {LibraryName}", showId, library.Name); - - return Right(Unit.Default); - }, - error => Task.FromResult>(error)); - } - private async Task> ScanSingleShowInternal( IJellyfinTelevisionRepository televisionRepository, JellyfinConnectionParameters connectionParameters, diff --git a/ErsatzTV.Scanner/Core/Metadata/LocalChaptersProvider.cs b/ErsatzTV.Scanner/Core/Metadata/LocalChaptersProvider.cs index 7aa234c06..e4a50936f 100644 --- a/ErsatzTV.Scanner/Core/Metadata/LocalChaptersProvider.cs +++ b/ErsatzTV.Scanner/Core/Metadata/LocalChaptersProvider.cs @@ -1,11 +1,11 @@ +using System.Text.RegularExpressions; +using System.Xml; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Scanner.Core.Interfaces.Metadata; using Microsoft.Extensions.Logging; -using System.Text.RegularExpressions; -using System.Xml; namespace ErsatzTV.Scanner.Core.Metadata; @@ -51,6 +51,12 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider } } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + public List LocateExternalChapters(string mediaItemPath) { var result = new List(); @@ -124,7 +130,7 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider var chapters = new List(); XmlNodeList? chapterAtoms = chaptersNode.SelectNodes(".//ChapterAtom") ?? - chaptersNode.SelectNodes(".//chapteratom"); + chaptersNode.SelectNodes(".//chapteratom"); if (chapterAtoms == null) { @@ -134,7 +140,7 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider long chapterId = 0; foreach (XmlNode chapterAtom in chapterAtoms) { - var chapter = ParseChapterAtom(chapterAtom, chapterId++); + MediaChapter? chapter = ParseChapterAtom(chapterAtom, chapterId++); if (chapter != null) { chapters.Add(chapter); @@ -143,7 +149,7 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider chapters.Sort((a, b) => a.StartTime.CompareTo(b.StartTime)); - for (int i = 0; i < chapters.Count; i++) + for (var i = 0; i < chapters.Count; i++) { chapters[i].ChapterId = i; } @@ -154,7 +160,7 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider private static MediaChapter? ParseChapterAtom(XmlNode chapterAtom, long chapterId) { XmlNode? startNode = chapterAtom.SelectSingleNode(".//ChapterTimeStart") ?? - chapterAtom.SelectSingleNode(".//chaptertimestart"); + chapterAtom.SelectSingleNode(".//chaptertimestart"); if (startNode?.InnerText == null) { @@ -168,7 +174,7 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider TimeSpan endTime = TimeSpan.Zero; XmlNode? endNode = chapterAtom.SelectSingleNode(".//ChapterTimeEnd") ?? - chapterAtom.SelectSingleNode(".//chaptertimeend"); + chapterAtom.SelectSingleNode(".//chaptertimeend"); if (endNode?.InnerText != null) { @@ -177,9 +183,9 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider string title = string.Empty; XmlNode? titleNode = chapterAtom.SelectSingleNode(".//ChapterString") ?? - chapterAtom.SelectSingleNode(".//ChapString") ?? - chapterAtom.SelectSingleNode(".//chapterstring") ?? - chapterAtom.SelectSingleNode(".//chapstring"); + chapterAtom.SelectSingleNode(".//ChapString") ?? + chapterAtom.SelectSingleNode(".//chapterstring") ?? + chapterAtom.SelectSingleNode(".//chapstring"); if (titleNode?.InnerText != null) { @@ -212,7 +218,7 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider } // Handle time format HH:MM:SS.mmm or HH:MM:SS,mmm - var timeFormats = new Regex[] + var timeFormats = new[] { GetParseFullTimeCodeRegex(), GetParseTimeCodeNoMilliRegex() @@ -220,14 +226,14 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider foreach (Regex pattern in timeFormats) { - var match = pattern.Match(timeString); + Match match = pattern.Match(timeString); if (match.Success) { if (int.TryParse(match.Groups[1].Value, out int hours) && int.TryParse(match.Groups[2].Value, out int minutes) && int.TryParse(match.Groups[3].Value, out int seconds)) { - int milliseconds = 0; + var milliseconds = 0; if (match.Groups.Count > 4 && !int.TryParse(match.Groups[4].Value, out milliseconds)) { milliseconds = 0; @@ -242,12 +248,6 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider return false; } - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - protected virtual void Dispose(bool disposing) { if (!_disposedValue) @@ -258,6 +258,7 @@ public partial class LocalChaptersProvider : ILocalChaptersProvider [GeneratedRegex(@"^(\d{1,2}):(\d{2}):(\d{2})[\.,](\d{3})$")] private static partial Regex GetParseFullTimeCodeRegex(); + [GeneratedRegex(@"^(\d{1,2}):(\d{2}):(\d{2})$")] private static partial Regex GetParseTimeCodeNoMilliRegex(); -} \ No newline at end of file +} diff --git a/ErsatzTV.Scanner/Core/Metadata/LocalMetadataProvider.cs b/ErsatzTV.Scanner/Core/Metadata/LocalMetadataProvider.cs index 50095c91f..f1ef57ba5 100644 --- a/ErsatzTV.Scanner/Core/Metadata/LocalMetadataProvider.cs +++ b/ErsatzTV.Scanner/Core/Metadata/LocalMetadataProvider.cs @@ -23,7 +23,6 @@ public class LocalMetadataProvider : ILocalMetadataProvider private readonly IEpisodeNfoReader _episodeNfoReader; private readonly IFallbackMetadataProvider _fallbackMetadataProvider; private readonly IImageRepository _imageRepository; - private readonly IRemoteStreamRepository _remoteStreamRepository; private readonly ILocalFileSystem _localFileSystem; private readonly ILocalStatisticsProvider _localStatisticsProvider; private readonly ILogger _logger; @@ -34,6 +33,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider private readonly IMusicVideoRepository _musicVideoRepository; private readonly IOtherVideoNfoReader _otherVideoNfoReader; private readonly IOtherVideoRepository _otherVideoRepository; + private readonly IRemoteStreamRepository _remoteStreamRepository; private readonly IShowNfoReader _showNfoReader; private readonly ISongRepository _songRepository; private readonly ITelevisionRepository _televisionRepository; @@ -189,7 +189,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider string diff = Path.GetRelativePath(parent, folder); var tags = diff.Split(Path.DirectorySeparatorChar) - .Filter(t => metadata.Tags.Any(mt => mt.Name == t) == false) + .Filter(t => metadata.Tags.All(mt => mt.Name != t)) .Map(t => new Tag { Name = t }) .ToList(); @@ -224,16 +224,13 @@ public class LocalMetadataProvider : ILocalMetadataProvider return await RefreshFallbackMetadata(image); } - public async Task RefreshTagMetadata(RemoteStream remoteStream) - { + public async Task RefreshTagMetadata(RemoteStream remoteStream) => // Option maybeMetadata = LoadRemoteStreamMetadata(remoteStream); // foreach (RemoteStreamMetadata metadata in maybeMetadata) // { // return await ApplyMetadataUpdate(remoteStream, metadata); // } - - return await RefreshFallbackMetadata(remoteStream); - } + await RefreshFallbackMetadata(remoteStream); public Task RefreshFallbackMetadata(Movie movie) => ApplyMetadataUpdate(movie, _fallbackMetadataProvider.GetFallbackMetadata(movie)); @@ -1471,11 +1468,12 @@ public class LocalMetadataProvider : ILocalMetadataProvider var tags = nfo.Tags.Map(t => new Tag { Name = t }).ToList(); foreach (string country in nfo.Countries) { - tags.Add(new Tag - { - Name = country, - ExternalTypeId = Tag.NfoCountryTypeId - }); + tags.Add( + new Tag + { + Name = country, + ExternalTypeId = Tag.NfoCountryTypeId + }); } return new MovieMetadata diff --git a/ErsatzTV.Scanner/Core/Metadata/MediaServerMovieLibraryScanner.cs b/ErsatzTV.Scanner/Core/Metadata/MediaServerMovieLibraryScanner.cs index eb1559933..f4ec5e8e5 100644 --- a/ErsatzTV.Scanner/Core/Metadata/MediaServerMovieLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Metadata/MediaServerMovieLibraryScanner.cs @@ -19,8 +19,8 @@ public abstract class MediaServerMovieLibraryScanner getLocalPath, IAsyncEnumerable> showEntries, bool deepScan, - CancellationToken cancellationToken) - { - return await InternalScanLibrary( + CancellationToken cancellationToken) => + await InternalScanLibrary( televisionRepository, connectionParameters, library, getLocalPath, showEntries, deepScan, - cleanupFileNotFoundItems: true, + true, cancellationToken); - } protected async Task> ScanLibraryWithoutCleanup( IMediaServerTelevisionRepository televisionRepository, @@ -218,18 +216,16 @@ public abstract class MediaServerTelevisionLibraryScanner getLocalPath, IAsyncEnumerable> showEntries, bool deepScan, - CancellationToken cancellationToken) - { - return await InternalScanLibrary( + CancellationToken cancellationToken) => + await InternalScanLibrary( televisionRepository, connectionParameters, library, getLocalPath, showEntries, deepScan, - cleanupFileNotFoundItems: false, + false, cancellationToken); - } protected abstract IAsyncEnumerable> GetSeasonLibraryItems( TLibrary library, @@ -408,7 +404,7 @@ public abstract class MediaServerTelevisionLibraryScanner _logger; private readonly IMediaItemRepository _mediaItemRepository; private readonly IMediator _mediator; diff --git a/ErsatzTV.Scanner/Core/Metadata/MusicVideoFolderScanner.cs b/ErsatzTV.Scanner/Core/Metadata/MusicVideoFolderScanner.cs index 90a5f0023..e500054ee 100644 --- a/ErsatzTV.Scanner/Core/Metadata/MusicVideoFolderScanner.cs +++ b/ErsatzTV.Scanner/Core/Metadata/MusicVideoFolderScanner.cs @@ -21,10 +21,10 @@ public class MusicVideoFolderScanner : LocalFolderScanner, IMusicVideoFolderScan private readonly IArtistRepository _artistRepository; private readonly IClient _client; private readonly ILibraryRepository _libraryRepository; + private readonly ILocalChaptersProvider _localChaptersProvider; private readonly ILocalFileSystem _localFileSystem; private readonly ILocalMetadataProvider _localMetadataProvider; private readonly ILocalSubtitlesProvider _localSubtitlesProvider; - private readonly ILocalChaptersProvider _localChaptersProvider; private readonly ILogger _logger; private readonly IMediaItemRepository _mediaItemRepository; private readonly IMediator _mediator; diff --git a/ErsatzTV.Scanner/Core/Metadata/Nfo/NfoReader.cs b/ErsatzTV.Scanner/Core/Metadata/Nfo/NfoReader.cs index afc00c785..3dd112a6a 100644 --- a/ErsatzTV.Scanner/Core/Metadata/Nfo/NfoReader.cs +++ b/ErsatzTV.Scanner/Core/Metadata/Nfo/NfoReader.cs @@ -49,7 +49,11 @@ public abstract class NfoReader : NfoReaderBase } catch (XmlException ex) { - _logger.LogWarning(ex, "Error reading string content from NFO {ElementName} file {FileName}", reader.Name, fileName); + _logger.LogWarning( + ex, + "Error reading string content from NFO {ElementName} file {FileName}", + reader.Name, + fileName); } } @@ -64,7 +68,11 @@ public abstract class NfoReader : NfoReaderBase } catch (XmlException ex) { - _logger.LogWarning(ex, "Error reading int content from NFO {ElementName} file {FileName}", reader.Name, fileName); + _logger.LogWarning( + ex, + "Error reading int content from NFO {ElementName} file {FileName}", + reader.Name, + fileName); } } @@ -81,7 +89,11 @@ public abstract class NfoReader : NfoReaderBase } catch (XmlException ex) { - _logger.LogWarning(ex, "Error reading date content from NFO {ElementName} file {FileName}", reader.Name, fileName); + _logger.LogWarning( + ex, + "Error reading date content from NFO {ElementName} file {FileName}", + reader.Name, + fileName); } } @@ -123,7 +135,11 @@ public abstract class NfoReader : NfoReaderBase } catch (XmlException ex) { - _logger.LogWarning(ex, "Error reading actor content from NFO {ElementName} file {FileName}", reader.Name, fileName); + _logger.LogWarning( + ex, + "Error reading actor content from NFO {ElementName} file {FileName}", + reader.Name, + fileName); } } @@ -146,7 +162,11 @@ public abstract class NfoReader : NfoReaderBase } catch (XmlException ex) { - _logger.LogWarning(ex, "Error reading uniqueid content from NFO {ElementName} file {FileName}", reader.Name, fileName); + _logger.LogWarning( + ex, + "Error reading uniqueid content from NFO {ElementName} file {FileName}", + reader.Name, + fileName); } } } diff --git a/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs b/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs index b8181543d..8361ff310 100644 --- a/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs +++ b/ErsatzTV.Scanner/Core/Metadata/Nfo/OtherVideoNfoReader.cs @@ -83,7 +83,8 @@ public class OtherVideoNfoReader : NfoReader, IOtherVideoNfoReade await ReadStringContent( reader, nfo, - (movie, contentRating) => movie.ContentRating = contentRating, fileName); + (movie, contentRating) => movie.ContentRating = contentRating, + fileName); break; case "premiered": await ReadDateTimeContent( diff --git a/ErsatzTV.Scanner/Core/Metadata/OtherVideoFolderScanner.cs b/ErsatzTV.Scanner/Core/Metadata/OtherVideoFolderScanner.cs index 729de7a31..20bb96603 100644 --- a/ErsatzTV.Scanner/Core/Metadata/OtherVideoFolderScanner.cs +++ b/ErsatzTV.Scanner/Core/Metadata/OtherVideoFolderScanner.cs @@ -20,10 +20,10 @@ public class OtherVideoFolderScanner : LocalFolderScanner, IOtherVideoFolderScan { private readonly IClient _client; private readonly ILibraryRepository _libraryRepository; + private readonly ILocalChaptersProvider _localChaptersProvider; private readonly ILocalFileSystem _localFileSystem; private readonly ILocalMetadataProvider _localMetadataProvider; private readonly ILocalSubtitlesProvider _localSubtitlesProvider; - private readonly ILocalChaptersProvider _localChaptersProvider; private readonly ILogger _logger; private readonly IMediaItemRepository _mediaItemRepository; private readonly IMediator _mediator; diff --git a/ErsatzTV.Scanner/Core/Metadata/RemoteStreamFolderScanner.cs b/ErsatzTV.Scanner/Core/Metadata/RemoteStreamFolderScanner.cs index 91eb13f98..a26502f10 100644 --- a/ErsatzTV.Scanner/Core/Metadata/RemoteStreamFolderScanner.cs +++ b/ErsatzTV.Scanner/Core/Metadata/RemoteStreamFolderScanner.cs @@ -22,13 +22,13 @@ namespace ErsatzTV.Scanner.Core.Metadata; public class RemoteStreamFolderScanner : LocalFolderScanner, IRemoteStreamFolderScanner { private readonly IClient _client; - private readonly IRemoteStreamRepository _remoteStreamRepository; private readonly ILibraryRepository _libraryRepository; private readonly ILocalFileSystem _localFileSystem; private readonly ILocalMetadataProvider _localMetadataProvider; private readonly ILogger _logger; private readonly IMediaItemRepository _mediaItemRepository; private readonly IMediator _mediator; + private readonly IRemoteStreamRepository _remoteStreamRepository; public RemoteStreamFolderScanner( ILocalFileSystem localFileSystem, @@ -126,7 +126,8 @@ public class RemoteStreamFolderScanner : LocalFolderScanner, IRemoteStreamFolder cancellationToken); string remoteStreamFolder = folderQueue.Dequeue(); - Option maybeParentFolder = await _libraryRepository.GetParentFolderId(libraryPath, remoteStreamFolder); + Option maybeParentFolder = + await _libraryRepository.GetParentFolderId(libraryPath, remoteStreamFolder); foldersCompleted++; @@ -155,7 +156,9 @@ public class RemoteStreamFolderScanner : LocalFolderScanner, IRemoteStreamFolder { if (allFiles.Any(allTrashedItems.Contains)) { - _logger.LogDebug("Previously trashed items are now present in folder {Folder}", remoteStreamFolder); + _logger.LogDebug( + "Previously trashed items are now present in folder {Folder}", + remoteStreamFolder); } else { @@ -281,10 +284,10 @@ public class RemoteStreamFolderScanner : LocalFolderScanner, IRemoteStreamFolder YamlRemoteStreamDefinition definition = deserializer.Deserialize(yaml); if (!definition.IsLive.HasValue) { - return BaseError.New($"Remote stream definition is missing required `is_live` property"); + return BaseError.New("Remote stream definition is missing required `is_live` property"); } - bool updated = false; + var updated = false; if (remoteStream.IsLive != definition.IsLive.Value) { remoteStream.IsLive = definition.IsLive.Value; diff --git a/ErsatzTV.Scanner/Core/Metadata/TelevisionFolderScanner.cs b/ErsatzTV.Scanner/Core/Metadata/TelevisionFolderScanner.cs index 7aaee76ed..8b0b669bd 100644 --- a/ErsatzTV.Scanner/Core/Metadata/TelevisionFolderScanner.cs +++ b/ErsatzTV.Scanner/Core/Metadata/TelevisionFolderScanner.cs @@ -21,10 +21,10 @@ public class TelevisionFolderScanner : LocalFolderScanner, ITelevisionFolderScan private readonly IClient _client; private readonly IFallbackMetadataProvider _fallbackMetadataProvider; private readonly ILibraryRepository _libraryRepository; + private readonly ILocalChaptersProvider _localChaptersProvider; private readonly ILocalFileSystem _localFileSystem; private readonly ILocalMetadataProvider _localMetadataProvider; private readonly ILocalSubtitlesProvider _localSubtitlesProvider; - private readonly ILocalChaptersProvider _localChaptersProvider; private readonly ILogger _logger; private readonly IMediaItemRepository _mediaItemRepository; private readonly IMediator _mediator; diff --git a/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs b/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs index 3ce4a6c69..47e0bc5bb 100644 --- a/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Plex/PlexOtherVideoLibraryScanner.cs @@ -4,9 +4,9 @@ using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; -using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Core.Metadata; using ErsatzTV.Core.Plex; +using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Scanner.Core.Metadata; using Microsoft.Extensions.Logging; diff --git a/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs b/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs index b984da61a..1e5cb9b5e 100644 --- a/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs +++ b/ErsatzTV.Scanner/Core/Plex/PlexTelevisionLibraryScanner.cs @@ -6,9 +6,9 @@ using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; -using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Core.Metadata; using ErsatzTV.Core.Plex; +using ErsatzTV.Scanner.Core.Interfaces.Metadata; using ErsatzTV.Scanner.Core.Metadata; using Microsoft.Extensions.Logging; @@ -120,9 +120,10 @@ public partial class PlexTelevisionLibraryScanner : return await showResult.Match( async maybeShow => { - foreach (var show in maybeShow) + foreach (PlexShow show in maybeShow) { - _logger.LogInformation("Found show '{ShowTitle}' with key {ShowKey}, starting targeted scan", + _logger.LogInformation( + "Found show '{ShowTitle}' with key {ShowKey}, starting targeted scan", showTitle, show.Key); @@ -144,7 +145,8 @@ public partial class PlexTelevisionLibraryScanner : } private async Task> ScanSingleShowInternal( - IMediaServerTelevisionRepository televisionRepository, + IMediaServerTelevisionRepository + televisionRepository, PlexConnectionParameters connectionParameters, PlexLibrary library, PlexShow targetShow, diff --git a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj index cae90dd19..74063630b 100644 --- a/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj +++ b/ErsatzTV.Scanner/ErsatzTV.Scanner.csproj @@ -33,7 +33,7 @@ - + @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV.Scanner/Program.cs b/ErsatzTV.Scanner/Program.cs index 422bc0cb4..f665407b1 100644 --- a/ErsatzTV.Scanner/Program.cs +++ b/ErsatzTV.Scanner/Program.cs @@ -13,7 +13,6 @@ using ErsatzTV.Core.Interfaces.Plex; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories.Caching; using ErsatzTV.Core.Interfaces.Search; -using ErsatzTV.Core.Interfaces.Streaming; using ErsatzTV.Core.Jellyfin; using ErsatzTV.Core.Metadata; using ErsatzTV.Core.Plex; @@ -30,7 +29,6 @@ using ErsatzTV.Infrastructure.Plex; using ErsatzTV.Infrastructure.Runtime; using ErsatzTV.Infrastructure.Search; using ErsatzTV.Infrastructure.Sqlite.Data; -using ErsatzTV.Infrastructure.Streaming; using ErsatzTV.Scanner.Core.Emby; using ErsatzTV.Scanner.Core.FFmpeg; using ErsatzTV.Scanner.Core.Interfaces.FFmpeg; diff --git a/ErsatzTV.Scanner/Worker.cs b/ErsatzTV.Scanner/Worker.cs index a04e1919b..8865cae69 100644 --- a/ErsatzTV.Scanner/Worker.cs +++ b/ErsatzTV.Scanner/Worker.cs @@ -34,7 +34,7 @@ public class Worker : BackgroundService string[] arguments = Environment.GetCommandLineArgs().Skip(1).ToArray(); ParseResult parseResult = rootCommand.Parse(arguments); - await parseResult.InvokeAsync(stoppingToken); + await parseResult.InvokeAsync(cancellationToken: stoppingToken); _appLifetime.StopApplication(); } @@ -117,7 +117,9 @@ public class Worker : BackgroundService scanEmbyShowCommand.Arguments.Add(showIdArgument); scanEmbyShowCommand.Options.Add(deepOption); - var scanJellyfinShowCommand = new Command("scan-jellyfin-show", "Scan a specific TV show in a Jellyfin library"); + var scanJellyfinShowCommand = new Command( + "scan-jellyfin-show", + "Scan a specific TV show in a Jellyfin library"); scanJellyfinShowCommand.Arguments.Add(libraryIdArgument); scanJellyfinShowCommand.Arguments.Add(showIdArgument); scanJellyfinShowCommand.Options.Add(deepOption); diff --git a/ErsatzTV/App.razor b/ErsatzTV/App.razor index 10f91c819..73af31a43 100644 --- a/ErsatzTV/App.razor +++ b/ErsatzTV/App.razor @@ -10,4 +10,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV/Controllers/Api/LibrariesController.cs b/ErsatzTV/Controllers/Api/LibrariesController.cs index ebea370a3..516aa81d7 100644 --- a/ErsatzTV/Controllers/Api/LibrariesController.cs +++ b/ErsatzTV/Controllers/Api/LibrariesController.cs @@ -22,9 +22,9 @@ public class LibrariesController(ITelevisionRepository televisionRepository, IMe return new BadRequestObjectResult(new { error = "ShowTitle is required" }); } - var trimmedTitle = request.ShowTitle.Trim(); - var maybeShowId = await televisionRepository.GetShowIdByTitle(id, trimmedTitle); - foreach (var showId in maybeShowId) + string trimmedTitle = request.ShowTitle.Trim(); + Option maybeShowId = await televisionRepository.GetShowIdByTitle(id, trimmedTitle); + foreach (int showId in maybeShowId) { bool result = await mediator.Send(new QueueShowScanByLibraryId(id, showId, trimmedTitle, request.DeepScan)); @@ -33,7 +33,8 @@ public class LibrariesController(ITelevisionRepository televisionRepository, IMe : new BadRequestObjectResult(new { error = "Unable to queue show scan. Library may not exist, may not support single show scanning, or may already be scanning." }); } - return new BadRequestObjectResult(new { error = $"Unable to locate show with title {request.ShowTitle} in library {id}" }); + return new BadRequestObjectResult( + new { error = $"Unable to locate show with title {request.ShowTitle} in library {id}" }); } } diff --git a/ErsatzTV/Controllers/Api/TroubleshootController.cs b/ErsatzTV/Controllers/Api/TroubleshootController.cs index 305cd9e20..a5ee0de11 100644 --- a/ErsatzTV/Controllers/Api/TroubleshootController.cs +++ b/ErsatzTV/Controllers/Api/TroubleshootController.cs @@ -41,7 +41,13 @@ public class TroubleshootController( Option ss = seekSeconds > 0 ? seekSeconds : Option.None; Either result = await mediator.Send( - new PrepareTroubleshootingPlayback(mediaItem, ffmpegProfile, watermark, graphicsElement, subtitleId, ss), + new PrepareTroubleshootingPlayback( + mediaItem, + ffmpegProfile, + watermark, + graphicsElement, + subtitleId, + ss), cancellationToken); if (result.IsLeft) @@ -49,7 +55,7 @@ public class TroubleshootController( return NotFound(); } - foreach (var playoutItemResult in result.RightToSeq()) + foreach (PlayoutItemResult playoutItemResult in result.RightToSeq()) { Either maybeMediaInfo = await mediator.Send(new GetMediaItemInfo(mediaItem), cancellationToken); @@ -70,7 +76,10 @@ public class TroubleshootController( troubleshootingInfo.Watermarks.RemoveAll(p => !watermark.Contains(p.Id)); await channelWriter.WriteAsync( - new StartTroubleshootingPlayback(sessionId, playoutItemResult, mediaInfo, + new StartTroubleshootingPlayback( + sessionId, + playoutItemResult, + mediaInfo, troubleshootingInfo), cancellationToken); @@ -92,7 +101,6 @@ public class TroubleshootController( { return Redirect("~/iptv/session/.troubleshooting/live.m3u8"); } - } finally { @@ -141,5 +149,4 @@ public class TroubleshootController( return NotFound(); } - } diff --git a/ErsatzTV/Controllers/InternalController.cs b/ErsatzTV/Controllers/InternalController.cs index c633ea6d5..831a9e685 100644 --- a/ErsatzTV/Controllers/InternalController.cs +++ b/ErsatzTV/Controllers/InternalController.cs @@ -76,7 +76,7 @@ public class InternalController : ControllerBase if (!string.IsNullOrWhiteSpace(remoteStream.Script)) { - string[] split = remoteStream.Script.Split(" "); + string[] split = remoteStream.Script.Split(" "); if (split.Length > 0) { Command command = Cli.Wrap(split.Head()); @@ -112,7 +112,6 @@ public class InternalController : ControllerBase } return NotFound(); - } [HttpGet("/media/plex/{plexMediaSourceId:int}/{*path}")] @@ -317,13 +316,12 @@ public class InternalController : ControllerBase _logger.LogDebug("ffmpeg arguments {FFmpegArguments}", process.Arguments); var cts = new CancellationTokenSource(); - HttpContext.Response.OnCompleted( - async () => - { - ffmpegProcess.Dispose(); - await cts.CancelAsync(); - cts.Dispose(); - }); + HttpContext.Response.OnCompleted(async () => + { + ffmpegProcess.Dispose(); + await cts.CancelAsync(); + cts.Dispose(); + }); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( cts.Token, @@ -332,8 +330,8 @@ public class InternalController : ControllerBase var pipe = new Pipe(); var stdErrBuffer = new StringBuilder(); - var processWithPipe = process; - foreach (var graphicsEngineContext in processModel.GraphicsEngineContext) + Command processWithPipe = process; + foreach (GraphicsEngineContext graphicsEngineContext in processModel.GraphicsEngineContext) { var gePipe = new Pipe(); processWithPipe = process.WithStandardInputPipe(PipeSource.FromStream(gePipe.Reader.AsStream())); @@ -345,7 +343,7 @@ public class InternalController : ControllerBase linkedCts.Token); } - var task = processWithPipe + CommandTask task = processWithPipe .WithStandardOutputPipe(PipeTarget.ToStream(pipe.Writer.AsStream())) .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer)) .WithValidation(CommandResultValidation.None) @@ -357,7 +355,7 @@ public class InternalController : ControllerBase pipe.Writer, TaskScheduler.Default); - var contentType = mode switch + string contentType = mode switch { "segmenter-v2" => "video/x-matroska", _ => "video/mp2t" diff --git a/ErsatzTV/Controllers/IptvController.cs b/ErsatzTV/Controllers/IptvController.cs index 4150a5903..75148a8ee 100644 --- a/ErsatzTV/Controllers/IptvController.cs +++ b/ErsatzTV/Controllers/IptvController.cs @@ -84,7 +84,7 @@ public class IptvController : ControllerBase string mode = null) { Option maybeChannel = await _mediator.Send(new GetChannelByNumber(channelNumber)); - if (maybeChannel.IsNone || await maybeChannel.Map(c => c.IsEnabled).IfNoneAsync(false) == false) + if (maybeChannel.IsNone || !await maybeChannel.Map(c => c.IsEnabled).IfNoneAsync(false)) { return NotFound(); } @@ -187,7 +187,7 @@ public class IptvController : ControllerBase string mode = "mixed") { Option maybeChannel = await _mediator.Send(new GetChannelByNumber(channelNumber)); - if (maybeChannel.IsNone || await maybeChannel.Map(c => c.IsEnabled).IfNoneAsync(false) == false) + if (maybeChannel.IsNone || !await maybeChannel.Map(c => c.IsEnabled).IfNoneAsync(false)) { return NotFound(); } diff --git a/ErsatzTV/ErsatzTV.csproj b/ErsatzTV/ErsatzTV.csproj index 4315160a0..8e81988ee 100644 --- a/ErsatzTV/ErsatzTV.csproj +++ b/ErsatzTV/ErsatzTV.csproj @@ -110,4 +110,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV/Pages/Artist.razor b/ErsatzTV/Pages/Artist.razor index b4e3300f4..1e3430795 100644 --- a/ErsatzTV/Pages/Artist.razor +++ b/ErsatzTV/Pages/Artist.razor @@ -20,18 +20,18 @@ } else { - + } @if (!string.IsNullOrWhiteSpace(_artist.Thumbnail)) { - + } else { - + }
@@ -43,28 +43,28 @@ @_artist.Disambiguation - - - @if (!string.IsNullOrWhiteSpace(_artist.Biography)) - { - - @if (_artist.Biography.Length > 400) - { - @(_artist.Biography.Substring(0, 400) + "...") - } - else - { - @_artist.Biography - } - - } - else - { -
-
- } -
-
+ + + @if (!string.IsNullOrWhiteSpace(_artist.Biography)) + { + + @if (_artist.Biography.Length > 400) + { + @(_artist.Biography.Substring(0, 400) + "...") + } + else + { + @_artist.Biography + } + + } + else + { +
+
+ } +
+
- + @if (musicVideo.State == MediaItemState.FileNotFound) {
@@ -325,4 +325,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/ArtistList.razor b/ErsatzTV/Pages/ArtistList.razor index 1a13a2199..f060130b2 100644 --- a/ErsatzTV/Pages/ArtistList.razor +++ b/ErsatzTV/Pages/ArtistList.razor @@ -129,4 +129,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/BlockEditor.razor b/ErsatzTV/Pages/BlockEditor.razor index 2016530c8..dc83e90bf 100644 --- a/ErsatzTV/Pages/BlockEditor.razor +++ b/ErsatzTV/Pages/BlockEditor.razor @@ -11,301 +11,306 @@ @inject IMediator Mediator - -
-
- - Save Block - - - Add Block Item - - - Preview Block Playout - + +
+
+ + Save Block + + + Add Block Item + + + Preview Block Playout + +
+
+
+ + + + + +
+
+
+
+ +Block + + +
+ Block Group Name +
+ +
+ +
+ Block Name +
+ +
+ +
+ Duration +
+ +
+ +
+ + + + + + + + + + + + + + +
+ +
+ Stop scheduling block items +
+ + Before Duration End + After Duration End + +
+Block Items + + + + + + + + + + + + Collection + Playback Order + Show In EPG + Disable Watermarks + + + + + + @context.CollectionName + + + + + @context.PlaybackOrder + + + + + + + + + +
+ + + + + + + + +
+
+
+
+@if (_selectedItem is not null) +{ + Block Item + + +
+ Collection Type +
+ + Collection + Television Show + Television Season + @* Artist *@ + @* Multi Collection *@ + Smart Collection + +
+ @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) + { + +
+ Collection +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) + { + +
+ Multi Collection +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) + { + +
+ Smart Collection
-
-
- - - - - + + + + Only the first 10 items are shown + + + + + } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) + { + +
+ Television Show
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) + { + +
+ Television Season +
+ + + + Only the first 20 items are shown + + + +
+ } + + +
+ Playback Order
- -
- - Block - - -
- Block Group Name -
- -
- -
- Block Name -
- -
- -
- Duration -
- -
- -
- - - - - - - - - - - - - - -
- -
- Stop scheduling block items -
- - Before Duration End - After Duration End - -
- Block Items - - - - - - - - - - - - Collection - Playback Order - Show In EPG - Disable Watermarks - - - - - - @context.CollectionName - - - - - @context.PlaybackOrder - - - - - - - - - -
- - - - - - - - -
-
-
-
- @if (_selectedItem is not null) + + @switch (_selectedItem.CollectionType) { - Block Item - - -
- Collection Type -
- - Collection - Television Show - Television Season - @* Artist *@ - @* Multi Collection *@ - Smart Collection - -
- @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) - { - -
- Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) - { - -
- Multi Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) - { - -
- Smart Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) - { - -
- Television Show -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) - { - -
- Television Season -
- - - - Only the first 20 items are shown - - - -
- } - -
- Playback Order -
- - @switch (_selectedItem.CollectionType) - { - case ProgramScheduleItemCollectionType.MultiCollection: - Shuffle - @* Shuffle In Order *@ - break; - case ProgramScheduleItemCollectionType.Collection: - case ProgramScheduleItemCollectionType.SmartCollection: - Chronological - Shuffle - Random - Random Rotation - @* Shuffle In Order *@ - break; - case ProgramScheduleItemCollectionType.TelevisionShow: - Season, Episode - Chronological - Shuffle - Random - @* Multi-Episode Shuffle *@ - break; - case ProgramScheduleItemCollectionType.TelevisionSeason: - case ProgramScheduleItemCollectionType.Artist: - case ProgramScheduleItemCollectionType.FakeCollection: - default: - Chronological - Shuffle - Random - break; - } - -
+ case ProgramScheduleItemCollectionType.MultiCollection: + Shuffle + @* Shuffle In Order *@ + break; + case ProgramScheduleItemCollectionType.Collection: + case ProgramScheduleItemCollectionType.SmartCollection: + Chronological + Shuffle + Random + Random Rotation + @* Shuffle In Order *@ + break; + case ProgramScheduleItemCollectionType.TelevisionShow: + Season, Episode + Chronological + Shuffle + Random + @* Multi-Episode Shuffle *@ + break; + case ProgramScheduleItemCollectionType.TelevisionSeason: + case ProgramScheduleItemCollectionType.Artist: + case ProgramScheduleItemCollectionType.FakeCollection: + default: + Chronological + Shuffle + Random + break; } - else if (_previewItems != null) - { - Preview - - - - Start - Finish - Media Item - Duration - - - @context.Start.ToString(@"hh\:mm\:ss") - @context.Finish.ToString(@"hh\:mm\:ss") - @context.Title - @context.Duration - - - } -
-
+ +
+} +else if (_previewItems != null) +{ + Preview + + + + Start + Finish + Media Item + Duration + + + @context.Start.ToString(@"hh\:mm\:ss") + @context.Finish.ToString(@"hh\:mm\:ss") + @context.Title + @context.Duration + + +} + +
@code { @@ -541,4 +546,4 @@ private string SelectedRowClassFunc(BlockItemEditViewModel element, int rowNumber) => _selectedItem != null && _selectedItem == element ? "selected" : string.Empty; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/BlockPlayoutEditor.razor b/ErsatzTV/Pages/BlockPlayoutEditor.razor index af8a9000a..8f2a25224 100644 --- a/ErsatzTV/Pages/BlockPlayoutEditor.razor +++ b/ErsatzTV/Pages/BlockPlayoutEditor.razor @@ -157,10 +157,7 @@ Snackbar.Add($"Unexpected error saving default deco: {error.Value}", Severity.Error); Logger.LogError("Unexpected error saving default deco: {Error}", error.Value); }, - () => - { - Snackbar.Add($"Saved default deco for playout {_channelName}", Severity.Success); - }); + () => { Snackbar.Add($"Saved default deco for playout {_channelName}", Severity.Success); }); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Blocks.razor b/ErsatzTV/Pages/Blocks.razor index 72f2df190..4cde9d427 100644 --- a/ErsatzTV/Pages/Blocks.razor +++ b/ErsatzTV/Pages/Blocks.razor @@ -208,4 +208,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/ChannelEditor.razor b/ErsatzTV/Pages/ChannelEditor.razor index 01ccb8b9f..d48a87742 100644 --- a/ErsatzTV/Pages/ChannelEditor.razor +++ b/ErsatzTV/Pages/ChannelEditor.razor @@ -19,260 +19,260 @@ @inject IMediator Mediator - - @(IsEdit ? "Save Channel" : "Add Channel") - -
- - Channel - - -
- Number -
- -
- -
- Name -
- -
- -
- Group -
- -
- -
- Categories -
- -
- -
- Transcode Mode -
- - On Demand - -
- -
- Idle Behavior -
- - Stop On Disconnect - Keep Running - -
- -
- Playout Mode -
- - Continuous - On Demand - -
- -
- Is Enabled -
- -
- -
- Show In EPG -
- -
- -
- Streaming Mode -
- - MPEG-TS - MPEG-TS (Legacy) - HLS Direct - HLS Segmenter - HLS Segmenter V2 - -
- -
- FFmpeg Profile -
- - @foreach (FFmpegProfileViewModel profile in _ffmpegProfiles) - { - @profile.Name - } - -
- -
- Stream Selector Mode -
- - Default - Custom - -
- @if (_model.StreamSelectorMode is ChannelStreamSelectorMode.Default) + + @(IsEdit ? "Save Channel" : "Add Channel") + +
+ +Channel + + +
+ Number +
+ +
+ +
+ Name +
+ +
+ +
+ Group +
+ +
+ +
+ Categories +
+ +
+ +
+ Transcode Mode +
+ + On Demand + +
+ +
+ Idle Behavior +
+ + Stop On Disconnect + Keep Running + +
+ +
+ Playout Mode +
+ + Continuous + On Demand + +
+ +
+ Is Enabled +
+ +
+ +
+ Show In EPG +
+ +
+ +
+ Streaming Mode +
+ + MPEG-TS + MPEG-TS (Legacy) + HLS Direct + HLS Segmenter + HLS Segmenter V2 + +
+ +
+ FFmpeg Profile +
+ + @foreach (FFmpegProfileViewModel profile in _ffmpegProfiles) + { + @profile.Name + } + +
+ +
+ Stream Selector Mode +
+ + Default + Custom + +
+@if (_model.StreamSelectorMode is ChannelStreamSelectorMode.Default) +{ + +
+ Preferred Audio Language +
+ + (none) + @foreach (LanguageCodeViewModel culture in _availableCultures) { - -
- Preferred Audio Language -
- - (none) - @foreach (LanguageCodeViewModel culture in _availableCultures) - { - @culture.EnglishName - } - -
- -
- Preferred Audio Title -
- -
- -
- Preferred Subtitle Language -
- - (none) - @foreach (LanguageCodeViewModel culture in _availableCultures) - { - @culture.EnglishName - } - -
- -
- Subtitle Mode -
- - None - Forced - Default - Any - -
+ @culture.EnglishName } - else +
+
+ +
+ Preferred Audio Title +
+ +
+ +
+ Preferred Subtitle Language +
+ + (none) + @foreach (LanguageCodeViewModel culture in _availableCultures) { - -
- Stream Selector -
- - (none) - @foreach (string selector in _streamSelectors) - { - @selector - } - -
+ @culture.EnglishName } - -
- Music Video Credits Mode -
- - None - Generate Subtitles - -
- -
- Music Video Credits Template -
- - (none) - @foreach (string template in _musicVideoCreditsTemplates) - { - @template - } - -
- -
- Song Video Mode -
- - Default - With Progress - -
- -
- Logo -
-
- -
- External Logo URL -
- -
- -
- Logo Preview -
- @if (!string.IsNullOrWhiteSpace(_model.Logo?.Path) || !string.IsNullOrWhiteSpace(_model.ExternalLogoUrl)) - { - - } -
- -
- Watermark -
- - (none) - @foreach (WatermarkViewModel watermark in _watermarks) - { - @watermark.Name - } - -
- -
- Fallback Filler -
- - (none) - @foreach (FillerPresetViewModel fillerPreset in _fillerPresets) - { - @fillerPreset.Name - } - -
-
+ + + +
+ Subtitle Mode +
+ + None + Forced + Default + Any + +
+} +else +{ + +
+ Stream Selector +
+ + (none) + @foreach (string selector in _streamSelectors) + { + @selector + } + +
+} + +
+ Music Video Credits Mode +
+ + None + Generate Subtitles + +
+ +
+ Music Video Credits Template +
+ + (none) + @foreach (string template in _musicVideoCreditsTemplates) + { + @template + } + +
+ +
+ Song Video Mode +
+ + Default + With Progress + +
+ +
+ Logo
+
+ +
+ External Logo URL +
+ +
+ +
+ Logo Preview +
+ @if (!string.IsNullOrWhiteSpace(_model.Logo?.Path) || !string.IsNullOrWhiteSpace(_model.ExternalLogoUrl)) + { + + } +
+ +
+ Watermark +
+ + (none) + @foreach (WatermarkViewModel watermark in _watermarks) + { + @watermark.Name + } + +
+ +
+ Fallback Filler +
+ + (none) + @foreach (FillerPresetViewModel fillerPreset in _fillerPresets) + { + @fillerPreset.Name + } + +
+ +
@code { @@ -434,4 +434,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Channels.razor b/ErsatzTV/Pages/Channels.razor index a2a0e5595..198c891e9 100644 --- a/ErsatzTV/Pages/Channels.razor +++ b/ErsatzTV/Pages/Channels.razor @@ -264,4 +264,4 @@ _ => "MPEG-TS (Legacy)" }; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/CollectionEditor.razor b/ErsatzTV/Pages/CollectionEditor.razor index b65ad50c4..ac0c66b15 100644 --- a/ErsatzTV/Pages/CollectionEditor.razor +++ b/ErsatzTV/Pages/CollectionEditor.razor @@ -77,4 +77,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/CollectionItems.razor b/ErsatzTV/Pages/CollectionItems.razor index 87d408cc2..631d6b857 100644 --- a/ErsatzTV/Pages/CollectionItems.razor +++ b/ErsatzTV/Pages/CollectionItems.razor @@ -6,361 +6,361 @@ @inject IJSRuntime JsRuntime - -
- @if (IsSelectMode()) - { -
- @SelectionLabel() -
-
- - Remove From Collection - - - Clear Selection - -
-
-
- - - - -
- } - else - { -
- @_data?.Name -
- - @if (_data?.MovieCards.Count > 0) - { - @_data.MovieCards.Count Movies - } - - @if (_data?.ShowCards.Count > 0) - { - @_data.ShowCards.Count Shows - } - - @if (_data?.SeasonCards.Count > 0) - { - @_data.SeasonCards.Count Seasons - } - - @if (_data?.EpisodeCards.Count > 0) - { - @_data.EpisodeCards.Count Episodes - } - - @if (_data?.ArtistCards.Count > 0) - { - @_data.ArtistCards.Count Artists - } - - @if (_data?.MusicVideoCards.Count > 0) - { - @_data.MusicVideoCards.Count Music Videos - } - - @if (_data?.OtherVideoCards.Count > 0) - { - @_data.OtherVideoCards.Count Other Videos - } - - @if (_data?.SongCards.Count > 0) - { - @_data.SongCards.Count Songs - } - - @if (_data?.ImageCards.Count > 0) - { - @_data.ImageCards.Count Images - } - - @if (_data?.RemoteStreamCards.Count > 0) - { - @_data.RemoteStreamCards.Count Remote Streams - } -
- @if (SupportsCustomOrdering()) - { -
- -
- } -
- -
-
- } -
-
-
- - @if (_data?.MovieCards.Count > 0) - { - - Movies - - - - - @foreach (MovieCardViewModel card in OrderMovies(_data.MovieCards)) + +
+ @if (IsSelectMode()) + { +
+ @SelectionLabel() +
+
+ + Remove From Collection + + + Clear Selection + +
+
+
+ + + + +
+ } + else + { +
+ @_data?.Name +
+ + @if (_data?.MovieCards.Count > 0) { - + @_data.MovieCards.Count Movies } - - } - @if (_data?.ShowCards.Count > 0) - { - - Shows - - - - - @foreach (TelevisionShowCardViewModel card in _data.ShowCards.OrderBy(m => m.SortTitle)) + @if (_data?.ShowCards.Count > 0) { - + @_data.ShowCards.Count Shows } - - } - @if (_data?.SeasonCards.Count > 0) - { - - Seasons - - - - - @foreach (TelevisionSeasonCardViewModel card in _data.SeasonCards.OrderBy(m => m.SortTitle)) + @if (_data?.SeasonCards.Count > 0) { - + @_data.SeasonCards.Count Seasons } - - } - @if (_data?.EpisodeCards.Count > 0) - { - - Episodes - - - - - @foreach (TelevisionEpisodeCardViewModel card in _data.EpisodeCards.OrderBy(e => e.Aired)) + @if (_data?.EpisodeCards.Count > 0) { - + @_data.EpisodeCards.Count Episodes } - - } - @if (_data?.ArtistCards.Count > 0) - { - - Artists - - - - - @foreach (ArtistCardViewModel card in _data.ArtistCards.OrderBy(e => e.SortTitle)) + @if (_data?.ArtistCards.Count > 0) { - + @_data.ArtistCards.Count Artists } - - } - @if (_data?.MusicVideoCards.Count > 0) - { - - Music Videos - - - - - @foreach (MusicVideoCardViewModel card in _data.MusicVideoCards.OrderBy(e => e.SortTitle)) + @if (_data?.MusicVideoCards.Count > 0) { - + @_data.MusicVideoCards.Count Music Videos } - - } - @if (_data?.OtherVideoCards.Count > 0) - { - - Other Videos - - - - - @foreach (OtherVideoCardViewModel card in _data.OtherVideoCards.OrderBy(e => e.SortTitle)) + @if (_data?.OtherVideoCards.Count > 0) { - + @_data.OtherVideoCards.Count Other Videos } - - } - @if (_data?.SongCards.Count > 0) - { - - Songs - - - - - @foreach (SongCardViewModel card in _data.SongCards.OrderBy(e => e.SortTitle)) + @if (_data?.SongCards.Count > 0) { - + @_data.SongCards.Count Songs } - - } - @if (_data?.ImageCards.Count > 0) - { - - Images - - - - - @foreach (ImageCardViewModel card in _data.ImageCards.OrderBy(e => e.SortTitle)) + @if (_data?.ImageCards.Count > 0) { - + @_data.ImageCards.Count Images } - - } - @if (_data?.RemoteStreamCards.Count > 0) - { - - Remote Streams - - - - - @foreach (RemoteStreamCardViewModel card in _data.RemoteStreamCards.OrderBy(e => e.SortTitle)) + @if (_data?.RemoteStreamCards.Count > 0) { - + @_data.RemoteStreamCards.Count Remote Streams } - - } - +
+ @if (SupportsCustomOrdering()) + { +
+ +
+ } +
+ +
+
+ }
+
+
+ + @if (_data?.MovieCards.Count > 0) + { + + Movies + + + + + @foreach (MovieCardViewModel card in OrderMovies(_data.MovieCards)) + { + + } + + } + + @if (_data?.ShowCards.Count > 0) + { + + Shows + + + + + @foreach (TelevisionShowCardViewModel card in _data.ShowCards.OrderBy(m => m.SortTitle)) + { + + } + + } + + @if (_data?.SeasonCards.Count > 0) + { + + Seasons + + + + + @foreach (TelevisionSeasonCardViewModel card in _data.SeasonCards.OrderBy(m => m.SortTitle)) + { + + } + + } + + @if (_data?.EpisodeCards.Count > 0) + { + + Episodes + + + + + @foreach (TelevisionEpisodeCardViewModel card in _data.EpisodeCards.OrderBy(e => e.Aired)) + { + + } + + } + + @if (_data?.ArtistCards.Count > 0) + { + + Artists + + + + + @foreach (ArtistCardViewModel card in _data.ArtistCards.OrderBy(e => e.SortTitle)) + { + + } + + } + + @if (_data?.MusicVideoCards.Count > 0) + { + + Music Videos + + + + + @foreach (MusicVideoCardViewModel card in _data.MusicVideoCards.OrderBy(e => e.SortTitle)) + { + + } + + } + + @if (_data?.OtherVideoCards.Count > 0) + { + + Other Videos + + + + + @foreach (OtherVideoCardViewModel card in _data.OtherVideoCards.OrderBy(e => e.SortTitle)) + { + + } + + } + + @if (_data?.SongCards.Count > 0) + { + + Songs + + + + + @foreach (SongCardViewModel card in _data.SongCards.OrderBy(e => e.SortTitle)) + { + + } + + } + + @if (_data?.ImageCards.Count > 0) + { + + Images + + + + + @foreach (ImageCardViewModel card in _data.ImageCards.OrderBy(e => e.SortTitle)) + { + + } + + } + + @if (_data?.RemoteStreamCards.Count > 0) + { + + Remote Streams + + + + + @foreach (RemoteStreamCardViewModel card in _data.RemoteStreamCards.OrderBy(e => e.SortTitle)) + { + + } + + } + +
@code { @@ -589,4 +589,4 @@ await Mediator.Send(request, CancellationToken); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Collections.razor b/ErsatzTV/Pages/Collections.razor index 8938ea6a1..2bec0639f 100644 --- a/ErsatzTV/Pages/Collections.razor +++ b/ErsatzTV/Pages/Collections.razor @@ -256,4 +256,4 @@ return new TableData { TotalItems = data.TotalCount, Items = data.Page }; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/DecoEditor.razor b/ErsatzTV/Pages/DecoEditor.razor index 07fd8072f..10987c7d2 100644 --- a/ErsatzTV/Pages/DecoEditor.razor +++ b/ErsatzTV/Pages/DecoEditor.razor @@ -13,325 +13,325 @@ @inject IMediator Mediator - - - Save Changes - - -
- - Deco - - -
- Deco Group Name -
- -
- -
- Deco Name -
- -
- Watermark - - -
- Watermark Mode -
- - Inherit - Disable - Override - -
- -
- Watermark Override -
- - @foreach (WatermarkViewModel watermark in _watermarks) - { - @watermark.Name - } - -
- -
- Use Watermark During Filler -
- -
- Default Filler - - After all blocks have been scheduled, a second pass will be made to fill unscheduled time using random items from this collection. - -
- Default Filler Mode -
- - Inherit - Disable - Override - -
- -
- Default Filler Collection Type -
- - Collection - Television Show - Television Season - Artist - Multi Collection - Smart Collection - -
- @if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.Collection) + + + Save Changes + + +
+ +Deco + + +
+ Deco Group Name +
+ +
+ +
+ Deco Name +
+ +
+Watermark + + +
+ Watermark Mode +
+ + Inherit + Disable + Override + +
+ +
+ Watermark Override +
+ + @foreach (WatermarkViewModel watermark in _watermarks) + { + @watermark.Name + } + +
+ +
+ Use Watermark During Filler +
+ +
+Default Filler + +After all blocks have been scheduled, a second pass will be made to fill unscheduled time using random items from this collection. + +
+ Default Filler Mode +
+ + Inherit + Disable + Override + +
+ +
+ Default Filler Collection Type +
+ + Collection + Television Show + Television Season + Artist + Multi Collection + Smart Collection + +
+@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.Collection) +{ + +
+ Collection +
+ + @foreach (MediaCollectionViewModel collection in _mediaCollections) { - -
- Collection -
- - @foreach (MediaCollectionViewModel collection in _mediaCollections) - { - @collection.Name - } - -
+ @collection.Name } - @if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.MultiCollection) +
+
+} +@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.MultiCollection) +{ + +
+ Multi Collection +
+ + @foreach (MultiCollectionViewModel collection in _multiCollections) { - -
- Multi Collection -
- - @foreach (MultiCollectionViewModel collection in _multiCollections) - { - @collection.Name - } - -
+ @collection.Name } - @if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.SmartCollection) +
+
+} +@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.SmartCollection) +{ + +
+ Smart Collection +
+ + @foreach (SmartCollectionViewModel collection in _smartCollections) { - -
- Smart Collection -
- - @foreach (SmartCollectionViewModel collection in _smartCollections) - { - @collection.Name - } - -
+ @collection.Name } - @if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.TelevisionShow) +
+
+} +@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.TelevisionShow) +{ + +
+ Television Show +
+ + @foreach (NamedMediaItemViewModel show in _televisionShows) { - -
- Television Show -
- - @foreach (NamedMediaItemViewModel show in _televisionShows) - { - @show.Name - } - -
+ @show.Name } - @if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) +
+
+} +@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) +{ + +
+ Television Season +
+ + @foreach (NamedMediaItemViewModel season in _televisionSeasons) { - -
- Television Season -
- - @foreach (NamedMediaItemViewModel season in _televisionSeasons) - { - @season.Name - } - -
+ @season.Name } - @if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.Artist) +
+
+} +@if (_deco.DefaultFillerCollectionType == ProgramScheduleItemCollectionType.Artist) +{ + +
+ Artist +
+ + @foreach (NamedMediaItemViewModel artist in _artists) { - -
- Artist -
- - @foreach (NamedMediaItemViewModel artist in _artists) - { - @artist.Name - } - -
+ @artist.Name } - -
- Trim To Fit -
- -
- Dead Air Fallback - - When no playout item is found for the current time, *one* item will be randomly selected from this collection and looped and trimmed to exactly fit until the start of the next playout item. - This replaces the "Channel is Offline" image that would otherwise display. - -
- Dead Air Fallback Mode -
- - Inherit - Disable - Override - -
- -
- Dead Air Fallback Collection Type -
- - Collection - Television Show - Television Season - Artist - Multi Collection - Smart Collection - -
- @if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.Collection) +
+
+} + +
+ Trim To Fit +
+ +
+Dead Air Fallback + +When no playout item is found for the current time, *one* item will be randomly selected from this collection and looped and trimmed to exactly fit until the start of the next playout item. +This replaces the "Channel is Offline" image that would otherwise display. + +
+ Dead Air Fallback Mode +
+ + Inherit + Disable + Override + +
+ +
+ Dead Air Fallback Collection Type +
+ + Collection + Television Show + Television Season + Artist + Multi Collection + Smart Collection + +
+@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.Collection) +{ + +
+ Collection +
+ + @foreach (MediaCollectionViewModel collection in _mediaCollections) { - -
- Collection -
- - @foreach (MediaCollectionViewModel collection in _mediaCollections) - { - @collection.Name - } - -
+ @collection.Name } - @if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.MultiCollection) +
+
+} +@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.MultiCollection) +{ + +
+ Multi Collection +
+ + @foreach (MultiCollectionViewModel collection in _multiCollections) { - -
- Multi Collection -
- - @foreach (MultiCollectionViewModel collection in _multiCollections) - { - @collection.Name - } - -
+ @collection.Name } - @if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.SmartCollection) +
+
+} +@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.SmartCollection) +{ + +
+ Smart Collection +
+ + @foreach (SmartCollectionViewModel collection in _smartCollections) { - -
- Smart Collection -
- - @foreach (SmartCollectionViewModel collection in _smartCollections) - { - @collection.Name - } - -
+ @collection.Name } - @if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.TelevisionShow) +
+
+} +@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.TelevisionShow) +{ + +
+ Television Show +
+ + @foreach (NamedMediaItemViewModel show in _televisionShows) { - -
- Television Show -
- - @foreach (NamedMediaItemViewModel show in _televisionShows) - { - @show.Name - } - -
+ @show.Name } - @if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) +
+
+} +@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) +{ + +
+ Television Season +
+ + @foreach (NamedMediaItemViewModel season in _televisionSeasons) { - -
- Television Season -
- - @foreach (NamedMediaItemViewModel season in _televisionSeasons) - { - @season.Name - } - -
+ @season.Name } - @if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.Artist) +
+
+} +@if (_deco.DeadAirFallbackCollectionType == ProgramScheduleItemCollectionType.Artist) +{ + +
+ Artist +
+ + @foreach (NamedMediaItemViewModel artist in _artists) { - -
- Artist -
- - @foreach (NamedMediaItemViewModel artist in _artists) - { - @artist.Name - } - -
+ @artist.Name } -
-
+ + +} +
+
@code { @@ -473,4 +473,4 @@ () => NavigationManager.NavigateTo("decos")); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/DecoTemplateEditor.razor b/ErsatzTV/Pages/DecoTemplateEditor.razor index 9cea5648c..44a36b433 100644 --- a/ErsatzTV/Pages/DecoTemplateEditor.razor +++ b/ErsatzTV/Pages/DecoTemplateEditor.razor @@ -225,7 +225,7 @@ while (maybeStart.Date == DateTime.Today) { DateTime maybeEnd = maybeStart.AddHours(_durationHours).AddMinutes(_durationMinutes); - if (IntersectsOthers(null, maybeStart, maybeEnd) == false) + if (!IntersectsOthers(null, maybeStart, maybeEnd)) { var item = new DecoTemplateItemEditViewModel { @@ -297,7 +297,7 @@ } } - return willFit == false; + return !willFit; } // private void RemoveDecoTemplateItem(DecoTemplateItemEditViewModel item) @@ -328,4 +328,4 @@ () => NavigationManager.NavigateTo("deco-templates")); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/DecoTemplates.razor b/ErsatzTV/Pages/DecoTemplates.razor index 06c54a555..4727745d7 100644 --- a/ErsatzTV/Pages/DecoTemplates.razor +++ b/ErsatzTV/Pages/DecoTemplates.razor @@ -203,4 +203,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Decos.razor b/ErsatzTV/Pages/Decos.razor index 82ba08f1d..a8902176a 100644 --- a/ErsatzTV/Pages/Decos.razor +++ b/ErsatzTV/Pages/Decos.razor @@ -203,4 +203,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/EmbyLibrariesEditor.razor b/ErsatzTV/Pages/EmbyLibrariesEditor.razor index 48069a44d..b6de4182c 100644 --- a/ErsatzTV/Pages/EmbyLibrariesEditor.razor +++ b/ErsatzTV/Pages/EmbyLibrariesEditor.razor @@ -52,4 +52,4 @@ return Unit.Default; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/EmbyMediaSourceEditor.razor b/ErsatzTV/Pages/EmbyMediaSourceEditor.razor index 78146e8b2..878cda0af 100644 --- a/ErsatzTV/Pages/EmbyMediaSourceEditor.razor +++ b/ErsatzTV/Pages/EmbyMediaSourceEditor.razor @@ -32,4 +32,4 @@ return await Mediator.Send(new SaveEmbySecrets(secrets), _cts.Token); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/EmbyMediaSources.razor b/ErsatzTV/Pages/EmbyMediaSources.razor index 36a66c74f..c110b52f1 100644 --- a/ErsatzTV/Pages/EmbyMediaSources.razor +++ b/ErsatzTV/Pages/EmbyMediaSources.razor @@ -28,4 +28,4 @@ private async Task RefreshLibraries(int mediaSourceId) => await ScannerWorkerChannel.WriteAsync(new SynchronizeEmbyLibraries(mediaSourceId), _cts.Token); -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/EmbyPathReplacementsEditor.razor b/ErsatzTV/Pages/EmbyPathReplacementsEditor.razor index 31cefc3bc..7bf7d878d 100644 --- a/ErsatzTV/Pages/EmbyPathReplacementsEditor.razor +++ b/ErsatzTV/Pages/EmbyPathReplacementsEditor.razor @@ -43,4 +43,4 @@ return new UpdateEmbyPathReplacements(Id, items); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/EpisodeList.razor b/ErsatzTV/Pages/EpisodeList.razor index cabee6bd7..2244cb39a 100644 --- a/ErsatzTV/Pages/EpisodeList.razor +++ b/ErsatzTV/Pages/EpisodeList.razor @@ -129,4 +129,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/FFmpeg.razor b/ErsatzTV/Pages/FFmpeg.razor index 83d95d8bf..cc31da491 100644 --- a/ErsatzTV/Pages/FFmpeg.razor +++ b/ErsatzTV/Pages/FFmpeg.razor @@ -114,4 +114,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/FFmpegEditor.razor b/ErsatzTV/Pages/FFmpegEditor.razor index 31faa6f6b..8461fb90f 100644 --- a/ErsatzTV/Pages/FFmpegEditor.razor +++ b/ErsatzTV/Pages/FFmpegEditor.razor @@ -413,4 +413,4 @@ _ => VideoFormat.Mpeg2Video }; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/FillerPresetEditor.razor b/ErsatzTV/Pages/FillerPresetEditor.razor index 00fc997d6..a15e4ab0e 100644 --- a/ErsatzTV/Pages/FillerPresetEditor.razor +++ b/ErsatzTV/Pages/FillerPresetEditor.razor @@ -233,19 +233,19 @@
total_points: total number of potential mid-roll points -
+
matched_points: number of mid-roll points that have already matched the expression -
+
total_duration: total duration of the content, in seconds -
+
total_progress: normalized position from 0 to 1 -
+
last_mid_filler: seconds since last mid-roll filler -
+
remaining_duration: duration of the content after this mid-roll point, in seconds -
+
point: the position of the mid-roll point, in seconds -
+
num: the mid-roll point number, starting with 1
@@ -282,18 +282,24 @@ protected override async Task OnParametersSetAsync() { - _mediaCollections.AddRange(await Mediator.Send(new GetAllCollections(), _cts.Token) - .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); - _multiCollections.AddRange(await Mediator.Send(new GetAllMultiCollections(), _cts.Token) - .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); - _smartCollections.AddRange(await Mediator.Send(new GetAllSmartCollections(), _cts.Token) - .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); - _televisionShows.AddRange(await Mediator.Send(new GetAllTelevisionShows(), _cts.Token) - .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); - _televisionSeasons.AddRange(await Mediator.Send(new GetAllTelevisionSeasons(), _cts.Token) - .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); - _artists.AddRange(await Mediator.Send(new GetAllArtists(), _cts.Token) - .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); + _mediaCollections.AddRange( + await Mediator.Send(new GetAllCollections(), _cts.Token) + .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); + _multiCollections.AddRange( + await Mediator.Send(new GetAllMultiCollections(), _cts.Token) + .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); + _smartCollections.AddRange( + await Mediator.Send(new GetAllSmartCollections(), _cts.Token) + .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); + _televisionShows.AddRange( + await Mediator.Send(new GetAllTelevisionShows(), _cts.Token) + .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); + _televisionSeasons.AddRange( + await Mediator.Send(new GetAllTelevisionSeasons(), _cts.Token) + .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); + _artists.AddRange( + await Mediator.Send(new GetAllArtists(), _cts.Token) + .Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList())); _playlistGroups.AddRange(await Mediator.Send(new GetAllPlaylistGroups(), _cts.Token)); @@ -375,4 +381,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/FillerPresets.razor b/ErsatzTV/Pages/FillerPresets.razor index 298e74d70..d9cdd52a7 100644 --- a/ErsatzTV/Pages/FillerPresets.razor +++ b/ErsatzTV/Pages/FillerPresets.razor @@ -112,4 +112,4 @@ return new TableData { TotalItems = data.TotalCount, Items = data.Page }; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/ImageBrowser.razor b/ErsatzTV/Pages/ImageBrowser.razor index 0775f5d82..d3358081d 100644 --- a/ErsatzTV/Pages/ImageBrowser.razor +++ b/ErsatzTV/Pages/ImageBrowser.razor @@ -101,4 +101,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/ImageList.razor b/ErsatzTV/Pages/ImageList.razor index fad1d89d6..16de10651 100644 --- a/ErsatzTV/Pages/ImageList.razor +++ b/ErsatzTV/Pages/ImageList.razor @@ -129,4 +129,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Index.razor b/ErsatzTV/Pages/Index.razor index 6bbffa49b..6ee3d46c1 100644 --- a/ErsatzTV/Pages/Index.razor +++ b/ErsatzTV/Pages/Index.razor @@ -231,4 +231,4 @@ }; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/JellyfinLibrariesEditor.razor b/ErsatzTV/Pages/JellyfinLibrariesEditor.razor index a1b711ea9..0b7a20e92 100644 --- a/ErsatzTV/Pages/JellyfinLibrariesEditor.razor +++ b/ErsatzTV/Pages/JellyfinLibrariesEditor.razor @@ -52,4 +52,4 @@ return Unit.Default; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/JellyfinMediaSourceEditor.razor b/ErsatzTV/Pages/JellyfinMediaSourceEditor.razor index a440018fc..0b4f0bc0c 100644 --- a/ErsatzTV/Pages/JellyfinMediaSourceEditor.razor +++ b/ErsatzTV/Pages/JellyfinMediaSourceEditor.razor @@ -32,4 +32,4 @@ return await Mediator.Send(new SaveJellyfinSecrets(secrets), _cts.Token); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/JellyfinMediaSources.razor b/ErsatzTV/Pages/JellyfinMediaSources.razor index dcde3228f..b6be9e9d6 100644 --- a/ErsatzTV/Pages/JellyfinMediaSources.razor +++ b/ErsatzTV/Pages/JellyfinMediaSources.razor @@ -28,4 +28,4 @@ private async Task RefreshLibraries(int mediaSourceId) => await ScannerWorkerChannel.WriteAsync(new SynchronizeJellyfinLibraries(mediaSourceId), _cts.Token); -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/JellyfinPathReplacementsEditor.razor b/ErsatzTV/Pages/JellyfinPathReplacementsEditor.razor index 95ff216a0..4fca6ed4d 100644 --- a/ErsatzTV/Pages/JellyfinPathReplacementsEditor.razor +++ b/ErsatzTV/Pages/JellyfinPathReplacementsEditor.razor @@ -43,4 +43,4 @@ return new UpdateJellyfinPathReplacements(Id, items); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Libraries.razor b/ErsatzTV/Pages/Libraries.razor index 8381e7111..8abc18610 100644 --- a/ErsatzTV/Pages/Libraries.razor +++ b/ErsatzTV/Pages/Libraries.razor @@ -289,4 +289,4 @@ _cts.Dispose(); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/LocalLibraries.razor b/ErsatzTV/Pages/LocalLibraries.razor index 9f9cca1ae..f3fe98e46 100644 --- a/ErsatzTV/Pages/LocalLibraries.razor +++ b/ErsatzTV/Pages/LocalLibraries.razor @@ -110,4 +110,4 @@ } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/LocalLibraryEditor.razor b/ErsatzTV/Pages/LocalLibraryEditor.razor index 45078bfdd..283dbe1a4 100644 --- a/ErsatzTV/Pages/LocalLibraryEditor.razor +++ b/ErsatzTV/Pages/LocalLibraryEditor.razor @@ -125,10 +125,7 @@ } } - protected override void OnInitialized() - { - Locker.OnLibraryChanged += LockChanged; - } + protected override void OnInitialized() => Locker.OnLibraryChanged += LockChanged; private void LockChanged(object sender, EventArgs e) => InvokeAsync(StateHasChanged); @@ -259,4 +256,4 @@ _cts.Dispose(); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Logs.razor b/ErsatzTV/Pages/Logs.razor index 45a882afb..3a9dd5bcf 100644 --- a/ErsatzTV/Pages/Logs.razor +++ b/ErsatzTV/Pages/Logs.razor @@ -15,7 +15,7 @@ Search
+ AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Medium"/> - + @if (_movie.MediaItemState == MediaItemState.FileNotFound) {
@@ -326,9 +326,6 @@ } } - private static string GetPosterUrl(string poster) - { - return poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/posters/{poster}"; - } + private static string GetPosterUrl(string poster) => poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/posters/{poster}"; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/MovieList.razor b/ErsatzTV/Pages/MovieList.razor index c74a853d6..9f137a837 100644 --- a/ErsatzTV/Pages/MovieList.razor +++ b/ErsatzTV/Pages/MovieList.razor @@ -158,4 +158,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/MultiCollectionEditor.razor b/ErsatzTV/Pages/MultiCollectionEditor.razor index 205bd19eb..0d23555dd 100644 --- a/ErsatzTV/Pages/MultiCollectionEditor.razor +++ b/ErsatzTV/Pages/MultiCollectionEditor.razor @@ -253,4 +253,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/MusicVideoList.razor b/ErsatzTV/Pages/MusicVideoList.razor index f99871cab..0cc793c05 100644 --- a/ErsatzTV/Pages/MusicVideoList.razor +++ b/ErsatzTV/Pages/MusicVideoList.razor @@ -129,4 +129,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/OtherVideoList.razor b/ErsatzTV/Pages/OtherVideoList.razor index b5beb569c..ff59a7457 100644 --- a/ErsatzTV/Pages/OtherVideoList.razor +++ b/ErsatzTV/Pages/OtherVideoList.razor @@ -129,4 +129,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlaybackTroubleshooting.razor b/ErsatzTV/Pages/PlaybackTroubleshooting.razor index f628fe890..f0394d147 100644 --- a/ErsatzTV/Pages/PlaybackTroubleshooting.razor +++ b/ErsatzTV/Pages/PlaybackTroubleshooting.razor @@ -34,13 +34,13 @@
Media Item ID
- +
Title
- +
Playback Settings @@ -96,13 +96,13 @@ + ValueChanged="@(c => OnStartFromBeginningChanged(c))"/>
Seek Seconds
- +
Preview @@ -129,8 +129,8 @@
-
-
+
+
@@ -202,24 +202,27 @@ var uri = new UriBuilder(NavigationManager.ToAbsoluteUri(NavigationManager.Uri)); uri.Path = uri.Path.Replace("/system/troubleshooting/playback", "/api/troubleshoot/playback.m3u8"); uri.Query = $"?mediaItem={MediaItemId}&ffmpegProfile={_ffmpegProfileId}&seekSeconds={_seekSeconds}"; - foreach (var watermarkName in _watermarkNames) + foreach (string watermarkName in _watermarkNames) { - foreach (var watermark in _watermarks.Where(wm => wm.Name == watermarkName)) + foreach (WatermarkViewModel watermark in _watermarks.Where(wm => wm.Name == watermarkName)) { uri.Query += $"&watermark={watermark.Id}"; } } - foreach (var graphicsElementName in _graphicsElementNames) + + foreach (string graphicsElementName in _graphicsElementNames) { - foreach (var graphicsElement in _graphicsElements.Where(ge => ge.Name == graphicsElementName)) + foreach (GraphicsElementViewModel graphicsElement in _graphicsElements.Where(ge => ge.Name == graphicsElementName)) { uri.Query += $"&graphicsElement={graphicsElement.Id}"; } } + if (_subtitleId is not null) { uri.Query += $"&subtitleId={_subtitleId.Value}"; } + await JsRuntime.InvokeVoidAsync("previewChannel", uri.ToString()); await Task.Delay(TimeSpan.FromSeconds(1)); @@ -258,17 +261,17 @@ { var uri = $"api/troubleshoot/playback/archive?mediaItem={MediaItemId ?? 0}&ffmpegProfile={_ffmpegProfileId}&seekSeconds={_seekSeconds}"; - foreach (var watermarkName in _watermarkNames) + foreach (string watermarkName in _watermarkNames) { - foreach (var watermark in _watermarks.Where(wm => wm.Name == watermarkName)) + foreach (WatermarkViewModel watermark in _watermarks.Where(wm => wm.Name == watermarkName)) { uri += $"&watermark={watermark.Id}"; } } - foreach (var graphicsElementName in _graphicsElementNames) + foreach (string graphicsElementName in _graphicsElementNames) { - foreach (var graphicsElement in _graphicsElements.Where(ge => ge.Name == graphicsElementName)) + foreach (GraphicsElementViewModel graphicsElement in _graphicsElements.Where(ge => ge.Name == graphicsElementName)) { uri += $"&graphicsElement={graphicsElement.Id}"; } @@ -289,4 +292,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlaylistEditor.razor b/ErsatzTV/Pages/PlaylistEditor.razor index 11745c2d8..f6183a05a 100644 --- a/ErsatzTV/Pages/PlaylistEditor.razor +++ b/ErsatzTV/Pages/PlaylistEditor.razor @@ -10,314 +10,321 @@ @inject IMediator Mediator - -
-
- - Save Playlist - - - Add Playlist Item - - - Preview Playlist Playout - + +
+
+ + Save Playlist + + + Add Playlist Item + + + Preview Playlist Playout + +
+
+
+ + + + + +
+
+
+
+ +Playlist + + +
+ Name +
+ +
+Playlist Items + + + + + + + + + + + + + + Item Type + Item + Playback Order + Play All + Show In EPG + + + + + + @context.ItemType + + + + + @context.ItemName + + + + + @(context.PlaybackOrder > 0 ? context.PlaybackOrder : "") + + + + + + + + + +
+ + + + + + + + +
+
+
+
+@if (!_playlist.IsSystem && _selectedItem is not null) +{ + Playlist Item + + +
+ Collection Type +
+ + Collection + Television Show + Television Season + Artist + Multi Collection + Smart Collection + Movie + Episode + Music Video + Other Video + Song + Image + +
+ @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) + { + +
+ Collection +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) + { + +
+ Multi Collection
-
-
- - - - - + + + + Only the first 10 items are shown + + + + + } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) + { + +
+ Smart Collection +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) + { + +
+ Television Show
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) + { + +
+ Television Season +
+ + + + Only the first 20 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Artist) + { + +
+ Artist +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Movie) + { + +
+ Movie +
+ + + + Only the first 10 items are shown + + + +
+ } + + +
+ Playback Order
- -
- - Playlist - - -
- Name -
- -
- Playlist Items - - - - - - - - - - - - - - Item Type - Item - Playback Order - Play All - Show In EPG - - - - - - @context.ItemType - - - - - @context.ItemName - - - - - @(context.PlaybackOrder > 0 ? context.PlaybackOrder : "") - - - - - - - - - -
- - - - - - - - -
-
-
-
- @if (!_playlist.IsSystem && _selectedItem is not null) - { - Playlist Item - - -
- Collection Type -
- - Collection - Television Show - Television Season - Artist - Multi Collection - Smart Collection - Movie - Episode - Music Video - Other Video - Song - Image - -
- @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) - { - -
- Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) - { - -
- Multi Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) - { - -
- Smart Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) - { - -
- Television Show -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) - { - -
- Television Season -
- - - - Only the first 20 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Artist) - { - -
- Artist -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Movie) - { - -
- Movie -
- - - - Only the first 10 items are shown - - - -
- } - -
- Playback Order -
- - @switch (_selectedItem.CollectionType) - { - case ProgramScheduleItemCollectionType.MultiCollection: - Shuffle - @* Shuffle In Order *@ - break; - case ProgramScheduleItemCollectionType.Collection: - case ProgramScheduleItemCollectionType.SmartCollection: - Chronological - Shuffle - Random - @* Shuffle In Order *@ - break; - case ProgramScheduleItemCollectionType.TelevisionShow: - Season, Episode - Chronological - Shuffle - Random - @* Multi-Episode Shuffle *@ - break; - case ProgramScheduleItemCollectionType.TelevisionSeason: - case ProgramScheduleItemCollectionType.Artist: - case ProgramScheduleItemCollectionType.FakeCollection: - default: - Chronological - Shuffle - Random - break; - } - -
- } - else if (_previewItems is not null) + + @switch (_selectedItem.CollectionType) { - Preview - - - - Playlist Playout Preview - - - Start - Finish - Media Item - Duration - - - @context.Start.ToString(@"hh\:mm\:ss") - @context.Finish.ToString(@"hh\:mm\:ss") - @context.Title - @context.Duration - - + case ProgramScheduleItemCollectionType.MultiCollection: + Shuffle + @* Shuffle In Order *@ + break; + case ProgramScheduleItemCollectionType.Collection: + case ProgramScheduleItemCollectionType.SmartCollection: + Chronological + Shuffle + Random + @* Shuffle In Order *@ + break; + case ProgramScheduleItemCollectionType.TelevisionShow: + Season, Episode + Chronological + Shuffle + Random + @* Multi-Episode Shuffle *@ + break; + case ProgramScheduleItemCollectionType.TelevisionSeason: + case ProgramScheduleItemCollectionType.Artist: + case ProgramScheduleItemCollectionType.FakeCollection: + default: + Chronological + Shuffle + Random + break; } -
-
+ +
+} +else if (_previewItems is not null) +{ + Preview + + + + Playlist Playout Preview + + + Start + Finish + Media Item + Duration + + + @context.Start.ToString(@"hh\:mm\:ss") + @context.Finish.ToString(@"hh\:mm\:ss") + @context.Title + @context.Duration + + +} + +
@code { @@ -552,4 +559,4 @@ private static void UpdatePlayAll(PlaylistItemEditViewModel context, bool playAll) => context.PlayAll = playAll; private string SelectedRowClassFunc(PlaylistItemEditViewModel element, int rowNumber) => _selectedItem != null && _selectedItem == element ? "selected" : string.Empty; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Playlists.razor b/ErsatzTV/Pages/Playlists.razor index bb908ec3a..7b0a2296f 100644 --- a/ErsatzTV/Pages/Playlists.razor +++ b/ErsatzTV/Pages/Playlists.razor @@ -220,4 +220,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlayoutAlternateSchedulesEditor.razor b/ErsatzTV/Pages/PlayoutAlternateSchedulesEditor.razor index 6b99af12f..a77f82a9c 100644 --- a/ErsatzTV/Pages/PlayoutAlternateSchedulesEditor.razor +++ b/ErsatzTV/Pages/PlayoutAlternateSchedulesEditor.razor @@ -373,4 +373,4 @@ _selectedItem.MonthsOfYear.AddRange(selectedMonths); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlayoutEditor.razor b/ErsatzTV/Pages/PlayoutEditor.razor index b7bb5fbb8..68427c162 100644 --- a/ErsatzTV/Pages/PlayoutEditor.razor +++ b/ErsatzTV/Pages/PlayoutEditor.razor @@ -137,4 +137,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlayoutTemplatesEditor.razor b/ErsatzTV/Pages/PlayoutTemplatesEditor.razor index 916dbd09f..4274df1bf 100644 --- a/ErsatzTV/Pages/PlayoutTemplatesEditor.razor +++ b/ErsatzTV/Pages/PlayoutTemplatesEditor.razor @@ -566,4 +566,4 @@ _selectedItem.MonthsOfYear.AddRange(selectedMonths); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Playouts.razor b/ErsatzTV/Pages/Playouts.razor index 247b15160..8aa37ca76 100644 --- a/ErsatzTV/Pages/Playouts.razor +++ b/ErsatzTV/Pages/Playouts.razor @@ -274,7 +274,7 @@ public async Task HandlePlayoutUpdated(PlayoutUpdatedNotification notification, CancellationToken cancellationToken) { // only refresh detail table on unlock operations (after playout has been modified) - if (notification.IsLocked == false) + if (!notification.IsLocked) { if (notification.PlayoutId == _selectedPlayoutId && _detailTable is not null) { @@ -411,4 +411,4 @@ return new TableData { TotalItems = 0 }; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlexLibrariesEditor.razor b/ErsatzTV/Pages/PlexLibrariesEditor.razor index b3600b132..0a5c66fee 100644 --- a/ErsatzTV/Pages/PlexLibrariesEditor.razor +++ b/ErsatzTV/Pages/PlexLibrariesEditor.razor @@ -52,4 +52,4 @@ return Unit.Default; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlexMediaSources.razor b/ErsatzTV/Pages/PlexMediaSources.razor index 15e035132..303faf525 100644 --- a/ErsatzTV/Pages/PlexMediaSources.razor +++ b/ErsatzTV/Pages/PlexMediaSources.razor @@ -167,4 +167,4 @@ void IDisposable.Dispose() => Locker.OnPlexChanged -= PlexChanged; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/PlexPathReplacementsEditor.razor b/ErsatzTV/Pages/PlexPathReplacementsEditor.razor index 45883db2d..2e199fc5a 100644 --- a/ErsatzTV/Pages/PlexPathReplacementsEditor.razor +++ b/ErsatzTV/Pages/PlexPathReplacementsEditor.razor @@ -43,4 +43,4 @@ return new UpdatePlexPathReplacements(Id, items); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/RemoteStreamList.razor b/ErsatzTV/Pages/RemoteStreamList.razor index 067738663..823999cd0 100644 --- a/ErsatzTV/Pages/RemoteStreamList.razor +++ b/ErsatzTV/Pages/RemoteStreamList.razor @@ -129,4 +129,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/ScheduleEditor.razor b/ErsatzTV/Pages/ScheduleEditor.razor index 33d8fd69b..1986c45ee 100644 --- a/ErsatzTV/Pages/ScheduleEditor.razor +++ b/ErsatzTV/Pages/ScheduleEditor.razor @@ -132,4 +132,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/ScheduleItemsEditor.razor b/ErsatzTV/Pages/ScheduleItemsEditor.razor index d0d93aff4..9492e6eea 100644 --- a/ErsatzTV/Pages/ScheduleItemsEditor.razor +++ b/ErsatzTV/Pages/ScheduleItemsEditor.razor @@ -14,607 +14,614 @@ @inject IMediator Mediator - -
-
- @if (_selectedItem is not null) + +
+
+ @if (_selectedItem is not null) + { + string start = _selectedItem.StartType == StartType.Fixed ? _selectedItem.StartTime == null ? string.Empty : DateTime.Today.Add(_selectedItem.StartTime.Value).ToShortTimeString() : "Dynamic"; + var mode = _selectedItem.PlayoutMode.ToString(); + @if (_selectedItem.PlayoutMode is PlayoutMode.Multiple) { - string start = _selectedItem.StartType == StartType.Fixed ? _selectedItem.StartTime == null ? string.Empty : DateTime.Today.Add(_selectedItem.StartTime.Value).ToShortTimeString() : "Dynamic"; - var mode = _selectedItem.PlayoutMode.ToString(); - @if (_selectedItem.PlayoutMode is PlayoutMode.Multiple) + switch (_selectedItem.MultipleMode) { - switch (_selectedItem.MultipleMode) - { - case MultipleMode.CollectionSize: - mode = "Collection Size"; - break; - case MultipleMode.PlaylistItemSize: - mode = "Playlist Item Size"; - break; - case MultipleMode.MultiEpisodeGroupSize: - mode = "Multi-Episode Group Size"; - break; - case MultipleMode.Count: - default: - mode += $" ({_selectedItem.MultipleCount})"; - break; - } + case MultipleMode.CollectionSize: + mode = "Collection Size"; + break; + case MultipleMode.PlaylistItemSize: + mode = "Playlist Item Size"; + break; + case MultipleMode.MultiEpisodeGroupSize: + mode = "Multi-Episode Group Size"; + break; + case MultipleMode.Count: + default: + mode += $" ({_selectedItem.MultipleCount})"; + break; } + } - var result = $"{_schedule.Name} ({start} - {_selectedItem.CollectionName} - {mode})"; - @result + var result = $"{_schedule.Name} ({start} - {_selectedItem.CollectionName} - {mode})"; + @result + } + else + { + @_schedule.Name + } +
+
+ + Save Schedule Items + + + Add Schedule Item + +
+
+
+ + + + +
+
+
+
+ +Schedule 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 + + + + + @if (context.PlayoutMode is PlayoutMode.Multiple) + { + switch (context.MultipleMode) + { + case MultipleMode.CollectionSize: + @:Collection Size + break; + case MultipleMode.PlaylistItemSize: + @:Playlist Item Size + break; + case MultipleMode.MultiEpisodeGroupSize: + @:Multi-Episode Group Size + break; + case MultipleMode.Count: + default: + @($"Multiple ({context.MultipleCount})") + break; + } } else { - @_schedule.Name + @context.PlayoutMode } + + + +
+ + + + + + + +
-
- - Save Schedule Items - - - Add Schedule Item - -
-
-
- - - - -
+
+
+
+@if (_selectedItem is not null) +{ + Schedule Item + + +
+ Start Type
- -
- - Schedule 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 - - - - - @if (context.PlayoutMode is PlayoutMode.Multiple) - { - switch (context.MultipleMode) - { - case MultipleMode.CollectionSize: - @:Collection Size - break; - case MultipleMode.PlaylistItemSize: - @:Playlist Item Size - break; - case MultipleMode.MultiEpisodeGroupSize: - @:Multi-Episode Group Size - break; - case MultipleMode.Count: - default: - @($"Multiple ({context.MultipleCount})") - break; - } - } - else - { - @context.PlayoutMode - } - - - -
- - - - - - - - -
-
-
-
- @if (_selectedItem is not null) + + Dynamic + @if (!_schedule.ShuffleScheduleItems) { - Schedule Item - - -
- Start Type -
- - Dynamic - @if (!_schedule.ShuffleScheduleItems) - { - Fixed - } - -
- -
- Start Time -
- -
- -
- Fixed Start Time Behavior -
- - Inherit - Strict - Always Wait For Exact Start Time - Flexible - Start As Soon As Possible After Start Time - -
- -
- Collection Type -
- - Collection - Television Show - Television Season - Artist - Multi Collection - Smart Collection - Playlist - -
- @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) - { - -
- Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) - { - -
- Multi Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) - { - -
- Smart Collection -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) - { - -
- Television Show -
- - - - Only the first 10 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) - { - -
- Television Season -
- - - - Only the first 20 items are shown - - - -
- } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Artist) + Fixed + } +
+ + +
+ Start Time +
+ +
+ +
+ Fixed Start Time Behavior +
+ + Inherit + Strict - Always Wait For Exact Start Time + Flexible - Start As Soon As Possible After Start Time + +
+ +
+ Collection Type +
+ + Collection + Television Show + Television Season + Artist + Multi Collection + Smart Collection + Playlist + +
+ @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Collection) + { + +
+ Collection +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.MultiCollection) + { + +
+ Multi Collection +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.SmartCollection) + { + +
+ Smart Collection +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionShow) + { + +
+ Television Show +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.TelevisionSeason) + { + +
+ Television Season +
+ + + + Only the first 20 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Artist) + { + +
+ Artist +
+ + + + Only the first 10 items are shown + + + +
+ } + + @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Playlist) + { + +
+ Playlist Group +
+ + @foreach (PlaylistGroupViewModel playlistGroup in _playlistGroups) { - -
- Artist -
- - - - Only the first 10 items are shown - - - -
+ @playlistGroup.Name } - @if (_selectedItem.CollectionType == ProgramScheduleItemCollectionType.Playlist) +
+
+ +
+ Playlist +
+ + @foreach (PlaylistViewModel playlist in _playlists) { - -
- Playlist Group -
- - @foreach (PlaylistGroupViewModel playlistGroup in _playlistGroups) - { - @playlistGroup.Name - } - -
- -
- Playlist -
- - @foreach (PlaylistViewModel playlist in _playlists) - { - @playlist.Name - } - -
+ @playlist.Name } - -
- Playback Order -
- - @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; - case ProgramScheduleItemCollectionType.Playlist: - break; - default: - Chronological - Random - Shuffle - break; - } - -
- -
- Playout Mode -
- - @if (!_schedule.ShuffleScheduleItems) - { - Flood - } - One - Multiple - Duration - -
- -
- Multiple Mode -
- - Count - - @if (_selectedItem.CollectionType is not ProgramScheduleItemCollectionType.Playlist) - { - Collection Size - } - else - { - Playlist Item Size - } - - @if (_selectedItem.PlaybackOrder is PlaybackOrder.Chronological) - { - Multi-Episode Group Size - } - -
- -
- Multiple Count -
- -
- -
- Playout Duration -
- -
- -
- -
- -
- Fill With Group Mode (Show or Artist) -
- - (none) - @if (_selectedItem.CanFillWithGroups) - { - Ordered Groups - Shuffled Groups - } - else - { - _selectedItem.FillWithGroupMode = FillWithGroupMode.None; - } - -
- -
- Tail Mode -
- - (none) - Offline - Filler - -
- -
- Discard To Fill Attempts -
- -
- -
- Custom Title -
- -
- -
- Guide Mode -
- - Normal - Filler - -
- Schedule Item Filler - - -
- Pre-Roll Filler -
- - @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.PreRoll)) - { - @filler.Name - } - -
- -
- Mid-Roll Filler -
- - @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.MidRoll)) - { - @filler.Name - } - -
- -
- Post-Roll Filler -
- - @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.PostRoll)) - { - @filler.Name - } - -
- -
- Tail Filler -
- - @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.Tail)) - { - @filler.Name - } - -
- -
- Fallback Filler -
- - @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.Fallback)) - { - @filler.Name - } - -
- Schedule Item Overrides - - -
- Watermarks -
- - @foreach (WatermarkViewModel watermark in _watermarks) - { - @watermark.Name - } - -
- -
- Preferred Audio Language -
- - (none) - @foreach (LanguageCodeViewModel culture in _availableCultures) - { - @culture.EnglishName - } - -
- -
- Preferred Audio Title -
- -
- -
- Preferred Subtitle Language -
- - (none) - @foreach (LanguageCodeViewModel culture in _availableCultures) - { - @culture.EnglishName - } - -
- -
- Subtitle Mode -
- - None - Forced - Default - Any - -
+
+
+ } + + +
+ Playback Order +
+ + @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; + case ProgramScheduleItemCollectionType.Playlist: + break; + default: + Chronological + Random + Shuffle + break; } -
-
+ +
+ +
+ Playout Mode +
+ + @if (!_schedule.ShuffleScheduleItems) + { + Flood + } + One + Multiple + Duration + +
+ +
+ Multiple Mode +
+ + Count + + @if (_selectedItem.CollectionType is not ProgramScheduleItemCollectionType.Playlist) + { + Collection Size + } + else + { + Playlist Item Size + } + + @if (_selectedItem.PlaybackOrder is PlaybackOrder.Chronological) + { + Multi-Episode Group Size + } + +
+ +
+ Multiple Count +
+ +
+ +
+ Playout Duration +
+ +
+ +
+ +
+ +
+ Fill With Group Mode (Show or Artist) +
+ + (none) + @if (_selectedItem.CanFillWithGroups) + { + Ordered Groups + Shuffled Groups + } + else + { + _selectedItem.FillWithGroupMode = FillWithGroupMode.None; + } + +
+ +
+ Tail Mode +
+ + (none) + Offline + Filler + +
+ +
+ Discard To Fill Attempts +
+ +
+ +
+ Custom Title +
+ +
+ +
+ Guide Mode +
+ + Normal + Filler + +
+ Schedule Item Filler + + +
+ Pre-Roll Filler +
+ + @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.PreRoll)) + { + @filler.Name + } + +
+ +
+ Mid-Roll Filler +
+ + @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.MidRoll)) + { + @filler.Name + } + +
+ +
+ Post-Roll Filler +
+ + @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.PostRoll)) + { + @filler.Name + } + +
+ +
+ Tail Filler +
+ + @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.Tail)) + { + @filler.Name + } + +
+ +
+ Fallback Filler +
+ + @foreach (FillerPresetViewModel filler in _fillerPresets.Where(f => f.FillerKind == FillerKind.Fallback)) + { + @filler.Name + } + +
+ Schedule Item Overrides + + +
+ Watermarks +
+ + @foreach (WatermarkViewModel watermark in _watermarks) + { + @watermark.Name + } + +
+ +
+ Preferred Audio Language +
+ + (none) + @foreach (LanguageCodeViewModel culture in _availableCultures) + { + @culture.EnglishName + } + +
+ +
+ Preferred Audio Title +
+ +
+ +
+ Preferred Subtitle Language +
+ + (none) + @foreach (LanguageCodeViewModel culture in _availableCultures) + { + @culture.EnglishName + } + +
+ +
+ Subtitle Mode +
+ + None + Forced + Default + Any + +
+} +
+
@code { @@ -950,4 +957,4 @@ private string SelectedRowClassFunc(ProgramScheduleItemEditViewModel element, int rowNumber) => _selectedItem != null && _selectedItem == element ? "selected" : string.Empty; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Schedules.razor b/ErsatzTV/Pages/Schedules.razor index 1cc0ecb83..f5a763bc6 100644 --- a/ErsatzTV/Pages/Schedules.razor +++ b/ErsatzTV/Pages/Schedules.razor @@ -199,4 +199,4 @@ }; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Search.razor b/ErsatzTV/Pages/Search.razor index ad9434802..ce9797220 100644 --- a/ErsatzTV/Pages/Search.razor +++ b/ErsatzTV/Pages/Search.razor @@ -9,407 +9,407 @@ @implements IDisposable - -
- @if (IsSelectMode()) - { -
- @SelectionLabel() -
-
- - Add To Collection - - - Add To Playlist - - - Clear Selection - -
-
-
- - - - - -
- } - else - { -
- @_query - @if (_movies?.Count > 0) - { - @_movies.Count Movies - } - - @if (_shows?.Count > 0) - { - @_shows.Count Shows - } - - @if (_seasons?.Count > 0) - { - @_seasons.Count Seasons - } - - @if (_episodes?.Count > 0) - { - @_episodes.Count Episodes - } - - @if (_artists?.Count > 0) - { - @_artists.Count Artists - } - - @if (_musicVideos?.Count > 0) - { - @_musicVideos.Count Music Videos - } - - @if (_otherVideos?.Count > 0) - { - @_otherVideos.Count Other Videos - } - - @if (_songs?.Count > 0) - { - @_songs.Count Songs - } - - @if (_images?.Count > 0) - { - @_images.Count Images - } - - @if (_remoteStreams?.Count > 0) - { - @_remoteStreams.Count Remote Streams - } -
-
- - - Add All - - - - - Add All - - - - - Save As - - -
-
-
-
- - - - - -
- } -
-
-
- - @if (_movies?.Count > 0) - { -
- - Movies - - @if (_movies.Count > 50) - { - See All >> - } -
- - - - @foreach (MovieCardViewModel card in _movies.Cards.OrderBy(m => m.SortTitle)) - { - - } - - } - - @if (_shows?.Count > 0) - { -
- - Shows - - @if (_shows.Count > 50) - { - See All >> - } -
- - - - @foreach (TelevisionShowCardViewModel card in _shows.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } - - @if (_seasons?.Count > 0) - { -
- - Seasons - - @if (_seasons.Count > 50) - { - See All >> - } -
- - - - @foreach (TelevisionSeasonCardViewModel card in _seasons.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } - - @if (_episodes?.Count > 0) - { -
- - Episodes - - @if (_episodes.Count > 50) - { - See All >> - } -
- - - - @foreach (TelevisionEpisodeCardViewModel card in _episodes.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } - - @if (_artists?.Count > 0) - { -
- - Artists - - @if (_artists.Count > 50) - { - See All >> - } -
- - - - @foreach (ArtistCardViewModel card in _artists.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } - - @if (_musicVideos?.Count > 0) - { -
- - Music Videos - - @if (_musicVideos.Count > 50) - { - See All >> - } + +
+ @if (IsSelectMode()) + { +
+ @SelectionLabel() +
+
+ + Add To Collection + + + Add To Playlist + + + Clear Selection + +
+
+
+ + + + + +
+ } + else + { +
+ @_query + @if (_movies?.Count > 0) + { + @_movies.Count Movies + } + + @if (_shows?.Count > 0) + { + @_shows.Count Shows + } + + @if (_seasons?.Count > 0) + { + @_seasons.Count Seasons + } + + @if (_episodes?.Count > 0) + { + @_episodes.Count Episodes + } + + @if (_artists?.Count > 0) + { + @_artists.Count Artists + } + + @if (_musicVideos?.Count > 0) + { + @_musicVideos.Count Music Videos + } + + @if (_otherVideos?.Count > 0) + { + @_otherVideos.Count Other Videos + } + + @if (_songs?.Count > 0) + { + @_songs.Count Songs + } + + @if (_images?.Count > 0) + { + @_images.Count Images + } + + @if (_remoteStreams?.Count > 0) + { + @_remoteStreams.Count Remote Streams + } +
+
+ + + Add All + + + + + Add All + + + + + Save As + +
- +
+
+
+ + + + + +
+ } +
+
+
+ + @if (_movies?.Count > 0) + { +
+ + Movies + + @if (_movies.Count > 50) + { + See All >> + } +
+ + + + @foreach (MovieCardViewModel card in _movies.Cards.OrderBy(m => m.SortTitle)) + { + + } + + } - - @foreach (MusicVideoCardViewModel card in _musicVideos.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_shows?.Count > 0) + { +
+ + Shows + + @if (_shows.Count > 50) + { + See All >> + } +
+ + + + @foreach (TelevisionShowCardViewModel card in _shows.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - @if (_otherVideos?.Count > 0) - { -
- - Other Videos - - @if (_otherVideos.Count > 50) - { - See All >> - } -
- + @if (_seasons?.Count > 0) + { +
+ + Seasons + + @if (_seasons.Count > 50) + { + See All >> + } +
+ + + + @foreach (TelevisionSeasonCardViewModel card in _seasons.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - - @foreach (OtherVideoCardViewModel card in _otherVideos.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_episodes?.Count > 0) + { +
+ + Episodes + + @if (_episodes.Count > 50) + { + See All >> + } +
+ + + + @foreach (TelevisionEpisodeCardViewModel card in _episodes.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - @if (_songs?.Count > 0) - { -
- - Songs - - @if (_songs.Count > 50) - { - See All >> - } -
- + @if (_artists?.Count > 0) + { +
+ + Artists + + @if (_artists.Count > 50) + { + See All >> + } +
+ + + + @foreach (ArtistCardViewModel card in _artists.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - - @foreach (SongCardViewModel card in _songs.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_musicVideos?.Count > 0) + { +
+ + Music Videos + + @if (_musicVideos.Count > 50) + { + See All >> + } +
+ + + + @foreach (MusicVideoCardViewModel card in _musicVideos.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - @if (_images?.Count > 0) - { -
- - Images - - @if (_images.Count > 50) - { - See All >> - } -
- + @if (_otherVideos?.Count > 0) + { +
+ + Other Videos + + @if (_otherVideos.Count > 50) + { + See All >> + } +
+ + + + @foreach (OtherVideoCardViewModel card in _otherVideos.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - - @foreach (ImageCardViewModel card in _images.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_songs?.Count > 0) + { +
+ + Songs + + @if (_songs.Count > 50) + { + See All >> + } +
+ + + + @foreach (SongCardViewModel card in _songs.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - @if (_remoteStreams?.Count > 0) - { -
- - Remote Streams - - @if (_remoteStreams.Count > 50) - { - See All >> - } -
- + @if (_images?.Count > 0) + { +
+ + Images + + @if (_images.Count > 50) + { + See All >> + } +
+ + + + @foreach (ImageCardViewModel card in _images.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - - @foreach (RemoteStreamCardViewModel card in _remoteStreams.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } -
-
+ @if (_remoteStreams?.Count > 0) + { +
+ + Remote Streams + + @if (_remoteStreams.Count > 50) + { + See All >> + } +
+ + + + @foreach (RemoteStreamCardViewModel card in _remoteStreams.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } + +
@code { @@ -971,4 +971,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Settings/FFmpegSettings.razor b/ErsatzTV/Pages/Settings/FFmpegSettings.razor index a330c384c..bb7b7d2f8 100644 --- a/ErsatzTV/Pages/Settings/FFmpegSettings.razor +++ b/ErsatzTV/Pages/Settings/FFmpegSettings.razor @@ -256,4 +256,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Settings/HDHRSettings.razor b/ErsatzTV/Pages/Settings/HDHRSettings.razor index d40311a1a..ac5f63631 100644 --- a/ErsatzTV/Pages/Settings/HDHRSettings.razor +++ b/ErsatzTV/Pages/Settings/HDHRSettings.razor @@ -71,4 +71,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Settings/LoggingSettings.razor b/ErsatzTV/Pages/Settings/LoggingSettings.razor index f7b710e7d..f26c5b3f3 100644 --- a/ErsatzTV/Pages/Settings/LoggingSettings.razor +++ b/ErsatzTV/Pages/Settings/LoggingSettings.razor @@ -109,4 +109,4 @@ Right: _ => Snackbar.Add("Successfully saved logging settings", Severity.Success)); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Settings/PlayoutSettings.razor b/ErsatzTV/Pages/Settings/PlayoutSettings.razor index 165a7874f..0a66a5b98 100644 --- a/ErsatzTV/Pages/Settings/PlayoutSettings.razor +++ b/ErsatzTV/Pages/Settings/PlayoutSettings.razor @@ -68,4 +68,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Settings/ScannerSettings.razor b/ErsatzTV/Pages/Settings/ScannerSettings.razor index f8726319b..17d4e2541 100644 --- a/ErsatzTV/Pages/Settings/ScannerSettings.razor +++ b/ErsatzTV/Pages/Settings/ScannerSettings.razor @@ -65,4 +65,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Settings/XMLTVSettings.razor b/ErsatzTV/Pages/Settings/XMLTVSettings.razor index 49542ea76..719dd00a2 100644 --- a/ErsatzTV/Pages/Settings/XMLTVSettings.razor +++ b/ErsatzTV/Pages/Settings/XMLTVSettings.razor @@ -59,4 +59,4 @@ Right: _ => Snackbar.Add("Successfully saved xmltv settings", Severity.Success)); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/SmartCollectionEditor.razor b/ErsatzTV/Pages/SmartCollectionEditor.razor index 9667a3b9b..123eac9d8 100644 --- a/ErsatzTV/Pages/SmartCollectionEditor.razor +++ b/ErsatzTV/Pages/SmartCollectionEditor.razor @@ -45,7 +45,7 @@ protected override async Task OnParametersSetAsync() { Option maybeSmartCollection = await Mediator.Send(new GetSmartCollectionById(Id), _cts.Token); - foreach (var smartCollection in maybeSmartCollection) + foreach (SmartCollectionViewModel smartCollection in maybeSmartCollection) { _model.Id = smartCollection.Id; _model.Name = smartCollection.Name; @@ -68,4 +68,5 @@ }); } } -} \ No newline at end of file + +} diff --git a/ErsatzTV/Pages/SongList.razor b/ErsatzTV/Pages/SongList.razor index 4c84f2c64..6d545a111 100644 --- a/ErsatzTV/Pages/SongList.razor +++ b/ErsatzTV/Pages/SongList.razor @@ -129,4 +129,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/TelevisionEpisodeList.razor b/ErsatzTV/Pages/TelevisionEpisodeList.razor index ea14ab7a7..922adf263 100644 --- a/ErsatzTV/Pages/TelevisionEpisodeList.razor +++ b/ErsatzTV/Pages/TelevisionEpisodeList.razor @@ -328,14 +328,8 @@ } } - private static string GetPosterUrl(string poster) - { - return poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/posters/{poster}"; - } + private static string GetPosterUrl(string poster) => poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/posters/{poster}"; - private static string GetThumbnailUrl(string poster) - { - return poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/thumbnails/{poster}"; - } + private static string GetThumbnailUrl(string poster) => poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/thumbnails/{poster}"; -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/TelevisionSeasonList.razor b/ErsatzTV/Pages/TelevisionSeasonList.razor index d4fd5ed3e..524ef46e9 100644 --- a/ErsatzTV/Pages/TelevisionSeasonList.razor +++ b/ErsatzTV/Pages/TelevisionSeasonList.razor @@ -29,7 +29,7 @@
- + @@ -285,10 +285,7 @@ } } - private static string GetPosterUrl(string poster) - { - return poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/posters/{poster}"; - } + private static string GetPosterUrl(string poster) => poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/posters/{poster}"; private async Task ScanShow(bool deepScan) { @@ -304,4 +301,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/TelevisionSeasonSearchResults.razor b/ErsatzTV/Pages/TelevisionSeasonSearchResults.razor index 60d637ad7..c697c4623 100644 --- a/ErsatzTV/Pages/TelevisionSeasonSearchResults.razor +++ b/ErsatzTV/Pages/TelevisionSeasonSearchResults.razor @@ -153,4 +153,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/TelevisionShowList.razor b/ErsatzTV/Pages/TelevisionShowList.razor index c08242cff..f21fe2e49 100644 --- a/ErsatzTV/Pages/TelevisionShowList.razor +++ b/ErsatzTV/Pages/TelevisionShowList.razor @@ -157,4 +157,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/TemplateEditor.razor b/ErsatzTV/Pages/TemplateEditor.razor index 9e3f14390..c794e2896 100644 --- a/ErsatzTV/Pages/TemplateEditor.razor +++ b/ErsatzTV/Pages/TemplateEditor.razor @@ -197,7 +197,7 @@ while (maybeStart.Date == DateTime.Today) { DateTime maybeEnd = maybeStart.AddMinutes(_selectedBlock.Minutes); - if (IntersectsOthers(null, maybeStart, maybeEnd) == false) + if (!IntersectsOthers(null, maybeStart, maybeEnd)) { var item = new TemplateItemEditViewModel { @@ -269,7 +269,7 @@ } } - return willFit == false; + return !willFit; } // private void RemoveTemplateItem(TemplateItemEditViewModel item) @@ -294,4 +294,4 @@ () => NavigationManager.NavigateTo("templates")); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Templates.razor b/ErsatzTV/Pages/Templates.razor index adf3532af..1516ef175 100644 --- a/ErsatzTV/Pages/Templates.razor +++ b/ErsatzTV/Pages/Templates.razor @@ -203,4 +203,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/TraktListEditor.razor b/ErsatzTV/Pages/TraktListEditor.razor index 706bc9281..6c8012077 100644 --- a/ErsatzTV/Pages/TraktListEditor.razor +++ b/ErsatzTV/Pages/TraktListEditor.razor @@ -20,7 +20,7 @@
Id
- +
@@ -92,4 +92,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/TraktLists.razor b/ErsatzTV/Pages/TraktLists.razor index 964c56375..e8023dd40 100644 --- a/ErsatzTV/Pages/TraktLists.razor +++ b/ErsatzTV/Pages/TraktLists.razor @@ -175,4 +175,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Trash.razor b/ErsatzTV/Pages/Trash.razor index 816773786..ea8beeb08 100644 --- a/ErsatzTV/Pages/Trash.razor +++ b/ErsatzTV/Pages/Trash.razor @@ -8,396 +8,396 @@ @inject PersistentComponentState ApplicationState - -
- @if (IsSelectMode()) - { -
- @SelectionLabel() -
-
- - Delete From Database - - - Clear Selection + +
+ @if (IsSelectMode()) + { +
+ @SelectionLabel() +
+
+ + Delete From Database + + + Clear Selection + +
+
+
+ + + + +
+ } + else if (IsNotEmpty) + { +
+ @if (_movies?.Cards.Count > 0) + { + @_movies.Count Movies + } + + @if (_shows?.Cards.Count > 0) + { + @_shows.Count Shows + } + + @if (_seasons?.Cards.Count > 0) + { + @_seasons.Count Seasons + } + + @if (_episodes?.Cards.Count > 0) + { + @_episodes.Count Episodes + } + + @if (_artists?.Cards.Count > 0) + { + @_artists.Count Artists + } + + @if (_musicVideos?.Cards.Count > 0) + { + @_musicVideos.Count Music Videos + } + + @if (_otherVideos?.Cards.Count > 0) + { + @_otherVideos.Count Other Videos + } + + @if (_songs?.Cards.Count > 0) + { + @_songs.Count Songs + } + + @if (_images?.Cards.Count > 0) + { + @_images.Count Images + } + + @if (_remoteStreams?.Cards.Count > 0) + { + @_remoteStreams.Count Remote Streams + } +
+
+ + Empty Trash
-
-
- - - - -
- } - else if (IsNotEmpty) - { -
- @if (_movies?.Cards.Count > 0) - { - @_movies.Count Movies - } - - @if (_shows?.Cards.Count > 0) - { - @_shows.Count Shows - } - - @if (_seasons?.Cards.Count > 0) - { - @_seasons.Count Seasons - } - - @if (_episodes?.Cards.Count > 0) - { - @_episodes.Count Episodes - } - - @if (_artists?.Cards.Count > 0) - { - @_artists.Count Artists - } - - @if (_musicVideos?.Cards.Count > 0) - { - @_musicVideos.Count Music Videos - } - - @if (_otherVideos?.Cards.Count > 0) - { - @_otherVideos.Count Other Videos - } - - @if (_songs?.Cards.Count > 0) - { - @_songs.Count Songs - } - - @if (_images?.Cards.Count > 0) - { - @_images.Count Images - } - - @if (_remoteStreams?.Cards.Count > 0) - { - @_remoteStreams.Count Remote Streams - } -
-
- - Empty Trash - -
-
-
-
-
- - Empty Trash - -
-
- } - else - { - Nothing to see here... - } -
- -
- - @if (_movies?.Cards.Count > 0) - { -
- - Movies - - @if (_movies.Count > 50) - { - See All >> - } +
+
+
+
+ + Empty Trash +
- - - - @foreach (MovieCardViewModel card in _movies.Cards.OrderBy(m => m.SortTitle)) - { - - } - - } +
+ } + else + { + Nothing to see here... + } +
+ +
+ + @if (_movies?.Cards.Count > 0) + { +
+ + Movies + + @if (_movies.Count > 50) + { + See All >> + } +
+ - @if (_shows?.Cards.Count > 0) - { -
- - Shows - - @if (_shows.Count > 50) - { - See All >> - } -
- - - - @foreach (TelevisionShowCardViewModel card in _shows.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + + @foreach (MovieCardViewModel card in _movies.Cards.OrderBy(m => m.SortTitle)) + { + + } + + } - @if (_seasons?.Cards.Count > 0) - { -
- - Seasons - - @if (_seasons.Count > 50) - { - See All >> - } -
- - - - @foreach (TelevisionSeasonCardViewModel card in _seasons.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_shows?.Cards.Count > 0) + { +
+ + Shows + + @if (_shows.Count > 50) + { + See All >> + } +
+ - @if (_episodes?.Cards.Count > 0) - { -
- - Episodes - - @if (_episodes.Count > 50) - { - See All >> - } -
- - - - @foreach (TelevisionEpisodeCardViewModel card in _episodes.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + + @foreach (TelevisionShowCardViewModel card in _shows.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - @if (_artists?.Cards.Count > 0) - { -
- - Artists - - @if (_artists.Count > 50) - { - See All >> - } -
- - - - @foreach (ArtistCardViewModel card in _artists.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_seasons?.Cards.Count > 0) + { +
+ + Seasons + + @if (_seasons.Count > 50) + { + See All >> + } +
+ - @if (_musicVideos?.Cards.Count > 0) - { -
- - Music Videos - - @if (_musicVideos.Count > 50) - { - See All >> - } -
- - - - @foreach (MusicVideoCardViewModel card in _musicVideos.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + + @foreach (TelevisionSeasonCardViewModel card in _seasons.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - @if (_otherVideos?.Cards.Count > 0) - { -
- - Other Videos - - @if (_otherVideos.Count > 50) - { - See All >> - } -
- - - - @foreach (OtherVideoCardViewModel card in _otherVideos.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_episodes?.Cards.Count > 0) + { +
+ + Episodes + + @if (_episodes.Count > 50) + { + See All >> + } +
+ - @if (_songs?.Cards.Count > 0) - { -
- - Songs - - @if (_songs.Count > 50) - { - See All >> - } -
- - - - @foreach (SongCardViewModel card in _songs.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + + @foreach (TelevisionEpisodeCardViewModel card in _episodes.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } - @if (_images?.Cards.Count > 0) - { -
- - Images - - @if (_images.Count > 50) - { - See All >> - } -
- - - - @foreach (ImageCardViewModel card in _images.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } + @if (_artists?.Cards.Count > 0) + { +
+ + Artists + + @if (_artists.Count > 50) + { + See All >> + } +
+ - @if (_remoteStreams?.Cards.Count > 0) - { -
- - Remote Streams - - @if (_remoteStreams.Count > 50) - { - See All >> - } -
- - - - @foreach (RemoteStreamCardViewModel card in _remoteStreams.Cards.OrderBy(s => s.SortTitle)) - { - - } - - } -
-
+ + @foreach (ArtistCardViewModel card in _artists.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } + + @if (_musicVideos?.Cards.Count > 0) + { +
+ + Music Videos + + @if (_musicVideos.Count > 50) + { + See All >> + } +
+ + + + @foreach (MusicVideoCardViewModel card in _musicVideos.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } + + @if (_otherVideos?.Cards.Count > 0) + { +
+ + Other Videos + + @if (_otherVideos.Count > 50) + { + See All >> + } +
+ + + + @foreach (OtherVideoCardViewModel card in _otherVideos.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } + + @if (_songs?.Cards.Count > 0) + { +
+ + Songs + + @if (_songs.Count > 50) + { + See All >> + } +
+ + + + @foreach (SongCardViewModel card in _songs.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } + + @if (_images?.Cards.Count > 0) + { +
+ + Images + + @if (_images.Count > 50) + { + See All >> + } +
+ + + + @foreach (ImageCardViewModel card in _images.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } + + @if (_remoteStreams?.Cards.Count > 0) + { +
+ + Remote Streams + + @if (_remoteStreams.Count > 50) + { + See All >> + } +
+ + + + @foreach (RemoteStreamCardViewModel card in _remoteStreams.Cards.OrderBy(s => s.SortTitle)) + { + + } + + } + +
@code { @@ -816,4 +816,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Troubleshooting.razor b/ErsatzTV/Pages/Troubleshooting.razor index 7090b3fd1..822338c16 100644 --- a/ErsatzTV/Pages/Troubleshooting.razor +++ b/ErsatzTV/Pages/Troubleshooting.razor @@ -167,4 +167,4 @@ private async Task CopyToClipboard(ElementReference view) => await JsRuntime.InvokeVoidAsync("clipboardCopy.copyText", view); -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/WatermarkEditor.razor b/ErsatzTV/Pages/WatermarkEditor.razor index 3704c685b..326a20ae5 100644 --- a/ErsatzTV/Pages/WatermarkEditor.razor +++ b/ErsatzTV/Pages/WatermarkEditor.razor @@ -69,12 +69,12 @@ @if (!string.IsNullOrWhiteSpace(_model.Image?.Path) && _model.ImageSource == ChannelWatermarkImageSource.Custom) {
- +
} else { - + } @@ -300,4 +300,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/Watermarks.razor b/ErsatzTV/Pages/Watermarks.razor index cb98927de..56fc7babf 100644 --- a/ErsatzTV/Pages/Watermarks.razor +++ b/ErsatzTV/Pages/Watermarks.razor @@ -118,4 +118,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/YamlPlayoutEditor.razor b/ErsatzTV/Pages/YamlPlayoutEditor.razor index 45852f39e..6f9b55299 100644 --- a/ErsatzTV/Pages/YamlPlayoutEditor.razor +++ b/ErsatzTV/Pages/YamlPlayoutEditor.razor @@ -95,10 +95,7 @@ await Mediator.Send(new UpdateYamlPlayout(_playout.PlayoutId, _playout.TemplateFile), _cts.Token); result.Match( - _ => - { - Snackbar.Add($"Saved YAML file for playout {_channelName}", Severity.Success); - }, + _ => { Snackbar.Add($"Saved YAML file for playout {_channelName}", Severity.Success); }, error => { Snackbar.Add($"Unexpected error saving YAML file: {error.Value}", Severity.Error); @@ -106,4 +103,4 @@ }); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Pages/YamlValidator.razor b/ErsatzTV/Pages/YamlValidator.razor index 634c80732..636ded816 100644 --- a/ErsatzTV/Pages/YamlValidator.razor +++ b/ErsatzTV/Pages/YamlValidator.razor @@ -12,7 +12,7 @@
YAML File
- +
@@ -68,8 +68,8 @@
-
-
+
+
@@ -100,7 +100,7 @@ try { _jsonText = YamlScheduleValidator.ToJson(_yamlText); - var messages = await YamlScheduleValidator.GetValidationMessages(_yamlText, _isImport); + IList messages = await YamlScheduleValidator.GetValidationMessages(_yamlText, _isImport); _messagesCount = messages.Count; _messages = string.Join("\n", messages); @@ -116,7 +116,7 @@ private async Task OnYamlFileChanged(string yamlFile) { - var extension = Path.GetExtension(yamlFile); + string extension = Path.GetExtension(yamlFile); _exists = extension is ".yaml" or ".yml" && File.Exists(yamlFile); await Task.Delay(100); @@ -129,4 +129,5 @@ StateHasChanged(); } -} \ No newline at end of file + +} diff --git a/ErsatzTV/Pages/_Host.cshtml b/ErsatzTV/Pages/_Host.cshtml index d601bb0b7..78cf2197d 100644 --- a/ErsatzTV/Pages/_Host.cshtml +++ b/ErsatzTV/Pages/_Host.cshtml @@ -151,4 +151,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV/Program.cs b/ErsatzTV/Program.cs index e644e0192..cbe0aa43d 100644 --- a/ErsatzTV/Program.cs +++ b/ErsatzTV/Program.cs @@ -75,7 +75,9 @@ public class Program LoggingLevelSwitches.SchedulingLevelSwitch) // searching - .MinimumLevel.Override("ErsatzTV.Infrastructure.Search.SearchQueryParser", LoggingLevelSwitches.SearchingLevelSwitch) + .MinimumLevel.Override( + "ErsatzTV.Infrastructure.Search.SearchQueryParser", + LoggingLevelSwitches.SearchingLevelSwitch) // streaming .MinimumLevel.Override("ErsatzTV.Application.Streaming", LoggingLevelSwitches.StreamingLevelSwitch) @@ -88,7 +90,9 @@ public class Program LoggingLevelSwitches.StreamingLevelSwitch) .MinimumLevel.Override("ErsatzTV.Controllers.IptvController", LoggingLevelSwitches.StreamingLevelSwitch) .MinimumLevel.Override("ErsatzTV.Controllers.InternalController", LoggingLevelSwitches.StreamingLevelSwitch) - .MinimumLevel.Override("ErsatzTV.Controllers.TroubleshootController", LoggingLevelSwitches.StreamingLevelSwitch) + .MinimumLevel.Override( + "ErsatzTV.Controllers.TroubleshootController", + LoggingLevelSwitches.StreamingLevelSwitch) // http .MinimumLevel.Override("Serilog.AspNetCore.RequestLoggingMiddleware", LoggingLevelSwitches.HttpLevelSwitch) diff --git a/ErsatzTV/Shared/AddCustomResolutionDialog.razor b/ErsatzTV/Shared/AddCustomResolutionDialog.razor index 0defc2526..070ffad09 100644 --- a/ErsatzTV/Shared/AddCustomResolutionDialog.razor +++ b/ErsatzTV/Shared/AddCustomResolutionDialog.razor @@ -41,4 +41,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/AddToCollectionDialog.razor b/ErsatzTV/Shared/AddToCollectionDialog.razor index cb68e1b86..1f0cbc0a5 100644 --- a/ErsatzTV/Shared/AddToCollectionDialog.razor +++ b/ErsatzTV/Shared/AddToCollectionDialog.razor @@ -143,4 +143,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/AddToPlaylistDialog.razor b/ErsatzTV/Shared/AddToPlaylistDialog.razor index faeb39aa4..185c4fae2 100644 --- a/ErsatzTV/Shared/AddToPlaylistDialog.razor +++ b/ErsatzTV/Shared/AddToPlaylistDialog.razor @@ -134,4 +134,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/AddToScheduleDialog.razor b/ErsatzTV/Shared/AddToScheduleDialog.razor index 8667fddfb..c4ab27e2c 100644 --- a/ErsatzTV/Shared/AddToScheduleDialog.razor +++ b/ErsatzTV/Shared/AddToScheduleDialog.razor @@ -74,4 +74,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/AddTraktListDialog.razor b/ErsatzTV/Shared/AddTraktListDialog.razor index 7a94098d6..1c2e9e2fb 100644 --- a/ErsatzTV/Shared/AddTraktListDialog.razor +++ b/ErsatzTV/Shared/AddTraktListDialog.razor @@ -59,4 +59,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/ChannelPreviewDialog.razor b/ErsatzTV/Shared/ChannelPreviewDialog.razor index b3c9cb2c9..733c3559c 100644 --- a/ErsatzTV/Shared/ChannelPreviewDialog.razor +++ b/ErsatzTV/Shared/ChannelPreviewDialog.razor @@ -57,4 +57,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/CopyFFmpegProfileDialog.razor b/ErsatzTV/Shared/CopyFFmpegProfileDialog.razor index fe00793d8..b88d1edea 100644 --- a/ErsatzTV/Shared/CopyFFmpegProfileDialog.razor +++ b/ErsatzTV/Shared/CopyFFmpegProfileDialog.razor @@ -70,4 +70,4 @@ } private void Cancel(MouseEventArgs e) => MudDialog.Cancel(); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/CopyScheduleDialog.razor b/ErsatzTV/Shared/CopyScheduleDialog.razor index 4ecbd2fb3..986c0fdaa 100644 --- a/ErsatzTV/Shared/CopyScheduleDialog.razor +++ b/ErsatzTV/Shared/CopyScheduleDialog.razor @@ -70,4 +70,4 @@ } private void Cancel(MouseEventArgs e) => MudDialog.Cancel(); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/CopyWatermarkDialog.razor b/ErsatzTV/Shared/CopyWatermarkDialog.razor index d9f608912..89e630a6f 100644 --- a/ErsatzTV/Shared/CopyWatermarkDialog.razor +++ b/ErsatzTV/Shared/CopyWatermarkDialog.razor @@ -70,4 +70,4 @@ } private void Cancel(MouseEventArgs e) => MudDialog.Cancel(); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/DeleteDialog.razor b/ErsatzTV/Shared/DeleteDialog.razor index 6fed94bb9..dd3baf543 100644 --- a/ErsatzTV/Shared/DeleteDialog.razor +++ b/ErsatzTV/Shared/DeleteDialog.razor @@ -55,4 +55,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/DeleteFromDatabaseDialog.razor b/ErsatzTV/Shared/DeleteFromDatabaseDialog.razor index 3c98b8107..aab52f5c7 100644 --- a/ErsatzTV/Shared/DeleteFromDatabaseDialog.razor +++ b/ErsatzTV/Shared/DeleteFromDatabaseDialog.razor @@ -48,4 +48,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/DisconnectRemoteMediaSourceDialog.razor b/ErsatzTV/Shared/DisconnectRemoteMediaSourceDialog.razor index 2fe9d800b..41eedb456 100644 --- a/ErsatzTV/Shared/DisconnectRemoteMediaSourceDialog.razor +++ b/ErsatzTV/Shared/DisconnectRemoteMediaSourceDialog.razor @@ -34,4 +34,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/EditExternalJsonFileDialog.razor b/ErsatzTV/Shared/EditExternalJsonFileDialog.razor index e9d5141fc..c9dfefd74 100644 --- a/ErsatzTV/Shared/EditExternalJsonFileDialog.razor +++ b/ErsatzTV/Shared/EditExternalJsonFileDialog.razor @@ -39,4 +39,4 @@ private void Submit() => MudDialog.Close(DialogResult.Ok(_externalJsonFile)); private void Cancel() => MudDialog.Cancel(); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/EditImageFolderDurationDialog.razor b/ErsatzTV/Shared/EditImageFolderDurationDialog.razor index 7b5ec455a..26f906d10 100644 --- a/ErsatzTV/Shared/EditImageFolderDurationDialog.razor +++ b/ErsatzTV/Shared/EditImageFolderDurationDialog.razor @@ -44,4 +44,4 @@ private void Submit() => MudDialog.Close(DialogResult.Ok(_imageDurationSeconds)); private void Cancel() => MudDialog.Cancel(); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/FragmentLetterAnchor.razor b/ErsatzTV/Shared/FragmentLetterAnchor.razor index e27ed9e3d..ef5f4d9d6 100644 --- a/ErsatzTV/Shared/FragmentLetterAnchor.razor +++ b/ErsatzTV/Shared/FragmentLetterAnchor.razor @@ -29,4 +29,4 @@ { @ChildContent } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/LetterBar.razor b/ErsatzTV/Shared/LetterBar.razor index 13ded7826..7121a8a77 100644 --- a/ErsatzTV/Shared/LetterBar.razor +++ b/ErsatzTV/Shared/LetterBar.razor @@ -55,4 +55,4 @@ return uri + $"#letter-{letter}"; } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/MainLayout.razor b/ErsatzTV/Shared/MainLayout.razor index a008cba60..43ef4ab28 100644 --- a/ErsatzTV/Shared/MainLayout.razor +++ b/ErsatzTV/Shared/MainLayout.razor @@ -93,7 +93,7 @@ - +
@@ -254,7 +254,7 @@ public string DarkLightModeButtonIcon => _isDarkMode switch { true => Icons.Material.Rounded.DarkMode, - false => Icons.Material.Outlined.LightMode, + false => Icons.Material.Outlined.LightMode }; public void Dispose() @@ -433,4 +433,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/MarkdownView.razor b/ErsatzTV/Shared/MarkdownView.razor index 9a558554b..a86559d4a 100644 --- a/ErsatzTV/Shared/MarkdownView.razor +++ b/ErsatzTV/Shared/MarkdownView.razor @@ -1 +1 @@ -@HtmlContent \ No newline at end of file +@HtmlContent diff --git a/ErsatzTV/Shared/MediaCard.razor b/ErsatzTV/Shared/MediaCard.razor index d69d4261d..75c9d67ae 100644 --- a/ErsatzTV/Shared/MediaCard.razor +++ b/ErsatzTV/Shared/MediaCard.razor @@ -39,7 +39,7 @@
} - + @if (SelectClicked.HasDelegate) { NavigationManager.NavigateTo($"system/troubleshooting/playback?mediaItem={Data.MediaItemId}"); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/MediaCardPager.razor b/ErsatzTV/Shared/MediaCardPager.razor index 879f92047..46658b280 100644 --- a/ErsatzTV/Shared/MediaCardPager.razor +++ b/ErsatzTV/Shared/MediaCardPager.razor @@ -1,5 +1,4 @@ @using System.Globalization -
@if (IsSelectMode()) { @@ -105,4 +104,5 @@ int length = maximum.ToString(CultureInfo.InvariantCulture).Length; return (MarkupString)value.ToString(CultureInfo.InvariantCulture).PadLeft(length, ' ').Replace(" ", " "); } -} \ No newline at end of file + +} diff --git a/ErsatzTV/Shared/MediaItemInfoDialog.razor b/ErsatzTV/Shared/MediaItemInfoDialog.razor index 6db611d36..30d4cfcf0 100644 --- a/ErsatzTV/Shared/MediaItemInfoDialog.razor +++ b/ErsatzTV/Shared/MediaItemInfoDialog.razor @@ -56,4 +56,4 @@ private async Task CopyToClipboard(ElementReference view) => await JsRuntime.InvokeVoidAsync("clipboardCopy.copyText", view); private void Close() => MudDialog.Close(DialogResult.Ok(true)); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/MoveLocalLibraryPathDialog.razor b/ErsatzTV/Shared/MoveLocalLibraryPathDialog.razor index 65ee2f068..72514ab1e 100644 --- a/ErsatzTV/Shared/MoveLocalLibraryPathDialog.razor +++ b/ErsatzTV/Shared/MoveLocalLibraryPathDialog.razor @@ -131,4 +131,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/RemoteMediaSourceEditor.razor b/ErsatzTV/Shared/RemoteMediaSourceEditor.razor index f92d215d1..19a143cfe 100644 --- a/ErsatzTV/Shared/RemoteMediaSourceEditor.razor +++ b/ErsatzTV/Shared/RemoteMediaSourceEditor.razor @@ -62,4 +62,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/RemoteMediaSourceLibrariesEditor.razor b/ErsatzTV/Shared/RemoteMediaSourceLibrariesEditor.razor index d268cb5ec..5d55d3161 100644 --- a/ErsatzTV/Shared/RemoteMediaSourceLibrariesEditor.razor +++ b/ErsatzTV/Shared/RemoteMediaSourceLibrariesEditor.razor @@ -125,4 +125,4 @@ }); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor b/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor index be88eba8a..47224eb2e 100644 --- a/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor +++ b/ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor @@ -152,4 +152,4 @@ () => NavigationManager.NavigateTo($"media/sources/{Name.ToLowerInvariant()}")); } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/RemoteMediaSources.razor b/ErsatzTV/Shared/RemoteMediaSources.razor index 6130898a6..bfea4689a 100644 --- a/ErsatzTV/Shared/RemoteMediaSources.razor +++ b/ErsatzTV/Shared/RemoteMediaSources.razor @@ -139,4 +139,4 @@ private async Task RefreshLibraries(int mediaSourceId) => await RefreshLibrariesCommand(mediaSourceId); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/RemoveFromCollectionDialog.razor b/ErsatzTV/Shared/RemoveFromCollectionDialog.razor index 68270709b..7533cd42f 100644 --- a/ErsatzTV/Shared/RemoveFromCollectionDialog.razor +++ b/ErsatzTV/Shared/RemoveFromCollectionDialog.razor @@ -48,4 +48,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/SaveAsSmartCollectionDialog.razor b/ErsatzTV/Shared/SaveAsSmartCollectionDialog.razor index 427ec6c40..87760d338 100644 --- a/ErsatzTV/Shared/SaveAsSmartCollectionDialog.razor +++ b/ErsatzTV/Shared/SaveAsSmartCollectionDialog.razor @@ -126,4 +126,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/SchedulePlayoutReset.razor b/ErsatzTV/Shared/SchedulePlayoutReset.razor index 44d036d0c..4067437be 100644 --- a/ErsatzTV/Shared/SchedulePlayoutReset.razor +++ b/ErsatzTV/Shared/SchedulePlayoutReset.razor @@ -81,4 +81,4 @@ } private void Cancel(MouseEventArgs e) => MudDialog.Cancel(); -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/SignOutOfPlexDialog.razor b/ErsatzTV/Shared/SignOutOfPlexDialog.razor index 4c1964872..8f1475ca0 100644 --- a/ErsatzTV/Shared/SignOutOfPlexDialog.razor +++ b/ErsatzTV/Shared/SignOutOfPlexDialog.razor @@ -31,4 +31,4 @@ } } -} \ No newline at end of file +} diff --git a/ErsatzTV/Shared/_Favicons.cshtml b/ErsatzTV/Shared/_Favicons.cshtml index a4e62c8bf..4adcb3eba 100644 --- a/ErsatzTV/Shared/_Favicons.cshtml +++ b/ErsatzTV/Shared/_Favicons.cshtml @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/ErsatzTV/Validators/ChannelEditViewModelValidator.cs b/ErsatzTV/Validators/ChannelEditViewModelValidator.cs index 42a2e9ad7..4ed93cc3f 100644 --- a/ErsatzTV/Validators/ChannelEditViewModelValidator.cs +++ b/ErsatzTV/Validators/ChannelEditViewModelValidator.cs @@ -26,11 +26,11 @@ public class ChannelEditViewModelValidator : AbstractValidator x.IsEnabled == false, + x => !x.IsEnabled, () => { RuleFor(x => x.ShowInEpg) - .Must(x => x == false) + .Must(x => !x) .WithMessage("Disabled channels cannot be shown in EPG"); }); } diff --git a/ErsatzTV/Validators/ProgramScheduleItemEditViewModelValidator.cs b/ErsatzTV/Validators/ProgramScheduleItemEditViewModelValidator.cs index 34b1cf5f5..919f96762 100644 --- a/ErsatzTV/Validators/ProgramScheduleItemEditViewModelValidator.cs +++ b/ErsatzTV/Validators/ProgramScheduleItemEditViewModelValidator.cs @@ -15,7 +15,9 @@ public class ProgramScheduleItemEditViewModelValidator : AbstractValidator i.PlayoutMode == PlayoutMode.Multiple, () => { - When(i => i.MultipleMode is MultipleMode.Count, () => RuleFor(i => i.MultipleCount).NotNull().GreaterThan(0)); + When( + i => i.MultipleMode is MultipleMode.Count, + () => RuleFor(i => i.MultipleCount).NotNull().GreaterThan(0)); }); When( i => i.PlayoutMode == PlayoutMode.Duration, diff --git a/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs b/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs index e0f369ae3..6462bb295 100644 --- a/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs +++ b/ErsatzTV/ViewModels/ProgramScheduleItemEditViewModel.cs @@ -16,11 +16,11 @@ public class ProgramScheduleItemEditViewModel : INotifyPropertyChanged private int? _discardToFillAttempts; private FixedStartTimeBehavior? _fixedStartTimeBehavior; private int? _multipleCount; + private PlaybackOrder _playbackOrder; private TimeSpan? _playoutDuration; private int _playoutDurationHours; private int _playoutDurationMinutes; private TimeSpan? _startTime; - private PlaybackOrder _playbackOrder; public int Id { get; set; } public int Index { get; set; } diff --git a/ErsatzTV/_Imports.razor b/ErsatzTV/_Imports.razor index 078974477..5f0912544 100644 --- a/ErsatzTV/_Imports.razor +++ b/ErsatzTV/_Imports.razor @@ -26,4 +26,4 @@ @using ErsatzTV.Infrastructure.Data @using ErsatzTV.Shared @using ErsatzTV.ViewModels -@using Unit = Unit; \ No newline at end of file +@using Unit = Unit; diff --git a/ErsatzTV/wwwroot/browserconfig.xml b/ErsatzTV/wwwroot/browserconfig.xml index c6f1bea66..013e022dc 100644 --- a/ErsatzTV/wwwroot/browserconfig.xml +++ b/ErsatzTV/wwwroot/browserconfig.xml @@ -7,4 +7,4 @@ #da532c - \ No newline at end of file +