Browse Source

use cancellation tokens in ui (#666)

* use cancellation tokens in pages

* use cancellation token in shared ui

* use cancellation tokens in artwork controller
pull/667/head
Jason Dove 4 years ago committed by GitHub
parent
commit
452f361384
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      ErsatzTV.Core/Interfaces/GitHub/IGitHubApiClient.cs
  2. 9
      ErsatzTV.Infrastructure/GitHub/GitHubApiClient.cs
  3. 5
      ErsatzTV.Infrastructure/GitHub/IGitHubApi.cs
  4. 68
      ErsatzTV/Controllers/ArtworkController.cs
  5. 19
      ErsatzTV/Pages/Artist.razor
  6. 4
      ErsatzTV/Pages/ArtistList.razor
  7. 41
      ErsatzTV/Pages/ChannelEditor.razor
  8. 20
      ErsatzTV/Pages/Channels.razor
  9. 17
      ErsatzTV/Pages/CollectionEditor.razor
  10. 6
      ErsatzTV/Pages/CollectionItems.razor
  11. 34
      ErsatzTV/Pages/Collections.razor
  12. 11
      ErsatzTV/Pages/EmbyLibrariesEditor.razor
  13. 13
      ErsatzTV/Pages/EmbyMediaSourceEditor.razor
  14. 11
      ErsatzTV/Pages/EmbyMediaSources.razor
  15. 13
      ErsatzTV/Pages/EmbyPathReplacementsEditor.razor
  16. 4
      ErsatzTV/Pages/EpisodeList.razor
  17. 20
      ErsatzTV/Pages/FFmpeg.razor
  18. 19
      ErsatzTV/Pages/FFmpegEditor.razor
  19. 26
      ErsatzTV/Pages/FillerPresetEditor.razor
  20. 19
      ErsatzTV/Pages/FillerPresets.razor
  21. 13
      ErsatzTV/Pages/FragmentNavigationBase.cs
  22. 15
      ErsatzTV/Pages/Index.razor
  23. 19
      ErsatzTV/Pages/JellyfinLibrariesEditor.razor
  24. 13
      ErsatzTV/Pages/JellyfinMediaSourceEditor.razor
  25. 11
      ErsatzTV/Pages/JellyfinMediaSources.razor
  26. 17
      ErsatzTV/Pages/JellyfinPathReplacementsEditor.razor
  27. 19
      ErsatzTV/Pages/Libraries.razor
  28. 18
      ErsatzTV/Pages/LocalLibraries.razor
  29. 17
      ErsatzTV/Pages/LocalLibraryEditor.razor
  30. 15
      ErsatzTV/Pages/LocalLibraryPathEditor.razor
  31. 21
      ErsatzTV/Pages/Logs.razor
  32. 15
      ErsatzTV/Pages/Movie.razor
  33. 11
      ErsatzTV/Pages/MovieList.razor
  34. 20
      ErsatzTV/Pages/MultiCollectionEditor.razor
  35. 6
      ErsatzTV/Pages/MultiSelectBase.cs
  36. 4
      ErsatzTV/Pages/MusicVideoList.razor
  37. 4
      ErsatzTV/Pages/OtherVideoList.razor
  38. 15
      ErsatzTV/Pages/PlayoutEditor.razor
  39. 30
      ErsatzTV/Pages/Playouts.razor
  40. 19
      ErsatzTV/Pages/PlexLibrariesEditor.razor
  41. 1
      ErsatzTV/Pages/PlexMediaSources.razor
  42. 17
      ErsatzTV/Pages/PlexPathReplacementsEditor.razor
  43. 17
      ErsatzTV/Pages/ScheduleEditor.razor
  44. 29
      ErsatzTV/Pages/ScheduleItemsEditor.razor
  45. 24
      ErsatzTV/Pages/Schedules.razor
  46. 32
      ErsatzTV/Pages/Search.razor
  47. 34
      ErsatzTV/Pages/Settings.razor
  48. 4
      ErsatzTV/Pages/SongList.razor
  49. 19
      ErsatzTV/Pages/TelevisionEpisodeList.razor
  50. 19
      ErsatzTV/Pages/TelevisionSeasonList.razor
  51. 4
      ErsatzTV/Pages/TelevisionSeasonSearchResults.razor
  52. 4
      ErsatzTV/Pages/TelevisionShowList.razor
  53. 22
      ErsatzTV/Pages/TraktLists.razor
  54. 20
      ErsatzTV/Pages/Trash.razor
  55. 22
      ErsatzTV/Pages/WatermarkEditor.razor
  56. 14
      ErsatzTV/Pages/Watermarks.razor
  57. 13
      ErsatzTV/Shared/AddToCollectionDialog.razor
  58. 11
      ErsatzTV/Shared/AddToScheduleDialog.razor
  59. 11
      ErsatzTV/Shared/CopyFFmpegProfileDialog.razor
  60. 11
      ErsatzTV/Shared/CopyWatermarkDialog.razor
  61. 13
      ErsatzTV/Shared/MoveLocalLibraryPathDialog.razor
  62. 11
      ErsatzTV/Shared/RemoteMediaSourceLibrariesEditor.razor
  63. 11
      ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor
  64. 13
      ErsatzTV/Shared/RemoteMediaSources.razor
  65. 13
      ErsatzTV/Shared/SaveAsSmartCollectionDialog.razor
  66. 11
      ErsatzTV/Shared/SchedulePlayoutRebuild.razor

7
ErsatzTV.Core/Interfaces/GitHub/IGitHubApiClient.cs

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Tasks;
using LanguageExt;
namespace ErsatzTV.Core.Interfaces.GitHub
{
public interface IGitHubApiClient
{
Task<Either<BaseError, string>> GetLatestReleaseNotes();
Task<Either<BaseError, string>> GetReleaseNotes(string tag);
Task<Either<BaseError, string>> GetLatestReleaseNotes(CancellationToken cancellationToken);
Task<Either<BaseError, string>> GetReleaseNotes(string tag, CancellationToken cancellationToken);
}
}

9
ErsatzTV.Infrastructure/GitHub/GitHubApiClient.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core;
using ErsatzTV.Core.Interfaces.GitHub;
@ -9,12 +10,12 @@ namespace ErsatzTV.Infrastructure.GitHub @@ -9,12 +10,12 @@ namespace ErsatzTV.Infrastructure.GitHub
{
public class GitHubApiClient : IGitHubApiClient
{
public async Task<Either<BaseError, string>> GetLatestReleaseNotes()
public async Task<Either<BaseError, string>> GetLatestReleaseNotes(CancellationToken cancellationToken)
{
try
{
IGitHubApi service = RestService.For<IGitHubApi>("https://api.github.com");
return await service.GetReleases().Map(releases => releases.Head().Body);
return await service.GetReleases(cancellationToken).Map(releases => releases.Head().Body);
}
catch (Exception ex)
{
@ -22,12 +23,12 @@ namespace ErsatzTV.Infrastructure.GitHub @@ -22,12 +23,12 @@ namespace ErsatzTV.Infrastructure.GitHub
}
}
public async Task<Either<BaseError, string>> GetReleaseNotes(string tag)
public async Task<Either<BaseError, string>> GetReleaseNotes(string tag, CancellationToken cancellationToken)
{
try
{
IGitHubApi service = RestService.For<IGitHubApi>("https://api.github.com");
return await service.GetTag(tag).Map(t => t.Body);
return await service.GetTag(tag, cancellationToken).Map(t => t.Body);
}
catch (Exception ex)
{

5
ErsatzTV.Infrastructure/GitHub/IGitHubApi.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Infrastructure.GitHub.Models;
using Refit;
@ -9,9 +10,9 @@ namespace ErsatzTV.Infrastructure.GitHub @@ -9,9 +10,9 @@ namespace ErsatzTV.Infrastructure.GitHub
public interface IGitHubApi
{
[Get("/repos/jasongdove/ErsatzTV/releases")]
public Task<List<GitHubTag>> GetReleases();
public Task<List<GitHubTag>> GetReleases(CancellationToken cancellationToken);
[Get("/repos/jasongdove/ErsatzTV/releases/tags/{tag}")]
public Task<GitHubTag> GetTag(string tag);
public Task<GitHubTag> GetTag(string tag, CancellationToken cancellationToken);
}
}

68
ErsatzTV/Controllers/ArtworkController.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Application.Emby;
using ErsatzTV.Application.Emby.Queries;
@ -40,30 +41,30 @@ namespace ErsatzTV.Controllers @@ -40,30 +41,30 @@ namespace ErsatzTV.Controllers
[HttpHead("/iptv/artwork/posters/{fileName}.jpg")]
[HttpGet("/iptv/artwork/posters/{fileName}.jpg")]
[HttpGet("/artwork/posters/{fileName}")]
public async Task<IActionResult> GetPoster(string fileName)
public async Task<IActionResult> GetPoster(string fileName, CancellationToken cancellationToken)
{
Either<BaseError, CachedImagePathViewModel> cachedImagePath =
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.Poster, 440));
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.Poster, 440), cancellationToken);
return cachedImagePath.Match<IActionResult>(
Left: _ => new NotFoundResult(),
Right: r => new PhysicalFileResult(r.FileName, r.MimeType));
}
[HttpGet("/artwork/watermarks/{fileName}")]
public async Task<IActionResult> GetWatermark(string fileName)
public async Task<IActionResult> GetWatermark(string fileName, CancellationToken cancellationToken)
{
Either<BaseError, CachedImagePathViewModel> cachedImagePath =
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.Watermark));
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.Watermark), cancellationToken);
return cachedImagePath.Match<IActionResult>(
Left: _ => new NotFoundResult(),
Right: r => new PhysicalFileResult(r.FileName, r.MimeType));
}
[HttpGet("/artwork/fanart/{fileName}")]
public async Task<IActionResult> GetFanArt(string fileName)
public async Task<IActionResult> GetFanArt(string fileName, CancellationToken cancellationToken)
{
Either<BaseError, CachedImagePathViewModel> cachedImagePath =
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.FanArt));
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.FanArt), cancellationToken);
return cachedImagePath.Match<IActionResult>(
Left: _ => new NotFoundResult(),
Right: r => new PhysicalFileResult(r.FileName, r.MimeType));
@ -77,14 +78,14 @@ namespace ErsatzTV.Controllers @@ -77,14 +78,14 @@ namespace ErsatzTV.Controllers
[HttpGet("/iptv/artwork/thumbnails/jellyfin/{*path}")]
[HttpGet("/artwork/thumbnails/jellyfin/{*path}")]
[HttpGet("/artwork/fanart/jellyfin/{*path}")]
public Task<IActionResult> GetJellyfin(string path)
public Task<IActionResult> GetJellyfin(string path, CancellationToken cancellationToken)
{
if (Request.QueryString.HasValue)
{
path += Request.QueryString.Value;
}
return GetJellyfinArtwork(path);
return GetJellyfinArtwork(path, cancellationToken);
}
[HttpHead("/iptv/artwork/posters/emby/{*path}")]
@ -94,54 +95,54 @@ namespace ErsatzTV.Controllers @@ -94,54 +95,54 @@ namespace ErsatzTV.Controllers
[HttpGet("/iptv/artwork/thumbnails/emby/{*path}")]
[HttpGet("/artwork/thumbnails/emby/{*path}")]
[HttpGet("/artwork/fanart/emby/{*path}")]
public Task<IActionResult> GetEmby(string path)
public Task<IActionResult> GetEmby(string path, CancellationToken cancellationToken)
{
if (Request.QueryString.HasValue)
{
path += Request.QueryString.Value;
}
return GetEmbyArtwork(path);
return GetEmbyArtwork(path, cancellationToken);
}
[HttpHead("/iptv/artwork/posters/plex/{plexMediaSourceId}/{*path}")]
[HttpGet("/iptv/artwork/posters/plex/{plexMediaSourceId}/{*path}")]
[HttpGet("/artwork/posters/plex/{plexMediaSourceId}/{*path}")]
public Task<IActionResult> GetPlexPoster(int plexMediaSourceId, string path) =>
public Task<IActionResult> GetPlexPoster(int plexMediaSourceId, string path, CancellationToken cancellationToken) =>
GetPlexArtwork(
plexMediaSourceId,
$"photo/:/transcode?url=/{path}&height=440&width=304&minSize=1&upscale=0");
$"photo/:/transcode?url=/{path}&height=440&width=304&minSize=1&upscale=0",
cancellationToken);
[HttpGet("/artwork/fanart/plex/{plexMediaSourceId}/{*path}")]
public Task<IActionResult> GetPlexFanArt(int plexMediaSourceId, string path) =>
GetPlexArtwork(
plexMediaSourceId,
$"/{path}");
public Task<IActionResult> GetPlexFanArt(int plexMediaSourceId, string path, CancellationToken cancellationToken) =>
GetPlexArtwork(plexMediaSourceId, $"/{path}", cancellationToken);
[HttpGet("/artwork/thumbnails/plex/{plexMediaSourceId}/{*path}")]
public Task<IActionResult> GetPlexThumbnail(int plexMediaSourceId, string path) =>
public Task<IActionResult> GetPlexThumbnail(int plexMediaSourceId, string path, CancellationToken cancellationToken) =>
GetPlexArtwork(
plexMediaSourceId,
$"photo/:/transcode?url=/{path}&height=220&width=392&minSize=1&upscale=0");
$"photo/:/transcode?url=/{path}&height=220&width=392&minSize=1&upscale=0",
cancellationToken);
[HttpHead("/iptv/artwork/thumbnails/{fileName}")]
[HttpGet("/iptv/artwork/thumbnails/{fileName}")]
[HttpHead("/iptv/artwork/thumbnails/{fileName}.jpg")]
[HttpGet("/iptv/artwork/thumbnails/{fileName}.jpg")]
[HttpGet("/artwork/thumbnails/{fileName}")]
public async Task<IActionResult> GetThumbnail(string fileName)
public async Task<IActionResult> GetThumbnail(string fileName, CancellationToken cancellationToken)
{
Either<BaseError, CachedImagePathViewModel> cachedImagePath =
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.Thumbnail, 220));
await _mediator.Send(new GetCachedImagePath(fileName, ArtworkKind.Thumbnail, 220), cancellationToken);
return cachedImagePath.Match<IActionResult>(
Left: _ => new NotFoundResult(),
Right: r => new PhysicalFileResult(r.FileName, r.MimeType));
}
private async Task<IActionResult> GetPlexArtwork(int plexMediaSourceId, string transcodePath)
private async Task<IActionResult> GetPlexArtwork(int plexMediaSourceId, string transcodePath, CancellationToken cancellationToken)
{
Either<BaseError, PlexConnectionParametersViewModel> connectionParameters =
await _mediator.Send(new GetPlexConnectionParameters(plexMediaSourceId));
await _mediator.Send(new GetPlexConnectionParameters(plexMediaSourceId), cancellationToken);
return await connectionParameters.Match<Task<IActionResult>>(
Left: _ => new NotFoundResult().AsTask<IActionResult>(),
@ -153,8 +154,9 @@ namespace ErsatzTV.Controllers @@ -153,8 +154,9 @@ namespace ErsatzTV.Controllers
var fullPath = new Uri(r.Uri, transcodePath);
HttpResponseMessage response = await client.GetAsync(
fullPath,
HttpCompletionOption.ResponseHeadersRead);
Stream stream = await response.Content.ReadAsStreamAsync();
HttpCompletionOption.ResponseHeadersRead,
cancellationToken);
Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return new FileStreamResult(
stream,
@ -162,10 +164,10 @@ namespace ErsatzTV.Controllers @@ -162,10 +164,10 @@ namespace ErsatzTV.Controllers
});
}
private async Task<IActionResult> GetJellyfinArtwork(string path)
private async Task<IActionResult> GetJellyfinArtwork(string path, CancellationToken cancellationToken)
{
Either<BaseError, JellyfinConnectionParametersViewModel> connectionParameters =
await _mediator.Send(new GetJellyfinConnectionParameters());
await _mediator.Send(new GetJellyfinConnectionParameters(), cancellationToken);
return await connectionParameters.Match<Task<IActionResult>>(
Left: _ => new NotFoundResult().AsTask<IActionResult>(),
@ -176,8 +178,9 @@ namespace ErsatzTV.Controllers @@ -176,8 +178,9 @@ namespace ErsatzTV.Controllers
Url fullPath = JellyfinUrl.ForArtwork(vm.Address, path);
HttpResponseMessage response = await client.GetAsync(
fullPath,
HttpCompletionOption.ResponseHeadersRead);
Stream stream = await response.Content.ReadAsStreamAsync();
HttpCompletionOption.ResponseHeadersRead,
cancellationToken);
Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return new FileStreamResult(
stream,
@ -185,10 +188,10 @@ namespace ErsatzTV.Controllers @@ -185,10 +188,10 @@ namespace ErsatzTV.Controllers
});
}
private async Task<IActionResult> GetEmbyArtwork(string path)
private async Task<IActionResult> GetEmbyArtwork(string path, CancellationToken cancellationToken)
{
Either<BaseError, EmbyConnectionParametersViewModel> connectionParameters =
await _mediator.Send(new GetEmbyConnectionParameters());
await _mediator.Send(new GetEmbyConnectionParameters(), cancellationToken);
return await connectionParameters.Match<Task<IActionResult>>(
Left: _ => new NotFoundResult().AsTask<IActionResult>(),
@ -199,8 +202,9 @@ namespace ErsatzTV.Controllers @@ -199,8 +202,9 @@ namespace ErsatzTV.Controllers
Url fullPath = EmbyUrl.ForArtwork(vm.Address, path);
HttpResponseMessage response = await client.GetAsync(
fullPath,
HttpCompletionOption.ResponseHeadersRead);
Stream stream = await response.Content.ReadAsStreamAsync();
HttpCompletionOption.ResponseHeadersRead,
cancellationToken);
Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken);
return new FileStreamResult(
stream,

19
ErsatzTV/Pages/Artist.razor

@ -8,8 +8,10 @@ @@ -8,8 +8,10 @@
@using ErsatzTV.Application.ProgramSchedules
@using ErsatzTV.Application.ProgramSchedules.Commands
@using System.Globalization
@using System.Threading
@using ErsatzTV.Extensions
@using Unit = LanguageExt.Unit
@implements IDisposable
@inject IMediator _mediator
@inject IDialogService _dialog
@inject NavigationManager _navigationManager
@ -185,6 +187,7 @@ @@ -185,6 +187,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int ArtistId { get; set; }
@ -195,12 +198,18 @@ @@ -195,12 +198,18 @@
private List<string> _sortedStyles = new();
private List<string> _sortedMoods = new();
private MusicVideoCardResultsViewModel _musicVideos;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override Task OnParametersSetAsync() => RefreshData();
private async Task RefreshData()
{
await _mediator.Send(new GetArtistById(ArtistId)).IfSomeAsync(vm =>
await _mediator.Send(new GetArtistById(ArtistId), _cts.Token).IfSomeAsync(vm =>
{
_artist = vm;
_sortedLanguages = _artist.Languages.OrderBy(ci => ci.EnglishName).ToList();
@ -209,7 +218,7 @@ @@ -209,7 +218,7 @@
_sortedMoods = _artist.Moods.OrderBy(m => m).ToList();
});
_musicVideos = await _mediator.Send(new GetMusicVideoCards(ArtistId, 1, 100));
_musicVideos = await _mediator.Send(new GetMusicVideoCards(ArtistId, 1, 100), _cts.Token);
}
private async Task AddToCollection()
@ -221,7 +230,7 @@ @@ -221,7 +230,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
await _mediator.Send(new AddArtistToCollection(collection.Id, ArtistId));
await _mediator.Send(new AddArtistToCollection(collection.Id, ArtistId), _cts.Token);
_navigationManager.NavigateTo($"/media/collections/{collection.Id}");
}
}
@ -235,7 +244,7 @@ @@ -235,7 +244,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is ProgramScheduleViewModel schedule)
{
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.Artist, null, null, null, ArtistId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null));
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.Artist, null, null, null, ArtistId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null), _cts.Token);
_navigationManager.NavigateTo($"/schedules/{schedule.Id}/items");
}
}
@ -250,7 +259,7 @@ @@ -250,7 +259,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddMusicVideoToCollection(collection.Id, musicVideo.MusicVideoId);
Either<BaseError, Unit> addResult = await _mediator.Send(request);
Either<BaseError, Unit> addResult = await _mediator.Send(request, _cts.Token);
addResult.Match(
Left: error =>
{

4
ErsatzTV/Pages/ArtistList.razor

@ -97,7 +97,7 @@ @@ -97,7 +97,7 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:artist" : $"type:artist AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexArtists(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexArtists(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
@ -144,7 +144,7 @@ @@ -144,7 +144,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddArtistToCollection(collection.Id, artist.ArtistId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

41
ErsatzTV/Pages/ChannelEditor.razor

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
@using ErsatzTV.Application.Images.Commands
@using ErsatzTV.Application.MediaItems.Queries
@using System.Globalization
@using System.Threading
@using ErsatzTV.Application.Channels
@using ErsatzTV.Application.Channels.Queries
@using ErsatzTV.Application.Filler
@ -13,6 +14,7 @@ @@ -13,6 +14,7 @@
@using ErsatzTV.Application.Watermarks
@using ErsatzTV.Application.Watermarks.Queries
@using ErsatzTV.Core.Domain.Filler
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<ChannelEditor> _logger
@inject ISnackbar _snackbar
@ -107,6 +109,7 @@ @@ -107,6 +109,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int? Id { get; set; }
@ -119,17 +122,23 @@ @@ -119,17 +122,23 @@
private List<CultureInfo> _availableCultures;
private List<WatermarkViewModel> _watermarks;
private List<FillerPresetViewModel> _fillerPresets;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
await LoadFFmpegProfiles();
_availableCultures = await _mediator.Send(new GetAllLanguageCodes());
await LoadWatermarks();
await LoadFillerPresets();
await LoadFFmpegProfiles(_cts.Token);
_availableCultures = await _mediator.Send(new GetAllLanguageCodes(), _cts.Token);
await LoadWatermarks(_cts.Token);
await LoadFillerPresets(_cts.Token);
if (Id.HasValue)
{
Option<ChannelViewModel> maybeChannel = await _mediator.Send(new GetChannelById(Id.Value));
Option<ChannelViewModel> maybeChannel = await _mediator.Send(new GetChannelById(Id.Value), _cts.Token);
maybeChannel.Match(
channelViewModel =>
{
@ -149,10 +158,10 @@ @@ -149,10 +158,10 @@
}
else
{
FFmpegSettingsViewModel ffmpegSettings = await _mediator.Send(new GetFFmpegSettings());
FFmpegSettingsViewModel ffmpegSettings = await _mediator.Send(new GetFFmpegSettings(), _cts.Token);
// TODO: command for new channel
IEnumerable<int> channelNumbers = await _mediator.Send(new GetAllChannels())
IEnumerable<int> channelNumbers = await _mediator.Send(new GetAllChannels(), _cts.Token)
.Map(list => list.Map(c => int.TryParse(c.Number.Split(".").Head(), out int result) ? result : 0));
int maxNumber = Optional(channelNumbers).Flatten().DefaultIfEmpty(0).Max();
_model.Number = (maxNumber + 1).ToString();
@ -171,14 +180,14 @@ @@ -171,14 +180,14 @@
private bool IsEdit => Id.HasValue;
private async Task LoadFFmpegProfiles() =>
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles());
private async Task LoadFFmpegProfiles(CancellationToken cancellationToken) =>
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles(), cancellationToken);
private async Task LoadWatermarks() =>
_watermarks = await _mediator.Send(new GetAllWatermarks());
private async Task LoadWatermarks(CancellationToken cancellationToken) =>
_watermarks = await _mediator.Send(new GetAllWatermarks(), cancellationToken);
private async Task LoadFillerPresets() =>
_fillerPresets = await _mediator.Send(new GetAllFillerPresets())
private async Task LoadFillerPresets(CancellationToken cancellationToken) =>
_fillerPresets = await _mediator.Send(new GetAllFillerPresets(), cancellationToken)
.Map(list => list.Filter(vm => vm.FillerKind == FillerKind.Fallback).ToList());
private async Task HandleSubmitAsync()
@ -187,8 +196,8 @@ @@ -187,8 +196,8 @@
if (_editContext.Validate())
{
Seq<BaseError> errorMessage = IsEdit ?
(await _mediator.Send(_model.ToUpdate())).LeftToSeq() :
(await _mediator.Send(_model.ToCreate())).LeftToSeq();
(await _mediator.Send(_model.ToUpdate(), _cts.Token)).LeftToSeq() :
(await _mediator.Send(_model.ToCreate(), _cts.Token)).LeftToSeq();
errorMessage.HeadOrNone().Match(
error =>
@ -205,7 +214,7 @@ @@ -205,7 +214,7 @@
try
{
Either<BaseError, string> maybeCacheFileName =
await _mediator.Send(new SaveArtworkToDisk(e.File.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024), ArtworkKind.Logo));
await _mediator.Send(new SaveArtworkToDisk(e.File.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024), ArtworkKind.Logo), _cts.Token);
maybeCacheFileName.Match(
relativeFileName =>
{

20
ErsatzTV/Pages/Channels.razor

@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
@using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Application.FFmpegProfiles.Queries
@using System.Globalization
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@ -82,15 +84,23 @@ @@ -82,15 +84,23 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private MudTable<ChannelViewModel> _table;
private List<FFmpegProfileViewModel> _ffmpegProfiles;
private int _rowsPerPage;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles());
_rowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.ChannelsPageSize))
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles(), _cts.Token);
_rowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.ChannelsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
}
@ -103,16 +113,16 @@ @@ -103,16 +113,16 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteChannel(channel.Id));
await _mediator.Send(new DeleteChannel(channel.Id), _cts.Token);
await _table.ReloadServerData();
}
}
private async Task<TableData<ChannelViewModel>> ServerReload(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.ChannelsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.ChannelsPageSize, state.PageSize.ToString()), _cts.Token);
List<ChannelViewModel> channels = await _mediator.Send(new GetAllChannels());
List<ChannelViewModel> channels = await _mediator.Send(new GetAllChannels(), _cts.Token);
IOrderedEnumerable<ChannelViewModel> sorted = channels.OrderBy(c => decimal.Parse(c.Number));
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);

17
ErsatzTV/Pages/CollectionEditor.razor

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Application.MediaCollections.Queries
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<CollectionEditor> _logger
@inject ISnackbar _snackbar
@ -29,19 +31,26 @@ @@ -29,19 +31,26 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
private readonly CollectionEditViewModel _model = new();
private EditContext _editContext;
private ValidationMessageStore _messageStore;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
if (IsEdit)
{
Option<MediaCollectionViewModel> maybeCollection = await _mediator.Send(new GetCollectionById(Id));
Option<MediaCollectionViewModel> maybeCollection = await _mediator.Send(new GetCollectionById(Id), _cts.Token);
maybeCollection.IfSome(collection =>
{
_model.Id = collection.Id;
@ -68,8 +77,8 @@ @@ -68,8 +77,8 @@
if (_editContext.Validate())
{
Seq<BaseError> errorMessage = IsEdit ?
(await _mediator.Send(new UpdateCollection(Id, _model.Name))).LeftToSeq() :
(await _mediator.Send(new CreateCollection(_model.Name))).LeftToSeq();
(await _mediator.Send(new UpdateCollection(Id, _model.Name), _cts.Token)).LeftToSeq() :
(await _mediator.Send(new CreateCollection(_model.Name), _cts.Token)).LeftToSeq();
errorMessage.HeadOrNone().Match(
error =>

6
ErsatzTV/Pages/CollectionItems.razor

@ -294,7 +294,7 @@ @@ -294,7 +294,7 @@
protected override async Task RefreshData()
{
Either<BaseError, CollectionCardResultsViewModel> maybeResult =
await Mediator.Send(new GetCollectionCards(Id));
await Mediator.Send(new GetCollectionCards(Id), CancellationToken);
maybeResult.Match(
result => _data = result,
@ -459,7 +459,7 @@ @@ -459,7 +459,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await Mediator.Send(request);
await Mediator.Send(request, CancellationToken);
await RefreshData();
}
}
@ -468,7 +468,7 @@ @@ -468,7 +468,7 @@
{
_data.UseCustomPlaybackOrder = !_data.UseCustomPlaybackOrder;
var request = new UpdateCollection(Id, _data.Name) { UseCustomPlaybackOrder = _data.UseCustomPlaybackOrder };
await Mediator.Send(request);
await Mediator.Send(request, CancellationToken);
}
}

34
ErsatzTV/Pages/Collections.razor

@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
@using ErsatzTV.Application.Configuration.Queries
@using ErsatzTV.Application.Configuration.Commands
@using ErsatzTV.Extensions
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@ -134,6 +136,8 @@ @@ -134,6 +136,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private MudTable<MediaCollectionViewModel> _collectionsTable;
private MudTable<MultiCollectionViewModel> _multiCollectionsTable;
private MudTable<SmartCollectionViewModel> _smartCollectionsTable;
@ -141,16 +145,22 @@ @@ -141,16 +145,22 @@
private int _collectionsRowsPerPage;
private int _multiCollectionsRowsPerPage;
private int _smartCollectionsRowsPerPage;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_collectionsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.CollectionsPageSize))
_collectionsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.CollectionsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
_multiCollectionsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.MultiCollectionsPageSize))
_multiCollectionsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.MultiCollectionsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
_smartCollectionsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.SmartCollectionsPageSize))
_smartCollectionsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.SmartCollectionsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
}
@ -163,7 +173,7 @@ @@ -163,7 +173,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteCollection(collection.Id));
await _mediator.Send(new DeleteCollection(collection.Id), _cts.Token);
await _collectionsTable.ReloadServerData();
}
}
@ -177,7 +187,7 @@ @@ -177,7 +187,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteMultiCollection(collection.Id));
await _mediator.Send(new DeleteMultiCollection(collection.Id), _cts.Token);
await _multiCollectionsTable.ReloadServerData();
}
}
@ -191,32 +201,32 @@ @@ -191,32 +201,32 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteSmartCollection(collection.Id));
await _mediator.Send(new DeleteSmartCollection(collection.Id), _cts.Token);
await _smartCollectionsTable.ReloadServerData();
}
}
private async Task<TableData<MediaCollectionViewModel>> ServerReloadCollections(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.CollectionsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.CollectionsPageSize, state.PageSize.ToString()), _cts.Token);
PagedMediaCollectionsViewModel data = await _mediator.Send(new GetPagedCollections(state.Page, state.PageSize));
PagedMediaCollectionsViewModel data = await _mediator.Send(new GetPagedCollections(state.Page, state.PageSize), _cts.Token);
return new TableData<MediaCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private async Task<TableData<MultiCollectionViewModel>> ServerReloadMultiCollections(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.MultiCollectionsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.MultiCollectionsPageSize, state.PageSize.ToString()), _cts.Token);
PagedMultiCollectionsViewModel data = await _mediator.Send(new GetPagedMultiCollections(state.Page, state.PageSize));
PagedMultiCollectionsViewModel data = await _mediator.Send(new GetPagedMultiCollections(state.Page, state.PageSize), _cts.Token);
return new TableData<MultiCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
private async Task<TableData<SmartCollectionViewModel>> ServerReloadSmartCollections(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SmartCollectionsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SmartCollectionsPageSize, state.PageSize.ToString()), _cts.Token);
PagedSmartCollectionsViewModel data = await _mediator.Send(new GetPagedSmartCollections(state.Page, state.PageSize));
PagedSmartCollectionsViewModel data = await _mediator.Send(new GetPagedSmartCollections(state.Page, state.PageSize), _cts.Token);
return new TableData<SmartCollectionViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}

11
ErsatzTV/Pages/EmbyLibrariesEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Emby.Queries
@using ErsatzTV.Application.MediaSources
@using ErsatzTV.Application.Emby
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ChannelWriter<IEmbyBackgroundServiceRequest> _channel
@ -16,10 +18,17 @@ @@ -16,10 +18,17 @@
SynchronizeLibraryByIdIfNeeded="SynchronizeLibraryByIdIfNeeded"/>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private IRequest<Either<BaseError, Unit>> GetUpdateLibraryRequest(List<RemoteMediaSourceLibraryEditViewModel> libraries) =>
new UpdateEmbyLibraryPreferences(
libraries.Map(l => new EmbyLibraryPreference(l.Id, l.ShouldSyncItems)).ToList());
@ -42,7 +51,7 @@ @@ -42,7 +51,7 @@
private async Task<Unit> SynchronizeLibraryByIdIfNeeded(int libraryId)
{
await _channel.WriteAsync(new SynchronizeEmbyLibraryByIdIfNeeded(libraryId));
await _channel.WriteAsync(new SynchronizeEmbyLibraryByIdIfNeeded(libraryId), _cts.Token);
return Unit.Default;
}

13
ErsatzTV/Pages/EmbyMediaSourceEditor.razor

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
@using ErsatzTV.Core.Emby
@using ErsatzTV.Application.Emby.Queries
@using ErsatzTV.Application.Emby.Commands
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject NavigationManager _navigationManager
@inject ISnackbar _snackbar
@ -14,10 +16,17 @@ @@ -14,10 +16,17 @@
SaveSecrets="SaveSecrets"/>
@code {
private readonly CancellationTokenSource _cts = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private async Task<Unit> LoadSecrets(RemoteMediaSourceEditViewModel viewModel)
{
EmbySecrets secrets = await _mediator.Send(new GetEmbySecrets());
EmbySecrets secrets = await _mediator.Send(new GetEmbySecrets(), _cts.Token);
viewModel.Address = secrets.Address;
viewModel.ApiKey = secrets.ApiKey;
return Unit.Default;
@ -26,7 +35,7 @@ @@ -26,7 +35,7 @@
private async Task<Either<BaseError, Unit>> SaveSecrets(RemoteMediaSourceEditViewModel viewModel)
{
var secrets = new EmbySecrets { Address = viewModel.Address, ApiKey = viewModel.ApiKey };
return await _mediator.Send(new SaveEmbySecrets(secrets));
return await _mediator.Send(new SaveEmbySecrets(secrets), _cts.Token);
}
}

11
ErsatzTV/Pages/EmbyMediaSources.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@using ErsatzTV.Core.Interfaces.Emby
@using ErsatzTV.Application.Emby.Commands
@using ErsatzTV.Application.Emby.Queries
@using System.Threading
@implements IDisposable
@inject IEmbySecretStore _embySecretStore
@inject ChannelWriter<IEmbyBackgroundServiceRequest> _channel
@ -16,8 +18,15 @@ @@ -16,8 +18,15 @@
SecretStore="@_embySecretStore"/>
@code {
private readonly CancellationTokenSource _cts = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private async Task RefreshLibraries(int mediaSourceId) =>
await _channel.WriteAsync(new SynchronizeEmbyLibraries(mediaSourceId));
await _channel.WriteAsync(new SynchronizeEmbyLibraries(mediaSourceId), _cts.Token);
}

13
ErsatzTV/Pages/EmbyPathReplacementsEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Emby
@using ErsatzTV.Application.Emby.Commands
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<ScheduleItemsEditor> _logger
@inject ISnackbar _snackbar
@ -17,16 +19,23 @@ @@ -17,16 +19,23 @@
GetPathReplacementsBySourceId="GetPathReplacementsBySourceId"/>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int id) =>
_mediator.Send(new GetEmbyMediaSourceById(Id))
_mediator.Send(new GetEmbyMediaSourceById(Id), _cts.Token)
.MapT(vm => new RemoteMediaSourceViewModel(vm.Id, vm.Name, vm.Address));
private Task<List<RemoteMediaSourcePathReplacementEditViewModel>> GetPathReplacementsBySourceId(int mediaSourceId) =>
_mediator.Send(new GetEmbyPathReplacementsBySourceId(Id))
_mediator.Send(new GetEmbyPathReplacementsBySourceId(Id), _cts.Token)
.Map(list => list.Map(ProjectToEditViewModel).ToList());
private RemoteMediaSourcePathReplacementEditViewModel ProjectToEditViewModel(EmbyPathReplacementViewModel item) =>

4
ErsatzTV/Pages/EpisodeList.razor

@ -97,7 +97,7 @@ @@ -97,7 +97,7 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:episode" : $"type:episode AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexEpisodes(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexEpisodes(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
@ -144,7 +144,7 @@ @@ -144,7 +144,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddEpisodeToCollection(collection.Id, episode.EpisodeId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

20
ErsatzTV/Pages/FFmpeg.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Application.FFmpegProfiles.Commands
@using ErsatzTV.Application.FFmpegProfiles.Queries
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@inject ILogger<FFmpeg> _logger
@ -78,12 +80,20 @@ @@ -78,12 +80,20 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private List<FFmpegProfileViewModel> _ffmpegProfiles;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync() => await LoadFFmpegProfilesAsync();
protected override async Task OnParametersSetAsync() => await LoadFFmpegProfilesAsync(_cts.Token);
private async Task LoadFFmpegProfilesAsync() =>
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles());
private async Task LoadFFmpegProfilesAsync(CancellationToken cancellationToken) =>
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles(), cancellationToken);
private async Task DeleteProfileAsync(FFmpegProfileViewModel ffmpegProfile)
{
@ -94,8 +104,8 @@ @@ -94,8 +104,8 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteFFmpegProfile(ffmpegProfile.Id));
await LoadFFmpegProfilesAsync();
await _mediator.Send(new DeleteFFmpegProfile(ffmpegProfile.Id), _cts.Token);
await LoadFFmpegProfilesAsync(_cts.Token);
}
}

19
ErsatzTV/Pages/FFmpegEditor.razor

@ -8,6 +8,8 @@ @@ -8,6 +8,8 @@
@using ErsatzTV.Core.FFmpeg
@using Microsoft.AspNetCore.Components
@using Microsoft.Extensions.Caching.Memory
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<FFmpegEditor> _logger
@inject ISnackbar _snackbar
@ -121,6 +123,7 @@ @@ -121,6 +123,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -132,20 +135,26 @@ @@ -132,20 +135,26 @@
private List<ResolutionViewModel> _resolutions;
private List<string> _vaapiDevices;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_resolutions = await _mediator.Send(new GetAllResolutions());
_resolutions = await _mediator.Send(new GetAllResolutions(), _cts.Token);
if (IsEdit)
{
Option<FFmpegProfileViewModel> profile = await _mediator.Send(new GetFFmpegProfileById(Id));
Option<FFmpegProfileViewModel> profile = await _mediator.Send(new GetFFmpegProfileById(Id), _cts.Token);
profile.Match(
ffmpegProfileViewModel => _model = new FFmpegProfileEditViewModel(ffmpegProfileViewModel),
() => _navigationManager.NavigateTo("404"));
}
else
{
_model = new FFmpegProfileEditViewModel(await _mediator.Send(new NewFFmpegProfile()));
_model = new FFmpegProfileEditViewModel(await _mediator.Send(new NewFFmpegProfile(), _cts.Token));
}
_editContext = new EditContext(_model);
@ -167,8 +176,8 @@ @@ -167,8 +176,8 @@
if (_editContext.Validate())
{
Seq<BaseError> errorMessage = IsEdit ?
(await _mediator.Send(_model.ToUpdate())).LeftToSeq() :
(await _mediator.Send(_model.ToCreate())).LeftToSeq();
(await _mediator.Send(_model.ToUpdate(), _cts.Token)).LeftToSeq() :
(await _mediator.Send(_model.ToCreate(), _cts.Token)).LeftToSeq();
errorMessage.HeadOrNone().Match(
error =>

26
ErsatzTV/Pages/FillerPresetEditor.razor

@ -9,6 +9,8 @@ @@ -9,6 +9,8 @@
@using ErsatzTV.Application.MediaItems
@using ErsatzTV.Core.Domain.Filler
@using Microsoft.AspNetCore.Components
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<FillerPresetEditor> _logger
@inject ISnackbar _snackbar
@ -134,6 +136,8 @@ @@ -134,6 +136,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -148,24 +152,30 @@ @@ -148,24 +152,30 @@
private List<NamedMediaItemViewModel> _televisionSeasons;
private List<NamedMediaItemViewModel> _artists;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_mediaCollections = await _mediator.Send(new GetAllCollections())
_mediaCollections = await _mediator.Send(new GetAllCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_multiCollections = await _mediator.Send(new GetAllMultiCollections())
_multiCollections = await _mediator.Send(new GetAllMultiCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_smartCollections = await _mediator.Send(new GetAllSmartCollections())
_smartCollections = await _mediator.Send(new GetAllSmartCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_televisionShows = await _mediator.Send(new GetAllTelevisionShows())
_televisionShows = await _mediator.Send(new GetAllTelevisionShows(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_televisionSeasons = await _mediator.Send(new GetAllTelevisionSeasons())
_televisionSeasons = await _mediator.Send(new GetAllTelevisionSeasons(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_artists = await _mediator.Send(new GetAllArtists())
_artists = await _mediator.Send(new GetAllArtists(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
if (IsEdit)
{
Option<FillerPresetViewModel> maybeFillerPreset = await _mediator.Send(new GetFillerPresetById(Id));
Option<FillerPresetViewModel> maybeFillerPreset = await _mediator.Send(new GetFillerPresetById(Id), _cts.Token);
maybeFillerPreset.IfSome(fillerPreset =>
{
_model.Id = fillerPreset.Id;
@ -212,7 +222,7 @@ @@ -212,7 +222,7 @@
{
IRequest<Either<BaseError, LanguageExt.Unit>> request = IsEdit ? _model.ToEdit() : _model.ToUpdate();
Seq<BaseError> errorMessage = await _mediator.Send(request)
Seq<BaseError> errorMessage = await _mediator.Send(request, _cts.Token)
.Map(result => result.LeftToSeq());
errorMessage.HeadOrNone().Match(

19
ErsatzTV/Pages/FillerPresets.razor

@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
@using ErsatzTV.Application.Filler.Commands
@using ErsatzTV.Application.Filler.Queries
@using ErsatzTV.Core.Domain.Filler
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@ -69,13 +71,21 @@ @@ -69,13 +71,21 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private MudTable<FillerPresetViewModel> _fillerPresetsTable;
private int _fillerPresetsRowsPerPage;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_fillerPresetsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.FillerPresetsPageSize))
_fillerPresetsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.FillerPresetsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
}
@ -88,16 +98,17 @@ @@ -88,16 +98,17 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteFillerPreset(fillerPreset.Id));
await _mediator.Send(new DeleteFillerPreset(fillerPreset.Id), _cts.Token);
await _fillerPresetsTable.ReloadServerData();
}
}
private async Task<TableData<FillerPresetViewModel>> ServerReloadFillerPresets(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.FillerPresetsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.FillerPresetsPageSize, state.PageSize.ToString()), _cts.Token);
PagedFillerPresetsViewModel data = await _mediator.Send(new GetPagedFillerPresets(state.Page, state.PageSize));
PagedFillerPresetsViewModel data = await _mediator.Send(new GetPagedFillerPresets(state.Page, state.PageSize), _cts.Token);
return new TableData<FillerPresetViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
}

13
ErsatzTV/Pages/FragmentNavigationBase.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Extensions;
using Microsoft.AspNetCore.Components;
@ -10,13 +11,23 @@ namespace ErsatzTV.Pages @@ -10,13 +11,23 @@ namespace ErsatzTV.Pages
{
public class FragmentNavigationBase : ComponentBase, IDisposable
{
private readonly CancellationTokenSource _cts = new();
protected CancellationToken CancellationToken => _cts.Token;
[Inject]
private NavigationManager NavManager { get; set; }
[Inject]
private IJSRuntime JsRuntime { get; set; }
public void Dispose() => NavManager.LocationChanged -= TryFragmentNavigation;
public void Dispose()
{
NavManager.LocationChanged -= TryFragmentNavigation;
_cts?.Cancel();
_cts?.Dispose();
}
protected override void OnInitialized() => NavManager.LocationChanged += TryFragmentNavigation;

15
ErsatzTV/Pages/Index.razor

@ -1,9 +1,11 @@ @@ -1,9 +1,11 @@
@page "/"
@using Microsoft.Extensions.Caching.Memory
@using System.Reflection
@using System.Threading
@using ErsatzTV.Application.Health.Queries
@using ErsatzTV.Core.Health
@using ErsatzTV.Core.Interfaces.GitHub
@implements IDisposable
@inject IGitHubApiClient _gitHubApiClient
@inject IMemoryCache _memoryCache
@inject IMediator _mediator
@ -71,9 +73,16 @@ @@ -71,9 +73,16 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private string _releaseNotes;
private MudTable<HealthCheckResult> _table;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
@ -101,12 +110,12 @@ @@ -101,12 +110,12 @@
gitHubVersion = $"v{gitHubVersion}";
}
maybeNotes = await _gitHubApiClient.GetReleaseNotes(gitHubVersion);
maybeNotes = await _gitHubApiClient.GetReleaseNotes(gitHubVersion, _cts.Token);
maybeNotes.IfRight(notes => _releaseNotes = notes);
}
else
{
maybeNotes = await _gitHubApiClient.GetLatestReleaseNotes();
maybeNotes = await _gitHubApiClient.GetLatestReleaseNotes(_cts.Token);
maybeNotes.IfRight(notes => _releaseNotes = notes);
}
}
@ -126,7 +135,7 @@ @@ -126,7 +135,7 @@
private async Task<TableData<HealthCheckResult>> ServerReload(TableState state)
{
List<HealthCheckResult> healthCheckResults = await _mediator.Send(new GetAllHealthCheckResults());
List<HealthCheckResult> healthCheckResults = await _mediator.Send(new GetAllHealthCheckResults(), _cts.Token);
return new TableData<HealthCheckResult>
{

19
ErsatzTV/Pages/JellyfinLibrariesEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Jellyfin.Queries
@using ErsatzTV.Application.MediaSources
@using ErsatzTV.Application.Jellyfin
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ChannelWriter<IJellyfinBackgroundServiceRequest> _channel
@ -16,20 +18,27 @@ @@ -16,20 +18,27 @@
SynchronizeLibraryByIdIfNeeded="SynchronizeLibraryByIdIfNeeded"/>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private IRequest<Either<BaseError, Unit>> GetUpdateLibraryRequest(List<RemoteMediaSourceLibraryEditViewModel> libraries) =>
new UpdateJellyfinLibraryPreferences(
libraries.Map(l => new JellyfinLibraryPreference(l.Id, l.ShouldSyncItems)).ToList());
private Task<List<RemoteMediaSourceLibraryEditViewModel>> GetLibrariesBySourceId(int mediaSourceId) =>
_mediator.Send(new GetJellyfinLibrariesBySourceId(Id))
private async Task<List<RemoteMediaSourceLibraryEditViewModel>> GetLibrariesBySourceId(int mediaSourceId) =>
await _mediator.Send(new GetJellyfinLibrariesBySourceId(Id), _cts.Token)
.Map(list => list.Map(ProjectToEditViewModel).OrderBy(x => x.MediaKind).ThenBy(x => x.Name).ToList());
private Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int mediaSourceId) =>
_mediator.Send(new GetJellyfinMediaSourceById(Id))
private async Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int mediaSourceId) =>
await _mediator.Send(new GetJellyfinMediaSourceById(Id), _cts.Token)
.MapT(vm => new RemoteMediaSourceViewModel(vm.Id, vm.Name, vm.Address));
private RemoteMediaSourceLibraryEditViewModel ProjectToEditViewModel(JellyfinLibraryViewModel library) => new()
@ -42,7 +51,7 @@ @@ -42,7 +51,7 @@
private async Task<Unit> SynchronizeLibraryByIdIfNeeded(int libraryId)
{
await _channel.WriteAsync(new SynchronizeJellyfinLibraryByIdIfNeeded(libraryId));
await _channel.WriteAsync(new SynchronizeJellyfinLibraryByIdIfNeeded(libraryId), _cts.Token);
return Unit.Default;
}

13
ErsatzTV/Pages/JellyfinMediaSourceEditor.razor

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
@using ErsatzTV.Core.Jellyfin
@using ErsatzTV.Application.Jellyfin.Queries
@using ErsatzTV.Application.Jellyfin.Commands
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject NavigationManager _navigationManager
@inject ISnackbar _snackbar
@ -14,10 +16,17 @@ @@ -14,10 +16,17 @@
SaveSecrets="SaveSecrets"/>
@code {
private readonly CancellationTokenSource _cts = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private async Task<Unit> LoadSecrets(RemoteMediaSourceEditViewModel viewModel)
{
JellyfinSecrets secrets = await _mediator.Send(new GetJellyfinSecrets());
JellyfinSecrets secrets = await _mediator.Send(new GetJellyfinSecrets(), _cts.Token);
viewModel.Address = secrets.Address;
viewModel.ApiKey = secrets.ApiKey;
return Unit.Default;
@ -26,7 +35,7 @@ @@ -26,7 +35,7 @@
private async Task<Either<BaseError, Unit>> SaveSecrets(RemoteMediaSourceEditViewModel viewModel)
{
var secrets = new JellyfinSecrets { Address = viewModel.Address, ApiKey = viewModel.ApiKey };
return await _mediator.Send(new SaveJellyfinSecrets(secrets));
return await _mediator.Send(new SaveJellyfinSecrets(secrets), _cts.Token);
}
}

11
ErsatzTV/Pages/JellyfinMediaSources.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@using ErsatzTV.Core.Interfaces.Jellyfin
@using ErsatzTV.Application.Jellyfin.Commands
@using ErsatzTV.Application.Jellyfin.Queries
@using System.Threading
@implements IDisposable
@inject IJellyfinSecretStore _jellyfinSecretStore
@inject ChannelWriter<IJellyfinBackgroundServiceRequest> _channel
@ -16,8 +18,15 @@ @@ -16,8 +18,15 @@
SecretStore="@_jellyfinSecretStore"/>
@code {
private readonly CancellationTokenSource _cts = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private async Task RefreshLibraries(int mediaSourceId) =>
await _channel.WriteAsync(new SynchronizeJellyfinLibraries(mediaSourceId));
await _channel.WriteAsync(new SynchronizeJellyfinLibraries(mediaSourceId), _cts.Token);
}

17
ErsatzTV/Pages/JellyfinPathReplacementsEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Jellyfin
@using ErsatzTV.Application.Jellyfin.Commands
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<ScheduleItemsEditor> _logger
@inject ISnackbar _snackbar
@ -17,16 +19,23 @@ @@ -17,16 +19,23 @@
GetPathReplacementsBySourceId="GetPathReplacementsBySourceId"/>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
private Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int id) =>
_mediator.Send(new GetJellyfinMediaSourceById(Id))
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private async Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int id) =>
await _mediator.Send(new GetJellyfinMediaSourceById(Id), _cts.Token)
.MapT(vm => new RemoteMediaSourceViewModel(vm.Id, vm.Name, vm.Address));
private Task<List<RemoteMediaSourcePathReplacementEditViewModel>> GetPathReplacementsBySourceId(int mediaSourceId) =>
_mediator.Send(new GetJellyfinPathReplacementsBySourceId(Id))
private async Task<List<RemoteMediaSourcePathReplacementEditViewModel>> GetPathReplacementsBySourceId(int mediaSourceId) =>
await _mediator.Send(new GetJellyfinPathReplacementsBySourceId(Id), _cts.Token)
.Map(list => list.Map(ProjectToEditViewModel).ToList());
private RemoteMediaSourcePathReplacementEditViewModel ProjectToEditViewModel(JellyfinPathReplacementViewModel item) =>

19
ErsatzTV/Pages/Libraries.razor

@ -78,6 +78,8 @@ @@ -78,6 +78,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private IList<LibraryViewModel> _libraries;
private Dictionary<int, int> _progressByLibrary;
@ -87,11 +89,11 @@ @@ -87,11 +89,11 @@
_courier.Subscribe<LibraryScanProgress>(HandleScanProgress);
}
protected override async Task OnParametersSetAsync() => await LoadLibraries();
protected override async Task OnParametersSetAsync() => await LoadLibraries(_cts.Token);
private async Task LoadLibraries()
private async Task LoadLibraries(CancellationToken cancellationToken)
{
_libraries = await _mediator.Send(new GetConfiguredLibraries());
_libraries = await _mediator.Send(new GetConfiguredLibraries(), cancellationToken);
_progressByLibrary = _libraries.ToDictionary(vm => vm.Id, _ => 0);
}
@ -102,16 +104,16 @@ @@ -102,16 +104,16 @@
switch (library)
{
case LocalLibraryViewModel:
await _workerChannel.WriteAsync(new ForceScanLocalLibrary(library.Id));
await _workerChannel.WriteAsync(new ForceScanLocalLibrary(library.Id), _cts.Token);
break;
case PlexLibraryViewModel:
await _plexWorkerChannel.WriteAsync(new ForceSynchronizePlexLibraryById(library.Id));
await _plexWorkerChannel.WriteAsync(new ForceSynchronizePlexLibraryById(library.Id), _cts.Token);
break;
case JellyfinLibraryViewModel:
await _jellyfinWorkerChannel.WriteAsync(new ForceSynchronizeJellyfinLibraryById(library.Id));
await _jellyfinWorkerChannel.WriteAsync(new ForceSynchronizeJellyfinLibraryById(library.Id), _cts.Token);
break;
case EmbyLibraryViewModel:
await _embyWorkerChannel.WriteAsync(new ForceSynchronizeEmbyLibraryById(library.Id));
await _embyWorkerChannel.WriteAsync(new ForceSynchronizeEmbyLibraryById(library.Id), _cts.Token);
break;
}
@ -142,6 +144,9 @@ @@ -142,6 +144,9 @@
{
_locker.OnLibraryChanged -= LockChanged;
_courier.UnSubscribe<LibraryScanProgress>(HandleScanProgress);
_cts.Cancel();
_cts.Dispose();
}
}

18
ErsatzTV/Pages/LocalLibraries.razor

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
@using ErsatzTV.Application.Libraries
@using ErsatzTV.Application.Libraries.Commands
@using ErsatzTV.Application.Libraries.Queries
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@ -53,6 +54,8 @@ @@ -53,6 +54,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private IList<LocalLibraryViewModel> _libraries;
protected override void OnInitialized()
@ -60,11 +63,11 @@ @@ -60,11 +63,11 @@
_locker.OnLibraryChanged += LockChanged;
}
protected override async Task OnParametersSetAsync() => await LoadLibraries();
protected override async Task OnParametersSetAsync() => await LoadLibraries(_cts.Token);
private async Task LoadLibraries()
private async Task LoadLibraries(CancellationToken cancellationToken)
{
_libraries = await _mediator.Send(new GetAllLocalLibraries());
_libraries = await _mediator.Send(new GetAllLocalLibraries(), cancellationToken);
}
private void LockChanged(object sender, EventArgs e) =>
@ -72,7 +75,7 @@ @@ -72,7 +75,7 @@
private async Task DeleteLibrary(LocalLibraryViewModel library)
{
int count = await _mediator.Send(new CountMediaItemsByLibrary(library.Id));
int count = await _mediator.Send(new CountMediaItemsByLibrary(library.Id), _cts.Token);
var parameters = new DialogParameters
{
{ "EntityType", "library" },
@ -86,14 +89,17 @@ @@ -86,14 +89,17 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteLocalLibrary(library.Id));
await LoadLibraries();
await _mediator.Send(new DeleteLocalLibrary(library.Id), _cts.Token);
await LoadLibraries(_cts.Token);
}
}
void IDisposable.Dispose()
{
_locker.OnLibraryChanged -= LockChanged;
_cts.Cancel();
_cts.Dispose();
}
}

17
ErsatzTV/Pages/LocalLibraryEditor.razor

@ -4,6 +4,7 @@ @@ -4,6 +4,7 @@
@using ErsatzTV.Application.Libraries.Commands
@using ErsatzTV.Application.Libraries.Queries
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IEntityLocker _locker
@ -76,6 +77,7 @@ @@ -76,6 +77,7 @@
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -91,7 +93,7 @@ @@ -91,7 +93,7 @@
{
if (IsEdit)
{
Option<LocalLibraryViewModel> maybeLibrary = await _mediator.Send(new GetLocalLibraryById(Id));
Option<LocalLibraryViewModel> maybeLibrary = await _mediator.Send(new GetLocalLibraryById(Id), _cts.Token);
await maybeLibrary.Match(
async library =>
{
@ -130,7 +132,7 @@ @@ -130,7 +132,7 @@
private async Task LoadLibraryPaths()
{
_model.HasChanges = false;
_model.Paths = await _mediator.Send(new GetLocalLibraryPaths(Id))
_model.Paths = await _mediator.Send(new GetLocalLibraryPaths(Id), _cts.Token)
.Map(list => list.Map(vm => new LocalLibraryPathEditViewModel
{
Id = vm.Id,
@ -148,7 +150,7 @@ @@ -148,7 +150,7 @@
if (!result.Cancelled && result.Data is LocalLibraryViewModel library)
{
var request = new MoveLocalLibraryPath(libraryPath.Id, library.Id);
Either<BaseError, Unit> moveResult = await _mediator.Send(request);
Either<BaseError, Unit> moveResult = await _mediator.Send(request, _cts.Token);
moveResult.Match(
_ => _navigationManager.NavigateTo($"/media/sources/local/{library.Id}/edit"),
error =>
@ -167,7 +169,7 @@ @@ -167,7 +169,7 @@
_model.Paths.Remove(libraryPath);
}
int count = await _mediator.Send(new CountMediaItemsByLibraryPath(libraryPath.Id));
int count = await _mediator.Send(new CountMediaItemsByLibraryPath(libraryPath.Id), _cts.Token);
var parameters = new DialogParameters
{
{ "EntityType", "library path" },
@ -209,11 +211,11 @@ @@ -209,11 +211,11 @@
? await _mediator.Send(new UpdateLocalLibrary(
_model.Id,
_model.Name,
_model.Paths.Map(p => new UpdateLocalLibraryPath(p.Id, p.Path)).ToList()))
_model.Paths.Map(p => new UpdateLocalLibraryPath(p.Id, p.Path)).ToList()), _cts.Token)
: await _mediator.Send(new CreateLocalLibrary(
_model.Name,
_model.MediaKind,
_model.Paths.Map(p => p.Path).ToList()));
_model.Paths.Map(p => p.Path).ToList()), _cts.Token);
result.Match(
_ => _navigationManager.NavigateTo("/media/sources/local"),
@ -235,6 +237,9 @@ @@ -235,6 +237,9 @@
void IDisposable.Dispose()
{
_locker.OnLibraryChanged -= LockChanged;
_cts.Cancel();
_cts.Dispose();
}
}

15
ErsatzTV/Pages/LocalLibraryPathEditor.razor

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
@using ErsatzTV.Application.Libraries.Commands
@using ErsatzTV.Application.Libraries.Queries
@using ErsatzTV.Application.MediaSources.Commands
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<LocalLibraryPathEditor> _logger
@inject ISnackbar _snackbar
@ -32,6 +34,7 @@ @@ -32,6 +34,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -41,10 +44,16 @@ @@ -41,10 +44,16 @@
private ValidationMessageStore _messageStore;
private LocalLibraryViewModel _library;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
Option<LocalLibraryViewModel> maybeLibrary = await _mediator.Send(new GetLocalLibraryById(Id));
Option<LocalLibraryViewModel> maybeLibrary = await _mediator.Send(new GetLocalLibraryById(Id), _cts.Token);
maybeLibrary.Match(
library => _library = library,
() => _navigationManager.NavigateTo("404"));
@ -62,7 +71,7 @@ @@ -62,7 +71,7 @@
if (_editContext.Validate())
{
var command = new CreateLocalLibraryPath(_library.Id, _model.Path);
Either<BaseError, LocalLibraryPathViewModel> result = await _mediator.Send(command);
Either<BaseError, LocalLibraryPathViewModel> result = await _mediator.Send(command, _cts.Token);
await result.Match(
Left: error =>
{
@ -74,7 +83,7 @@ @@ -74,7 +83,7 @@
{
if (_locker.LockLibrary(_library.Id))
{
await _channel.WriteAsync(new ScanLocalLibraryIfNeeded(_library.Id));
await _channel.WriteAsync(new ScanLocalLibraryIfNeeded(_library.Id), _cts.Token);
_navigationManager.NavigateTo("/media/libraries");
}
});

21
ErsatzTV/Pages/Logs.razor

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
@using ErsatzTV.Application.Logs.Queries
@using ErsatzTV.Application.Configuration.Queries
@using ErsatzTV.Application.Configuration.Commands
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="pt-8">
@ -36,15 +38,24 @@ @@ -36,15 +38,24 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private MudTable<LogEntryViewModel> _table;
private int _rowsPerPage;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync() => _rowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.LogsPageSize))
protected override async Task OnParametersSetAsync() => _rowsPerPage =
await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.LogsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
private async Task<TableData<LogEntryViewModel>> ServerReload(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.LogsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.LogsPageSize, state.PageSize.ToString()), _cts.Token);
PagedLogEntriesViewModel data;
@ -57,7 +68,7 @@ @@ -57,7 +68,7 @@
SortDescending = state.SortDirection == SortDirection.None
? Option<bool>.None
: state.SortDirection == SortDirection.Descending
});
}, _cts.Token);
break;
case "level":
data = await _mediator.Send(new GetRecentLogEntries(state.Page, state.PageSize)
@ -66,13 +77,13 @@ @@ -66,13 +77,13 @@
SortDescending = state.SortDirection == SortDirection.None
? Option<bool>.None
: state.SortDirection == SortDirection.Descending
});
}, _cts.Token);
break;
default:
data = await _mediator.Send(new GetRecentLogEntries(state.Page, state.PageSize)
{
SortDescending = Option<bool>.None
});
}, _cts.Token);
break;
}

15
ErsatzTV/Pages/Movie.razor

@ -6,6 +6,8 @@ @@ -6,6 +6,8 @@
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Extensions
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject IDialogService _dialog
@inject NavigationManager _navigationManager
@ -187,6 +189,7 @@ @@ -187,6 +189,7 @@
}
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int MovieId { get; set; }
@ -200,10 +203,16 @@ @@ -200,10 +203,16 @@
private List<string> _sortedGenres = new();
private List<string> _sortedTags = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override Task OnParametersSetAsync() => RefreshData();
private Task RefreshData() =>
_mediator.Send(new GetMovieById(MovieId)).IfSomeAsync(vm =>
private async Task RefreshData() =>
await _mediator.Send(new GetMovieById(MovieId), _cts.Token).IfSomeAsync(vm =>
{
_movie = vm;
_sortedContentRatings = _movie.ContentRatings.OrderBy(cr => cr).ToList();
@ -224,7 +233,7 @@ @@ -224,7 +233,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
await _mediator.Send(new AddMovieToCollection(collection.Id, MovieId));
await _mediator.Send(new AddMovieToCollection(collection.Id, MovieId), _cts.Token);
_navigationManager.NavigateTo($"/media/collections/{collection.Id}");
}
}

11
ErsatzTV/Pages/MovieList.razor

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
@page "/media/movies"
@page "/media/movies/page/{PageNumber:int}"
@using LanguageExt.UnsafeValueAccess
@using Microsoft.AspNetCore.WebUtilities
@using ErsatzTV.Application.MediaCards
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@ -81,7 +80,7 @@ @@ -81,7 +80,7 @@
private MovieCardResultsViewModel _data;
private string _query;
protected override Task OnParametersSetAsync()
{
if (PageNumber == 0)
@ -96,12 +95,12 @@ @@ -96,12 +95,12 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:movie" : $"type:movie AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexMovies(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexMovies(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
{
var uri = $"/media/movies/page/{PageNumber - 1}";
string uri = $"/media/movies/page/{PageNumber - 1}";
if (!string.IsNullOrWhiteSpace(_query))
{
(string key, string value) = _query.EncodeQuery();
@ -112,7 +111,7 @@ @@ -112,7 +111,7 @@
private void NextPage()
{
var uri = $"/media/movies/page/{PageNumber + 1}";
string uri = $"/media/movies/page/{PageNumber + 1}";
if (!string.IsNullOrWhiteSpace(_query))
{
(string key, string value) = _query.EncodeQuery();
@ -143,7 +142,7 @@ @@ -143,7 +142,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddMovieToCollection(collection.Id, movie.MovieId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

20
ErsatzTV/Pages/MultiCollectionEditor.razor

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Application.MediaCollections.Queries
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject NavigationManager _navigationManager
@inject ISnackbar _snackbar
@ -104,6 +106,8 @@ @@ -104,6 +106,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -117,17 +121,23 @@ @@ -117,17 +121,23 @@
private MudSelect<MediaCollectionViewModel> _collectionSelect;
private MudSelect<SmartCollectionViewModel> _smartCollectionSelect;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_collections = await _mediator.Send(new GetAllCollections())
_collections = await _mediator.Send(new GetAllCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_smartCollections = await _mediator.Send(new GetAllSmartCollections())
_smartCollections = await _mediator.Send(new GetAllSmartCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
if (IsEdit)
{
Option<MultiCollectionViewModel> maybeCollection = await _mediator.Send(new GetMultiCollectionById(Id));
Option<MultiCollectionViewModel> maybeCollection = await _mediator.Send(new GetMultiCollectionById(Id), _cts.Token);
maybeCollection.IfSome(collection =>
{
_model.Id = collection.Id;
@ -170,8 +180,8 @@ @@ -170,8 +180,8 @@
if (_editContext.Validate())
{
Seq<BaseError> errorMessage = IsEdit ?
(await _mediator.Send(new UpdateMultiCollection(Id, _model.Name, GetUpdateItems()))).LeftToSeq() :
(await _mediator.Send(new CreateMultiCollection(_model.Name, GetCreateItems()))).LeftToSeq();
(await _mediator.Send(new UpdateMultiCollection(Id, _model.Name, GetUpdateItems()), _cts.Token)).LeftToSeq() :
(await _mediator.Send(new CreateMultiCollection(_model.Name, GetCreateItems()), _cts.Token)).LeftToSeq();
errorMessage.HeadOrNone().Match(
error =>

6
ErsatzTV/Pages/MultiSelectBase.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Application.MediaCards;
using ErsatzTV.Application.MediaCollections;
@ -137,7 +138,7 @@ namespace ErsatzTV.Pages @@ -137,7 +138,7 @@ namespace ErsatzTV.Pages
otherVideoIds,
songIds);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{
@ -173,7 +174,8 @@ namespace ErsatzTV.Pages @@ -173,7 +174,8 @@ namespace ErsatzTV.Pages
new RemoveItemsFromCollection(collectionId)
{
MediaItemIds = itemIds
});
},
CancellationToken);
await RefreshData();
ClearSelection();

4
ErsatzTV/Pages/MusicVideoList.razor

@ -97,7 +97,7 @@ @@ -97,7 +97,7 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:music_video" : $"type:music_video AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexMusicVideos(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexMusicVideos(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
@ -144,7 +144,7 @@ @@ -144,7 +144,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddMusicVideoToCollection(collection.Id, musicVideo.MusicVideoId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

4
ErsatzTV/Pages/OtherVideoList.razor

@ -96,7 +96,7 @@ @@ -96,7 +96,7 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:other_video" : $"type:other_video AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexOtherVideos(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexOtherVideos(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
@ -143,7 +143,7 @@ @@ -143,7 +143,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddOtherVideoToCollection(collection.Id, otherVideo.OtherVideoId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

15
ErsatzTV/Pages/PlayoutEditor.razor

@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
@using ErsatzTV.Application.Channels.Queries
@using ErsatzTV.Application.ProgramSchedules
@using ErsatzTV.Application.ProgramSchedules.Queries
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<PlayoutEditor> _logger
@inject ISnackbar _snackbar
@ -48,6 +50,7 @@ @@ -48,6 +50,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private readonly PlayoutEditViewModel _model = new();
private List<ChannelViewModel> _channels;
@ -55,12 +58,18 @@ @@ -55,12 +58,18 @@
private EditContext _editContext;
private ValidationMessageStore _messageStore;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_channels = await _mediator.Send(new GetAllChannels())
_channels = await _mediator.Send(new GetAllChannels(), _cts.Token)
.Map(list => list.OrderBy(vm => decimal.Parse(vm.Number)).ToList());
_programSchedules = await _mediator.Send(new GetAllProgramSchedules())
_programSchedules = await _mediator.Send(new GetAllProgramSchedules(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name).ToList());
}
@ -75,7 +84,7 @@ @@ -75,7 +84,7 @@
_messageStore.Clear();
if (_editContext.Validate())
{
Seq<BaseError> errorMessage = (await _mediator.Send(_model.ToCreate())).LeftToSeq();
Seq<BaseError> errorMessage = (await _mediator.Send(_model.ToCreate(), _cts.Token)).LeftToSeq();
errorMessage.HeadOrNone().Match(
error =>

30
ErsatzTV/Pages/Playouts.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Playouts.Queries
@using ErsatzTV.Application.Configuration.Commands
@using ErsatzTV.Application.Configuration.Queries
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@ -99,6 +101,8 @@ @@ -99,6 +101,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private MudTable<PlayoutNameViewModel> _table;
private MudTable<PlayoutItemViewModel> _detailTable;
private int _rowsPerPage;
@ -118,14 +122,20 @@ @@ -118,14 +122,20 @@
}
}
}
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_rowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.PlayoutsPageSize))
_rowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.PlayoutsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
_detailRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.PlayoutsDetailPageSize))
_detailRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.PlayoutsDetailPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
_showFiller = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.PlayoutsDetailShowFiller))
_showFiller = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.PlayoutsDetailShowFiller), _cts.Token)
.Map(maybeShow => maybeShow.Match(ce => bool.TryParse(ce.Value, out bool show) && show, () => false));
}
@ -152,7 +162,7 @@ @@ -152,7 +162,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeletePlayout(playout.PlayoutId));
await _mediator.Send(new DeletePlayout(playout.PlayoutId), _cts.Token);
await _table.ReloadServerData();
if (_selectedPlayoutId == playout.PlayoutId)
{
@ -163,7 +173,7 @@ @@ -163,7 +173,7 @@
private async Task RebuildPlayout(PlayoutNameViewModel playout)
{
await _mediator.Send(new BuildPlayout(playout.PlayoutId, true));
await _mediator.Send(new BuildPlayout(playout.PlayoutId, true), _cts.Token);
await _table.ReloadServerData();
if (_selectedPlayoutId == playout.PlayoutId)
{
@ -188,9 +198,9 @@ @@ -188,9 +198,9 @@
private async Task<TableData<PlayoutNameViewModel>> ServerReload(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PlayoutsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PlayoutsPageSize, state.PageSize.ToString()), _cts.Token);
List<PlayoutNameViewModel> playouts = await _mediator.Send(new GetAllPlayouts());
List<PlayoutNameViewModel> playouts = await _mediator.Send(new GetAllPlayouts(), _cts.Token);
IOrderedEnumerable<PlayoutNameViewModel> sorted = playouts.OrderBy(p => decimal.Parse(p.ChannelNumber));
// TODO: properly page this data
@ -203,13 +213,13 @@ @@ -203,13 +213,13 @@
private async Task<TableData<PlayoutItemViewModel>> DetailServerReload(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PlayoutsDetailPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PlayoutsDetailShowFiller, _showFiller.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PlayoutsDetailPageSize, state.PageSize.ToString()), _cts.Token);
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.PlayoutsDetailShowFiller, _showFiller.ToString()), _cts.Token);
if (_selectedPlayoutId.HasValue)
{
PagedPlayoutItemsViewModel data =
await _mediator.Send(new GetFuturePlayoutItemsById(_selectedPlayoutId.Value, _showFiller, state.Page, state.PageSize));
await _mediator.Send(new GetFuturePlayoutItemsById(_selectedPlayoutId.Value, _showFiller, state.Page, state.PageSize), _cts.Token);
return new TableData<PlayoutItemViewModel>
{
TotalItems = data.TotalCount,

19
ErsatzTV/Pages/PlexLibrariesEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Plex.Queries
@using ErsatzTV.Application.MediaSources
@using ErsatzTV.Application.Plex
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ChannelWriter<IPlexBackgroundServiceRequest> _channel
@ -16,6 +18,7 @@ @@ -16,6 +18,7 @@
SynchronizeLibraryByIdIfNeeded="SynchronizeLibraryByIdIfNeeded"/>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -24,14 +27,20 @@ @@ -24,14 +27,20 @@
new UpdatePlexLibraryPreferences(
libraries.Map(l => new PlexLibraryPreference(l.Id, l.ShouldSyncItems)).ToList());
private Task<List<RemoteMediaSourceLibraryEditViewModel>> GetLibrariesBySourceId(int mediaSourceId) =>
_mediator.Send(new GetPlexLibrariesBySourceId(Id))
private async Task<List<RemoteMediaSourceLibraryEditViewModel>> GetLibrariesBySourceId(int mediaSourceId) =>
await _mediator.Send(new GetPlexLibrariesBySourceId(Id), _cts.Token)
.Map(list => list.Map(ProjectToEditViewModel).OrderBy(x => x.MediaKind).ThenBy(x => x.Name).ToList());
private Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int mediaSourceId) =>
_mediator.Send(new GetPlexMediaSourceById(Id))
private async Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int mediaSourceId) =>
await _mediator.Send(new GetPlexMediaSourceById(Id), _cts.Token)
.MapT(vm => new RemoteMediaSourceViewModel(vm.Id, vm.Name, vm.Address));
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private RemoteMediaSourceLibraryEditViewModel ProjectToEditViewModel(PlexLibraryViewModel library) => new()
{
Id = library.Id,
@ -42,7 +51,7 @@ @@ -42,7 +51,7 @@
private async Task<Unit> SynchronizeLibraryByIdIfNeeded(int libraryId)
{
await _channel.WriteAsync(new SynchronizePlexLibraryByIdIfNeeded(libraryId));
await _channel.WriteAsync(new SynchronizePlexLibraryByIdIfNeeded(libraryId), _cts.Token);
return Unit.Default;
}

1
ErsatzTV/Pages/PlexMediaSources.razor

@ -3,6 +3,7 @@ @@ -3,6 +3,7 @@
@using ErsatzTV.Application.Plex
@using ErsatzTV.Application.Plex.Commands
@using ErsatzTV.Application.Plex.Queries
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator

17
ErsatzTV/Pages/PlexPathReplacementsEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Plex.Commands
@using ErsatzTV.Application.Plex.Queries
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<ScheduleItemsEditor> _logger
@inject ISnackbar _snackbar
@ -17,16 +19,23 @@ @@ -17,16 +19,23 @@
GetPathReplacementsBySourceId="GetPathReplacementsBySourceId"/>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int id) =>
_mediator.Send(new GetPlexMediaSourceById(Id))
private async Task<Option<RemoteMediaSourceViewModel>> GetMediaSourceById(int id) =>
await _mediator.Send(new GetPlexMediaSourceById(Id), _cts.Token)
.MapT(vm => new RemoteMediaSourceViewModel(vm.Id, vm.Name, vm.Address));
private Task<List<RemoteMediaSourcePathReplacementEditViewModel>> GetPathReplacementsBySourceId(int mediaSourceId) =>
_mediator.Send(new GetPlexPathReplacementsBySourceId(Id))
private async Task<List<RemoteMediaSourcePathReplacementEditViewModel>> GetPathReplacementsBySourceId(int mediaSourceId) =>
await _mediator.Send(new GetPlexPathReplacementsBySourceId(Id), _cts.Token)
.Map(list => list.Map(ProjectToEditViewModel).ToList());
private RemoteMediaSourcePathReplacementEditViewModel ProjectToEditViewModel(PlexPathReplacementViewModel item) =>

17
ErsatzTV/Pages/ScheduleEditor.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@page "/schedules/add"
@using ErsatzTV.Application.ProgramSchedules
@using ErsatzTV.Application.ProgramSchedules.Queries
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<ScheduleEditor> _logger
@inject ISnackbar _snackbar
@ -50,6 +52,7 @@ @@ -50,6 +52,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -57,12 +60,18 @@ @@ -57,12 +60,18 @@
private readonly ProgramScheduleEditViewModel _model = new();
private EditContext _editContext;
private ValidationMessageStore _messageStore;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
if (IsEdit)
{
Option<ProgramScheduleViewModel> maybeProgramSchedule = await _mediator.Send(new GetProgramScheduleById(Id));
Option<ProgramScheduleViewModel> maybeProgramSchedule = await _mediator.Send(new GetProgramScheduleById(Id), _cts.Token);
maybeProgramSchedule.Match(
viewModel =>
{
@ -93,9 +102,9 @@ @@ -93,9 +102,9 @@
_messageStore.Clear();
if (_editContext.Validate())
{
Either<BaseError, EntityIdResult> result = IsEdit ?
await _mediator.Send(_model.ToUpdate()).MapT(r => r as EntityIdResult) :
await _mediator.Send(_model.ToCreate()).MapT(r => r as EntityIdResult);
Either<BaseError, EntityIdResult> result = IsEdit
? await _mediator.Send(_model.ToUpdate(), _cts.Token).MapT(r => r as EntityIdResult)
: await _mediator.Send(_model.ToCreate(), _cts.Token).MapT(r => r as EntityIdResult);
result.Match(
programSchedule =>

29
ErsatzTV/Pages/ScheduleItemsEditor.razor

@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
@using ErsatzTV.Application.Filler
@using ErsatzTV.Application.Filler.Queries
@using ErsatzTV.Core.Domain.Filler
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<ScheduleItemsEditor> _logger
@inject ISnackbar _snackbar
@ -289,6 +291,7 @@ @@ -289,6 +291,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -303,37 +306,43 @@ @@ -303,37 +306,43 @@
private List<FillerPresetViewModel> _fillerPresets;
private ProgramScheduleItemEditViewModel _selectedItem;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override Task OnParametersSetAsync() => LoadScheduleItems();
private async Task LoadScheduleItems()
{
// TODO: fix performance
_mediaCollections = await _mediator.Send(new GetAllCollections())
_mediaCollections = await _mediator.Send(new GetAllCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_multiCollections = await _mediator.Send(new GetAllMultiCollections())
_multiCollections = await _mediator.Send(new GetAllMultiCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_smartCollections = await _mediator.Send(new GetAllSmartCollections())
_smartCollections = await _mediator.Send(new GetAllSmartCollections(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_televisionShows = await _mediator.Send(new GetAllTelevisionShows())
_televisionShows = await _mediator.Send(new GetAllTelevisionShows(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_televisionSeasons = await _mediator.Send(new GetAllTelevisionSeasons())
_televisionSeasons = await _mediator.Send(new GetAllTelevisionSeasons(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_artists = await _mediator.Send(new GetAllArtists())
_artists = await _mediator.Send(new GetAllArtists(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
_fillerPresets = await _mediator.Send(new GetAllFillerPresets())
_fillerPresets = await _mediator.Send(new GetAllFillerPresets(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase).ToList());
string name = string.Empty;
var shuffleScheduleItems = false;
Option<ProgramScheduleViewModel> maybeSchedule = await _mediator.Send(new GetProgramScheduleById(Id));
Option<ProgramScheduleViewModel> maybeSchedule = await _mediator.Send(new GetProgramScheduleById(Id), _cts.Token);
foreach (ProgramScheduleViewModel schedule in maybeSchedule)
{
name = schedule.Name;
shuffleScheduleItems = schedule.ShuffleScheduleItems;
}
Option<IEnumerable<ProgramScheduleItemViewModel>> maybeResults = await _mediator.Send(new GetProgramScheduleItems(Id));
Option<IEnumerable<ProgramScheduleItemViewModel>> maybeResults = await _mediator.Send(new GetProgramScheduleItems(Id), _cts.Token);
foreach (IEnumerable<ProgramScheduleItemViewModel> items in maybeResults)
{
_schedule = new ProgramScheduleItemsEditViewModel
@ -450,7 +459,7 @@ @@ -450,7 +459,7 @@
item.TailFiller?.Id,
item.FallbackFiller?.Id)).ToList();
Seq<BaseError> errorMessages = await _mediator.Send(new ReplaceProgramScheduleItems(Id, items)).Map(e => e.LeftToSeq());
Seq<BaseError> errorMessages = await _mediator.Send(new ReplaceProgramScheduleItems(Id, items), _cts.Token).Map(e => e.LeftToSeq());
errorMessages.HeadOrNone().Match(
error =>

24
ErsatzTV/Pages/Schedules.razor

@ -5,6 +5,8 @@ @@ -5,6 +5,8 @@
@using ErsatzTV.Application.Configuration.Queries
@using ErsatzTV.Application.Configuration.Commands
@using NaturalSort.Extension
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@ -90,17 +92,25 @@ @@ -90,17 +92,25 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private MudTable<ProgramScheduleViewModel> _table;
private MudTable<ProgramScheduleItemViewModel> _detailTable;
private int _rowsPerPage;
private int _detailRowsPerPage;
private ProgramScheduleViewModel _selectedSchedule;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
_rowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.SchedulesPageSize))
_rowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.SchedulesPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
_detailRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.SchedulesDetailPageSize))
_detailRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.SchedulesDetailPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
}
@ -119,7 +129,7 @@ @@ -119,7 +129,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteProgramSchedule(programSchedule.Id));
await _mediator.Send(new DeleteProgramSchedule(programSchedule.Id), _cts.Token);
await _table.ReloadServerData();
if (_selectedSchedule == programSchedule)
{
@ -130,9 +140,9 @@ @@ -130,9 +140,9 @@
private async Task<TableData<ProgramScheduleViewModel>> ServerReload(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SchedulesPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SchedulesPageSize, state.PageSize.ToString()), _cts.Token);
List<ProgramScheduleViewModel> schedules = await _mediator.Send(new GetAllProgramSchedules());
List<ProgramScheduleViewModel> schedules = await _mediator.Send(new GetAllProgramSchedules(), _cts.Token);
IOrderedEnumerable<ProgramScheduleViewModel> sorted = schedules.OrderBy(s => s.Name, new NaturalSortComparer(StringComparison.CurrentCultureIgnoreCase));
// TODO: properly page this data
@ -145,9 +155,9 @@ @@ -145,9 +155,9 @@
private async Task<TableData<ProgramScheduleItemViewModel>> DetailServerReload(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SchedulesDetailPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.SchedulesDetailPageSize, state.PageSize.ToString()), _cts.Token);
List<ProgramScheduleItemViewModel> scheduleItems = await _mediator.Send(new GetProgramScheduleItems(_selectedSchedule.Id));
List<ProgramScheduleItemViewModel> scheduleItems = await _mediator.Send(new GetProgramScheduleItems(_selectedSchedule.Id), _cts.Token);
IOrderedEnumerable<ProgramScheduleItemViewModel> sorted = scheduleItems.OrderBy(s => s.Index);
// TODO: properly page this data

32
ErsatzTV/Pages/Search.razor

@ -333,14 +333,14 @@ @@ -333,14 +333,14 @@
_query = _navigationManager.Uri.GetSearchQuery();
if (!string.IsNullOrWhiteSpace(_query))
{
_movies = await Mediator.Send(new QuerySearchIndexMovies($"type:movie AND ({_query})", 1, 50));
_shows = await Mediator.Send(new QuerySearchIndexShows($"type:show AND ({_query})", 1, 50));
_seasons = await Mediator.Send(new QuerySearchIndexSeasons($"type:season AND ({_query})", 1, 50));
_episodes = await Mediator.Send(new QuerySearchIndexEpisodes($"type:episode AND ({_query})", 1, 50));
_musicVideos = await Mediator.Send(new QuerySearchIndexMusicVideos($"type:music_video AND ({_query})", 1, 50));
_otherVideos = await Mediator.Send(new QuerySearchIndexOtherVideos($"type:other_video AND ({_query})", 1, 50));
_songs = await Mediator.Send(new QuerySearchIndexSongs($"type:song AND ({_query})", 1, 50));
_artists = await Mediator.Send(new QuerySearchIndexArtists($"type:artist AND ({_query})", 1, 50));
_movies = await Mediator.Send(new QuerySearchIndexMovies($"type:movie AND ({_query})", 1, 50), CancellationToken);
_shows = await Mediator.Send(new QuerySearchIndexShows($"type:show AND ({_query})", 1, 50), CancellationToken);
_seasons = await Mediator.Send(new QuerySearchIndexSeasons($"type:season AND ({_query})", 1, 50), CancellationToken);
_episodes = await Mediator.Send(new QuerySearchIndexEpisodes($"type:episode AND ({_query})", 1, 50), CancellationToken);
_musicVideos = await Mediator.Send(new QuerySearchIndexMusicVideos($"type:music_video AND ({_query})", 1, 50), CancellationToken);
_otherVideos = await Mediator.Send(new QuerySearchIndexOtherVideos($"type:other_video AND ({_query})", 1, 50), CancellationToken);
_songs = await Mediator.Send(new QuerySearchIndexSongs($"type:song AND ({_query})", 1, 50), CancellationToken);
_artists = await Mediator.Send(new QuerySearchIndexArtists($"type:artist AND ({_query})", 1, 50), CancellationToken);
}
}
@ -374,7 +374,7 @@ @@ -374,7 +374,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddMovieToCollection(collection.Id, movie.MovieId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{
@ -395,7 +395,7 @@ @@ -395,7 +395,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddShowToCollection(collection.Id, show.TelevisionShowId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{
@ -416,7 +416,7 @@ @@ -416,7 +416,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddEpisodeToCollection(collection.Id, episode.EpisodeId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{
@ -437,7 +437,7 @@ @@ -437,7 +437,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddArtistToCollection(collection.Id, artist.ArtistId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{
@ -458,7 +458,7 @@ @@ -458,7 +458,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddMusicVideoToCollection(collection.Id, musicVideo.MusicVideoId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{
@ -479,7 +479,7 @@ @@ -479,7 +479,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddOtherVideoToCollection(collection.Id, otherVideo.OtherVideoId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{
@ -581,7 +581,7 @@ @@ -581,7 +581,7 @@
private async Task AddAllToCollection(MouseEventArgs _)
{
SearchResultAllItemsViewModel results = await Mediator.Send(new QuerySearchIndexAllItems(_query));
SearchResultAllItemsViewModel results = await Mediator.Send(new QuerySearchIndexAllItems(_query), CancellationToken);
await AddItemsToCollection(
results.MovieIds,
results.ShowIds,
@ -606,7 +606,7 @@ @@ -606,7 +606,7 @@
collection.Id,
_query);
Either<BaseError, Unit> updateResult = await Mediator.Send(request);
Either<BaseError, Unit> updateResult = await Mediator.Send(request, CancellationToken);
updateResult.Match(
Left: error =>
{

34
ErsatzTV/Pages/Settings.razor

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
@using ErsatzTV.Core.Domain.Filler
@using ErsatzTV.Core.FFmpeg
@using Microsoft.AspNetCore.Components
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ISnackbar _snackbar
@inject ILogger<Settings> _logger
@ -177,6 +179,8 @@ @@ -177,6 +179,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private bool _success;
private bool _hdhrSuccess;
private bool _scannerSuccess;
@ -189,22 +193,28 @@ @@ -189,22 +193,28 @@
private int _tunerCount;
private int _libraryRefreshInterval;
private int _playoutDaysToBuild;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
await LoadFFmpegProfilesAsync();
_ffmpegSettings = await _mediator.Send(new GetFFmpegSettings());
_ffmpegSettings = await _mediator.Send(new GetFFmpegSettings(), _cts.Token);
_success = File.Exists(_ffmpegSettings.FFmpegPath) && File.Exists(_ffmpegSettings.FFprobePath);
_availableCultures = await _mediator.Send(new GetAllLanguageCodes());
_watermarks = await _mediator.Send(new GetAllWatermarks());
_fillerPresets = await _mediator.Send(new GetAllFillerPresets())
_availableCultures = await _mediator.Send(new GetAllLanguageCodes(), _cts.Token);
_watermarks = await _mediator.Send(new GetAllWatermarks(), _cts.Token);
_fillerPresets = await _mediator.Send(new GetAllFillerPresets(), _cts.Token)
.Map(list => list.Filter(fp => fp.FillerKind == FillerKind.Fallback).ToList());
_tunerCount = await _mediator.Send(new GetHDHRTunerCount());
_tunerCount = await _mediator.Send(new GetHDHRTunerCount(), _cts.Token);
_hdhrSuccess = string.IsNullOrWhiteSpace(ValidateTunerCount(_tunerCount));
_libraryRefreshInterval = await _mediator.Send(new GetLibraryRefreshInterval());
_libraryRefreshInterval = await _mediator.Send(new GetLibraryRefreshInterval(), _cts.Token);
_scannerSuccess = _libraryRefreshInterval > 0;
_playoutDaysToBuild = await _mediator.Send(new GetPlayoutDaysToBuild());
_playoutDaysToBuild = await _mediator.Send(new GetPlayoutDaysToBuild(), _cts.Token);
_playoutSuccess = _playoutDaysToBuild > 0;
}
@ -223,11 +233,11 @@ @@ -223,11 +233,11 @@
private static string ValidateInitialSegmentCount(int count) => count < 1 ? "HLS Segmenter initial segment count must be greater than or equal to 1" : null;
private async Task LoadFFmpegProfilesAsync() =>
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles());
_ffmpegProfiles = await _mediator.Send(new GetAllFFmpegProfiles(), _cts.Token);
private async Task SaveFFmpegSettings()
{
Either<BaseError, Unit> result = await _mediator.Send(new UpdateFFmpegSettings(_ffmpegSettings));
Either<BaseError, Unit> result = await _mediator.Send(new UpdateFFmpegSettings(_ffmpegSettings), _cts.Token);
result.Match(
Left: error =>
{
@ -239,7 +249,7 @@ @@ -239,7 +249,7 @@
private async Task SaveHDHRSettings()
{
Either<BaseError, Unit> result = await _mediator.Send(new UpdateHDHRTunerCount(_tunerCount));
Either<BaseError, Unit> result = await _mediator.Send(new UpdateHDHRTunerCount(_tunerCount), _cts.Token);
result.Match(
Left: error =>
{
@ -251,7 +261,7 @@ @@ -251,7 +261,7 @@
private async Task SaveScannerSettings()
{
Either<BaseError, Unit> result = await _mediator.Send(new UpdateLibraryRefreshInterval(_libraryRefreshInterval));
Either<BaseError, Unit> result = await _mediator.Send(new UpdateLibraryRefreshInterval(_libraryRefreshInterval), _cts.Token);
result.Match(
Left: error =>
{
@ -263,7 +273,7 @@ @@ -263,7 +273,7 @@
private async Task SavePlayoutSettings()
{
Either<BaseError, Unit> result = await _mediator.Send(new UpdatePlayoutDaysToBuild(_playoutDaysToBuild));
Either<BaseError, Unit> result = await _mediator.Send(new UpdatePlayoutDaysToBuild(_playoutDaysToBuild), _cts.Token);
result.Match(
Left: error =>
{

4
ErsatzTV/Pages/SongList.razor

@ -96,7 +96,7 @@ @@ -96,7 +96,7 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:song" : $"type:song AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexSongs(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexSongs(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
@ -143,7 +143,7 @@ @@ -143,7 +143,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddSongToCollection(collection.Id, song.SongId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

19
ErsatzTV/Pages/TelevisionEpisodeList.razor

@ -9,6 +9,8 @@ @@ -9,6 +9,8 @@
@using ErsatzTV.Application.ProgramSchedules
@using ErsatzTV.Application.ProgramSchedules.Commands
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ILogger<TelevisionEpisodeList> _logger
@inject ISnackbar _snackbar
@ -156,6 +158,7 @@ @@ -156,6 +158,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int SeasonId { get; set; }
@ -166,6 +169,12 @@ @@ -166,6 +169,12 @@
private readonly int _pageNumber = 1;
private TelevisionEpisodeCardResultsViewModel _data;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
@ -179,10 +188,10 @@ @@ -179,10 +188,10 @@
private async Task RefreshData()
{
await _mediator.Send(new GetTelevisionSeasonById(SeasonId))
await _mediator.Send(new GetTelevisionSeasonById(SeasonId), _cts.Token)
.IfSomeAsync(vm => _season = vm);
_data = await _mediator.Send(new GetTelevisionEpisodeCards(SeasonId, _pageNumber, _pageSize));
_data = await _mediator.Send(new GetTelevisionEpisodeCards(SeasonId, _pageNumber, _pageSize), _cts.Token);
}
private async Task AddToCollection()
@ -194,7 +203,7 @@ @@ -194,7 +203,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
await _mediator.Send(new AddSeasonToCollection(collection.Id, SeasonId));
await _mediator.Send(new AddSeasonToCollection(collection.Id, SeasonId), _cts.Token);
_navigationManager.NavigateTo($"/media/collections/{collection.Id}");
}
}
@ -209,7 +218,7 @@ @@ -209,7 +218,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is ProgramScheduleViewModel schedule)
{
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionSeason, null, null, null, SeasonId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null));
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionSeason, null, null, null, SeasonId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null), _cts.Token);
_navigationManager.NavigateTo($"/schedules/{schedule.Id}/items");
}
}
@ -224,7 +233,7 @@ @@ -224,7 +233,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddEpisodeToCollection(collection.Id, episode.EpisodeId);
Either<BaseError, Unit> addResult = await _mediator.Send(request);
Either<BaseError, Unit> addResult = await _mediator.Send(request, _cts.Token);
addResult.Match(
Left: error =>
{

19
ErsatzTV/Pages/TelevisionSeasonList.razor

@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
@using ErsatzTV.Application.ProgramSchedules.Commands
@using ErsatzTV.Extensions
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ILogger<TelevisionSeasonList> _logger
@inject ISnackbar _snackbar
@ -168,6 +170,7 @@ @@ -168,6 +170,7 @@
}
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int ShowId { get; set; }
@ -183,12 +186,18 @@ @@ -183,12 +186,18 @@
private readonly int _pageNumber = 1;
private TelevisionSeasonCardResultsViewModel _data;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override Task OnParametersSetAsync() => RefreshData();
private async Task RefreshData()
{
await _mediator.Send(new GetTelevisionShowById(ShowId))
await _mediator.Send(new GetTelevisionShowById(ShowId), _cts.Token)
.IfSomeAsync(vm =>
{
_show = vm;
@ -199,7 +208,7 @@ @@ -199,7 +208,7 @@
_sortedTags = _show.Tags.OrderBy(t => t).ToList();
});
_data = await _mediator.Send(new GetTelevisionSeasonCards(ShowId, _pageNumber, _pageSize));
_data = await _mediator.Send(new GetTelevisionSeasonCards(ShowId, _pageNumber, _pageSize), _cts.Token);
}
private async Task AddToCollection()
@ -211,7 +220,7 @@ @@ -211,7 +220,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
await _mediator.Send(new AddShowToCollection(collection.Id, ShowId));
await _mediator.Send(new AddShowToCollection(collection.Id, ShowId), _cts.Token);
_navigationManager.NavigateTo($"/media/collections/{collection.Id}");
}
}
@ -225,7 +234,7 @@ @@ -225,7 +234,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is ProgramScheduleViewModel schedule)
{
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionShow, null, null, null, ShowId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null));
await _mediator.Send(new AddProgramScheduleItem(schedule.Id, StartType.Dynamic, null, PlayoutMode.One, ProgramScheduleItemCollectionType.TelevisionShow, null, null, null, ShowId, PlaybackOrder.Shuffle, null, null, TailMode.None, null, GuideMode.Normal, null, null, null, null, null), _cts.Token);
_navigationManager.NavigateTo($"/schedules/{schedule.Id}/items");
}
}
@ -242,7 +251,7 @@ @@ -242,7 +251,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddSeasonToCollection(collection.Id, season.TelevisionSeasonId);
Either<BaseError, Unit> addResult = await _mediator.Send(request);
Either<BaseError, Unit> addResult = await _mediator.Send(request, _cts.Token);
addResult.Match(
Left: error =>
{

4
ErsatzTV/Pages/TelevisionSeasonSearchResults.razor

@ -95,7 +95,7 @@ @@ -95,7 +95,7 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:season" : $"type:season AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexSeasons(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexSeasons(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
@ -142,7 +142,7 @@ @@ -142,7 +142,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddSeasonToCollection(collection.Id, season.TelevisionSeasonId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

4
ErsatzTV/Pages/TelevisionShowList.razor

@ -95,7 +95,7 @@ @@ -95,7 +95,7 @@
protected override async Task RefreshData()
{
string searchQuery = string.IsNullOrWhiteSpace(_query) ? "type:show" : $"type:show AND ({_query})";
_data = await Mediator.Send(new QuerySearchIndexShows(searchQuery, PageNumber, PageSize));
_data = await Mediator.Send(new QuerySearchIndexShows(searchQuery, PageNumber, PageSize), CancellationToken);
}
private void PrevPage()
@ -142,7 +142,7 @@ @@ -142,7 +142,7 @@
if (!result.Cancelled && result.Data is MediaCollectionViewModel collection)
{
var request = new AddShowToCollection(collection.Id, show.TelevisionShowId);
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
addResult.Match(
Left: error =>
{

22
ErsatzTV/Pages/TraktLists.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Configuration.Queries
@using ErsatzTV.Application.Configuration.Commands
@using ErsatzTV.Application.MediaCollections.Commands
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@inject ISnackbar _snackbar
@ -75,9 +77,17 @@ @@ -75,9 +77,17 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private MudTable<TraktListViewModel> _traktListsTable;
private int _traktListsRowsPerPage;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override void OnInitialized()
{
@ -86,7 +96,7 @@ @@ -86,7 +96,7 @@
protected override async Task OnParametersSetAsync()
{
_traktListsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.TraktListsPageSize))
_traktListsRowsPerPage = await _mediator.Send(new GetConfigElementByKey(ConfigElementKey.TraktListsPageSize), _cts.Token)
.Map(maybeRows => maybeRows.Match(ce => int.TryParse(ce.Value, out int rows) ? rows : 10, () => 10));
}
@ -104,7 +114,7 @@ @@ -104,7 +114,7 @@
{
if (_locker.LockTrakt())
{
await _workerChannel.WriteAsync(new MatchTraktListItems(traktList.Id));
await _workerChannel.WriteAsync(new MatchTraktListItems(traktList.Id), _cts.Token);
}
}
@ -119,16 +129,16 @@ @@ -119,16 +129,16 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _workerChannel.WriteAsync(new DeleteTraktList(traktList.Id));
await _workerChannel.WriteAsync(new DeleteTraktList(traktList.Id), _cts.Token);
}
}
}
private async Task<TableData<TraktListViewModel>> ServerReloadTraktLists(TableState state)
{
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.TraktListsPageSize, state.PageSize.ToString()));
await _mediator.Send(new SaveConfigElementByKey(ConfigElementKey.TraktListsPageSize, state.PageSize.ToString()), _cts.Token);
PagedTraktListsViewModel data = await _mediator.Send(new GetPagedTraktLists(state.Page, state.PageSize));
PagedTraktListsViewModel data = await _mediator.Send(new GetPagedTraktLists(state.Page, state.PageSize), _cts.Token);
return new TableData<TraktListViewModel> { TotalItems = data.TotalCount, Items = data.Page };
}
@ -141,7 +151,7 @@ @@ -141,7 +151,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled && result.Data is string url)
{
await _workerChannel.WriteAsync(new AddTraktList(url));
await _workerChannel.WriteAsync(new AddTraktList(url), _cts.Token);
}
}
}

20
ErsatzTV/Pages/Trash.razor

@ -327,14 +327,14 @@ @@ -327,14 +327,14 @@
_query = "state:FileNotFound";
if (!string.IsNullOrWhiteSpace(_query))
{
_movies = await Mediator.Send(new QuerySearchIndexMovies($"type:movie AND ({_query})", 1, 50));
_shows = await Mediator.Send(new QuerySearchIndexShows($"type:show AND ({_query})", 1, 50));
_seasons = await Mediator.Send(new QuerySearchIndexSeasons($"type:season AND ({_query})", 1, 50));
_episodes = await Mediator.Send(new QuerySearchIndexEpisodes($"type:episode AND ({_query})", 1, 50));
_musicVideos = await Mediator.Send(new QuerySearchIndexMusicVideos($"type:music_video AND ({_query})", 1, 50));
_otherVideos = await Mediator.Send(new QuerySearchIndexOtherVideos($"type:other_video AND ({_query})", 1, 50));
_songs = await Mediator.Send(new QuerySearchIndexSongs($"type:song AND ({_query})", 1, 50));
_artists = await Mediator.Send(new QuerySearchIndexArtists($"type:artist AND ({_query})", 1, 50));
_movies = await Mediator.Send(new QuerySearchIndexMovies($"type:movie AND ({_query})", 1, 50), CancellationToken);
_shows = await Mediator.Send(new QuerySearchIndexShows($"type:show AND ({_query})", 1, 50), CancellationToken);
_seasons = await Mediator.Send(new QuerySearchIndexSeasons($"type:season AND ({_query})", 1, 50), CancellationToken);
_episodes = await Mediator.Send(new QuerySearchIndexEpisodes($"type:episode AND ({_query})", 1, 50), CancellationToken);
_musicVideos = await Mediator.Send(new QuerySearchIndexMusicVideos($"type:music_video AND ({_query})", 1, 50), CancellationToken);
_otherVideos = await Mediator.Send(new QuerySearchIndexOtherVideos($"type:other_video AND ({_query})", 1, 50), CancellationToken);
_songs = await Mediator.Send(new QuerySearchIndexSongs($"type:song AND ({_query})", 1, 50), CancellationToken);
_artists = await Mediator.Send(new QuerySearchIndexArtists($"type:artist AND ({_query})", 1, 50), CancellationToken);
}
}
@ -486,7 +486,7 @@ @@ -486,7 +486,7 @@
.Append(songIds)
.ToList());
Either<BaseError, Unit> addResult = await Mediator.Send(request);
Either<BaseError, Unit> addResult = await Mediator.Send(request, CancellationToken);
await addResult.Match(
Left: error =>
{
@ -556,7 +556,7 @@ @@ -556,7 +556,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await Mediator.Send(request);
await Mediator.Send(request, CancellationToken);
await RefreshData();
}
}

22
ErsatzTV/Pages/WatermarkEditor.razor

@ -4,6 +4,8 @@ @@ -4,6 +4,8 @@
@using ErsatzTV.Application.Watermarks
@using ErsatzTV.Application.Watermarks.Queries
@using ErsatzTV.FFmpeg.State
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<WatermarkEditor> _logger
@inject ISnackbar _snackbar
@ -146,7 +148,8 @@ @@ -146,7 +148,8 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -154,11 +157,17 @@ @@ -154,11 +157,17 @@
private EditContext _editContext;
private ValidationMessageStore _messageStore;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync()
{
if (IsEdit)
{
Option<WatermarkViewModel> watermark = await _mediator.Send(new GetWatermarkById(Id));
Option<WatermarkViewModel> watermark = await _mediator.Send(new GetWatermarkById(Id), _cts.Token);
watermark.Match(
watermarkViewModel => _model = new WatermarkEditViewModel(watermarkViewModel),
() => _navigationManager.NavigateTo("404"));
@ -193,8 +202,8 @@ @@ -193,8 +202,8 @@
if (_editContext.Validate())
{
Seq<BaseError> errorMessage = IsEdit ?
(await _mediator.Send(_model.ToUpdate())).LeftToSeq() :
(await _mediator.Send(_model.ToCreate())).LeftToSeq();
(await _mediator.Send(_model.ToUpdate(), _cts.Token)).LeftToSeq() :
(await _mediator.Send(_model.ToCreate(), _cts.Token)).LeftToSeq();
errorMessage.HeadOrNone().Match(
error =>
@ -210,8 +219,9 @@ @@ -210,8 +219,9 @@
{
try
{
Either<BaseError, string> maybeCacheFileName = await _mediator
.Send(new SaveArtworkToDisk(e.File.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024), ArtworkKind.Watermark));
Either<BaseError, string> maybeCacheFileName = await _mediator.Send(
new SaveArtworkToDisk(e.File.OpenReadStream(maxAllowedSize: 10 * 1024 * 1024), ArtworkKind.Watermark),
_cts.Token);
maybeCacheFileName.Match(
relativeFileName =>
{

14
ErsatzTV/Pages/Watermarks.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@using ErsatzTV.Application.Watermarks
@using ErsatzTV.Application.Watermarks.Commands
@using ErsatzTV.Application.Watermarks.Queries
@using System.Threading
@implements IDisposable
@inject IDialogService _dialog
@inject IMediator _mediator
@inject ILogger<Watermarks> _logger
@ -72,12 +74,20 @@ @@ -72,12 +74,20 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
private List<WatermarkViewModel> _watermarks;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync() => await LoadWatermarksAsync();
private async Task LoadWatermarksAsync() =>
_watermarks = await _mediator.Send(new GetAllWatermarks());
_watermarks = await _mediator.Send(new GetAllWatermarks(), _cts.Token);
private async Task DeleteWatermarkAsync(WatermarkViewModel watermark)
{
@ -88,7 +98,7 @@ @@ -88,7 +98,7 @@
DialogResult result = await dialog.Result;
if (!result.Cancelled)
{
await _mediator.Send(new DeleteWatermark(watermark.Id));
await _mediator.Send(new DeleteWatermark(watermark.Id), _cts.Token);
await LoadWatermarksAsync();
}
}

13
ErsatzTV/Shared/AddToCollectionDialog.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Application.MediaCollections.Queries
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject IMemoryCache _memoryCache
@inject ISnackbar _snackbar
@ -39,6 +41,7 @@ @@ -39,6 +41,7 @@
@code {
private readonly CancellationTokenSource _cts = new();
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
@ -65,13 +68,19 @@ @@ -65,13 +68,19 @@
private record DummyModel;
private readonly DummyModel _dummyModel = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private bool CanSubmit() =>
_selectedCollection != null && (_selectedCollection != _newCollection || !string.IsNullOrWhiteSpace(_newCollectionName));
protected override async Task OnParametersSetAsync()
{
_collections = await _mediator.Send(new GetAllCollections())
_collections = await _mediator.Send(new GetAllCollections(), _cts.Token)
.Map(list => new[] { _newCollection }.Append(list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase)).ToList());
if (_memoryCache.TryGetValue("AddToCollectionDialog.SelectedCollectionId", out int id))
@ -96,7 +105,7 @@ @@ -96,7 +105,7 @@
if (_selectedCollection == _newCollection)
{
Either<BaseError, MediaCollectionViewModel> maybeResult =
await _mediator.Send(new CreateCollection(_newCollectionName));
await _mediator.Send(new CreateCollection(_newCollectionName), _cts.Token);
maybeResult.Match(
collection =>

11
ErsatzTV/Shared/AddToScheduleDialog.razor

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
@using ErsatzTV.Application.ProgramSchedules
@using ErsatzTV.Application.ProgramSchedules.Queries
@using NaturalSort.Extension
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
<div @onkeydown="@OnKeyDown">
@ -29,6 +31,7 @@ @@ -29,6 +31,7 @@
</div>
@code {
private readonly CancellationTokenSource _cts = new();
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
@ -48,9 +51,15 @@ @@ -48,9 +51,15 @@
private List<ProgramScheduleViewModel> _schedules;
private ProgramScheduleViewModel _selectedSchedule;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync() =>
_schedules = await _mediator.Send(new GetAllProgramSchedules())
_schedules = await _mediator.Send(new GetAllProgramSchedules(), _cts.Token)
.Map(list => list.OrderBy(vm => vm.Name, new NaturalSortComparer(StringComparison.CurrentCultureIgnoreCase)).ToList());
private string FormatText() => $"Select the schedule to add the {EntityType} {EntityName}";

11
ErsatzTV/Shared/CopyFFmpegProfileDialog.razor

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
@using ErsatzTV.Application.FFmpegProfiles
@using ErsatzTV.Application.FFmpegProfiles.Commands
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ISnackbar _snackbar
@inject ILogger<CopyFFmpegProfileDialog> _logger
@ -27,6 +29,7 @@ @@ -27,6 +29,7 @@
</MudDialog>
@code {
private readonly CancellationTokenSource _cts = new();
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
@ -40,6 +43,12 @@ @@ -40,6 +43,12 @@
private string _newFFmpegProfileName;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private bool CanSubmit() => !string.IsNullOrWhiteSpace(_newFFmpegProfileName);
private async Task Submit()
@ -50,7 +59,7 @@ @@ -50,7 +59,7 @@
}
Either<BaseError, FFmpegProfileViewModel> maybeResult =
await _mediator.Send(new CopyFFmpegProfile(FFmpegProfileId, _newFFmpegProfileName));
await _mediator.Send(new CopyFFmpegProfile(FFmpegProfileId, _newFFmpegProfileName), _cts.Token);
maybeResult.Match(
ffmpegProfile => { MudDialog.Close(DialogResult.Ok(ffmpegProfile)); },

11
ErsatzTV/Shared/CopyWatermarkDialog.razor

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
@using ErsatzTV.Application.Watermarks
@using ErsatzTV.Application.Watermarks.Commands
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ISnackbar _snackbar
@inject ILogger<CopyWatermarkDialog> _logger
@ -27,6 +29,7 @@ @@ -27,6 +29,7 @@
</MudDialog>
@code {
private readonly CancellationTokenSource _cts = new();
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
@ -39,6 +42,12 @@ @@ -39,6 +42,12 @@
private readonly DummyModel _dummyModel = new();
private string _newWatermarkName;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private bool CanSubmit() => !string.IsNullOrWhiteSpace(_newWatermarkName);
@ -50,7 +59,7 @@ @@ -50,7 +59,7 @@
}
Either<BaseError, WatermarkViewModel> maybeResult =
await _mediator.Send(new CopyWatermark(WatermarkId, _newWatermarkName));
await _mediator.Send(new CopyWatermark(WatermarkId, _newWatermarkName), _cts.Token);
maybeResult.Match(
watermark => { MudDialog.Close(DialogResult.Ok(watermark)); },

13
ErsatzTV/Shared/MoveLocalLibraryPathDialog.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@using ErsatzTV.Application.Libraries
@using ErsatzTV.Application.Libraries.Commands
@using ErsatzTV.Application.Libraries.Queries
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject IMemoryCache _memoryCache
@inject ISnackbar _snackbar
@ -38,6 +40,7 @@ @@ -38,6 +40,7 @@
@code {
private readonly CancellationTokenSource _cts = new();
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
@ -58,6 +61,12 @@ @@ -58,6 +61,12 @@
private record DummyModel;
private readonly DummyModel _dummyModel = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private bool CanSubmit() =>
_selectedLibrary != null && (_selectedLibrary != _newLibrary || !string.IsNullOrWhiteSpace(_newLibraryName));
@ -66,7 +75,7 @@ @@ -66,7 +75,7 @@
{
_newLibrary = new LocalLibraryViewModel(-1, "(New Library)", MediaKind);
_libraries = await _mediator.Send(new GetAllLocalLibraries())
_libraries = await _mediator.Send(new GetAllLocalLibraries(), _cts.Token)
.Map(list => list.Filter(ll => ll.MediaKind == MediaKind && ll.Id != SourceLibraryId))
.Map(list => new[] { _newLibrary }.Append(list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase)).ToList());
@ -90,7 +99,7 @@ @@ -90,7 +99,7 @@
if (_selectedLibrary == _newLibrary)
{
Either<BaseError, LocalLibraryViewModel> maybeResult =
await _mediator.Send(new CreateLocalLibrary(_newLibraryName, MediaKind, new List<string>()));
await _mediator.Send(new CreateLocalLibrary(_newLibraryName, MediaKind, new List<string>()), _cts.Token);
maybeResult.Match(
collection =>

11
ErsatzTV/Shared/RemoteMediaSourceLibrariesEditor.razor

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
@using ErsatzTV.Application.MediaSources
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject NavigationManager _navigationManager
@inject ILogger<RemoteMediaSourceLibrariesEditor> _logger
@ -43,6 +45,7 @@ @@ -43,6 +45,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -64,6 +67,12 @@ @@ -64,6 +67,12 @@
private RemoteMediaSourceViewModel _source;
private List<RemoteMediaSourceLibraryEditViewModel> _libraries;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override Task OnParametersSetAsync() => LoadData();
@ -86,7 +95,7 @@ @@ -86,7 +95,7 @@
private async Task SaveChanges()
{
IRequest<Either<BaseError, Unit>> request = GetUpdateLibraryRequest(_libraries);
Seq<BaseError> errorMessages = await _mediator.Send(request).Map(e => e.LeftToSeq());
Seq<BaseError> errorMessages = await _mediator.Send(request, _cts.Token).Map(e => e.LeftToSeq());
await errorMessages.HeadOrNone().Match(
error =>

11
ErsatzTV/Shared/RemoteMediaSourcePathReplacementsEditor.razor

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
@using ErsatzTV.Application.MediaSources
@using Unit = LanguageExt.Unit
@using System.Threading
@implements IDisposable
@inject NavigationManager _navigationManager
@inject ILogger<RemoteMediaSourcePathReplacementsEditor> _logger
@inject ISnackbar _snackbar
@ -69,6 +71,7 @@ @@ -69,6 +71,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public int Id { get; set; }
@ -89,6 +92,12 @@ @@ -89,6 +92,12 @@
private List<RemoteMediaSourcePathReplacementEditViewModel> _pathReplacements;
private RemoteMediaSourcePathReplacementEditViewModel _selectedItem;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override Task OnParametersSetAsync() => LoadData();
@ -123,7 +132,7 @@ @@ -123,7 +132,7 @@
private async Task SaveChanges()
{
Seq<BaseError> errorMessages = await _mediator.Send(GetUpdatePathReplacementsRequest(_pathReplacements))
Seq<BaseError> errorMessages = await _mediator.Send(GetUpdatePathReplacementsRequest(_pathReplacements), _cts.Token)
.Map(e => e.LeftToSeq());
errorMessages.HeadOrNone().Match(

13
ErsatzTV/Shared/RemoteMediaSources.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@typeparam TSecrets
@using Unit = LanguageExt.Unit
@using ErsatzTV.Core.Interfaces.MediaSources
@using System.Threading
@implements IDisposable
@typeparam TMediaSource
@inject IMediator _mediator
@inject IDialogService _dialog
@ -79,6 +81,7 @@ @@ -79,6 +81,7 @@
</MudContainer>
@code {
private readonly CancellationTokenSource _cts = new();
[Parameter]
public string Name { get; set; }
@ -98,6 +101,12 @@ @@ -98,6 +101,12 @@
private List<TViewModel> _mediaSources = new();
private bool _isAuthorized;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override async Task OnParametersSetAsync() => await LoadMediaSources();
@ -105,7 +114,7 @@ @@ -105,7 +114,7 @@
{
_isAuthorized = await SecretStore.ReadSecrets()
.Map(secrets => !string.IsNullOrWhiteSpace(secrets.Address) && !string.IsNullOrWhiteSpace(secrets.ApiKey));
_mediaSources = await _mediator.Send(GetAllMediaSourcesCommand);
_mediaSources = await _mediator.Send(GetAllMediaSourcesCommand, _cts.Token);
}
private async Task Disconnect()
@ -118,7 +127,7 @@ @@ -118,7 +127,7 @@
{
if (_locker.LockRemoteMediaSource<TMediaSource>())
{
await _mediator.Send(DisconnectCommand);
await _mediator.Send(DisconnectCommand, _cts.Token);
await LoadMediaSources();
}
}

13
ErsatzTV/Shared/SaveAsSmartCollectionDialog.razor

@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Application.MediaCollections.Queries
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject IMemoryCache _memoryCache
@inject ISnackbar _snackbar
@ -38,6 +40,7 @@ @@ -38,6 +40,7 @@
@code {
private readonly CancellationTokenSource _cts = new();
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
@ -52,13 +55,19 @@ @@ -52,13 +55,19 @@
private record DummyModel;
private readonly DummyModel _dummyModel = new();
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
private bool CanSubmit() =>
_selectedCollection != null && (_selectedCollection != _newCollection || !string.IsNullOrWhiteSpace(_newCollectionName));
protected override async Task OnParametersSetAsync()
{
_collections = await _mediator.Send(new GetAllSmartCollections())
_collections = await _mediator.Send(new GetAllSmartCollections(), _cts.Token)
.Map(list => new[] { _newCollection }.Append(list.OrderBy(vm => vm.Name, StringComparer.CurrentCultureIgnoreCase)).ToList());
if (_memoryCache.TryGetValue("SaveAsSmartCollectionDialog.SelectedCollectionId", out int id))
@ -81,7 +90,7 @@ @@ -81,7 +90,7 @@
if (_selectedCollection == _newCollection)
{
Either<BaseError, SmartCollectionViewModel> maybeResult =
await _mediator.Send(new CreateSmartCollection(string.Empty, _newCollectionName));
await _mediator.Send(new CreateSmartCollection(string.Empty, _newCollectionName), _cts.Token);
maybeResult.Match(
collection =>

11
ErsatzTV/Shared/SchedulePlayoutRebuild.razor

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
@using System.Globalization
@using ErsatzTV.Application.Playouts
@using ErsatzTV.Application.Playouts.Commands
@using System.Threading
@implements IDisposable
@inject IMediator _mediator
@inject ISnackbar _snackbar
@inject ILogger<SchedulePlayoutRebuild> _logger
@ -33,6 +35,7 @@ @@ -33,6 +35,7 @@
</MudDialog>
@code {
private readonly CancellationTokenSource _cts = new();
[CascadingParameter]
MudDialogInstance MudDialog { get; set; }
@ -56,6 +59,12 @@ @@ -56,6 +59,12 @@
private readonly DummyModel _dummyModel = new();
private Option<TimeSpan> _rebuildTime;
public void Dispose()
{
_cts.Cancel();
_cts.Dispose();
}
protected override void OnParametersSet()
{
@ -65,7 +74,7 @@ @@ -65,7 +74,7 @@
private async Task Submit()
{
Either<BaseError, PlayoutNameViewModel> maybeResult =
await _mediator.Send(new UpdatePlayout(PlayoutId, _rebuildTime));
await _mediator.Send(new UpdatePlayout(PlayoutId, _rebuildTime), _cts.Token);
maybeResult.Match(
playout => { MudDialog.Close(DialogResult.Ok(playout)); },

Loading…
Cancel
Save