Browse Source

add movie, show, artist language buttons. search by english language name (#162)

pull/163/head
Jason Dove 5 years ago committed by GitHub
parent
commit
237729e79d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      ErsatzTV.Application/Artists/ArtistViewModel.cs
  2. 25
      ErsatzTV.Application/Artists/Mapper.cs
  3. 26
      ErsatzTV.Application/Artists/Queries/GetArtistByIdHandler.cs
  4. 2
      ErsatzTV.Application/MediaItems/Queries/GetAllLanguageCodesHandler.cs
  5. 25
      ErsatzTV.Application/Movies/Mapper.cs
  6. 4
      ErsatzTV.Application/Movies/MovieViewModel.cs
  7. 24
      ErsatzTV.Application/Television/Mapper.cs
  8. 28
      ErsatzTV.Application/Television/Queries/GetTelevisionShowByIdHandler.cs
  9. 4
      ErsatzTV.Application/Television/TelevisionShowViewModel.cs
  10. 2
      ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs
  11. 50
      ErsatzTV.Infrastructure/Search/SearchIndex.cs
  12. 11
      ErsatzTV/Pages/Artist.razor
  13. 11
      ErsatzTV/Pages/Movie.razor
  14. 11
      ErsatzTV/Pages/TelevisionSeasonList.razor

4
ErsatzTV.Application/Artists/ArtistViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Globalization;
namespace ErsatzTV.Application.Artists
{
@ -10,5 +11,6 @@ namespace ErsatzTV.Application.Artists @@ -10,5 +11,6 @@ namespace ErsatzTV.Application.Artists
string FanArt,
List<string> Genres,
List<string> Styles,
List<string> Moods);
List<string> Moods,
List<CultureInfo> Languages);
}

25
ErsatzTV.Application/Artists/Mapper.cs

@ -1,12 +1,16 @@ @@ -1,12 +1,16 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using ErsatzTV.Core.Domain;
using LanguageExt;
using static LanguageExt.Prelude;
namespace ErsatzTV.Application.Artists
{
internal static class Mapper
{
internal static ArtistViewModel ProjectToViewModel(Artist artist)
internal static ArtistViewModel ProjectToViewModel(Artist artist, List<string> languages)
{
ArtistMetadata metadata = Optional(artist.ArtistMetadata).Flatten().Head();
return new ArtistViewModel(
@ -17,11 +21,26 @@ namespace ErsatzTV.Application.Artists @@ -17,11 +21,26 @@ namespace ErsatzTV.Application.Artists
Artwork(metadata, ArtworkKind.FanArt),
metadata.Genres.Map(g => g.Name).ToList(),
metadata.Styles.Map(s => s.Name).ToList(),
metadata.Moods.Map(m => m.Name).ToList());
metadata.Moods.Map(m => m.Name).ToList(),
LanguagesForArtist(languages));
}
private static string Artwork(Metadata metadata, ArtworkKind artworkKind) =>
Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == artworkKind))
.Match(a => a.Path, string.Empty);
private static List<CultureInfo> LanguagesForArtist(List<string> languages)
{
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
return languages
.Distinct()
.Map(
lang => allCultures.Filter(
ci => string.Equals(ci.ThreeLetterISOLanguageName, lang, StringComparison.OrdinalIgnoreCase)))
.Sequence()
.Flatten()
.ToList();
}
}
}

26
ErsatzTV.Application/Artists/Queries/GetArtistByIdHandler.cs

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
using System.Threading;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
@ -10,12 +12,26 @@ namespace ErsatzTV.Application.Artists.Queries @@ -10,12 +12,26 @@ namespace ErsatzTV.Application.Artists.Queries
public class GetArtistByIdHandler : IRequestHandler<GetArtistById, Option<ArtistViewModel>>
{
private readonly IArtistRepository _artistRepository;
private readonly ISearchRepository _searchRepository;
public GetArtistByIdHandler(IArtistRepository artistRepository) => _artistRepository = artistRepository;
public GetArtistByIdHandler(IArtistRepository artistRepository, ISearchRepository searchRepository)
{
_artistRepository = artistRepository;
_searchRepository = searchRepository;
}
public Task<Option<ArtistViewModel>> Handle(
public async Task<Option<ArtistViewModel>> Handle(
GetArtistById request,
CancellationToken cancellationToken) =>
_artistRepository.GetArtist(request.ArtistId).MapT(ProjectToViewModel);
CancellationToken cancellationToken)
{
Option<Artist> maybeArtist = await _artistRepository.GetArtist(request.ArtistId);
return await maybeArtist.Match<Task<Option<ArtistViewModel>>>(
async artist =>
{
List<string> languages = await _searchRepository.GetLanguagesForArtist(artist);
return ProjectToViewModel(artist, languages);
},
() => Task.FromResult(Option<ArtistViewModel>.None));
}
}
}

2
ErsatzTV.Application/MediaItems/Queries/GetAllLanguageCodesHandler.cs

@ -20,7 +20,7 @@ namespace ErsatzTV.Application.MediaItems.Queries @@ -20,7 +20,7 @@ namespace ErsatzTV.Application.MediaItems.Queries
{
var result = new List<CultureInfo>();
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
List<string> allLanguageCodes = await _mediaItemRepository.GetAllLanguageCodes();
foreach (string code in allLanguageCodes)
{

25
ErsatzTV.Application/Movies/Mapper.cs

@ -1,5 +1,9 @@ @@ -1,5 +1,9 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using ErsatzTV.Core.Domain;
using LanguageExt;
using static LanguageExt.Prelude;
namespace ErsatzTV.Application.Movies
@ -17,7 +21,24 @@ namespace ErsatzTV.Application.Movies @@ -17,7 +21,24 @@ namespace ErsatzTV.Application.Movies
Artwork(metadata, ArtworkKind.FanArt),
metadata.Genres.Map(g => g.Name).ToList(),
metadata.Tags.Map(t => t.Name).ToList(),
metadata.Studios.Map(s => s.Name).ToList());
metadata.Studios.Map(s => s.Name).ToList(),
LanguagesForMovie(movie));
}
private static List<CultureInfo> LanguagesForMovie(Movie movie)
{
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
return movie.MediaVersions
.Map(mv => mv.Streams.Filter(s => s.MediaStreamKind == MediaStreamKind.Audio).Map(s => s.Language))
.Flatten()
.Distinct()
.Map(
lang => allCultures.Filter(
ci => string.Equals(ci.ThreeLetterISOLanguageName, lang, StringComparison.OrdinalIgnoreCase)))
.Sequence()
.Flatten()
.ToList();
}
private static string Artwork(Metadata metadata, ArtworkKind artworkKind) =>

4
ErsatzTV.Application/Movies/MovieViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Globalization;
namespace ErsatzTV.Application.Movies
{
@ -10,5 +11,6 @@ namespace ErsatzTV.Application.Movies @@ -10,5 +11,6 @@ namespace ErsatzTV.Application.Movies
string FanArt,
List<string> Genres,
List<string> Tags,
List<string> Studios);
List<string> Studios,
List<CultureInfo> Languages);
}

24
ErsatzTV.Application/Television/Mapper.cs

@ -1,13 +1,16 @@ @@ -1,13 +1,16 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using ErsatzTV.Core.Domain;
using LanguageExt;
using static LanguageExt.Prelude;
namespace ErsatzTV.Application.Television
{
internal static class Mapper
{
internal static TelevisionShowViewModel ProjectToViewModel(Show show) =>
internal static TelevisionShowViewModel ProjectToViewModel(Show show, List<string> languages) =>
new(
show.Id,
show.ShowMetadata.HeadOrNone().Map(m => m.Title ?? string.Empty).IfNone(string.Empty),
@ -18,7 +21,8 @@ namespace ErsatzTV.Application.Television @@ -18,7 +21,8 @@ namespace ErsatzTV.Application.Television
show.ShowMetadata.HeadOrNone().Map(m => m.Genres.Map(g => g.Name).ToList()).IfNone(new List<string>()),
show.ShowMetadata.HeadOrNone().Map(m => m.Tags.Map(g => g.Name).ToList()).IfNone(new List<string>()),
show.ShowMetadata.HeadOrNone().Map(m => m.Studios.Map(s => s.Name).ToList())
.IfNone(new List<string>()));
.IfNone(new List<string>()),
LanguagesForShow(languages));
internal static TelevisionSeasonViewModel ProjectToViewModel(Season season) =>
new(
@ -48,5 +52,19 @@ namespace ErsatzTV.Application.Television @@ -48,5 +52,19 @@ namespace ErsatzTV.Application.Television
private static string GetArtwork(Metadata metadata, ArtworkKind artworkKind) =>
Optional(metadata.Artwork.FirstOrDefault(a => a.ArtworkKind == artworkKind))
.Match(a => a.Path, string.Empty);
private static List<CultureInfo> LanguagesForShow(List<string> languages)
{
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.NeutralCultures);
return languages
.Distinct()
.Map(
lang => allCultures.Filter(
ci => string.Equals(ci.ThreeLetterISOLanguageName, lang, StringComparison.OrdinalIgnoreCase)))
.Sequence()
.Flatten()
.ToList();
}
}
}

28
ErsatzTV.Application/Television/Queries/GetTelevisionShowByIdHandler.cs

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
using System.Threading;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using MediatR;
@ -9,15 +11,29 @@ namespace ErsatzTV.Application.Television.Queries @@ -9,15 +11,29 @@ namespace ErsatzTV.Application.Television.Queries
{
public class GetTelevisionShowByIdHandler : IRequestHandler<GetTelevisionShowById, Option<TelevisionShowViewModel>>
{
private readonly ISearchRepository _searchRepository;
private readonly ITelevisionRepository _televisionRepository;
public GetTelevisionShowByIdHandler(ITelevisionRepository televisionRepository) =>
public GetTelevisionShowByIdHandler(
ITelevisionRepository televisionRepository,
ISearchRepository searchRepository)
{
_televisionRepository = televisionRepository;
_searchRepository = searchRepository;
}
public Task<Option<TelevisionShowViewModel>> Handle(
public async Task<Option<TelevisionShowViewModel>> Handle(
GetTelevisionShowById request,
CancellationToken cancellationToken) =>
_televisionRepository.GetShow(request.Id)
.MapT(ProjectToViewModel);
CancellationToken cancellationToken)
{
Option<Show> maybeShow = await _televisionRepository.GetShow(request.Id);
return await maybeShow.Match<Task<Option<TelevisionShowViewModel>>>(
async show =>
{
List<string> languages = await _searchRepository.GetLanguagesForShow(show);
return ProjectToViewModel(show, languages);
},
() => Task.FromResult(Option<TelevisionShowViewModel>.None));
}
}
}

4
ErsatzTV.Application/Television/TelevisionShowViewModel.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Globalization;
namespace ErsatzTV.Application.Television
{
@ -11,5 +12,6 @@ namespace ErsatzTV.Application.Television @@ -11,5 +12,6 @@ namespace ErsatzTV.Application.Television
string FanArt,
List<string> Genres,
List<string> Tags,
List<string> Studios);
List<string> Studios,
List<CultureInfo> Languages);
}

2
ErsatzTV.Infrastructure/Data/Repositories/MovieRepository.cs

@ -43,6 +43,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories @@ -43,6 +43,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
.ThenInclude(m => m.Tags)
.Include(m => m.MovieMetadata)
.ThenInclude(m => m.Studios)
.Include(m => m.MediaVersions)
.ThenInclude(mv => mv.Streams)
.OrderBy(m => m.Id)
.SingleOrDefaultAsync(m => m.Id == movieId)
.Map(Optional);

50
ErsatzTV.Infrastructure/Search/SearchIndex.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using ErsatzTV.Core;
@ -47,15 +48,20 @@ namespace ErsatzTV.Infrastructure.Search @@ -47,15 +48,20 @@ namespace ErsatzTV.Infrastructure.Search
private const string ShowType = "show";
private const string ArtistType = "artist";
private const string MusicVideoType = "music_video";
private readonly List<CultureInfo> _cultureInfos;
private readonly ILogger<SearchIndex> _logger;
private FSDirectory _directory;
private IndexWriter _writer;
public SearchIndex(ILogger<SearchIndex> logger) => _logger = logger;
public SearchIndex(ILogger<SearchIndex> logger)
{
_logger = logger;
_cultureInfos = CultureInfo.GetCultures(CultureTypes.NeutralCultures).ToList();
}
public int Version => 6;
public int Version => 7;
public Task<bool> Initialize(ILocalFileSystem localFileSystem)
{
@ -296,11 +302,17 @@ namespace ErsatzTV.Infrastructure.Search @@ -296,11 +302,17 @@ namespace ErsatzTV.Infrastructure.Search
if (maybeVersion.IsSome)
{
MediaVersion version = maybeVersion.ValueUnsafe();
foreach (string lang in version.Streams.Filter(ms => ms.MediaStreamKind == MediaStreamKind.Video)
foreach (CultureInfo cultureInfo in version.Streams
.Filter(ms => ms.MediaStreamKind == MediaStreamKind.Audio)
.Map(ms => ms.Language).Distinct()
.Filter(s => !string.IsNullOrWhiteSpace(s)))
.Filter(s => !string.IsNullOrWhiteSpace(s))
.Map(
l => _cultureInfos.Filter(
c => string.Equals(c.ThreeLetterISOLanguageName, l, StringComparison.OrdinalIgnoreCase)))
.Sequence()
.Flatten())
{
doc.Add(new StringField(LanguageField, lang, Field.Store.NO));
doc.Add(new TextField(LanguageField, cultureInfo.EnglishName, Field.Store.NO));
}
}
}
@ -326,9 +338,19 @@ namespace ErsatzTV.Infrastructure.Search @@ -326,9 +338,19 @@ namespace ErsatzTV.Infrastructure.Search
};
List<string> languages = await searchRepository.GetLanguagesForShow(show);
foreach (string lang in languages.Distinct().Filter(s => !string.IsNullOrWhiteSpace(s)))
foreach (CultureInfo cultureInfo in languages
.Distinct()
.Filter(s => !string.IsNullOrWhiteSpace(s))
.Map(
l => _cultureInfos.Filter(
c => string.Equals(
c.ThreeLetterISOLanguageName,
l,
StringComparison.OrdinalIgnoreCase)))
.Sequence()
.Flatten())
{
doc.Add(new StringField(LanguageField, lang, Field.Store.NO));
doc.Add(new TextField(LanguageField, cultureInfo.EnglishName, Field.Store.NO));
}
if (metadata.ReleaseDate.HasValue)
@ -391,9 +413,19 @@ namespace ErsatzTV.Infrastructure.Search @@ -391,9 +413,19 @@ namespace ErsatzTV.Infrastructure.Search
};
List<string> languages = await searchRepository.GetLanguagesForArtist(artist);
foreach (string lang in languages.Distinct().Filter(s => !string.IsNullOrWhiteSpace(s)))
foreach (CultureInfo cultureInfo in languages
.Distinct()
.Filter(s => !string.IsNullOrWhiteSpace(s))
.Map(
l => _cultureInfos.Filter(
c => string.Equals(
c.ThreeLetterISOLanguageName,
l,
StringComparison.OrdinalIgnoreCase)))
.Sequence()
.Flatten())
{
doc.Add(new StringField(LanguageField, lang, Field.Store.NO));
doc.Add(new TextField(LanguageField, cultureInfo.EnglishName, Field.Store.NO));
}
foreach (Genre genre in metadata.Genres)

11
ErsatzTV/Pages/Artist.razor

@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
@using ErsatzTV.Application.MediaCards.Queries
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@using System.Globalization
@using Unit = LanguageExt.Unit
@inject IMediator Mediator
@inject IDialogService Dialog
@ -57,6 +58,16 @@ @@ -57,6 +58,16 @@
</div>
</div>
</div>
@if (_artist.Languages.Any())
{
<MudText GutterBottom="true">Languages</MudText>
<div class="mb-2">
@foreach (CultureInfo language in _artist.Languages.OrderBy(l => l.EnglishName))
{
<MudFab Color="Color.Info" Size="Size.Small" Label="@language.EnglishName" Class="mr-2 mb-2" Link="@($"/search?query=language%3a%22{Uri.EscapeDataString(language.EnglishName.ToLowerInvariant())}%22")"/>
}
</div>
}
@if (_artist.Genres.Any())
{
<MudText GutterBottom="true">Genres</MudText>

11
ErsatzTV/Pages/Movie.razor

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
@page "/media/movies/{MovieId:int}"
@using ErsatzTV.Application.Movies
@using ErsatzTV.Application.Movies.Queries
@using System.Globalization
@using ErsatzTV.Application.MediaCollections
@using ErsatzTV.Application.MediaCollections.Commands
@inject IMediator Mediator
@ -43,6 +44,16 @@ @@ -43,6 +44,16 @@
</div>
</div>
</div>
@if (_movie.Languages.Any())
{
<MudText GutterBottom="true">Languages</MudText>
<div class="mb-2">
@foreach (CultureInfo language in _movie.Languages.OrderBy(l => l.EnglishName))
{
<MudFab Color="Color.Info" Size="Size.Small" Label="@language.EnglishName" Class="mr-2 mb-2" Link="@($"/search?query=language%3a%22{Uri.EscapeDataString(language.EnglishName.ToLowerInvariant())}%22")"/>
}
</div>
}
@if (_movie.Studios.Any())
{
<MudText GutterBottom="true">Studios</MudText>

11
ErsatzTV/Pages/TelevisionSeasonList.razor

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
@using ErsatzTV.Application.MediaCollections.Commands
@using ErsatzTV.Application.ProgramSchedules
@using ErsatzTV.Application.ProgramSchedules.Commands
@using System.Globalization
@using Unit = LanguageExt.Unit
@inject IMediator Mediator
@inject ILogger<TelevisionSeasonList> Logger
@ -58,6 +59,16 @@ @@ -58,6 +59,16 @@
</div>
</div>
</div>
@if (_show.Languages.Any())
{
<MudText GutterBottom="true">Languages</MudText>
<div class="mb-2">
@foreach (CultureInfo language in _show.Languages.OrderBy(l => l.EnglishName))
{
<MudFab Color="Color.Info" Size="Size.Small" Label="@language.EnglishName" Class="mr-2 mb-2" Link="@($"/search?query=language%3a%22{Uri.EscapeDataString(language.EnglishName.ToLowerInvariant())}%22")"/>
}
</div>
}
@if (_show.Studios.Any())
{
<MudText GutterBottom="true">Studios</MudText>

Loading…
Cancel
Save