overdue code cleanup (#1271)
This commit is contained in:
@@ -10,10 +10,7 @@ public class GetAllArtistsHandler : IRequestHandler<GetAllArtists, List<NamedMed
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public GetAllArtistsHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
public GetAllArtistsHandler(IDbContextFactory<TvContext> dbContextFactory) => _dbContextFactory = dbContextFactory;
|
||||
|
||||
public async Task<List<NamedMediaItemViewModel>> Handle(
|
||||
GetAllArtists request,
|
||||
@@ -24,7 +21,7 @@ public class GetAllArtistsHandler : IRequestHandler<GetAllArtists, List<NamedMed
|
||||
List<Artist> allArtists = await dbContext.Artists
|
||||
.AsNoTracking()
|
||||
.Include(a => a.ArtistMetadata)
|
||||
.ToListAsync(cancellationToken: cancellationToken);
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return allArtists.Bind(a => ProjectArtist(a)).ToList();
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using ErsatzTV.Core;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using ErsatzTV.Infrastructure.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Channel = ErsatzTV.Core.Domain.Channel;
|
||||
|
||||
namespace ErsatzTV.Application.Channels;
|
||||
|
||||
public class DeleteChannelHandler : IRequestHandler<DeleteChannel, Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
|
||||
public DeleteChannelHandler(
|
||||
ChannelWriter<IBackgroundServiceRequest> workerChannel,
|
||||
@@ -28,12 +27,12 @@ public class DeleteChannelHandler : IRequestHandler<DeleteChannel, Either<BaseEr
|
||||
public async Task<Either<BaseError, Unit>> Handle(DeleteChannel request, CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, Core.Domain.Channel> validation = await ChannelMustExist(dbContext, request);
|
||||
Validation<BaseError, Channel> validation = await ChannelMustExist(dbContext, request);
|
||||
|
||||
return await validation.Apply(c => DoDeletion(dbContext, c, cancellationToken));
|
||||
return await LanguageExtensions.Apply(validation, c => DoDeletion(dbContext, c, cancellationToken));
|
||||
}
|
||||
|
||||
private async Task<Unit> DoDeletion(TvContext dbContext, Core.Domain.Channel channel, CancellationToken cancellationToken)
|
||||
private async Task<Unit> DoDeletion(TvContext dbContext, Channel channel, CancellationToken cancellationToken)
|
||||
{
|
||||
dbContext.Channels.Remove(channel);
|
||||
await dbContext.SaveChangesAsync();
|
||||
@@ -51,9 +50,11 @@ public class DeleteChannelHandler : IRequestHandler<DeleteChannel, Either<BaseEr
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
private static async Task<Validation<BaseError, Core.Domain.Channel>> ChannelMustExist(TvContext dbContext, DeleteChannel deleteChannel)
|
||||
private static async Task<Validation<BaseError, Channel>> ChannelMustExist(
|
||||
TvContext dbContext,
|
||||
DeleteChannel deleteChannel)
|
||||
{
|
||||
Option<Core.Domain.Channel> maybeChannel = await dbContext.Channels
|
||||
Option<Channel> maybeChannel = await dbContext.Channels
|
||||
.SelectOneAsync(c => c.Id, c => c.Id == deleteChannel.ChannelId);
|
||||
return maybeChannel.ToValidation<BaseError>($"Channel {deleteChannel.ChannelId} does not exist.");
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ namespace ErsatzTV.Application.Channels;
|
||||
|
||||
public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
{
|
||||
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ILogger<RefreshChannelDataHandler> _logger;
|
||||
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
|
||||
|
||||
public RefreshChannelDataHandler(
|
||||
RecyclableMemoryStreamManager recyclableMemoryStreamManager,
|
||||
@@ -499,8 +499,6 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
}).Flatten();
|
||||
}
|
||||
|
||||
private record ContentRating(Option<string> System, string Value);
|
||||
|
||||
private string GetPrioritizedArtworkPath(Metadata metadata)
|
||||
{
|
||||
Option<string> maybeArtwork = Optional(metadata.Artwork).Flatten()
|
||||
@@ -518,4 +516,6 @@ public class RefreshChannelDataHandler : IRequestHandler<RefreshChannelData>
|
||||
|
||||
return maybeArtwork.IfNone(string.Empty);
|
||||
}
|
||||
|
||||
private record ContentRating(Option<string> System, string Value);
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ namespace ErsatzTV.Application.Channels;
|
||||
|
||||
public class RefreshChannelListHandler : IRequestHandler<RefreshChannelList>
|
||||
{
|
||||
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
|
||||
|
||||
public RefreshChannelListHandler(
|
||||
RecyclableMemoryStreamManager recyclableMemoryStreamManager,
|
||||
@@ -89,11 +89,14 @@ public class RefreshChannelListHandler : IRequestHandler<RefreshChannelList>
|
||||
await using var reader = (DbDataReader)await dbContext.Connection.ExecuteReaderAsync(QUERY);
|
||||
Func<IDataReader, ChannelResult> rowParser = reader.GetRowParser<ChannelResult>();
|
||||
|
||||
while (await reader.ReadAsync()) {
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
yield return rowParser(reader);
|
||||
}
|
||||
|
||||
while (await reader.NextResultAsync()) {}
|
||||
while (await reader.NextResultAsync())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> GetCategories(string categories) =>
|
||||
|
||||
@@ -31,7 +31,7 @@ public class UpdateChannelHandler : IRequestHandler<UpdateChannel, Either<BaseEr
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, Channel> validation = await Validate(dbContext, request);
|
||||
return await validation.Apply(c => ApplyUpdateRequest(dbContext, c, request));
|
||||
return await LanguageExtensions.Apply(validation, c => ApplyUpdateRequest(dbContext, c, request));
|
||||
}
|
||||
|
||||
private async Task<ChannelViewModel> ApplyUpdateRequest(TvContext dbContext, Channel c, UpdateChannel update)
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace ErsatzTV.Application.Channels;
|
||||
public class GetChannelGuideHandler : IRequestHandler<GetChannelGuide, Either<BaseError, ChannelGuide>>
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
|
||||
|
||||
public GetChannelGuideHandler(
|
||||
IDbContextFactory<TvContext> dbContextFactory,
|
||||
|
||||
@@ -8,10 +8,8 @@ public class GetChannelNameByPlayoutIdHandler : IRequestHandler<GetChannelNameBy
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public GetChannelNameByPlayoutIdHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
public GetChannelNameByPlayoutIdHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<Option<string>> Handle(GetChannelNameByPlayoutId request, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
@@ -2,4 +2,5 @@ using ErsatzTV.Core.Iptv;
|
||||
|
||||
namespace ErsatzTV.Application.Channels;
|
||||
|
||||
public record GetChannelPlaylist(string Scheme, string Host, string BaseUrl, string Mode, string AccessToken) : IRequest<ChannelPlaylist>;
|
||||
public record GetChannelPlaylist
|
||||
(string Scheme, string Host, string BaseUrl, string Mode, string AccessToken) : IRequest<ChannelPlaylist>;
|
||||
|
||||
@@ -14,7 +14,13 @@ public class GetChannelPlaylistHandler : IRequestHandler<GetChannelPlaylist, Cha
|
||||
public Task<ChannelPlaylist> Handle(GetChannelPlaylist request, CancellationToken cancellationToken) =>
|
||||
_channelRepository.GetAll()
|
||||
.Map(channels => EnsureMode(channels, request.Mode))
|
||||
.Map(channels => new ChannelPlaylist(request.Scheme, request.Host, request.BaseUrl, channels, request.AccessToken));
|
||||
.Map(
|
||||
channels => new ChannelPlaylist(
|
||||
request.Scheme,
|
||||
request.Host,
|
||||
request.BaseUrl,
|
||||
channels,
|
||||
request.AccessToken));
|
||||
|
||||
private static List<Channel> EnsureMode(IEnumerable<Channel> channels, string mode)
|
||||
{
|
||||
|
||||
@@ -9,8 +9,6 @@ public class SaveConfigElementByKeyHandler : IRequestHandler<SaveConfigElementBy
|
||||
public SaveConfigElementByKeyHandler(IConfigElementRepository configElementRepository) =>
|
||||
_configElementRepository = configElementRepository;
|
||||
|
||||
public async Task Handle(SaveConfigElementByKey request, CancellationToken cancellationToken)
|
||||
{
|
||||
public async Task Handle(SaveConfigElementByKey request, CancellationToken cancellationToken) =>
|
||||
await _configElementRepository.Upsert(request.Key, request.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,23 @@ public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler<Synchr
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, Unit>>
|
||||
Handle(SynchronizeEmbyCollections request, CancellationToken cancellationToken)
|
||||
{
|
||||
Validation<BaseError, string> validation = await Validate(request);
|
||||
return await validation.Match(
|
||||
scanner => PerformScan(scanner, request, cancellationToken),
|
||||
error =>
|
||||
{
|
||||
foreach (ScanIsNotRequired scanIsNotRequired in error.OfType<ScanIsNotRequired>())
|
||||
{
|
||||
return Task.FromResult<Either<BaseError, Unit>>(scanIsNotRequired);
|
||||
}
|
||||
|
||||
return Task.FromResult<Either<BaseError, Unit>>(error.Join());
|
||||
});
|
||||
}
|
||||
|
||||
protected override async Task<DateTimeOffset> GetLastScan(TvContext dbContext, SynchronizeEmbyCollections request)
|
||||
{
|
||||
DateTime minDateTime = await dbContext.EmbyMediaSources
|
||||
@@ -42,24 +59,7 @@ public class CallEmbyCollectionScannerHandler : CallLibraryScannerHandler<Synchr
|
||||
}
|
||||
|
||||
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval);
|
||||
return request.ForceScan || (libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now);
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, Unit>>
|
||||
Handle(SynchronizeEmbyCollections request, CancellationToken cancellationToken)
|
||||
{
|
||||
Validation<BaseError, string> validation = await Validate(request);
|
||||
return await validation.Match(
|
||||
scanner => PerformScan(scanner, request, cancellationToken),
|
||||
error =>
|
||||
{
|
||||
foreach (ScanIsNotRequired scanIsNotRequired in error.OfType<ScanIsNotRequired>())
|
||||
{
|
||||
return Task.FromResult<Either<BaseError, Unit>>(scanIsNotRequired);
|
||||
}
|
||||
|
||||
return Task.FromResult<Either<BaseError, Unit>>(error.Join());
|
||||
});
|
||||
return request.ForceScan || libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now;
|
||||
}
|
||||
|
||||
private async Task<Either<BaseError, Unit>> PerformScan(
|
||||
|
||||
@@ -28,9 +28,10 @@ public class CallEmbyLibraryScannerHandler : CallLibraryScannerHandler<ISynchron
|
||||
ForceSynchronizeEmbyLibraryById request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
|
||||
Task<Either<BaseError, string>> IRequestHandler<SynchronizeEmbyLibraryByIdIfNeeded, Either<BaseError, string>>.Handle(
|
||||
SynchronizeEmbyLibraryByIdIfNeeded request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
Task<Either<BaseError, string>> IRequestHandler<SynchronizeEmbyLibraryByIdIfNeeded, Either<BaseError, string>>.
|
||||
Handle(
|
||||
SynchronizeEmbyLibraryByIdIfNeeded request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
|
||||
private async Task<Either<BaseError, string>> Handle(
|
||||
ISynchronizeEmbyLibraryById request,
|
||||
@@ -95,6 +96,6 @@ public class CallEmbyLibraryScannerHandler : CallLibraryScannerHandler<ISynchron
|
||||
}
|
||||
|
||||
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval);
|
||||
return request.ForceScan || (libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now);
|
||||
return request.ForceScan || libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace ErsatzTV.Application.Emby;
|
||||
public class SynchronizeEmbyMediaSourcesHandler : IRequestHandler<SynchronizeEmbyMediaSources,
|
||||
Either<BaseError, List<EmbyMediaSource>>>
|
||||
{
|
||||
private readonly ChannelWriter<IScannerBackgroundServiceRequest> _scannerWorkerChannel;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly ChannelWriter<IScannerBackgroundServiceRequest> _scannerWorkerChannel;
|
||||
|
||||
public SynchronizeEmbyMediaSourcesHandler(
|
||||
IMediaSourceRepository mediaSourceRepository,
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace ErsatzTV.Application.Emby;
|
||||
public class GetEmbyConnectionParametersHandler : IRequestHandler<GetEmbyConnectionParameters,
|
||||
Either<BaseError, EmbyConnectionParametersViewModel>>
|
||||
{
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IEmbySecretStore _embySecretStore;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
|
||||
public GetEmbyConnectionParametersHandler(
|
||||
|
||||
@@ -19,7 +19,7 @@ public class DeleteFFmpegProfileHandler : IRequestHandler<DeleteFFmpegProfile, E
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, FFmpegProfile> validation = await FFmpegProfileMustExist(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, p => DoDeletion(dbContext, p));
|
||||
return await validation.Apply(p => DoDeletion(dbContext, p));
|
||||
}
|
||||
|
||||
private static async Task<Unit> DoDeletion(TvContext dbContext, FFmpegProfile ffmpegProfile)
|
||||
|
||||
@@ -20,7 +20,7 @@ public class
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, FFmpegProfile> validation = await Validate(dbContext, request);
|
||||
return await validation.Apply(p => ApplyUpdateRequest(dbContext, p, request));
|
||||
return await LanguageExtensions.Apply(validation, p => ApplyUpdateRequest(dbContext, p, request));
|
||||
}
|
||||
|
||||
private async Task<UpdateFFmpegProfileResult> ApplyUpdateRequest(
|
||||
|
||||
@@ -19,7 +19,7 @@ public class DeleteFillerPresetHandler : IRequestHandler<DeleteFillerPreset, Eit
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, FillerPreset> validation = await FillerPresetMustExist(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, ps => DoDeletion(dbContext, ps));
|
||||
return await validation.Apply(ps => DoDeletion(dbContext, ps));
|
||||
}
|
||||
|
||||
private static Task<Unit> DoDeletion(TvContext dbContext, FillerPreset fillerPreset)
|
||||
|
||||
@@ -24,13 +24,15 @@ public class CallJellyfinLibraryScannerHandler : CallLibraryScannerHandler<ISync
|
||||
{
|
||||
}
|
||||
|
||||
Task<Either<BaseError, string>> IRequestHandler<ForceSynchronizeJellyfinLibraryById, Either<BaseError, string>>.Handle(
|
||||
ForceSynchronizeJellyfinLibraryById request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
Task<Either<BaseError, string>> IRequestHandler<ForceSynchronizeJellyfinLibraryById, Either<BaseError, string>>.
|
||||
Handle(
|
||||
ForceSynchronizeJellyfinLibraryById request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
|
||||
Task<Either<BaseError, string>> IRequestHandler<SynchronizeJellyfinLibraryByIdIfNeeded, Either<BaseError, string>>.Handle(
|
||||
SynchronizeJellyfinLibraryByIdIfNeeded request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
Task<Either<BaseError, string>> IRequestHandler<SynchronizeJellyfinLibraryByIdIfNeeded, Either<BaseError, string>>.
|
||||
Handle(
|
||||
SynchronizeJellyfinLibraryByIdIfNeeded request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
|
||||
private async Task<Either<BaseError, string>> Handle(
|
||||
ISynchronizeJellyfinLibraryById request,
|
||||
@@ -95,6 +97,6 @@ public class CallJellyfinLibraryScannerHandler : CallLibraryScannerHandler<ISync
|
||||
}
|
||||
|
||||
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval);
|
||||
return request.ForceScan || (libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now);
|
||||
return request.ForceScan || libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace ErsatzTV.Application.Jellyfin;
|
||||
public class SynchronizeJellyfinMediaSourcesHandler : IRequestHandler<SynchronizeJellyfinMediaSources,
|
||||
Either<BaseError, List<JellyfinMediaSource>>>
|
||||
{
|
||||
private readonly ChannelWriter<IScannerBackgroundServiceRequest> _scannerWorkerChannel;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly ChannelWriter<IScannerBackgroundServiceRequest> _scannerWorkerChannel;
|
||||
|
||||
public SynchronizeJellyfinMediaSourcesHandler(
|
||||
IMediaSourceRepository mediaSourceRepository,
|
||||
@@ -26,7 +26,9 @@ public class SynchronizeJellyfinMediaSourcesHandler : IRequestHandler<Synchroniz
|
||||
List<JellyfinMediaSource> mediaSources = await _mediaSourceRepository.GetAllJellyfin();
|
||||
foreach (JellyfinMediaSource mediaSource in mediaSources)
|
||||
{
|
||||
await _scannerWorkerChannel.WriteAsync(new SynchronizeJellyfinAdminUserId(mediaSource.Id), cancellationToken);
|
||||
await _scannerWorkerChannel.WriteAsync(
|
||||
new SynchronizeJellyfinAdminUserId(mediaSource.Id),
|
||||
cancellationToken);
|
||||
await _scannerWorkerChannel.WriteAsync(new SynchronizeJellyfinLibraries(mediaSource.Id), cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ namespace ErsatzTV.Application.Libraries;
|
||||
|
||||
public abstract class CallLibraryScannerHandler<TRequest>
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ChannelWriter<ISearchIndexBackgroundServiceRequest> _channel;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IMediator _mediator;
|
||||
private readonly IRuntimeInfo _runtimeInfo;
|
||||
private string _libraryName;
|
||||
|
||||
@@ -32,7 +32,7 @@ public class CreateLocalLibraryHandler : LocalLibraryHandlerBase,
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, LocalLibrary> validation = await Validate(dbContext, request);
|
||||
return await validation.Apply(localLibrary => PersistLocalLibrary(dbContext, localLibrary));
|
||||
return await LanguageExtensions.Apply(validation, localLibrary => PersistLocalLibrary(dbContext, localLibrary));
|
||||
}
|
||||
|
||||
private async Task<LocalLibraryViewModel> PersistLocalLibrary(
|
||||
|
||||
@@ -39,7 +39,7 @@ public class MoveLocalLibraryPathHandler : IRequestHandler<MoveLocalLibraryPath,
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, Parameters> validation = await Validate(dbContext, request);
|
||||
return await validation.Apply(parameters => MovePath(dbContext, parameters));
|
||||
return await LanguageExtensions.Apply(validation, parameters => MovePath(dbContext, parameters));
|
||||
}
|
||||
|
||||
private async Task<Unit> MovePath(TvContext dbContext, Parameters parameters)
|
||||
|
||||
@@ -16,8 +16,8 @@ public class UpdateLocalLibraryHandler : LocalLibraryHandlerBase,
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IEntityLocker _entityLocker;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
private readonly ChannelWriter<IScannerBackgroundServiceRequest> _scannerWorkerChannel;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
|
||||
public UpdateLocalLibraryHandler(
|
||||
ChannelWriter<IScannerBackgroundServiceRequest> scannerWorkerChannel,
|
||||
|
||||
@@ -8,10 +8,8 @@ public class GetExternalCollectionsHandler : IRequestHandler<GetExternalCollecti
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public GetExternalCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
public GetExternalCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<List<LibraryViewModel>> Handle(
|
||||
GetExternalCollections request,
|
||||
@@ -21,7 +19,7 @@ public class GetExternalCollectionsHandler : IRequestHandler<GetExternalCollecti
|
||||
List<int> mediaSourceIds = await dbContext.EmbyMediaSources
|
||||
.Filter(ems => ems.Libraries.Any(l => ((EmbyLibrary)l).ShouldSyncItems))
|
||||
.Map(ems => ems.Id)
|
||||
.ToListAsync(cancellationToken: cancellationToken);
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
return mediaSourceIds.Map(
|
||||
id => new LibraryViewModel(
|
||||
|
||||
@@ -8,10 +8,7 @@ public class GetRecentLogEntriesHandler : IRequestHandler<GetRecentLogEntries, P
|
||||
{
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
|
||||
public GetRecentLogEntriesHandler(ILocalFileSystem localFileSystem)
|
||||
{
|
||||
_localFileSystem = localFileSystem;
|
||||
}
|
||||
public GetRecentLogEntriesHandler(ILocalFileSystem localFileSystem) => _localFileSystem = localFileSystem;
|
||||
|
||||
public Task<PagedLogEntriesViewModel> Handle(
|
||||
GetRecentLogEntries request,
|
||||
|
||||
@@ -9,10 +9,8 @@ public class DeleteOrphanedSubtitlesHandler : IRequestHandler<DeleteOrphanedSubt
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public DeleteOrphanedSubtitlesHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
public DeleteOrphanedSubtitlesHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, Unit>> Handle(
|
||||
DeleteOrphanedSubtitles request,
|
||||
|
||||
@@ -31,12 +31,12 @@ public class ReleaseMemoryHandler : IRequestHandler<ReleaseMemory>
|
||||
if (request.ForceAggressive || !hasActiveWorkers)
|
||||
{
|
||||
_logger.LogDebug("Starting aggressive garbage collection");
|
||||
GC.Collect(2, GCCollectionMode.Aggressive, blocking: true, compacting: true);
|
||||
GC.Collect(2, GCCollectionMode.Aggressive, true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug("Starting garbage collection");
|
||||
GC.Collect(2, GCCollectionMode.Forced, blocking: false);
|
||||
GC.Collect(2, GCCollectionMode.Forced, false);
|
||||
}
|
||||
|
||||
GC.WaitForPendingFinalizers();
|
||||
|
||||
@@ -20,7 +20,7 @@ public class CreateCollectionHandler :
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, Collection> validation = await Validate(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, c => PersistCollection(dbContext, c));
|
||||
return await validation.Apply(c => PersistCollection(dbContext, c));
|
||||
}
|
||||
|
||||
private static async Task<MediaCollectionViewModel> PersistCollection(
|
||||
|
||||
@@ -20,7 +20,7 @@ public class CreateMultiCollectionHandler :
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, MultiCollection> validation = await Validate(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, c => PersistCollection(dbContext, c));
|
||||
return await validation.Apply(c => PersistCollection(dbContext, c));
|
||||
}
|
||||
|
||||
private static async Task<MultiCollectionViewModel> PersistCollection(
|
||||
|
||||
@@ -20,7 +20,7 @@ public class CreateSmartCollectionHandler :
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, SmartCollection> validation = await Validate(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, c => PersistCollection(dbContext, c));
|
||||
return await validation.Apply(c => PersistCollection(dbContext, c));
|
||||
}
|
||||
|
||||
private static async Task<SmartCollectionViewModel> PersistCollection(
|
||||
|
||||
@@ -20,7 +20,7 @@ public class DeleteCollectionHandler : IRequestHandler<DeleteCollection, Either<
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
Validation<BaseError, Collection> validation = await CollectionMustExist(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, c => DoDeletion(dbContext, c));
|
||||
return await validation.Apply(c => DoDeletion(dbContext, c));
|
||||
}
|
||||
|
||||
private static Task<Unit> DoDeletion(TvContext dbContext, Collection collection)
|
||||
|
||||
@@ -20,7 +20,7 @@ public class DeleteMultiCollectionHandler : IRequestHandler<DeleteMultiCollectio
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
Validation<BaseError, MultiCollection> validation = await MultiCollectionMustExist(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, c => DoDeletion(dbContext, c));
|
||||
return await validation.Apply(c => DoDeletion(dbContext, c));
|
||||
}
|
||||
|
||||
private static Task<Unit> DoDeletion(TvContext dbContext, MultiCollection multiCollection)
|
||||
|
||||
@@ -20,7 +20,7 @@ public class DeleteSmartCollectionHandler : IRequestHandler<DeleteSmartCollectio
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
Validation<BaseError, SmartCollection> validation = await SmartCollectionMustExist(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, c => DoDeletion(dbContext, c));
|
||||
return await validation.Apply(c => DoDeletion(dbContext, c));
|
||||
}
|
||||
|
||||
private static Task<Unit> DoDeletion(TvContext dbContext, SmartCollection smartCollection)
|
||||
|
||||
@@ -11,10 +11,8 @@ public class GetMediaItemInfoHandler : IRequestHandler<GetMediaItemInfo, Either<
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public GetMediaItemInfoHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
public GetMediaItemInfoHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, MediaItemInfo>> Handle(
|
||||
GetMediaItemInfo request,
|
||||
|
||||
@@ -90,6 +90,6 @@ public class CallLocalLibraryScannerHandler : CallLibraryScannerHandler<IScanLoc
|
||||
}
|
||||
|
||||
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval);
|
||||
return request.ForceScan || (libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now);
|
||||
return request.ForceScan || libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
private readonly IClient _client;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
private readonly IPlayoutBuilder _playoutBuilder;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
|
||||
public BuildPlayoutHandler(
|
||||
IClient client,
|
||||
@@ -108,7 +108,6 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
dbContext.Playouts
|
||||
.Include(p => p.Channel)
|
||||
.Include(p => p.Items)
|
||||
|
||||
.Include(p => p.ProgramScheduleAlternates)
|
||||
.ThenInclude(a => a.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
@@ -137,12 +136,10 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.ThenInclude(a => a.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
|
||||
.Include(p => p.ProgramScheduleAnchors)
|
||||
.ThenInclude(psa => psa.EnumeratorState)
|
||||
.Include(p => p.ProgramScheduleAnchors)
|
||||
.ThenInclude(a => a.MediaItem)
|
||||
|
||||
.Include(p => p.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.Collection)
|
||||
@@ -164,7 +161,6 @@ public class BuildPlayoutHandler : IRequestHandler<BuildPlayout, Either<BaseErro
|
||||
.Include(p => p.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
|
||||
.SelectOneAsync(p => p.Id, p => p.Id == buildPlayout.PlayoutId)
|
||||
.Map(o => o.ToValidation<BaseError>("Playout does not exist."));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class CreatePlayoutHandler : IRequestHandler<CreatePlayout, Either<BaseEr
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, Playout> validation = await Validate(dbContext, request);
|
||||
return await validation.Apply(playout => PersistPlayout(dbContext, playout));
|
||||
return await LanguageExtensions.Apply(validation, playout => PersistPlayout(dbContext, playout));
|
||||
}
|
||||
|
||||
private async Task<CreatePlayoutResponse> PersistPlayout(TvContext dbContext, Playout playout)
|
||||
|
||||
@@ -11,9 +11,9 @@ namespace ErsatzTV.Application.Playouts;
|
||||
|
||||
public class DeletePlayoutHandler : IRequestHandler<DeletePlayout, Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
|
||||
public DeletePlayoutHandler(
|
||||
ChannelWriter<IBackgroundServiceRequest> workerChannel,
|
||||
|
||||
+1
-1
@@ -12,8 +12,8 @@ namespace ErsatzTV.Application.Playouts;
|
||||
public class ReplacePlayoutAlternateScheduleItemsHandler :
|
||||
IRequestHandler<ReplacePlayoutAlternateScheduleItems, Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _channel;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ILogger<ReplacePlayoutAlternateScheduleItemsHandler> _logger;
|
||||
|
||||
public ReplacePlayoutAlternateScheduleItemsHandler(
|
||||
|
||||
@@ -19,7 +19,7 @@ public class UpdatePlayoutHandler : IRequestHandler<UpdatePlayout, Either<BaseEr
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, Playout> validation = await Validate(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, playout => ApplyUpdateRequest(dbContext, request, playout));
|
||||
return await validation.Apply(playout => ApplyUpdateRequest(dbContext, request, playout));
|
||||
}
|
||||
|
||||
private static async Task<PlayoutNameViewModel> ApplyUpdateRequest(
|
||||
|
||||
@@ -28,9 +28,10 @@ public class CallPlexLibraryScannerHandler : CallLibraryScannerHandler<ISynchron
|
||||
ForceSynchronizePlexLibraryById request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
|
||||
Task<Either<BaseError, string>> IRequestHandler<SynchronizePlexLibraryByIdIfNeeded, Either<BaseError, string>>.Handle(
|
||||
SynchronizePlexLibraryByIdIfNeeded request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
Task<Either<BaseError, string>> IRequestHandler<SynchronizePlexLibraryByIdIfNeeded, Either<BaseError, string>>.
|
||||
Handle(
|
||||
SynchronizePlexLibraryByIdIfNeeded request,
|
||||
CancellationToken cancellationToken) => Handle(request, cancellationToken);
|
||||
|
||||
private async Task<Either<BaseError, string>> Handle(
|
||||
ISynchronizePlexLibraryById request,
|
||||
@@ -77,7 +78,7 @@ public class CallPlexLibraryScannerHandler : CallLibraryScannerHandler<ISynchron
|
||||
TvContext dbContext,
|
||||
ISynchronizePlexLibraryById request)
|
||||
{
|
||||
DateTime minDateTime = await dbContext.PlexLibraries
|
||||
DateTime minDateTime = await dbContext.PlexLibraries
|
||||
.SelectOneAsync(l => l.Id, l => l.Id == request.PlexLibraryId)
|
||||
.Match(l => l.LastScan ?? SystemTime.MinValueUtc, () => SystemTime.MaxValueUtc);
|
||||
|
||||
@@ -95,6 +96,6 @@ public class CallPlexLibraryScannerHandler : CallLibraryScannerHandler<ISynchron
|
||||
}
|
||||
|
||||
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(libraryRefreshInterval);
|
||||
return request.ForceScan || (libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now);
|
||||
return request.ForceScan || libraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,9 @@ public class
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
Validation<BaseError, ProgramSchedule> validation = await Validate(dbContext, request);
|
||||
return await validation.Apply(p => PerformCopy(dbContext, p, request, cancellationToken));
|
||||
return await LanguageExtensions.Apply(
|
||||
validation,
|
||||
p => PerformCopy(dbContext, p, request, cancellationToken));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ public class DeleteProgramScheduleHandler : IRequestHandler<DeleteProgramSchedul
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, ProgramSchedule> validation = await ProgramScheduleMustExist(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, ps => DoDeletion(dbContext, ps));
|
||||
return await validation.Apply(ps => DoDeletion(dbContext, ps));
|
||||
}
|
||||
|
||||
private static Task<Unit> DoDeletion(TvContext dbContext, ProgramSchedule programSchedule)
|
||||
|
||||
@@ -19,13 +19,13 @@ namespace ErsatzTV.Application.Search;
|
||||
public class
|
||||
QuerySearchIndexEpisodesHandler : IRequestHandler<QuerySearchIndexEpisodes, TelevisionEpisodeCardResultsViewModel>
|
||||
{
|
||||
private readonly IClient _client;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IEmbyPathReplacementService _embyPathReplacementService;
|
||||
private readonly IFallbackMetadataProvider _fallbackMetadataProvider;
|
||||
private readonly IJellyfinPathReplacementService _jellyfinPathReplacementService;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IPlexPathReplacementService _plexPathReplacementService;
|
||||
private readonly IClient _client;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace ErsatzTV.Application.Search;
|
||||
|
||||
public class QuerySearchIndexMoviesHandler : IRequestHandler<QuerySearchIndexMovies, MovieCardResultsViewModel>
|
||||
{
|
||||
private readonly IClient _client;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IMovieRepository _movieRepository;
|
||||
private readonly IClient _client;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
|
||||
public QuerySearchIndexMoviesHandler(
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace ErsatzTV.Application.Search;
|
||||
public class
|
||||
QuerySearchIndexMusicVideosHandler : IRequestHandler<QuerySearchIndexMusicVideos, MusicVideoCardResultsViewModel>
|
||||
{
|
||||
private readonly IClient _client;
|
||||
private readonly IEmbyPathReplacementService _embyPathReplacementService;
|
||||
private readonly IJellyfinPathReplacementService _jellyfinPathReplacementService;
|
||||
private readonly IMusicVideoRepository _musicVideoRepository;
|
||||
private readonly IPlexPathReplacementService _plexPathReplacementService;
|
||||
private readonly IClient _client;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
|
||||
public QuerySearchIndexMusicVideosHandler(
|
||||
|
||||
@@ -11,8 +11,8 @@ public class
|
||||
QuerySearchIndexOtherVideosHandler : IRequestHandler<QuerySearchIndexOtherVideos,
|
||||
OtherVideoCardResultsViewModel>
|
||||
{
|
||||
private readonly IOtherVideoRepository _otherVideoRepository;
|
||||
private readonly IClient _client;
|
||||
private readonly IOtherVideoRepository _otherVideoRepository;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
|
||||
public QuerySearchIndexOtherVideosHandler(
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace ErsatzTV.Application.Search;
|
||||
public class
|
||||
QuerySearchIndexSeasonsHandler : IRequestHandler<QuerySearchIndexSeasons, TelevisionSeasonCardResultsViewModel>
|
||||
{
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IClient _client;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace ErsatzTV.Application.Search;
|
||||
public class
|
||||
QuerySearchIndexShowsHandler : IRequestHandler<QuerySearchIndexShows, TelevisionShowCardResultsViewModel>
|
||||
{
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly IClient _client;
|
||||
private readonly IMediaSourceRepository _mediaSourceRepository;
|
||||
private readonly ISearchIndex _searchIndex;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ public class SearchCollectionsHandler : IRequestHandler<SearchCollections, List<
|
||||
public SearchCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
|
||||
public async Task<List<MediaCollectionViewModel>> Handle(SearchCollections request, CancellationToken cancellationToken)
|
||||
public async Task<List<MediaCollectionViewModel>> Handle(
|
||||
SearchCollections request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
return await dbContext.Collections.FromSqlRaw(
|
||||
|
||||
@@ -12,7 +12,9 @@ public class SearchMultiCollectionsHandler : IRequestHandler<SearchMultiCollecti
|
||||
public SearchMultiCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
|
||||
public async Task<List<MultiCollectionViewModel>> Handle(SearchMultiCollections request, CancellationToken cancellationToken)
|
||||
public async Task<List<MultiCollectionViewModel>> Handle(
|
||||
SearchMultiCollections request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
return await dbContext.MultiCollections.FromSqlRaw(
|
||||
|
||||
@@ -12,7 +12,9 @@ public class SearchSmartCollectionsHandler : IRequestHandler<SearchSmartCollecti
|
||||
public SearchSmartCollectionsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
|
||||
public async Task<List<SmartCollectionViewModel>> Handle(SearchSmartCollections request, CancellationToken cancellationToken)
|
||||
public async Task<List<SmartCollectionViewModel>> Handle(
|
||||
SearchSmartCollections request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
return await dbContext.SmartCollections.FromSqlRaw(
|
||||
|
||||
@@ -12,7 +12,9 @@ public class SearchTelevisionSeasonsHandler : IRequestHandler<SearchTelevisionSe
|
||||
public SearchTelevisionSeasonsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
|
||||
public async Task<List<NamedMediaItemViewModel>> Handle(SearchTelevisionSeasons request, CancellationToken cancellationToken)
|
||||
public async Task<List<NamedMediaItemViewModel>> Handle(
|
||||
SearchTelevisionSeasons request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
return await dbContext.Connection.QueryAsync<TelevisionSeason>(
|
||||
|
||||
@@ -12,7 +12,9 @@ public class SearchTelevisionShowsHandler : IRequestHandler<SearchTelevisionShow
|
||||
public SearchTelevisionShowsHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
|
||||
public async Task<List<NamedMediaItemViewModel>> Handle(SearchTelevisionShows request, CancellationToken cancellationToken)
|
||||
public async Task<List<NamedMediaItemViewModel>> Handle(
|
||||
SearchTelevisionShows request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
return await dbContext.Connection.QueryAsync<TelevisionShow>(
|
||||
|
||||
@@ -16,11 +16,11 @@ namespace ErsatzTV.Application.Streaming;
|
||||
public class StartFFmpegSessionHandler : IRequestHandler<StartFFmpegSession, Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ILogger<StartFFmpegSessionHandler> _logger;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
|
||||
public StartFFmpegSessionHandler(
|
||||
ILocalFileSystem localFileSystem,
|
||||
@@ -104,7 +104,7 @@ public class StartFFmpegSessionHandler : IRequestHandler<StartFFmpegSession, Eit
|
||||
_logger.LogDebug("Playlist exists");
|
||||
|
||||
var segmentCount = 0;
|
||||
var lastSegmentCount = -1;
|
||||
int lastSegmentCount = -1;
|
||||
while (DateTimeOffset.Now < finish && segmentCount < initialSegmentCount)
|
||||
{
|
||||
if (segmentCount != lastSegmentCount)
|
||||
|
||||
@@ -20,9 +20,9 @@ namespace ErsatzTV.Application.Subtitles;
|
||||
public class ExtractEmbeddedSubtitlesHandler : IRequestHandler<ExtractEmbeddedSubtitles, Either<BaseError, Unit>>
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ILogger<ExtractEmbeddedSubtitlesHandler> _logger;
|
||||
private readonly ChannelWriter<IBackgroundServiceRequest> _workerChannel;
|
||||
|
||||
public ExtractEmbeddedSubtitlesHandler(
|
||||
IDbContextFactory<TvContext> dbContextFactory,
|
||||
@@ -177,7 +177,8 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler<ExtractEmbeddedSu
|
||||
.Filter(
|
||||
em => em.Subtitles.Any(
|
||||
s => s.SubtitleKind == SubtitleKind.Embedded &&
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" && s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" &&
|
||||
s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
.Map(em => em.EpisodeId)
|
||||
.ToListAsync(cancellationToken);
|
||||
result.AddRange(episodeIds);
|
||||
@@ -188,7 +189,8 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler<ExtractEmbeddedSu
|
||||
.Filter(
|
||||
mm => mm.Subtitles.Any(
|
||||
s => s.SubtitleKind == SubtitleKind.Embedded &&
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" && s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" &&
|
||||
s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
.Map(mm => mm.MovieId)
|
||||
.ToListAsync(cancellationToken);
|
||||
result.AddRange(movieIds);
|
||||
@@ -199,7 +201,8 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler<ExtractEmbeddedSu
|
||||
.Filter(
|
||||
mm => mm.Subtitles.Any(
|
||||
s => s.SubtitleKind == SubtitleKind.Embedded &&
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" && s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" &&
|
||||
s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
.Map(mm => mm.MusicVideoId)
|
||||
.ToListAsync(cancellationToken);
|
||||
result.AddRange(musicVideoIds);
|
||||
@@ -210,7 +213,8 @@ public class ExtractEmbeddedSubtitlesHandler : IRequestHandler<ExtractEmbeddedSu
|
||||
.Filter(
|
||||
ovm => ovm.Subtitles.Any(
|
||||
s => s.SubtitleKind == SubtitleKind.Embedded &&
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" && s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
s.Codec != "hdmv_pgs_subtitle" && s.Codec != "dvd_subtitle" && s.Codec != "dvdsub" &&
|
||||
s.Codec != "vobsub" && s.Codec != "pgssub" && s.Codec != "pgs"))
|
||||
.Map(ovm => ovm.OtherVideoId)
|
||||
.ToListAsync(cancellationToken);
|
||||
result.AddRange(otherVideoIds);
|
||||
|
||||
@@ -12,10 +12,8 @@ public class GetSubtitlePathByIdHandler : IRequestHandler<GetSubtitlePathById, E
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
|
||||
public GetSubtitlePathByIdHandler(IDbContextFactory<TvContext> dbContextFactory)
|
||||
{
|
||||
public GetSubtitlePathByIdHandler(IDbContextFactory<TvContext> dbContextFactory) =>
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<Either<BaseError, string>> Handle(
|
||||
GetSubtitlePathById request,
|
||||
|
||||
@@ -7,10 +7,7 @@ public class GetMusicVideoCreditTemplatesHandler : IRequestHandler<GetMusicVideo
|
||||
{
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
|
||||
public GetMusicVideoCreditTemplatesHandler(ILocalFileSystem localFileSystem)
|
||||
{
|
||||
_localFileSystem = localFileSystem;
|
||||
}
|
||||
public GetMusicVideoCreditTemplatesHandler(ILocalFileSystem localFileSystem) => _localFileSystem = localFileSystem;
|
||||
|
||||
public Task<List<string>> Handle(GetMusicVideoCreditTemplates request, CancellationToken cancellationToken) =>
|
||||
_localFileSystem.ListFiles(FileSystemLayout.MusicVideoCreditsTemplatesFolder)
|
||||
|
||||
@@ -9,7 +9,6 @@ using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.FFmpeg.Capabilities;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using ErsatzTV.Infrastructure.Data;
|
||||
using ErsatzTV.Infrastructure.Runtime;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
@@ -17,12 +16,12 @@ namespace ErsatzTV.Application.Troubleshooting.Queries;
|
||||
|
||||
public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingInfo, TroubleshootingInfo>
|
||||
{
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IHealthCheckService _healthCheckService;
|
||||
private readonly IHardwareCapabilitiesFactory _hardwareCapabilitiesFactory;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly IRuntimeInfo _runtimeInfo;
|
||||
private readonly IDbContextFactory<TvContext> _dbContextFactory;
|
||||
private readonly IHardwareCapabilitiesFactory _hardwareCapabilitiesFactory;
|
||||
private readonly IHealthCheckService _healthCheckService;
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly IRuntimeInfo _runtimeInfo;
|
||||
|
||||
public GetTroubleshootingInfoHandler(
|
||||
IDbContextFactory<TvContext> dbContextFactory,
|
||||
@@ -105,7 +104,8 @@ public class GetTroubleshootingInfoHandler : IRequestHandler<GetTroubleshootingI
|
||||
Optional(GetDriverName(activeDriver)),
|
||||
vaapiDevice))
|
||||
{
|
||||
vaapiCapabilities += $"Checking driver {activeDriver} device {vaapiDevice}{Environment.NewLine}{Environment.NewLine}";
|
||||
vaapiCapabilities +=
|
||||
$"Checking driver {activeDriver} device {vaapiDevice}{Environment.NewLine}{Environment.NewLine}";
|
||||
vaapiCapabilities += output;
|
||||
vaapiCapabilities += Environment.NewLine + Environment.NewLine;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public class CreateWatermarkHandler : IRequestHandler<CreateWatermark, Either<Ba
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, ChannelWatermark> validation = Validate(request);
|
||||
return await LanguageExtensions.Apply(validation, profile => PersistChannelWatermark(dbContext, profile));
|
||||
return await validation.Apply(profile => PersistChannelWatermark(dbContext, profile));
|
||||
}
|
||||
|
||||
private static async Task<CreateWatermarkResult> PersistChannelWatermark(
|
||||
|
||||
@@ -19,7 +19,7 @@ public class DeleteWatermarkHandler : IRequestHandler<DeleteWatermark, Either<Ba
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
Validation<BaseError, ChannelWatermark> validation = await WatermarkMustExist(dbContext, request);
|
||||
return await LanguageExtensions.Apply(validation, p => DoDeletion(dbContext, p));
|
||||
return await validation.Apply(p => DoDeletion(dbContext, p));
|
||||
}
|
||||
|
||||
private static async Task<Unit> DoDeletion(TvContext dbContext, ChannelWatermark watermark)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using ErsatzTV.Core.Jellyfin;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using ErsatzTV.Core.Plex;
|
||||
using ErsatzTV.FFmpeg.Runtime;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
|
||||
@@ -8,13 +8,10 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class ChronologicalContentTests
|
||||
{
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[Test]
|
||||
public void Episodes_Should_Sort_By_Aired()
|
||||
|
||||
@@ -10,10 +10,7 @@ public class CustomOrderContentTests
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
[Test]
|
||||
public void MediaItems_Should_Sort_By_CustomOrder()
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class PlayoutBuilderTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private readonly ILogger<PlayoutBuilder> _logger;
|
||||
|
||||
public PlayoutBuilderTests()
|
||||
@@ -34,12 +37,6 @@ public class PlayoutBuilderTests
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
public class NewPlayout : PlayoutBuilderTests
|
||||
{
|
||||
@@ -2369,7 +2366,7 @@ public class PlayoutBuilderTests
|
||||
public async Task ShuffleFlood_Should_MaintainRandomSeed_MultipleDays()
|
||||
{
|
||||
var mediaItems = new List<MediaItem>();
|
||||
for (int i = 1; i <= 25; i++)
|
||||
for (var i = 1; i <= 25; i++)
|
||||
{
|
||||
mediaItems.Add(TestMovie(i, TimeSpan.FromMinutes(55), DateTime.Today.AddHours(i)));
|
||||
}
|
||||
@@ -2392,8 +2389,10 @@ public class PlayoutBuilderTests
|
||||
|
||||
// we need to mess up the ordering to trigger the problematic behavior
|
||||
// this simulates the way the rows are loaded with EF
|
||||
PlayoutProgramScheduleAnchor oldest = result.ProgramScheduleAnchors.OrderByDescending(a => a.AnchorDate).Last();
|
||||
PlayoutProgramScheduleAnchor newest = result.ProgramScheduleAnchors.OrderByDescending(a => a.AnchorDate).First();
|
||||
PlayoutProgramScheduleAnchor oldest = result.ProgramScheduleAnchors.OrderByDescending(a => a.AnchorDate)
|
||||
.Last();
|
||||
PlayoutProgramScheduleAnchor newest = result.ProgramScheduleAnchors.OrderByDescending(a => a.AnchorDate)
|
||||
.First();
|
||||
|
||||
result.ProgramScheduleAnchors = new List<PlayoutProgramScheduleAnchor>
|
||||
{
|
||||
@@ -2440,7 +2439,8 @@ public class PlayoutBuilderTests
|
||||
|
||||
result.Items.Count.Should().Be(6);
|
||||
result.ProgramScheduleAnchors.Count.Should().Be(2);
|
||||
PlayoutProgramScheduleAnchor primaryAnchor = result.ProgramScheduleAnchors.First(a => a.SmartCollectionId == 1);
|
||||
PlayoutProgramScheduleAnchor primaryAnchor =
|
||||
result.ProgramScheduleAnchors.First(a => a.SmartCollectionId == 1);
|
||||
primaryAnchor.EnumeratorState.Seed.Should().BeGreaterThan(0);
|
||||
primaryAnchor.EnumeratorState.Index.Should().Be(0);
|
||||
|
||||
@@ -2468,7 +2468,7 @@ public class PlayoutBuilderTests
|
||||
public async Task ShuffleFlood_MultipleSmartCollections_Should_MaintainRandomSeed_MultipleDays()
|
||||
{
|
||||
var mediaItems = new List<MediaItem>();
|
||||
for (int i = 1; i <= 100; i++)
|
||||
for (var i = 1; i <= 100; i++)
|
||||
{
|
||||
mediaItems.Add(TestMovie(i, TimeSpan.FromMinutes(55), DateTime.Today.AddHours(i)));
|
||||
}
|
||||
@@ -2866,7 +2866,7 @@ public class PlayoutBuilderTests
|
||||
Collection = mediaCollection,
|
||||
CollectionId = mediaCollection.Id,
|
||||
StartTime = null,
|
||||
PlaybackOrder = playbackOrder,
|
||||
PlaybackOrder = playbackOrder
|
||||
};
|
||||
|
||||
private static ProgramScheduleItem Flood(
|
||||
|
||||
@@ -13,9 +13,6 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class PlayoutModeSchedulerBaseTests : SchedulerTestBase
|
||||
{
|
||||
private CancellationToken _cancellationToken;
|
||||
private PlayoutModeSchedulerBase<ProgramScheduleItem> _scheduler;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
@@ -23,6 +20,9 @@ public class PlayoutModeSchedulerBaseTests : SchedulerTestBase
|
||||
_scheduler = new TestScheduler();
|
||||
}
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
private PlayoutModeSchedulerBase<ProgramScheduleItem> _scheduler;
|
||||
|
||||
[TestFixture]
|
||||
public class CalculateEndTimeWithFiller : PlayoutModeSchedulerBaseTests
|
||||
{
|
||||
|
||||
@@ -11,13 +11,10 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class PlayoutModeSchedulerDurationTests : SchedulerTestBase
|
||||
{
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[Test]
|
||||
public void Should_Fill_Exact_Duration()
|
||||
|
||||
@@ -11,13 +11,10 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class PlayoutModeSchedulerFloodTests : SchedulerTestBase
|
||||
{
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[Test]
|
||||
public void Should_Fill_Exactly_To_Next_Schedule_Item()
|
||||
|
||||
@@ -11,13 +11,10 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class PlayoutModeSchedulerMultipleTests : SchedulerTestBase
|
||||
{
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[Test]
|
||||
public void Should_Fill_Exactly_To_Next_Schedule_Item()
|
||||
|
||||
@@ -11,13 +11,10 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class PlayoutModeSchedulerOneTests : SchedulerTestBase
|
||||
{
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[Test]
|
||||
public void Should_Have_Gap_With_No_Tail_No_Fallback()
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class RandomizedContentTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private const int KnownSeed = 22295;
|
||||
|
||||
private readonly List<int> _expected = new()
|
||||
@@ -19,12 +22,6 @@ public class RandomizedContentTests
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Episodes_Should_Randomize()
|
||||
{
|
||||
|
||||
@@ -30,10 +30,12 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[Explicit]
|
||||
public class ScheduleIntegrationTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
public ScheduleIntegrationTests()
|
||||
{
|
||||
public ScheduleIntegrationTests() =>
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||
@@ -41,13 +43,6 @@ public class ScheduleIntegrationTests
|
||||
.WriteTo.Console()
|
||||
.Destructure.UsingAttributes()
|
||||
.CreateLogger();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(1)).Token;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestExistingData()
|
||||
@@ -140,7 +135,7 @@ public class ScheduleIntegrationTests
|
||||
await context.SaveChangesAsync(_cancellationToken);
|
||||
}
|
||||
|
||||
for (var i = 1; i <= (24 * 1); i++)
|
||||
for (var i = 1; i <= 24 * 1; i++)
|
||||
{
|
||||
await using TvContext context = await factory.CreateDbContextAsync(_cancellationToken);
|
||||
|
||||
@@ -287,7 +282,7 @@ public class ScheduleIntegrationTests
|
||||
|
||||
int playoutId = await AddTestData(dbContext, scheduleItems);
|
||||
|
||||
DateTimeOffset start = new DateTimeOffset(2022, 7, 26, 8, 0, 5, TimeSpan.FromHours(-5));
|
||||
var start = new DateTimeOffset(2022, 7, 26, 8, 0, 5, TimeSpan.FromHours(-5));
|
||||
DateTimeOffset finish = start.AddDays(2);
|
||||
|
||||
var builder = new PlayoutBuilder(
|
||||
@@ -299,7 +294,7 @@ public class ScheduleIntegrationTests
|
||||
new Mock<ILocalFileSystem>().Object,
|
||||
provider.GetRequiredService<ILogger<PlayoutBuilder>>());
|
||||
|
||||
for (var i = 0; i <= (24 * 4); i++)
|
||||
for (var i = 0; i <= 24 * 4; i++)
|
||||
{
|
||||
await using TvContext context = await factory.CreateDbContextAsync(_cancellationToken);
|
||||
|
||||
@@ -360,12 +355,10 @@ public class ScheduleIntegrationTests
|
||||
return playout.Id;
|
||||
}
|
||||
|
||||
private static async Task<Option<Playout>> GetPlayout(TvContext dbContext, int playoutId)
|
||||
{
|
||||
return await dbContext.Playouts
|
||||
private static async Task<Option<Playout>> GetPlayout(TvContext dbContext, int playoutId) =>
|
||||
await dbContext.Playouts
|
||||
.Include(p => p.Channel)
|
||||
.Include(p => p.Items)
|
||||
|
||||
.Include(p => p.ProgramScheduleAlternates)
|
||||
.ThenInclude(a => a.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
@@ -394,12 +387,10 @@ public class ScheduleIntegrationTests
|
||||
.ThenInclude(a => a.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
|
||||
.Include(p => p.ProgramScheduleAnchors)
|
||||
.ThenInclude(a => a.EnumeratorState)
|
||||
.Include(p => p.ProgramScheduleAnchors)
|
||||
.ThenInclude(a => a.MediaItem)
|
||||
|
||||
.Include(p => p.ProgramSchedule)
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.Collection)
|
||||
@@ -422,5 +413,4 @@ public class ScheduleIntegrationTests
|
||||
.ThenInclude(ps => ps.Items)
|
||||
.ThenInclude(psi => psi.FallbackFiller)
|
||||
.SelectOneAsync(p => p.Id, p => p.Id == playoutId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,10 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class SeasonEpisodeContentTests
|
||||
{
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[Test]
|
||||
public void Episodes_Should_Sort_By_EpisodeNumber()
|
||||
|
||||
@@ -8,17 +8,14 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class ShuffledContentTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
// this seed will produce (shuffle) 1-10 in order
|
||||
private const int MagicSeed = 670596;
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Episodes_Should_Not_Duplicate_When_Reshuffling()
|
||||
{
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace ErsatzTV.Core.Tests.Scheduling;
|
||||
[TestFixture]
|
||||
public class ShuffledMediaCollectionEnumeratorTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
|
||||
private readonly List<GroupedMediaItem> _mediaItems = new()
|
||||
{
|
||||
new GroupedMediaItem(new MediaItem { Id = 1 }, new List<MediaItem>()),
|
||||
@@ -18,12 +21,6 @@ public class ShuffledMediaCollectionEnumeratorTests
|
||||
|
||||
private CancellationToken _cancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Peek_Zero_Should_Match_Current()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
namespace ErsatzTV.Core.Domain;
|
||||
|
||||
[DebuggerDisplay("{EpisodeMetadata != null && EpisodeMetadata.Count > 0 ? EpisodeMetadata[0].Title : \"[unknown episode]\"}")]
|
||||
[DebuggerDisplay(
|
||||
"{EpisodeMetadata != null && EpisodeMetadata.Count > 0 ? EpisodeMetadata[0].Title : \"[unknown episode]\"}")]
|
||||
public class Episode : MediaItem
|
||||
{
|
||||
public int SeasonId { get; set; }
|
||||
|
||||
@@ -2,6 +2,16 @@ namespace ErsatzTV.Core.Domain;
|
||||
|
||||
public class ProgramScheduleAlternate
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int PlayoutId { get; set; }
|
||||
public Playout Playout { get; set; }
|
||||
public int ProgramScheduleId { get; set; }
|
||||
public ProgramSchedule ProgramSchedule { get; set; }
|
||||
public int Index { get; set; }
|
||||
public ICollection<DayOfWeek> DaysOfWeek { get; set; }
|
||||
public ICollection<int> DaysOfMonth { get; set; }
|
||||
public ICollection<int> MonthsOfYear { get; set; }
|
||||
|
||||
public static List<DayOfWeek> AllDaysOfWeek() => new()
|
||||
{
|
||||
DayOfWeek.Monday,
|
||||
@@ -15,14 +25,4 @@ public class ProgramScheduleAlternate
|
||||
|
||||
public static List<int> AllDaysOfMonth() => Enumerable.Range(1, 31).ToList();
|
||||
public static List<int> AllMonthsOfYear() => Enumerable.Range(1, 12).ToList();
|
||||
|
||||
public int Id { get; set; }
|
||||
public int PlayoutId { get; set; }
|
||||
public Playout Playout { get; set; }
|
||||
public int ProgramScheduleId { get; set; }
|
||||
public ProgramSchedule ProgramSchedule { get; set; }
|
||||
public int Index { get; set; }
|
||||
public ICollection<DayOfWeek> DaysOfWeek { get; set; }
|
||||
public ICollection<int> DaysOfMonth { get; set; }
|
||||
public ICollection<int> MonthsOfYear { get; set; }
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ public class FFmpegComplexFilterBuilder
|
||||
|
||||
var complexFilter = new StringBuilder();
|
||||
|
||||
string videoLabel = $"{videoInput}:{(isSong ? "v" : videoStreamIndex.ToString())}";
|
||||
var videoLabel = $"{videoInput}:{(isSong ? "v" : videoStreamIndex.ToString())}";
|
||||
string audioLabel = audioStreamIndex.Match(index => $"{audioInput}:{index}", () => "0:a");
|
||||
|
||||
var videoFilterQueue = new List<string>();
|
||||
@@ -229,7 +229,7 @@ public class FFmpegComplexFilterBuilder
|
||||
complexFilter.Append("[vt];");
|
||||
}
|
||||
|
||||
string watermarkLabel = $"[{audioInput + 1}:v]";
|
||||
var watermarkLabel = $"[{audioInput + 1}:v]";
|
||||
foreach (int index in _watermarkIndex)
|
||||
{
|
||||
watermarkLabel = $"[{audioInput + 1}:{index}]";
|
||||
|
||||
@@ -18,9 +18,9 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
private readonly FFmpegProcessService _ffmpegProcessService;
|
||||
private readonly IFFmpegStreamSelector _ffmpegStreamSelector;
|
||||
private readonly ILogger<FFmpegLibraryProcessService> _logger;
|
||||
private readonly IPipelineBuilderFactory _pipelineBuilderFactory;
|
||||
private readonly FFmpegPlaybackSettingsCalculator _playbackSettingsCalculator;
|
||||
private readonly ITempFilePool _tempFilePool;
|
||||
private readonly IPipelineBuilderFactory _pipelineBuilderFactory;
|
||||
|
||||
public FFmpegLibraryProcessService(
|
||||
FFmpegProcessService ffmpegProcessService,
|
||||
@@ -154,7 +154,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
|
||||
videoPath != audioPath || channel.StreamingMode == StreamingMode.HttpLiveStreamingDirect;
|
||||
ILogger<FFmpegLibraryProcessService> pixelFormatLogger = isUnknownPixelFormatExpected ? null : _logger;
|
||||
|
||||
IPixelFormat pixelFormat = await AvailablePixelFormats.ForPixelFormat(videoStream.PixelFormat, pixelFormatLogger)
|
||||
IPixelFormat pixelFormat = await AvailablePixelFormats
|
||||
.ForPixelFormat(videoStream.PixelFormat, pixelFormatLogger)
|
||||
.IfNoneAsync(
|
||||
() =>
|
||||
{
|
||||
|
||||
@@ -6,10 +6,7 @@ public class FFmpegProcess : Process
|
||||
{
|
||||
public static int ProcessCount;
|
||||
|
||||
public FFmpegProcess()
|
||||
{
|
||||
Interlocked.Increment(ref ProcessCount);
|
||||
}
|
||||
public FFmpegProcess() => Interlocked.Increment(ref ProcessCount);
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
|
||||
@@ -31,10 +31,7 @@ internal class FFmpegProcessBuilder
|
||||
private readonly string _ffmpegPath;
|
||||
private FFmpegComplexFilterBuilder _complexFilterBuilder = new();
|
||||
|
||||
public FFmpegProcessBuilder(string ffmpegPath)
|
||||
{
|
||||
_ffmpegPath = ffmpegPath;
|
||||
}
|
||||
public FFmpegProcessBuilder(string ffmpegPath) => _ffmpegPath = ffmpegPath;
|
||||
|
||||
public FFmpegProcessBuilder WithThreads(int threads)
|
||||
{
|
||||
@@ -166,8 +163,8 @@ internal class FFmpegProcessBuilder
|
||||
audioIndex = 1;
|
||||
}
|
||||
|
||||
string videoLabel = $"{videoIndex}:{videoStreamIndex}";
|
||||
string audioLabel = $"{audioIndex}:{maybeIndex.Match(i => i.ToString(), () => "a")}";
|
||||
var videoLabel = $"{videoIndex}:{videoStreamIndex}";
|
||||
var audioLabel = $"{audioIndex}:{maybeIndex.Match(i => i.ToString(), () => "a")}";
|
||||
|
||||
Option<FFmpegComplexFilter> maybeFilter = _complexFilterBuilder.Build(
|
||||
audioPath.IsNone,
|
||||
|
||||
@@ -12,12 +12,12 @@ namespace ErsatzTV.Core.FFmpeg;
|
||||
|
||||
public class FFmpegStreamSelector : IFFmpegStreamSelector
|
||||
{
|
||||
private readonly IScriptEngine _scriptEngine;
|
||||
private readonly IStreamSelectorRepository _streamSelectorRepository;
|
||||
private readonly ISearchRepository _searchRepository;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ILogger<FFmpegStreamSelector> _logger;
|
||||
private readonly IScriptEngine _scriptEngine;
|
||||
private readonly ISearchRepository _searchRepository;
|
||||
private readonly IStreamSelectorRepository _streamSelectorRepository;
|
||||
|
||||
public FFmpegStreamSelector(
|
||||
IScriptEngine scriptEngine,
|
||||
@@ -92,6 +92,7 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
break;
|
||||
case Movie:
|
||||
var sw2 = Stopwatch.StartNew();
|
||||
@@ -106,6 +107,7 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector
|
||||
{
|
||||
return result2;
|
||||
}
|
||||
|
||||
break;
|
||||
// let default fall through
|
||||
}
|
||||
@@ -322,7 +324,8 @@ public class FFmpegStreamSelector : IFFmpegStreamSelector
|
||||
_logger.LogDebug("Checking for JS Script at {Path}", jsScriptPath);
|
||||
if (!_localFileSystem.FileExists(jsScriptPath))
|
||||
{
|
||||
_logger.LogInformation("Unable to locate movie audio stream selector script; falling back to built-in logic");
|
||||
_logger.LogInformation(
|
||||
"Unable to locate movie audio stream selector script; falling back to built-in logic");
|
||||
return Option<MediaStream>.None;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ public class SongVideoGenerator : ISongVideoGenerator
|
||||
Streams = new List<MediaStream>
|
||||
{
|
||||
new() { MediaStreamKind = MediaStreamKind.Video, Index = 0 }
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
string customPath = _imageCache.GetPathForImage(
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace ErsatzTV.Core.Iptv;
|
||||
|
||||
public class ChannelGuide
|
||||
{
|
||||
private readonly string _channelsFragment;
|
||||
private readonly Dictionary<string, string> _channelDataFragments;
|
||||
private readonly string _channelsFragment;
|
||||
private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
|
||||
|
||||
public ChannelGuide(
|
||||
|
||||
@@ -5,11 +5,11 @@ namespace ErsatzTV.Core.Iptv;
|
||||
|
||||
public class ChannelPlaylist
|
||||
{
|
||||
private readonly string _accessToken;
|
||||
private readonly string _baseUrl;
|
||||
private readonly List<Channel> _channels;
|
||||
private readonly string _host;
|
||||
private readonly string _baseUrl;
|
||||
private readonly string _scheme;
|
||||
private readonly string _accessToken;
|
||||
|
||||
public ChannelPlaylist(string scheme, string host, string baseUrl, List<Channel> channels, string accessToken)
|
||||
{
|
||||
|
||||
@@ -39,9 +39,6 @@ public partial class FallbackMetadataProvider : IFallbackMetadataProvider
|
||||
return None;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"s(?:eason)?\s?(\d+)(?![e\d])", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex SeasonNumber();
|
||||
|
||||
public ShowMetadata GetFallbackMetadataForShow(string showFolder)
|
||||
{
|
||||
string fileName = Path.GetFileName(showFolder);
|
||||
@@ -156,6 +153,9 @@ public partial class FallbackMetadataProvider : IFallbackMetadataProvider
|
||||
return GetSongMetadata(path, metadata);
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"s(?:eason)?\s?(\d+)(?![e\d])", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex SeasonNumber();
|
||||
|
||||
private List<EpisodeMetadata> GetEpisodeMetadata(string fileName, EpisodeMetadata baseMetadata)
|
||||
{
|
||||
var result = new List<EpisodeMetadata> { baseMetadata };
|
||||
|
||||
@@ -23,20 +23,20 @@ public class MultiCollectionGroup : GroupedMediaItem
|
||||
switch (collectionWithItems.PlaybackOrder)
|
||||
{
|
||||
case PlaybackOrder.Chronological:
|
||||
{
|
||||
var sortedItems = collectionWithItems.MediaItems.OrderBy(identity, new ChronologicalMediaComparer())
|
||||
.ToList();
|
||||
First = sortedItems.Head();
|
||||
Additional = sortedItems.Tail().ToList();
|
||||
}
|
||||
{
|
||||
var sortedItems = collectionWithItems.MediaItems.OrderBy(identity, new ChronologicalMediaComparer())
|
||||
.ToList();
|
||||
First = sortedItems.Head();
|
||||
Additional = sortedItems.Tail().ToList();
|
||||
}
|
||||
break;
|
||||
case PlaybackOrder.SeasonEpisode:
|
||||
{
|
||||
var sortedItems = collectionWithItems.MediaItems.OrderBy(identity, new SeasonEpisodeMediaComparer())
|
||||
.ToList();
|
||||
First = sortedItems.Head();
|
||||
Additional = sortedItems.Tail().ToList();
|
||||
}
|
||||
{
|
||||
var sortedItems = collectionWithItems.MediaItems.OrderBy(identity, new SeasonEpisodeMediaComparer())
|
||||
.ToList();
|
||||
First = sortedItems.Head();
|
||||
Additional = sortedItems.Tail().ToList();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(
|
||||
|
||||
@@ -3,7 +3,6 @@ using ErsatzTV.Core.Extensions;
|
||||
using ErsatzTV.Core.Interfaces.Metadata;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Interfaces.Scheduling;
|
||||
using LanguageExt.UnsafeValueAccess;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Map = LanguageExt.Map;
|
||||
|
||||
@@ -15,11 +14,11 @@ public class PlayoutBuilder : IPlayoutBuilder
|
||||
{
|
||||
private static readonly Random Random = new();
|
||||
private readonly IArtistRepository _artistRepository;
|
||||
private readonly IMultiEpisodeShuffleCollectionEnumeratorFactory _multiEpisodeFactory;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly IConfigElementRepository _configElementRepository;
|
||||
private readonly ILocalFileSystem _localFileSystem;
|
||||
private readonly ILogger<PlayoutBuilder> _logger;
|
||||
private readonly IMediaCollectionRepository _mediaCollectionRepository;
|
||||
private readonly IMultiEpisodeShuffleCollectionEnumeratorFactory _multiEpisodeFactory;
|
||||
private readonly ITelevisionRepository _televisionRepository;
|
||||
|
||||
public PlayoutBuilder(
|
||||
|
||||
@@ -5,10 +5,10 @@ namespace ErsatzTV.Core.Scheduling;
|
||||
|
||||
public class ShuffleInOrderCollectionEnumerator : IMediaCollectionEnumerator
|
||||
{
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly IList<CollectionWithItems> _collections;
|
||||
private readonly int _mediaItemCount;
|
||||
private readonly bool _randomStartPoint;
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private Random _random;
|
||||
private IList<MediaItem> _shuffled;
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace ErsatzTV.Core.Scheduling;
|
||||
|
||||
public class ShuffledMediaCollectionEnumerator : IMediaCollectionEnumerator
|
||||
{
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly int _mediaItemCount;
|
||||
private readonly IList<GroupedMediaItem> _mediaItems;
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private CloneableRandom _random;
|
||||
private IList<MediaItem> _shuffled;
|
||||
|
||||
|
||||
@@ -223,7 +223,9 @@ public class PipelineBuilderBaseTests
|
||||
public void Wrap_Segmenter_Test()
|
||||
{
|
||||
var resolution = new FrameSize(1920, 1080);
|
||||
var concatInputFile = new ConcatInputFile("http://localhost:8080/iptv/channel/1.m3u8?mode=segmenter", resolution);
|
||||
var concatInputFile = new ConcatInputFile(
|
||||
"http://localhost:8080/iptv/channel/1.m3u8?mode=segmenter",
|
||||
resolution);
|
||||
|
||||
var builder = new SoftwarePipelineBuilder(
|
||||
new DefaultFFmpegCapabilities(),
|
||||
|
||||
@@ -9,7 +9,10 @@ public class AmfHardwareCapabilities : IHardwareCapabilities
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat) => FFmpegCapability.Software;
|
||||
|
||||
public FFmpegCapability CanEncode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat)
|
||||
public FFmpegCapability CanEncode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
|
||||
@@ -4,7 +4,10 @@ namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
|
||||
public class DefaultHardwareCapabilities : IHardwareCapabilities
|
||||
{
|
||||
public FFmpegCapability CanDecode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat)
|
||||
public FFmpegCapability CanDecode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
@@ -17,7 +20,10 @@ public class DefaultHardwareCapabilities : IHardwareCapabilities
|
||||
};
|
||||
}
|
||||
|
||||
public FFmpegCapability CanEncode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat)
|
||||
public FFmpegCapability CanEncode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
public class FFmpegCapabilities : IFFmpegCapabilities
|
||||
{
|
||||
private readonly IReadOnlySet<string> _ffmpegDecoders;
|
||||
private readonly IReadOnlySet<string> _ffmpegFilters;
|
||||
private readonly IReadOnlySet<string> _ffmpegEncoders;
|
||||
private readonly IReadOnlySet<string> _ffmpegFilters;
|
||||
|
||||
public FFmpegCapabilities(
|
||||
IReadOnlySet<string> ffmpegDecoders,
|
||||
|
||||
@@ -39,16 +39,14 @@ public class HardwareCapabilitiesFactory : IHardwareCapabilitiesFactory
|
||||
string ffmpegPath,
|
||||
HardwareAccelerationMode hardwareAccelerationMode,
|
||||
Option<string> vaapiDriver,
|
||||
Option<string> vaapiDevice)
|
||||
{
|
||||
return hardwareAccelerationMode switch
|
||||
Option<string> vaapiDevice) =>
|
||||
hardwareAccelerationMode switch
|
||||
{
|
||||
HardwareAccelerationMode.Nvenc => await GetNvidiaCapabilities(ffmpegPath, ffmpegCapabilities),
|
||||
HardwareAccelerationMode.Vaapi => await GetVaapiCapabilities(vaapiDriver, vaapiDevice),
|
||||
HardwareAccelerationMode.Amf => new AmfHardwareCapabilities(),
|
||||
_ => new DefaultHardwareCapabilities()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<string> GetNvidiaOutput(string ffmpegPath)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,13 @@ namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
|
||||
public interface IHardwareCapabilities
|
||||
{
|
||||
public FFmpegCapability CanDecode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat);
|
||||
public FFmpegCapability CanEncode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat);
|
||||
public FFmpegCapability CanDecode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat);
|
||||
|
||||
public FFmpegCapability CanEncode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,15 @@ namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
|
||||
public class NoHardwareCapabilities : IHardwareCapabilities
|
||||
{
|
||||
public FFmpegCapability CanDecode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat) =>
|
||||
public FFmpegCapability CanDecode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat) =>
|
||||
FFmpegCapability.Software;
|
||||
|
||||
public FFmpegCapability CanEncode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat) =>
|
||||
public FFmpegCapability CanEncode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat) =>
|
||||
FFmpegCapability.Software;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
public class NvidiaHardwareCapabilities : IHardwareCapabilities
|
||||
{
|
||||
private readonly int _architecture;
|
||||
private readonly List<string> _maxwellGm206 = new() { "GTX 750", "GTX 950", "GTX 960", "GTX 965M" };
|
||||
private readonly string _model;
|
||||
private readonly IFFmpegCapabilities _ffmpegCapabilities;
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<string> _maxwellGm206 = new() { "GTX 750", "GTX 950", "GTX 960", "GTX 965M" };
|
||||
private readonly string _model;
|
||||
|
||||
public NvidiaHardwareCapabilities(
|
||||
int architecture,
|
||||
@@ -23,7 +23,13 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public FFmpegCapability CanDecode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat)
|
||||
// this fails with some 1650 cards, so let's try greater than 75
|
||||
public bool HevcBFrames => _architecture > 75;
|
||||
|
||||
public FFmpegCapability CanDecode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
@@ -75,7 +81,10 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities
|
||||
return FFmpegCapability.Software;
|
||||
}
|
||||
|
||||
public FFmpegCapability CanEncode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat)
|
||||
public FFmpegCapability CanEncode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
@@ -96,9 +105,6 @@ public class NvidiaHardwareCapabilities : IHardwareCapabilities
|
||||
return isHardware ? FFmpegCapability.Hardware : FFmpegCapability.Software;
|
||||
}
|
||||
|
||||
// this fails with some 1650 cards, so let's try greater than 75
|
||||
public bool HevcBFrames => _architecture > 75;
|
||||
|
||||
private FFmpegCapability CheckHardwareCodec(string codec, Func<string, bool> check)
|
||||
{
|
||||
if (check(codec))
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace ErsatzTV.FFmpeg.Capabilities;
|
||||
|
||||
public class VaapiHardwareCapabilities : IHardwareCapabilities
|
||||
{
|
||||
private readonly List<VaapiProfileEntrypoint> _profileEntrypoints;
|
||||
private readonly ILogger _logger;
|
||||
private readonly List<VaapiProfileEntrypoint> _profileEntrypoints;
|
||||
|
||||
public VaapiHardwareCapabilities(List<VaapiProfileEntrypoint> profileEntrypoints, ILogger logger)
|
||||
{
|
||||
@@ -15,7 +15,10 @@ public class VaapiHardwareCapabilities : IHardwareCapabilities
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public FFmpegCapability CanDecode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat)
|
||||
public FFmpegCapability CanDecode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
@@ -106,7 +109,10 @@ public class VaapiHardwareCapabilities : IHardwareCapabilities
|
||||
return isHardware ? FFmpegCapability.Hardware : FFmpegCapability.Software;
|
||||
}
|
||||
|
||||
public FFmpegCapability CanEncode(string videoFormat, Option<string> videoProfile, Option<IPixelFormat> maybePixelFormat)
|
||||
public FFmpegCapability CanEncode(
|
||||
string videoFormat,
|
||||
Option<string> videoProfile,
|
||||
Option<IPixelFormat> maybePixelFormat)
|
||||
{
|
||||
int bitDepth = maybePixelFormat.Map(pf => pf.BitDepth).IfNone(8);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user