@page "/search" @inherits MultiSelectBase @inject NavigationManager NavigationManager @inject PersistentComponentState ApplicationState @using ErsatzTV.Application.MediaCards @using ErsatzTV.Application.MediaCollections @using ErsatzTV.Application.Search @using ErsatzTV.Extensions @implements IDisposable
@if (IsSelectMode()) {
@SelectionLabel()
Add To Collection Add To Playlist Clear Selection
} else {
@_query @if (_movies?.Count > 0) { @_movies.Count Movies } @if (_shows?.Count > 0) { @_shows.Count Shows } @if (_seasons?.Count > 0) { @_seasons.Count Seasons } @if (_episodes?.Count > 0) { @_episodes.Count Episodes } @if (_artists?.Count > 0) { @_artists.Count Artists } @if (_musicVideos?.Count > 0) { @_musicVideos.Count Music Videos } @if (_otherVideos?.Count > 0) { @_otherVideos.Count Other Videos } @if (_songs?.Count > 0) { @_songs.Count Songs } @if (_images?.Count > 0) { @_images.Count Images } @if (_remoteStreams?.Count > 0) { @_remoteStreams.Count Remote Streams }
Add All Add All Save As
}
@if (_movies?.Count > 0) {
Movies @if (_movies.Count > 50) { See All >> }
@foreach (MovieCardViewModel card in _movies.Cards.OrderBy(m => m.SortTitle)) { } } @if (_shows?.Count > 0) {
Shows @if (_shows.Count > 50) { See All >> }
@foreach (TelevisionShowCardViewModel card in _shows.Cards.OrderBy(s => s.SortTitle)) { } } @if (_seasons?.Count > 0) {
Seasons @if (_seasons.Count > 50) { See All >> }
@foreach (TelevisionSeasonCardViewModel card in _seasons.Cards.OrderBy(s => s.SortTitle)) { } } @if (_episodes?.Count > 0) {
Episodes @if (_episodes.Count > 50) { See All >> }
@foreach (TelevisionEpisodeCardViewModel card in _episodes.Cards.OrderBy(s => s.SortTitle)) { } } @if (_artists?.Count > 0) {
Artists @if (_artists.Count > 50) { See All >> }
@foreach (ArtistCardViewModel card in _artists.Cards.OrderBy(s => s.SortTitle)) { } } @if (_musicVideos?.Count > 0) {
Music Videos @if (_musicVideos.Count > 50) { See All >> }
@foreach (MusicVideoCardViewModel card in _musicVideos.Cards.OrderBy(s => s.SortTitle)) { } } @if (_otherVideos?.Count > 0) {
Other Videos @if (_otherVideos.Count > 50) { See All >> }
@foreach (OtherVideoCardViewModel card in _otherVideos.Cards.OrderBy(s => s.SortTitle)) { } } @if (_songs?.Count > 0) {
Songs @if (_songs.Count > 50) { See All >> }
@foreach (SongCardViewModel card in _songs.Cards.OrderBy(s => s.SortTitle)) { } } @if (_images?.Count > 0) {
Images @if (_images.Count > 50) { See All >> }
@foreach (ImageCardViewModel card in _images.Cards.OrderBy(s => s.SortTitle)) { } } @if (_remoteStreams?.Count > 0) {
Remote Streams @if (_remoteStreams.Count > 50) { See All >> }
@foreach (RemoteStreamCardViewModel card in _remoteStreams.Cards.OrderBy(s => s.SortTitle)) { } }
@code { private string _query; private MovieCardResultsViewModel _movies; private TelevisionShowCardResultsViewModel _shows; private TelevisionSeasonCardResultsViewModel _seasons; private TelevisionEpisodeCardResultsViewModel _episodes; private MusicVideoCardResultsViewModel _musicVideos; private OtherVideoCardResultsViewModel _otherVideos; private SongCardResultsViewModel _songs; private ImageCardResultsViewModel _images; private RemoteStreamCardResultsViewModel _remoteStreams; private ArtistCardResultsViewModel _artists; private PersistingComponentStateSubscription _persistingSubscription; protected override async Task OnInitializedAsync() { _persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData); _query = NavigationManager.Uri.GetSearchQuery(); if (!string.IsNullOrWhiteSpace(_query)) { if (!ApplicationState.TryTakeFromJson("_movies", out MovieCardResultsViewModel restoredMovies)) { _movies = await Mediator.Send(new QuerySearchIndexMovies($"type:movie AND ({_query})", 1, 50), CancellationToken); } else { _movies = restoredMovies; } if (!ApplicationState.TryTakeFromJson("_shows", out TelevisionShowCardResultsViewModel restoredShows)) { _shows = await Mediator.Send(new QuerySearchIndexShows($"type:show AND ({_query})", 1, 50), CancellationToken); } else { _shows = restoredShows; } if (!ApplicationState.TryTakeFromJson("_seasons", out TelevisionSeasonCardResultsViewModel restoredSeasons)) { _seasons = await Mediator.Send(new QuerySearchIndexSeasons($"type:season AND ({_query})", 1, 50), CancellationToken); } else { _seasons = restoredSeasons; } if (!ApplicationState.TryTakeFromJson("_episodes", out TelevisionEpisodeCardResultsViewModel restoredEpisodes)) { _episodes = await Mediator.Send(new QuerySearchIndexEpisodes($"type:episode AND ({_query})", 1, 50), CancellationToken); } else { _episodes = restoredEpisodes; } if (!ApplicationState.TryTakeFromJson("_musicVideos", out MusicVideoCardResultsViewModel restoredMusicVideos)) { _musicVideos = await Mediator.Send(new QuerySearchIndexMusicVideos($"type:music_video AND ({_query})", 1, 50), CancellationToken); } else { _musicVideos = restoredMusicVideos; } if (!ApplicationState.TryTakeFromJson("_otherVideos", out OtherVideoCardResultsViewModel restoredOtherVideos)) { _otherVideos = await Mediator.Send(new QuerySearchIndexOtherVideos($"type:other_video AND ({_query})", 1, 50), CancellationToken); } else { _otherVideos = restoredOtherVideos; } if (!ApplicationState.TryTakeFromJson("_songs", out SongCardResultsViewModel restoredSongs)) { _songs = await Mediator.Send(new QuerySearchIndexSongs($"type:song AND ({_query})", 1, 50), CancellationToken); } else { _songs = restoredSongs; } if (!ApplicationState.TryTakeFromJson("_images", out ImageCardResultsViewModel restoredImages)) { _images = await Mediator.Send(new QuerySearchIndexImages($"type:image AND ({_query})", 1, 50), CancellationToken); } else { _images = restoredImages; } if (!ApplicationState.TryTakeFromJson("_remoteStreams", out RemoteStreamCardResultsViewModel restoredRemoteStreams)) { _remoteStreams = await Mediator.Send(new QuerySearchIndexRemoteStreams($"type:remote_stream AND ({_query})", 1, 50), CancellationToken); } else { _remoteStreams = restoredRemoteStreams; } if (!ApplicationState.TryTakeFromJson("_artists", out ArtistCardResultsViewModel restoredArtists)) { _artists = await Mediator.Send(new QuerySearchIndexArtists($"type:artist AND ({_query})", 1, 50), CancellationToken); } else { _artists = restoredArtists; } } } private Task PersistData() { ApplicationState.PersistAsJson("_movies", _movies); ApplicationState.PersistAsJson("_shows", _shows); ApplicationState.PersistAsJson("_seasons", _seasons); ApplicationState.PersistAsJson("_episodes", _episodes); ApplicationState.PersistAsJson("_musicVideos", _musicVideos); ApplicationState.PersistAsJson("_otherVideos", _otherVideos); ApplicationState.PersistAsJson("_songs", _songs); ApplicationState.PersistAsJson("_images", _images); ApplicationState.PersistAsJson("_remoteStreams", _remoteStreams); ApplicationState.PersistAsJson("_artists", _artists); return Task.CompletedTask; } void IDisposable.Dispose() { _persistingSubscription.Dispose(); base.Dispose(); } private void SelectClicked(MediaCardViewModel card, MouseEventArgs e) { List GetSortedItems() { return _movies.Cards.OrderBy(m => m.SortTitle) .Append(_shows.Cards.OrderBy(s => s.SortTitle)) .Append(_seasons.Cards.OrderBy(s => s.SortTitle)) .Append(_episodes.Cards.OrderBy(ep => ep.SortTitle)) .Append(_artists.Cards.OrderBy(a => a.SortTitle)) .Append(_musicVideos.Cards.OrderBy(mv => mv.SortTitle)) .Append(_otherVideos.Cards.OrderBy(ov => ov.SortTitle)) .Append(_images.Cards.OrderBy(ov => ov.SortTitle)) .Append(_remoteStreams.Cards.OrderBy(ov => ov.SortTitle)) .ToList(); } SelectClicked(GetSortedItems, card, e); } private async Task AddToCollection(MediaCardViewModel card) { if (card is MovieCardViewModel movie) { var parameters = new DialogParameters { { "EntityType", "movie" }, { "EntityName", movie.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 AddMovieToCollection(collection.Id, movie.MovieId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding movie to collection: {error.Value}"); Logger.LogError("Unexpected error adding movie to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {movie.Title} to collection {collection.Name}", Severity.Success)); } } if (card is TelevisionShowCardViewModel show) { 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 }) { var request = new AddShowToCollection(collection.Id, show.TelevisionShowId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding show to collection: {error.Value}"); Logger.LogError("Unexpected error adding show to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {show.Title} to collection {collection.Name}", Severity.Success)); } } if (card is TelevisionEpisodeCardViewModel episode) { var parameters = new DialogParameters { { "EntityType", "episode" }, { "EntityName", episode.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 AddEpisodeToCollection(collection.Id, episode.EpisodeId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding episode to collection: {error.Value}"); Logger.LogError("Unexpected error adding episode to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {episode.Title} to collection {collection.Name}", Severity.Success)); } } if (card is TelevisionSeasonCardViewModel season) { var parameters = new DialogParameters { { "EntityType", "season" }, { "EntityName", $"{season.Title} Season {season.TelevisionSeasonNumber}" } }; 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, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding season to collection: {error.Value}"); Logger.LogError("Unexpected error adding season to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {season.Title} Season {season.TelevisionSeasonNumber} to collection {collection.Name}", Severity.Success)); } } if (card is ArtistCardViewModel artist) { var parameters = new DialogParameters { { "EntityType", "artist" }, { "EntityName", artist.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 AddArtistToCollection(collection.Id, artist.ArtistId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding artist to collection: {error.Value}"); Logger.LogError("Unexpected error adding artist to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {artist.Title} to collection {collection.Name}", Severity.Success)); } } if (card is MusicVideoCardViewModel musicVideo) { var parameters = new DialogParameters { { "EntityType", "music video" }, { "EntityName", musicVideo.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 AddMusicVideoToCollection(collection.Id, musicVideo.MusicVideoId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding music video to collection: {error.Value}"); Logger.LogError("Unexpected error adding music video to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {musicVideo.Title} to collection {collection.Name}", Severity.Success)); } } if (card is OtherVideoCardViewModel otherVideo) { var parameters = new DialogParameters { { "EntityType", "other video" }, { "EntityName", otherVideo.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 AddOtherVideoToCollection(collection.Id, otherVideo.OtherVideoId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding other video to collection: {error.Value}"); Logger.LogError("Unexpected error adding other video to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {otherVideo.Title} to collection {collection.Name}", Severity.Success)); } } if (card is SongCardViewModel song) { var parameters = new DialogParameters { { "EntityType", "song" }, { "EntityName", song.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 AddSongToCollection(collection.Id, song.SongId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding song to collection: {error.Value}"); Logger.LogError("Unexpected error adding song to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {song.Title} to collection {collection.Name}", Severity.Success)); } } if (card is ImageCardViewModel image) { var parameters = new DialogParameters { { "EntityType", "image" }, { "EntityName", image.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 AddImageToCollection(collection.Id, image.ImageId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding image to collection: {error.Value}"); Logger.LogError("Unexpected error adding image to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {image.Title} to collection {collection.Name}", Severity.Success)); } } if (card is RemoteStreamCardViewModel remoteStream) { var parameters = new DialogParameters { { "EntityType", "remote stream" }, { "EntityName", remoteStream.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 AddMediaItemToCollection(collection.Id, remoteStream.RemoteStreamId); Either addResult = await Mediator.Send(request, CancellationToken); addResult.Match( Left: error => { Snackbar.Add($"Unexpected error adding remote stream to collection: {error.Value}"); Logger.LogError("Unexpected error adding remote stream to collection: {Error}", error.Value); }, Right: _ => Snackbar.Add($"Added {remoteStream.Title} to collection {collection.Name}", Severity.Success)); } } } private string GetMoviesLink() { var uri = "media/movies/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetShowsLink() { var uri = "media/tv/shows/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetSeasonsLink() { var uri = "media/tv/seasons/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetEpisodesLink() { var uri = "media/tv/episodes/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetArtistsLink() { var uri = "media/music/artists/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetMusicVideosLink() { var uri = "media/music/videos/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetOtherVideosLink() { var uri = "media/other/videos/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetSongsLink() { var uri = "media/music/songs/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetImagesLink() { var uri = "media/images/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private string GetRemoteStreamsLink() { var uri = "media/remote/streams/page/1"; if (!string.IsNullOrWhiteSpace(_query)) { (string key, string value) = _query.EncodeQuery(); uri = $"{uri}?{key}={value}"; } return uri; } private async Task AddAllToCollection(MouseEventArgs _) { SearchResultAllItemsViewModel results = await Mediator.Send(new QuerySearchIndexAllItems(_query), CancellationToken); await AddItemsToCollection( results.MovieIds, results.ShowIds, results.SeasonIds, results.EpisodeIds, results.ArtistIds, results.MusicVideoIds, results.OtherVideoIds, results.SongIds, results.ImageIds, results.RemoteStreamIds, "search results"); } private async Task AddAllToPlaylist(MouseEventArgs _) { SearchResultAllItemsViewModel results = await Mediator.Send(new QuerySearchIndexAllItems(_query), CancellationToken); await AddItemsToPlaylist( results.MovieIds, results.ShowIds, results.SeasonIds, results.EpisodeIds, results.ArtistIds, results.MusicVideoIds, results.OtherVideoIds, results.SongIds, results.ImageIds, results.RemoteStreamIds, "search results"); } private async Task SaveAsSmartCollection(MouseEventArgs _) { var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.ExtraSmall }; var parameters = new DialogParameters { { x => x.Query, _query } }; IDialogReference dialog = await Dialog.ShowAsync("Save As Smart Collection", parameters, options); DialogResult result = await dialog.Result; if (result is { Canceled: false, Data: Tuple collectionResult }) { // Item2 => update is needed if (collectionResult.Item2) { var request = new UpdateSmartCollection( collectionResult.Item1.Id, collectionResult.Item1.Name, _query); Either updateResult = await Mediator.Send(request, CancellationToken); updateResult.Match( Left: error => { Snackbar.Add($"Unexpected error saving smart collection: {error.Value}"); Logger.LogError("Unexpected error saving smart collection: {Error}", error.Value); }, Right: _ => { Snackbar.Add( $"Saved smart collection {collectionResult.Item1.Name}", Severity.Success); ClearSelection(); }); } } } }