Browse Source

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
pull/143/head
Jason Dove 5 years ago committed by GitHub
parent
commit
9809c60924
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      ErsatzTV.Application/ISearchBackgroundServiceRequest.cs
  2. 26
      ErsatzTV.Application/MediaSources/Commands/ScanLocalLibraryHandler.cs
  3. 22
      ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs
  4. 9
      ErsatzTV.Application/Search/Commands/AddItemsToSearchIndex.cs
  5. 17
      ErsatzTV.Application/Search/Commands/AddItemsToSearchIndexHandler.cs
  6. 2
      ErsatzTV.Application/Search/Commands/RebuildSearchIndex.cs
  7. 8
      ErsatzTV.Application/Search/Commands/RemoveItemsFromSearchIndex.cs
  8. 17
      ErsatzTV.Application/Search/Commands/RemoveItemsFromSearchIndexHandler.cs
  9. 59
      ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs
  10. 4
      ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs
  11. 9
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs
  12. 9
      ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs
  13. 12
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs
  14. 12
      ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs
  15. 3
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegStreamSelector.cs
  16. 5
      ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs
  17. 5
      ErsatzTV.Core/Interfaces/Metadata/IMusicVideoFolderScanner.cs
  18. 5
      ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs
  19. 8
      ErsatzTV.Core/Interfaces/Plex/IPlexMovieLibraryScanner.cs
  20. 8
      ErsatzTV.Core/Interfaces/Plex/IPlexTelevisionLibraryScanner.cs
  21. 22
      ErsatzTV.Core/Metadata/MovieFolderScanner.cs
  22. 22
      ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs
  23. 20
      ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs
  24. 25
      ErsatzTV.Core/Plex/PlexMovieLibraryScanner.cs
  25. 23
      ErsatzTV.Core/Plex/PlexTelevisionLibraryScanner.cs
  26. 2
      ErsatzTV/Pages/Movie.razor
  27. 5
      ErsatzTV/Pages/MovieList.razor
  28. 5
      ErsatzTV/Pages/MusicVideoList.razor
  29. 2
      ErsatzTV/Pages/TelevisionEpisodeList.razor
  30. 2
      ErsatzTV/Pages/TelevisionSeasonList.razor
  31. 5
      ErsatzTV/Pages/TelevisionShowList.razor
  32. 5
      ErsatzTV/Services/SchedulerService.cs
  33. 61
      ErsatzTV/Services/SearchIndexService.cs
  34. 3
      ErsatzTV/Shared/FragmentLetterAnchor.razor
  35. 1
      ErsatzTV/Startup.cs

6
ErsatzTV.Application/ISearchBackgroundServiceRequest.cs

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
namespace ErsatzTV.Application
{
public interface ISearchBackgroundServiceRequest
{
}
}

26
ErsatzTV.Application/MediaSources/Commands/ScanLocalLibraryHandler.cs

@ -1,11 +1,8 @@ @@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using ErsatzTV.Application.Search.Commands;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Locking;
@ -29,7 +26,6 @@ namespace ErsatzTV.Application.MediaSources.Commands @@ -29,7 +26,6 @@ namespace ErsatzTV.Application.MediaSources.Commands
private readonly IMediator _mediator;
private readonly IMovieFolderScanner _movieFolderScanner;
private readonly IMusicVideoFolderScanner _musicVideoFolderScanner;
private readonly ChannelWriter<ISearchBackgroundServiceRequest> _searchChannel;
private readonly ITelevisionFolderScanner _televisionFolderScanner;
public ScanLocalLibraryHandler(
@ -40,8 +36,7 @@ namespace ErsatzTV.Application.MediaSources.Commands @@ -40,8 +36,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
IMusicVideoFolderScanner musicVideoFolderScanner,
IEntityLocker entityLocker,
IMediator mediator,
ILogger<ScanLocalLibraryHandler> logger,
ChannelWriter<ISearchBackgroundServiceRequest> searchChannel)
ILogger<ScanLocalLibraryHandler> logger)
{
_libraryRepository = libraryRepository;
_configElementRepository = configElementRepository;
@ -51,7 +46,6 @@ namespace ErsatzTV.Application.MediaSources.Commands @@ -51,7 +46,6 @@ namespace ErsatzTV.Application.MediaSources.Commands
_entityLocker = entityLocker;
_mediator = mediator;
_logger = logger;
_searchChannel = searchChannel;
}
public Task<Either<BaseError, string>> Handle(
@ -93,9 +87,7 @@ namespace ErsatzTV.Application.MediaSources.Commands @@ -93,9 +87,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
ffprobePath,
lastScan,
progressMin,
progressMax,
AddToSearchIndex,
RemoveFromSearchIndex);
progressMax);
break;
case LibraryMediaKind.Shows:
await _televisionFolderScanner.ScanFolder(
@ -103,9 +95,7 @@ namespace ErsatzTV.Application.MediaSources.Commands @@ -103,9 +95,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
ffprobePath,
lastScan,
progressMin,
progressMax,
AddToSearchIndex,
RemoveFromSearchIndex);
progressMax);
break;
case LibraryMediaKind.MusicVideos:
await _musicVideoFolderScanner.ScanFolder(
@ -113,9 +103,7 @@ namespace ErsatzTV.Application.MediaSources.Commands @@ -113,9 +103,7 @@ namespace ErsatzTV.Application.MediaSources.Commands
ffprobePath,
lastScan,
progressMin,
progressMax,
AddToSearchIndex,
RemoveFromSearchIndex);
progressMax);
break;
}
@ -138,12 +126,6 @@ namespace ErsatzTV.Application.MediaSources.Commands @@ -138,12 +126,6 @@ namespace ErsatzTV.Application.MediaSources.Commands
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) =>
(await LocalLibraryMustExist(request), await ValidateFFprobePath())
.Apply(

22
ErsatzTV.Application/Plex/Commands/SynchronizePlexLibraryByIdHandler.cs

@ -1,10 +1,7 @@ @@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
using ErsatzTV.Application.Search.Commands;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Locking;
@ -29,7 +26,6 @@ namespace ErsatzTV.Application.Plex.Commands @@ -29,7 +26,6 @@ namespace ErsatzTV.Application.Plex.Commands
private readonly IPlexMovieLibraryScanner _plexMovieLibraryScanner;
private readonly IPlexSecretStore _plexSecretStore;
private readonly IPlexTelevisionLibraryScanner _plexTelevisionLibraryScanner;
private readonly ChannelWriter<ISearchBackgroundServiceRequest> _searchChannel;
public SynchronizePlexLibraryByIdHandler(
IMediaSourceRepository mediaSourceRepository,
@ -38,8 +34,7 @@ namespace ErsatzTV.Application.Plex.Commands @@ -38,8 +34,7 @@ namespace ErsatzTV.Application.Plex.Commands
IPlexTelevisionLibraryScanner plexTelevisionLibraryScanner,
ILibraryRepository libraryRepository,
IEntityLocker entityLocker,
ILogger<SynchronizePlexLibraryByIdHandler> logger,
ChannelWriter<ISearchBackgroundServiceRequest> searchChannel)
ILogger<SynchronizePlexLibraryByIdHandler> logger)
{
_mediaSourceRepository = mediaSourceRepository;
_plexSecretStore = plexSecretStore;
@ -48,7 +43,6 @@ namespace ErsatzTV.Application.Plex.Commands @@ -48,7 +43,6 @@ namespace ErsatzTV.Application.Plex.Commands
_libraryRepository = libraryRepository;
_entityLocker = entityLocker;
_logger = logger;
_searchChannel = searchChannel;
}
public Task<Either<BaseError, string>> Handle(
@ -76,17 +70,13 @@ namespace ErsatzTV.Application.Plex.Commands @@ -76,17 +70,13 @@ namespace ErsatzTV.Application.Plex.Commands
await _plexMovieLibraryScanner.ScanLibrary(
parameters.ConnectionParameters.ActiveConnection,
parameters.ConnectionParameters.PlexServerAuthToken,
parameters.Library,
AddToSearchIndex,
RemoveFromSearchIndex);
parameters.Library);
break;
case LibraryMediaKind.Shows:
await _plexTelevisionLibraryScanner.ScanLibrary(
parameters.ConnectionParameters.ActiveConnection,
parameters.ConnectionParameters.PlexServerAuthToken,
parameters.Library,
AddToSearchIndex,
RemoveFromSearchIndex);
parameters.Library);
break;
}
@ -104,12 +94,6 @@ namespace ErsatzTV.Application.Plex.Commands @@ -104,12 +94,6 @@ namespace ErsatzTV.Application.Plex.Commands
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) =>
(await ValidateConnection(request), await PlexLibraryMustExist(request))
.Apply(

9
ErsatzTV.Application/Search/Commands/AddItemsToSearchIndex.cs

@ -1,9 +0,0 @@ @@ -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;
}

17
ErsatzTV.Application/Search/Commands/AddItemsToSearchIndexHandler.cs

@ -1,17 +0,0 @@ @@ -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
ErsatzTV.Application/Search/Commands/RebuildSearchIndex.cs

@ -2,5 +2,5 @@ @@ -2,5 +2,5 @@
namespace ErsatzTV.Application.Search.Commands
{
public record RebuildSearchIndex : MediatR.IRequest<Unit>, ISearchBackgroundServiceRequest;
public record RebuildSearchIndex : MediatR.IRequest<Unit>, IBackgroundServiceRequest;
}

8
ErsatzTV.Application/Search/Commands/RemoveItemsFromSearchIndex.cs

@ -1,8 +0,0 @@ @@ -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;
}

17
ErsatzTV.Application/Search/Commands/RemoveItemsFromSearchIndexHandler.cs

@ -1,17 +0,0 @@ @@ -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);
}
}

59
ErsatzTV.Core.Tests/Metadata/MovieFolderScannerTests.cs

@ -9,6 +9,7 @@ using ErsatzTV.Core.Errors; @@ -9,6 +9,7 @@ using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Core.Metadata;
using ErsatzTV.Core.Tests.Fakes;
using FluentAssertions;
@ -87,9 +88,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -87,9 +88,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsLeft.Should().BeTrue();
result.IfLeft(error => error.Should().BeOfType<MediaSourceInaccessible>());
@ -114,9 +113,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -114,9 +113,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -157,10 +154,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -157,10 +154,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -202,10 +196,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -202,10 +196,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -251,10 +242,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -251,10 +242,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -303,10 +291,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -303,10 +291,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -355,10 +340,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -355,10 +340,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -406,10 +388,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -406,10 +388,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -453,10 +432,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -453,10 +432,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -494,10 +470,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -494,10 +470,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -537,9 +510,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -537,9 +510,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -568,10 +539,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -568,10 +539,7 @@ namespace ErsatzTV.Core.Tests.Metadata
FFprobePath,
DateTimeOffset.MinValue,
0,
1,
_ => ValueTask.CompletedTask,
_ => ValueTask.CompletedTask);
1);
result.IsRight.Should().BeTrue();
@ -588,6 +556,7 @@ namespace ErsatzTV.Core.Tests.Metadata @@ -588,6 +556,7 @@ namespace ErsatzTV.Core.Tests.Metadata
_localMetadataProvider.Object,
new Mock<IMetadataRepository>().Object,
_imageCache.Object,
new Mock<ISearchIndex>().Object,
new Mock<IMediator>().Object,
new Mock<ILogger<MovieFolderScanner>>().Object
);

4
ErsatzTV.Core/FFmpeg/FFmpegComplexFilterBuilder.cs

@ -68,12 +68,12 @@ namespace ErsatzTV.Core.FFmpeg @@ -68,12 +68,12 @@ namespace ErsatzTV.Core.FFmpeg
return this;
}
public Option<FFmpegComplexFilter> Build(int videoStreamIndex, int audioStreamIndex)
public Option<FFmpegComplexFilter> Build(int videoStreamIndex, Option<int> audioStreamIndex)
{
var complexFilter = new StringBuilder();
var videoLabel = $"0:{videoStreamIndex}";
var audioLabel = $"0:{audioStreamIndex}";
string audioLabel = audioStreamIndex.Match(index => $"0:{index}", () => "0:a");
HardwareAccelerationKind acceleration = _hardwareAccelerationKind.IfNone(HardwareAccelerationKind.None);
bool isHardwareDecode = acceleration switch

9
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -22,6 +22,7 @@ using System; @@ -22,6 +22,7 @@ using System;
using System.Collections.Generic;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.FFmpeg;
using LanguageExt;
using static LanguageExt.Prelude;
namespace ErsatzTV.Core.FFmpeg
@ -46,7 +47,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -46,7 +47,7 @@ namespace ErsatzTV.Core.FFmpeg
FFmpegProfile ffmpegProfile,
MediaVersion version,
MediaStream videoStream,
MediaStream audioStream,
Option<MediaStream> audioStream,
DateTimeOffset start,
DateTimeOffset now)
{
@ -113,10 +114,14 @@ namespace ErsatzTV.Core.FFmpeg @@ -113,10 +114,14 @@ namespace ErsatzTV.Core.FFmpeg
result.AudioBitrate = ffmpegProfile.AudioBitrate;
result.AudioBufferSize = ffmpegProfile.AudioBufferSize;
if (audioStream.Channels != ffmpegProfile.AudioChannels)
audioStream.IfSome(
stream =>
{
if (stream.Channels != ffmpegProfile.AudioChannels)
{
result.AudioChannels = ffmpegProfile.AudioChannels;
}
});
result.AudioSampleRate = ffmpegProfile.AudioSampleRate;
result.AudioDuration = version.Duration;

9
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -354,12 +354,15 @@ namespace ErsatzTV.Core.FFmpeg @@ -354,12 +354,15 @@ namespace ErsatzTV.Core.FFmpeg
return this;
}
public FFmpegProcessBuilder WithFilterComplex(int videoStreamIndex, int audioStreamIndex)
public FFmpegProcessBuilder WithFilterComplex(MediaStream videoStream, Option<MediaStream> maybeAudioStream)
{
int videoStreamIndex = videoStream.Index;
Option<int> maybeIndex = maybeAudioStream.Map(ms => ms.Index);
var videoLabel = $"0:{videoStreamIndex}";
var audioLabel = $"0:{audioStreamIndex}";
var audioLabel = $"0:{maybeIndex.Match(i => i.ToString(), () => "a")}";
Option<FFmpegComplexFilter> maybeFilter = _complexFilterBuilder.Build(videoStreamIndex, audioStreamIndex);
Option<FFmpegComplexFilter> maybeFilter = _complexFilterBuilder.Build(videoStreamIndex, maybeIndex);
maybeFilter.IfSome(
filter =>
{

12
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -30,14 +30,14 @@ namespace ErsatzTV.Core.FFmpeg @@ -30,14 +30,14 @@ namespace ErsatzTV.Core.FFmpeg
DateTimeOffset now)
{
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(
channel.StreamingMode,
channel.FFmpegProfile,
version,
videoStream,
audioStream,
maybeAudioStream,
start,
now);
@ -67,7 +67,7 @@ namespace ErsatzTV.Core.FFmpeg @@ -67,7 +67,7 @@ namespace ErsatzTV.Core.FFmpeg
}
builder = builder
.WithFilterComplex(videoStream.Index, audioStream.Index);
.WithFilterComplex(videoStream, maybeAudioStream);
},
() =>
{
@ -76,18 +76,18 @@ namespace ErsatzTV.Core.FFmpeg @@ -76,18 +76,18 @@ namespace ErsatzTV.Core.FFmpeg
builder = builder
.WithDeinterlace(playbackSettings.Deinterlace)
.WithBlackBars(channel.FFmpegProfile.Resolution)
.WithFilterComplex(videoStream.Index, audioStream.Index);
.WithFilterComplex(videoStream, maybeAudioStream);
}
else if (playbackSettings.Deinterlace)
{
builder = builder.WithDeinterlace(playbackSettings.Deinterlace)
.WithAlignedAudio(playbackSettings.AudioDuration)
.WithFilterComplex(videoStream.Index, audioStream.Index);
.WithFilterComplex(videoStream, maybeAudioStream);
}
else
{
builder = builder
.WithFilterComplex(videoStream.Index, audioStream.Index);
.WithFilterComplex(videoStream, maybeAudioStream);
}
});

12
ErsatzTV.Core/FFmpeg/FFmpegStreamSelector.cs

@ -6,6 +6,7 @@ using ErsatzTV.Core.Interfaces.FFmpeg; @@ -6,6 +6,7 @@ using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Repositories;
using LanguageExt;
using Microsoft.Extensions.Logging;
using static LanguageExt.Prelude;
namespace ErsatzTV.Core.FFmpeg
{
@ -25,8 +26,17 @@ namespace ErsatzTV.Core.FFmpeg @@ -25,8 +26,17 @@ namespace ErsatzTV.Core.FFmpeg
public Task<MediaStream> SelectVideoStream(Channel channel, MediaVersion version) =>
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();
string language = (channel.PreferredLanguageCode ?? string.Empty).ToLowerInvariant();

3
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegStreamSelector.cs

@ -1,11 +1,12 @@ @@ -1,11 +1,12 @@
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using LanguageExt;
namespace ErsatzTV.Core.Interfaces.FFmpeg
{
public interface IFFmpegStreamSelector
{
Task<MediaStream> SelectVideoStream(Channel channel, MediaVersion version);
Task<MediaStream> SelectAudioStream(Channel channel, MediaVersion version);
Task<Option<MediaStream>> SelectAudioStream(Channel channel, MediaVersion version);
}
}

5
ErsatzTV.Core/Interfaces/Metadata/IMovieFolderScanner.cs

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using LanguageExt;
@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata @@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
decimal progressMax);
}
}

5
ErsatzTV.Core/Interfaces/Metadata/IMusicVideoFolderScanner.cs

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using LanguageExt;
@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata @@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
decimal progressMax);
}
}

5
ErsatzTV.Core/Interfaces/Metadata/ITelevisionFolderScanner.cs

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using LanguageExt;
@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata @@ -13,8 +12,6 @@ namespace ErsatzTV.Core.Interfaces.Metadata
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
decimal progressMax);
}
}

8
ErsatzTV.Core/Interfaces/Plex/IPlexMovieLibraryScanner.cs

@ -1,6 +1,4 @@ @@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Plex;
using LanguageExt;
@ -12,8 +10,6 @@ namespace ErsatzTV.Core.Interfaces.Plex @@ -12,8 +10,6 @@ namespace ErsatzTV.Core.Interfaces.Plex
Task<Either<BaseError, Unit>> ScanLibrary(
PlexConnection connection,
PlexServerAuthToken token,
PlexLibrary plexMediaSourceLibrary,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
PlexLibrary plexMediaSourceLibrary);
}
}

8
ErsatzTV.Core/Interfaces/Plex/IPlexTelevisionLibraryScanner.cs

@ -1,6 +1,4 @@ @@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Plex;
using LanguageExt;
@ -12,8 +10,6 @@ namespace ErsatzTV.Core.Interfaces.Plex @@ -12,8 +10,6 @@ namespace ErsatzTV.Core.Interfaces.Plex
Task<Either<BaseError, Unit>> ScanLibrary(
PlexConnection connection,
PlexServerAuthToken token,
PlexLibrary plexMediaSourceLibrary,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
PlexLibrary plexMediaSourceLibrary);
}
}

22
ErsatzTV.Core/Metadata/MovieFolderScanner.cs

@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors; @@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using LanguageExt;
using MediatR;
using Microsoft.Extensions.Logging;
@ -24,6 +25,7 @@ namespace ErsatzTV.Core.Metadata @@ -24,6 +25,7 @@ namespace ErsatzTV.Core.Metadata
private readonly ILogger<MovieFolderScanner> _logger;
private readonly IMediator _mediator;
private readonly IMovieRepository _movieRepository;
private readonly ISearchIndex _searchIndex;
public MovieFolderScanner(
ILocalFileSystem localFileSystem,
@ -32,6 +34,7 @@ namespace ErsatzTV.Core.Metadata @@ -32,6 +34,7 @@ namespace ErsatzTV.Core.Metadata
ILocalMetadataProvider localMetadataProvider,
IMetadataRepository metadataRepository,
IImageCache imageCache,
ISearchIndex searchIndex,
IMediator mediator,
ILogger<MovieFolderScanner> logger)
: base(localFileSystem, localStatisticsProvider, metadataRepository, imageCache, logger)
@ -39,6 +42,7 @@ namespace ErsatzTV.Core.Metadata @@ -39,6 +42,7 @@ namespace ErsatzTV.Core.Metadata
_localFileSystem = localFileSystem;
_movieRepository = movieRepository;
_localMetadataProvider = localMetadataProvider;
_searchIndex = searchIndex;
_mediator = mediator;
_logger = logger;
}
@ -48,9 +52,7 @@ namespace ErsatzTV.Core.Metadata @@ -48,9 +52,7 @@ namespace ErsatzTV.Core.Metadata
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex)
decimal progressMax)
{
decimal progressSpread = progressMax - progressMin;
@ -109,7 +111,17 @@ namespace ErsatzTV.Core.Metadata @@ -109,7 +111,17 @@ namespace ErsatzTV.Core.Metadata
.BindT(movie => UpdateArtwork(movie, ArtworkKind.FanArt));
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 =>
{
_logger.LogWarning("Error processing movie at {Path}: {Error}", file, error.Value);
@ -124,7 +136,7 @@ namespace ErsatzTV.Core.Metadata @@ -124,7 +136,7 @@ namespace ErsatzTV.Core.Metadata
{
_logger.LogInformation("Removing missing movie at {Path}", path);
List<int> ids = await _movieRepository.DeleteByPath(libraryPath, path);
await removeFromSearchIndex(ids);
await _searchIndex.RemoveItems(ids);
}
}

22
ErsatzTV.Core/Metadata/MusicVideoFolderScanner.cs

@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors; @@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using LanguageExt;
using MediatR;
using Microsoft.Extensions.Logging;
@ -23,6 +24,7 @@ namespace ErsatzTV.Core.Metadata @@ -23,6 +24,7 @@ namespace ErsatzTV.Core.Metadata
private readonly ILogger<MusicVideoFolderScanner> _logger;
private readonly IMediator _mediator;
private readonly IMusicVideoRepository _musicVideoRepository;
private readonly ISearchIndex _searchIndex;
public MusicVideoFolderScanner(
ILocalFileSystem localFileSystem,
@ -30,6 +32,7 @@ namespace ErsatzTV.Core.Metadata @@ -30,6 +32,7 @@ namespace ErsatzTV.Core.Metadata
ILocalMetadataProvider localMetadataProvider,
IMetadataRepository metadataRepository,
IImageCache imageCache,
ISearchIndex searchIndex,
IMusicVideoRepository musicVideoRepository,
IMediator mediator,
ILogger<MusicVideoFolderScanner> logger) : base(
@ -41,6 +44,7 @@ namespace ErsatzTV.Core.Metadata @@ -41,6 +44,7 @@ namespace ErsatzTV.Core.Metadata
{
_localFileSystem = localFileSystem;
_localMetadataProvider = localMetadataProvider;
_searchIndex = searchIndex;
_musicVideoRepository = musicVideoRepository;
_mediator = mediator;
_logger = logger;
@ -51,9 +55,7 @@ namespace ErsatzTV.Core.Metadata @@ -51,9 +55,7 @@ namespace ErsatzTV.Core.Metadata
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex)
decimal progressMax)
{
decimal progressSpread = progressMax - progressMin;
@ -102,7 +104,17 @@ namespace ErsatzTV.Core.Metadata @@ -102,7 +104,17 @@ namespace ErsatzTV.Core.Metadata
.BindT(UpdateThumbnail);
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 =>
{
_logger.LogWarning("Error processing music video at {Path}: {Error}", file, error.Value);
@ -117,7 +129,7 @@ namespace ErsatzTV.Core.Metadata @@ -117,7 +129,7 @@ namespace ErsatzTV.Core.Metadata
{
_logger.LogInformation("Removing missing music video at {Path}", path);
List<int> ids = await _musicVideoRepository.DeleteByPath(libraryPath, path);
await removeFromSearchIndex(ids);
await _searchIndex.RemoveItems(ids);
}
}

20
ErsatzTV.Core/Metadata/TelevisionFolderScanner.cs

@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors; @@ -8,6 +8,7 @@ using ErsatzTV.Core.Errors;
using ErsatzTV.Core.Interfaces.Images;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using LanguageExt;
using MediatR;
using Microsoft.Extensions.Logging;
@ -22,6 +23,7 @@ namespace ErsatzTV.Core.Metadata @@ -22,6 +23,7 @@ namespace ErsatzTV.Core.Metadata
private readonly ILocalMetadataProvider _localMetadataProvider;
private readonly ILogger<TelevisionFolderScanner> _logger;
private readonly IMediator _mediator;
private readonly ISearchIndex _searchIndex;
private readonly ITelevisionRepository _televisionRepository;
public TelevisionFolderScanner(
@ -31,6 +33,7 @@ namespace ErsatzTV.Core.Metadata @@ -31,6 +33,7 @@ namespace ErsatzTV.Core.Metadata
ILocalMetadataProvider localMetadataProvider,
IMetadataRepository metadataRepository,
IImageCache imageCache,
ISearchIndex searchIndex,
IMediator mediator,
ILogger<TelevisionFolderScanner> logger) : base(
localFileSystem,
@ -42,6 +45,7 @@ namespace ErsatzTV.Core.Metadata @@ -42,6 +45,7 @@ namespace ErsatzTV.Core.Metadata
_localFileSystem = localFileSystem;
_televisionRepository = televisionRepository;
_localMetadataProvider = localMetadataProvider;
_searchIndex = searchIndex;
_mediator = mediator;
_logger = logger;
}
@ -51,9 +55,7 @@ namespace ErsatzTV.Core.Metadata @@ -51,9 +55,7 @@ namespace ErsatzTV.Core.Metadata
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex)
decimal progressMax)
{
decimal progressSpread = progressMax - progressMin;
@ -82,7 +84,15 @@ namespace ErsatzTV.Core.Metadata @@ -82,7 +84,15 @@ namespace ErsatzTV.Core.Metadata
await maybeShow.Match(
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(
libraryPath,
ffprobePath,
@ -112,7 +122,7 @@ namespace ErsatzTV.Core.Metadata @@ -112,7 +122,7 @@ namespace ErsatzTV.Core.Metadata
await _televisionRepository.DeleteEmptySeasons(libraryPath);
List<int> ids = await _televisionRepository.DeleteEmptyShows(libraryPath);
await removeFromSearchIndex(ids);
await _searchIndex.RemoveItems(ids);
return Unit.Default;
}

25
ErsatzTV.Core/Plex/PlexMovieLibraryScanner.cs

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Plex;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Core.Metadata;
using LanguageExt;
using MediatR;
@ -20,11 +20,13 @@ namespace ErsatzTV.Core.Plex @@ -20,11 +20,13 @@ namespace ErsatzTV.Core.Plex
private readonly IMetadataRepository _metadataRepository;
private readonly IMovieRepository _movieRepository;
private readonly IPlexServerApiClient _plexServerApiClient;
private readonly ISearchIndex _searchIndex;
public PlexMovieLibraryScanner(
IPlexServerApiClient plexServerApiClient,
IMovieRepository movieRepository,
IMetadataRepository metadataRepository,
ISearchIndex searchIndex,
IMediator mediator,
ILogger<PlexMovieLibraryScanner> logger)
: base(metadataRepository, logger)
@ -32,6 +34,7 @@ namespace ErsatzTV.Core.Plex @@ -32,6 +34,7 @@ namespace ErsatzTV.Core.Plex
_plexServerApiClient = plexServerApiClient;
_movieRepository = movieRepository;
_metadataRepository = metadataRepository;
_searchIndex = searchIndex;
_mediator = mediator;
_logger = logger;
}
@ -39,9 +42,7 @@ namespace ErsatzTV.Core.Plex @@ -39,9 +42,7 @@ namespace ErsatzTV.Core.Plex
public async Task<Either<BaseError, Unit>> ScanLibrary(
PlexConnection connection,
PlexServerAuthToken token,
PlexLibrary plexMediaSourceLibrary,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex)
PlexLibrary plexMediaSourceLibrary)
{
Either<BaseError, List<PlexMovie>> entries = await _plexServerApiClient.GetMovieLibraryContents(
plexMediaSourceLibrary,
@ -64,7 +65,17 @@ namespace ErsatzTV.Core.Plex @@ -64,7 +65,17 @@ namespace ErsatzTV.Core.Plex
.BindT(existing => UpdateArtwork(existing, incoming));
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 =>
{
_logger.LogWarning(
@ -77,7 +88,7 @@ namespace ErsatzTV.Core.Plex @@ -77,7 +88,7 @@ namespace ErsatzTV.Core.Plex
var movieKeys = movieEntries.Map(s => s.Key).ToList();
List<int> ids = await _movieRepository.RemoveMissingPlexMovies(plexMediaSourceLibrary, movieKeys);
await removeFromSearchIndex(ids);
await _searchIndex.RemoveItems(ids);
await _mediator.Publish(new LibraryScanProgress(plexMediaSourceLibrary.Id, 0));
},

23
ErsatzTV.Core/Plex/PlexTelevisionLibraryScanner.cs

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Plex;
using ErsatzTV.Core.Interfaces.Repositories;
using ErsatzTV.Core.Interfaces.Search;
using ErsatzTV.Core.Metadata;
using LanguageExt;
using MediatR;
@ -20,12 +20,14 @@ namespace ErsatzTV.Core.Plex @@ -20,12 +20,14 @@ namespace ErsatzTV.Core.Plex
private readonly IMediator _mediator;
private readonly IMetadataRepository _metadataRepository;
private readonly IPlexServerApiClient _plexServerApiClient;
private readonly ISearchIndex _searchIndex;
private readonly ITelevisionRepository _televisionRepository;
public PlexTelevisionLibraryScanner(
IPlexServerApiClient plexServerApiClient,
ITelevisionRepository televisionRepository,
IMetadataRepository metadataRepository,
ISearchIndex searchIndex,
IMediator mediator,
ILogger<PlexTelevisionLibraryScanner> logger)
: base(metadataRepository, logger)
@ -33,6 +35,7 @@ namespace ErsatzTV.Core.Plex @@ -33,6 +35,7 @@ namespace ErsatzTV.Core.Plex
_plexServerApiClient = plexServerApiClient;
_televisionRepository = televisionRepository;
_metadataRepository = metadataRepository;
_searchIndex = searchIndex;
_mediator = mediator;
_logger = logger;
}
@ -40,9 +43,7 @@ namespace ErsatzTV.Core.Plex @@ -40,9 +43,7 @@ namespace ErsatzTV.Core.Plex
public async Task<Either<BaseError, Unit>> ScanLibrary(
PlexConnection connection,
PlexServerAuthToken token,
PlexLibrary plexMediaSourceLibrary,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex)
PlexLibrary plexMediaSourceLibrary)
{
Either<BaseError, List<PlexShow>> entries = await _plexServerApiClient.GetShowLibraryContents(
plexMediaSourceLibrary,
@ -66,7 +67,15 @@ namespace ErsatzTV.Core.Plex @@ -66,7 +67,15 @@ namespace ErsatzTV.Core.Plex
await maybeShow.Match(
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);
},
error =>
@ -82,7 +91,7 @@ namespace ErsatzTV.Core.Plex @@ -82,7 +91,7 @@ namespace ErsatzTV.Core.Plex
var showKeys = showEntries.Map(s => s.Key).ToList();
List<int> ids =
await _televisionRepository.RemoveMissingPlexShows(plexMediaSourceLibrary, showKeys);
await removeFromSearchIndex(ids);
await _searchIndex.RemoveItems(ids);
await _mediator.Publish(new LibraryScanProgress(plexMediaSourceLibrary.Id, 0));

2
ErsatzTV/Pages/Movie.razor

@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
@if (!string.IsNullOrWhiteSpace(_movie.Poster))
{
<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"/>
}
<div style="display: flex; flex-direction: column; height: 100%">

5
ErsatzTV/Pages/MovieList.razor

@ -130,10 +130,7 @@ @@ -130,10 +130,7 @@
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
{
List<MediaCardViewModel> GetSortedItems()
{
return _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
}
List<MediaCardViewModel> GetSortedItems() => _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
SelectClicked(GetSortedItems, card, e);
}

5
ErsatzTV/Pages/MusicVideoList.razor

@ -131,10 +131,7 @@ @@ -131,10 +131,7 @@
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
{
List<MediaCardViewModel> GetSortedItems()
{
return _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
}
List<MediaCardViewModel> GetSortedItems() => _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
SelectClicked(GetSortedItems, card, e);
}

2
ErsatzTV/Pages/TelevisionEpisodeList.razor

@ -29,7 +29,7 @@ @@ -29,7 +29,7 @@
@if (!string.IsNullOrWhiteSpace(_season.Poster))
{
<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"/>
}
<div style="display: flex; flex-direction: column; height: 100%">

2
ErsatzTV/Pages/TelevisionSeasonList.razor

@ -27,7 +27,7 @@ @@ -27,7 +27,7 @@
@if (!string.IsNullOrWhiteSpace(_show.Poster))
{
<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"/>
}
<div style="display: flex; flex-direction: column; height: 100%">

5
ErsatzTV/Pages/TelevisionShowList.razor

@ -130,10 +130,7 @@ @@ -130,10 +130,7 @@
private void SelectClicked(MediaCardViewModel card, MouseEventArgs e)
{
List<MediaCardViewModel> GetSortedItems()
{
return _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
}
List<MediaCardViewModel> GetSortedItems() => _data.Cards.OrderBy(m => m.SortTitle).ToList<MediaCardViewModel>();
SelectClicked(GetSortedItems, card, e);
}

5
ErsatzTV/Services/SchedulerService.cs

@ -24,7 +24,6 @@ namespace ErsatzTV.Services @@ -24,7 +24,6 @@ namespace ErsatzTV.Services
private readonly IEntityLocker _entityLocker;
private readonly ILogger<SchedulerService> _logger;
private readonly ChannelWriter<IPlexBackgroundServiceRequest> _plexWorkerChannel;
private readonly ChannelWriter<ISearchBackgroundServiceRequest> _searchWorkerChannel;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
@ -32,14 +31,12 @@ namespace ErsatzTV.Services @@ -32,14 +31,12 @@ namespace ErsatzTV.Services
IServiceScopeFactory serviceScopeFactory,
ChannelWriter<IBackgroundServiceRequest> workerChannel,
ChannelWriter<IPlexBackgroundServiceRequest> plexWorkerChannel,
ChannelWriter<ISearchBackgroundServiceRequest> searchWorkerChannel,
IEntityLocker entityLocker,
ILogger<SchedulerService> logger)
{
_serviceScopeFactory = serviceScopeFactory;
_workerChannel = workerChannel;
_plexWorkerChannel = plexWorkerChannel;
_searchWorkerChannel = searchWorkerChannel;
_entityLocker = entityLocker;
_logger = logger;
}
@ -126,6 +123,6 @@ namespace ErsatzTV.Services @@ -126,6 +123,6 @@ namespace ErsatzTV.Services
}
private ValueTask RebuildSearchIndex(CancellationToken cancellationToken) =>
_searchWorkerChannel.WriteAsync(new RebuildSearchIndex(), cancellationToken);
_workerChannel.WriteAsync(new RebuildSearchIndex(), cancellationToken);
}
}

61
ErsatzTV/Services/SearchIndexService.cs

@ -1,61 +0,0 @@ @@ -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");
}
}
}
}
}

3
ErsatzTV/Shared/FragmentLetterAnchor.razor

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
@using LanguageExt.UnsafeValueAccess
@using ErsatzTV.Application.MediaCards
@using LanguageExt.UnsafeValueAccess
@typeparam TCard
@{ var letters = new System.Collections.Generic.HashSet<char>(); }

1
ErsatzTV/Startup.cs

@ -187,7 +187,6 @@ namespace ErsatzTV @@ -187,7 +187,6 @@ namespace ErsatzTV
services.AddSingleton<IEntityLocker, EntityLocker>();
AddChannel<IBackgroundServiceRequest>(services);
AddChannel<IPlexBackgroundServiceRequest>(services);
AddChannel<ISearchBackgroundServiceRequest>(services);
services.AddScoped<IChannelRepository, ChannelRepository>();
services.AddScoped<IFFmpegProfileRepository, FFmpegProfileRepository>();

Loading…
Cancel
Save