add chapters to search index (#2199)

This commit is contained in:
Jason Dove
2025-07-24 21:03:58 +00:00
committed by GitHub
parent fde05a0299
commit dd9a6d5a06
6 changed files with 39 additions and 2 deletions
+1
View File
@@ -114,6 +114,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `duration`: when the content is live and does not have duration metadata, this must be provided to allow scheduling
- The remote stream definition (YAML file) may provide either a `url` or a `script`
- If both are provided, `url` will be used
- Include number of chapters in search index as `chapters`
### Changed
- Allow `Other Video` libraries and `Image` libraries to use the same folders
@@ -35,6 +35,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Movie).MovieMetadata)
.ThenInclude(em => em.Guids)
.Include(mi => (mi as Movie).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Movie).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Episode).EpisodeMetadata)
.ThenInclude(em => em.Genres)
@@ -51,6 +53,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Episode).EpisodeMetadata)
.ThenInclude(em => em.Guids)
.Include(mi => (mi as Episode).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Episode).MediaVersions)
.ThenInclude(em => em.Streams)
.Include(mi => (mi as Episode).MediaVersions)
.ThenInclude(em => em.MediaFiles)
@@ -108,6 +112,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as MusicVideo).MusicVideoMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as MusicVideo).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as MusicVideo).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Artist).ArtistMetadata)
.ThenInclude(mm => mm.Genres)
@@ -132,6 +138,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as OtherVideo).OtherVideoMetadata)
.ThenInclude(ovm => ovm.Guids)
.Include(mi => (mi as OtherVideo).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as OtherVideo).MediaVersions)
.ThenInclude(ovm => ovm.Streams)
.Include(mi => (mi as Song).SongMetadata)
.ThenInclude(mm => mm.Tags)
@@ -140,6 +148,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Song).SongMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as Song).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Song).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Image).ImageMetadata)
.ThenInclude(mm => mm.Tags)
@@ -148,6 +158,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Image).ImageMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as Image).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Image).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Image).MediaVersions)
.ThenInclude(mm => mm.MediaFiles)
@@ -158,6 +170,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as RemoteStream).RemoteStreamMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as RemoteStream).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as RemoteStream).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as RemoteStream).MediaVersions)
.ThenInclude(mm => mm.MediaFiles)
@@ -271,6 +285,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Movie).MovieMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as Movie).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Movie).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Episode).EpisodeMetadata)
.ThenInclude(em => em.Genres)
@@ -287,6 +303,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Episode).EpisodeMetadata)
.ThenInclude(em => em.Guids)
.Include(mi => (mi as Episode).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Episode).MediaVersions)
.ThenInclude(em => em.Streams)
.Include(mi => (mi as Episode).MediaVersions)
.ThenInclude(em => em.MediaFiles)
@@ -344,6 +362,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as MusicVideo).MusicVideoMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as MusicVideo).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as MusicVideo).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Artist).ArtistMetadata)
.ThenInclude(mm => mm.Genres)
@@ -368,6 +388,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as OtherVideo).OtherVideoMetadata)
.ThenInclude(ovm => ovm.Guids)
.Include(mi => (mi as OtherVideo).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as OtherVideo).MediaVersions)
.ThenInclude(ovm => ovm.Streams)
.Include(mi => (mi as Song).SongMetadata)
.ThenInclude(mm => mm.Tags)
@@ -376,6 +398,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Song).SongMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as Song).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Song).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Image).ImageMetadata)
.ThenInclude(mm => mm.Tags)
@@ -384,6 +408,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as Image).ImageMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as Image).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as Image).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as Image).MediaVersions)
.ThenInclude(mm => mm.MediaFiles)
@@ -394,6 +420,8 @@ public class SearchRepository : ISearchRepository
.Include(mi => (mi as RemoteStream).RemoteStreamMetadata)
.ThenInclude(mm => mm.Guids)
.Include(mi => (mi as RemoteStream).MediaVersions)
.ThenInclude(mv => mv.Chapters)
.Include(mi => (mi as RemoteStream).MediaVersions)
.ThenInclude(mm => mm.Streams)
.Include(mi => (mi as RemoteStream).MediaVersions)
.ThenInclude(mm => mm.MediaFiles)
@@ -14,6 +14,7 @@ public class CustomMultiFieldQueryParser : MultiFieldQueryParser
{
private static readonly List<string> NumericFields =
[
LuceneSearchIndex.ChaptersField,
LuceneSearchIndex.MinutesField,
LuceneSearchIndex.SecondsField,
LuceneSearchIndex.HeightField,
@@ -49,7 +49,7 @@ public class ElasticSearchIndex : ISearchIndex
return exists.IsValidResponse;
}
public int Version => 47;
public int Version => 48;
public async Task<bool> Initialize(
ILocalFileSystem localFileSystem,
@@ -926,6 +926,7 @@ public class ElasticSearchIndex : ISearchIndex
{
foreach (MediaVersion version in mediaVersions.HeadOrNone())
{
doc.Chapters = (version.Chapters ?? []).Count;
doc.Minutes = (int)Math.Ceiling(version.Duration.TotalMinutes);
doc.Seconds = (int)Math.Ceiling(version.Duration.TotalSeconds);
@@ -79,6 +79,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
internal const string AddedDateField = "added_date";
internal const string ReleaseDateField = "release_date";
internal const string VideoBitDepthField = "video_bit_depth";
internal const string ChaptersField = "chapters";
public const string MovieType = "movie";
public const string ShowType = "show";
@@ -118,7 +119,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
return Task.FromResult(directoryExists && fileExists);
}
public int Version => 48;
public int Version => 49;
public async Task<bool> Initialize(
ILocalFileSystem localFileSystem,
@@ -1508,6 +1509,8 @@ public sealed class LuceneSearchIndex : ISearchIndex
{
foreach (MediaVersion version in mediaVersions)
{
doc.Add(new Int32Field(ChaptersField, (version.Chapters ?? []).Count, Field.Store.NO));
doc.Add(new Int32Field(MinutesField, (int)Math.Ceiling(version.Duration.TotalMinutes), Field.Store.NO));
doc.Add(new Int32Field(SecondsField, (int)Math.Ceiling(version.Duration.TotalSeconds), Field.Store.NO));
@@ -37,6 +37,9 @@ public class ElasticSearchItem : MinimalElasticSearchItem
[JsonPropertyName(LuceneSearchIndex.SubLanguageTagField)]
public List<string> SubLanguageTag { get; set; }
[JsonPropertyName(LuceneSearchIndex.ChaptersField)]
public int Chapters { get; set; }
[JsonPropertyName(LuceneSearchIndex.MinutesField)]
public int Minutes { get; set; }