Browse Source

add collection (name) to search index (#2054)

pull/2055/head
Jason Dove 2 months ago committed by GitHub
parent
commit
5be929da18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 8
      ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollectionHandler.cs
  3. 8
      ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs
  4. 8
      ErsatzTV.Application/MediaCollections/Commands/AddImageToCollectionHandler.cs
  5. 8
      ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollectionHandler.cs
  6. 8
      ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs
  7. 8
      ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs
  8. 8
      ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs
  9. 8
      ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs
  10. 8
      ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs
  11. 8
      ErsatzTV.Application/MediaCollections/Commands/AddSongToCollectionHandler.cs
  12. 8
      ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs
  13. 2
      ErsatzTV.Infrastructure/Data/Repositories/SearchRepository.cs
  14. 14
      ErsatzTV.Infrastructure/Search/ElasticSearchIndex.cs
  15. 21
      ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs
  16. 5
      ErsatzTV.Infrastructure/Search/Models/ElasticSearchItem.cs
  17. 3
      ErsatzTV.Infrastructure/Search/SearchQueryParser.cs

2
CHANGELOG.md

@ -40,6 +40,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Add `repeat` property to YAML sequence instruction - Add `repeat` property to YAML sequence instruction
- This tells the playout builder how many times this sequence should repeat - This tells the playout builder how many times this sequence should repeat
- Omitting this value is the same as setting it to `1` - Omitting this value is the same as setting it to `1`
- Add `collection` (name) to search index for manual collections created within ETV
- Collections synchronized from media servers are still indexed as `tag`
### Changed ### Changed
- Start to make UI minimally responsive (functional on smaller screens) - Start to make UI minimally responsive (functional on smaller screens)

8
ErsatzTV.Application/MediaCollections/Commands/AddArtistToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddArtistToCollectionHandler :
IRequestHandler<AddArtistToCollection, Either<BaseError, Unit>> IRequestHandler<AddArtistToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddArtistToCollectionHandler( public AddArtistToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -41,6 +45,8 @@ public class AddArtistToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.Artist); parameters.Collection.MediaItems.Add(parameters.Artist);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.Artist.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddEpisodeToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddEpisodeToCollectionHandler :
IRequestHandler<AddEpisodeToCollection, Either<BaseError, Unit>> IRequestHandler<AddEpisodeToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddEpisodeToCollectionHandler( public AddEpisodeToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -43,6 +47,8 @@ public class AddEpisodeToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.Episode); parameters.Collection.MediaItems.Add(parameters.Episode);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.Episode.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddImageToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -13,17 +14,20 @@ namespace ErsatzTV.Application.MediaCollections;
public class AddImageToCollectionHandler : IRequestHandler<AddImageToCollection, Either<BaseError, Unit>> public class AddImageToCollectionHandler : IRequestHandler<AddImageToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddImageToCollectionHandler( public AddImageToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -40,6 +44,8 @@ public class AddImageToCollectionHandler : IRequestHandler<AddImageToCollection,
parameters.Collection.MediaItems.Add(parameters.Image); parameters.Collection.MediaItems.Add(parameters.Image);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.Image.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddItemsToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,6 +15,7 @@ public class AddItemsToCollectionHandler :
IRequestHandler<AddItemsToCollection, Either<BaseError, Unit>> IRequestHandler<AddItemsToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
private readonly IMovieRepository _movieRepository; private readonly IMovieRepository _movieRepository;
@ -24,13 +26,15 @@ public class AddItemsToCollectionHandler :
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
IMovieRepository movieRepository, IMovieRepository movieRepository,
ITelevisionRepository televisionRepository, ITelevisionRepository televisionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_movieRepository = movieRepository; _movieRepository = movieRepository;
_televisionRepository = televisionRepository; _televisionRepository = televisionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -67,6 +71,8 @@ public class AddItemsToCollectionHandler :
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems(toAddIds.ToArray()));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(request.CollectionId)) .PlayoutIdsUsingCollection(request.CollectionId))

8
ErsatzTV.Application/MediaCollections/Commands/AddMovieToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddMovieToCollectionHandler :
IRequestHandler<AddMovieToCollection, Either<BaseError, Unit>> IRequestHandler<AddMovieToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddMovieToCollectionHandler( public AddMovieToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -41,6 +45,8 @@ public class AddMovieToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.Movie); parameters.Collection.MediaItems.Add(parameters.Movie);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.Movie.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddMusicVideoToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddMusicVideoToCollectionHandler :
IRequestHandler<AddMusicVideoToCollection, Either<BaseError, Unit>> IRequestHandler<AddMusicVideoToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddMusicVideoToCollectionHandler( public AddMusicVideoToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -43,6 +47,8 @@ public class AddMusicVideoToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.MusicVideo); parameters.Collection.MediaItems.Add(parameters.MusicVideo);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.MusicVideo.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddOtherVideoToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddOtherVideoToCollectionHandler :
IRequestHandler<AddOtherVideoToCollection, Either<BaseError, Unit>> IRequestHandler<AddOtherVideoToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddOtherVideoToCollectionHandler( public AddOtherVideoToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -43,6 +47,8 @@ public class AddOtherVideoToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.OtherVideo); parameters.Collection.MediaItems.Add(parameters.OtherVideo);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.OtherVideo.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddSeasonToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddSeasonToCollectionHandler :
IRequestHandler<AddSeasonToCollection, Either<BaseError, Unit>> IRequestHandler<AddSeasonToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddSeasonToCollectionHandler( public AddSeasonToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -41,6 +45,8 @@ public class AddSeasonToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.Season); parameters.Collection.MediaItems.Add(parameters.Season);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.Season.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddShowToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddShowToCollectionHandler :
IRequestHandler<AddShowToCollection, Either<BaseError, Unit>> IRequestHandler<AddShowToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddShowToCollectionHandler( public AddShowToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -41,6 +45,8 @@ public class AddShowToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.Show); parameters.Collection.MediaItems.Add(parameters.Show);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.Show.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/AddSongToCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -14,17 +15,20 @@ public class AddSongToCollectionHandler :
IRequestHandler<AddSongToCollection, Either<BaseError, Unit>> IRequestHandler<AddSongToCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public AddSongToCollectionHandler( public AddSongToCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -41,6 +45,8 @@ public class AddSongToCollectionHandler :
parameters.Collection.MediaItems.Add(parameters.Song); parameters.Collection.MediaItems.Add(parameters.Song);
if (await dbContext.SaveChangesAsync() > 0) if (await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems([parameters.Song.Id]));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository foreach (int playoutId in await _mediaCollectionRepository
.PlayoutIdsUsingCollection(parameters.Collection.Id)) .PlayoutIdsUsingCollection(parameters.Collection.Id))

8
ErsatzTV.Application/MediaCollections/Commands/RemoveItemsFromCollectionHandler.cs

@ -1,5 +1,6 @@
using System.Threading.Channels; using System.Threading.Channels;
using ErsatzTV.Application.Playouts; using ErsatzTV.Application.Playouts;
using ErsatzTV.Application.Search;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.Domain; using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Repositories;
@ -13,17 +14,20 @@ namespace ErsatzTV.Application.MediaCollections;
public class RemoveItemsFromCollectionHandler : IRequestHandler<RemoveItemsFromCollection, Either<BaseError, Unit>> public class RemoveItemsFromCollectionHandler : IRequestHandler<RemoveItemsFromCollection, Either<BaseError, Unit>>
{ {
private readonly ChannelWriter<IBackgroundServiceRequest> _channel; private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _searchChannel;
private readonly IDbContextFactory<TvContext> _dbContextFactory; private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IMediaCollectionRepository _mediaCollectionRepository; private readonly IMediaCollectionRepository _mediaCollectionRepository;
public RemoveItemsFromCollectionHandler( public RemoveItemsFromCollectionHandler(
IDbContextFactory<TvContext> dbContextFactory, IDbContextFactory<TvContext> dbContextFactory,
IMediaCollectionRepository mediaCollectionRepository, IMediaCollectionRepository mediaCollectionRepository,
ChannelWriter<IBackgroundServiceRequest> channel) ChannelWriter<IBackgroundServiceRequest> channel,
ChannelWriter<ISearchIndexBackgroundServiceRequest> searchChannel)
{ {
_dbContextFactory = dbContextFactory; _dbContextFactory = dbContextFactory;
_mediaCollectionRepository = mediaCollectionRepository; _mediaCollectionRepository = mediaCollectionRepository;
_channel = channel; _channel = channel;
_searchChannel = searchChannel;
} }
public async Task<Either<BaseError, Unit>> Handle( public async Task<Either<BaseError, Unit>> Handle(
@ -48,6 +52,8 @@ public class RemoveItemsFromCollectionHandler : IRequestHandler<RemoveItemsFromC
if (itemsToRemove.Count != 0 && await dbContext.SaveChangesAsync() > 0) if (itemsToRemove.Count != 0 && await dbContext.SaveChangesAsync() > 0)
{ {
await _searchChannel.WriteAsync(new ReindexMediaItems(itemsToRemove.Select(mi => mi.Id).ToArray()));
// refresh all playouts that use this collection // refresh all playouts that use this collection
foreach (int playoutId in await _mediaCollectionRepository.PlayoutIdsUsingCollection(collection.Id)) foreach (int playoutId in await _mediaCollectionRepository.PlayoutIdsUsingCollection(collection.Id))
{ {

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

@ -17,6 +17,7 @@ public class SearchRepository : ISearchRepository
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
return await dbContext.MediaItems return await dbContext.MediaItems
.AsNoTracking() .AsNoTracking()
.Include(mi => mi.Collections)
.Include(mi => mi.LibraryPath) .Include(mi => mi.LibraryPath)
.ThenInclude(lp => lp.Library) .ThenInclude(lp => lp.Library)
.Include(mi => (mi as Movie).MovieMetadata) .Include(mi => (mi as Movie).MovieMetadata)
@ -242,6 +243,7 @@ public class SearchRepository : ISearchRepository
TvContext dbContext = _dbContextFactory.CreateDbContext(); TvContext dbContext = _dbContextFactory.CreateDbContext();
return dbContext.MediaItems return dbContext.MediaItems
.AsNoTracking() .AsNoTracking()
.Include(mi => mi.Collections)
.Include(mi => mi.LibraryPath) .Include(mi => mi.LibraryPath)
.ThenInclude(lp => lp.Library) .ThenInclude(lp => lp.Library)
.Include(mi => (mi as Movie).MovieMetadata) .Include(mi => (mi as Movie).MovieMetadata)

14
ErsatzTV.Infrastructure/Search/ElasticSearchIndex.cs

@ -324,6 +324,7 @@ public class ElasticSearchIndex : ISearchIndex
}; };
AddStatistics(doc, movie.MediaVersions); AddStatistics(doc, movie.MediaVersions);
AddCollections(doc, movie.Collections);
foreach ((string key, List<string> value) in GetMetadataGuids(metadata)) foreach ((string key, List<string> value) in GetMetadataGuids(metadata))
{ {
@ -551,6 +552,7 @@ public class ElasticSearchIndex : ISearchIndex
doc.Artist = artists.ToList(); doc.Artist = artists.ToList();
AddStatistics(doc, musicVideo.MediaVersions); AddStatistics(doc, musicVideo.MediaVersions);
AddCollections(doc, musicVideo.Collections);
foreach ((string key, List<string> value) in GetMetadataGuids(metadata)) foreach ((string key, List<string> value) in GetMetadataGuids(metadata))
{ {
@ -630,6 +632,7 @@ public class ElasticSearchIndex : ISearchIndex
} }
AddStatistics(doc, episode.MediaVersions); AddStatistics(doc, episode.MediaVersions);
AddCollections(doc, episode.Collections);
foreach ((string key, List<string> value) in GetMetadataGuids(metadata)) foreach ((string key, List<string> value) in GetMetadataGuids(metadata))
{ {
@ -682,6 +685,7 @@ public class ElasticSearchIndex : ISearchIndex
}; };
AddStatistics(doc, otherVideo.MediaVersions); AddStatistics(doc, otherVideo.MediaVersions);
AddCollections(doc, otherVideo.Collections);
foreach ((string key, List<string> value) in GetMetadataGuids(metadata)) foreach ((string key, List<string> value) in GetMetadataGuids(metadata))
{ {
@ -730,6 +734,7 @@ public class ElasticSearchIndex : ISearchIndex
}; };
AddStatistics(doc, song.MediaVersions); AddStatistics(doc, song.MediaVersions);
AddCollections(doc, song.Collections);
foreach ((string key, List<string> value) in GetMetadataGuids(metadata)) foreach ((string key, List<string> value) in GetMetadataGuids(metadata))
{ {
@ -784,6 +789,7 @@ public class ElasticSearchIndex : ISearchIndex
} }
AddStatistics(doc, image.MediaVersions); AddStatistics(doc, image.MediaVersions);
AddCollections(doc, image.Collections);
foreach ((string key, List<string> value) in GetMetadataGuids(metadata)) foreach ((string key, List<string> value) in GetMetadataGuids(metadata))
{ {
@ -939,6 +945,14 @@ public class ElasticSearchIndex : ISearchIndex
} }
} }
private static void AddCollections(ElasticSearchItem doc, IEnumerable<Collection> collections)
{
foreach (Collection collection in collections)
{
doc.Collection.Add(collection.Name);
}
}
private static Dictionary<string, List<string>> GetMetadataGuids(Core.Domain.Metadata metadata) private static Dictionary<string, List<string>> GetMetadataGuids(Core.Domain.Metadata metadata)
{ {
var result = new Dictionary<string, List<string>>(); var result = new Dictionary<string, List<string>>();

21
ErsatzTV.Infrastructure/Search/LuceneSearchIndex.cs

@ -64,7 +64,8 @@ public sealed class LuceneSearchIndex : ISearchIndex
internal const string ShowContentRatingField = "show_content_rating"; internal const string ShowContentRatingField = "show_content_rating";
internal const string MetadataKindField = "metadata_kind"; internal const string MetadataKindField = "metadata_kind";
internal const string VideoCodecField = "video_codec"; internal const string VideoCodecField = "video_codec";
internal const string VideoDynamicRange = "video_dynamic_range"; internal const string VideoDynamicRangeField = "video_dynamic_range";
internal const string CollectionField = "collection";
internal const string MinutesField = "minutes"; internal const string MinutesField = "minutes";
internal const string SecondsField = "seconds"; internal const string SecondsField = "seconds";
@ -111,7 +112,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
return Task.FromResult(directoryExists && fileExists); return Task.FromResult(directoryExists && fileExists);
} }
public int Version => 45; public int Version => 46;
public async Task<bool> Initialize( public async Task<bool> Initialize(
ILocalFileSystem localFileSystem, ILocalFileSystem localFileSystem,
@ -425,6 +426,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
await AddLanguages(searchRepository, doc, movie.MediaVersions); await AddLanguages(searchRepository, doc, movie.MediaVersions);
AddStatistics(doc, movie.MediaVersions); AddStatistics(doc, movie.MediaVersions);
AddCollections(doc, movie.Collections);
if (!string.IsNullOrWhiteSpace(metadata.ContentRating)) if (!string.IsNullOrWhiteSpace(metadata.ContentRating))
{ {
@ -898,6 +900,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
await AddLanguages(searchRepository, doc, musicVideo.MediaVersions); await AddLanguages(searchRepository, doc, musicVideo.MediaVersions);
AddStatistics(doc, musicVideo.MediaVersions); AddStatistics(doc, musicVideo.MediaVersions);
AddCollections(doc, musicVideo.Collections);
if (metadata.ReleaseDate.HasValue) if (metadata.ReleaseDate.HasValue)
{ {
@ -1052,6 +1055,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
await AddLanguages(searchRepository, doc, episode.MediaVersions); await AddLanguages(searchRepository, doc, episode.MediaVersions);
AddStatistics(doc, episode.MediaVersions); AddStatistics(doc, episode.MediaVersions);
AddCollections(doc, episode.Collections);
if (metadata.ReleaseDate.HasValue) if (metadata.ReleaseDate.HasValue)
{ {
@ -1154,6 +1158,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
await AddLanguages(searchRepository, doc, otherVideo.MediaVersions); await AddLanguages(searchRepository, doc, otherVideo.MediaVersions);
AddStatistics(doc, otherVideo.MediaVersions); AddStatistics(doc, otherVideo.MediaVersions);
AddCollections(doc, otherVideo.Collections);
if (!string.IsNullOrWhiteSpace(metadata.ContentRating)) if (!string.IsNullOrWhiteSpace(metadata.ContentRating))
{ {
@ -1256,6 +1261,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
await AddLanguages(searchRepository, doc, song.MediaVersions); await AddLanguages(searchRepository, doc, song.MediaVersions);
AddStatistics(doc, song.MediaVersions); AddStatistics(doc, song.MediaVersions);
AddCollections(doc, song.Collections);
doc.Add( doc.Add(
new StringField( new StringField(
@ -1343,6 +1349,7 @@ public sealed class LuceneSearchIndex : ISearchIndex
await AddLanguages(searchRepository, doc, image.MediaVersions); await AddLanguages(searchRepository, doc, image.MediaVersions);
AddStatistics(doc, image.MediaVersions); AddStatistics(doc, image.MediaVersions);
AddCollections(doc, image.Collections);
doc.Add( doc.Add(
new StringField( new StringField(
@ -1417,11 +1424,19 @@ public sealed class LuceneSearchIndex : ISearchIndex
string dynamicRange = colorParams.IsHdr ? "hdr" : "sdr"; string dynamicRange = colorParams.IsHdr ? "hdr" : "sdr";
doc.Add(new StringField(VideoDynamicRange, dynamicRange, Field.Store.NO)); doc.Add(new StringField(VideoDynamicRangeField, dynamicRange, Field.Store.NO));
} }
} }
} }
private static void AddCollections(Document doc, List<Collection> collections)
{
foreach (Collection collection in collections)
{
doc.Add(new StringField(CollectionField, collection.Name.ToLowerInvariant(), Field.Store.NO));
}
}
private static void AddMetadataGuids(Core.Domain.Metadata metadata, Document doc) private static void AddMetadataGuids(Core.Domain.Metadata metadata, Document doc)
{ {
foreach (MetadataGuid guid in metadata.Guids) foreach (MetadataGuid guid in metadata.Guids)

5
ErsatzTV.Infrastructure/Search/Models/ElasticSearchItem.cs

@ -55,9 +55,12 @@ public class ElasticSearchItem : MinimalElasticSearchItem
[JsonPropertyName(LuceneSearchIndex.VideoBitDepthField)] [JsonPropertyName(LuceneSearchIndex.VideoBitDepthField)]
public int VideoBitDepth { get; set; } public int VideoBitDepth { get; set; }
[JsonPropertyName(LuceneSearchIndex.VideoDynamicRange)] [JsonPropertyName(LuceneSearchIndex.VideoDynamicRangeField)]
public string VideoDynamicRange { get; set; } public string VideoDynamicRange { get; set; }
[JsonPropertyName(LuceneSearchIndex.CollectionField)]
public List<string> Collection { get; set; }
[JsonPropertyName(LuceneSearchIndex.ContentRatingField)] [JsonPropertyName(LuceneSearchIndex.ContentRatingField)]
public List<string> ContentRating { get; set; } public List<string> ContentRating { get; set; }

3
ErsatzTV.Infrastructure/Search/SearchQueryParser.cs

@ -34,8 +34,9 @@ public static class SearchQueryParser
{ LuceneSearchIndex.ShowContentRatingField, keywordAnalyzer }, { LuceneSearchIndex.ShowContentRatingField, keywordAnalyzer },
{ LuceneSearchIndex.LibraryFolderIdField, keywordAnalyzer }, { LuceneSearchIndex.LibraryFolderIdField, keywordAnalyzer },
{ LuceneSearchIndex.VideoCodecField, keywordAnalyzer }, { LuceneSearchIndex.VideoCodecField, keywordAnalyzer },
{ LuceneSearchIndex.VideoDynamicRange, keywordAnalyzer }, { LuceneSearchIndex.VideoDynamicRangeField, keywordAnalyzer },
{ LuceneSearchIndex.TagFullField, keywordAnalyzer }, { LuceneSearchIndex.TagFullField, keywordAnalyzer },
{ LuceneSearchIndex.CollectionField, keywordAnalyzer },
{ LuceneSearchIndex.PlotField, new StandardAnalyzer(LuceneSearchIndex.AppLuceneVersion) } { LuceneSearchIndex.PlotField, new StandardAnalyzer(LuceneSearchIndex.AppLuceneVersion) }
}; };

Loading…
Cancel
Save