fix languages that have multiple codes (#270)
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
using ErsatzTV.Core.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Data.Configurations
|
||||
{
|
||||
public class LanguageCodeConfiguration : IEntityTypeConfiguration<LanguageCode>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<LanguageCode> builder) => builder.ToTable("LanguageCode");
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
@@ -12,6 +14,43 @@ namespace ErsatzTV.Infrastructure.Data
|
||||
{
|
||||
public static async Task<Unit> Initialize(TvContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!context.LanguageCodes.Any())
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
if (assembly != null)
|
||||
{
|
||||
await using Stream resource =
|
||||
assembly.GetManifestResourceStream("ErsatzTV.Resources.ISO-639-2_utf-8.txt");
|
||||
if (resource != null)
|
||||
{
|
||||
using var reader = new StreamReader(resource);
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string line = await reader.ReadLineAsync();
|
||||
if (line != null)
|
||||
{
|
||||
string[] split = line.Split("|");
|
||||
if (split.Length == 5)
|
||||
{
|
||||
var languageCode = new LanguageCode
|
||||
{
|
||||
ThreeCode1 = split[0],
|
||||
ThreeCode2 = split[1],
|
||||
TwoCode = split[2],
|
||||
EnglishName = split[3],
|
||||
FrenchName = split[4]
|
||||
};
|
||||
|
||||
await context.LanguageCodes.AddAsync(languageCode, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
if (context.Resolutions.Any())
|
||||
{
|
||||
return Unit.Default;
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Infrastructure.Extensions;
|
||||
using LanguageExt;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using static LanguageExt.Prelude;
|
||||
@@ -110,5 +111,11 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
INNER JOIN Artist A on MV.ArtistId = A.Id
|
||||
WHERE MediaStreamKind = 2 AND A.Id = @ArtistId",
|
||||
new { ArtistId = artist.Id }).Map(result => result.ToList());
|
||||
|
||||
public async Task<List<string>> GetAllLanguageCodes(List<string> mediaCodes)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
return await dbContext.LanguageCodes.GetAllLanguageCodes(mediaCodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace ErsatzTV.Infrastructure.Data
|
||||
public DbSet<PlayoutProgramScheduleAnchor> PlayoutProgramScheduleItemAnchors { get; set; }
|
||||
public DbSet<FFmpegProfile> FFmpegProfiles { get; set; }
|
||||
public DbSet<Resolution> Resolutions { get; set; }
|
||||
public DbSet<LanguageCode> LanguageCodes { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
|
||||
optionsBuilder.UseLoggerFactory(_loggerFactory);
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Extensions
|
||||
{
|
||||
public static class LanguageCodeQueryableExtensions
|
||||
{
|
||||
public static async Task<List<string>> GetAllLanguageCodes(
|
||||
this IQueryable<LanguageCode> languageCodes,
|
||||
string mediaCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(mediaCode))
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
string code = mediaCode.ToLowerInvariant();
|
||||
|
||||
List<LanguageCode> maybeLanguages = await languageCodes
|
||||
.Filter(lc => lc.ThreeCode1 == code || lc.ThreeCode2 == code)
|
||||
.ToListAsync();
|
||||
|
||||
var result = new HashSet<string>();
|
||||
foreach (LanguageCode language in maybeLanguages)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(language.ThreeCode1))
|
||||
{
|
||||
result.Add(language.ThreeCode1);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(language.ThreeCode2))
|
||||
{
|
||||
result.Add(language.ThreeCode2);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToList();
|
||||
}
|
||||
|
||||
public static async Task<List<string>> GetAllLanguageCodes(
|
||||
this IQueryable<LanguageCode> languageCodes,
|
||||
List<string> mediaCodes)
|
||||
{
|
||||
var validCodes = mediaCodes
|
||||
.Filter(c => !string.IsNullOrWhiteSpace(c))
|
||||
.Map(c => c.ToLowerInvariant())
|
||||
.ToList();
|
||||
|
||||
if (validCodes.Count == 0)
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
List<LanguageCode> maybeLanguages = await languageCodes
|
||||
.Filter(lc => validCodes.Contains(lc.ThreeCode1) || validCodes.Contains(lc.ThreeCode2))
|
||||
.ToListAsync();
|
||||
|
||||
var result = new HashSet<string>();
|
||||
foreach (LanguageCode language in maybeLanguages)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(language.ThreeCode1))
|
||||
{
|
||||
result.Add(language.ThreeCode1);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(language.ThreeCode2))
|
||||
{
|
||||
result.Add(language.ThreeCode2);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
+2970
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Add_LanguageCode : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "LanguageCode",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
ThreeCode1 = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ThreeCode2 = table.Column<string>(type: "TEXT", nullable: true),
|
||||
TwoCode = table.Column<string>(type: "TEXT", nullable: true),
|
||||
EnglishName = table.Column<string>(type: "TEXT", nullable: true),
|
||||
FrenchName = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_LanguageCode", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "LanguageCode");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -583,6 +583,32 @@ namespace ErsatzTV.Infrastructure.Migrations
|
||||
b.ToTable("JellyfinPathReplacement");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.LanguageCode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("EnglishName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FrenchName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ThreeCode1")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ThreeCode2")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("TwoCode")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("LanguageCode");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Library", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
public int Version => 13;
|
||||
public int Version => 14;
|
||||
|
||||
public Task<bool> Initialize(ILocalFileSystem localFileSystem)
|
||||
{
|
||||
@@ -91,41 +91,6 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
return Task.FromResult(_initialized);
|
||||
}
|
||||
|
||||
public async Task<Unit> Rebuild(ISearchRepository searchRepository, List<int> itemIds)
|
||||
{
|
||||
_writer.DeleteAll();
|
||||
|
||||
foreach (int id in itemIds)
|
||||
{
|
||||
Option<MediaItem> maybeMediaItem = await searchRepository.GetItemToIndex(id);
|
||||
if (maybeMediaItem.IsSome)
|
||||
{
|
||||
MediaItem mediaItem = maybeMediaItem.ValueUnsafe();
|
||||
switch (mediaItem)
|
||||
{
|
||||
case Movie movie:
|
||||
UpdateMovie(movie);
|
||||
break;
|
||||
case Show show:
|
||||
await UpdateShow(searchRepository, show);
|
||||
break;
|
||||
case Artist artist:
|
||||
await UpdateArtist(searchRepository, artist);
|
||||
break;
|
||||
case MusicVideo musicVideo:
|
||||
UpdateMusicVideo(musicVideo);
|
||||
break;
|
||||
case Episode episode:
|
||||
UpdateEpisode(episode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_writer.Commit();
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
public Task<Unit> AddItems(ISearchRepository searchRepository, List<MediaItem> items) =>
|
||||
UpdateItems(searchRepository, items);
|
||||
|
||||
@@ -136,7 +101,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
switch (item)
|
||||
{
|
||||
case Movie movie:
|
||||
UpdateMovie(movie);
|
||||
await UpdateMovie(searchRepository, movie);
|
||||
break;
|
||||
case Show show:
|
||||
await UpdateShow(searchRepository, show);
|
||||
@@ -145,10 +110,10 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
await UpdateArtist(searchRepository, artist);
|
||||
break;
|
||||
case MusicVideo musicVideo:
|
||||
UpdateMusicVideo(musicVideo);
|
||||
await UpdateMusicVideo(searchRepository, musicVideo);
|
||||
break;
|
||||
case Episode episode:
|
||||
UpdateEpisode(episode);
|
||||
await UpdateEpisode(searchRepository, episode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -217,6 +182,41 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
_directory?.Dispose();
|
||||
}
|
||||
|
||||
public async Task<Unit> Rebuild(ISearchRepository searchRepository, List<int> itemIds)
|
||||
{
|
||||
_writer.DeleteAll();
|
||||
|
||||
foreach (int id in itemIds)
|
||||
{
|
||||
Option<MediaItem> maybeMediaItem = await searchRepository.GetItemToIndex(id);
|
||||
if (maybeMediaItem.IsSome)
|
||||
{
|
||||
MediaItem mediaItem = maybeMediaItem.ValueUnsafe();
|
||||
switch (mediaItem)
|
||||
{
|
||||
case Movie movie:
|
||||
await UpdateMovie(searchRepository, movie);
|
||||
break;
|
||||
case Show show:
|
||||
await UpdateShow(searchRepository, show);
|
||||
break;
|
||||
case Artist artist:
|
||||
await UpdateArtist(searchRepository, artist);
|
||||
break;
|
||||
case MusicVideo musicVideo:
|
||||
await UpdateMusicVideo(searchRepository, musicVideo);
|
||||
break;
|
||||
case Episode episode:
|
||||
await UpdateEpisode(searchRepository, episode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_writer.Commit();
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private static Option<SearchPageMap> GetSearchPageMap(
|
||||
IndexSearcher searcher,
|
||||
Query query,
|
||||
@@ -263,7 +263,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
return new SearchPageMap(map);
|
||||
}
|
||||
|
||||
private void UpdateMovie(Movie movie)
|
||||
private async Task UpdateMovie(ISearchRepository searchRepository, Movie movie)
|
||||
{
|
||||
Option<MovieMetadata> maybeMetadata = movie.MovieMetadata.HeadOrNone();
|
||||
if (maybeMetadata.IsSome)
|
||||
@@ -284,7 +284,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
new StringField(JumpLetterField, GetJumpLetter(metadata), Field.Store.YES)
|
||||
};
|
||||
|
||||
AddLanguages(doc, movie.MediaVersions);
|
||||
await AddLanguages(searchRepository, doc, movie.MediaVersions);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadata.ContentRating))
|
||||
{
|
||||
@@ -349,25 +349,38 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
}
|
||||
}
|
||||
|
||||
private void AddLanguages(Document doc, List<MediaVersion> mediaVersions)
|
||||
private async Task AddLanguages(ISearchRepository searchRepository, Document doc, List<MediaVersion> mediaVersions)
|
||||
{
|
||||
Option<MediaVersion> maybeVersion = mediaVersions.HeadOrNone();
|
||||
if (maybeVersion.IsSome)
|
||||
{
|
||||
MediaVersion version = maybeVersion.ValueUnsafe();
|
||||
foreach (CultureInfo cultureInfo in version.Streams
|
||||
var mediaCodes = version.Streams
|
||||
.Filter(ms => ms.MediaStreamKind == MediaStreamKind.Audio)
|
||||
.Map(ms => ms.Language).Distinct()
|
||||
.Filter(s => !string.IsNullOrWhiteSpace(s))
|
||||
.Map(
|
||||
l => _cultureInfos.Filter(
|
||||
c => string.Equals(c.ThreeLetterISOLanguageName, l, StringComparison.OrdinalIgnoreCase)))
|
||||
.Sequence()
|
||||
.Flatten())
|
||||
.ToList();
|
||||
|
||||
await AddLanguages(searchRepository, doc, mediaCodes);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddLanguages(ISearchRepository searchRepository, Document doc, List<string> mediaCodes)
|
||||
{
|
||||
var englishNames = new System.Collections.Generic.HashSet<string>();
|
||||
foreach (string code in await searchRepository.GetAllLanguageCodes(mediaCodes))
|
||||
{
|
||||
Option<CultureInfo> maybeCultureInfo = _cultureInfos.Find(
|
||||
ci => string.Equals(ci.ThreeLetterISOLanguageName, code, StringComparison.OrdinalIgnoreCase));
|
||||
foreach (CultureInfo cultureInfo in maybeCultureInfo)
|
||||
{
|
||||
doc.Add(new TextField(LanguageField, cultureInfo.EnglishName, Field.Store.NO));
|
||||
englishNames.Add(cultureInfo.EnglishName);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string englishName in englishNames)
|
||||
{
|
||||
doc.Add(new TextField(LanguageField, englishName, Field.Store.NO));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateShow(ISearchRepository searchRepository, Show show)
|
||||
@@ -392,20 +405,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
};
|
||||
|
||||
List<string> languages = await searchRepository.GetLanguagesForShow(show);
|
||||
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 TextField(LanguageField, cultureInfo.EnglishName, Field.Store.NO));
|
||||
}
|
||||
await AddLanguages(searchRepository, doc, languages);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadata.ContentRating))
|
||||
{
|
||||
@@ -482,20 +482,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
};
|
||||
|
||||
List<string> languages = await searchRepository.GetLanguagesForArtist(artist);
|
||||
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 TextField(LanguageField, cultureInfo.EnglishName, Field.Store.NO));
|
||||
}
|
||||
await AddLanguages(searchRepository, doc, languages);
|
||||
|
||||
foreach (Genre genre in metadata.Genres)
|
||||
{
|
||||
@@ -522,7 +509,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMusicVideo(MusicVideo musicVideo)
|
||||
private async Task UpdateMusicVideo(ISearchRepository searchRepository, MusicVideo musicVideo)
|
||||
{
|
||||
Option<MusicVideoMetadata> maybeMetadata = musicVideo.MusicVideoMetadata.HeadOrNone();
|
||||
if (maybeMetadata.IsSome)
|
||||
@@ -543,7 +530,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
new StringField(JumpLetterField, GetJumpLetter(metadata), Field.Store.YES)
|
||||
};
|
||||
|
||||
AddLanguages(doc, musicVideo.MediaVersions);
|
||||
await AddLanguages(searchRepository, doc, musicVideo.MediaVersions);
|
||||
|
||||
if (metadata.ReleaseDate.HasValue)
|
||||
{
|
||||
@@ -584,7 +571,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEpisode(Episode episode)
|
||||
private async Task UpdateEpisode(ISearchRepository searchRepository, Episode episode)
|
||||
{
|
||||
foreach (EpisodeMetadata metadata in episode.EpisodeMetadata)
|
||||
{
|
||||
@@ -611,7 +598,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
doc.Add(new StringField(TitleAndYearField, GetTitleAndYear(metadata), Field.Store.NO));
|
||||
doc.Add(new StringField(JumpLetterField, GetJumpLetter(metadata), Field.Store.YES));
|
||||
|
||||
AddLanguages(doc, episode.MediaVersions);
|
||||
await AddLanguages(searchRepository, doc, episode.MediaVersions);
|
||||
|
||||
if (metadata.ReleaseDate.HasValue)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user