@page "/media/tv/shows/{ShowId:int}" @using System.Globalization @using ErsatzTV.Application.Libraries @using ErsatzTV.Application.MediaCards @using ErsatzTV.Application.MediaCollections @using ErsatzTV.Application.ProgramSchedules @using ErsatzTV.Application.Television @using ErsatzTV.Extensions @implements IDisposable @inject IMediator Mediator @inject ILogger Logger @inject ISnackbar Snackbar @inject IDialogService Dialog @inject NavigationManager NavigationManager
@if (!string.IsNullOrWhiteSpace(_show?.FanArt)) { if (_show.FanArt.StartsWith("http://") || _show.FanArt.StartsWith("https://")) { fan art } else { fan art } }
@_show?.Title @_show?.Title @_show?.Year @if (!string.IsNullOrWhiteSpace(_show?.Plot)) { @_show.Plot } Add To @if (_show?.MediaSourceKind is MediaSourceKind.Plex or MediaSourceKind.Jellyfin or MediaSourceKind.Emby) { Scan } @if (_sortedContentRatings.Any()) {
Content Ratings:  @_sortedContentRatings.Head() @foreach (string contentRating in _sortedContentRatings.Skip(1)) { @contentRating }
} @if (_sortedLanguages.Any()) {
Languages:  @_sortedLanguages.Head().EnglishName @foreach (CultureInfo language in _sortedLanguages.Skip(1)) { @language.EnglishName }
} @if (_sortedStudios.Any()) {
Studios:  @_sortedStudios.Head() @foreach (string studio in _sortedStudios.Skip(1)) { @studio }
} @if (_sortedNetworks.Any()) {
Networks:  @_sortedNetworks.Head() @foreach (string network in _sortedNetworks.Skip(1)) { @network }
} @if (_sortedGenres.Any()) {
Genres:  @_sortedGenres.Head() @foreach (string genre in _sortedGenres.Skip(1)) { @genre }
} @if (_sortedTags.Any()) {
Tags:  @_sortedTags.Head() @foreach (string tag in _sortedTags.Skip(1)) { @tag }
}
Seasons @foreach (TelevisionSeasonCardViewModel card in _data.Cards) { } @if ((_show?.Actors?.Count ?? 0) > 0) { Actors @foreach (ActorCardViewModel actor in _show.Actors) { } } @code { private CancellationTokenSource _cts; [Parameter] public int ShowId { get; set; } private TelevisionShowViewModel _show; private List _sortedContentRatings = []; private List _sortedLanguages = []; private List _sortedStudios = []; private List _sortedNetworks = []; private List _sortedGenres = []; private List _sortedTags = []; private static int PageSize => 100; private const int PageNumber = 1; private TelevisionSeasonCardResultsViewModel _data = new(0, [], null); public void Dispose() { _cts?.Cancel(); _cts?.Dispose(); } protected override async Task OnParametersSetAsync() { _cts?.Cancel(); _cts?.Dispose(); _cts = new CancellationTokenSource(); var token = _cts.Token; try { Option maybeShow = await Mediator.Send(new GetTelevisionShowById(ShowId), token); foreach (TelevisionShowViewModel show in maybeShow) { _show = show; _sortedContentRatings = _show.ContentRatings.OrderBy(cr => cr).ToList(); _sortedLanguages = _show.Languages.OrderBy(ci => ci.EnglishName).ToList(); _sortedStudios = _show.Studios.OrderBy(s => s).ToList(); _sortedGenres = _show.Genres.OrderBy(g => g).ToList(); _sortedTags = _show.Tags.OrderBy(t => t).ToList(); _sortedNetworks = _show.Networks.OrderBy(n => n).ToList(); } _data = await Mediator.Send(new GetTelevisionSeasonCards(ShowId, PageNumber, PageSize), token); } catch (OperationCanceledException) { // do nothing } } private async Task AddToCollection() { var parameters = new DialogParameters { { "EntityType", "show" }, { "EntityName", _show.Title } }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; IDialogReference dialog = await Dialog.ShowAsync("Add To Collection", parameters, options); DialogResult result = await dialog.Result; if (result is { Canceled: false, Data: MediaCollectionViewModel collection }) { await Mediator.Send(new AddShowToCollection(collection.Id, ShowId), _cts.Token); NavigationManager.NavigateTo($"media/collections/{collection.Id}"); } } private async Task AddToPlaylist() { var parameters = new DialogParameters { { "EntityType", "show" }, { "EntityName", _show.Title } }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; IDialogReference dialog = await Dialog.ShowAsync("Add To Playlist", parameters, options); DialogResult result = await dialog.Result; if (result is { Canceled: false, Data: PlaylistViewModel playlist }) { await Mediator.Send(new AddShowToPlaylist(playlist.Id, ShowId), _cts.Token); NavigationManager.NavigateTo($"media/playlists/{playlist.Id}"); } } private async Task AddToSchedule() { var parameters = new DialogParameters { { "EntityType", "show" }, { "EntityName", _show.Title } }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; IDialogReference dialog = await Dialog.ShowAsync("Add To Schedule", parameters, options); DialogResult result = await dialog.Result; if (result is { Canceled: false, Data: ProgramScheduleViewModel schedule }) { await Mediator.Send(AddProgramScheduleItem.ForMediaItem(schedule.Id, CollectionType.TelevisionShow, ShowId), _cts.Token); NavigationManager.NavigateTo($"schedules/{schedule.Id}/items"); } } private async Task AddSeasonToCollection(MediaCardViewModel card) { if (card is TelevisionSeasonCardViewModel season) { var parameters = new DialogParameters { { "EntityType", "season" }, { "EntityName", season.Title } }; var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; IDialogReference dialog = await Dialog.ShowAsync("Add To Collection", parameters, options); DialogResult result = await dialog.Result; if (result is { Canceled: false, Data: MediaCollectionViewModel collection }) { var request = new AddSeasonToCollection(collection.Id, season.TelevisionSeasonId); Either addResult = await Mediator.Send(request, _cts.Token); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding season to collection: {error.Value}", Severity.Error); Logger.LogError("Unexpected error adding season to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {season.Title} to collection {collection.Name}", Severity.Success)); } } } private static string GetPosterUrl(string poster) { if (string.IsNullOrWhiteSpace(poster)) { return poster; } return poster.StartsWith("http://") || poster.StartsWith("https://") ? poster : $"artwork/posters/{poster}"; } private async Task ScanShow(bool deepScan) { bool result = await Mediator.Send(new QueueShowScanByLibraryId(_show.LibraryId, _show.Id, _show.Title, deepScan)); if (!result) { Snackbar.Add($"Unable to scan show {_show.Title}", Severity.Error); } else { Snackbar.Add($"Done scanning show {_show.Title}", Severity.Success); StateHasChanged(); } } }