send all audio streams on hls channels with no preferred language (#142)
* Revert "fix search index threading (#141)"
This reverts commit 3fb6da0754.
* send all audio streams on hls channels with no preferred language
This commit is contained in:
@@ -1,6 +0,0 @@
|
|||||||
namespace ErsatzTV.Application
|
|
||||||
{
|
|
||||||
public interface ISearchBackgroundServiceRequest
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Channels;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Application.Search.Commands;
|
|
||||||
using ErsatzTV.Core;
|
using ErsatzTV.Core;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Interfaces.Locking;
|
using ErsatzTV.Core.Interfaces.Locking;
|
||||||
@@ -29,7 +26,6 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
|||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
private readonly IMovieFolderScanner _movieFolderScanner;
|
private readonly IMovieFolderScanner _movieFolderScanner;
|
||||||
private readonly IMusicVideoFolderScanner _musicVideoFolderScanner;
|
private readonly IMusicVideoFolderScanner _musicVideoFolderScanner;
|
||||||
private readonly ChannelWriter<ISearchBackgroundServiceRequest> _searchChannel;
|
|
||||||
private readonly ITelevisionFolderScanner _televisionFolderScanner;
|
private readonly ITelevisionFolderScanner _televisionFolderScanner;
|
||||||
|
|
||||||
public ScanLocalLibraryHandler(
|
public ScanLocalLibraryHandler(
|
||||||
@@ -40,8 +36,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
|||||||
IMusicVideoFolderScanner musicVideoFolderScanner,
|
IMusicVideoFolderScanner musicVideoFolderScanner,
|
||||||
IEntityLocker entityLocker,
|
IEntityLocker entityLocker,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
ILogger<ScanLocalLibraryHandler> logger,
|
ILogger<ScanLocalLibraryHandler> logger)
|
||||||
ChannelWriter<ISearchBackgroundServiceRequest> searchChannel)
|
|
||||||
{
|
{
|
||||||
_libraryRepository = libraryRepository;
|
_libraryRepository = libraryRepository;
|
||||||
_configElementRepository = configElementRepository;
|
_configElementRepository = configElementRepository;
|
||||||
@@ -51,7 +46,6 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
|||||||
_entityLocker = entityLocker;
|
_entityLocker = entityLocker;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_searchChannel = searchChannel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Either<BaseError, string>> Handle(
|
public Task<Either<BaseError, string>> Handle(
|
||||||
@@ -93,9 +87,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
|||||||
ffprobePath,
|
ffprobePath,
|
||||||
lastScan,
|
lastScan,
|
||||||
progressMin,
|
progressMin,
|
||||||
progressMax,
|
progressMax);
|
||||||
AddToSearchIndex,
|
|
||||||
RemoveFromSearchIndex);
|
|
||||||
break;
|
break;
|
||||||
case LibraryMediaKind.Shows:
|
case LibraryMediaKind.Shows:
|
||||||
await _televisionFolderScanner.ScanFolder(
|
await _televisionFolderScanner.ScanFolder(
|
||||||
@@ -103,9 +95,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
|||||||
ffprobePath,
|
ffprobePath,
|
||||||
lastScan,
|
lastScan,
|
||||||
progressMin,
|
progressMin,
|
||||||
progressMax,
|
progressMax);
|
||||||
AddToSearchIndex,
|
|
||||||
RemoveFromSearchIndex);
|
|
||||||
break;
|
break;
|
||||||
case LibraryMediaKind.MusicVideos:
|
case LibraryMediaKind.MusicVideos:
|
||||||
await _musicVideoFolderScanner.ScanFolder(
|
await _musicVideoFolderScanner.ScanFolder(
|
||||||
@@ -113,9 +103,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
|||||||
ffprobePath,
|
ffprobePath,
|
||||||
lastScan,
|
lastScan,
|
||||||
progressMin,
|
progressMin,
|
||||||
progressMax,
|
progressMax);
|
||||||
AddToSearchIndex,
|
|
||||||
RemoveFromSearchIndex);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +126,6 @@ namespace ErsatzTV.Application.MediaSources.Commands
|
|||||||
return Unit.Default;
|
return Unit.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValueTask AddToSearchIndex(List<MediaItem> mediaItems) =>
|
|
||||||
_searchChannel.WriteAsync(new AddItemsToSearchIndex(mediaItems));
|
|
||||||
|
|
||||||
private ValueTask RemoveFromSearchIndex(List<int> mediaItemIds) =>
|
|
||||||
_searchChannel.WriteAsync(new RemoveItemsFromSearchIndex(mediaItemIds));
|
|
||||||
|
|
||||||
private async Task<Validation<BaseError, RequestParameters>> Validate(IScanLocalLibrary request) =>
|
private async Task<Validation<BaseError, RequestParameters>> Validate(IScanLocalLibrary request) =>
|
||||||
(await LocalLibraryMustExist(request), await ValidateFFprobePath())
|
(await LocalLibraryMustExist(request), await ValidateFFprobePath())
|
||||||
.Apply(
|
.Apply(
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Channels;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Application.Search.Commands;
|
|
||||||
using ErsatzTV.Core;
|
using ErsatzTV.Core;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Interfaces.Locking;
|
using ErsatzTV.Core.Interfaces.Locking;
|
||||||
@@ -29,7 +26,6 @@ namespace ErsatzTV.Application.Plex.Commands
|
|||||||
private readonly IPlexMovieLibraryScanner _plexMovieLibraryScanner;
|
private readonly IPlexMovieLibraryScanner _plexMovieLibraryScanner;
|
||||||
private readonly IPlexSecretStore _plexSecretStore;
|
private readonly IPlexSecretStore _plexSecretStore;
|
||||||
private readonly IPlexTelevisionLibraryScanner _plexTelevisionLibraryScanner;
|
private readonly IPlexTelevisionLibraryScanner _plexTelevisionLibraryScanner;
|
||||||
private readonly ChannelWriter<ISearchBackgroundServiceRequest> _searchChannel;
|
|
||||||
|
|
||||||
public SynchronizePlexLibraryByIdHandler(
|
public SynchronizePlexLibraryByIdHandler(
|
||||||
IMediaSourceRepository mediaSourceRepository,
|
IMediaSourceRepository mediaSourceRepository,
|
||||||
@@ -38,8 +34,7 @@ namespace ErsatzTV.Application.Plex.Commands
|
|||||||
IPlexTelevisionLibraryScanner plexTelevisionLibraryScanner,
|
IPlexTelevisionLibraryScanner plexTelevisionLibraryScanner,
|
||||||
ILibraryRepository libraryRepository,
|
ILibraryRepository libraryRepository,
|
||||||
IEntityLocker entityLocker,
|
IEntityLocker entityLocker,
|
||||||
ILogger<SynchronizePlexLibraryByIdHandler> logger,
|
ILogger<SynchronizePlexLibraryByIdHandler> logger)
|
||||||
ChannelWriter<ISearchBackgroundServiceRequest> searchChannel)
|
|
||||||
{
|
{
|
||||||
_mediaSourceRepository = mediaSourceRepository;
|
_mediaSourceRepository = mediaSourceRepository;
|
||||||
_plexSecretStore = plexSecretStore;
|
_plexSecretStore = plexSecretStore;
|
||||||
@@ -48,7 +43,6 @@ namespace ErsatzTV.Application.Plex.Commands
|
|||||||
_libraryRepository = libraryRepository;
|
_libraryRepository = libraryRepository;
|
||||||
_entityLocker = entityLocker;
|
_entityLocker = entityLocker;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_searchChannel = searchChannel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Either<BaseError, string>> Handle(
|
public Task<Either<BaseError, string>> Handle(
|
||||||
@@ -76,17 +70,13 @@ namespace ErsatzTV.Application.Plex.Commands
|
|||||||
await _plexMovieLibraryScanner.ScanLibrary(
|
await _plexMovieLibraryScanner.ScanLibrary(
|
||||||
parameters.ConnectionParameters.ActiveConnection,
|
parameters.ConnectionParameters.ActiveConnection,
|
||||||
parameters.ConnectionParameters.PlexServerAuthToken,
|
parameters.ConnectionParameters.PlexServerAuthToken,
|
||||||
parameters.Library,
|
parameters.Library);
|
||||||
AddToSearchIndex,
|
|
||||||
RemoveFromSearchIndex);
|
|
||||||
break;
|
break;
|
||||||
case LibraryMediaKind.Shows:
|
case LibraryMediaKind.Shows:
|
||||||
await _plexTelevisionLibraryScanner.ScanLibrary(
|
await _plexTelevisionLibraryScanner.ScanLibrary(
|
||||||
parameters.ConnectionParameters.ActiveConnection,
|
parameters.ConnectionParameters.ActiveConnection,
|
||||||
parameters.ConnectionParameters.PlexServerAuthToken,
|
parameters.ConnectionParameters.PlexServerAuthToken,
|
||||||
parameters.Library,
|
parameters.Library);
|
||||||
AddToSearchIndex,
|
|
||||||
RemoveFromSearchIndex);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,12 +94,6 @@ namespace ErsatzTV.Application.Plex.Commands
|
|||||||
return Unit.Default;
|
return Unit.Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValueTask AddToSearchIndex(List<MediaItem> mediaItems) =>
|
|
||||||
_searchChannel.WriteAsync(new AddItemsToSearchIndex(mediaItems));
|
|
||||||
|
|
||||||
private ValueTask RemoveFromSearchIndex(List<int> mediaItemIds) =>
|
|
||||||
_searchChannel.WriteAsync(new RemoveItemsFromSearchIndex(mediaItemIds));
|
|
||||||
|
|
||||||
private async Task<Validation<BaseError, RequestParameters>> Validate(ISynchronizePlexLibraryById request) =>
|
private async Task<Validation<BaseError, RequestParameters>> Validate(ISynchronizePlexLibraryById request) =>
|
||||||
(await ValidateConnection(request), await PlexLibraryMustExist(request))
|
(await ValidateConnection(request), await PlexLibraryMustExist(request))
|
||||||
.Apply(
|
.Apply(
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using ErsatzTV.Core.Domain;
|
|
||||||
using LanguageExt;
|
|
||||||
|
|
||||||
namespace ErsatzTV.Application.Search.Commands
|
|
||||||
{
|
|
||||||
public record AddItemsToSearchIndex(List<MediaItem> MediaItems) : MediatR.IRequest<Unit>,
|
|
||||||
ISearchBackgroundServiceRequest;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ErsatzTV.Core.Interfaces.Search;
|
|
||||||
using LanguageExt;
|
|
||||||
|
|
||||||
namespace ErsatzTV.Application.Search.Commands
|
|
||||||
{
|
|
||||||
public class AddItemsToSearchIndexHandler : MediatR.IRequestHandler<AddItemsToSearchIndex, Unit>
|
|
||||||
{
|
|
||||||
private readonly ISearchIndex _searchIndex;
|
|
||||||
|
|
||||||
public AddItemsToSearchIndexHandler(ISearchIndex searchIndex) => _searchIndex = searchIndex;
|
|
||||||
|
|
||||||
public Task<Unit> Handle(AddItemsToSearchIndex request, CancellationToken cancellationToken) =>
|
|
||||||
_searchIndex.AddItems(request.MediaItems);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
namespace ErsatzTV.Application.Search.Commands
|
namespace ErsatzTV.Application.Search.Commands
|
||||||
{
|
{
|
||||||
public record RebuildSearchIndex : MediatR.IRequest<Unit>, ISearchBackgroundServiceRequest;
|
public record RebuildSearchIndex : MediatR.IRequest<Unit>, IBackgroundServiceRequest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using LanguageExt;
|
|
||||||
|
|
||||||
namespace ErsatzTV.Application.Search.Commands
|
|
||||||
{
|
|
||||||
public record RemoveItemsFromSearchIndex(List<int> MediaItemIds) : MediatR.IRequest<Unit>,
|
|
||||||
ISearchBackgroundServiceRequest;
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ErsatzTV.Core.Interfaces.Search;
|
|
||||||
using LanguageExt;
|
|
||||||
|
|
||||||
namespace ErsatzTV.Application.Search.Commands
|
|
||||||
{
|
|
||||||
public class RemoveItemsFromSearchIndexHandler : MediatR.IRequestHandler<RemoveItemsFromSearchIndex, Unit>
|
|
||||||
{
|
|
||||||
private readonly ISearchIndex _searchIndex;
|
|
||||||
|
|
||||||
public RemoveItemsFromSearchIndexHandler(ISearchIndex searchIndex) => _searchIndex = searchIndex;
|
|
||||||
|
|
||||||
public Task<Unit> Handle(RemoveItemsFromSearchIndex request, CancellationToken cancellationToken) =>
|
|
||||||
_searchIndex.RemoveItems(request.MediaItemIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,7 @@ using ErsatzTV.Core.Errors;
|
|||||||
using ErsatzTV.Core.Interfaces.Images;
|
using ErsatzTV.Core.Interfaces.Images;
|
||||||
using ErsatzTV.Core.Interfaces.Metadata;
|
using ErsatzTV.Core.Interfaces.Metadata;
|
||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using ErsatzTV.Core.Interfaces.Search;
|
||||||
using ErsatzTV.Core.Metadata;
|
using ErsatzTV.Core.Metadata;
|
||||||
using ErsatzTV.Core.Tests.Fakes;
|
using ErsatzTV.Core.Tests.Fakes;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
@@ -87,9 +88,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
result.IsLeft.Should().BeTrue();
|
result.IsLeft.Should().BeTrue();
|
||||||
result.IfLeft(error => error.Should().BeOfType<MediaSourceInaccessible>());
|
result.IfLeft(error => error.Should().BeOfType<MediaSourceInaccessible>());
|
||||||
@@ -114,9 +113,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -157,10 +154,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -202,10 +196,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -251,10 +242,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -303,10 +291,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -355,10 +340,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -406,10 +388,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -453,10 +432,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -494,10 +470,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -537,9 +510,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -568,10 +539,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
FFprobePath,
|
FFprobePath,
|
||||||
DateTimeOffset.MinValue,
|
DateTimeOffset.MinValue,
|
||||||
0,
|
0,
|
||||||
1,
|
1);
|
||||||
_ => ValueTask.CompletedTask,
|
|
||||||
_ => ValueTask.CompletedTask);
|
|
||||||
|
|
||||||
|
|
||||||
result.IsRight.Should().BeTrue();
|
result.IsRight.Should().BeTrue();
|
||||||
|
|
||||||
@@ -588,6 +556,7 @@ namespace ErsatzTV.Core.Tests.Metadata
|
|||||||
_localMetadataProvider.Object,
|
_localMetadataProvider.Object,
|
||||||
new Mock<IMetadataRepository>().Object,
|
new Mock<IMetadataRepository>().Object,
|
||||||
_imageCache.Object,
|
_imageCache.Object,
|
||||||
|
new Mock<ISearchIndex>().Object,
|
||||||
new Mock<IMediator>().Object,
|
new Mock<IMediator>().Object,
|
||||||
new Mock<ILogger<MovieFolderScanner>>().Object
|
new Mock<ILogger<MovieFolderScanner>>().Object
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Option<FFmpegComplexFilter> Build(int videoStreamIndex, int audioStreamIndex)
|
public Option<FFmpegComplexFilter> Build(int videoStreamIndex, Option<int> audioStreamIndex)
|
||||||
{
|
{
|
||||||
var complexFilter = new StringBuilder();
|
var complexFilter = new StringBuilder();
|
||||||
|
|
||||||
var videoLabel = $"0:{videoStreamIndex}";
|
var videoLabel = $"0:{videoStreamIndex}";
|
||||||
var audioLabel = $"0:{audioStreamIndex}";
|
string audioLabel = audioStreamIndex.Match(index => $"0:{index}", () => "0:a");
|
||||||
|
|
||||||
HardwareAccelerationKind acceleration = _hardwareAccelerationKind.IfNone(HardwareAccelerationKind.None);
|
HardwareAccelerationKind acceleration = _hardwareAccelerationKind.IfNone(HardwareAccelerationKind.None);
|
||||||
bool isHardwareDecode = acceleration switch
|
bool isHardwareDecode = acceleration switch
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Interfaces.FFmpeg;
|
using ErsatzTV.Core.Interfaces.FFmpeg;
|
||||||
|
using LanguageExt;
|
||||||
using static LanguageExt.Prelude;
|
using static LanguageExt.Prelude;
|
||||||
|
|
||||||
namespace ErsatzTV.Core.FFmpeg
|
namespace ErsatzTV.Core.FFmpeg
|
||||||
@@ -46,7 +47,7 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
FFmpegProfile ffmpegProfile,
|
FFmpegProfile ffmpegProfile,
|
||||||
MediaVersion version,
|
MediaVersion version,
|
||||||
MediaStream videoStream,
|
MediaStream videoStream,
|
||||||
MediaStream audioStream,
|
Option<MediaStream> audioStream,
|
||||||
DateTimeOffset start,
|
DateTimeOffset start,
|
||||||
DateTimeOffset now)
|
DateTimeOffset now)
|
||||||
{
|
{
|
||||||
@@ -113,10 +114,14 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
result.AudioBitrate = ffmpegProfile.AudioBitrate;
|
result.AudioBitrate = ffmpegProfile.AudioBitrate;
|
||||||
result.AudioBufferSize = ffmpegProfile.AudioBufferSize;
|
result.AudioBufferSize = ffmpegProfile.AudioBufferSize;
|
||||||
|
|
||||||
if (audioStream.Channels != ffmpegProfile.AudioChannels)
|
audioStream.IfSome(
|
||||||
{
|
stream =>
|
||||||
result.AudioChannels = ffmpegProfile.AudioChannels;
|
{
|
||||||
}
|
if (stream.Channels != ffmpegProfile.AudioChannels)
|
||||||
|
{
|
||||||
|
result.AudioChannels = ffmpegProfile.AudioChannels;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
result.AudioSampleRate = ffmpegProfile.AudioSampleRate;
|
result.AudioSampleRate = ffmpegProfile.AudioSampleRate;
|
||||||
result.AudioDuration = version.Duration;
|
result.AudioDuration = version.Duration;
|
||||||
|
|||||||
@@ -354,12 +354,15 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FFmpegProcessBuilder WithFilterComplex(int videoStreamIndex, int audioStreamIndex)
|
public FFmpegProcessBuilder WithFilterComplex(MediaStream videoStream, Option<MediaStream> maybeAudioStream)
|
||||||
{
|
{
|
||||||
var videoLabel = $"0:{videoStreamIndex}";
|
int videoStreamIndex = videoStream.Index;
|
||||||
var audioLabel = $"0:{audioStreamIndex}";
|
Option<int> maybeIndex = maybeAudioStream.Map(ms => ms.Index);
|
||||||
|
|
||||||
Option<FFmpegComplexFilter> maybeFilter = _complexFilterBuilder.Build(videoStreamIndex, audioStreamIndex);
|
var videoLabel = $"0:{videoStreamIndex}";
|
||||||
|
var audioLabel = $"0:{maybeIndex.Match(i => i.ToString(), () => "a")}";
|
||||||
|
|
||||||
|
Option<FFmpegComplexFilter> maybeFilter = _complexFilterBuilder.Build(videoStreamIndex, maybeIndex);
|
||||||
maybeFilter.IfSome(
|
maybeFilter.IfSome(
|
||||||
filter =>
|
filter =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
DateTimeOffset now)
|
DateTimeOffset now)
|
||||||
{
|
{
|
||||||
MediaStream videoStream = await _ffmpegStreamSelector.SelectVideoStream(channel, version);
|
MediaStream videoStream = await _ffmpegStreamSelector.SelectVideoStream(channel, version);
|
||||||
MediaStream audioStream = await _ffmpegStreamSelector.SelectAudioStream(channel, version);
|
Option<MediaStream> maybeAudioStream = await _ffmpegStreamSelector.SelectAudioStream(channel, version);
|
||||||
|
|
||||||
FFmpegPlaybackSettings playbackSettings = _playbackSettingsCalculator.CalculateSettings(
|
FFmpegPlaybackSettings playbackSettings = _playbackSettingsCalculator.CalculateSettings(
|
||||||
channel.StreamingMode,
|
channel.StreamingMode,
|
||||||
channel.FFmpegProfile,
|
channel.FFmpegProfile,
|
||||||
version,
|
version,
|
||||||
videoStream,
|
videoStream,
|
||||||
audioStream,
|
maybeAudioStream,
|
||||||
start,
|
start,
|
||||||
now);
|
now);
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
}
|
}
|
||||||
|
|
||||||
builder = builder
|
builder = builder
|
||||||
.WithFilterComplex(videoStream.Index, audioStream.Index);
|
.WithFilterComplex(videoStream, maybeAudioStream);
|
||||||
},
|
},
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
@@ -76,18 +76,18 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
builder = builder
|
builder = builder
|
||||||
.WithDeinterlace(playbackSettings.Deinterlace)
|
.WithDeinterlace(playbackSettings.Deinterlace)
|
||||||
.WithBlackBars(channel.FFmpegProfile.Resolution)
|
.WithBlackBars(channel.FFmpegProfile.Resolution)
|
||||||
.WithFilterComplex(videoStream.Index, audioStream.Index);
|
.WithFilterComplex(videoStream, maybeAudioStream);
|
||||||
}
|
}
|
||||||
else if (playbackSettings.Deinterlace)
|
else if (playbackSettings.Deinterlace)
|
||||||
{
|
{
|
||||||
builder = builder.WithDeinterlace(playbackSettings.Deinterlace)
|
builder = builder.WithDeinterlace(playbackSettings.Deinterlace)
|
||||||
.WithAlignedAudio(playbackSettings.AudioDuration)
|
.WithAlignedAudio(playbackSettings.AudioDuration)
|
||||||
.WithFilterComplex(videoStream.Index, audioStream.Index);
|
.WithFilterComplex(videoStream, maybeAudioStream);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
builder = builder
|
builder = builder
|
||||||
.WithFilterComplex(videoStream.Index, audioStream.Index);
|
.WithFilterComplex(videoStream, maybeAudioStream);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using ErsatzTV.Core.Interfaces.FFmpeg;
|
|||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using static LanguageExt.Prelude;
|
||||||
|
|
||||||
namespace ErsatzTV.Core.FFmpeg
|
namespace ErsatzTV.Core.FFmpeg
|
||||||
{
|
{
|
||||||
@@ -25,8 +26,17 @@ namespace ErsatzTV.Core.FFmpeg
|
|||||||
public Task<MediaStream> SelectVideoStream(Channel channel, MediaVersion version) =>
|
public Task<MediaStream> SelectVideoStream(Channel channel, MediaVersion version) =>
|
||||||
version.Streams.First(s => s.MediaStreamKind == MediaStreamKind.Video).AsTask();
|
version.Streams.First(s => s.MediaStreamKind == MediaStreamKind.Video).AsTask();
|
||||||
|
|
||||||
public async Task<MediaStream> SelectAudioStream(Channel channel, MediaVersion version)
|
public async Task<Option<MediaStream>> SelectAudioStream(Channel channel, MediaVersion version)
|
||||||
{
|
{
|
||||||
|
if (channel.StreamingMode == StreamingMode.HttpLiveStreaming &&
|
||||||
|
string.IsNullOrWhiteSpace(channel.PreferredLanguageCode))
|
||||||
|
{
|
||||||
|
_logger.LogDebug(
|
||||||
|
"Channel {Number} is HLS with no preferred language; using all audio streams",
|
||||||
|
channel.Number);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
var audioStreams = version.Streams.Filter(s => s.MediaStreamKind == MediaStreamKind.Audio).ToList();
|
var audioStreams = version.Streams.Filter(s => s.MediaStreamKind == MediaStreamKind.Audio).ToList();
|
||||||
|
|
||||||
string language = (channel.PreferredLanguageCode ?? string.Empty).ToLowerInvariant();
|
string language = (channel.PreferredLanguageCode ?? string.Empty).ToLowerInvariant();
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
|
using LanguageExt;
|
||||||
|
|
||||||
namespace ErsatzTV.Core.Interfaces.FFmpeg
|
namespace ErsatzTV.Core.Interfaces.FFmpeg
|
||||||
{
|
{
|
||||||
public interface IFFmpegStreamSelector
|
public interface IFFmpegStreamSelector
|
||||||
{
|
{
|
||||||
Task<MediaStream> SelectVideoStream(Channel channel, MediaVersion version);
|
Task<MediaStream> SelectVideoStream(Channel channel, MediaVersion version);
|
||||||
Task<MediaStream> SelectAudioStream(Channel channel, MediaVersion version);
|
Task<Option<MediaStream>> SelectAudioStream(Channel channel, MediaVersion version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
@@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
|
|||||||
string ffprobePath,
|
string ffprobePath,
|
||||||
DateTimeOffset lastScan,
|
DateTimeOffset lastScan,
|
||||||
decimal progressMin,
|
decimal progressMin,
|
||||||
decimal progressMax,
|
decimal progressMax);
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
@@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
|
|||||||
string ffprobePath,
|
string ffprobePath,
|
||||||
DateTimeOffset lastScan,
|
DateTimeOffset lastScan,
|
||||||
decimal progressMin,
|
decimal progressMin,
|
||||||
decimal progressMax,
|
decimal progressMax);
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
@@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
|
|||||||
string ffprobePath,
|
string ffprobePath,
|
||||||
DateTimeOffset lastScan,
|
DateTimeOffset lastScan,
|
||||||
decimal progressMin,
|
decimal progressMin,
|
||||||
decimal progressMax,
|
decimal progressMax);
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Plex;
|
using ErsatzTV.Core.Plex;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
@@ -12,8 +10,6 @@ namespace ErsatzTV.Core.Interfaces.Plex
|
|||||||
Task<Either<BaseError, Unit>> ScanLibrary(
|
Task<Either<BaseError, Unit>> ScanLibrary(
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
PlexLibrary plexMediaSourceLibrary,
|
PlexLibrary plexMediaSourceLibrary);
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Plex;
|
using ErsatzTV.Core.Plex;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
@@ -12,8 +10,6 @@ namespace ErsatzTV.Core.Interfaces.Plex
|
|||||||
Task<Either<BaseError, Unit>> ScanLibrary(
|
Task<Either<BaseError, Unit>> ScanLibrary(
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
PlexLibrary plexMediaSourceLibrary,
|
PlexLibrary plexMediaSourceLibrary);
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors;
|
|||||||
using ErsatzTV.Core.Interfaces.Images;
|
using ErsatzTV.Core.Interfaces.Images;
|
||||||
using ErsatzTV.Core.Interfaces.Metadata;
|
using ErsatzTV.Core.Interfaces.Metadata;
|
||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using ErsatzTV.Core.Interfaces.Search;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -24,6 +25,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
private readonly ILogger<MovieFolderScanner> _logger;
|
private readonly ILogger<MovieFolderScanner> _logger;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
private readonly IMovieRepository _movieRepository;
|
private readonly IMovieRepository _movieRepository;
|
||||||
|
private readonly ISearchIndex _searchIndex;
|
||||||
|
|
||||||
public MovieFolderScanner(
|
public MovieFolderScanner(
|
||||||
ILocalFileSystem localFileSystem,
|
ILocalFileSystem localFileSystem,
|
||||||
@@ -32,6 +34,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
ILocalMetadataProvider localMetadataProvider,
|
ILocalMetadataProvider localMetadataProvider,
|
||||||
IMetadataRepository metadataRepository,
|
IMetadataRepository metadataRepository,
|
||||||
IImageCache imageCache,
|
IImageCache imageCache,
|
||||||
|
ISearchIndex searchIndex,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
ILogger<MovieFolderScanner> logger)
|
ILogger<MovieFolderScanner> logger)
|
||||||
: base(localFileSystem, localStatisticsProvider, metadataRepository, imageCache, logger)
|
: base(localFileSystem, localStatisticsProvider, metadataRepository, imageCache, logger)
|
||||||
@@ -39,6 +42,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
_localFileSystem = localFileSystem;
|
_localFileSystem = localFileSystem;
|
||||||
_movieRepository = movieRepository;
|
_movieRepository = movieRepository;
|
||||||
_localMetadataProvider = localMetadataProvider;
|
_localMetadataProvider = localMetadataProvider;
|
||||||
|
_searchIndex = searchIndex;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -48,9 +52,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
string ffprobePath,
|
string ffprobePath,
|
||||||
DateTimeOffset lastScan,
|
DateTimeOffset lastScan,
|
||||||
decimal progressMin,
|
decimal progressMin,
|
||||||
decimal progressMax,
|
decimal progressMax)
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex)
|
|
||||||
{
|
{
|
||||||
decimal progressSpread = progressMax - progressMin;
|
decimal progressSpread = progressMax - progressMin;
|
||||||
|
|
||||||
@@ -109,7 +111,17 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
.BindT(movie => UpdateArtwork(movie, ArtworkKind.FanArt));
|
.BindT(movie => UpdateArtwork(movie, ArtworkKind.FanArt));
|
||||||
|
|
||||||
await maybeMovie.Match(
|
await maybeMovie.Match(
|
||||||
async result => await addToSearchIndex(new List<MediaItem> { result.Item }),
|
async result =>
|
||||||
|
{
|
||||||
|
if (result.IsAdded)
|
||||||
|
{
|
||||||
|
await _searchIndex.AddItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
else if (result.IsUpdated)
|
||||||
|
{
|
||||||
|
await _searchIndex.UpdateItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
},
|
||||||
error =>
|
error =>
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Error processing movie at {Path}: {Error}", file, error.Value);
|
_logger.LogWarning("Error processing movie at {Path}: {Error}", file, error.Value);
|
||||||
@@ -124,7 +136,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("Removing missing movie at {Path}", path);
|
_logger.LogInformation("Removing missing movie at {Path}", path);
|
||||||
List<int> ids = await _movieRepository.DeleteByPath(libraryPath, path);
|
List<int> ids = await _movieRepository.DeleteByPath(libraryPath, path);
|
||||||
await removeFromSearchIndex(ids);
|
await _searchIndex.RemoveItems(ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors;
|
|||||||
using ErsatzTV.Core.Interfaces.Images;
|
using ErsatzTV.Core.Interfaces.Images;
|
||||||
using ErsatzTV.Core.Interfaces.Metadata;
|
using ErsatzTV.Core.Interfaces.Metadata;
|
||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using ErsatzTV.Core.Interfaces.Search;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -23,6 +24,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
private readonly ILogger<MusicVideoFolderScanner> _logger;
|
private readonly ILogger<MusicVideoFolderScanner> _logger;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
private readonly IMusicVideoRepository _musicVideoRepository;
|
private readonly IMusicVideoRepository _musicVideoRepository;
|
||||||
|
private readonly ISearchIndex _searchIndex;
|
||||||
|
|
||||||
public MusicVideoFolderScanner(
|
public MusicVideoFolderScanner(
|
||||||
ILocalFileSystem localFileSystem,
|
ILocalFileSystem localFileSystem,
|
||||||
@@ -30,6 +32,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
ILocalMetadataProvider localMetadataProvider,
|
ILocalMetadataProvider localMetadataProvider,
|
||||||
IMetadataRepository metadataRepository,
|
IMetadataRepository metadataRepository,
|
||||||
IImageCache imageCache,
|
IImageCache imageCache,
|
||||||
|
ISearchIndex searchIndex,
|
||||||
IMusicVideoRepository musicVideoRepository,
|
IMusicVideoRepository musicVideoRepository,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
ILogger<MusicVideoFolderScanner> logger) : base(
|
ILogger<MusicVideoFolderScanner> logger) : base(
|
||||||
@@ -41,6 +44,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
{
|
{
|
||||||
_localFileSystem = localFileSystem;
|
_localFileSystem = localFileSystem;
|
||||||
_localMetadataProvider = localMetadataProvider;
|
_localMetadataProvider = localMetadataProvider;
|
||||||
|
_searchIndex = searchIndex;
|
||||||
_musicVideoRepository = musicVideoRepository;
|
_musicVideoRepository = musicVideoRepository;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -51,9 +55,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
string ffprobePath,
|
string ffprobePath,
|
||||||
DateTimeOffset lastScan,
|
DateTimeOffset lastScan,
|
||||||
decimal progressMin,
|
decimal progressMin,
|
||||||
decimal progressMax,
|
decimal progressMax)
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex)
|
|
||||||
{
|
{
|
||||||
decimal progressSpread = progressMax - progressMin;
|
decimal progressSpread = progressMax - progressMin;
|
||||||
|
|
||||||
@@ -102,7 +104,17 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
.BindT(UpdateThumbnail);
|
.BindT(UpdateThumbnail);
|
||||||
|
|
||||||
await maybeMusicVideo.Match(
|
await maybeMusicVideo.Match(
|
||||||
async result => await addToSearchIndex(new List<MediaItem> { result.Item }),
|
async result =>
|
||||||
|
{
|
||||||
|
if (result.IsAdded)
|
||||||
|
{
|
||||||
|
await _searchIndex.AddItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
else if (result.IsUpdated)
|
||||||
|
{
|
||||||
|
await _searchIndex.UpdateItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
},
|
||||||
error =>
|
error =>
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Error processing music video at {Path}: {Error}", file, error.Value);
|
_logger.LogWarning("Error processing music video at {Path}: {Error}", file, error.Value);
|
||||||
@@ -117,7 +129,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("Removing missing music video at {Path}", path);
|
_logger.LogInformation("Removing missing music video at {Path}", path);
|
||||||
List<int> ids = await _musicVideoRepository.DeleteByPath(libraryPath, path);
|
List<int> ids = await _musicVideoRepository.DeleteByPath(libraryPath, path);
|
||||||
await removeFromSearchIndex(ids);
|
await _searchIndex.RemoveItems(ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors;
|
|||||||
using ErsatzTV.Core.Interfaces.Images;
|
using ErsatzTV.Core.Interfaces.Images;
|
||||||
using ErsatzTV.Core.Interfaces.Metadata;
|
using ErsatzTV.Core.Interfaces.Metadata;
|
||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using ErsatzTV.Core.Interfaces.Search;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -22,6 +23,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
private readonly ILocalMetadataProvider _localMetadataProvider;
|
private readonly ILocalMetadataProvider _localMetadataProvider;
|
||||||
private readonly ILogger<TelevisionFolderScanner> _logger;
|
private readonly ILogger<TelevisionFolderScanner> _logger;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
|
private readonly ISearchIndex _searchIndex;
|
||||||
private readonly ITelevisionRepository _televisionRepository;
|
private readonly ITelevisionRepository _televisionRepository;
|
||||||
|
|
||||||
public TelevisionFolderScanner(
|
public TelevisionFolderScanner(
|
||||||
@@ -31,6 +33,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
ILocalMetadataProvider localMetadataProvider,
|
ILocalMetadataProvider localMetadataProvider,
|
||||||
IMetadataRepository metadataRepository,
|
IMetadataRepository metadataRepository,
|
||||||
IImageCache imageCache,
|
IImageCache imageCache,
|
||||||
|
ISearchIndex searchIndex,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
ILogger<TelevisionFolderScanner> logger) : base(
|
ILogger<TelevisionFolderScanner> logger) : base(
|
||||||
localFileSystem,
|
localFileSystem,
|
||||||
@@ -42,6 +45,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
_localFileSystem = localFileSystem;
|
_localFileSystem = localFileSystem;
|
||||||
_televisionRepository = televisionRepository;
|
_televisionRepository = televisionRepository;
|
||||||
_localMetadataProvider = localMetadataProvider;
|
_localMetadataProvider = localMetadataProvider;
|
||||||
|
_searchIndex = searchIndex;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -51,9 +55,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
string ffprobePath,
|
string ffprobePath,
|
||||||
DateTimeOffset lastScan,
|
DateTimeOffset lastScan,
|
||||||
decimal progressMin,
|
decimal progressMin,
|
||||||
decimal progressMax,
|
decimal progressMax)
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex)
|
|
||||||
{
|
{
|
||||||
decimal progressSpread = progressMax - progressMin;
|
decimal progressSpread = progressMax - progressMin;
|
||||||
|
|
||||||
@@ -82,7 +84,15 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
await maybeShow.Match(
|
await maybeShow.Match(
|
||||||
async result =>
|
async result =>
|
||||||
{
|
{
|
||||||
await addToSearchIndex(new List<MediaItem> { result.Item });
|
if (result.IsAdded)
|
||||||
|
{
|
||||||
|
await _searchIndex.AddItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
else if (result.IsUpdated)
|
||||||
|
{
|
||||||
|
await _searchIndex.UpdateItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
|
||||||
await ScanSeasons(
|
await ScanSeasons(
|
||||||
libraryPath,
|
libraryPath,
|
||||||
ffprobePath,
|
ffprobePath,
|
||||||
@@ -112,7 +122,7 @@ namespace ErsatzTV.Core.Metadata
|
|||||||
|
|
||||||
await _televisionRepository.DeleteEmptySeasons(libraryPath);
|
await _televisionRepository.DeleteEmptySeasons(libraryPath);
|
||||||
List<int> ids = await _televisionRepository.DeleteEmptyShows(libraryPath);
|
List<int> ids = await _televisionRepository.DeleteEmptyShows(libraryPath);
|
||||||
await removeFromSearchIndex(ids);
|
await _searchIndex.RemoveItems(ids);
|
||||||
|
|
||||||
return Unit.Default;
|
return Unit.Default;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Interfaces.Plex;
|
using ErsatzTV.Core.Interfaces.Plex;
|
||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using ErsatzTV.Core.Interfaces.Search;
|
||||||
using ErsatzTV.Core.Metadata;
|
using ErsatzTV.Core.Metadata;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
@@ -20,11 +20,13 @@ namespace ErsatzTV.Core.Plex
|
|||||||
private readonly IMetadataRepository _metadataRepository;
|
private readonly IMetadataRepository _metadataRepository;
|
||||||
private readonly IMovieRepository _movieRepository;
|
private readonly IMovieRepository _movieRepository;
|
||||||
private readonly IPlexServerApiClient _plexServerApiClient;
|
private readonly IPlexServerApiClient _plexServerApiClient;
|
||||||
|
private readonly ISearchIndex _searchIndex;
|
||||||
|
|
||||||
public PlexMovieLibraryScanner(
|
public PlexMovieLibraryScanner(
|
||||||
IPlexServerApiClient plexServerApiClient,
|
IPlexServerApiClient plexServerApiClient,
|
||||||
IMovieRepository movieRepository,
|
IMovieRepository movieRepository,
|
||||||
IMetadataRepository metadataRepository,
|
IMetadataRepository metadataRepository,
|
||||||
|
ISearchIndex searchIndex,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
ILogger<PlexMovieLibraryScanner> logger)
|
ILogger<PlexMovieLibraryScanner> logger)
|
||||||
: base(metadataRepository, logger)
|
: base(metadataRepository, logger)
|
||||||
@@ -32,6 +34,7 @@ namespace ErsatzTV.Core.Plex
|
|||||||
_plexServerApiClient = plexServerApiClient;
|
_plexServerApiClient = plexServerApiClient;
|
||||||
_movieRepository = movieRepository;
|
_movieRepository = movieRepository;
|
||||||
_metadataRepository = metadataRepository;
|
_metadataRepository = metadataRepository;
|
||||||
|
_searchIndex = searchIndex;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -39,9 +42,7 @@ namespace ErsatzTV.Core.Plex
|
|||||||
public async Task<Either<BaseError, Unit>> ScanLibrary(
|
public async Task<Either<BaseError, Unit>> ScanLibrary(
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
PlexLibrary plexMediaSourceLibrary,
|
PlexLibrary plexMediaSourceLibrary)
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex)
|
|
||||||
{
|
{
|
||||||
Either<BaseError, List<PlexMovie>> entries = await _plexServerApiClient.GetMovieLibraryContents(
|
Either<BaseError, List<PlexMovie>> entries = await _plexServerApiClient.GetMovieLibraryContents(
|
||||||
plexMediaSourceLibrary,
|
plexMediaSourceLibrary,
|
||||||
@@ -64,7 +65,17 @@ namespace ErsatzTV.Core.Plex
|
|||||||
.BindT(existing => UpdateArtwork(existing, incoming));
|
.BindT(existing => UpdateArtwork(existing, incoming));
|
||||||
|
|
||||||
await maybeMovie.Match(
|
await maybeMovie.Match(
|
||||||
async result => await addToSearchIndex(new List<MediaItem> { result.Item }),
|
async result =>
|
||||||
|
{
|
||||||
|
if (result.IsAdded)
|
||||||
|
{
|
||||||
|
await _searchIndex.AddItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
else if (result.IsUpdated)
|
||||||
|
{
|
||||||
|
await _searchIndex.UpdateItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
},
|
||||||
error =>
|
error =>
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
@@ -77,7 +88,7 @@ namespace ErsatzTV.Core.Plex
|
|||||||
|
|
||||||
var movieKeys = movieEntries.Map(s => s.Key).ToList();
|
var movieKeys = movieEntries.Map(s => s.Key).ToList();
|
||||||
List<int> ids = await _movieRepository.RemoveMissingPlexMovies(plexMediaSourceLibrary, movieKeys);
|
List<int> ids = await _movieRepository.RemoveMissingPlexMovies(plexMediaSourceLibrary, movieKeys);
|
||||||
await removeFromSearchIndex(ids);
|
await _searchIndex.RemoveItems(ids);
|
||||||
|
|
||||||
await _mediator.Publish(new LibraryScanProgress(plexMediaSourceLibrary.Id, 0));
|
await _mediator.Publish(new LibraryScanProgress(plexMediaSourceLibrary.Id, 0));
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ErsatzTV.Core.Domain;
|
using ErsatzTV.Core.Domain;
|
||||||
using ErsatzTV.Core.Interfaces.Plex;
|
using ErsatzTV.Core.Interfaces.Plex;
|
||||||
using ErsatzTV.Core.Interfaces.Repositories;
|
using ErsatzTV.Core.Interfaces.Repositories;
|
||||||
|
using ErsatzTV.Core.Interfaces.Search;
|
||||||
using ErsatzTV.Core.Metadata;
|
using ErsatzTV.Core.Metadata;
|
||||||
using LanguageExt;
|
using LanguageExt;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
@@ -20,12 +20,14 @@ namespace ErsatzTV.Core.Plex
|
|||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
private readonly IMetadataRepository _metadataRepository;
|
private readonly IMetadataRepository _metadataRepository;
|
||||||
private readonly IPlexServerApiClient _plexServerApiClient;
|
private readonly IPlexServerApiClient _plexServerApiClient;
|
||||||
|
private readonly ISearchIndex _searchIndex;
|
||||||
private readonly ITelevisionRepository _televisionRepository;
|
private readonly ITelevisionRepository _televisionRepository;
|
||||||
|
|
||||||
public PlexTelevisionLibraryScanner(
|
public PlexTelevisionLibraryScanner(
|
||||||
IPlexServerApiClient plexServerApiClient,
|
IPlexServerApiClient plexServerApiClient,
|
||||||
ITelevisionRepository televisionRepository,
|
ITelevisionRepository televisionRepository,
|
||||||
IMetadataRepository metadataRepository,
|
IMetadataRepository metadataRepository,
|
||||||
|
ISearchIndex searchIndex,
|
||||||
IMediator mediator,
|
IMediator mediator,
|
||||||
ILogger<PlexTelevisionLibraryScanner> logger)
|
ILogger<PlexTelevisionLibraryScanner> logger)
|
||||||
: base(metadataRepository, logger)
|
: base(metadataRepository, logger)
|
||||||
@@ -33,6 +35,7 @@ namespace ErsatzTV.Core.Plex
|
|||||||
_plexServerApiClient = plexServerApiClient;
|
_plexServerApiClient = plexServerApiClient;
|
||||||
_televisionRepository = televisionRepository;
|
_televisionRepository = televisionRepository;
|
||||||
_metadataRepository = metadataRepository;
|
_metadataRepository = metadataRepository;
|
||||||
|
_searchIndex = searchIndex;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -40,9 +43,7 @@ namespace ErsatzTV.Core.Plex
|
|||||||
public async Task<Either<BaseError, Unit>> ScanLibrary(
|
public async Task<Either<BaseError, Unit>> ScanLibrary(
|
||||||
PlexConnection connection,
|
PlexConnection connection,
|
||||||
PlexServerAuthToken token,
|
PlexServerAuthToken token,
|
||||||
PlexLibrary plexMediaSourceLibrary,
|
PlexLibrary plexMediaSourceLibrary)
|
||||||
Func<List<MediaItem>, ValueTask> addToSearchIndex,
|
|
||||||
Func<List<int>, ValueTask> removeFromSearchIndex)
|
|
||||||
{
|
{
|
||||||
Either<BaseError, List<PlexShow>> entries = await _plexServerApiClient.GetShowLibraryContents(
|
Either<BaseError, List<PlexShow>> entries = await _plexServerApiClient.GetShowLibraryContents(
|
||||||
plexMediaSourceLibrary,
|
plexMediaSourceLibrary,
|
||||||
@@ -66,7 +67,15 @@ namespace ErsatzTV.Core.Plex
|
|||||||
await maybeShow.Match(
|
await maybeShow.Match(
|
||||||
async result =>
|
async result =>
|
||||||
{
|
{
|
||||||
await addToSearchIndex(new List<MediaItem> { result.Item });
|
if (result.IsAdded)
|
||||||
|
{
|
||||||
|
await _searchIndex.AddItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
else if (result.IsUpdated)
|
||||||
|
{
|
||||||
|
await _searchIndex.UpdateItems(new List<MediaItem> { result.Item });
|
||||||
|
}
|
||||||
|
|
||||||
await ScanSeasons(plexMediaSourceLibrary, result.Item, connection, token);
|
await ScanSeasons(plexMediaSourceLibrary, result.Item, connection, token);
|
||||||
},
|
},
|
||||||
error =>
|
error =>
|
||||||
@@ -82,7 +91,7 @@ namespace ErsatzTV.Core.Plex
|
|||||||
var showKeys = showEntries.Map(s => s.Key).ToList();
|
var showKeys = showEntries.Map(s => s.Key).ToList();
|
||||||
List<int> ids =
|
List<int> ids =
|
||||||
await _televisionRepository.RemoveMissingPlexShows(plexMediaSourceLibrary, showKeys);
|
await _televisionRepository.RemoveMissingPlexShows(plexMediaSourceLibrary, showKeys);
|
||||||
await removeFromSearchIndex(ids);
|
await _searchIndex.RemoveItems(ids);
|
||||||
|
|
||||||
await _mediator.Publish(new LibraryScanProgress(plexMediaSourceLibrary.Id, 0));
|
await _mediator.Publish(new LibraryScanProgress(plexMediaSourceLibrary.Id, 0));
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
@if (!string.IsNullOrWhiteSpace(_movie.Poster))
|
@if (!string.IsNullOrWhiteSpace(_movie.Poster))
|
||||||
{
|
{
|
||||||
<img class="mud-elevation-2 mr-6"
|
<img class="mud-elevation-2 mr-6"
|
||||||
style="border-radius: 4px; flex-shrink: 0; max-height: 440px;"
|
style="border-radius: 4px; max-height: 440px; flex-shrink: 0"
|
||||||
src="@($"/artwork/posters/{_movie.Poster}")" alt="movie poster"/>
|
src="@($"/artwork/posters/{_movie.Poster}")" alt="movie poster"/>
|
||||||
}
|
}
|
||||||
<div style="display: flex; flex-direction: column; height: 100%">
|
<div style="display: flex; flex-direction: column; height: 100%">
|
||||||
|
|||||||
@@ -130,10 +130,7 @@
|
|||||||
|
|
||||||
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
|
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
List<MediaCardViewModel> GetSortedItems()
|
List<MediaCardViewModel> GetSortedItems() => _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
|
||||||
{
|
|
||||||
return _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectClicked(GetSortedItems, card, e);
|
SelectClicked(GetSortedItems, card, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,10 +131,7 @@
|
|||||||
|
|
||||||
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
|
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
List<MediaCardViewModel> GetSortedItems()
|
List<MediaCardViewModel> GetSortedItems() => _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
|
||||||
{
|
|
||||||
return _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectClicked(GetSortedItems, card, e);
|
SelectClicked(GetSortedItems, card, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
@if (!string.IsNullOrWhiteSpace(_season.Poster))
|
@if (!string.IsNullOrWhiteSpace(_season.Poster))
|
||||||
{
|
{
|
||||||
<img class="mud-elevation-2 mr-6"
|
<img class="mud-elevation-2 mr-6"
|
||||||
style="border-radius: 4px; flex-shrink: 0; max-height: 440px;"
|
style="border-radius: 4px; max-height: 440px; flex-shrink: 0"
|
||||||
src="@($"/artwork/posters/{_season.Poster}")" alt="show poster"/>
|
src="@($"/artwork/posters/{_season.Poster}")" alt="show poster"/>
|
||||||
}
|
}
|
||||||
<div style="display: flex; flex-direction: column; height: 100%">
|
<div style="display: flex; flex-direction: column; height: 100%">
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
@if (!string.IsNullOrWhiteSpace(_show.Poster))
|
@if (!string.IsNullOrWhiteSpace(_show.Poster))
|
||||||
{
|
{
|
||||||
<img class="mud-elevation-2 mr-6"
|
<img class="mud-elevation-2 mr-6"
|
||||||
style="border-radius: 4px; flex-shrink: 0; max-height: 440px;"
|
style="border-radius: 4px; max-height: 440px; flex-shrink: 0"
|
||||||
src="@($"/artwork/posters/{_show.Poster}")" alt="show poster"/>
|
src="@($"/artwork/posters/{_show.Poster}")" alt="show poster"/>
|
||||||
}
|
}
|
||||||
<div style="display: flex; flex-direction: column; height: 100%">
|
<div style="display: flex; flex-direction: column; height: 100%">
|
||||||
|
|||||||
@@ -130,10 +130,7 @@
|
|||||||
|
|
||||||
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
|
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
List<MediaCardViewModel> GetSortedItems()
|
List<MediaCardViewModel> GetSortedItems() => _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
|
||||||
{
|
|
||||||
return _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectClicked(GetSortedItems, card, e);
|
SelectClicked(GetSortedItems, card, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ namespace ErsatzTV.Services
|
|||||||
private readonly IEntityLocker _entityLocker;
|
private readonly IEntityLocker _entityLocker;
|
||||||
private readonly ILogger<SchedulerService> _logger;
|
private readonly ILogger<SchedulerService> _logger;
|
||||||
private readonly ChannelWriter<IPlexBackgroundServiceRequest> _plexWorkerChannel;
|
private readonly ChannelWriter<IPlexBackgroundServiceRequest> _plexWorkerChannel;
|
||||||
private readonly ChannelWriter<ISearchBackgroundServiceRequest> _searchWorkerChannel;
|
|
||||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||||
|
|
||||||
@@ -32,14 +31,12 @@ namespace ErsatzTV.Services
|
|||||||
IServiceScopeFactory serviceScopeFactory,
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
ChannelWriter<IBackgroundServiceRequest> workerChannel,
|
ChannelWriter<IBackgroundServiceRequest> workerChannel,
|
||||||
ChannelWriter<IPlexBackgroundServiceRequest> plexWorkerChannel,
|
ChannelWriter<IPlexBackgroundServiceRequest> plexWorkerChannel,
|
||||||
ChannelWriter<ISearchBackgroundServiceRequest> searchWorkerChannel,
|
|
||||||
IEntityLocker entityLocker,
|
IEntityLocker entityLocker,
|
||||||
ILogger<SchedulerService> logger)
|
ILogger<SchedulerService> logger)
|
||||||
{
|
{
|
||||||
_serviceScopeFactory = serviceScopeFactory;
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
_workerChannel = workerChannel;
|
_workerChannel = workerChannel;
|
||||||
_plexWorkerChannel = plexWorkerChannel;
|
_plexWorkerChannel = plexWorkerChannel;
|
||||||
_searchWorkerChannel = searchWorkerChannel;
|
|
||||||
_entityLocker = entityLocker;
|
_entityLocker = entityLocker;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@@ -126,6 +123,6 @@ namespace ErsatzTV.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ValueTask RebuildSearchIndex(CancellationToken cancellationToken) =>
|
private ValueTask RebuildSearchIndex(CancellationToken cancellationToken) =>
|
||||||
_searchWorkerChannel.WriteAsync(new RebuildSearchIndex(), cancellationToken);
|
_workerChannel.WriteAsync(new RebuildSearchIndex(), cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Channels;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ErsatzTV.Application;
|
|
||||||
using ErsatzTV.Application.Search.Commands;
|
|
||||||
using MediatR;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace ErsatzTV.Services
|
|
||||||
{
|
|
||||||
public class SearchIndexService : BackgroundService
|
|
||||||
{
|
|
||||||
private readonly ChannelReader<ISearchBackgroundServiceRequest> _channel;
|
|
||||||
private readonly ILogger<SearchIndexService> _logger;
|
|
||||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
|
||||||
|
|
||||||
public SearchIndexService(
|
|
||||||
ChannelReader<ISearchBackgroundServiceRequest> channel,
|
|
||||||
IServiceScopeFactory serviceScopeFactory,
|
|
||||||
ILogger<SearchIndexService> logger)
|
|
||||||
{
|
|
||||||
_channel = channel;
|
|
||||||
_serviceScopeFactory = serviceScopeFactory;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Search index service started");
|
|
||||||
|
|
||||||
await foreach (ISearchBackgroundServiceRequest request in _channel.ReadAllAsync(cancellationToken))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using IServiceScope scope = _serviceScopeFactory.CreateScope();
|
|
||||||
IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
|
|
||||||
|
|
||||||
switch (request)
|
|
||||||
{
|
|
||||||
case RebuildSearchIndex rebuildSearchIndex:
|
|
||||||
await mediator.Send(rebuildSearchIndex, cancellationToken);
|
|
||||||
break;
|
|
||||||
case AddItemsToSearchIndex addItemsToSearchIndex:
|
|
||||||
await mediator.Send(addItemsToSearchIndex, cancellationToken);
|
|
||||||
break;
|
|
||||||
case RemoveItemsFromSearchIndex removeItemsFromSearchIndex:
|
|
||||||
await mediator.Send(removeItemsFromSearchIndex, cancellationToken);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to process search index service request");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@using LanguageExt.UnsafeValueAccess
|
@using ErsatzTV.Application.MediaCards
|
||||||
|
@using LanguageExt.UnsafeValueAccess
|
||||||
@typeparam TCard
|
@typeparam TCard
|
||||||
|
|
||||||
@{ var letters = new System.Collections.Generic.HashSet<char>(); }
|
@{ var letters = new System.Collections.Generic.HashSet<char>(); }
|
||||||
@@ -24,4 +25,4 @@
|
|||||||
{
|
{
|
||||||
@ChildContent
|
@ChildContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,7 +187,6 @@ namespace ErsatzTV
|
|||||||
services.AddSingleton<IEntityLocker, EntityLocker>();
|
services.AddSingleton<IEntityLocker, EntityLocker>();
|
||||||
AddChannel<IBackgroundServiceRequest>(services);
|
AddChannel<IBackgroundServiceRequest>(services);
|
||||||
AddChannel<IPlexBackgroundServiceRequest>(services);
|
AddChannel<IPlexBackgroundServiceRequest>(services);
|
||||||
AddChannel<ISearchBackgroundServiceRequest>(services);
|
|
||||||
|
|
||||||
services.AddScoped<IChannelRepository, ChannelRepository>();
|
services.AddScoped<IChannelRepository, ChannelRepository>();
|
||||||
services.AddScoped<IFFmpegProfileRepository, FFmpegProfileRepository>();
|
services.AddScoped<IFFmpegProfileRepository, FFmpegProfileRepository>();
|
||||||
|
|||||||
Reference in New Issue
Block a user