add content rating (#218)

* add new columns

* store local content ratings

* display and search content ratings

* add content_rating to search docs

* sync content rating from jellyfin, emby, plex

* force sync content rating for all libraries

* code cleanup
This commit is contained in:
Jason Dove
2021-05-27 06:41:24 -05:00
committed by GitHub
parent 6504ca10a8
commit 68123a2f9c
34 changed files with 2939 additions and 13 deletions
+2
View File
@@ -26,6 +26,8 @@ namespace ErsatzTV.Application.Movies
metadata.Genres.Map(g => g.Name).ToList(),
metadata.Tags.Map(t => t.Name).ToList(),
metadata.Studios.Map(s => s.Name).ToList(),
(metadata.ContentRating ?? string.Empty).Split("/").Map(s => s.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x)).ToList(),
LanguagesForMovie(movie),
metadata.Actors.OrderBy(a => a.Order).ThenBy(a => a.Id)
.Map(a => MediaCards.Mapper.ProjectToViewModel(a, maybeJellyfin, maybeEmby))
@@ -11,6 +11,7 @@ namespace ErsatzTV.Application.Movies
List<string> Genres,
List<string> Tags,
List<string> Studios,
List<string> ContentRatings,
List<CultureInfo> Languages,
List<ActorCardViewModel> Actors)
{
@@ -30,6 +30,10 @@ namespace ErsatzTV.Application.Television
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>()),
show.ShowMetadata.HeadOrNone()
.Map(
m => (m.ContentRating ?? string.Empty).Split("/").Map(s => s.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x)).ToList()).IfNone(new List<string>()),
LanguagesForShow(languages),
show.ShowMetadata.HeadOrNone()
.Map(
@@ -14,6 +14,7 @@ namespace ErsatzTV.Application.Television
List<string> Genres,
List<string> Tags,
List<string> Studios,
List<string> ContentRatings,
List<CultureInfo> Languages,
List<ActorCardViewModel> Actors);
}
@@ -4,6 +4,6 @@
{
Fallback = 0,
Sidecar = 1,
External
External = 2
}
}
@@ -2,6 +2,7 @@
{
public class MovieMetadata : Metadata
{
public string ContentRating { get; set; }
public string Outline { get; set; }
public string Plot { get; set; }
public string Tagline { get; set; }
@@ -2,6 +2,7 @@
{
public class ShowMetadata : Metadata
{
public string ContentRating { get; set; }
public string Outline { get; set; }
public string Plot { get; set; }
public string Tagline { get; set; }
@@ -24,5 +24,9 @@ namespace ErsatzTV.Core.Interfaces.Repositories
Task<Unit> MarkAsUpdated(SeasonMetadata metadata, DateTime dateUpdated);
Task<Unit> MarkAsUpdated(MovieMetadata metadata, DateTime dateUpdated);
Task<Unit> MarkAsUpdated(EpisodeMetadata metadata, DateTime dateUpdated);
Task<Unit> MarkAsExternal(ShowMetadata metadata);
Task<Unit> SetContentRating(ShowMetadata metadata, string contentRating);
Task<Unit> MarkAsExternal(MovieMetadata metadata);
Task<Unit> SetContentRating(MovieMetadata metadata, string contentRating);
}
}
@@ -232,6 +232,7 @@ namespace ErsatzTV.Core.Metadata
Optional(movie.MovieMetadata).Flatten().HeadOrNone().Match(
async existing =>
{
existing.ContentRating = metadata.ContentRating;
existing.Outline = metadata.Outline;
existing.Plot = metadata.Plot;
existing.Tagline = metadata.Tagline;
@@ -276,6 +277,7 @@ namespace ErsatzTV.Core.Metadata
Optional(show.ShowMetadata).Flatten().HeadOrNone().Match(
async existing =>
{
existing.ContentRating = metadata.ContentRating;
existing.Outline = metadata.Outline;
existing.Plot = metadata.Plot;
existing.Tagline = metadata.Tagline;
@@ -474,6 +476,7 @@ namespace ErsatzTV.Core.Metadata
Plot = nfo.Plot,
Outline = nfo.Outline,
Tagline = nfo.Tagline,
ContentRating = nfo.ContentRating,
Year = GetYear(nfo.Year, nfo.Premiered),
ReleaseDate = GetAired(nfo.Year, nfo.Premiered),
Genres = nfo.Genres.Map(g => new Genre { Name = g }).ToList(),
@@ -571,6 +574,7 @@ namespace ErsatzTV.Core.Metadata
DateUpdated = dateUpdated,
Title = nfo.Title,
Year = nfo.Year,
ContentRating = nfo.ContentRating,
ReleaseDate = nfo.Premiered,
Plot = nfo.Plot,
Outline = nfo.Outline,
+1 -1
View File
@@ -108,7 +108,7 @@ namespace ErsatzTV.Core.Metadata
.HeadOrNone();
// skip folder if etag matches
if (await knownFolder.Map(f => f.Etag).IfNoneAsync(string.Empty) == etag)
if (await knownFolder.Map(f => f.Etag ?? string.Empty).IfNoneAsync(string.Empty) == etag)
{
continue;
}
@@ -252,7 +252,7 @@ namespace ErsatzTV.Core.Metadata
.HeadOrNone();
// skip folder if etag matches
if (await knownFolder.Map(f => f.Etag).IfNoneAsync(string.Empty) == etag)
if (await knownFolder.Map(f => f.Etag ?? string.Empty).IfNoneAsync(string.Empty) == etag)
{
continue;
}
+3
View File
@@ -21,6 +21,9 @@ namespace ErsatzTV.Core.Metadata.Nfo
[XmlElement("tagline")]
public string Tagline { get; set; }
[XmlElement("mpaa")]
public string ContentRating { get; set; }
[XmlElement("premiered")]
public string Premiered { get; set; }
@@ -158,7 +158,7 @@ namespace ErsatzTV.Core.Metadata
.HeadOrNone();
// skip folder if etag matches
if (await knownFolder.Map(f => f.Etag).IfNoneAsync(string.Empty) == etag)
if (await knownFolder.Map(f => f.Etag ?? string.Empty).IfNoneAsync(string.Empty) == etag)
{
continue;
}
@@ -173,6 +173,19 @@ namespace ErsatzTV.Core.Plex
await maybeMetadata.Match(
async fullMetadata =>
{
if (existingMetadata.MetadataKind != MetadataKind.External)
{
existingMetadata.MetadataKind = MetadataKind.External;
await _metadataRepository.MarkAsExternal(existingMetadata);
}
if (existingMetadata.ContentRating != fullMetadata.ContentRating)
{
existingMetadata.ContentRating = fullMetadata.ContentRating;
await _metadataRepository.SetContentRating(existingMetadata, fullMetadata.ContentRating);
result.IsUpdated = true;
}
foreach (Genre genre in existingMetadata.Genres
.Filter(g => fullMetadata.Genres.All(g2 => g2.Name != g.Name))
.ToList())
@@ -146,6 +146,19 @@ namespace ErsatzTV.Core.Plex
await maybeMetadata.Match(
async fullMetadata =>
{
if (existingMetadata.MetadataKind != MetadataKind.External)
{
existingMetadata.MetadataKind = MetadataKind.External;
await _metadataRepository.MarkAsExternal(existingMetadata);
}
if (existingMetadata.ContentRating != fullMetadata.ContentRating)
{
existingMetadata.ContentRating = fullMetadata.ContentRating;
await _metadataRepository.SetContentRating(existingMetadata, fullMetadata.ContentRating);
result.IsUpdated = true;
}
foreach (Genre genre in existingMetadata.Genres
.Filter(g => fullMetadata.Genres.All(g2 => g2.Name != g.Name))
.ToList())
@@ -105,6 +105,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
// metadata
ShowMetadata metadata = existing.ShowMetadata.Head();
ShowMetadata incomingMetadata = show.ShowMetadata.Head();
metadata.MetadataKind = incomingMetadata.MetadataKind;
metadata.ContentRating = incomingMetadata.ContentRating;
metadata.Title = incomingMetadata.Title;
metadata.SortTitle = incomingMetadata.SortTitle;
metadata.Plot = incomingMetadata.Plot;
@@ -105,6 +105,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
// metadata
ShowMetadata metadata = existing.ShowMetadata.Head();
ShowMetadata incomingMetadata = show.ShowMetadata.Head();
metadata.MetadataKind = incomingMetadata.MetadataKind;
metadata.ContentRating = incomingMetadata.ContentRating;
metadata.Title = incomingMetadata.Title;
metadata.SortTitle = incomingMetadata.SortTitle;
metadata.Plot = incomingMetadata.Plot;
@@ -228,6 +228,26 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
@"UPDATE EpisodeMetadata SET DateUpdated = @DateUpdated WHERE Id = @Id",
new { DateUpdated = dateUpdated, metadata.Id }).ToUnit();
public Task<Unit> MarkAsExternal(ShowMetadata metadata) =>
_dbConnection.ExecuteAsync(
@"UPDATE ShowMetadata SET MetadataKind = @Kind WHERE Id = @Id",
new { metadata.Id, Kind = (int) MetadataKind.External }).ToUnit();
public Task<Unit> SetContentRating(ShowMetadata metadata, string contentRating) =>
_dbConnection.ExecuteAsync(
@"UPDATE ShowMetadata SET ContentRating = @ContentRating WHERE Id = @Id",
new { metadata.Id, ContentRating = contentRating }).ToUnit();
public Task<Unit> MarkAsExternal(MovieMetadata metadata) =>
_dbConnection.ExecuteAsync(
@"UPDATE MovieMetadata SET MetadataKind = @Kind WHERE Id = @Id",
new { metadata.Id, Kind = (int) MetadataKind.External }).ToUnit();
public Task<Unit> SetContentRating(MovieMetadata metadata, string contentRating) =>
_dbConnection.ExecuteAsync(
@"UPDATE MovieMetadata SET ContentRating = @ContentRating WHERE Id = @Id",
new { metadata.Id, ContentRating = contentRating }).ToUnit();
public Task<bool> RemoveGenre(Genre genre) =>
_dbConnection.ExecuteAsync("DELETE FROM Genre WHERE Id = @GenreId", new { GenreId = genre.Id })
.Map(result => result > 0);
@@ -327,6 +327,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
// metadata
MovieMetadata metadata = existing.MovieMetadata.Head();
MovieMetadata incomingMetadata = movie.MovieMetadata.Head();
metadata.MetadataKind = incomingMetadata.MetadataKind;
metadata.ContentRating = incomingMetadata.ContentRating;
metadata.Title = incomingMetadata.Title;
metadata.SortTitle = incomingMetadata.SortTitle;
metadata.Plot = incomingMetadata.Plot;
@@ -528,6 +530,8 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
// metadata
MovieMetadata metadata = existing.MovieMetadata.Head();
MovieMetadata incomingMetadata = movie.MovieMetadata.Head();
metadata.MetadataKind = incomingMetadata.MetadataKind;
metadata.ContentRating = incomingMetadata.ContentRating;
metadata.Title = incomingMetadata.Title;
metadata.SortTitle = incomingMetadata.SortTitle;
metadata.Plot = incomingMetadata.Plot;
@@ -230,12 +230,14 @@ namespace ErsatzTV.Infrastructure.Emby
var metadata = new MovieMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Plot = item.Overview,
Year = item.ProductionYear,
Tagline = Optional(item.Taglines).Flatten().HeadOrNone().IfNone(string.Empty),
DateAdded = dateAdded,
ContentRating = item.OfficialRating,
Genres = Optional(item.Genres).Flatten().Map(g => new Genre { Name = g }).ToList(),
Tags = Optional(item.Tags).Flatten().Map(t => new Tag { Name = t }).ToList(),
Studios = Optional(item.Studios).Flatten().Map(s => new Studio { Name = s.Name }).ToList(),
@@ -324,12 +326,14 @@ namespace ErsatzTV.Infrastructure.Emby
var metadata = new ShowMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Plot = item.Overview,
Year = item.ProductionYear,
Tagline = Optional(item.Taglines).Flatten().HeadOrNone().IfNone(string.Empty),
DateAdded = dateAdded,
ContentRating = item.OfficialRating,
Genres = Optional(item.Genres).Flatten().Map(g => new Genre { Name = g }).ToList(),
Tags = Optional(item.Tags).Flatten().Map(t => new Tag { Name = t }).ToList(),
Studios = Optional(item.Studios).Flatten().Map(s => new Studio { Name = s.Name }).ToList(),
@@ -393,6 +397,7 @@ namespace ErsatzTV.Infrastructure.Emby
var metadata = new SeasonMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Year = item.ProductionYear,
@@ -498,6 +503,7 @@ namespace ErsatzTV.Infrastructure.Emby
var metadata = new EpisodeMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Plot = item.Overview,
+2 -2
View File
@@ -28,7 +28,7 @@ namespace ErsatzTV.Infrastructure.Emby
string parentId,
[Query]
string fields =
"Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People,ProductionYear,PremiereDate,MediaSources",
"Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People,ProductionYear,PremiereDate,MediaSources,OfficialRating",
[Query]
string includeItemTypes = "Movie");
@@ -40,7 +40,7 @@ namespace ErsatzTV.Infrastructure.Emby
string parentId,
[Query]
string fields =
"Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People,ProductionYear,PremiereDate,MediaSources",
"Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People,ProductionYear,PremiereDate,MediaSources,OfficialRating",
[Query]
string includeItemTypes = "Series");
@@ -9,6 +9,7 @@ namespace ErsatzTV.Infrastructure.Emby.Models
public string Id { get; set; }
public string Etag { get; set; }
public string Path { get; set; }
public string OfficialRating { get; set; }
public DateTimeOffset DateCreated { get; set; }
public long RunTimeTicks { get; set; }
public List<string> Genres { get; set; }
@@ -34,7 +34,7 @@ namespace ErsatzTV.Infrastructure.Jellyfin
[Query]
string parentId,
[Query]
string fields = "Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People",
string fields = "Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People,OfficialRating",
[Query]
string includeItemTypes = "Movie");
@@ -47,7 +47,7 @@ namespace ErsatzTV.Infrastructure.Jellyfin
[Query]
string parentId,
[Query]
string fields = "Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People",
string fields = "Path,Genres,Tags,DateCreated,Etag,Overview,Taglines,Studios,People,OfficialRating",
[Query]
string includeItemTypes = "Series");
@@ -276,12 +276,14 @@ namespace ErsatzTV.Infrastructure.Jellyfin
var metadata = new MovieMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Plot = item.Overview,
Year = item.ProductionYear,
Tagline = Optional(item.Taglines).Flatten().HeadOrNone().IfNone(string.Empty),
DateAdded = dateAdded,
ContentRating = item.OfficialRating,
Genres = Optional(item.Genres).Flatten().Map(g => new Genre { Name = g }).ToList(),
Tags = Optional(item.Tags).Flatten().Map(t => new Tag { Name = t }).ToList(),
Studios = Optional(item.Studios).Flatten().Map(s => new Studio { Name = s.Name }).ToList(),
@@ -375,12 +377,14 @@ namespace ErsatzTV.Infrastructure.Jellyfin
var metadata = new ShowMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Plot = item.Overview,
Year = item.ProductionYear,
Tagline = Optional(item.Taglines).Flatten().HeadOrNone().IfNone(string.Empty),
DateAdded = dateAdded,
ContentRating = item.OfficialRating,
Genres = Optional(item.Genres).Flatten().Map(g => new Genre { Name = g }).ToList(),
Tags = Optional(item.Tags).Flatten().Map(t => new Tag { Name = t }).ToList(),
Studios = Optional(item.Studios).Flatten().Map(s => new Studio { Name = s.Name }).ToList(),
@@ -449,6 +453,7 @@ namespace ErsatzTV.Infrastructure.Jellyfin
var metadata = new SeasonMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Year = item.ProductionYear,
@@ -494,7 +499,7 @@ namespace ErsatzTV.Infrastructure.Jellyfin
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error projecting Jellyfin show");
_logger.LogWarning(ex, "Error projecting Jellyfin season");
return None;
}
}
@@ -542,7 +547,7 @@ namespace ErsatzTV.Infrastructure.Jellyfin
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error projecting Jellyfin movie");
_logger.LogWarning(ex, "Error projecting Jellyfin episode");
return None;
}
}
@@ -554,6 +559,7 @@ namespace ErsatzTV.Infrastructure.Jellyfin
var metadata = new EpisodeMetadata
{
MetadataKind = MetadataKind.External,
Title = item.Name,
SortTitle = _fallbackMetadataProvider.GetSortTitle(item.Name),
Plot = item.Overview,
@@ -9,6 +9,7 @@ namespace ErsatzTV.Infrastructure.Jellyfin.Models
public string Id { get; set; }
public string Etag { get; set; }
public string Path { get; set; }
public string OfficialRating { get; set; }
public DateTimeOffset DateCreated { get; set; }
public long RunTimeTicks { get; set; }
public List<string> Genres { get; set; }
@@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace ErsatzTV.Infrastructure.Migrations
{
public partial class Add_MovieMetadataShowMetadataContentRating : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
// local and plex
migrationBuilder.Sql("UPDATE MovieMetadata SET DateUpdated = '0001-01-01 00:00:00'");
migrationBuilder.Sql("UPDATE ShowMetadata SET DateUpdated = '0001-01-01 00:00:00'");
migrationBuilder.Sql(
@"UPDATE LibraryFolder SET Etag = NULL WHERE LibraryPathId IN
(SELECT LibraryPathId FROM LibraryPath LP
INNER JOIN Library L on LP.LibraryId = L.Id
WHERE L.MediaKind = 1)");
// emby
migrationBuilder.Sql("UPDATE EmbyMovie SET Etag = NULL");
migrationBuilder.Sql("UPDATE EmbyShow SET Etag = NULL");
// jellyfin
migrationBuilder.Sql("UPDATE JellyfinMovie SET Etag = NULL");
migrationBuilder.Sql("UPDATE JellyfinShow SET Etag = NULL");
migrationBuilder.AddColumn<string>(
"ContentRating",
"ShowMetadata",
"TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
"ContentRating",
"MovieMetadata",
"TEXT",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
"ContentRating",
"ShowMetadata");
migrationBuilder.DropColumn(
"ContentRating",
"MovieMetadata");
}
}
}
@@ -794,6 +794,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ContentRating")
.HasColumnType("TEXT");
b.Property<DateTime>("DateAdded")
.HasColumnType("TEXT");
@@ -1170,6 +1173,9 @@ namespace ErsatzTV.Infrastructure.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ContentRating")
.HasColumnType("TEXT");
b.Property<DateTime>("DateAdded")
.HasColumnType("TEXT");
@@ -6,6 +6,7 @@ namespace ErsatzTV.Infrastructure.Plex.Models
{
public string Key { get; set; }
public string Title { get; set; }
public string ContentRating { get; set; }
public string Summary { get; set; }
public int Year { get; set; }
public string Tagline { get; set; }
@@ -250,11 +250,13 @@ namespace ErsatzTV.Infrastructure.Plex
var metadata = new MovieMetadata
{
MetadataKind = MetadataKind.External,
Title = response.Title,
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
Plot = response.Summary,
Year = response.Year,
Tagline = response.Tagline,
ContentRating = response.ContentRating,
DateAdded = dateAdded,
DateUpdated = lastWriteTime,
Genres = Optional(response.Genre).Flatten().Map(g => new Genre { Name = g.Tag }).ToList(),
@@ -398,11 +400,13 @@ namespace ErsatzTV.Infrastructure.Plex
var metadata = new ShowMetadata
{
MetadataKind = MetadataKind.External,
Title = response.Title,
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
Plot = response.Summary,
Year = response.Year,
Tagline = response.Tagline,
ContentRating = response.ContentRating,
DateAdded = dateAdded,
DateUpdated = lastWriteTime,
Genres = Optional(response.Genre).Flatten().Map(g => new Genre { Name = g.Tag }).ToList(),
@@ -462,6 +466,7 @@ namespace ErsatzTV.Infrastructure.Plex
var metadata = new SeasonMetadata
{
MetadataKind = MetadataKind.External,
Title = response.Title,
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
Year = response.Year,
@@ -518,6 +523,7 @@ namespace ErsatzTV.Infrastructure.Plex
var metadata = new EpisodeMetadata
{
MetadataKind = MetadataKind.External,
Title = response.Title,
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
Plot = response.Summary,
+30 -3
View File
@@ -11,6 +11,9 @@ using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Core.Search;
using LanguageExt;
using LanguageExt.UnsafeValueAccess;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Core;
using Lucene.Net.Analysis.Miscellaneous;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
@@ -45,6 +48,7 @@ namespace ErsatzTV.Infrastructure.Search
private const string StyleField = "style";
private const string MoodField = "mood";
private const string ActorField = "actor";
private const string ContentRatingField = "content_rating";
private const string MovieType = "movie";
private const string ShowType = "show";
@@ -65,7 +69,7 @@ namespace ErsatzTV.Infrastructure.Search
_initialized = false;
}
public int Version => 9;
public int Version => 10;
public Task<bool> Initialize(ILocalFileSystem localFileSystem)
{
@@ -162,9 +166,14 @@ namespace ErsatzTV.Infrastructure.Search
var searcher = new IndexSearcher(reader);
int hitsLimit = limit == 0 ? searcher.IndexReader.MaxDoc : skip + limit;
using var analyzer = new StandardAnalyzer(AppLuceneVersion);
var customAnalyzers = new Dictionary<string, Analyzer>
{
{ ContentRatingField, new KeywordAnalyzer() }
};
using var analyzerWrapper = new PerFieldAnalyzerWrapper(analyzer, customAnalyzers);
QueryParser parser = !string.IsNullOrWhiteSpace(searchField)
? new QueryParser(AppLuceneVersion, searchField, analyzer)
: new MultiFieldQueryParser(AppLuceneVersion, new[] { TitleField }, analyzer);
? new QueryParser(AppLuceneVersion, searchField, analyzerWrapper)
: new MultiFieldQueryParser(AppLuceneVersion, new[] { TitleField }, analyzerWrapper);
parser.AllowLeadingWildcard = true;
Query query = ParseQuery(searchQuery, parser);
var filter = new DuplicateFilter(TitleAndYearField);
@@ -266,6 +275,15 @@ namespace ErsatzTV.Infrastructure.Search
AddLanguages(doc, movie.MediaVersions);
if (!string.IsNullOrWhiteSpace(metadata.ContentRating))
{
foreach (string contentRating in (metadata.ContentRating ?? string.Empty).Split("/")
.Map(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)))
{
doc.Add(new StringField(ContentRatingField, contentRating, Field.Store.NO));
}
}
if (metadata.ReleaseDate.HasValue)
{
doc.Add(
@@ -368,6 +386,15 @@ namespace ErsatzTV.Infrastructure.Search
doc.Add(new TextField(LanguageField, cultureInfo.EnglishName, Field.Store.NO));
}
if (!string.IsNullOrWhiteSpace(metadata.ContentRating))
{
foreach (string contentRating in (metadata.ContentRating ?? string.Empty).Split("/")
.Map(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)))
{
doc.Add(new StringField(ContentRatingField, contentRating, Field.Store.NO));
}
}
if (metadata.ReleaseDate.HasValue)
{
doc.Add(
+14
View File
@@ -63,6 +63,18 @@
</div>
<MudCard Class="mb-6">
<MudCardContent>
@if (_sortedContentRatings.Any())
{
<div style="display: flex; flex-direction: row; flex-wrap: wrap">
<MudText GutterBottom="true">Content Ratings:&nbsp;</MudText>
<MudLink Href="@($"/search?query=content_rating%3a%22{Uri.EscapeDataString(_sortedContentRatings.Head())}%22")">@_sortedContentRatings.Head()</MudLink>
@foreach (string contentRating in _sortedContentRatings.Skip(1))
{
<MudText>,&nbsp;</MudText>
<MudLink Href="@($"/search?query=content_rating%3a%22{Uri.EscapeDataString(contentRating)}%22")">@contentRating</MudLink>
}
</div>
}
@if (_sortedLanguages.Any())
{
<div style="display: flex; flex-direction: row; flex-wrap: wrap">
@@ -136,6 +148,7 @@
public int MovieId { get; set; }
private MovieViewModel _movie;
private List<string> _sortedContentRatings = new();
private List<CultureInfo> _sortedLanguages = new();
private List<string> _sortedStudios = new();
private List<string> _sortedGenres = new();
@@ -147,6 +160,7 @@
_mediator.Send(new GetMovieById(MovieId)).IfSomeAsync(vm =>
{
_movie = vm;
_sortedContentRatings = _movie.ContentRatings.OrderBy(cr => cr).ToList();
_sortedLanguages = _movie.Languages.OrderBy(ci => ci.EnglishName).ToList();
_sortedStudios = _movie.Studios.OrderBy(s => s).ToList();
_sortedGenres = _movie.Genres.OrderBy(g => g).ToList();
+14
View File
@@ -77,6 +77,18 @@
</div>
<MudCard Class="mb-6">
<MudCardContent>
@if (_sortedContentRatings.Any())
{
<div style="display: flex; flex-direction: row; flex-wrap: wrap">
<MudText GutterBottom="true">Content Ratings:&nbsp;</MudText>
<MudLink Href="@($"/search?query=content_rating%3a%22{Uri.EscapeDataString(_sortedContentRatings.Head())}%22")">@_sortedContentRatings.Head()</MudLink>
@foreach (string contentRating in _sortedContentRatings.Skip(1))
{
<MudText>,&nbsp;</MudText>
<MudLink Href="@($"/search?query=content_rating%3a%22{Uri.EscapeDataString(contentRating)}%22")">@contentRating</MudLink>
}
</div>
}
@if (_sortedLanguages.Any())
{
<div style="display: flex; flex-direction: row; flex-wrap: wrap">
@@ -161,6 +173,7 @@
public int ShowId { get; set; }
private TelevisionShowViewModel _show;
private List<string> _sortedContentRatings = new();
private List<CultureInfo> _sortedLanguages = new();
private List<string> _sortedStudios = new();
private List<string> _sortedGenres = new();
@@ -179,6 +192,7 @@
.IfSomeAsync(vm =>
{
_show = vm;
_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();
+2
View File
@@ -19,6 +19,7 @@ The following fields are available for searching movies:
- `studio`: The movie studio
- `actor`: An actor from the movie
- `library_name`: The name of the library that contains the movie
- `content_rating`: The movie content rating (case-sensitive)
- `language`: The movie audio stream language
- `release_date`: The movie release date (YYYYMMDD)
- `type`: Always `movie`
@@ -34,6 +35,7 @@ The following fields are available for searching shows:
- `studio`: The show studio
- `actor`: An actor from the show
- `library_name`: The name of the library that contains the show
- `content_rating`: The movie content rating (case-sensitive)
- `language`: The show audio stream language
- `release_date`: The show release date (YYYYMMDD)
- `type`: Always `show`