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:
Jason Dove
2021-04-05 16:06:13 -05:00
committed by GitHub
parent 3fb6da0754
commit 9809c60924
35 changed files with 160 additions and 299 deletions
@@ -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
@@ -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
FFmpegProfile ffmpegProfile,
MediaVersion version,
MediaStream videoStream,
MediaStream audioStream,
Option<MediaStream> audioStream,
DateTimeOffset start,
DateTimeOffset now)
{
@@ -113,10 +114,14 @@ namespace ErsatzTV.Core.FFmpeg
result.AudioBitrate = ffmpegProfile.AudioBitrate;
result.AudioBufferSize = ffmpegProfile.AudioBufferSize;
if (audioStream.Channels != ffmpegProfile.AudioChannels)
{
result.AudioChannels = ffmpegProfile.AudioChannels;
}
audioStream.IfSome(
stream =>
{
if (stream.Channels != ffmpegProfile.AudioChannels)
{
result.AudioChannels = ffmpegProfile.AudioChannels;
}
});
result.AudioSampleRate = ffmpegProfile.AudioSampleRate;
result.AudioDuration = version.Duration;
+7 -4
View File
@@ -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)
{
var videoLabel = $"0:{videoStreamIndex}";
var audioLabel = $"0:{audioStreamIndex}";
int videoStreamIndex = videoStream.Index;
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(
filter =>
{
+6 -6
View File
@@ -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
}
builder = builder
.WithFilterComplex(videoStream.Index, audioStream.Index);
.WithFilterComplex(videoStream, maybeAudioStream);
},
() =>
{
@@ -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);
}
});
+11 -1
View File
@@ -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
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();
@@ -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);
}
}
@@ -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
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
decimal progressMax);
}
}
@@ -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
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
decimal progressMax);
}
}
@@ -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
string ffprobePath,
DateTimeOffset lastScan,
decimal progressMin,
decimal progressMax,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
decimal progressMax);
}
}
@@ -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
Task<Either<BaseError, Unit>> ScanLibrary(
PlexConnection connection,
PlexServerAuthToken token,
PlexLibrary plexMediaSourceLibrary,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
PlexLibrary plexMediaSourceLibrary);
}
}
@@ -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
Task<Either<BaseError, Unit>> ScanLibrary(
PlexConnection connection,
PlexServerAuthToken token,
PlexLibrary plexMediaSourceLibrary,
Func<List<MediaItem>, ValueTask> addToSearchIndex,
Func<List<int>, ValueTask> removeFromSearchIndex);
PlexLibrary plexMediaSourceLibrary);
}
}
+17 -5
View File
@@ -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
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
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
_localFileSystem = localFileSystem;
_movieRepository = movieRepository;
_localMetadataProvider = localMetadataProvider;
_searchIndex = searchIndex;
_mediator = mediator;
_logger = logger;
}
@@ -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
.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
{
_logger.LogInformation("Removing missing movie at {Path}", 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.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
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
ILocalMetadataProvider localMetadataProvider,
IMetadataRepository metadataRepository,
IImageCache imageCache,
ISearchIndex searchIndex,
IMusicVideoRepository musicVideoRepository,
IMediator mediator,
ILogger<MusicVideoFolderScanner> logger) : base(
@@ -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
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
.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
{
_logger.LogInformation("Removing missing music video at {Path}", 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.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
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
ILocalMetadataProvider localMetadataProvider,
IMetadataRepository metadataRepository,
IImageCache imageCache,
ISearchIndex searchIndex,
IMediator mediator,
ILogger<TelevisionFolderScanner> logger) : base(
localFileSystem,
@@ -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
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
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
await _televisionRepository.DeleteEmptySeasons(libraryPath);
List<int> ids = await _televisionRepository.DeleteEmptyShows(libraryPath);
await removeFromSearchIndex(ids);
await _searchIndex.RemoveItems(ids);
return Unit.Default;
}
+18 -7
View File
@@ -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
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
_plexServerApiClient = plexServerApiClient;
_movieRepository = movieRepository;
_metadataRepository = metadataRepository;
_searchIndex = searchIndex;
_mediator = mediator;
_logger = logger;
}
@@ -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
.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
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));
},
@@ -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
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
_plexServerApiClient = plexServerApiClient;
_televisionRepository = televisionRepository;
_metadataRepository = metadataRepository;
_searchIndex = searchIndex;
_mediator = mediator;
_logger = logger;
}
@@ -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
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
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));