From 1c07df5bc31ea03d1f2e1844c454bf5624b61687 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Tue, 26 Aug 2025 22:20:35 -0500 Subject: [PATCH] use cancellation tokens in many places (#2350) * use cancellation tokens everywhere * more cancellation tokens --- .../Artworks/Queries/GetArtworkHandler.cs | 2 +- .../Channels/Commands/CreateChannelHandler.cs | 32 ++-- .../Channels/Commands/DeleteChannelHandler.cs | 7 +- .../Commands/RefreshChannelDataHandler.cs | 23 ++- .../Channels/Commands/UpdateChannelHandler.cs | 39 +++-- .../GetChannelNameByPlayoutIdHandler.cs | 2 +- .../GetChannelResolutionAndBitrateHandler.cs | 2 +- .../Commands/SaveConfigElementByKeyHandler.cs | 2 +- .../UpdateLibraryRefreshIntervalHandler.cs | 3 +- .../Commands/UpdateLoggingSettingsHandler.cs | 16 +- .../Commands/UpdatePlayoutSettingsHandler.cs | 12 +- .../Commands/UpdateXmltvSettingsHandler.cs | 18 +- .../Queries/GetConfigElementByKeyHandler.cs | 2 +- .../GetLibraryRefreshIntervalHandler.cs | 2 +- .../Queries/GetLoggingSettingsHandler.cs | 22 ++- .../Queries/GetPlayoutSettingsHandler.cs | 6 +- .../Queries/GetXmltvSettingsHandler.cs | 10 +- .../CallEmbyCollectionScannerHandler.cs | 9 +- .../Commands/CallEmbyLibraryScannerHandler.cs | 7 +- .../Commands/CallEmbyShowScannerHandler.cs | 5 +- .../SynchronizeEmbyLibrariesHandler.cs | 22 ++- .../SynchronizeEmbyMediaSourcesHandler.cs | 2 +- .../UpdateEmbyPathReplacementsHandler.cs | 10 +- .../Queries/GetAllEmbyMediaSourcesHandler.cs | 2 +- .../GetEmbyConnectionParametersHandler.cs | 11 +- .../Queries/GetEmbyMediaSourceByIdHandler.cs | 2 +- .../Commands/CreateFFmpegProfileHandler.cs | 13 +- .../Commands/DeleteFFmpegProfileHandler.cs | 7 +- .../Commands/NewFFmpegProfileHandler.cs | 2 +- .../Commands/UpdateFFmpegProfileHandler.cs | 27 +-- .../Commands/UpdateFFmpegSettingsHandler.cs | 47 +++-- .../GetFFmpegProfileByIdForApiHandler.cs | 2 +- .../Queries/GetFFmpegProfileByIdHandler.cs | 2 +- .../Queries/GetFFmpegSettingsHandler.cs | 26 +-- ...pportedHardwareAccelerationKindsHandler.cs | 10 +- .../Commands/DeleteFillerPresetHandler.cs | 7 +- .../Commands/UpdateFillerPresetHandler.cs | 10 +- .../Queries/GetFillerPresetByIdHandler.cs | 2 +- .../Commands/UpdateHDHRTunerCountHandler.cs | 2 +- .../HDHR/Queries/GetHDHRTunerCountHandler.cs | 2 +- .../HDHR/Queries/GetHDHRUUIDHandler.cs | 4 +- .../UpdateImageFolderDurationHandler.cs | 6 +- .../Queries/GetCachedImagePathHandler.cs | 17 +- .../CallJellyfinCollectionScannerHandler.cs | 7 +- .../CallJellyfinLibraryScannerHandler.cs | 7 +- .../CallJellyfinShowScannerHandler.cs | 5 +- .../SynchronizeJellyfinLibrariesHandler.cs | 9 +- .../SynchronizeJellyfinMediaSourcesHandler.cs | 2 +- .../GetAllJellyfinMediaSourcesHandler.cs | 2 +- .../GetJellyfinConnectionParametersHandler.cs | 11 +- .../Commands/CallLibraryScannerHandler.cs | 13 +- .../Commands/DeleteLocalLibraryHandler.cs | 10 +- .../Commands/MoveLocalLibraryPathHandler.cs | 28 +-- .../QueueLibraryScanByLibraryIdHandler.cs | 2 +- .../QueueShowScanByLibraryIdHandler.cs | 2 +- .../Commands/UpdateLocalLibraryHandler.cs | 12 +- .../Maintenance/Commands/EmptyTrashHandler.cs | 8 +- .../Queries/GetCollectionCardsHandler.cs | 6 +- .../Queries/GetMusicVideoCardsHandler.cs | 1 + .../GetTelevisionEpisodeCardsHandler.cs | 5 +- .../GetTelevisionSeasonCardsHandler.cs | 6 +- .../Commands/AddArtistToCollectionHandler.cs | 18 +- .../Commands/AddEpisodeToCollectionHandler.cs | 18 +- .../Commands/AddEpisodeToPlaylistHandler.cs | 18 +- .../Commands/AddImageToCollectionHandler.cs | 18 +- .../Commands/AddItemsToCollectionHandler.cs | 25 +-- .../Commands/AddItemsToPlaylistHandler.cs | 19 +- .../AddMediaItemToCollectionHandler.cs | 18 +- .../Commands/AddMovieToCollectionHandler.cs | 18 +- .../Commands/AddMovieToPlaylistHandler.cs | 18 +- .../AddMusicVideoToCollectionHandler.cs | 18 +- .../AddOtherVideoToCollectionHandler.cs | 18 +- .../Commands/AddSeasonToCollectionHandler.cs | 18 +- .../Commands/AddSeasonToPlaylistHandler.cs | 18 +- .../Commands/AddShowToCollectionHandler.cs | 18 +- .../Commands/AddShowToPlaylistHandler.cs | 18 +- .../Commands/AddSongToCollectionHandler.cs | 18 +- .../Commands/AddTraktListHandler.cs | 10 +- .../Commands/CreateSmartCollectionHandler.cs | 11 +- .../Commands/DeleteCollectionHandler.cs | 13 +- .../Commands/DeleteMultiCollectionHandler.cs | 19 +- .../Commands/DeletePlaylistGroupHandler.cs | 2 +- .../Commands/DeletePlaylistHandler.cs | 2 +- .../Commands/DeleteSmartCollectionHandler.cs | 21 ++- .../Commands/DeleteTraktListHandler.cs | 17 +- .../Commands/MatchTraktListItemsHandler.cs | 7 +- .../Commands/PreviewPlaylistPlayoutHandler.cs | 6 +- .../RemoveItemsFromCollectionHandler.cs | 25 +-- .../Commands/ReplacePlaylistItemsHandler.cs | 23 ++- .../Commands/TraktCommandBase.cs | 34 ++-- .../UpdateCollectionCustomOrderHandler.cs | 12 +- .../Commands/UpdateCollectionHandler.cs | 24 ++- .../Commands/UpdateMultiCollectionHandler.cs | 26 +-- .../Commands/UpdateSmartCollectionHandler.cs | 24 ++- .../Commands/UpdateTraktListHandler.cs | 2 +- .../Queries/GetCollectionByIdHandler.cs | 3 +- .../Queries/GetMultiCollectionByIdHandler.cs | 5 +- .../Queries/GetPlaylistByIdHandler.cs | 2 +- .../Queries/GetSmartCollectionByIdHandler.cs | 3 +- .../Queries/GetTraktListByIdHandler.cs | 3 +- .../Queries/GetMediaItemInfoHandler.cs | 2 +- .../Queries/GetRemoteStreamByIdHandler.cs | 3 +- .../CallLocalLibraryScannerHandler.cs | 9 +- .../Movies/Queries/GetMovieByIdHandler.cs | 5 +- .../Playouts/Commands/BuildPlayoutHandler.cs | 26 +-- .../Commands/CreateBlockPlayoutHandler.cs | 12 +- .../Commands/CreateClassicPlayoutHandler.cs | 18 +- .../CreateExternalJsonPlayoutHandler.cs | 12 +- .../Commands/CreateScriptedPlayoutHandler.cs | 32 ++-- .../CreateSequentialPlayoutHandler.cs | 31 ++-- .../Playouts/Commands/DeletePlayoutHandler.cs | 2 +- ...acePlayoutAlternateScheduleItemsHandler.cs | 2 +- .../TimeShiftOnDemandPlayoutHandler.cs | 2 +- .../UpdateExternalJsonPlayoutHandler.cs | 21 ++- .../UpdateOnDemandCheckpointHandler.cs | 6 +- .../Playouts/Commands/UpdatePlayoutHandler.cs | 21 ++- .../Commands/UpdateScriptedPlayoutHandler.cs | 21 ++- .../UpdateSequentialPlayoutHandler.cs | 21 ++- .../GetPlayoutAlternateSchedulesHandler.cs | 6 +- .../Playouts/Queries/GetPlayoutByIdHandler.cs | 2 +- .../CallPlexCollectionScannerHandler.cs | 9 +- .../Commands/CallPlexLibraryScannerHandler.cs | 7 +- .../Commands/CallPlexNetworkScannerHandler.cs | 9 +- .../Commands/CallPlexShowScannerHandler.cs | 5 +- .../SynchronizePlexLibrariesHandler.cs | 23 ++- .../UpdatePlexPathReplacementsHandler.cs | 15 +- .../GetPlexConnectionParametersHandler.cs | 3 +- .../GetPlexLibrariesBySourceIdHandler.cs | 4 +- .../Queries/GetPlexMediaSourceByIdHandler.cs | 2 +- .../Commands/AddProgramScheduleItemHandler.cs | 16 +- .../Commands/CopyProgramScheduleHandler.cs | 12 +- .../Commands/DeleteProgramScheduleHandler.cs | 10 +- .../ProgramScheduleItemCommandBase.cs | 5 +- .../ReplaceProgramScheduleItemsHandler.cs | 16 +- .../Commands/UpdateProgramScheduleHandler.cs | 12 +- .../Queries/GetProgramScheduleByIdHandler.cs | 3 +- .../Queries/GetProgramScheduleItemsHandler.cs | 2 +- .../Commands/DeleteCustomResolutionHandler.cs | 2 +- .../Commands/DeleteBlockGroupHandler.cs | 2 +- .../Scheduling/Commands/DeleteBlockHandler.cs | 2 +- .../Commands/DeleteDecoGroupHandler.cs | 2 +- .../Scheduling/Commands/DeleteDecoHandler.cs | 2 +- .../DeleteDecoTemplateGroupHandler.cs | 2 +- .../Commands/DeleteDecoTemplateHandler.cs | 2 +- .../Commands/DeleteTemplateGroupHandler.cs | 2 +- .../Commands/DeleteTemplateHandler.cs | 2 +- .../Commands/ErasePlayoutHistoryHandler.cs | 2 +- .../Commands/ErasePlayoutItemsHandler.cs | 2 +- .../Commands/PreviewBlockPlayoutHandler.cs | 2 +- .../Commands/ReplaceBlockItemsHandler.cs | 23 ++- .../ReplaceDecoTemplateItemsHandler.cs | 21 ++- .../ReplacePlayoutTemplateItemsHandler.cs | 2 +- .../Commands/ReplaceTemplateItemsHandler.cs | 25 ++- .../Scheduling/Commands/UpdateDecoHandler.cs | 21 ++- .../Scheduling/Queries/GetBlockByIdHandler.cs | 2 +- .../Scheduling/Queries/GetDecoByIdHandler.cs | 2 +- .../Queries/GetDecoByPlayoutIdHandler.cs | 2 +- .../Queries/GetDecoTemplateByIdHandler.cs | 2 +- .../Queries/GetTemplateByIdHandler.cs | 2 +- .../Commands/RebuildSearchIndexHandler.cs | 11 +- .../Commands/ReindexMediaItemsHandler.cs | 2 +- .../QuerySearchIndexAllItemsHandler.cs | 25 +-- .../Queries/QuerySearchIndexArtistsHandler.cs | 39 +++-- .../QuerySearchIndexEpisodesHandler.cs | 82 ++++----- .../Queries/QuerySearchIndexHandlerBase.cs | 30 ++++ .../Queries/QuerySearchIndexImagesHandler.cs | 26 ++- .../Queries/QuerySearchIndexMoviesHandler.cs | 52 +++--- .../QuerySearchIndexMusicVideosHandler.cs | 60 +++---- .../QuerySearchIndexOtherVideosHandler.cs | 44 ++--- .../QuerySearchIndexRemoteStreamsHandler.cs | 23 ++- .../Queries/QuerySearchIndexSeasonsHandler.cs | 52 +++--- .../Queries/QuerySearchIndexShowsHandler.cs | 48 +++-- .../Queries/QuerySearchIndexSongsHandler.cs | 39 +++-- .../Queries/QuerySearchTargetsHandler.cs | 63 ++++--- .../Search/Queries/SearchArtistsHandler.cs | 10 +- .../Queries/SearchCollectionsHandler.cs | 10 +- .../Queries/SearchMultiCollectionsHandler.cs | 10 +- .../Queries/SearchSmartCollectionsHandler.cs | 10 +- .../Queries/SearchTelevisionSeasonsHandler.cs | 10 +- .../Queries/SearchTelevisionShowsHandler.cs | 10 +- .../Commands/StartFFmpegSessionHandler.cs | 4 +- .../Streaming/HlsSessionWorker.cs | 8 +- .../Streaming/Queries/FFmpegProcessHandler.cs | 32 ++-- .../GetConcatProcessByChannelNumberHandler.cs | 2 +- ...tSegmenterProcessByChannelNumberHandler.cs | 2 +- .../GetHlsPlaylistByChannelNumberHandler.cs | 16 +- .../Queries/GetLastPtsTimeHandler.cs | 13 +- ...layoutItemProcessByChannelNumberHandler.cs | 71 +++++--- .../GetSeekTextSubtitleProcessHandler.cs | 14 +- ...GetWrappedProcessByChannelNumberHandler.cs | 2 +- .../ExtractEmbeddedSubtitlesHandler.cs | 25 +-- .../Queries/GetSubtitlePathByIdHandler.cs | 2 +- .../Queries/GetTelevisionSeasonByIdHandler.cs | 4 +- .../Queries/GetTelevisionShowByIdHandler.cs | 6 +- .../PrepareTroubleshootingPlaybackHandler.cs | 82 ++++++--- .../Queries/GetTroubleshootingInfoHandler.cs | 46 +++-- .../GetTroubleshootingSubtitlesHandler.cs | 2 +- .../Commands/DeleteWatermarkHandler.cs | 10 +- .../Commands/UpdateWatermarkHandler.cs | 12 +- .../Queries/GetWatermarkByIdHandler.cs | 2 +- .../Emby/EmbyPathReplacementServiceTests.cs | 54 ++++-- .../FFmpeg/FFmpegStreamSelectorTests.cs | 18 +- .../Fakes/FakeMediaCollectionRepository.cs | 40 +++-- .../Fakes/FakeTelevisionRepository.cs | 15 +- .../JellyfinPathReplacementServiceTests.cs | 60 ++++--- .../Plex/PlexPathReplacementServiceTests.cs | 54 ++++-- .../ClassicScheduling/NewPlayoutTests.cs | 24 ++- .../Scheduling/ScheduleIntegrationTests.cs | 21 ++- .../Emby/EmbyPathReplacementService.cs | 13 +- .../Extensions/MediaItemExtensions.cs | 7 + .../FFmpeg/FFmpegLibraryProcessService.cs | 11 +- ErsatzTV.Core/FFmpeg/FFmpegLocator.cs | 27 +-- ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs | 11 +- .../Emby/IEmbyPathReplacementService.cs | 7 +- .../Interfaces/FFmpeg/IFFmpegLocator.cs | 2 +- .../FFmpeg/IFFmpegProcessService.cs | 3 +- .../FFmpeg/IFFmpegStreamSelector.cs | 6 +- .../IJellyfinPathReplacementService.cs | 7 +- .../Plex/IPlexPathReplacementService.cs | 7 +- .../Repositories/IArtistRepository.cs | 1 - .../Repositories/IConfigElementRepository.cs | 10 +- .../IGraphicsElementRepository.cs | 2 +- .../Repositories/IImageRepository.cs | 4 +- .../Repositories/ILibraryRepository.cs | 2 +- .../IMediaCollectionRepository.cs | 44 +++-- .../IMediaServerMovieRepository.cs | 8 +- .../IMediaServerOtherVideoRepository.cs | 3 +- .../IMediaServerTelevisionRepository.cs | 20 ++- .../Repositories/IMediaSourceRepository.cs | 34 ++-- .../Repositories/IMetadataRepository.cs | 4 +- .../Repositories/IMovieRepository.cs | 4 +- .../Repositories/IMusicVideoRepository.cs | 1 - .../Repositories/IOtherVideoRepository.cs | 5 +- .../Repositories/IRemoteStreamRepository.cs | 4 +- .../Repositories/ISearchRepository.cs | 2 +- .../Repositories/ISongRepository.cs | 1 - .../Repositories/ITelevisionRepository.cs | 16 +- .../Repositories/ITemplateDataRepository.cs | 4 +- .../Scheduling/IPlayoutTimeShifter.cs | 2 +- .../Interfaces/Search/ISearchIndex.cs | 25 ++- .../IExternalJsonPlayoutItemProvider.cs | 3 +- .../JellyfinPathReplacementService.cs | 8 +- .../Plex/PlexPathReplacementService.cs | 8 +- .../BlockScheduling/BlockPlayoutBuilder.cs | 15 +- .../BlockPlayoutFillerBuilder.cs | 3 +- .../BlockPlayoutPreviewBuilder.cs | 2 +- .../Scheduling/Engine/ISchedulingEngine.cs | 34 +++- .../Scheduling/Engine/MarathonHelper.cs | 2 +- .../Scheduling/Engine/SchedulingEngine.cs | 55 ++++-- .../Scheduling/MediaItemsForCollection.cs | 13 +- .../Scheduling/PlaylistEnumerator.cs | 6 +- ErsatzTV.Core/Scheduling/PlayoutBuilder.cs | 64 ++++--- .../Modules/ContentModule.cs | 18 +- .../Modules/PlayoutModule.cs | 16 +- .../ScriptedPlayoutBuilder.cs | 10 +- .../YamlScheduling/EnumeratorCache.cs | 19 +- .../Handlers/YamlPlayoutGraphicsOffHandler.cs | 8 +- .../Handlers/YamlPlayoutGraphicsOnHandler.cs | 8 +- .../SequentialPlayoutBuilder.cs | 6 +- ErsatzTV.Core/Search/ISmartCollectionCache.cs | 6 +- .../Search/SearchQueryParserTests.cs | 2 +- .../Data/Repositories/ArtistRepository.cs | 12 -- .../Caching/CachingSearchRepository.cs | 3 +- .../Repositories/ConfigElementRepository.cs | 45 ++--- .../Data/Repositories/EmbyMovieRepository.cs | 27 +-- .../Repositories/EmbyTelevisionRepository.cs | 45 +++-- .../Repositories/GraphicsElementRepository.cs | 8 +- .../Data/Repositories/ImageRepository.cs | 42 ++--- .../Repositories/JellyfinMovieRepository.cs | 27 +-- .../JellyfinTelevisionRepository.cs | 41 +++-- .../Data/Repositories/LibraryRepository.cs | 9 +- .../Repositories/MediaCollectionRepository.cs | 137 +++++++++------ .../Data/Repositories/MediaItemRepository.cs | 13 +- .../Repositories/MediaSourceRepository.cs | 165 +++++++++--------- .../Data/Repositories/MetadataRepository.cs | 33 ++-- .../Data/Repositories/MovieRepository.cs | 41 ++--- .../Data/Repositories/MusicVideoRepository.cs | 17 -- .../Data/Repositories/OtherVideoRepository.cs | 42 ++--- .../Data/Repositories/PlexMovieRepository.cs | 27 +-- .../Repositories/PlexOtherVideoRepository.cs | 27 +-- .../Repositories/PlexTelevisionRepository.cs | 47 +++-- .../Repositories/RemoteStreamRepository.cs | 42 ++--- .../Data/Repositories/SearchRepository.cs | 6 +- .../Data/Repositories/SongRepository.cs | 15 -- .../Data/Repositories/TelevisionRepository.cs | 102 ++++------- .../Repositories/TemplateDataRepository.cs | 28 +-- .../ConfigElementQueryableExtensions.cs | 5 +- .../Extensions/QueryableExtensions.cs | 5 +- .../Health/Checks/FFmpegReportsHealthCheck.cs | 2 +- .../Health/Checks/FFmpegVersionHealthCheck.cs | 4 +- .../Checks/HardwareAccelerationHealthCheck.cs | 2 +- .../Health/Checks/UnavailableHealthCheck.cs | 1 + .../Health/Checks/VaapiDriverHealthCheck.cs | 3 +- .../Scheduling/PlayoutTimeShifter.cs | 10 +- .../Search/ElasticSearchIndex.cs | 30 ++-- .../Search/LuceneSearchIndex.cs | 20 ++- .../Search/SearchQueryParser.cs | 13 +- .../Search/SmartCollectionCache.cs | 16 +- .../ExternalJsonPlayoutItemProvider.cs | 32 ++-- .../Streaming/Graphics/GraphicsEngine.cs | 2 +- .../Core/FFmpeg/TranscodingTests.cs | 12 +- .../Core/Metadata/MovieFolderScannerTests.cs | 101 ++++++++--- .../SynchronizeEmbyCollectionsHandler.cs | 19 +- .../SynchronizeEmbyLibraryByIdHandler.cs | 17 +- .../SynchronizeEmbyShowByIdHandler.cs | 14 +- .../SynchronizeJellyfinCollectionsHandler.cs | 12 +- .../SynchronizeJellyfinLibraryByIdHandler.cs | 11 +- .../Commands/ScanLocalLibraryHandler.cs | 26 +-- .../SynchronizePlexCollectionsHandler.cs | 19 +- .../SynchronizePlexLibraryByIdHandler.cs | 13 +- .../SynchronizePlexNetworksHandler.cs | 13 +- .../Core/Emby/EmbyMovieLibraryScanner.cs | 3 +- .../Core/Emby/EmbyTelevisionLibraryScanner.cs | 3 +- .../Metadata/ILocalChaptersProvider.cs | 2 +- .../Metadata/ILocalSubtitlesProvider.cs | 6 +- .../Jellyfin/JellyfinMovieLibraryScanner.cs | 3 +- .../JellyfinTelevisionLibraryScanner.cs | 3 +- .../Core/Metadata/ImageFolderScanner.cs | 7 +- .../Core/Metadata/LocalChaptersProvider.cs | 7 +- .../Core/Metadata/LocalSubtitlesProvider.cs | 10 +- .../MediaServerMovieLibraryScanner.cs | 40 +++-- .../MediaServerOtherVideoLibraryScanner.cs | 44 +++-- .../MediaServerTelevisionLibraryScanner.cs | 44 +++-- .../Core/Metadata/MovieFolderScanner.cs | 23 ++- .../Core/Metadata/MusicVideoFolderScanner.cs | 19 +- .../Core/Metadata/OtherVideoFolderScanner.cs | 18 +- .../Metadata/RemoteStreamFolderScanner.cs | 4 +- .../Core/Metadata/SongFolderScanner.cs | 3 +- .../Core/Metadata/TelevisionFolderScanner.cs | 20 ++- .../Core/Plex/PlexMovieLibraryScanner.cs | 5 +- .../Core/Plex/PlexOtherVideoLibraryScanner.cs | 5 +- .../Core/Plex/PlexTelevisionLibraryScanner.cs | 5 +- ErsatzTV/Pages/ArtistList.razor | 6 +- ErsatzTV/Pages/Channels.razor | 35 +++- ErsatzTV/Pages/CollectionItems.razor | 8 +- ErsatzTV/Pages/EmbyLibrariesEditor.razor | 23 +-- ErsatzTV/Pages/EpisodeList.razor | 6 +- ErsatzTV/Pages/FFmpegEditor.razor | 156 ++++++++++------- ErsatzTV/Pages/ImageList.razor | 6 +- ErsatzTV/Pages/JellyfinLibrariesEditor.razor | 24 +-- ErsatzTV/Pages/LocalLibraries.razor | 38 ++-- ErsatzTV/Pages/MovieList.razor | 6 +- ErsatzTV/Pages/MultiSelectBase.cs | 4 +- ErsatzTV/Pages/MusicVideoList.razor | 6 +- ErsatzTV/Pages/OtherVideoList.razor | 6 +- ErsatzTV/Pages/PlexLibrariesEditor.razor | 23 +-- ErsatzTV/Pages/RemoteStreamList.razor | 6 +- ErsatzTV/Pages/ScheduleItemsEditor.razor | 4 +- ErsatzTV/Pages/SongList.razor | 6 +- .../Pages/TelevisionSeasonSearchResults.razor | 6 +- ErsatzTV/Pages/TelevisionShowList.razor | 6 +- ErsatzTV/Pages/Trash.razor | 131 +++++++------- ErsatzTV/Services/FFmpegLocatorService.cs | 7 +- .../RunOnce/LoadLoggingLevelService.cs | 10 +- .../RunOnce/RebuildSearchIndexService.cs | 2 +- .../RemoteMediaSourceLibrariesEditor.razor | 67 ++++--- 356 files changed, 3430 insertions(+), 2454 deletions(-) create mode 100644 ErsatzTV.Application/Search/Queries/QuerySearchIndexHandlerBase.cs diff --git a/ErsatzTV.Application/Artworks/Queries/GetArtworkHandler.cs b/ErsatzTV.Application/Artworks/Queries/GetArtworkHandler.cs index f09acaa68..77f27ed53 100644 --- a/ErsatzTV.Application/Artworks/Queries/GetArtworkHandler.cs +++ b/ErsatzTV.Application/Artworks/Queries/GetArtworkHandler.cs @@ -21,7 +21,7 @@ public class GetArtworkHandler(IDbContextFactory dbContextFactory) Option artwork = await dbContext.Artwork .AsNoTracking() - .SelectOneAsync(a => a.Id, a => a.Id == request.Id) + .SelectOneAsync(a => a.Id, a => a.Id == request.Id, cancellationToken) .MapT(Project); return artwork.ToEither(BaseError.New("Artwork not found")); diff --git a/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs b/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs index 01cd3ba04..15e1d27e4 100644 --- a/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/CreateChannelHandler.cs @@ -22,7 +22,7 @@ public class CreateChannelHandler( CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(c => PersistChannel(dbContext, c)); } @@ -35,11 +35,11 @@ public class CreateChannelHandler( return new CreateChannelResult(channel.Id); } - private static async Task> Validate(TvContext dbContext, CreateChannel request) => - (ValidateName(request), await ValidateNumber(dbContext, request), - await FFmpegProfileMustExist(dbContext, request), - await WatermarkMustExist(dbContext, request), - await FillerPresetMustExist(dbContext, request)) + private static async Task> Validate(TvContext dbContext, CreateChannel request, CancellationToken cancellationToken) => + (ValidateName(request), await ValidateNumber(dbContext, request, cancellationToken), + await FFmpegProfileMustExist(dbContext, request, cancellationToken), + await WatermarkMustExist(dbContext, request, cancellationToken), + await FillerPresetMustExist(dbContext, request, cancellationToken)) .Apply(( name, number, @@ -113,10 +113,11 @@ public class CreateChannelHandler( private static async Task> ValidateNumber( TvContext dbContext, - CreateChannel createChannel) + CreateChannel createChannel, + CancellationToken cancellationToken) { Option maybeExistingChannel = await dbContext.Channels - .SelectOneAsync(c => c.Number, c => c.Number == createChannel.Number); + .SelectOneAsync(c => c.Number, c => c.Number == createChannel.Number, cancellationToken); return maybeExistingChannel.Match>( _ => BaseError.New("Channel number must be unique"), () => @@ -132,9 +133,10 @@ public class CreateChannelHandler( private static Task> FFmpegProfileMustExist( TvContext dbContext, - CreateChannel createChannel) => + CreateChannel createChannel, + CancellationToken cancellationToken) => dbContext.FFmpegProfiles - .CountAsync(p => p.Id == createChannel.FFmpegProfileId) + .CountAsync(p => p.Id == createChannel.FFmpegProfileId, cancellationToken) .Map(Optional) .Filter(c => c > 0) .MapT(_ => createChannel.FFmpegProfileId) @@ -142,7 +144,8 @@ public class CreateChannelHandler( private static async Task>> WatermarkMustExist( TvContext dbContext, - CreateChannel createChannel) + CreateChannel createChannel, + CancellationToken cancellationToken) { if (createChannel.WatermarkId is null) { @@ -150,7 +153,7 @@ public class CreateChannelHandler( } return await dbContext.ChannelWatermarks - .CountAsync(w => w.Id == createChannel.WatermarkId) + .CountAsync(w => w.Id == createChannel.WatermarkId, cancellationToken) .Map(Optional) .Filter(c => c > 0) .MapT(_ => Optional(createChannel.WatermarkId)) @@ -159,7 +162,8 @@ public class CreateChannelHandler( private static async Task>> FillerPresetMustExist( TvContext dbContext, - CreateChannel createChannel) + CreateChannel createChannel, + CancellationToken cancellationToken) { if (createChannel.FallbackFillerId is null) { @@ -168,7 +172,7 @@ public class CreateChannelHandler( return await dbContext.FillerPresets .Filter(fp => fp.FillerKind == FillerKind.Fallback) - .CountAsync(w => w.Id == createChannel.FallbackFillerId) + .CountAsync(w => w.Id == createChannel.FallbackFillerId, cancellationToken) .Map(Optional) .Filter(c => c > 0) .MapT(_ => Optional(createChannel.FallbackFillerId)) diff --git a/ErsatzTV.Application/Channels/Commands/DeleteChannelHandler.cs b/ErsatzTV.Application/Channels/Commands/DeleteChannelHandler.cs index 2ea9db31d..b397daf5b 100644 --- a/ErsatzTV.Application/Channels/Commands/DeleteChannelHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/DeleteChannelHandler.cs @@ -31,7 +31,7 @@ public class DeleteChannelHandler : IRequestHandler> Handle(DeleteChannel request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await ChannelMustExist(dbContext, request); + Validation validation = await ChannelMustExist(dbContext, request, cancellationToken); return await validation.Apply(c => DoDeletion(dbContext, c, cancellationToken)); } @@ -57,10 +57,11 @@ public class DeleteChannelHandler : IRequestHandler> ChannelMustExist( TvContext dbContext, - DeleteChannel deleteChannel) + DeleteChannel deleteChannel, + CancellationToken cancellationToken) { Option maybeChannel = await dbContext.Channels - .SelectOneAsync(c => c.Id, c => c.Id == deleteChannel.ChannelId); + .SelectOneAsync(c => c.Id, c => c.Id == deleteChannel.ChannelId, cancellationToken); return maybeChannel.ToValidation($"Channel {deleteChannel.ChannelId} does not exist."); } } diff --git a/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs b/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs index dc9382c8a..1bc2462fe 100644 --- a/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/RefreshChannelDataHandler.cs @@ -183,7 +183,7 @@ public class RefreshChannelDataHandler : IRequestHandler new XmlWriterSettings { Async = true, ConformanceLevel = ConformanceLevel.Fragment }); int daysToBuild = await _configElementRepository - .GetValue(ConfigElementKey.XmltvDaysToBuild) + .GetValue(ConfigElementKey.XmltvDaysToBuild, cancellationToken) .IfNoneAsync(2); DateTimeOffset finish = DateTimeOffset.UtcNow.AddDays(daysToBuild); @@ -210,7 +210,8 @@ public class RefreshChannelDataHandler : IRequestHandler songTemplate, otherVideoTemplate, minifier, - xml); + xml, + cancellationToken); break; case PlayoutScheduleKind.Block: var blockSorted = playouts @@ -228,7 +229,8 @@ public class RefreshChannelDataHandler : IRequestHandler songTemplate, otherVideoTemplate, minifier, - xml); + xml, + cancellationToken); break; case PlayoutScheduleKind.ExternalJson: var externalJsonSorted = (await CollectExternalJsonItems(playout.ScheduleFile)) @@ -245,7 +247,8 @@ public class RefreshChannelDataHandler : IRequestHandler songTemplate, otherVideoTemplate, minifier, - xml); + xml, + cancellationToken); break; } } @@ -268,10 +271,11 @@ public class RefreshChannelDataHandler : IRequestHandler Template songTemplate, Template otherVideoTemplate, XmlMinifier minifier, - XmlWriter xml) + XmlWriter xml, + CancellationToken cancellationToken) { XmltvTimeZone xmltvTimeZone = await _configElementRepository - .GetValue(ConfigElementKey.XmltvTimeZone) + .GetValue(ConfigElementKey.XmltvTimeZone, cancellationToken) .IfNoneAsync(XmltvTimeZone.Local); // skip all filler that isn't pre-roll @@ -357,14 +361,15 @@ public class RefreshChannelDataHandler : IRequestHandler Template songTemplate, Template otherVideoTemplate, XmlMinifier minifier, - XmlWriter xml) + XmlWriter xml, + CancellationToken cancellationToken) { XmltvTimeZone xmltvTimeZone = await _configElementRepository - .GetValue(ConfigElementKey.XmltvTimeZone) + .GetValue(ConfigElementKey.XmltvTimeZone, cancellationToken) .IfNoneAsync(XmltvTimeZone.Local); XmltvBlockBehavior xmltvBlockBehavior = await _configElementRepository - .GetValue(ConfigElementKey.XmltvBlockBehavior) + .GetValue(ConfigElementKey.XmltvBlockBehavior, cancellationToken) .IfNoneAsync(XmltvBlockBehavior.SplitTimeEvenly); var groups = sorted.GroupBy(s => new { s.GuideStart, s.GuideFinish, s.GuideGroup }); diff --git a/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs b/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs index 128e05a7b..90f4195e0 100644 --- a/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs +++ b/ErsatzTV.Application/Channels/Commands/UpdateChannelHandler.cs @@ -23,11 +23,15 @@ public class UpdateChannelHandler( CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request, cancellationToken)); } - private async Task ApplyUpdateRequest(TvContext dbContext, Channel c, UpdateChannel update) + private async Task ApplyUpdateRequest( + TvContext dbContext, + Channel c, + UpdateChannel update, + CancellationToken cancellationToken) { c.Name = update.Name; c.Number = update.Number; @@ -86,7 +90,7 @@ public class UpdateChannelHandler( { await dbContext.Entry(c) .Collection(channel => channel.Artwork) - .LoadAsync(); + .LoadAsync(cancellationToken); foreach (Artwork artwork in c.Artwork.Where(x => x.ArtworkKind is ArtworkKind.Logo).ToList()) { @@ -99,38 +103,42 @@ public class UpdateChannelHandler( c.StreamingMode = update.StreamingMode; c.WatermarkId = update.WatermarkId; c.FallbackFillerId = update.FallbackFillerId; - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); searchTargets.SearchTargetsChanged(); if (c.SubtitleMode != ChannelSubtitleMode.None) { Option maybePlayout = await dbContext.Playouts - .SelectOneAsync(p => p.ChannelId, p => p.ChannelId == c.Id); + .SelectOneAsync(p => p.ChannelId, p => p.ChannelId == c.Id, cancellationToken); foreach (Playout playout in maybePlayout) { - await workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id)); + await workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(playout.Id), cancellationToken); } } - await workerChannel.WriteAsync(new RefreshChannelList()); + await workerChannel.WriteAsync(new RefreshChannelList(), cancellationToken); return ProjectToViewModel(c); } - private static async Task> Validate(TvContext dbContext, UpdateChannel request) => - (await ChannelMustExist(dbContext, request), ValidateName(request), - await ValidateNumber(dbContext, request)) + private static async Task> Validate( + TvContext dbContext, + UpdateChannel request, + CancellationToken cancellationToken) => + (await ChannelMustExist(dbContext, request, cancellationToken), ValidateName(request), + await ValidateNumber(dbContext, request, cancellationToken)) .Apply((channelToUpdate, _, _) => channelToUpdate); private static Task> ChannelMustExist( TvContext dbContext, - UpdateChannel updateChannel) => + UpdateChannel updateChannel, + CancellationToken cancellationToken) => dbContext.Channels .Include(c => c.Artwork) .Include(c => c.Watermark) - .SelectOneAsync(c => c.Id, c => c.Id == updateChannel.ChannelId) + .SelectOneAsync(c => c.Id, c => c.Id == updateChannel.ChannelId, cancellationToken) .Map(o => o.ToValidation("Channel does not exist.")); private static Validation ValidateName(UpdateChannel updateChannel) => @@ -139,10 +147,11 @@ public class UpdateChannelHandler( private static async Task> ValidateNumber( TvContext dbContext, - UpdateChannel updateChannel) + UpdateChannel updateChannel, + CancellationToken cancellationToken) { int matchId = await dbContext.Channels - .SelectOneAsync(c => c.Number, c => c.Number == updateChannel.Number) + .SelectOneAsync(c => c.Number, c => c.Number == updateChannel.Number, cancellationToken) .Match(c => c.Id, () => updateChannel.ChannelId); if (matchId == updateChannel.ChannelId) diff --git a/ErsatzTV.Application/Channels/Queries/GetChannelNameByPlayoutIdHandler.cs b/ErsatzTV.Application/Channels/Queries/GetChannelNameByPlayoutIdHandler.cs index 379d496ab..1413a35b9 100644 --- a/ErsatzTV.Application/Channels/Queries/GetChannelNameByPlayoutIdHandler.cs +++ b/ErsatzTV.Application/Channels/Queries/GetChannelNameByPlayoutIdHandler.cs @@ -16,7 +16,7 @@ public class GetChannelNameByPlayoutIdHandler : IRequestHandler p.Channel) - .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId) + .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId, cancellationToken) .MapT(p => p.Channel.Name); } } diff --git a/ErsatzTV.Application/Channels/Queries/GetChannelResolutionAndBitrateHandler.cs b/ErsatzTV.Application/Channels/Queries/GetChannelResolutionAndBitrateHandler.cs index 3813d7098..3c32f6b17 100644 --- a/ErsatzTV.Application/Channels/Queries/GetChannelResolutionAndBitrateHandler.cs +++ b/ErsatzTV.Application/Channels/Queries/GetChannelResolutionAndBitrateHandler.cs @@ -18,7 +18,7 @@ public class GetChannelResolutionAndBitrateHandler(IDbContextFactory .AsNoTracking() .Include(c => c.FFmpegProfile) .ThenInclude(ff => ff.Resolution) - .SelectOneAsync(c => c.Number, c => c.Number == request.ChannelNumber); + .SelectOneAsync(c => c.Number, c => c.Number == request.ChannelNumber, cancellationToken); return maybeChannel.Map(c => Mapper.ProjectToViewModel( c.FFmpegProfile.Resolution, diff --git a/ErsatzTV.Application/Configuration/Commands/SaveConfigElementByKeyHandler.cs b/ErsatzTV.Application/Configuration/Commands/SaveConfigElementByKeyHandler.cs index a075a5377..5374ff79e 100644 --- a/ErsatzTV.Application/Configuration/Commands/SaveConfigElementByKeyHandler.cs +++ b/ErsatzTV.Application/Configuration/Commands/SaveConfigElementByKeyHandler.cs @@ -10,5 +10,5 @@ public class SaveConfigElementByKeyHandler : IRequestHandler - await _configElementRepository.Upsert(request.Key, request.Value); + await _configElementRepository.Upsert(request.Key, request.Value, cancellationToken); } diff --git a/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs b/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs index 3449103b6..5027ddeb5 100644 --- a/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs +++ b/ErsatzTV.Application/Configuration/Commands/UpdateLibraryRefreshIntervalHandler.cs @@ -18,7 +18,8 @@ public class UpdateLibraryRefreshIntervalHandler : Validate(request) .MapT(_ => _configElementRepository.Upsert( ConfigElementKey.LibraryRefreshInterval, - request.LibraryRefreshInterval)) + request.LibraryRefreshInterval, + cancellationToken)) .Bind(v => v.ToEitherAsync()); private static Task> Validate(UpdateLibraryRefreshInterval request) => diff --git a/ErsatzTV.Application/Configuration/Commands/UpdateLoggingSettingsHandler.cs b/ErsatzTV.Application/Configuration/Commands/UpdateLoggingSettingsHandler.cs index e9b879511..657f4fb41 100644 --- a/ErsatzTV.Application/Configuration/Commands/UpdateLoggingSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Commands/UpdateLoggingSettingsHandler.cs @@ -19,36 +19,36 @@ public class UpdateLoggingSettingsHandler : IRequestHandler> Handle( UpdateLoggingSettings request, - CancellationToken cancellationToken) => await ApplyUpdate(request.LoggingSettings); + CancellationToken cancellationToken) => await ApplyUpdate(request.LoggingSettings, cancellationToken); - private async Task ApplyUpdate(LoggingSettingsViewModel loggingSettings) + private async Task ApplyUpdate(LoggingSettingsViewModel loggingSettings, CancellationToken cancellationToken) { - await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, loggingSettings.DefaultMinimumLogLevel); + await _configElementRepository.Upsert(ConfigElementKey.MinimumLogLevel, loggingSettings.DefaultMinimumLogLevel, cancellationToken); _loggingLevelSwitches.DefaultLevelSwitch.MinimumLevel = loggingSettings.DefaultMinimumLogLevel; await _configElementRepository.Upsert( ConfigElementKey.MinimumLogLevelScanning, - loggingSettings.ScanningMinimumLogLevel); + loggingSettings.ScanningMinimumLogLevel, cancellationToken); _loggingLevelSwitches.ScanningLevelSwitch.MinimumLevel = loggingSettings.ScanningMinimumLogLevel; await _configElementRepository.Upsert( ConfigElementKey.MinimumLogLevelScheduling, - loggingSettings.SchedulingMinimumLogLevel); + loggingSettings.SchedulingMinimumLogLevel, cancellationToken); _loggingLevelSwitches.SchedulingLevelSwitch.MinimumLevel = loggingSettings.SchedulingMinimumLogLevel; await _configElementRepository.Upsert( ConfigElementKey.MinimumLogLevelSearching, - loggingSettings.SearchingMinimumLogLevel); + loggingSettings.SearchingMinimumLogLevel, cancellationToken); _loggingLevelSwitches.SearchingLevelSwitch.MinimumLevel = loggingSettings.SearchingMinimumLogLevel; await _configElementRepository.Upsert( ConfigElementKey.MinimumLogLevelStreaming, - loggingSettings.StreamingMinimumLogLevel); + loggingSettings.StreamingMinimumLogLevel, cancellationToken); _loggingLevelSwitches.StreamingLevelSwitch.MinimumLevel = loggingSettings.StreamingMinimumLogLevel; await _configElementRepository.Upsert( ConfigElementKey.MinimumLogLevelHttp, - loggingSettings.HttpMinimumLogLevel); + loggingSettings.HttpMinimumLogLevel, cancellationToken); _loggingLevelSwitches.HttpLevelSwitch.MinimumLevel = loggingSettings.HttpMinimumLogLevel; return Unit.Default; diff --git a/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs b/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs index af98d955c..80841f0d4 100644 --- a/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Commands/UpdatePlayoutSettingsHandler.cs @@ -32,24 +32,24 @@ public class UpdatePlayoutSettingsHandler : IRequestHandler validation = await Validate(request); - return await validation.Apply(_ => ApplyUpdate(dbContext, request.PlayoutSettings)); + return await validation.Apply(_ => ApplyUpdate(dbContext, request.PlayoutSettings, cancellationToken)); } - private async Task ApplyUpdate(TvContext dbContext, PlayoutSettingsViewModel playoutSettings) + private async Task ApplyUpdate(TvContext dbContext, PlayoutSettingsViewModel playoutSettings, CancellationToken cancellationToken) { - await _configElementRepository.Upsert(ConfigElementKey.PlayoutDaysToBuild, playoutSettings.DaysToBuild); + await _configElementRepository.Upsert(ConfigElementKey.PlayoutDaysToBuild, playoutSettings.DaysToBuild, cancellationToken); await _configElementRepository.Upsert( ConfigElementKey.PlayoutSkipMissingItems, - playoutSettings.SkipMissingItems); + playoutSettings.SkipMissingItems, cancellationToken); // continue all playouts to proper number of days List playouts = await dbContext.Playouts .Include(p => p.Channel) - .ToListAsync(); + .ToListAsync(cancellationToken); foreach (int playoutId in playouts.OrderBy(p => decimal.Parse(p.Channel.Number, CultureInfo.InvariantCulture)) .Map(p => p.Id)) { - await _workerChannel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Continue)); + await _workerChannel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Continue), cancellationToken); } return Unit.Default; diff --git a/ErsatzTV.Application/Configuration/Commands/UpdateXmltvSettingsHandler.cs b/ErsatzTV.Application/Configuration/Commands/UpdateXmltvSettingsHandler.cs index ccd44d9ba..40100a040 100644 --- a/ErsatzTV.Application/Configuration/Commands/UpdateXmltvSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Commands/UpdateXmltvSettingsHandler.cs @@ -20,7 +20,7 @@ public class UpdateXmltvSettingsHandler( { int playoutDaysToBuild = await configElementRepository - .GetValue(ConfigElementKey.PlayoutDaysToBuild) + .GetValue(ConfigElementKey.PlayoutDaysToBuild, cancellationToken) .IfNoneAsync(2); if (playoutDaysToBuild < request.XmltvSettings.DaysToBuild) @@ -29,20 +29,20 @@ public class UpdateXmltvSettingsHandler( $"XMLTV days to build ({request.XmltvSettings.DaysToBuild}) cannot be greater than Playout days to build ({playoutDaysToBuild})"); } - return await ApplyUpdate(request.XmltvSettings); + return await ApplyUpdate(request.XmltvSettings, cancellationToken); } - private async Task ApplyUpdate(XmltvSettingsViewModel xmltvSettings) + private async Task ApplyUpdate(XmltvSettingsViewModel xmltvSettings, CancellationToken cancellationToken) { - await configElementRepository.Upsert(ConfigElementKey.XmltvTimeZone, xmltvSettings.TimeZone); - await configElementRepository.Upsert(ConfigElementKey.XmltvDaysToBuild, xmltvSettings.DaysToBuild); - await configElementRepository.Upsert(ConfigElementKey.XmltvBlockBehavior, xmltvSettings.BlockBehavior); + await configElementRepository.Upsert(ConfigElementKey.XmltvTimeZone, xmltvSettings.TimeZone, cancellationToken); + await configElementRepository.Upsert(ConfigElementKey.XmltvDaysToBuild, xmltvSettings.DaysToBuild, cancellationToken); + await configElementRepository.Upsert(ConfigElementKey.XmltvBlockBehavior, xmltvSettings.BlockBehavior, cancellationToken); - await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(); + await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - foreach (string channelNumber in await dbContext.Channels.Map(c => c.Number).ToListAsync()) + foreach (string channelNumber in await dbContext.Channels.Map(c => c.Number).ToListAsync(cancellationToken)) { - await workerChannel.WriteAsync(new RefreshChannelData(channelNumber)); + await workerChannel.WriteAsync(new RefreshChannelData(channelNumber), cancellationToken); } return Unit.Default; diff --git a/ErsatzTV.Application/Configuration/Queries/GetConfigElementByKeyHandler.cs b/ErsatzTV.Application/Configuration/Queries/GetConfigElementByKeyHandler.cs index 391601ad4..1274f4701 100644 --- a/ErsatzTV.Application/Configuration/Queries/GetConfigElementByKeyHandler.cs +++ b/ErsatzTV.Application/Configuration/Queries/GetConfigElementByKeyHandler.cs @@ -13,5 +13,5 @@ public class GetConfigElementByKeyHandler : IRequestHandler> Handle( GetConfigElementByKey request, CancellationToken cancellationToken) => - _configElementRepository.GetConfigElement(request.Key).MapT(ProjectToViewModel); + _configElementRepository.GetConfigElement(request.Key, cancellationToken).MapT(ProjectToViewModel); } diff --git a/ErsatzTV.Application/Configuration/Queries/GetLibraryRefreshIntervalHandler.cs b/ErsatzTV.Application/Configuration/Queries/GetLibraryRefreshIntervalHandler.cs index 289d12eeb..a26f06a1a 100644 --- a/ErsatzTV.Application/Configuration/Queries/GetLibraryRefreshIntervalHandler.cs +++ b/ErsatzTV.Application/Configuration/Queries/GetLibraryRefreshIntervalHandler.cs @@ -11,6 +11,6 @@ public class GetLibraryRefreshIntervalHandler : IRequestHandler Handle(GetLibraryRefreshInterval request, CancellationToken cancellationToken) => - _configElementRepository.GetValue(ConfigElementKey.LibraryRefreshInterval) + _configElementRepository.GetValue(ConfigElementKey.LibraryRefreshInterval, cancellationToken) .Map(result => result.IfNone(6)); } diff --git a/ErsatzTV.Application/Configuration/Queries/GetLoggingSettingsHandler.cs b/ErsatzTV.Application/Configuration/Queries/GetLoggingSettingsHandler.cs index 12341d430..081232298 100644 --- a/ErsatzTV.Application/Configuration/Queries/GetLoggingSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Queries/GetLoggingSettingsHandler.cs @@ -14,22 +14,32 @@ public class GetLoggingSettingsHandler : IRequestHandler Handle(GetLoggingSettings request, CancellationToken cancellationToken) { Option maybeDefaultLevel = - await _configElementRepository.GetValue(ConfigElementKey.MinimumLogLevel); + await _configElementRepository.GetValue(ConfigElementKey.MinimumLogLevel, cancellationToken); Option maybeScanningLevel = - await _configElementRepository.GetValue(ConfigElementKey.MinimumLogLevelScanning); + await _configElementRepository.GetValue( + ConfigElementKey.MinimumLogLevelScanning, + cancellationToken); Option maybeSchedulingLevel = - await _configElementRepository.GetValue(ConfigElementKey.MinimumLogLevelScheduling); + await _configElementRepository.GetValue( + ConfigElementKey.MinimumLogLevelScheduling, + cancellationToken); Option maybeSearchingLevel = - await _configElementRepository.GetValue(ConfigElementKey.MinimumLogLevelSearching); + await _configElementRepository.GetValue( + ConfigElementKey.MinimumLogLevelSearching, + cancellationToken); Option maybeStreamingLevel = - await _configElementRepository.GetValue(ConfigElementKey.MinimumLogLevelStreaming); + await _configElementRepository.GetValue( + ConfigElementKey.MinimumLogLevelStreaming, + cancellationToken); Option maybeHttpLevel = - await _configElementRepository.GetValue(ConfigElementKey.MinimumLogLevelHttp); + await _configElementRepository.GetValue( + ConfigElementKey.MinimumLogLevelHttp, + cancellationToken); return new LoggingSettingsViewModel { diff --git a/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs b/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs index 6807799a6..d7bca37e9 100644 --- a/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Queries/GetPlayoutSettingsHandler.cs @@ -12,10 +12,12 @@ public class GetPlayoutSettingsHandler : IRequestHandler Handle(GetPlayoutSettings request, CancellationToken cancellationToken) { - Option daysToBuild = await _configElementRepository.GetValue(ConfigElementKey.PlayoutDaysToBuild); + Option daysToBuild = await _configElementRepository.GetValue( + ConfigElementKey.PlayoutDaysToBuild, + cancellationToken); Option skipMissingItems = - await _configElementRepository.GetValue(ConfigElementKey.PlayoutSkipMissingItems); + await _configElementRepository.GetValue(ConfigElementKey.PlayoutSkipMissingItems, cancellationToken); return new PlayoutSettingsViewModel { diff --git a/ErsatzTV.Application/Configuration/Queries/GetXmltvSettingsHandler.cs b/ErsatzTV.Application/Configuration/Queries/GetXmltvSettingsHandler.cs index 7c62f6b8e..65f012f91 100644 --- a/ErsatzTV.Application/Configuration/Queries/GetXmltvSettingsHandler.cs +++ b/ErsatzTV.Application/Configuration/Queries/GetXmltvSettingsHandler.cs @@ -8,13 +8,17 @@ public class GetXmltvSettingsHandler(IConfigElementRepository configElementRepos { public async Task Handle(GetXmltvSettings request, CancellationToken cancellationToken) { - Option daysToBuild = await configElementRepository.GetValue(ConfigElementKey.XmltvDaysToBuild); + Option daysToBuild = await configElementRepository.GetValue( + ConfigElementKey.XmltvDaysToBuild, + cancellationToken); Option maybeTimeZone = - await configElementRepository.GetValue(ConfigElementKey.XmltvTimeZone); + await configElementRepository.GetValue(ConfigElementKey.XmltvTimeZone, cancellationToken); Option maybeBlockBehavior = - await configElementRepository.GetValue(ConfigElementKey.XmltvBlockBehavior); + await configElementRepository.GetValue( + ConfigElementKey.XmltvBlockBehavior, + cancellationToken); return new XmltvSettingsViewModel { diff --git a/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs b/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs index 006170def..3b8bcb765 100644 --- a/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/CallEmbyCollectionScannerHandler.cs @@ -26,7 +26,7 @@ public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler> Handle(SynchronizeEmbyCollections request, CancellationToken cancellationToken) { - Validation validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -40,10 +40,13 @@ public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler GetLastScan(TvContext dbContext, SynchronizeEmbyCollections request) + protected override async Task GetLastScan( + TvContext dbContext, + SynchronizeEmbyCollections request, + CancellationToken cancellationToken) { DateTime minDateTime = await dbContext.EmbyMediaSources - .SelectOneAsync(l => l.Id, l => l.Id == request.EmbyMediaSourceId) + .SelectOneAsync(l => l.Id, l => l.Id == request.EmbyMediaSourceId, cancellationToken) .Match(l => l.LastCollectionsScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); return new DateTimeOffset(minDateTime, TimeSpan.Zero); diff --git a/ErsatzTV.Application/Emby/Commands/CallEmbyLibraryScannerHandler.cs b/ErsatzTV.Application/Emby/Commands/CallEmbyLibraryScannerHandler.cs index 6bc38a352..4538f1b62 100644 --- a/ErsatzTV.Application/Emby/Commands/CallEmbyLibraryScannerHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/CallEmbyLibraryScannerHandler.cs @@ -38,7 +38,7 @@ public class CallEmbyLibraryScannerHandler : CallLibraryScannerHandler validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -77,10 +77,11 @@ public class CallEmbyLibraryScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - ISynchronizeEmbyLibraryById request) + ISynchronizeEmbyLibraryById request, + CancellationToken cancellationToken) { DateTime minDateTime = await dbContext.EmbyLibraries - .SelectOneAsync(l => l.Id, l => l.Id == request.EmbyLibraryId) + .SelectOneAsync(l => l.Id, l => l.Id == request.EmbyLibraryId, cancellationToken) .Match(l => l.LastScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); return new DateTimeOffset(minDateTime, TimeSpan.Zero); diff --git a/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs b/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs index ae616ca0d..c36709993 100644 --- a/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/CallEmbyShowScannerHandler.cs @@ -31,7 +31,7 @@ public class CallEmbyShowScannerHandler : CallLibraryScannerHandler validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -67,7 +67,8 @@ public class CallEmbyShowScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - SynchronizeEmbyShowById request) => + SynchronizeEmbyShowById request, + CancellationToken cancellationToken) => Task.FromResult(DateTimeOffset.MinValue); protected override bool ScanIsRequired( diff --git a/ErsatzTV.Application/Emby/Commands/SynchronizeEmbyLibrariesHandler.cs b/ErsatzTV.Application/Emby/Commands/SynchronizeEmbyLibrariesHandler.cs index ffb451d2b..e324a3c4d 100644 --- a/ErsatzTV.Application/Emby/Commands/SynchronizeEmbyLibrariesHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/SynchronizeEmbyLibrariesHandler.cs @@ -33,18 +33,21 @@ public class SynchronizeEmbyLibrariesHandler : IRequestHandler> Handle( SynchronizeEmbyLibraries request, CancellationToken cancellationToken) => - Validate(request) - .MapT(SynchronizeLibraries) + Validate(request, cancellationToken) + .MapT(p => SynchronizeLibraries(p, cancellationToken)) .Bind(v => v.ToEitherAsync()); - private Task> Validate(SynchronizeEmbyLibraries request) => - MediaSourceMustExist(request) + private Task> Validate( + SynchronizeEmbyLibraries request, + CancellationToken cancellationToken) => + MediaSourceMustExist(request, cancellationToken) .BindT(MediaSourceMustHaveActiveConnection) .BindT(MediaSourceMustHaveApiKey); private Task> MediaSourceMustExist( - SynchronizeEmbyLibraries request) => - _mediaSourceRepository.GetEmby(request.EmbyMediaSourceId) + SynchronizeEmbyLibraries request, + CancellationToken cancellationToken) => + _mediaSourceRepository.GetEmby(request.EmbyMediaSourceId, cancellationToken) .Map(o => o.ToValidation("Emby media source does not exist.")); private Validation MediaSourceMustHaveActiveConnection( @@ -65,7 +68,9 @@ public class SynchronizeEmbyLibrariesHandler : IRequestHandler("Emby media source requires an api key"); } - private async Task SynchronizeLibraries(ConnectionParameters connectionParameters) + private async Task SynchronizeLibraries( + ConnectionParameters connectionParameters, + CancellationToken cancellationToken) { Either> maybeLibraries = await _embyApiClient.GetLibraries( connectionParameters.ActiveConnection.Address, @@ -91,7 +96,8 @@ public class SynchronizeEmbyLibrariesHandler : IRequestHandler mediaSources = await _mediaSourceRepository.GetAllEmby(); + List mediaSources = await _mediaSourceRepository.GetAllEmby(cancellationToken); foreach (EmbyMediaSource mediaSource in mediaSources) { await _scannerWorkerChannel.WriteAsync(new SynchronizeEmbyLibraries(mediaSource.Id), cancellationToken); diff --git a/ErsatzTV.Application/Emby/Commands/UpdateEmbyPathReplacementsHandler.cs b/ErsatzTV.Application/Emby/Commands/UpdateEmbyPathReplacementsHandler.cs index aecbcb66c..66661fbed 100644 --- a/ErsatzTV.Application/Emby/Commands/UpdateEmbyPathReplacementsHandler.cs +++ b/ErsatzTV.Application/Emby/Commands/UpdateEmbyPathReplacementsHandler.cs @@ -15,7 +15,7 @@ public class UpdateEmbyPathReplacementsHandler : IRequestHandler> Handle( UpdateEmbyPathReplacements request, CancellationToken cancellationToken) => - Validate(request) + Validate(request, cancellationToken) .MapT(pms => MergePathReplacements(request, pms)) .Bind(v => v.ToEitherAsync()); @@ -37,12 +37,12 @@ public class UpdateEmbyPathReplacementsHandler : IRequestHandler new() { Id = vm.Id, EmbyPath = vm.EmbyPath, LocalPath = vm.LocalPath }; - private Task> Validate(UpdateEmbyPathReplacements request) => - EmbyMediaSourceMustExist(request); + private Task> Validate(UpdateEmbyPathReplacements request, CancellationToken cancellationToken) => + EmbyMediaSourceMustExist(request, cancellationToken); private Task> EmbyMediaSourceMustExist( - UpdateEmbyPathReplacements request) => - _mediaSourceRepository.GetEmby(request.EmbyMediaSourceId) + UpdateEmbyPathReplacements request, CancellationToken cancellationToken) => + _mediaSourceRepository.GetEmby(request.EmbyMediaSourceId, cancellationToken) .Map(v => v.ToValidation( $"Emby media source {request.EmbyMediaSourceId} does not exist.")); } diff --git a/ErsatzTV.Application/Emby/Queries/GetAllEmbyMediaSourcesHandler.cs b/ErsatzTV.Application/Emby/Queries/GetAllEmbyMediaSourcesHandler.cs index c17c48220..d0f762b95 100644 --- a/ErsatzTV.Application/Emby/Queries/GetAllEmbyMediaSourcesHandler.cs +++ b/ErsatzTV.Application/Emby/Queries/GetAllEmbyMediaSourcesHandler.cs @@ -13,5 +13,5 @@ public class GetAllEmbyMediaSourcesHandler : IRequestHandler> Handle( GetAllEmbyMediaSources request, CancellationToken cancellationToken) => - _mediaSourceRepository.GetAllEmby().Map(list => list.Map(ProjectToViewModel).ToList()); + _mediaSourceRepository.GetAllEmby(cancellationToken).Map(list => list.Map(ProjectToViewModel).ToList()); } diff --git a/ErsatzTV.Application/Emby/Queries/GetEmbyConnectionParametersHandler.cs b/ErsatzTV.Application/Emby/Queries/GetEmbyConnectionParametersHandler.cs index 46d91b1e9..c0a90b671 100644 --- a/ErsatzTV.Application/Emby/Queries/GetEmbyConnectionParametersHandler.cs +++ b/ErsatzTV.Application/Emby/Queries/GetEmbyConnectionParametersHandler.cs @@ -34,7 +34,7 @@ public class GetEmbyConnectionParametersHandler : IRequestHandler maybeParameters = - await Validate() + await Validate(cancellationToken) .MapT(cp => new EmbyConnectionParametersViewModel(cp.ActiveConnection.Address, cp.ApiKey)) .Map(v => v.ToEither()); @@ -47,13 +47,14 @@ public class GetEmbyConnectionParametersHandler : IRequestHandler error); } - private Task> Validate() => - EmbyMediaSourceMustExist() + private Task> Validate(CancellationToken cancellationToken) => + EmbyMediaSourceMustExist(cancellationToken) .BindT(MediaSourceMustHaveActiveConnection) .BindT(MediaSourceMustHaveApiKey); - private Task> EmbyMediaSourceMustExist() => - _mediaSourceRepository.GetAllEmby().Map(list => list.HeadOrNone()) + private Task> + EmbyMediaSourceMustExist(CancellationToken cancellationToken) => + _mediaSourceRepository.GetAllEmby(cancellationToken).Map(list => list.HeadOrNone()) .Map(v => v.ToValidation( "Emby media source does not exist.")); diff --git a/ErsatzTV.Application/Emby/Queries/GetEmbyMediaSourceByIdHandler.cs b/ErsatzTV.Application/Emby/Queries/GetEmbyMediaSourceByIdHandler.cs index ba2bfe4d8..ad7ad7ea8 100644 --- a/ErsatzTV.Application/Emby/Queries/GetEmbyMediaSourceByIdHandler.cs +++ b/ErsatzTV.Application/Emby/Queries/GetEmbyMediaSourceByIdHandler.cs @@ -14,5 +14,5 @@ public class public Task> Handle( GetEmbyMediaSourceById request, CancellationToken cancellationToken) => - _mediaSourceRepository.GetEmby(request.EmbyMediaSourceId).MapT(ProjectToViewModel); + _mediaSourceRepository.GetEmby(request.EmbyMediaSourceId, cancellationToken).MapT(ProjectToViewModel); } diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs index e6fc93604..8fe10d89d 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/CreateFFmpegProfileHandler.cs @@ -24,7 +24,7 @@ public class CreateFFmpegProfileHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(profile => PersistFFmpegProfile(dbContext, profile)); } @@ -40,8 +40,10 @@ public class CreateFFmpegProfileHandler : private static async Task> Validate( TvContext dbContext, - CreateFFmpegProfile request) => - (ValidateName(request), ValidateThreadCount(request), await ResolutionMustExist(dbContext, request)) + CreateFFmpegProfile request, + CancellationToken cancellationToken) => + (ValidateName(request), ValidateThreadCount(request), + await ResolutionMustExist(dbContext, request, cancellationToken)) .Apply((name, threadCount, resolutionId) => new FFmpegProfile { Name = name, @@ -84,9 +86,10 @@ public class CreateFFmpegProfileHandler : private static Task> ResolutionMustExist( TvContext dbContext, - CreateFFmpegProfile createFFmpegProfile) => + CreateFFmpegProfile createFFmpegProfile, + CancellationToken cancellationToken) => dbContext.Resolutions - .SelectOneAsync(r => r.Id, r => r.Id == createFFmpegProfile.ResolutionId) + .SelectOneAsync(r => r.Id, r => r.Id == createFFmpegProfile.ResolutionId, cancellationToken) .MapT(r => r.Id) .Map(o => o.ToValidation($"[Resolution] {createFFmpegProfile.ResolutionId} does not exist")); } diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/DeleteFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/DeleteFFmpegProfileHandler.cs index b7432d8dd..088380bbc 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/DeleteFFmpegProfileHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/DeleteFFmpegProfileHandler.cs @@ -23,7 +23,7 @@ public class DeleteFFmpegProfileHandler : IRequestHandler validation = await FFmpegProfileMustExist(dbContext, request); + Validation validation = await FFmpegProfileMustExist(dbContext, request, cancellationToken); return await validation.Apply(p => DoDeletion(dbContext, p)); } @@ -37,8 +37,9 @@ public class DeleteFFmpegProfileHandler : IRequestHandler> FFmpegProfileMustExist( TvContext dbContext, - DeleteFFmpegProfile request) => + DeleteFFmpegProfile request, + CancellationToken cancellationToken) => dbContext.FFmpegProfiles - .SelectOneAsync(p => p.Id, p => p.Id == request.FFmpegProfileId) + .SelectOneAsync(p => p.Id, p => p.Id == request.FFmpegProfileId, cancellationToken) .Map(o => o.ToValidation($"FFmpegProfile {request.FFmpegProfileId} does not exist")); } diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/NewFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/NewFFmpegProfileHandler.cs index b63065b06..aa2fca683 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/NewFFmpegProfileHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/NewFFmpegProfileHandler.cs @@ -18,7 +18,7 @@ public class NewFFmpegProfileHandler : IRequestHandler(ConfigElementKey.FFmpegDefaultResolutionId) + .GetValue(ConfigElementKey.FFmpegDefaultResolutionId, cancellationToken) .IfNoneAsync(0); List allResolutions = await dbContext.Resolutions diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs index d0631a046..ce44a5a66 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegProfileHandler.cs @@ -24,14 +24,15 @@ public class CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(p => ApplyUpdateRequest(dbContext, p, request)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(p => ApplyUpdateRequest(dbContext, p, request, cancellationToken)); } private async Task ApplyUpdateRequest( TvContext dbContext, FFmpegProfile p, - UpdateFFmpegProfile update) + UpdateFFmpegProfile update, + CancellationToken cancellationToken) { p.Name = update.Name; p.ThreadCount = update.ThreadCount; @@ -63,7 +64,7 @@ public class p.AudioSampleRate = update.AudioSampleRate; p.NormalizeFramerate = update.NormalizeFramerate; p.DeinterlaceVideo = update.DeinterlaceVideo; - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); _searchTargets.SearchTargetsChanged(); @@ -72,16 +73,19 @@ public class private static async Task> Validate( TvContext dbContext, - UpdateFFmpegProfile request) => - (await FFmpegProfileMustExist(dbContext, request), ValidateName(request), ValidateThreadCount(request), - await ResolutionMustExist(dbContext, request)) + UpdateFFmpegProfile request, + CancellationToken cancellationToken) => + (await FFmpegProfileMustExist(dbContext, request, cancellationToken), ValidateName(request), + ValidateThreadCount(request), + await ResolutionMustExist(dbContext, request, cancellationToken)) .Apply((ffmpegProfileToUpdate, _, _, _) => ffmpegProfileToUpdate); private static Task> FFmpegProfileMustExist( TvContext dbContext, - UpdateFFmpegProfile updateFFmpegProfile) => + UpdateFFmpegProfile updateFFmpegProfile, + CancellationToken cancellationToken) => dbContext.FFmpegProfiles - .SelectOneAsync(p => p.Id, p => p.Id == updateFFmpegProfile.FFmpegProfileId) + .SelectOneAsync(p => p.Id, p => p.Id == updateFFmpegProfile.FFmpegProfileId, cancellationToken) .Map(o => o.ToValidation("FFmpegProfile does not exist.")); private static Validation ValidateName(UpdateFFmpegProfile updateFFmpegProfile) => @@ -93,9 +97,10 @@ public class private static Task> ResolutionMustExist( TvContext dbContext, - UpdateFFmpegProfile updateFFmpegProfile) => + UpdateFFmpegProfile updateFFmpegProfile, + CancellationToken cancellationToken) => dbContext.Resolutions - .SelectOneAsync(r => r.Id, r => r.Id == updateFFmpegProfile.ResolutionId) + .SelectOneAsync(r => r.Id, r => r.Id == updateFFmpegProfile.ResolutionId, cancellationToken) .MapT(r => r.Id) .Map(o => o.ToValidation($"[Resolution] {updateFFmpegProfile.ResolutionId} does not exist")); } diff --git a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs index 1d5a30d68..2d97809a5 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Commands/UpdateFFmpegSettingsHandler.cs @@ -29,7 +29,7 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler Validate(request) - .MapT(_ => ApplyUpdate(request)) + .MapT(_ => ApplyUpdate(request, cancellationToken)) .Bind(v => v.ToEitherAsync()); private async Task> Validate(UpdateFFmpegSettings request) => @@ -69,19 +69,22 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler ApplyUpdate(UpdateFFmpegSettings request) + private async Task ApplyUpdate(UpdateFFmpegSettings request, CancellationToken cancellationToken) { - await _configElementRepository.Upsert(ConfigElementKey.FFmpegPath, request.Settings.FFmpegPath); - await _configElementRepository.Upsert(ConfigElementKey.FFprobePath, request.Settings.FFprobePath); + await _configElementRepository.Upsert(ConfigElementKey.FFmpegPath, request.Settings.FFmpegPath, cancellationToken); + await _configElementRepository.Upsert(ConfigElementKey.FFprobePath, request.Settings.FFprobePath, cancellationToken); await _configElementRepository.Upsert( ConfigElementKey.FFmpegDefaultProfileId, - request.Settings.DefaultFFmpegProfileId.ToString(CultureInfo.InvariantCulture)); + request.Settings.DefaultFFmpegProfileId.ToString(CultureInfo.InvariantCulture), + cancellationToken); await _configElementRepository.Upsert( ConfigElementKey.FFmpegSaveReports, - request.Settings.SaveReports.ToString()); + request.Settings.SaveReports.ToString(), + cancellationToken); await _configElementRepository.Upsert( ConfigElementKey.FFmpegHlsDirectOutputFormat, - request.Settings.HlsDirectOutputFormat); + request.Settings.HlsDirectOutputFormat, + cancellationToken); if (request.Settings.SaveReports && !Directory.Exists(FileSystemLayout.FFmpegReportsFolder)) { @@ -90,11 +93,13 @@ public class UpdateFFmpegSettingsHandler : IRequestHandler.None)); + await _workerChannel.WriteAsync(new ExtractEmbeddedSubtitles(Option.None), cancellationToken); } if (request.Settings.GlobalWatermarkId is not null) { await _configElementRepository.Upsert( ConfigElementKey.FFmpegGlobalWatermarkId, - request.Settings.GlobalWatermarkId.Value); + request.Settings.GlobalWatermarkId.Value, + cancellationToken); } else { - await _configElementRepository.Delete(ConfigElementKey.FFmpegGlobalWatermarkId); + await _configElementRepository.Delete(ConfigElementKey.FFmpegGlobalWatermarkId, cancellationToken); } if (request.Settings.GlobalFallbackFillerId is not null) { await _configElementRepository.Upsert( ConfigElementKey.FFmpegGlobalFallbackFillerId, - request.Settings.GlobalFallbackFillerId.Value); + request.Settings.GlobalFallbackFillerId.Value, + cancellationToken); } else { - await _configElementRepository.Delete(ConfigElementKey.FFmpegGlobalFallbackFillerId); + await _configElementRepository.Delete(ConfigElementKey.FFmpegGlobalFallbackFillerId, cancellationToken); } await _configElementRepository.Upsert( ConfigElementKey.FFmpegSegmenterTimeout, - request.Settings.HlsSegmenterIdleTimeout); + request.Settings.HlsSegmenterIdleTimeout, + cancellationToken); await _configElementRepository.Upsert( ConfigElementKey.FFmpegWorkAheadSegmenters, - request.Settings.WorkAheadSegmenterLimit); + request.Settings.WorkAheadSegmenterLimit, + cancellationToken); await _configElementRepository.Upsert( ConfigElementKey.FFmpegInitialSegmentCount, - request.Settings.InitialSegmentCount); + request.Settings.InitialSegmentCount, + cancellationToken); return Unit.Default; } diff --git a/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdForApiHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdForApiHandler.cs index 8fea0ef14..55f4d85e7 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdForApiHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdForApiHandler.cs @@ -22,7 +22,7 @@ public class await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); return await dbContext.FFmpegProfiles .Include(p => p.Resolution) - .SelectOneAsync(p => p.Id, p => p.Id == request.Id) + .SelectOneAsync(p => p.Id, p => p.Id == request.Id, cancellationToken) .MapT(ProjectToFullResponseModel); } } diff --git a/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdHandler.cs index ea1ccc6f4..3ca0bf007 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegProfileByIdHandler.cs @@ -19,7 +19,7 @@ public class GetFFmpegProfileByIdHandler : IRequestHandler p.Resolution) - .SelectOneAsync(p => p.Id, p => p.Id == request.Id) + .SelectOneAsync(p => p.Id, p => p.Id == request.Id, cancellationToken) .MapT(ProjectToViewModel); } } diff --git a/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs index 8ab4e1ea9..b66cf764d 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Queries/GetFFmpegSettingsHandler.cs @@ -15,30 +15,30 @@ public class GetFFmpegSettingsHandler : IRequestHandler ffmpegPath = await _configElementRepository.GetValue(ConfigElementKey.FFmpegPath); - Option ffprobePath = await _configElementRepository.GetValue(ConfigElementKey.FFprobePath); + Option ffmpegPath = await _configElementRepository.GetValue(ConfigElementKey.FFmpegPath, cancellationToken); + Option ffprobePath = await _configElementRepository.GetValue(ConfigElementKey.FFprobePath, cancellationToken); Option defaultFFmpegProfileId = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegDefaultProfileId); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegDefaultProfileId, cancellationToken); Option saveReports = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegSaveReports); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegSaveReports, cancellationToken); Option preferredAudioLanguageCode = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegPreferredLanguageCode); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegPreferredLanguageCode, cancellationToken); Option useEmbeddedSubtitles = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegUseEmbeddedSubtitles); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegUseEmbeddedSubtitles, cancellationToken); Option extractEmbeddedSubtitles = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegExtractEmbeddedSubtitles); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegExtractEmbeddedSubtitles, cancellationToken); Option watermark = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegGlobalWatermarkId); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegGlobalWatermarkId, cancellationToken); Option fallbackFiller = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegGlobalFallbackFillerId); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegGlobalFallbackFillerId, cancellationToken); Option hlsSegmenterIdleTimeout = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegSegmenterTimeout); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegSegmenterTimeout, cancellationToken); Option workAheadSegmenterLimit = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegWorkAheadSegmenters); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegWorkAheadSegmenters, cancellationToken); Option initialSegmentCount = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegInitialSegmentCount); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegInitialSegmentCount, cancellationToken); Option outputFormatKind = - await _configElementRepository.GetValue(ConfigElementKey.FFmpegHlsDirectOutputFormat); + await _configElementRepository.GetValue(ConfigElementKey.FFmpegHlsDirectOutputFormat, cancellationToken); var result = new FFmpegSettingsViewModel { diff --git a/ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs b/ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs index 3b109e44a..27a55e980 100644 --- a/ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs +++ b/ErsatzTV.Application/FFmpegProfiles/Queries/GetSupportedHardwareAccelerationKindsHandler.cs @@ -28,7 +28,7 @@ public class CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext); + Validation validation = await Validate(dbContext, cancellationToken); return await validation.Match( GetHardwareAccelerationKinds, @@ -69,11 +69,11 @@ public class return result; } - private static async Task> Validate(TvContext dbContext) => - await FFmpegPathMustExist(dbContext); + private static async Task> Validate(TvContext dbContext, CancellationToken cancellationToken) => + await FFmpegPathMustExist(dbContext, cancellationToken); - private static Task> FFmpegPathMustExist(TvContext dbContext) => - dbContext.ConfigElements.GetValue(ConfigElementKey.FFmpegPath) + private static Task> FFmpegPathMustExist(TvContext dbContext, CancellationToken cancellationToken) => + dbContext.ConfigElements.GetValue(ConfigElementKey.FFmpegPath, cancellationToken) .FilterT(File.Exists) .Map(maybePath => maybePath.ToValidation("FFmpeg path does not exist on filesystem")); } diff --git a/ErsatzTV.Application/Filler/Commands/DeleteFillerPresetHandler.cs b/ErsatzTV.Application/Filler/Commands/DeleteFillerPresetHandler.cs index b03ab19e8..b3ae50580 100644 --- a/ErsatzTV.Application/Filler/Commands/DeleteFillerPresetHandler.cs +++ b/ErsatzTV.Application/Filler/Commands/DeleteFillerPresetHandler.cs @@ -18,7 +18,7 @@ public class DeleteFillerPresetHandler : IRequestHandler validation = await FillerPresetMustExist(dbContext, request); + Validation validation = await FillerPresetMustExist(dbContext, request, cancellationToken); return await validation.Apply(ps => DoDeletion(dbContext, ps)); } @@ -30,8 +30,9 @@ public class DeleteFillerPresetHandler : IRequestHandler> FillerPresetMustExist( TvContext dbContext, - DeleteFillerPreset request) => + DeleteFillerPreset request, + CancellationToken cancellationToken) => dbContext.FillerPresets - .SelectOneAsync(fp => fp.Id, ps => ps.Id == request.FillerPresetId) + .SelectOneAsync(fp => fp.Id, ps => ps.Id == request.FillerPresetId, cancellationToken) .Map(o => o.ToValidation($"FillerPreset {request.FillerPresetId} does not exist.")); } diff --git a/ErsatzTV.Application/Filler/Commands/UpdateFillerPresetHandler.cs b/ErsatzTV.Application/Filler/Commands/UpdateFillerPresetHandler.cs index adb3f1060..7c77d746b 100644 --- a/ErsatzTV.Application/Filler/Commands/UpdateFillerPresetHandler.cs +++ b/ErsatzTV.Application/Filler/Commands/UpdateFillerPresetHandler.cs @@ -16,7 +16,10 @@ public class UpdateFillerPresetHandler : IRequestHandler> Handle(UpdateFillerPreset request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await FillerPresetMustExist(dbContext, request); + Validation validation = await FillerPresetMustExist( + dbContext, + request, + cancellationToken); return await validation.Apply(ps => ApplyUpdateRequest(dbContext, ps, request)); } @@ -49,8 +52,9 @@ public class UpdateFillerPresetHandler : IRequestHandler> FillerPresetMustExist( TvContext dbContext, - UpdateFillerPreset request) => + UpdateFillerPreset request, + CancellationToken cancellationToken) => dbContext.FillerPresets - .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.Id) + .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.Id, cancellationToken) .Map(o => o.ToValidation("FillerPreset does not exist")); } diff --git a/ErsatzTV.Application/Filler/Queries/GetFillerPresetByIdHandler.cs b/ErsatzTV.Application/Filler/Queries/GetFillerPresetByIdHandler.cs index 99e4447f5..140b132f7 100644 --- a/ErsatzTV.Application/Filler/Queries/GetFillerPresetByIdHandler.cs +++ b/ErsatzTV.Application/Filler/Queries/GetFillerPresetByIdHandler.cs @@ -16,7 +16,7 @@ public class GetFillerPresetByIdHandler(IDbContextFactory dbContextFa return await dbContext.FillerPresets .AsNoTracking() .Include(i => i.Playlist) - .SelectOneAsync(c => c.Id, c => c.Id == request.Id) + .SelectOneAsync(c => c.Id, c => c.Id == request.Id, cancellationToken) .MapT(ProjectToViewModel); } } diff --git a/ErsatzTV.Application/HDHR/Commands/UpdateHDHRTunerCountHandler.cs b/ErsatzTV.Application/HDHR/Commands/UpdateHDHRTunerCountHandler.cs index ec4aff717..53f720f7b 100644 --- a/ErsatzTV.Application/HDHR/Commands/UpdateHDHRTunerCountHandler.cs +++ b/ErsatzTV.Application/HDHR/Commands/UpdateHDHRTunerCountHandler.cs @@ -18,7 +18,7 @@ public class UpdateHDHRTunerCountHandler : IRequestHandler _configElementRepository.Upsert( ConfigElementKey.HDHRTunerCount, - request.TunerCount.ToString(CultureInfo.InvariantCulture))) + request.TunerCount.ToString(CultureInfo.InvariantCulture), cancellationToken)) .Bind(v => v.ToEitherAsync()); private static Task> Validate(UpdateHDHRTunerCount request) => diff --git a/ErsatzTV.Application/HDHR/Queries/GetHDHRTunerCountHandler.cs b/ErsatzTV.Application/HDHR/Queries/GetHDHRTunerCountHandler.cs index fd67f683b..2be7cb649 100644 --- a/ErsatzTV.Application/HDHR/Queries/GetHDHRTunerCountHandler.cs +++ b/ErsatzTV.Application/HDHR/Queries/GetHDHRTunerCountHandler.cs @@ -11,6 +11,6 @@ public class GetHDHRTunerCountHandler : IRequestHandler _configElementRepository = configElementRepository; public Task Handle(GetHDHRTunerCount request, CancellationToken cancellationToken) => - _configElementRepository.GetValue(ConfigElementKey.HDHRTunerCount) + _configElementRepository.GetValue(ConfigElementKey.HDHRTunerCount, cancellationToken) .Map(result => result.IfNone(2)); } diff --git a/ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs b/ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs index fefdbf34f..f39024e41 100644 --- a/ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs +++ b/ErsatzTV.Application/HDHR/Queries/GetHDHRUUIDHandler.cs @@ -12,11 +12,11 @@ public class GetHDHRUUIDHandler : IRequestHandler public async Task Handle(GetHDHRUUID request, CancellationToken cancellationToken) { - Option maybeGuid = await _configElementRepository.GetValue(ConfigElementKey.HDHRUUID); + Option maybeGuid = await _configElementRepository.GetValue(ConfigElementKey.HDHRUUID, cancellationToken); return await maybeGuid.IfNoneAsync(async () => { var guid = Guid.NewGuid(); - await _configElementRepository.Upsert(ConfigElementKey.HDHRUUID, guid); + await _configElementRepository.Upsert(ConfigElementKey.HDHRUUID, guid, cancellationToken); return guid; }); } diff --git a/ErsatzTV.Application/Images/Commands/UpdateImageFolderDurationHandler.cs b/ErsatzTV.Application/Images/Commands/UpdateImageFolderDurationHandler.cs index 57d242918..e005edf1e 100644 --- a/ErsatzTV.Application/Images/Commands/UpdateImageFolderDurationHandler.cs +++ b/ErsatzTV.Application/Images/Commands/UpdateImageFolderDurationHandler.cs @@ -28,7 +28,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory dbCon else { Option maybeExisting = await dbContext.ImageFolderDurations - .SelectOneAsync(ifd => ifd.LibraryFolderId, ifd => ifd.LibraryFolderId == request.LibraryFolderId); + .SelectOneAsync(ifd => ifd.LibraryFolderId, ifd => ifd.LibraryFolderId == request.LibraryFolderId, cancellationToken); if (maybeExisting.IsNone) { @@ -53,7 +53,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory dbCon Option maybeFolder = await dbContext.LibraryFolders .AsNoTracking() .Include(lf => lf.ImageFolderDuration) - .SelectOneAsync(lf => lf.Id, lf => lf.Id == request.LibraryFolderId); + .SelectOneAsync(lf => lf.Id, lf => lf.Id == request.LibraryFolderId, cancellationToken); var queue = new Queue(); foreach (LibraryFolder libraryFolder in maybeFolder) @@ -67,7 +67,7 @@ public class UpdateImageFolderDurationHandler(IDbContextFactory dbCon Option maybeParent = await dbContext.LibraryFolders .AsNoTracking() .Include(lf => lf.ImageFolderDuration) - .SelectOneAsync(lf => lf.Id, lf => lf.Id == currentFolder.ParentId); + .SelectOneAsync(lf => lf.Id, lf => lf.Id == currentFolder.ParentId, cancellationToken); if (maybeParent.IsNone) { diff --git a/ErsatzTV.Application/Images/Queries/GetCachedImagePathHandler.cs b/ErsatzTV.Application/Images/Queries/GetCachedImagePathHandler.cs index 5cb0b645a..d0097ca2b 100644 --- a/ErsatzTV.Application/Images/Queries/GetCachedImagePathHandler.cs +++ b/ErsatzTV.Application/Images/Queries/GetCachedImagePathHandler.cs @@ -30,15 +30,16 @@ public class GetCachedImagePath request, CancellationToken cancellationToken) { - Validation validation = await Validate(); + Validation validation = await Validate(cancellationToken); return await validation.Match( - ffmpegPath => Handle(ffmpegPath, request), + ffmpegPath => Handle(ffmpegPath, request, cancellationToken), error => Task.FromResult>(error.Join())); } private async Task> Handle( string ffmpegPath, - GetCachedImagePath request) + GetCachedImagePath request, + CancellationToken cancellationToken) { try { @@ -75,7 +76,7 @@ public class withExtension, request.MaxHeight.Value); - CommandResult resize = await process.ExecuteAsync(); + CommandResult resize = await process.ExecuteAsync(cancellationToken); if (resize.ExitCode != 0) { @@ -106,11 +107,11 @@ public class } } - private async Task> Validate() => - await ValidateFFmpegPath(); + private async Task> Validate(CancellationToken cancellationToken) => + await ValidateFFmpegPath(cancellationToken); - private Task> ValidateFFmpegPath() => - _configElementRepository.GetValue(ConfigElementKey.FFmpegPath) + private Task> ValidateFFmpegPath(CancellationToken cancellationToken) => + _configElementRepository.GetValue(ConfigElementKey.FFmpegPath, cancellationToken) .FilterT(File.Exists) .Map(ffmpegPath => ffmpegPath.ToValidation("FFmpeg path does not exist on the file system")); } diff --git a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs index eea784cda..ac8c98cb7 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinCollectionScannerHandler.cs @@ -26,7 +26,7 @@ public class CallJellyfinCollectionScannerHandler : CallLibraryScannerHandler> Handle(SynchronizeJellyfinCollections request, CancellationToken cancellationToken) { - Validation validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -42,10 +42,11 @@ public class CallJellyfinCollectionScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - SynchronizeJellyfinCollections request) + SynchronizeJellyfinCollections request, + CancellationToken cancellationToken) { DateTime minDateTime = await dbContext.JellyfinMediaSources - .SelectOneAsync(l => l.Id, l => l.Id == request.JellyfinMediaSourceId) + .SelectOneAsync(l => l.Id, l => l.Id == request.JellyfinMediaSourceId, cancellationToken) .Match(l => l.LastCollectionsScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); return new DateTimeOffset(minDateTime, TimeSpan.Zero); diff --git a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinLibraryScannerHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinLibraryScannerHandler.cs index 96f8f1333..8fb68974a 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinLibraryScannerHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinLibraryScannerHandler.cs @@ -39,7 +39,7 @@ public class CallJellyfinLibraryScannerHandler : CallLibraryScannerHandler validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -78,10 +78,11 @@ public class CallJellyfinLibraryScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - ISynchronizeJellyfinLibraryById request) + ISynchronizeJellyfinLibraryById request, + CancellationToken cancellationToken) { DateTime minDateTime = await dbContext.JellyfinLibraries - .SelectOneAsync(l => l.Id, l => l.Id == request.JellyfinLibraryId) + .SelectOneAsync(l => l.Id, l => l.Id == request.JellyfinLibraryId, cancellationToken) .Match(l => l.LastScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); return new DateTimeOffset(minDateTime, TimeSpan.Zero); diff --git a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs index d4132cc6b..212a1b196 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/CallJellyfinShowScannerHandler.cs @@ -31,7 +31,7 @@ public class CallJellyfinShowScannerHandler : CallLibraryScannerHandler validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -67,7 +67,8 @@ public class CallJellyfinShowScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - SynchronizeJellyfinShowById request) => + SynchronizeJellyfinShowById request, + CancellationToken cancellationToken) => Task.FromResult(DateTimeOffset.MinValue); protected override bool ScanIsRequired( diff --git a/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinLibrariesHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinLibrariesHandler.cs index f96d4852e..508a9d081 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinLibrariesHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinLibrariesHandler.cs @@ -35,7 +35,7 @@ public class SynchronizeJellyfinLibraries request, CancellationToken cancellationToken) => Validate(request) - .MapT(SynchronizeLibraries) + .MapT(p => SynchronizeLibraries(p, cancellationToken)) .Bind(v => v.ToEitherAsync()); private Task> Validate(SynchronizeJellyfinLibraries request) => @@ -66,7 +66,9 @@ public class .ToValidation("Jellyfin media source requires an api key"); } - private async Task SynchronizeLibraries(ConnectionParameters connectionParameters) + private async Task SynchronizeLibraries( + ConnectionParameters connectionParameters, + CancellationToken cancellationToken) { Either> maybeLibraries = await _jellyfinApiClient.GetLibraries( connectionParameters.ActiveConnection.Address, @@ -93,7 +95,8 @@ public class connectionParameters.JellyfinMediaSource.Id, toAdd, toRemove, - toUpdate); + toUpdate, + cancellationToken); if (ids.Count != 0) { await _searchIndex.RemoveItems(ids); diff --git a/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinMediaSourcesHandler.cs b/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinMediaSourcesHandler.cs index 6813239ae..b40ff56cd 100644 --- a/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinMediaSourcesHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Commands/SynchronizeJellyfinMediaSourcesHandler.cs @@ -23,7 +23,7 @@ public class SynchronizeJellyfinMediaSourcesHandler : IRequestHandler mediaSources = await _mediaSourceRepository.GetAllJellyfin(); + List mediaSources = await _mediaSourceRepository.GetAllJellyfin(cancellationToken); foreach (JellyfinMediaSource mediaSource in mediaSources) { await _scannerWorkerChannel.WriteAsync(new SynchronizeJellyfinLibraries(mediaSource.Id), cancellationToken); diff --git a/ErsatzTV.Application/Jellyfin/Queries/GetAllJellyfinMediaSourcesHandler.cs b/ErsatzTV.Application/Jellyfin/Queries/GetAllJellyfinMediaSourcesHandler.cs index b969b9947..632c47e14 100644 --- a/ErsatzTV.Application/Jellyfin/Queries/GetAllJellyfinMediaSourcesHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Queries/GetAllJellyfinMediaSourcesHandler.cs @@ -15,5 +15,5 @@ public class public Task> Handle( GetAllJellyfinMediaSources request, CancellationToken cancellationToken) => - _mediaSourceRepository.GetAllJellyfin().Map(list => list.Map(ProjectToViewModel).ToList()); + _mediaSourceRepository.GetAllJellyfin(cancellationToken).Map(list => list.Map(ProjectToViewModel).ToList()); } diff --git a/ErsatzTV.Application/Jellyfin/Queries/GetJellyfinConnectionParametersHandler.cs b/ErsatzTV.Application/Jellyfin/Queries/GetJellyfinConnectionParametersHandler.cs index 426bc2dac..3d2135f61 100644 --- a/ErsatzTV.Application/Jellyfin/Queries/GetJellyfinConnectionParametersHandler.cs +++ b/ErsatzTV.Application/Jellyfin/Queries/GetJellyfinConnectionParametersHandler.cs @@ -29,7 +29,7 @@ public class GetJellyfinConnectionParametersHandler : IRequestHandler maybeParameters = - await Validate() + await Validate(cancellationToken) .MapT(cp => new JellyfinConnectionParametersViewModel(cp.ActiveConnection.Address)) .Map(v => v.ToEither()); @@ -42,12 +42,13 @@ public class GetJellyfinConnectionParametersHandler : IRequestHandler error); } - private Task> Validate() => - JellyfinMediaSourceMustExist() + private Task> Validate(CancellationToken cancellationToken) => + JellyfinMediaSourceMustExist(cancellationToken) .BindT(MediaSourceMustHaveActiveConnection); - private Task> JellyfinMediaSourceMustExist() => - _mediaSourceRepository.GetAllJellyfin().Map(list => list.HeadOrNone()) + private Task> JellyfinMediaSourceMustExist( + CancellationToken cancellationToken) => + _mediaSourceRepository.GetAllJellyfin(cancellationToken).Map(list => list.HeadOrNone()) .Map(v => v.ToValidation( "Jellyfin media source does not exist.")); diff --git a/ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs b/ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs index 999df9ff8..ed31b4f97 100644 --- a/ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/CallLibraryScannerHandler.cs @@ -169,20 +169,23 @@ public abstract class CallLibraryScannerHandler } } - protected abstract Task GetLastScan(TvContext dbContext, TRequest request); + protected abstract Task GetLastScan( + TvContext dbContext, + TRequest request, + CancellationToken cancellationToken); protected abstract bool ScanIsRequired(DateTimeOffset lastScan, int libraryRefreshInterval, TRequest request); - protected async Task> Validate(TRequest request) + protected async Task> Validate(TRequest request, CancellationToken cancellationToken) { int libraryRefreshInterval = await _configElementRepository - .GetValue(ConfigElementKey.LibraryRefreshInterval) + .GetValue(ConfigElementKey.LibraryRefreshInterval, cancellationToken) .IfNoneAsync(0); libraryRefreshInterval = Math.Clamp(libraryRefreshInterval, 0, 999_999); - await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); + await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - DateTimeOffset lastScan = await GetLastScan(dbContext, request); + DateTimeOffset lastScan = await GetLastScan(dbContext, request, cancellationToken); if (!ScanIsRequired(lastScan, libraryRefreshInterval, request)) { return new ScanIsNotRequired(); diff --git a/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryHandler.cs b/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryHandler.cs index 9512c0afe..670354bd3 100644 --- a/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/DeleteLocalLibraryHandler.cs @@ -25,7 +25,10 @@ public class DeleteLocalLibraryHandler : LocalLibraryHandlerBase, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await LocalLibraryMustExist(dbContext, request); + Validation validation = await LocalLibraryMustExist( + dbContext, + request, + cancellationToken); return await validation.Apply(localLibrary => DoDeletion(dbContext, localLibrary)); } @@ -77,8 +80,9 @@ public class DeleteLocalLibraryHandler : LocalLibraryHandlerBase, private static Task> LocalLibraryMustExist( TvContext dbContext, - DeleteLocalLibrary request) => + DeleteLocalLibrary request, + CancellationToken cancellationToken) => dbContext.LocalLibraries - .SelectOneAsync(ll => ll.Id, ll => ll.Id == request.LocalLibraryId) + .SelectOneAsync(ll => ll.Id, ll => ll.Id == request.LocalLibraryId, cancellationToken) .Map(o => o.ToValidation($"Local library {request.LocalLibraryId} does not exist.")); } diff --git a/ErsatzTV.Application/Libraries/Commands/MoveLocalLibraryPathHandler.cs b/ErsatzTV.Application/Libraries/Commands/MoveLocalLibraryPathHandler.cs index ca58c908a..699fc10e8 100644 --- a/ErsatzTV.Application/Libraries/Commands/MoveLocalLibraryPathHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/MoveLocalLibraryPathHandler.cs @@ -38,17 +38,17 @@ public class MoveLocalLibraryPathHandler : IRequestHandler validation = await Validate(dbContext, request); - return await validation.Apply(parameters => MovePath(dbContext, parameters)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(parameters => MovePath(dbContext, parameters, cancellationToken)); } - private async Task MovePath(TvContext dbContext, Parameters parameters) + private async Task MovePath(TvContext dbContext, Parameters parameters, CancellationToken cancellationToken) { LibraryPath path = parameters.LibraryPath; LocalLibrary newLibrary = parameters.Library; path.LibraryId = newLibrary.Id; - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { List ids = await dbContext.Connection.QueryAsync( @"SELECT MediaItem.Id FROM MediaItem WHERE LibraryPathId = @LibraryPathId", @@ -57,14 +57,14 @@ public class MoveLocalLibraryPathHandler : IRequestHandler maybeMediaItem = await _searchRepository.GetItemToIndex(id); + Option maybeMediaItem = await _searchRepository.GetItemToIndex(id, cancellationToken); foreach (MediaItem mediaItem in maybeMediaItem) { _logger.LogInformation("Moving item at {Path}", await GetPath(dbContext, mediaItem)); await _searchIndex.UpdateItems( _searchRepository, _fallbackMetadataProvider, - new List { mediaItem }); + [mediaItem]); } } } @@ -74,24 +74,28 @@ public class MoveLocalLibraryPathHandler : IRequestHandler> Validate( TvContext dbContext, - MoveLocalLibraryPath request) => - (await LibraryPathMustExist(dbContext, request), await LocalLibraryMustExist(dbContext, request)) + MoveLocalLibraryPath request, + CancellationToken cancellationToken) => + (await LibraryPathMustExist(dbContext, request, cancellationToken), + await LocalLibraryMustExist(dbContext, request, cancellationToken)) .Apply((libraryPath, localLibrary) => new Parameters(libraryPath, localLibrary)); private static Task> LibraryPathMustExist( TvContext dbContext, - MoveLocalLibraryPath request) => + MoveLocalLibraryPath request, + CancellationToken cancellationToken) => dbContext.LibraryPaths .Include(lp => lp.Library) - .SelectOneAsync(c => c.Id, c => c.Id == request.LibraryPathId) + .SelectOneAsync(c => c.Id, c => c.Id == request.LibraryPathId, cancellationToken) .Map(o => o.ToValidation("LibraryPath does not exist.")); private static Task> LocalLibraryMustExist( TvContext dbContext, - MoveLocalLibraryPath request) => + MoveLocalLibraryPath request, + CancellationToken cancellationToken) => dbContext.LocalLibraries .Include(ll => ll.Paths) - .SelectOneAsync(a => a.Id, a => a.Id == request.TargetLibraryId) + .SelectOneAsync(a => a.Id, a => a.Id == request.TargetLibraryId, cancellationToken) .Map(o => o.ToValidation("LocalLibrary does not exist")); private static async Task GetPath(TvContext dbContext, MediaItem mediaItem) => diff --git a/ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryIdHandler.cs b/ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryIdHandler.cs index 18105626e..2b62826c7 100644 --- a/ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryIdHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/QueueLibraryScanByLibraryIdHandler.cs @@ -25,7 +25,7 @@ public class QueueLibraryScanByLibraryIdHandler( Option maybeLibrary = await dbContext.Libraries .AsNoTracking() - .SelectOneAsync(l => l.Id, l => l.Id == request.LibraryId); + .SelectOneAsync(l => l.Id, l => l.Id == request.LibraryId, cancellationToken); foreach (Library library in maybeLibrary) { diff --git a/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs b/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs index 989247c12..b96d52f3a 100644 --- a/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/QueueShowScanByLibraryIdHandler.cs @@ -24,7 +24,7 @@ public class QueueShowScanByLibraryIdHandler( Option maybeLibrary = await dbContext.Libraries .AsNoTracking() - .SelectOneAsync(l => l.Id, l => l.Id == request.LibraryId); + .SelectOneAsync(l => l.Id, l => l.Id == request.LibraryId, cancellationToken); foreach (Library library in maybeLibrary) { diff --git a/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs b/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs index bc6833960..7914d667e 100644 --- a/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs +++ b/ErsatzTV.Application/Libraries/Commands/UpdateLocalLibraryHandler.cs @@ -37,7 +37,7 @@ public class UpdateLocalLibraryHandler : LocalLibraryHandlerBase, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => UpdateLocalLibrary(dbContext, parameters)); } @@ -105,18 +105,20 @@ public class UpdateLocalLibraryHandler : LocalLibraryHandlerBase, private static Task> Validate( TvContext dbContext, - UpdateLocalLibrary request) => - LocalLibraryMustExist(dbContext, request) + UpdateLocalLibrary request, + CancellationToken cancellationToken) => + LocalLibraryMustExist(dbContext, request, cancellationToken) .BindT(parameters => NameMustBeValid(request, parameters.Incoming).MapT(_ => parameters)) .BindT(parameters => PathsMustBeValid(dbContext, parameters.Incoming, parameters.Existing.Id) .MapT(_ => parameters)); private static Task> LocalLibraryMustExist( TvContext dbContext, - UpdateLocalLibrary request) => + UpdateLocalLibrary request, + CancellationToken cancellationToken) => dbContext.LocalLibraries .Include(ll => ll.Paths) - .SelectOneAsync(ll => ll.Id, ll => ll.Id == request.Id) + .SelectOneAsync(ll => ll.Id, ll => ll.Id == request.Id, cancellationToken) .MapT(existing => { var incoming = new LocalLibrary diff --git a/ErsatzTV.Application/Maintenance/Commands/EmptyTrashHandler.cs b/ErsatzTV.Application/Maintenance/Commands/EmptyTrashHandler.cs index 32bf06e37..e4c0e4e5e 100644 --- a/ErsatzTV.Application/Maintenance/Commands/EmptyTrashHandler.cs +++ b/ErsatzTV.Application/Maintenance/Commands/EmptyTrashHandler.cs @@ -26,7 +26,13 @@ public class EmptyTrashHandler : IRequestHandler i.Id).ToList(); // ElasticSearch remove items may fail, so do that first diff --git a/ErsatzTV.Application/MediaCards/Queries/GetCollectionCardsHandler.cs b/ErsatzTV.Application/MediaCards/Queries/GetCollectionCardsHandler.cs index fb55ea352..712dcf045 100644 --- a/ErsatzTV.Application/MediaCards/Queries/GetCollectionCardsHandler.cs +++ b/ErsatzTV.Application/MediaCards/Queries/GetCollectionCardsHandler.cs @@ -28,10 +28,10 @@ public class GetCollectionCardsHandler : { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Option maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin() + Option maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin(cancellationToken) .Map(list => list.HeadOrNone()); - Option maybeEmby = await _mediaSourceRepository.GetAllEmby() + Option maybeEmby = await _mediaSourceRepository.GetAllEmby(cancellationToken) .Map(list => list.HeadOrNone()); return await dbContext.Collections @@ -111,7 +111,7 @@ public class GetCollectionCardsHandler : .Include(c => c.MediaItems) .ThenInclude(i => (i as RemoteStream).MediaVersions) .ThenInclude(mv => mv.MediaFiles) - .SelectOneAsync(c => c.Id, c => c.Id == request.Id) + .SelectOneAsync(c => c.Id, c => c.Id == request.Id, cancellationToken) .Map(c => c.ToEither(BaseError.New("Unable to load collection"))) .MapT(c => ProjectToViewModel(c, maybeJellyfin, maybeEmby)); } diff --git a/ErsatzTV.Application/MediaCards/Queries/GetMusicVideoCardsHandler.cs b/ErsatzTV.Application/MediaCards/Queries/GetMusicVideoCardsHandler.cs index cc9e33071..d5661e3fa 100644 --- a/ErsatzTV.Application/MediaCards/Queries/GetMusicVideoCardsHandler.cs +++ b/ErsatzTV.Application/MediaCards/Queries/GetMusicVideoCardsHandler.cs @@ -44,6 +44,7 @@ public class GetMusicVideoCardsHandler : IRequestHandler maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin() + Option maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin(cancellationToken) .Map(list => list.HeadOrNone()); - Option maybeEmby = await _mediaSourceRepository.GetAllEmby() + Option maybeEmby = await _mediaSourceRepository.GetAllEmby(cancellationToken) .Map(list => list.HeadOrNone()); List episodes = await _televisionRepository @@ -53,6 +53,7 @@ public class _plexPathReplacementService, _jellyfinPathReplacementService, _embyPathReplacementService, + cancellationToken, false); results.Add(ProjectToViewModel(episodeMetadata, maybeJellyfin, maybeEmby, false, localPath)); diff --git a/ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCardsHandler.cs b/ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCardsHandler.cs index 7f08dbbbc..559c1c36b 100644 --- a/ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCardsHandler.cs +++ b/ErsatzTV.Application/MediaCards/Queries/GetTelevisionSeasonCardsHandler.cs @@ -25,14 +25,14 @@ public class { int count = await _televisionRepository.GetSeasonCount(request.TelevisionShowId); - Option maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin() + Option maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin(cancellationToken) .Map(list => list.HeadOrNone()); - Option maybeEmby = await _mediaSourceRepository.GetAllEmby() + Option maybeEmby = await _mediaSourceRepository.GetAllEmby(cancellationToken) .Map(list => list.HeadOrNone()); List results = await _televisionRepository - .GetPagedSeasons(request.TelevisionShowId, request.PageNumber, request.PageSize) + .GetPagedSeasons(request.TelevisionShowId, request.PageNumber, request.PageSize, cancellationToken) .Map(list => list.Map(s => ProjectToViewModel(s, maybeJellyfin, maybeEmby)).ToList()); return new TelevisionSeasonCardResultsViewModel(count, results, null); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollectionHandler.cs index c6ca07646..d5027c5b9 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddArtistToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddArtistRequest(dbContext, parameters)); } @@ -60,23 +60,27 @@ public class AddArtistToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddArtistToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateArtist(dbContext, request)) + AddArtistToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateArtist(dbContext, request, cancellationToken)) .Apply((collection, artist) => new Parameters(collection, artist)); private static Task> CollectionMustExist( TvContext dbContext, - AddArtistToCollection request) => + AddArtistToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateArtist( TvContext dbContext, - AddArtistToCollection request) => + AddArtistToCollection request, + CancellationToken cancellationToken) => dbContext.Artists - .SelectOneAsync(a => a.Id, a => a.Id == request.ArtistId) + .SelectOneAsync(a => a.Id, a => a.Id == request.ArtistId, cancellationToken) .Map(o => o.ToValidation("Artist does not exist")); private sealed record Parameters(Collection Collection, Artist Artist); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs index b8751c628..3ce2bfc16 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddEpisodeToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await LanguageExtensions.Apply( validation, parameters => ApplyAddTelevisionEpisodeRequest(dbContext, parameters)); @@ -62,23 +62,27 @@ public class AddEpisodeToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddEpisodeToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateEpisode(dbContext, request)) + AddEpisodeToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateEpisode(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddEpisodeToCollection request) => + AddEpisodeToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateEpisode( TvContext dbContext, - AddEpisodeToCollection request) => + AddEpisodeToCollection request, + CancellationToken cancellationToken) => dbContext.Episodes - .SelectOneAsync(e => e.Id, e => e.Id == request.EpisodeId) + .SelectOneAsync(e => e.Id, e => e.Id == request.EpisodeId, cancellationToken) .Map(o => o.ToValidation("Episode does not exist")); private sealed record Parameters(Collection Collection, Episode Episode); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToPlaylistHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToPlaylistHandler.cs index 2a93cbea6..8be9f87dd 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToPlaylistHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToPlaylistHandler.cs @@ -14,7 +14,7 @@ public class AddEpisodeToPlaylistHandler(IDbContextFactory dbContextF CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddEpisodeRequest(dbContext, parameters)); } @@ -36,23 +36,27 @@ public class AddEpisodeToPlaylistHandler(IDbContextFactory dbContextF private static async Task> Validate( TvContext dbContext, - AddEpisodeToPlaylist request) => - (await PlaylistMustExist(dbContext, request), await ValidateEpisode(dbContext, request)) + AddEpisodeToPlaylist request, + CancellationToken cancellationToken) => + (await PlaylistMustExist(dbContext, request, cancellationToken), + await ValidateEpisode(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> PlaylistMustExist( TvContext dbContext, - AddEpisodeToPlaylist request) => + AddEpisodeToPlaylist request, + CancellationToken cancellationToken) => dbContext.Playlists .Include(c => c.Items) - .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId) + .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId, cancellationToken) .Map(o => o.ToValidation("Playlist does not exist.")); private static Task> ValidateEpisode( TvContext dbContext, - AddEpisodeToPlaylist request) => + AddEpisodeToPlaylist request, + CancellationToken cancellationToken) => dbContext.Episodes - .SelectOneAsync(m => m.Id, e => e.Id == request.EpisodeId) + .SelectOneAsync(m => m.Id, e => e.Id == request.EpisodeId, cancellationToken) .Map(o => o.ToValidation("Episode does not exist")); private sealed record Parameters(Playlist Playlist, Episode Episode); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddImageToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddImageToCollectionHandler.cs index fae0bbf50..d41255a2f 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddImageToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddImageToCollectionHandler.cs @@ -35,7 +35,7 @@ public class AddImageToCollectionHandler : IRequestHandler validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddImageRequest(dbContext, parameters)); } @@ -59,23 +59,27 @@ public class AddImageToCollectionHandler : IRequestHandler> Validate( TvContext dbContext, - AddImageToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateImage(dbContext, request)) + AddImageToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateImage(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddImageToCollection request) => + AddImageToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateImage( TvContext dbContext, - AddImageToCollection request) => + AddImageToCollection request, + CancellationToken cancellationToken) => dbContext.Images - .SelectOneAsync(m => m.Id, e => e.Id == request.ImageId) + .SelectOneAsync(m => m.Id, e => e.Id == request.ImageId, cancellationToken) .Map(o => o.ToValidation("Image does not exist")); private sealed record Parameters(Collection Collection, Image Image); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollectionHandler.cs index 09fabe2d3..845ffe1ec 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollectionHandler.cs @@ -42,14 +42,15 @@ public class AddItemsToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(c => ApplyAddItemsRequest(dbContext, c, request)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(c => ApplyAddItemsRequest(dbContext, c, request, cancellationToken)); } private async Task ApplyAddItemsRequest( TvContext dbContext, Collection collection, - AddItemsToCollection request) + AddItemsToCollection request, + CancellationToken cancellationToken) { var allItems = request.MovieIds .Append(request.ShowIds) @@ -66,19 +67,19 @@ public class AddItemsToCollectionHandler : var toAddIds = allItems.Where(item => collection.MediaItems.All(mi => mi.Id != item)).ToList(); List toAdd = await dbContext.MediaItems .Filter(mi => toAddIds.Contains(mi.Id)) - .ToListAsync(); + .ToListAsync(cancellationToken); collection.MediaItems.AddRange(toAdd); - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { - await _searchChannel.WriteAsync(new ReindexMediaItems(toAddIds.ToArray())); + await _searchChannel.WriteAsync(new ReindexMediaItems(toAddIds.ToArray()), cancellationToken); // refresh all playouts that use this collection foreach (int playoutId in await _mediaCollectionRepository .PlayoutIdsUsingCollection(request.CollectionId)) { - await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh)); + await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh), cancellationToken); } } @@ -87,8 +88,9 @@ public class AddItemsToCollectionHandler : private async Task> Validate( TvContext dbContext, - AddItemsToCollection request) => - (await CollectionMustExist(dbContext, request), + AddItemsToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), await ValidateMovies(request), await ValidateShows(request), await ValidateSeasons(request), @@ -97,10 +99,11 @@ public class AddItemsToCollectionHandler : private static Task> CollectionMustExist( TvContext dbContext, - AddItemsToCollection request) => + AddItemsToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private Task> ValidateMovies(AddItemsToCollection request) => diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToPlaylistHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToPlaylistHandler.cs index 0ceb6deea..790138b44 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddItemsToPlaylistHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddItemsToPlaylistHandler.cs @@ -26,14 +26,15 @@ public class AddItemsToPlaylistHandler : IRequestHandler> Handle(AddItemsToPlaylist request, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(c => ApplyAddItemsRequest(dbContext, c, request)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(c => ApplyAddItemsRequest(dbContext, c, request, cancellationToken)); } private static async Task ApplyAddItemsRequest( TvContext dbContext, Playlist playlist, - AddItemsToPlaylist request) + AddItemsToPlaylist request, + CancellationToken cancellationToken) { var allItems = new Dictionary> { @@ -67,15 +68,16 @@ public class AddItemsToPlaylistHandler : IRequestHandler> Validate( TvContext dbContext, - AddItemsToPlaylist request) => - (await PlaylistMustExist(dbContext, request), + AddItemsToPlaylist request, + CancellationToken cancellationToken) => + (await PlaylistMustExist(dbContext, request, cancellationToken), await ValidateMovies(request), await ValidateShows(request), await ValidateSeasons(request), @@ -84,10 +86,11 @@ public class AddItemsToPlaylistHandler : IRequestHandler> PlaylistMustExist( TvContext dbContext, - AddItemsToPlaylist request) => + AddItemsToPlaylist request, + CancellationToken cancellationToken) => dbContext.Playlists .Include(c => c.Items) - .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId) + .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId, cancellationToken) .Map(o => o.ToValidation("Playlist does not exist.")); private Task> ValidateMovies(AddItemsToPlaylist request) => diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMediaItemToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMediaItemToCollectionHandler.cs index aa003d8bc..187db312d 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddMediaItemToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddMediaItemToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddMediaItemToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddMediaItemRequest(dbContext, parameters)); } @@ -60,23 +60,27 @@ public class AddMediaItemToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddMediaItemToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateMediaItem(dbContext, request)) + AddMediaItemToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateMediaItem(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddMediaItemToCollection request) => + AddMediaItemToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateMediaItem( TvContext dbContext, - AddMediaItemToCollection request) => + AddMediaItemToCollection request, + CancellationToken cancellationToken) => dbContext.MediaItems - .SelectOneAsync(m => m.Id, e => e.Id == request.MediaItemId) + .SelectOneAsync(m => m.Id, e => e.Id == request.MediaItemId, cancellationToken) .Map(o => o.ToValidation("MediaItem does not exist")); private sealed record Parameters(Collection Collection, MediaItem MediaItem); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs index f00ac8ef6..0a2bd8269 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddMovieToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddMovieRequest(dbContext, parameters)); } @@ -60,23 +60,27 @@ public class AddMovieToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddMovieToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateMovie(dbContext, request)) + AddMovieToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateMovie(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddMovieToCollection request) => + AddMovieToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateMovie( TvContext dbContext, - AddMovieToCollection request) => + AddMovieToCollection request, + CancellationToken cancellationToken) => dbContext.Movies - .SelectOneAsync(m => m.Id, e => e.Id == request.MovieId) + .SelectOneAsync(m => m.Id, e => e.Id == request.MovieId, cancellationToken) .Map(o => o.ToValidation("Movie does not exist")); private sealed record Parameters(Collection Collection, Movie Movie); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToPlaylistHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToPlaylistHandler.cs index 1a3adf964..3f8039e63 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddMovieToPlaylistHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddMovieToPlaylistHandler.cs @@ -14,7 +14,7 @@ public class AddMovieToPlaylistHandler(IDbContextFactory dbContextFac CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddMovieRequest(dbContext, parameters)); } @@ -36,23 +36,27 @@ public class AddMovieToPlaylistHandler(IDbContextFactory dbContextFac private static async Task> Validate( TvContext dbContext, - AddMovieToPlaylist request) => - (await PlaylistMustExist(dbContext, request), await ValidateMovie(dbContext, request)) + AddMovieToPlaylist request, + CancellationToken cancellationToken) => + (await PlaylistMustExist(dbContext, request, cancellationToken), + await ValidateMovie(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> PlaylistMustExist( TvContext dbContext, - AddMovieToPlaylist request) => + AddMovieToPlaylist request, + CancellationToken cancellationToken) => dbContext.Playlists .Include(c => c.Items) - .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId) + .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId, cancellationToken) .Map(o => o.ToValidation("Playlist does not exist.")); private static Task> ValidateMovie( TvContext dbContext, - AddMovieToPlaylist request) => + AddMovieToPlaylist request, + CancellationToken cancellationToken) => dbContext.Movies - .SelectOneAsync(m => m.Id, e => e.Id == request.MovieId) + .SelectOneAsync(m => m.Id, e => e.Id == request.MovieId, cancellationToken) .Map(o => o.ToValidation("Movie does not exist")); private sealed record Parameters(Playlist Playlist, Movie Movie); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs index e82e41209..05d194073 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddMusicVideoToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await LanguageExtensions.Apply( validation, parameters => ApplyAddMusicVideoRequest(dbContext, parameters)); @@ -62,23 +62,27 @@ public class AddMusicVideoToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddMusicVideoToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateMusicVideo(dbContext, request)) + AddMusicVideoToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateMusicVideo(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddMusicVideoToCollection request) => + AddMusicVideoToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateMusicVideo( TvContext dbContext, - AddMusicVideoToCollection request) => + AddMusicVideoToCollection request, + CancellationToken cancellationToken) => dbContext.MusicVideos - .SelectOneAsync(m => m.Id, e => e.Id == request.MusicVideoId) + .SelectOneAsync(m => m.Id, e => e.Id == request.MusicVideoId, cancellationToken) .Map(o => o.ToValidation("MusicVideo does not exist")); private sealed record Parameters(Collection Collection, MusicVideo MusicVideo); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs index 6a40c9bd7..a4a0a78d2 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddOtherVideoToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await LanguageExtensions.Apply( validation, parameters => ApplyAddOtherVideoRequest(dbContext, parameters)); @@ -62,23 +62,27 @@ public class AddOtherVideoToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddOtherVideoToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateOtherVideo(dbContext, request)) + AddOtherVideoToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateOtherVideo(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddOtherVideoToCollection request) => + AddOtherVideoToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateOtherVideo( TvContext dbContext, - AddOtherVideoToCollection request) => + AddOtherVideoToCollection request, + CancellationToken cancellationToken) => dbContext.OtherVideos - .SelectOneAsync(m => m.Id, e => e.Id == request.OtherVideoId) + .SelectOneAsync(m => m.Id, e => e.Id == request.OtherVideoId, cancellationToken) .Map(o => o.ToValidation("OtherVideo does not exist")); private sealed record Parameters(Collection Collection, OtherVideo OtherVideo); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs index 0c923218b..549163fcf 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddSeasonToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddSeasonRequest(dbContext, parameters)); } @@ -60,23 +60,27 @@ public class AddSeasonToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddSeasonToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateSeason(dbContext, request)) + AddSeasonToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateSeason(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddSeasonToCollection request) => + AddSeasonToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateSeason( TvContext dbContext, - AddSeasonToCollection request) => + AddSeasonToCollection request, + CancellationToken cancellationToken) => dbContext.Seasons - .SelectOneAsync(m => m.Id, e => e.Id == request.SeasonId) + .SelectOneAsync(m => m.Id, e => e.Id == request.SeasonId, cancellationToken) .Map(o => o.ToValidation("Season does not exist")); private sealed record Parameters(Collection Collection, Season Season); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToPlaylistHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToPlaylistHandler.cs index 7b9b2af20..8289b06d6 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToPlaylistHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddSeasonToPlaylistHandler.cs @@ -14,7 +14,7 @@ public class AddSeasonToPlaylistHandler(IDbContextFactory dbContextFa CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddSeasonRequest(dbContext, parameters)); } @@ -36,23 +36,27 @@ public class AddSeasonToPlaylistHandler(IDbContextFactory dbContextFa private static async Task> Validate( TvContext dbContext, - AddSeasonToPlaylist request) => - (await PlaylistMustExist(dbContext, request), await ValidateSeason(dbContext, request)) + AddSeasonToPlaylist request, + CancellationToken cancellationToken) => + (await PlaylistMustExist(dbContext, request, cancellationToken), + await ValidateSeason(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> PlaylistMustExist( TvContext dbContext, - AddSeasonToPlaylist request) => + AddSeasonToPlaylist request, + CancellationToken cancellationToken) => dbContext.Playlists .Include(c => c.Items) - .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId) + .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId, cancellationToken) .Map(o => o.ToValidation("Playlist does not exist.")); private static Task> ValidateSeason( TvContext dbContext, - AddSeasonToPlaylist request) => + AddSeasonToPlaylist request, + CancellationToken cancellationToken) => dbContext.Seasons - .SelectOneAsync(m => m.Id, e => e.Id == request.SeasonId) + .SelectOneAsync(m => m.Id, e => e.Id == request.SeasonId, cancellationToken) .Map(o => o.ToValidation("Season does not exist")); private sealed record Parameters(Playlist Playlist, Season Season); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs index 80afe1cb4..24911678a 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddShowToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddShowRequest(dbContext, parameters)); } @@ -60,23 +60,27 @@ public class AddShowToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddShowToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateShow(dbContext, request)) + AddShowToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateShow(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddShowToCollection request) => + AddShowToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateShow( TvContext dbContext, - AddShowToCollection request) => + AddShowToCollection request, + CancellationToken cancellationToken) => dbContext.Shows - .SelectOneAsync(m => m.Id, e => e.Id == request.ShowId) + .SelectOneAsync(m => m.Id, e => e.Id == request.ShowId, cancellationToken) .Map(o => o.ToValidation("Show does not exist")); private sealed record Parameters(Collection Collection, Show Show); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddShowToPlaylistHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddShowToPlaylistHandler.cs index c4c25a868..1972ea5fd 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddShowToPlaylistHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddShowToPlaylistHandler.cs @@ -14,7 +14,7 @@ public class AddShowToPlaylistHandler(IDbContextFactory dbContextFact CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddShowRequest(dbContext, parameters)); } @@ -36,23 +36,27 @@ public class AddShowToPlaylistHandler(IDbContextFactory dbContextFact private static async Task> Validate( TvContext dbContext, - AddShowToPlaylist request) => - (await PlaylistMustExist(dbContext, request), await ValidateShow(dbContext, request)) + AddShowToPlaylist request, + CancellationToken cancellationToken) => + (await PlaylistMustExist(dbContext, request, cancellationToken), + await ValidateShow(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> PlaylistMustExist( TvContext dbContext, - AddShowToPlaylist request) => + AddShowToPlaylist request, + CancellationToken cancellationToken) => dbContext.Playlists .Include(c => c.Items) - .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId) + .SelectOneAsync(c => c.Id, c => c.Id == request.PlaylistId, cancellationToken) .Map(o => o.ToValidation("Playlist does not exist.")); private static Task> ValidateShow( TvContext dbContext, - AddShowToPlaylist request) => + AddShowToPlaylist request, + CancellationToken cancellationToken) => dbContext.Shows - .SelectOneAsync(m => m.Id, e => e.Id == request.ShowId) + .SelectOneAsync(m => m.Id, e => e.Id == request.ShowId, cancellationToken) .Map(o => o.ToValidation("Show does not exist")); private sealed record Parameters(Playlist Playlist, Show Show); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddSongToCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddSongToCollectionHandler.cs index 1d23dc585..d00339ffa 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddSongToCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddSongToCollectionHandler.cs @@ -36,7 +36,7 @@ public class AddSongToCollectionHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(parameters => ApplyAddSongRequest(dbContext, parameters)); } @@ -60,23 +60,27 @@ public class AddSongToCollectionHandler : private static async Task> Validate( TvContext dbContext, - AddSongToCollection request) => - (await CollectionMustExist(dbContext, request), await ValidateSong(dbContext, request)) + AddSongToCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), + await ValidateSong(dbContext, request, cancellationToken)) .Apply((collection, episode) => new Parameters(collection, episode)); private static Task> CollectionMustExist( TvContext dbContext, - AddSongToCollection request) => + AddSongToCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Task> ValidateSong( TvContext dbContext, - AddSongToCollection request) => + AddSongToCollection request, + CancellationToken cancellationToken) => dbContext.Songs - .SelectOneAsync(m => m.Id, e => e.Id == request.SongId) + .SelectOneAsync(m => m.Id, e => e.Id == request.SongId, cancellationToken) .Map(o => o.ToValidation("Song does not exist")); private sealed record Parameters(Collection Collection, Song Song); diff --git a/ErsatzTV.Application/MediaCollections/Commands/AddTraktListHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/AddTraktListHandler.cs index 7799e431e..60fb54985 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/AddTraktListHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/AddTraktListHandler.cs @@ -37,7 +37,7 @@ public partial class AddTraktListHandler : TraktCommandBase, IRequestHandler validation = ValidateUrl(request); return await validation.Match( - DoAdd, + p => DoAdd(p, cancellationToken), error => Task.FromResult>(error.Join())); } finally @@ -82,9 +82,9 @@ public partial class AddTraktListHandler : TraktCommandBase, IRequestHandler> DoAdd(Parameters parameters) + private async Task> DoAdd(Parameters parameters, CancellationToken cancellationToken) { - await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); + await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Logger.LogDebug("Searching for trakt list: {User}/{List}", parameters.User, parameters.List); Either maybeList = await TraktApiClient.GetUserList(parameters.User, parameters.List); @@ -92,7 +92,7 @@ public partial class AddTraktListHandler : TraktCommandBase, IRequestHandler Unit.Default); diff --git a/ErsatzTV.Application/MediaCollections/Commands/CreateSmartCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/CreateSmartCollectionHandler.cs index 8309cb6b1..c4591982d 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/CreateSmartCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/CreateSmartCollectionHandler.cs @@ -31,17 +31,18 @@ public class CreateSmartCollectionHandler : { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); Validation validation = await Validate(dbContext, request); - return await validation.Apply(c => PersistCollection(dbContext, c)); + return await validation.Apply(c => PersistCollection(dbContext, c, cancellationToken)); } private async Task PersistCollection( TvContext dbContext, - SmartCollection smartCollection) + SmartCollection smartCollection, + CancellationToken cancellationToken) { - await dbContext.SmartCollections.AddAsync(smartCollection); - await dbContext.SaveChangesAsync(); + await dbContext.SmartCollections.AddAsync(smartCollection, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); _searchTargets.SearchTargetsChanged(); - await _smartCollectionCache.Refresh(); + await _smartCollectionCache.Refresh(cancellationToken); return ProjectToViewModel(smartCollection); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeleteCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/DeleteCollectionHandler.cs index aa428ceed..c18a00c11 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeleteCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeleteCollectionHandler.cs @@ -23,22 +23,23 @@ public class DeleteCollectionHandler : IRequestHandler validation = await CollectionMustExist(dbContext, request); - return await validation.Apply(c => DoDeletion(dbContext, c)); + Validation validation = await CollectionMustExist(dbContext, request, cancellationToken); + return await validation.Apply(c => DoDeletion(dbContext, c, cancellationToken)); } - private async Task DoDeletion(TvContext dbContext, Collection collection) + private async Task DoDeletion(TvContext dbContext, Collection collection, CancellationToken cancellationToken) { dbContext.Collections.Remove(collection); - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); _searchTargets.SearchTargetsChanged(); return Unit.Default; } private static Task> CollectionMustExist( TvContext dbContext, - DeleteCollection request) => + DeleteCollection request, + CancellationToken cancellationToken) => dbContext.Collections - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation($"Collection {request.CollectionId} does not exist.")); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeleteMultiCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/DeleteMultiCollectionHandler.cs index 0811c0d01..10ee20c07 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeleteMultiCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeleteMultiCollectionHandler.cs @@ -24,22 +24,29 @@ public class DeleteMultiCollectionHandler : IRequestHandler validation = await MultiCollectionMustExist(dbContext, request); - return await validation.Apply(c => DoDeletion(dbContext, c)); + Validation validation = await MultiCollectionMustExist( + dbContext, + request, + cancellationToken); + return await validation.Apply(c => DoDeletion(dbContext, c, cancellationToken)); } - private async Task DoDeletion(TvContext dbContext, MultiCollection multiCollection) + private async Task DoDeletion( + TvContext dbContext, + MultiCollection multiCollection, + CancellationToken cancellationToken) { dbContext.MultiCollections.Remove(multiCollection); - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); _searchTargets.SearchTargetsChanged(); return Unit.Default; } private static Task> MultiCollectionMustExist( TvContext dbContext, - DeleteMultiCollection request) => + DeleteMultiCollection request, + CancellationToken cancellationToken) => dbContext.MultiCollections - .SelectOneAsync(c => c.Id, c => c.Id == request.MultiCollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.MultiCollectionId, cancellationToken) .Map(o => o.ToValidation($"MultiCollection {request.MultiCollectionId} does not exist.")); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistGroupHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistGroupHandler.cs index d9a5b8020..030e1ffb0 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistGroupHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistGroupHandler.cs @@ -14,7 +14,7 @@ public class DeletePlaylistGroupHandler(IDbContextFactory dbContextFa await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybePlaylistGroup = await dbContext.PlaylistGroups - .SelectOneAsync(p => p.Id, p => p.Id == request.PlaylistGroupId); + .SelectOneAsync(p => p.Id, p => p.Id == request.PlaylistGroupId, cancellationToken); foreach (PlaylistGroup playlistGroup in maybePlaylistGroup) { diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistHandler.cs index 60de02f3a..071f10dd2 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeletePlaylistHandler.cs @@ -14,7 +14,7 @@ public class DeletePlaylistHandler(IDbContextFactory dbContextFactory await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybePlaylist = await dbContext.Playlists - .SelectOneAsync(p => p.Id, p => p.Id == request.PlaylistId); + .SelectOneAsync(p => p.Id, p => p.Id == request.PlaylistId, cancellationToken); foreach (Playlist playlist in maybePlaylist) { diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeleteSmartCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/DeleteSmartCollectionHandler.cs index c29362056..00dd35336 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeleteSmartCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeleteSmartCollectionHandler.cs @@ -29,23 +29,30 @@ public class DeleteSmartCollectionHandler : IRequestHandler validation = await SmartCollectionMustExist(dbContext, request); - return await validation.Apply(c => DoDeletion(dbContext, c)); + Validation validation = await SmartCollectionMustExist( + dbContext, + request, + cancellationToken); + return await validation.Apply(c => DoDeletion(dbContext, c, cancellationToken)); } - private async Task DoDeletion(TvContext dbContext, SmartCollection smartCollection) + private async Task DoDeletion( + TvContext dbContext, + SmartCollection smartCollection, + CancellationToken cancellationToken) { dbContext.SmartCollections.Remove(smartCollection); - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); _searchTargets.SearchTargetsChanged(); - await _smartCollectionCache.Refresh(); + await _smartCollectionCache.Refresh(cancellationToken); return Unit.Default; } private static Task> SmartCollectionMustExist( TvContext dbContext, - DeleteSmartCollection request) => + DeleteSmartCollection request, + CancellationToken cancellationToken) => dbContext.SmartCollections - .SelectOneAsync(c => c.Id, c => c.Id == request.SmartCollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.SmartCollectionId, cancellationToken) .Map(o => o.ToValidation($"SmartCollection {request.SmartCollectionId} does not exist.")); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/DeleteTraktListHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/DeleteTraktListHandler.cs index 31061cbe1..4bf59dbb4 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/DeleteTraktListHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/DeleteTraktListHandler.cs @@ -43,8 +43,11 @@ public class DeleteTraktListHandler : TraktCommandBase, IRequestHandler validation = await TraktListMustExist(dbContext, request.TraktListId); - return await validation.Apply(c => DoDeletion(dbContext, c)); + Validation validation = await TraktListMustExist( + dbContext, + request.TraktListId, + cancellationToken); + return await validation.Apply(c => DoDeletion(dbContext, c, cancellationToken)); } finally { @@ -52,14 +55,18 @@ public class DeleteTraktListHandler : TraktCommandBase, IRequestHandler DoDeletion(TvContext dbContext, TraktList traktList) + private async Task DoDeletion(TvContext dbContext, TraktList traktList, CancellationToken cancellationToken) { var mediaItemIds = traktList.Items.Bind(i => Optional(i.MediaItemId)).ToList(); dbContext.TraktLists.Remove(traktList); - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { - await _searchIndex.RebuildItems(_searchRepository, _fallbackMetadataProvider, mediaItemIds); + await _searchIndex.RebuildItems( + _searchRepository, + _fallbackMetadataProvider, + mediaItemIds, + cancellationToken); } _searchIndex.Commit(); diff --git a/ErsatzTV.Application/MediaCollections/Commands/MatchTraktListItemsHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/MatchTraktListItemsHandler.cs index eb1ec6f0b..b410747e3 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/MatchTraktListItemsHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/MatchTraktListItemsHandler.cs @@ -43,9 +43,12 @@ public class MatchTraktListItemsHandler : TraktCommandBase, { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await TraktListMustExist(dbContext, request.TraktListId); + Validation validation = await TraktListMustExist( + dbContext, + request.TraktListId, + cancellationToken); return await validation.Match( - async l => await MatchListItems(dbContext, l).MapT(_ => Unit.Default), + async l => await MatchListItems(dbContext, l, cancellationToken).MapT(_ => Unit.Default), error => Task.FromResult>(error.Join())); } finally diff --git a/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs index 5447fdcd6..375e06add 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/PreviewPlaylistPlayoutHandler.cs @@ -63,7 +63,9 @@ public class PreviewPlaylistPlayoutHandler( var maxItems = 0; Dictionary> map = - await mediaCollectionRepository.GetPlaylistItemMap(playout.ProgramSchedule.Items[0].Playlist); + await mediaCollectionRepository.GetPlaylistItemMap( + playout.ProgramSchedule.Items[0].Playlist, + cancellationToken); foreach (PlaylistItem item in playout.ProgramSchedule.Items[0].Playlist.Items) { if (item.PlayAll) @@ -102,7 +104,7 @@ public class PreviewPlaylistPlayoutHandler( .Include(mi => (mi as Song).MediaVersions) .Include(mi => (mi as Image).ImageMetadata) .Include(mi => (mi as Image).MediaVersions) - .SelectOneAsync(mi => mi.Id, mi => mi.Id == playoutItem.MediaItemId); + .SelectOneAsync(mi => mi.Id, mi => mi.Id == playoutItem.MediaItemId, cancellationToken); foreach (MediaItem mediaItem in maybeMediaItem) { diff --git a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs index b9a64cc72..317fde28a 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs @@ -35,14 +35,15 @@ public class RemoveItemsFromCollectionHandler : IRequestHandler validation = await Validate(dbContext, request); - return await validation.Apply(c => ApplyRemoveItemsRequest(dbContext, request, c)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(c => ApplyRemoveItemsRequest(dbContext, request, c, cancellationToken)); } private async Task ApplyRemoveItemsRequest( TvContext dbContext, RemoveItemsFromCollection request, - Collection collection) + Collection collection, + CancellationToken cancellationToken) { var itemsToRemove = collection.MediaItems .Filter(m => request.MediaItemIds.Contains(m.Id)) @@ -50,14 +51,16 @@ public class RemoveItemsFromCollectionHandler : IRequestHandler collection.MediaItems.Remove(m)); - if (itemsToRemove.Count != 0 && await dbContext.SaveChangesAsync() > 0) + if (itemsToRemove.Count != 0 && await dbContext.SaveChangesAsync(cancellationToken) > 0) { - await _searchChannel.WriteAsync(new ReindexMediaItems(itemsToRemove.Select(mi => mi.Id).ToArray())); + await _searchChannel.WriteAsync( + new ReindexMediaItems(itemsToRemove.Select(mi => mi.Id).ToArray()), + cancellationToken); // refresh all playouts that use this collection foreach (int playoutId in await _mediaCollectionRepository.PlayoutIdsUsingCollection(collection.Id)) { - await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh)); + await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh), cancellationToken); } } @@ -66,14 +69,16 @@ public class RemoveItemsFromCollectionHandler : IRequestHandler> Validate( TvContext dbContext, - RemoveItemsFromCollection request) => - CollectionMustExist(dbContext, request); + RemoveItemsFromCollection request, + CancellationToken cancellationToken) => + CollectionMustExist(dbContext, request, cancellationToken); private static Task> CollectionMustExist( TvContext dbContext, - RemoveItemsFromCollection request) => + RemoveItemsFromCollection request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.MediaItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.MediaCollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.MediaCollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/ReplacePlaylistItemsHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/ReplacePlaylistItemsHandler.cs index d8292914d..fe14daa39 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/ReplacePlaylistItemsHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/ReplacePlaylistItemsHandler.cs @@ -14,14 +14,15 @@ public class ReplacePlaylistItemsHandler(IDbContextFactory dbContextF CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(ps => Persist(dbContext, request, ps)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(ps => Persist(dbContext, request, ps, cancellationToken)); } private static async Task> Persist( TvContext dbContext, ReplacePlaylistItems request, - Playlist playlist) + Playlist playlist, + CancellationToken cancellationToken) { playlist.Name = request.Name; //playlist.DateUpdated = DateTime.UtcNow; @@ -29,7 +30,7 @@ public class ReplacePlaylistItemsHandler(IDbContextFactory dbContextF dbContext.RemoveRange(playlist.Items); playlist.Items = request.Items.Map(i => BuildItem(playlist, i.Index, i)).ToList(); - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); return playlist.Items.Map(Mapper.ProjectToViewModel).ToList(); } @@ -49,14 +50,20 @@ public class ReplacePlaylistItemsHandler(IDbContextFactory dbContextF IncludeInProgramGuide = item.IncludeInProgramGuide }; - private static Task> Validate(TvContext dbContext, ReplacePlaylistItems request) => - PlaylistMustExist(dbContext, request.PlaylistId) + private static Task> Validate( + TvContext dbContext, + ReplacePlaylistItems request, + CancellationToken cancellationToken) => + PlaylistMustExist(dbContext, request.PlaylistId, cancellationToken) .BindT(playlist => CollectionTypesMustBeValid(request, playlist)); - private static Task> PlaylistMustExist(TvContext dbContext, int playlistId) => + private static Task> PlaylistMustExist( + TvContext dbContext, + int playlistId, + CancellationToken cancellationToken) => dbContext.Playlists .Include(b => b.Items) - .SelectOneAsync(b => b.Id, b => b.Id == playlistId) + .SelectOneAsync(b => b.Id, b => b.Id == playlistId, cancellationToken) .Map(o => o.ToValidation("[PlaylistId] does not exist.")); private static Validation CollectionTypesMustBeValid( diff --git a/ErsatzTV.Application/MediaCollections/Commands/TraktCommandBase.cs b/ErsatzTV.Application/MediaCollections/Commands/TraktCommandBase.cs index 9b0a08df5..2253fb3a8 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/TraktCommandBase.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/TraktCommandBase.cs @@ -40,23 +40,26 @@ public abstract class TraktCommandBase protected ILogger Logger { get; } protected static Task> - TraktListMustExist(TvContext dbContext, int traktListId) => + TraktListMustExist(TvContext dbContext, int traktListId, CancellationToken cancellationToken) => dbContext.TraktLists .Include(l => l.Items) .ThenInclude(i => i.Guids) .Include(tl => tl.Playlist) .ThenInclude(tl => tl.Items) - .SelectOneAsync(c => c.Id, c => c.Id == traktListId) + .SelectOneAsync(c => c.Id, c => c.Id == traktListId, cancellationToken) .Map(o => o.ToValidation($"TraktList {traktListId} does not exist.")); - protected async Task> SaveList(TvContext dbContext, TraktList list) + protected async Task> SaveList( + TvContext dbContext, + TraktList list, + CancellationToken cancellationToken) { _logger.LogDebug("Saving trakt list to database: {User}/{List}", list.User, list.List); Option maybeExisting = await dbContext.TraktLists .Include(l => l.Items) .ThenInclude(i => i.Guids) - .SelectOneAsync(tl => tl.Id, tl => tl.User == list.User && tl.List == list.List); + .SelectOneAsync(tl => tl.Id, tl => tl.User == list.User && tl.List == list.List, cancellationToken); return await maybeExisting.Match( async existing => @@ -65,14 +68,14 @@ public abstract class TraktCommandBase existing.Description = list.Description; existing.ItemCount = list.ItemCount; - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); return existing; }, async () => { - await dbContext.TraktLists.AddAsync(list); - await dbContext.SaveChangesAsync(); + await dbContext.TraktLists.AddAsync(list, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); return list; }); @@ -125,7 +128,10 @@ public abstract class TraktCommandBase error => Task.FromResult(Left(error))); } - protected async Task> MatchListItems(TvContext dbContext, TraktList list) + protected async Task> MatchListItems( + TvContext dbContext, + TraktList list, + CancellationToken cancellationToken) { try { @@ -192,7 +198,7 @@ public abstract class TraktCommandBase IncludeInProgramGuide = true }; - await dbContext.PlaylistItems.AddAsync(playlistItem); + await dbContext.PlaylistItems.AddAsync(playlistItem, cancellationToken); } playlistItem.CollectionType = item.Kind switch @@ -216,15 +222,19 @@ public abstract class TraktCommandBase } } - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { - await _searchIndex.RebuildItems(_searchRepository, _fallbackMetadataProvider, ids.ToList()); + await _searchIndex.RebuildItems( + _searchRepository, + _fallbackMetadataProvider, + ids.ToList(), + cancellationToken); } _searchIndex.Commit(); list.LastMatch = DateTime.UtcNow; - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); return list; } diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionCustomOrderHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionCustomOrderHandler.cs index 2bb249750..c8333f71b 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionCustomOrderHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionCustomOrderHandler.cs @@ -31,7 +31,7 @@ public class UpdateCollectionCustomOrderHandler : IRequestHandler validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request)); } @@ -66,14 +66,16 @@ public class UpdateCollectionCustomOrderHandler : IRequestHandler> Validate( TvContext dbContext, - UpdateCollectionCustomOrder request) => - CollectionMustExist(dbContext, request); + UpdateCollectionCustomOrder request, + CancellationToken cancellationToken) => + CollectionMustExist(dbContext, request, cancellationToken); private static Task> CollectionMustExist( TvContext dbContext, - UpdateCollectionCustomOrder request) => + UpdateCollectionCustomOrder request, + CancellationToken cancellationToken) => dbContext.Collections .Include(c => c.CollectionItems) - .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == request.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs index db18eded9..1ce264e2e 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateCollectionHandler.cs @@ -35,11 +35,15 @@ public class UpdateCollectionHandler : IRequestHandler validation = await Validate(dbContext, request); - return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request, cancellationToken)); } - private async Task ApplyUpdateRequest(TvContext dbContext, Collection c, UpdateCollection request) + private async Task ApplyUpdateRequest( + TvContext dbContext, + Collection c, + UpdateCollection request, + CancellationToken cancellationToken) { c.Name = request.Name; foreach (bool useCustomPlaybackOrder in request.UseCustomPlaybackOrder) @@ -47,13 +51,13 @@ public class UpdateCollectionHandler : IRequestHandler 0 && request.UseCustomPlaybackOrder.IsSome) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0 && request.UseCustomPlaybackOrder.IsSome) { // refresh all playouts that use this collection foreach (int playoutId in await _mediaCollectionRepository.PlayoutIdsUsingCollection( request.CollectionId)) { - await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh)); + await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh), cancellationToken); } } @@ -64,15 +68,17 @@ public class UpdateCollectionHandler : IRequestHandler> Validate( TvContext dbContext, - UpdateCollection request) => - (await CollectionMustExist(dbContext, request), ValidateName(request)) + UpdateCollection request, + CancellationToken cancellationToken) => + (await CollectionMustExist(dbContext, request, cancellationToken), ValidateName(request)) .Apply((collectionToUpdate, _) => collectionToUpdate); private static Task> CollectionMustExist( TvContext dbContext, - UpdateCollection updateCollection) => + UpdateCollection updateCollection, + CancellationToken cancellationToken) => dbContext.Collections - .SelectOneAsync(c => c.Id, c => c.Id == updateCollection.CollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == updateCollection.CollectionId, cancellationToken) .Map(o => o.ToValidation("Collection does not exist.")); private static Validation ValidateName(UpdateCollection updateSimpleMediaCollection) => diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs index f261dde06..23b00f588 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateMultiCollectionHandler.cs @@ -35,16 +35,20 @@ public class UpdateMultiCollectionHandler : IRequestHandler validation = await Validate(dbContext, request); - return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request, cancellationToken)); } - private async Task ApplyUpdateRequest(TvContext dbContext, MultiCollection c, UpdateMultiCollection request) + private async Task ApplyUpdateRequest( + TvContext dbContext, + MultiCollection c, + UpdateMultiCollection request, + CancellationToken cancellationToken) { c.Name = request.Name; // save name first so playouts don't get rebuilt for a name change - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); var toAdd = request.Items .Filter(i => i.CollectionId.HasValue) @@ -115,7 +119,7 @@ public class UpdateMultiCollectionHandler : IRequestHandler 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { _searchTargets.SearchTargetsChanged(); @@ -123,7 +127,7 @@ public class UpdateMultiCollectionHandler : IRequestHandler> Validate( TvContext dbContext, - UpdateMultiCollection request) => - (await MultiCollectionMustExist(dbContext, request), await ValidateName(dbContext, request)) + UpdateMultiCollection request, + CancellationToken cancellationToken) => + (await MultiCollectionMustExist(dbContext, request, cancellationToken), await ValidateName(dbContext, request)) .Apply((collectionToUpdate, _) => collectionToUpdate); private static Task> MultiCollectionMustExist( TvContext dbContext, - UpdateMultiCollection updateCollection) => + UpdateMultiCollection updateCollection, + CancellationToken cancellationToken) => dbContext.MultiCollections .Include(mc => mc.MultiCollectionItems) .Include(mc => mc.MultiCollectionSmartItems) - .SelectOneAsync(c => c.Id, c => c.Id == updateCollection.MultiCollectionId) + .SelectOneAsync(c => c.Id, c => c.Id == updateCollection.MultiCollectionId, cancellationToken) .Map(o => o.ToValidation("MultiCollection does not exist.")); private static async Task> ValidateName( diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs index 88adc00b3..f13308088 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateSmartCollectionHandler.cs @@ -39,25 +39,29 @@ public class UpdateSmartCollectionHandler : IRequestHandler validation = await Validate(dbContext, request); - return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request, cancellationToken)); } - private async Task ApplyUpdateRequest(TvContext dbContext, SmartCollection c, UpdateSmartCollection request) + private async Task ApplyUpdateRequest( + TvContext dbContext, + SmartCollection c, + UpdateSmartCollection request, + CancellationToken cancellationToken) { c.Query = request.Query; c.Name = request.Name; // rebuild playouts - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { _searchTargets.SearchTargetsChanged(); - await _smartCollectionCache.Refresh(); + await _smartCollectionCache.Refresh(cancellationToken); // refresh all playouts that use this smart collection foreach (int playoutId in await _mediaCollectionRepository.PlayoutIdsUsingSmartCollection(request.Id)) { - await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh)); + await _channel.WriteAsync(new BuildPlayout(playoutId, PlayoutBuildMode.Refresh), cancellationToken); } } @@ -66,12 +70,14 @@ public class UpdateSmartCollectionHandler : IRequestHandler> Validate( TvContext dbContext, - UpdateSmartCollection request) => SmartCollectionMustExist(dbContext, request); + UpdateSmartCollection request, + CancellationToken cancellationToken) => SmartCollectionMustExist(dbContext, request, cancellationToken); private static Task> SmartCollectionMustExist( TvContext dbContext, - UpdateSmartCollection updateCollection) => + UpdateSmartCollection updateCollection, + CancellationToken cancellationToken) => dbContext.SmartCollections - .SelectOneAsync(c => c.Id, c => c.Id == updateCollection.Id) + .SelectOneAsync(c => c.Id, c => c.Id == updateCollection.Id, cancellationToken) .Map(o => o.ToValidation("SmartCollection does not exist.")); } diff --git a/ErsatzTV.Application/MediaCollections/Commands/UpdateTraktListHandler.cs b/ErsatzTV.Application/MediaCollections/Commands/UpdateTraktListHandler.cs index ebbb17bed..e7a486ce7 100644 --- a/ErsatzTV.Application/MediaCollections/Commands/UpdateTraktListHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Commands/UpdateTraktListHandler.cs @@ -22,7 +22,7 @@ public class UpdateTraktListHandler( Option maybeTraktList = await dbContext.TraktLists .Include(tl => tl.Playlist) - .SelectOneAsync(t => t.Id, t => t.Id == request.Id); + .SelectOneAsync(t => t.Id, t => t.Id == request.Id, cancellationToken); foreach (TraktList traktList in maybeTraktList) { diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetCollectionByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionByIdHandler.cs index 78689c61e..bfe2d5480 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetCollectionByIdHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetCollectionByIdHandler.cs @@ -19,7 +19,8 @@ public class GetCollectionByIdHandler : { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); return await dbContext.Collections - .SelectOneAsync(c => c.Id, c => c.Id == request.Id) + .AsNoTracking() + .SelectOneAsync(c => c.Id, c => c.Id == request.Id, cancellationToken) .MapT(ProjectToViewModel); } } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetMultiCollectionByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetMultiCollectionByIdHandler.cs index 4bc51d82d..5d12d0701 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetMultiCollectionByIdHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetMultiCollectionByIdHandler.cs @@ -16,13 +16,14 @@ public class GetMultiCollectionByIdHandler : IRequestHandler mc.MultiCollectionItems) .ThenInclude(mc => mc.Collection) .Include(mc => mc.MultiCollectionSmartItems) .ThenInclude(mc => mc.SmartCollection) - .SelectOneAsync(c => c.Id, c => c.Id == request.Id) + .SelectOneAsync(c => c.Id, c => c.Id == request.Id, cancellationToken) .MapT(ProjectToViewModel); } } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistByIdHandler.cs index 365355827..8e9bf83e9 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistByIdHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetPlaylistByIdHandler.cs @@ -11,7 +11,7 @@ public class GetPlaylistByIdHandler(IDbContextFactory dbContextFactor { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); return await dbContext.Playlists - .SelectOneAsync(b => b.Id, b => b.Id == request.PlaylistId) + .SelectOneAsync(b => b.Id, b => b.Id == request.PlaylistId, cancellationToken) .MapT(Mapper.ProjectToViewModel); } } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs index f4a13130a..d9413b9c0 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetSmartCollectionByIdHandler.cs @@ -14,7 +14,8 @@ public class GetSmartCollectionByIdHandler(IDbContextFactory dbContex { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); return await dbContext.SmartCollections - .SelectOneAsync(c => c.Id, c => c.Id == request.Id) + .AsNoTracking() + .SelectOneAsync(c => c.Id, c => c.Id == request.Id, cancellationToken) .MapT(ProjectToViewModel); } } diff --git a/ErsatzTV.Application/MediaCollections/Queries/GetTraktListByIdHandler.cs b/ErsatzTV.Application/MediaCollections/Queries/GetTraktListByIdHandler.cs index 7f68c9cf7..2e8e3ff84 100644 --- a/ErsatzTV.Application/MediaCollections/Queries/GetTraktListByIdHandler.cs +++ b/ErsatzTV.Application/MediaCollections/Queries/GetTraktListByIdHandler.cs @@ -11,8 +11,9 @@ public class GetTraktListByIdHandler(IDbContextFactory dbContextFacto { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); return await dbContext.TraktLists + .AsNoTracking() .Include(tl => tl.Items) - .SelectOneAsync(tl => tl.Id, tl => tl.Id == request.Id) + .SelectOneAsync(tl => tl.Id, tl => tl.Id == request.Id, cancellationToken) .MapT(Mapper.ProjectToViewModel); } } diff --git a/ErsatzTV.Application/MediaItems/Queries/GetMediaItemInfoHandler.cs b/ErsatzTV.Application/MediaItems/Queries/GetMediaItemInfoHandler.cs index 4214383b1..df32093d7 100644 --- a/ErsatzTV.Application/MediaItems/Queries/GetMediaItemInfoHandler.cs +++ b/ErsatzTV.Application/MediaItems/Queries/GetMediaItemInfoHandler.cs @@ -76,7 +76,7 @@ public class GetMediaItemInfoHandler : IRequestHandler mv.Streams) .Include(i => (i as MusicVideo).Artist) .ThenInclude(a => a.ArtistMetadata) - .SelectOneAsync(i => i.Id, i => i.Id == request.Id) + .SelectOneAsync(i => i.Id, i => i.Id == request.Id, cancellationToken) .MapT(Project); return mediaItem.ToEither(new UnableToLocateMediaItem()); diff --git a/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamByIdHandler.cs b/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamByIdHandler.cs index 9178fb25b..f34c6683a 100644 --- a/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamByIdHandler.cs +++ b/ErsatzTV.Application/MediaItems/Queries/GetRemoteStreamByIdHandler.cs @@ -13,7 +13,8 @@ public class GetRemoteStreamByIdHandler(IDbContextFactory dbContextFa { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); return await dbContext.RemoteStreams - .SelectOneAsync(rs => rs.Id, rs => rs.Id == request.RemoteStreamId) + .AsNoTracking() + .SelectOneAsync(rs => rs.Id, rs => rs.Id == request.RemoteStreamId, cancellationToken) .MapT(Mapper.ProjectToViewModel); } } diff --git a/ErsatzTV.Application/MediaSources/Commands/CallLocalLibraryScannerHandler.cs b/ErsatzTV.Application/MediaSources/Commands/CallLocalLibraryScannerHandler.cs index 0db39edce..f033318f3 100644 --- a/ErsatzTV.Application/MediaSources/Commands/CallLocalLibraryScannerHandler.cs +++ b/ErsatzTV.Application/MediaSources/Commands/CallLocalLibraryScannerHandler.cs @@ -35,7 +35,7 @@ public class CallLocalLibraryScannerHandler : CallLibraryScannerHandler> Handle(IScanLocalLibrary request, CancellationToken cancellationToken) { - Validation validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -67,11 +67,14 @@ public class CallLocalLibraryScannerHandler : CallLibraryScannerHandler GetLastScan(TvContext dbContext, IScanLocalLibrary request) + protected override async Task GetLastScan( + TvContext dbContext, + IScanLocalLibrary request, + CancellationToken cancellationToken) { List libraryPaths = await dbContext.LibraryPaths .Filter(lp => lp.LibraryId == request.LibraryId) - .ToListAsync(); + .ToListAsync(cancellationToken); DateTime minDateTime = libraryPaths.Count != 0 ? libraryPaths.Min(lp => lp.LastScan ?? SystemTime.MinValueUtc) diff --git a/ErsatzTV.Application/Movies/Queries/GetMovieByIdHandler.cs b/ErsatzTV.Application/Movies/Queries/GetMovieByIdHandler.cs index 320443071..f084a740e 100644 --- a/ErsatzTV.Application/Movies/Queries/GetMovieByIdHandler.cs +++ b/ErsatzTV.Application/Movies/Queries/GetMovieByIdHandler.cs @@ -42,10 +42,10 @@ public class GetMovieByIdHandler : IRequestHandler maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin() + Option maybeJellyfin = await _mediaSourceRepository.GetAllJellyfin(cancellationToken) .Map(list => list.HeadOrNone()); - Option maybeEmby = await _mediaSourceRepository.GetAllEmby() + Option maybeEmby = await _mediaSourceRepository.GetAllEmby(cancellationToken) .Map(list => list.HeadOrNone()); Option maybeMovie = await _movieRepository.GetMovie(request.Id); @@ -68,6 +68,7 @@ public class GetMovieByIdHandler : IRequestHandler result; { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); result = await validation.Match( playout => ApplyUpdateRequest(dbContext, request, playout, cancellationToken), error => Task.FromResult>(error.Join())); @@ -86,7 +86,7 @@ public class BuildPlayoutHandler : IRequestHandler> Validate(TvContext dbContext, BuildPlayout request) => - PlayoutMustExist(dbContext, request).BindT(DiscardAttemptsMustBeValid); + private static Task> Validate( + TvContext dbContext, + BuildPlayout request, + CancellationToken cancellationToken) => + PlayoutMustExist(dbContext, request, cancellationToken).BindT(DiscardAttemptsMustBeValid); private static Validation DiscardAttemptsMustBeValid(Playout playout) { @@ -284,11 +287,12 @@ public class BuildPlayoutHandler : IRequestHandler> PlayoutMustExist( TvContext dbContext, - BuildPlayout buildPlayout) + BuildPlayout buildPlayout, + CancellationToken cancellationToken) { Option maybePlayout = await dbContext.Playouts .Include(p => p.Anchor) - .SelectOneAsync(p => p.Id, p => p.Id == buildPlayout.PlayoutId); + .SelectOneAsync(p => p.Id, p => p.Id == buildPlayout.PlayoutId, cancellationToken); foreach (Playout playout in maybePlayout) { @@ -297,24 +301,24 @@ public class BuildPlayoutHandler : IRequestHandler p.FillGroupIndices) - .LoadAsync(); + .LoadAsync(cancellationToken); foreach (PlayoutScheduleItemFillGroupIndex fillGroupIndex in playout.FillGroupIndices) { await dbContext.Entry(fillGroupIndex) .Reference(fgi => fgi.EnumeratorState) - .LoadAsync(); + .LoadAsync(cancellationToken); } await dbContext.Entry(playout) .Collection(p => p.ProgramScheduleAnchors) - .LoadAsync(); + .LoadAsync(cancellationToken); foreach (PlayoutProgramScheduleAnchor anchor in playout.ProgramScheduleAnchors) { await dbContext.Entry(anchor) .Reference(a => a.EnumeratorState) - .LoadAsync(); + .LoadAsync(cancellationToken); } break; diff --git a/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs index a2b03444c..d46d133d7 100644 --- a/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/CreateBlockPlayoutHandler.cs @@ -20,7 +20,7 @@ public class CreateBlockPlayoutHandler( CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(playout => PersistPlayout(dbContext, playout)); } @@ -40,8 +40,9 @@ public class CreateBlockPlayoutHandler( private static async Task> Validate( TvContext dbContext, - CreateBlockPlayout request) => - (await ValidateChannel(dbContext, request), ValidateScheduleKind(request)) + CreateBlockPlayout request, + CancellationToken cancellationToken) => + (await ValidateChannel(dbContext, request, cancellationToken), ValidateScheduleKind(request)) .Apply((channel, scheduleKind) => new Playout { ChannelId = channel.Id, @@ -51,10 +52,11 @@ public class CreateBlockPlayoutHandler( private static Task> ValidateChannel( TvContext dbContext, - CreateBlockPlayout createBlockPlayout) => + CreateBlockPlayout createBlockPlayout, + CancellationToken cancellationToken) => dbContext.Channels .Include(c => c.Playouts) - .SelectOneAsync(c => c.Id, c => c.Id == createBlockPlayout.ChannelId) + .SelectOneAsync(c => c.Id, c => c.Id == createBlockPlayout.ChannelId, cancellationToken) .Map(o => o.ToValidation("Channel does not exist")) .BindT(ChannelMustNotHavePlayouts); diff --git a/ErsatzTV.Application/Playouts/Commands/CreateClassicPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/CreateClassicPlayoutHandler.cs index abeeabe42..8606b26a9 100644 --- a/ErsatzTV.Application/Playouts/Commands/CreateClassicPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/CreateClassicPlayoutHandler.cs @@ -28,7 +28,7 @@ public class CreateClassicPlayoutHandler : IRequestHandler validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(playout => PersistPlayout(dbContext, playout)); } @@ -48,8 +48,10 @@ public class CreateClassicPlayoutHandler : IRequestHandler> Validate( TvContext dbContext, - CreateClassicPlayout request) => - (await ValidateChannel(dbContext, request), await ValidateProgramSchedule(dbContext, request), + CreateClassicPlayout request, + CancellationToken cancellationToken) => + (await ValidateChannel(dbContext, request, cancellationToken), + await ValidateProgramSchedule(dbContext, request, cancellationToken), ValidateScheduleKind(request)) .Apply((channel, programSchedule, scheduleKind) => new Playout { @@ -60,10 +62,11 @@ public class CreateClassicPlayoutHandler : IRequestHandler> ValidateChannel( TvContext dbContext, - CreateClassicPlayout createClassicPlayout) => + CreateClassicPlayout createClassicPlayout, + CancellationToken cancellationToken) => dbContext.Channels .Include(c => c.Playouts) - .SelectOneAsync(c => c.Id, c => c.Id == createClassicPlayout.ChannelId) + .SelectOneAsync(c => c.Id, c => c.Id == createClassicPlayout.ChannelId, cancellationToken) .Map(o => o.ToValidation("Channel does not exist")) .BindT(ChannelMustNotHavePlayouts); @@ -75,10 +78,11 @@ public class CreateClassicPlayoutHandler : IRequestHandler> ValidateProgramSchedule( TvContext dbContext, - CreateClassicPlayout createClassicPlayout) => + CreateClassicPlayout createClassicPlayout, + CancellationToken cancellationToken) => dbContext.ProgramSchedules .Include(ps => ps.Items) - .SelectOneAsync(ps => ps.Id, ps => ps.Id == createClassicPlayout.ProgramScheduleId) + .SelectOneAsync(ps => ps.Id, ps => ps.Id == createClassicPlayout.ProgramScheduleId, cancellationToken) .Map(o => o.ToValidation("Program schedule does not exist")) .BindT(ProgramScheduleMustHaveItems); diff --git a/ErsatzTV.Application/Playouts/Commands/CreateExternalJsonPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/CreateExternalJsonPlayoutHandler.cs index 263a3b511..763d473e1 100644 --- a/ErsatzTV.Application/Playouts/Commands/CreateExternalJsonPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/CreateExternalJsonPlayoutHandler.cs @@ -33,7 +33,7 @@ public class CreateExternalJsonPlayoutHandler CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(playout => PersistPlayout(dbContext, playout)); } @@ -48,8 +48,9 @@ public class CreateExternalJsonPlayoutHandler private async Task> Validate( TvContext dbContext, - CreateExternalJsonPlayout request) => - (await ValidateChannel(dbContext, request), ValidateExternalJsonFile(request), ValidateScheduleKind(request)) + CreateExternalJsonPlayout request, + CancellationToken cancellationToken) => + (await ValidateChannel(dbContext, request, cancellationToken), ValidateExternalJsonFile(request), ValidateScheduleKind(request)) .Apply((channel, externalJsonFile, scheduleKind) => new Playout { ChannelId = channel.Id, @@ -59,10 +60,11 @@ public class CreateExternalJsonPlayoutHandler private static Task> ValidateChannel( TvContext dbContext, - CreateExternalJsonPlayout createExternalJsonPlayout) => + CreateExternalJsonPlayout createExternalJsonPlayout, + CancellationToken cancellationToken) => dbContext.Channels .Include(c => c.Playouts) - .SelectOneAsync(c => c.Id, c => c.Id == createExternalJsonPlayout.ChannelId) + .SelectOneAsync(c => c.Id, c => c.Id == createExternalJsonPlayout.ChannelId, cancellationToken) .Map(o => o.ToValidation("Channel does not exist")) .BindT(ChannelMustNotHavePlayouts); diff --git a/ErsatzTV.Application/Playouts/Commands/CreateScriptedPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/CreateScriptedPlayoutHandler.cs index f95c86249..c829c6019 100644 --- a/ErsatzTV.Application/Playouts/Commands/CreateScriptedPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/CreateScriptedPlayoutHandler.cs @@ -33,28 +33,35 @@ public class CreateScriptedPlayoutHandler CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(playout => PersistPlayout(dbContext, playout)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(playout => PersistPlayout(dbContext, playout, cancellationToken)); } - private async Task PersistPlayout(TvContext dbContext, Playout playout) + private async Task PersistPlayout( + TvContext dbContext, + Playout playout, + CancellationToken cancellationToken) { - await dbContext.Playouts.AddAsync(playout); - await dbContext.SaveChangesAsync(); - await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Reset)); + await dbContext.Playouts.AddAsync(playout, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Reset), cancellationToken); if (playout.Channel.PlayoutMode is ChannelPlayoutMode.OnDemand) { - await _channel.WriteAsync(new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false)); + await _channel.WriteAsync( + new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false), + cancellationToken); } - await _channel.WriteAsync(new RefreshChannelList()); + await _channel.WriteAsync(new RefreshChannelList(), cancellationToken); return new CreatePlayoutResponse(playout.Id); } private async Task> Validate( TvContext dbContext, - CreateScriptedPlayout request) => - (await ValidateChannel(dbContext, request), ValidateScheduleFile(request), ValidateScheduleKind(request)) + CreateScriptedPlayout request, + CancellationToken cancellationToken) => + (await ValidateChannel(dbContext, request, cancellationToken), ValidateScheduleFile(request), + ValidateScheduleKind(request)) .Apply((channel, yamlFile, scheduleKind) => new Playout { ChannelId = channel.Id, @@ -65,10 +72,11 @@ public class CreateScriptedPlayoutHandler private static Task> ValidateChannel( TvContext dbContext, - CreateScriptedPlayout createScriptedPlayout) => + CreateScriptedPlayout createScriptedPlayout, + CancellationToken cancellationToken) => dbContext.Channels .Include(c => c.Playouts) - .SelectOneAsync(c => c.Id, c => c.Id == createScriptedPlayout.ChannelId) + .SelectOneAsync(c => c.Id, c => c.Id == createScriptedPlayout.ChannelId, cancellationToken) .Map(o => o.ToValidation("Channel does not exist")) .BindT(ChannelMustNotHavePlayouts); diff --git a/ErsatzTV.Application/Playouts/Commands/CreateSequentialPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/CreateSequentialPlayoutHandler.cs index 7981150a0..48e4ef31c 100644 --- a/ErsatzTV.Application/Playouts/Commands/CreateSequentialPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/CreateSequentialPlayoutHandler.cs @@ -33,28 +33,34 @@ public class CreateSequentialPlayoutHandler CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(playout => PersistPlayout(dbContext, playout)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(playout => PersistPlayout(dbContext, playout, cancellationToken)); } - private async Task PersistPlayout(TvContext dbContext, Playout playout) + private async Task PersistPlayout( + TvContext dbContext, + Playout playout, + CancellationToken cancellationToken) { - await dbContext.Playouts.AddAsync(playout); - await dbContext.SaveChangesAsync(); - await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Reset)); + await dbContext.Playouts.AddAsync(playout, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Reset), cancellationToken); if (playout.Channel.PlayoutMode is ChannelPlayoutMode.OnDemand) { - await _channel.WriteAsync(new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false)); + await _channel.WriteAsync( + new TimeShiftOnDemandPlayout(playout.Id, DateTimeOffset.Now, false), + cancellationToken); } - await _channel.WriteAsync(new RefreshChannelList()); + await _channel.WriteAsync(new RefreshChannelList(), cancellationToken); return new CreatePlayoutResponse(playout.Id); } private async Task> Validate( TvContext dbContext, - CreateSequentialPlayout request) => - (await ValidateChannel(dbContext, request), ValidateYamlFile(request), ValidateScheduleKind(request)) + CreateSequentialPlayout request, + CancellationToken cancellationToken) => + (await ValidateChannel(dbContext, request, cancellationToken), ValidateYamlFile(request), ValidateScheduleKind(request)) .Apply((channel, yamlFile, scheduleKind) => new Playout { ChannelId = channel.Id, @@ -65,10 +71,11 @@ public class CreateSequentialPlayoutHandler private static Task> ValidateChannel( TvContext dbContext, - CreateSequentialPlayout createSequentialPlayout) => + CreateSequentialPlayout createSequentialPlayout, + CancellationToken cancellationToken) => dbContext.Channels .Include(c => c.Playouts) - .SelectOneAsync(c => c.Id, c => c.Id == createSequentialPlayout.ChannelId) + .SelectOneAsync(c => c.Id, c => c.Id == createSequentialPlayout.ChannelId, cancellationToken) .Map(o => o.ToValidation("Channel does not exist")) .BindT(ChannelMustNotHavePlayouts); diff --git a/ErsatzTV.Application/Playouts/Commands/DeletePlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/DeletePlayoutHandler.cs index 7cf5ae464..33516fcbd 100644 --- a/ErsatzTV.Application/Playouts/Commands/DeletePlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/DeletePlayoutHandler.cs @@ -31,7 +31,7 @@ public class DeletePlayoutHandler : IRequestHandler maybePlayout = await dbContext.Playouts .Include(p => p.Channel) - .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId); + .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId, cancellationToken); foreach (Playout playout in maybePlayout) { diff --git a/ErsatzTV.Application/Playouts/Commands/ReplacePlayoutAlternateScheduleItemsHandler.cs b/ErsatzTV.Application/Playouts/Commands/ReplacePlayoutAlternateScheduleItemsHandler.cs index d3381a752..d20173bc4 100644 --- a/ErsatzTV.Application/Playouts/Commands/ReplacePlayoutAlternateScheduleItemsHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/ReplacePlayoutAlternateScheduleItemsHandler.cs @@ -40,7 +40,7 @@ public class ReplacePlayoutAlternateScheduleItemsHandler : .Include(p => p.ProgramSchedule) .Include(p => p.ProgramScheduleAlternates) .ThenInclude(p => p.ProgramSchedule) - .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId); + .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId, cancellationToken); foreach (Playout playout in maybePlayout) { diff --git a/ErsatzTV.Application/Playouts/Commands/TimeShiftOnDemandPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/TimeShiftOnDemandPlayoutHandler.cs index 98c747f20..83cf9429a 100644 --- a/ErsatzTV.Application/Playouts/Commands/TimeShiftOnDemandPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/TimeShiftOnDemandPlayoutHandler.cs @@ -10,7 +10,7 @@ public class TimeShiftOnDemandPlayoutHandler(IPlayoutTimeShifter playoutTimeShif { try { - await playoutTimeShifter.TimeShift(request.PlayoutId, request.Now, request.Force); + await playoutTimeShifter.TimeShift(request.PlayoutId, request.Now, request.Force, cancellationToken); } catch (Exception ex) { diff --git a/ErsatzTV.Application/Playouts/Commands/UpdateExternalJsonPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/UpdateExternalJsonPlayoutHandler.cs index e37658756..8f04fc261 100644 --- a/ErsatzTV.Application/Playouts/Commands/UpdateExternalJsonPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/UpdateExternalJsonPlayoutHandler.cs @@ -28,20 +28,21 @@ public class CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout, cancellationToken)); } private async Task ApplyUpdateRequest( TvContext dbContext, UpdateExternalJsonPlayout request, - Playout playout) + Playout playout, + CancellationToken cancellationToken) { playout.ScheduleFile = request.ScheduleFile; - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { - await _workerChannel.WriteAsync(new RefreshChannelData(playout.Channel.Number)); + await _workerChannel.WriteAsync(new RefreshChannelData(playout.Channel.Number), cancellationToken); } return new PlayoutNameViewModel( @@ -57,14 +58,16 @@ public class private static Task> Validate( TvContext dbContext, - UpdateExternalJsonPlayout request) => - PlayoutMustExist(dbContext, request); + UpdateExternalJsonPlayout request, + CancellationToken cancellationToken) => + PlayoutMustExist(dbContext, request, cancellationToken); private static Task> PlayoutMustExist( TvContext dbContext, - UpdateExternalJsonPlayout updatePlayout) => + UpdateExternalJsonPlayout updatePlayout, + CancellationToken cancellationToken) => dbContext.Playouts .Include(p => p.Channel) - .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId) + .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId, cancellationToken) .Map(o => o.ToValidation("Playout does not exist.")); } diff --git a/ErsatzTV.Application/Playouts/Commands/UpdateOnDemandCheckpointHandler.cs b/ErsatzTV.Application/Playouts/Commands/UpdateOnDemandCheckpointHandler.cs index 888e43ea2..19c15367d 100644 --- a/ErsatzTV.Application/Playouts/Commands/UpdateOnDemandCheckpointHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/UpdateOnDemandCheckpointHandler.cs @@ -21,7 +21,7 @@ public class UpdateOnDemandCheckpointHandler( Option maybePlayout = await dbContext.Playouts .Include(p => p.Channel) .Include(p => p.Items) - .SelectOneAsync(p => p.Channel.Number, p => p.Channel.Number == request.ChannelNumber); + .SelectOneAsync(p => p.Channel.Number, p => p.Channel.Number == request.ChannelNumber, cancellationToken); foreach (Playout playout in maybePlayout) { @@ -32,7 +32,9 @@ public class UpdateOnDemandCheckpointHandler( playout.OnDemandCheckpoint ??= SystemTime.MinValueUtc; - int timeout = await (await configElementRepository.GetValue(ConfigElementKey.FFmpegSegmenterTimeout)) + int timeout = await (await configElementRepository.GetValue( + ConfigElementKey.FFmpegSegmenterTimeout, + cancellationToken)) .IfNoneAsync(60); // don't move checkpoint back in time diff --git a/ErsatzTV.Application/Playouts/Commands/UpdatePlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/UpdatePlayoutHandler.cs index a2099a970..f7fd77c7d 100644 --- a/ErsatzTV.Application/Playouts/Commands/UpdatePlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/UpdatePlayoutHandler.cs @@ -18,14 +18,15 @@ public class UpdatePlayoutHandler : IRequestHandler validation = await Validate(dbContext, request); - return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout, cancellationToken)); } private static async Task ApplyUpdateRequest( TvContext dbContext, UpdatePlayout request, - Playout playout) + Playout playout, + CancellationToken cancellationToken) { playout.DailyRebuildTime = null; @@ -34,7 +35,7 @@ public class UpdatePlayoutHandler : IRequestHandler> Validate(TvContext dbContext, UpdatePlayout request) => - PlayoutMustExist(dbContext, request); + private static Task> Validate( + TvContext dbContext, + UpdatePlayout request, + CancellationToken cancellationToken) => + PlayoutMustExist(dbContext, request, cancellationToken); private static Task> PlayoutMustExist( TvContext dbContext, - UpdatePlayout updatePlayout) => + UpdatePlayout updatePlayout, + CancellationToken cancellationToken) => dbContext.Playouts .Include(p => p.Channel) .Include(p => p.ProgramSchedule) - .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId) + .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId, cancellationToken) .Map(o => o.ToValidation("Playout does not exist.")); } diff --git a/ErsatzTV.Application/Playouts/Commands/UpdateScriptedPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/UpdateScriptedPlayoutHandler.cs index d89ab8e7e..052c6c1d3 100644 --- a/ErsatzTV.Application/Playouts/Commands/UpdateScriptedPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/UpdateScriptedPlayoutHandler.cs @@ -20,20 +20,21 @@ public class CancellationToken cancellationToken) { await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout, cancellationToken)); } private async Task ApplyUpdateRequest( TvContext dbContext, UpdateScriptedPlayout request, - Playout playout) + Playout playout, + CancellationToken cancellationToken) { playout.ScheduleFile = request.ScheduleFile; - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { - await workerChannel.WriteAsync(new RefreshChannelData(playout.Channel.Number)); + await workerChannel.WriteAsync(new RefreshChannelData(playout.Channel.Number), cancellationToken); } return new PlayoutNameViewModel( @@ -49,14 +50,16 @@ public class private static Task> Validate( TvContext dbContext, - UpdateScriptedPlayout request) => - PlayoutMustExist(dbContext, request); + UpdateScriptedPlayout request, + CancellationToken cancellationToken) => + PlayoutMustExist(dbContext, request, cancellationToken); private static Task> PlayoutMustExist( TvContext dbContext, - UpdateScriptedPlayout updatePlayout) => + UpdateScriptedPlayout updatePlayout, + CancellationToken cancellationToken) => dbContext.Playouts .Include(p => p.Channel) - .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId) + .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId, cancellationToken) .Map(o => o.ToValidation("Playout does not exist.")); } diff --git a/ErsatzTV.Application/Playouts/Commands/UpdateSequentialPlayoutHandler.cs b/ErsatzTV.Application/Playouts/Commands/UpdateSequentialPlayoutHandler.cs index ba7b23f0c..3ff9a58cb 100644 --- a/ErsatzTV.Application/Playouts/Commands/UpdateSequentialPlayoutHandler.cs +++ b/ErsatzTV.Application/Playouts/Commands/UpdateSequentialPlayoutHandler.cs @@ -28,20 +28,21 @@ public class CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout, cancellationToken)); } private async Task ApplyUpdateRequest( TvContext dbContext, UpdateSequentialPlayout request, - Playout playout) + Playout playout, + CancellationToken cancellationToken) { playout.ScheduleFile = request.ScheduleFile; - if (await dbContext.SaveChangesAsync() > 0) + if (await dbContext.SaveChangesAsync(cancellationToken) > 0) { - await _workerChannel.WriteAsync(new RefreshChannelData(playout.Channel.Number)); + await _workerChannel.WriteAsync(new RefreshChannelData(playout.Channel.Number), cancellationToken); } return new PlayoutNameViewModel( @@ -57,14 +58,16 @@ public class private static Task> Validate( TvContext dbContext, - UpdateSequentialPlayout request) => - PlayoutMustExist(dbContext, request); + UpdateSequentialPlayout request, + CancellationToken cancellationToken) => + PlayoutMustExist(dbContext, request, cancellationToken); private static Task> PlayoutMustExist( TvContext dbContext, - UpdateSequentialPlayout updatePlayout) => + UpdateSequentialPlayout updatePlayout, + CancellationToken cancellationToken) => dbContext.Playouts .Include(p => p.Channel) - .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId) + .SelectOneAsync(p => p.Id, p => p.Id == updatePlayout.PlayoutId, cancellationToken) .Map(o => o.ToValidation("Playout does not exist.")); } diff --git a/ErsatzTV.Application/Playouts/Queries/GetPlayoutAlternateSchedulesHandler.cs b/ErsatzTV.Application/Playouts/Queries/GetPlayoutAlternateSchedulesHandler.cs index bb2636b00..e04fa7a5b 100644 --- a/ErsatzTV.Application/Playouts/Queries/GetPlayoutAlternateSchedulesHandler.cs +++ b/ErsatzTV.Application/Playouts/Queries/GetPlayoutAlternateSchedulesHandler.cs @@ -23,12 +23,12 @@ public class GetPlayoutAlternateSchedulesHandler : List result = await dbContext.ProgramScheduleAlternates .Filter(psa => psa.PlayoutId == request.PlayoutId) .Include(psa => psa.ProgramSchedule) - .Map(psa => ProjectToViewModel(psa)) - .ToListAsync(cancellationToken); + .ToListAsync(cancellationToken) + .Map(list => list.Map(ProjectToViewModel).ToList()); Option maybeDefaultSchedule = await dbContext.Playouts .Include(p => p.ProgramSchedule) - .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId) + .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId, cancellationToken) .MapT(p => p.ProgramSchedule); foreach (ProgramSchedule defaultSchedule in maybeDefaultSchedule) diff --git a/ErsatzTV.Application/Playouts/Queries/GetPlayoutByIdHandler.cs b/ErsatzTV.Application/Playouts/Queries/GetPlayoutByIdHandler.cs index 152e8e40a..9a4c2dadc 100644 --- a/ErsatzTV.Application/Playouts/Queries/GetPlayoutByIdHandler.cs +++ b/ErsatzTV.Application/Playouts/Queries/GetPlayoutByIdHandler.cs @@ -16,7 +16,7 @@ public class GetPlayoutByIdHandler(IDbContextFactory dbContextFactory .AsNoTracking() .Include(p => p.ProgramSchedule) .Include(p => p.Channel) - .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId) + .SelectOneAsync(p => p.Id, p => p.Id == request.PlayoutId, cancellationToken) .MapT(p => new PlayoutNameViewModel( p.Id, p.ScheduleKind, diff --git a/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs b/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs index e5579f5a6..d854419fb 100644 --- a/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/CallPlexCollectionScannerHandler.cs @@ -26,7 +26,7 @@ public class CallPlexCollectionScannerHandler : CallLibraryScannerHandler> Handle(SynchronizePlexCollections request, CancellationToken cancellationToken) { - Validation validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -40,10 +40,13 @@ public class CallPlexCollectionScannerHandler : CallLibraryScannerHandler GetLastScan(TvContext dbContext, SynchronizePlexCollections request) + protected override async Task GetLastScan( + TvContext dbContext, + SynchronizePlexCollections request, + CancellationToken cancellationToken) { DateTime minDateTime = await dbContext.PlexMediaSources - .SelectOneAsync(l => l.Id, l => l.Id == request.PlexMediaSourceId) + .SelectOneAsync(l => l.Id, l => l.Id == request.PlexMediaSourceId, cancellationToken) .Match(l => l.LastCollectionsScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); return new DateTimeOffset(minDateTime, TimeSpan.Zero); diff --git a/ErsatzTV.Application/Plex/Commands/CallPlexLibraryScannerHandler.cs b/ErsatzTV.Application/Plex/Commands/CallPlexLibraryScannerHandler.cs index c9893355e..cece8d7f6 100644 --- a/ErsatzTV.Application/Plex/Commands/CallPlexLibraryScannerHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/CallPlexLibraryScannerHandler.cs @@ -38,7 +38,7 @@ public class CallPlexLibraryScannerHandler : CallLibraryScannerHandler validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -77,10 +77,11 @@ public class CallPlexLibraryScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - ISynchronizePlexLibraryById request) + ISynchronizePlexLibraryById request, + CancellationToken cancellationToken) { DateTime minDateTime = await dbContext.PlexLibraries - .SelectOneAsync(l => l.Id, l => l.Id == request.PlexLibraryId) + .SelectOneAsync(l => l.Id, l => l.Id == request.PlexLibraryId, cancellationToken) .Match(l => l.LastScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); return new DateTimeOffset(minDateTime, TimeSpan.Zero); diff --git a/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs b/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs index 5d750096c..b250e17f4 100644 --- a/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/CallPlexNetworkScannerHandler.cs @@ -27,7 +27,7 @@ public class CallPlexNetworkScannerHandler : CallLibraryScannerHandler> Handle(SynchronizePlexNetworks request, CancellationToken cancellationToken) { - Validation validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -41,11 +41,14 @@ public class CallPlexNetworkScannerHandler : CallLibraryScannerHandler GetLastScan(TvContext dbContext, SynchronizePlexNetworks request) + protected override async Task GetLastScan( + TvContext dbContext, + SynchronizePlexNetworks request, + CancellationToken cancellationToken) { DateTime minDateTime = await dbContext.PlexLibraries .Filter(l => l.MediaKind == LibraryMediaKind.Shows) - .SelectOneAsync(l => l.Id, l => l.Id == request.PlexLibraryId) + .SelectOneAsync(l => l.Id, l => l.Id == request.PlexLibraryId, cancellationToken) .Match(l => l.LastNetworksScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc); return new DateTimeOffset(minDateTime, TimeSpan.Zero); diff --git a/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs b/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs index 720a432f2..0033a97b1 100644 --- a/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/CallPlexShowScannerHandler.cs @@ -31,7 +31,7 @@ public class CallPlexShowScannerHandler : CallLibraryScannerHandler validation = await Validate(request); + Validation validation = await Validate(request, cancellationToken); return await validation.Match( scanner => PerformScan(scanner, request, cancellationToken), error => @@ -67,7 +67,8 @@ public class CallPlexShowScannerHandler : CallLibraryScannerHandler GetLastScan( TvContext dbContext, - SynchronizePlexShowById request) => + SynchronizePlexShowById request, + CancellationToken cancellationToken) => Task.FromResult(DateTimeOffset.MinValue); protected override bool ScanIsRequired( diff --git a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs index 017aaece8..e9d694f18 100644 --- a/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/SynchronizePlexLibrariesHandler.cs @@ -34,17 +34,21 @@ public class public Task> Handle( SynchronizePlexLibraries request, CancellationToken cancellationToken) => - Validate(request) - .MapT(SynchronizeLibraries) + Validate(request, cancellationToken) + .MapT(p => SynchronizeLibraries(p, cancellationToken)) .Bind(v => v.ToEitherAsync()); - private Task> Validate(SynchronizePlexLibraries request) => - MediaSourceMustExist(request) + private Task> Validate( + SynchronizePlexLibraries request, + CancellationToken cancellationToken) => + MediaSourceMustExist(request, cancellationToken) .BindT(MediaSourceMustHaveActiveConnection) .BindT(MediaSourceMustHaveToken); - private Task> MediaSourceMustExist(SynchronizePlexLibraries request) => - _mediaSourceRepository.GetPlex(request.PlexMediaSourceId) + private Task> MediaSourceMustExist( + SynchronizePlexLibraries request, + CancellationToken cancellationToken) => + _mediaSourceRepository.GetPlex(request.PlexMediaSourceId, cancellationToken) .Map(o => o.ToValidation("Plex media source does not exist.")); private Validation MediaSourceMustHaveActiveConnection( @@ -65,7 +69,9 @@ public class .ToValidation("Plex media source requires a token"); } - private async Task SynchronizeLibraries(ConnectionParameters connectionParameters) + private async Task SynchronizeLibraries( + ConnectionParameters connectionParameters, + CancellationToken cancellationToken) { Either> maybeLibraries = await _plexServerApiClient.GetLibraries( connectionParameters.ActiveConnection, @@ -90,7 +96,8 @@ public class connectionParameters.PlexMediaSource.Id, toAdd, toRemove, - toUpdate); + toUpdate, + cancellationToken); if (ids.Count != 0) { await _searchIndex.RemoveItems(ids); diff --git a/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs b/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs index 6bd9e5f68..6192f90dd 100644 --- a/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs +++ b/ErsatzTV.Application/Plex/Commands/UpdatePlexPathReplacementsHandler.cs @@ -15,13 +15,13 @@ public class public Task> Handle( UpdatePlexPathReplacements request, CancellationToken cancellationToken) => - Validate(request) + Validate(request, cancellationToken) .MapT(pms => MergePathReplacements(request, pms)) .Bind(v => v.ToEitherAsync()); private Task MergePathReplacements(UpdatePlexPathReplacements request, PlexMediaSource plexMediaSource) { - plexMediaSource.PathReplacements ??= new List(); + plexMediaSource.PathReplacements ??= []; var incoming = request.PathReplacements.Map(Project).ToList(); @@ -35,11 +35,14 @@ public class private static PlexPathReplacement Project(PlexPathReplacementItem vm) => new() { Id = vm.Id, PlexPath = vm.PlexPath, LocalPath = vm.LocalPath }; - private Task> Validate(UpdatePlexPathReplacements request) => - PlexMediaSourceMustExist(request); + private Task> Validate( + UpdatePlexPathReplacements request, + CancellationToken cancellationToken) => + PlexMediaSourceMustExist(request, cancellationToken); private Task> PlexMediaSourceMustExist( - UpdatePlexPathReplacements request) => - _mediaSourceRepository.GetPlex(request.PlexMediaSourceId) + UpdatePlexPathReplacements request, + CancellationToken cancellationToken) => + _mediaSourceRepository.GetPlex(request.PlexMediaSourceId, cancellationToken) .Map(v => v.ToValidation($"Plex media source {request.PlexMediaSourceId} does not exist.")); } diff --git a/ErsatzTV.Application/Plex/Queries/GetPlexConnectionParametersHandler.cs b/ErsatzTV.Application/Plex/Queries/GetPlexConnectionParametersHandler.cs index aca811499..9dea4a07c 100644 --- a/ErsatzTV.Application/Plex/Queries/GetPlexConnectionParametersHandler.cs +++ b/ErsatzTV.Application/Plex/Queries/GetPlexConnectionParametersHandler.cs @@ -42,7 +42,8 @@ public class GetPlexConnectionParametersHandler : PlexBaseConnectionHandler, return parameters; } - Option maybeMediaSource = await _mediaSourceRepository.GetPlex(request.PlexMediaSourceId); + Option maybeMediaSource = + await _mediaSourceRepository.GetPlex(request.PlexMediaSourceId, cancellationToken); foreach (PlexMediaSource mediaSource in maybeMediaSource) { Option maybeToken = diff --git a/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceIdHandler.cs b/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceIdHandler.cs index 7b1a8552f..5346365de 100644 --- a/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceIdHandler.cs +++ b/ErsatzTV.Application/Plex/Queries/GetPlexLibrariesBySourceIdHandler.cs @@ -20,7 +20,7 @@ public class GetPlexLibrariesBySourceIdHandler : IRequestHandler l.MediaSourceId == request.PlexMediaSourceId) - .Map(pl => ProjectToViewModel(pl)) - .ToListAsync(cancellationToken); + .ToListAsync(cancellationToken) + .Map(list => list.Map(ProjectToViewModel).ToList()); } } diff --git a/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceByIdHandler.cs b/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceByIdHandler.cs index 7550fd53a..31be4be97 100644 --- a/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceByIdHandler.cs +++ b/ErsatzTV.Application/Plex/Queries/GetPlexMediaSourceByIdHandler.cs @@ -14,5 +14,5 @@ public class public Task> Handle( GetPlexMediaSourceById request, CancellationToken cancellationToken) => - _mediaSourceRepository.GetPlex(request.PlexMediaSourceId).MapT(ProjectToViewModel); + _mediaSourceRepository.GetPlex(request.PlexMediaSourceId, cancellationToken).MapT(ProjectToViewModel); } diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItemHandler.cs b/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItemHandler.cs index 19897c639..6228d4ecb 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItemHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/AddProgramScheduleItemHandler.cs @@ -28,26 +28,27 @@ public class AddProgramScheduleItemHandler : ProgramScheduleItemCommandBase, CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(ps => PersistItem(dbContext, request, ps)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(ps => PersistItem(dbContext, request, ps, cancellationToken)); } private async Task PersistItem( TvContext dbContext, AddProgramScheduleItem request, - ProgramSchedule programSchedule) + ProgramSchedule programSchedule, + CancellationToken cancellationToken) { int nextIndex = programSchedule.Items.Select(i => i.Index).DefaultIfEmpty(0).Max() + 1; ProgramScheduleItem item = BuildItem(programSchedule, nextIndex, request); programSchedule.Items.Add(item); - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); // refresh any playouts that use this schedule foreach (Playout playout in programSchedule.Playouts) { - await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Refresh)); + await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Refresh), cancellationToken); } return ProjectToViewModel(item); @@ -55,7 +56,8 @@ public class AddProgramScheduleItemHandler : ProgramScheduleItemCommandBase, private static Task> Validate( TvContext dbContext, - AddProgramScheduleItem request) => - ProgramScheduleMustExist(dbContext, request.ProgramScheduleId) + AddProgramScheduleItem request, + CancellationToken cancellationToken) => + ProgramScheduleMustExist(dbContext, request.ProgramScheduleId, cancellationToken) .BindT(programSchedule => PlayoutModeMustBeValid(request, programSchedule)); } diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramScheduleHandler.cs b/ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramScheduleHandler.cs index 7b6cabd0d..24f45c053 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramScheduleHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/CopyProgramScheduleHandler.cs @@ -22,7 +22,7 @@ public class try { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(p => PerformCopy(dbContext, p, request, cancellationToken)); } catch (Exception ex) @@ -68,18 +68,20 @@ public class private static async Task> Validate( TvContext dbContext, - CopyProgramSchedule request) => - (await ScheduleMustExist(dbContext, request), await ValidateName(dbContext, request)) + CopyProgramSchedule request, + CancellationToken cancellationToken) => + (await ScheduleMustExist(dbContext, request, cancellationToken), await ValidateName(dbContext, request)) .Apply((programSchedule, _) => programSchedule); private static Task> ScheduleMustExist( TvContext dbContext, - CopyProgramSchedule request) => + CopyProgramSchedule request, + CancellationToken cancellationToken) => dbContext.ProgramSchedules .AsNoTracking() .Include(ps => ps.Items) .ThenInclude(ps => ps.ProgramScheduleItemWatermarks) - .SelectOneAsync(p => p.Id, p => p.Id == request.ProgramScheduleId) + .SelectOneAsync(p => p.Id, p => p.Id == request.ProgramScheduleId, cancellationToken) .Map(o => o.ToValidation("Schedule does not exist.")); private static async Task> ValidateName( diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/DeleteProgramScheduleHandler.cs b/ErsatzTV.Application/ProgramSchedules/Commands/DeleteProgramScheduleHandler.cs index b192078a0..0d7756475 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/DeleteProgramScheduleHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/DeleteProgramScheduleHandler.cs @@ -18,7 +18,10 @@ public class DeleteProgramScheduleHandler : IRequestHandler validation = await ProgramScheduleMustExist(dbContext, request); + Validation validation = await ProgramScheduleMustExist( + dbContext, + request, + cancellationToken); return await validation.Apply(ps => DoDeletion(dbContext, ps)); } @@ -30,8 +33,9 @@ public class DeleteProgramScheduleHandler : IRequestHandler> ProgramScheduleMustExist( TvContext dbContext, - DeleteProgramSchedule request) => + DeleteProgramSchedule request, + CancellationToken cancellationToken) => dbContext.ProgramSchedules - .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.ProgramScheduleId) + .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.ProgramScheduleId, cancellationToken) .Map(o => o.ToValidation($"ProgramSchedule {request.ProgramScheduleId} does not exist.")); } diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs index 157e4d486..baef7ebe4 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/ProgramScheduleItemCommandBase.cs @@ -11,11 +11,12 @@ public abstract class ProgramScheduleItemCommandBase { protected static Task> ProgramScheduleMustExist( TvContext dbContext, - int programScheduleId) => + int programScheduleId, + CancellationToken cancellationToken) => dbContext.ProgramSchedules .Include(ps => ps.Items) .Include(ps => ps.Playouts) - .SelectOneAsync(ps => ps.Id, ps => ps.Id == programScheduleId) + .SelectOneAsync(ps => ps.Id, ps => ps.Id == programScheduleId, cancellationToken) .Map(o => o.ToValidation("[ProgramScheduleId] does not exist.")); protected static async Task> FillerConfigurationMustBeValid( diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs b/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs index 8c0c8e2f7..97540c946 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/ReplaceProgramScheduleItemsHandler.cs @@ -28,24 +28,25 @@ public class ReplaceProgramScheduleItemsHandler : ProgramScheduleItemCommandBase CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); - return await validation.Apply(ps => PersistItems(dbContext, request, ps)); + Validation validation = await Validate(dbContext, request, cancellationToken); + return await validation.Apply(ps => PersistItems(dbContext, request, ps, cancellationToken)); } private async Task> PersistItems( TvContext dbContext, ReplaceProgramScheduleItems request, - ProgramSchedule programSchedule) + ProgramSchedule programSchedule, + CancellationToken cancellationToken) { dbContext.RemoveRange(programSchedule.Items); programSchedule.Items = request.Items.Map(i => BuildItem(programSchedule, i.Index, i)).ToList(); - await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(cancellationToken); // refresh any playouts that use this schedule foreach (Playout playout in programSchedule.Playouts) { - await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Refresh)); + await _channel.WriteAsync(new BuildPlayout(playout.Id, PlayoutBuildMode.Refresh), cancellationToken); } return programSchedule.Items.Map(ProjectToViewModel); @@ -53,8 +54,9 @@ public class ReplaceProgramScheduleItemsHandler : ProgramScheduleItemCommandBase private static Task> Validate( TvContext dbContext, - ReplaceProgramScheduleItems request) => - ProgramScheduleMustExist(dbContext, request.ProgramScheduleId) + ReplaceProgramScheduleItems request, + CancellationToken cancellationToken) => + ProgramScheduleMustExist(dbContext, request.ProgramScheduleId, cancellationToken) .BindT(programSchedule => PlayoutModesMustBeValid(request, programSchedule)) .BindT(programSchedule => CollectionTypesMustBeValid(request, programSchedule)) .BindT(programSchedule => PlaybackOrdersMustBeValid(request, programSchedule)) diff --git a/ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramScheduleHandler.cs b/ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramScheduleHandler.cs index 0a5641dc2..0aa7bf280 100644 --- a/ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramScheduleHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Commands/UpdateProgramScheduleHandler.cs @@ -28,7 +28,7 @@ public class UpdateProgramScheduleHandler : CancellationToken cancellationToken) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); - Validation validation = await Validate(dbContext, request); + Validation validation = await Validate(dbContext, request, cancellationToken); return await validation.Apply(ps => ApplyUpdateRequest(dbContext, ps, request)); } @@ -73,15 +73,17 @@ public class UpdateProgramScheduleHandler : private static async Task> Validate( TvContext dbContext, - UpdateProgramSchedule request) => - (await ProgramScheduleMustExist(dbContext, request), ValidateName(request)) + UpdateProgramSchedule request, + CancellationToken cancellationToken) => + (await ProgramScheduleMustExist(dbContext, request, cancellationToken), ValidateName(request)) .Apply((programSchedule, _) => programSchedule); private static Task> ProgramScheduleMustExist( TvContext dbContext, - UpdateProgramSchedule request) => + UpdateProgramSchedule request, + CancellationToken cancellationToken) => dbContext.ProgramSchedules - .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.ProgramScheduleId) + .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.ProgramScheduleId, cancellationToken) .Map(o => o.ToValidation("ProgramSchedule does not exist")); private static Validation ValidateName(UpdateProgramSchedule request) => diff --git a/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleByIdHandler.cs b/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleByIdHandler.cs index 47a5bff9b..a75ee0282 100644 --- a/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleByIdHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleByIdHandler.cs @@ -19,7 +19,8 @@ public class GetProgramScheduleByIdHandler : { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken); return await dbContext.ProgramSchedules - .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.Id) + .AsNoTracking() + .SelectOneAsync(ps => ps.Id, ps => ps.Id == request.Id, cancellationToken) .MapT(ProjectToViewModel); } } diff --git a/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleItemsHandler.cs b/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleItemsHandler.cs index f7d24138b..1492293e2 100644 --- a/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleItemsHandler.cs +++ b/ErsatzTV.Application/ProgramSchedules/Queries/GetProgramScheduleItemsHandler.cs @@ -16,7 +16,7 @@ public class GetProgramScheduleItemsHandler(IDbContextFactory dbConte await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeProgramSchedule = - await dbContext.ProgramSchedules.SelectOneAsync(ps => ps.Id, ps => ps.Id == request.Id); + await dbContext.ProgramSchedules.SelectOneAsync(ps => ps.Id, ps => ps.Id == request.Id, cancellationToken); return await dbContext.ProgramScheduleItems .Filter(psi => psi.ProgramScheduleId == request.Id) diff --git a/ErsatzTV.Application/Resolutions/Commands/DeleteCustomResolutionHandler.cs b/ErsatzTV.Application/Resolutions/Commands/DeleteCustomResolutionHandler.cs index b8fd61340..6b8ce65a1 100644 --- a/ErsatzTV.Application/Resolutions/Commands/DeleteCustomResolutionHandler.cs +++ b/ErsatzTV.Application/Resolutions/Commands/DeleteCustomResolutionHandler.cs @@ -20,7 +20,7 @@ public class DeleteCustomResolutionHandler : IRequestHandler maybeResolution = await dbContext.Resolutions .AsNoTracking() - .SelectOneAsync(p => p.Id, p => p.Id == request.ResolutionId && p.IsCustom == true); + .SelectOneAsync(p => p.Id, p => p.Id == request.ResolutionId && p.IsCustom == true, cancellationToken); foreach (Resolution resolution in maybeResolution) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteBlockGroupHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteBlockGroupHandler.cs index 28949cdf8..c8f5369d8 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteBlockGroupHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteBlockGroupHandler.cs @@ -14,7 +14,7 @@ public class DeleteBlockGroupHandler(IDbContextFactory dbContextFacto await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeBlockGroup = await dbContext.BlockGroups - .SelectOneAsync(p => p.Id, p => p.Id == request.BlockGroupId); + .SelectOneAsync(p => p.Id, p => p.Id == request.BlockGroupId, cancellationToken); foreach (BlockGroup blockGroup in maybeBlockGroup) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteBlockHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteBlockHandler.cs index e84173053..bbbf8f312 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteBlockHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteBlockHandler.cs @@ -14,7 +14,7 @@ public class DeleteBlockHandler(IDbContextFactory dbContextFactory) await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeBlock = await dbContext.Blocks - .SelectOneAsync(p => p.Id, p => p.Id == request.BlockId); + .SelectOneAsync(p => p.Id, p => p.Id == request.BlockId, cancellationToken); foreach (Block block in maybeBlock) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoGroupHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoGroupHandler.cs index 019982d9c..418b05072 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoGroupHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoGroupHandler.cs @@ -14,7 +14,7 @@ public class DeleteDecoGroupHandler(IDbContextFactory dbContextFactor await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeDecoGroup = await dbContext.DecoGroups - .SelectOneAsync(p => p.Id, p => p.Id == request.DecoGroupId); + .SelectOneAsync(p => p.Id, p => p.Id == request.DecoGroupId, cancellationToken); foreach (DecoGroup decoGroup in maybeDecoGroup) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoHandler.cs index 7a4172cc6..3270534dd 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoHandler.cs @@ -14,7 +14,7 @@ public class DeleteDecoHandler(IDbContextFactory dbContextFactory) await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeDeco = await dbContext.Decos - .SelectOneAsync(p => p.Id, p => p.Id == request.DecoId); + .SelectOneAsync(p => p.Id, p => p.Id == request.DecoId, cancellationToken); foreach (Deco deco in maybeDeco) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateGroupHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateGroupHandler.cs index b7eb96133..e9b708565 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateGroupHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateGroupHandler.cs @@ -14,7 +14,7 @@ public class DeleteDecoTemplateGroupHandler(IDbContextFactory dbConte await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeDecoTemplateGroup = await dbContext.DecoTemplateGroups - .SelectOneAsync(p => p.Id, p => p.Id == request.DecoTemplateGroupId); + .SelectOneAsync(p => p.Id, p => p.Id == request.DecoTemplateGroupId, cancellationToken); foreach (DecoTemplateGroup decoTemplateGroup in maybeDecoTemplateGroup) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateHandler.cs index 82f556d7d..1ca5f85b5 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteDecoTemplateHandler.cs @@ -14,7 +14,7 @@ public class DeleteDecoTemplateHandler(IDbContextFactory dbContextFac await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeDecoTemplate = await dbContext.DecoTemplates - .SelectOneAsync(p => p.Id, p => p.Id == request.DecoTemplateId); + .SelectOneAsync(p => p.Id, p => p.Id == request.DecoTemplateId, cancellationToken); foreach (DecoTemplate decoTemplate in maybeDecoTemplate) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateGroupHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateGroupHandler.cs index cd99139ad..803934a27 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateGroupHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateGroupHandler.cs @@ -14,7 +14,7 @@ public class DeleteTemplateGroupHandler(IDbContextFactory dbContextFa await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option maybeTemplateGroup = await dbContext.TemplateGroups - .SelectOneAsync(p => p.Id, p => p.Id == request.TemplateGroupId); + .SelectOneAsync(p => p.Id, p => p.Id == request.TemplateGroupId, cancellationToken); foreach (TemplateGroup templateGroup in maybeTemplateGroup) { diff --git a/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateHandler.cs b/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateHandler.cs index 5cdee2189..dea0b3f0f 100644 --- a/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateHandler.cs +++ b/ErsatzTV.Application/Scheduling/Commands/DeleteTemplateHandler.cs @@ -14,7 +14,7 @@ public class DeleteTemplateHandler(IDbContextFactory dbContextFactory await using TvContext dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); Option