131 lines
5.6 KiB
C#
131 lines
5.6 KiB
C#
using ErsatzTV.Core;
|
|
using ErsatzTV.Core.Domain;
|
|
using ErsatzTV.Core.Interfaces.Plex;
|
|
using ErsatzTV.Core.Interfaces.Repositories;
|
|
using ErsatzTV.Core.Plex;
|
|
using ErsatzTV.Scanner.Core.Interfaces;
|
|
|
|
namespace ErsatzTV.Scanner.Application.Plex;
|
|
|
|
public class SynchronizePlexCollectionsHandler : IRequestHandler<SynchronizePlexCollections, Either<BaseError, Unit>>
|
|
{
|
|
private readonly IConfigElementRepository _configElementRepository;
|
|
private readonly IScannerProxy _scannerProxy;
|
|
private readonly IMediaSourceRepository _mediaSourceRepository;
|
|
private readonly IPlexSecretStore _plexSecretStore;
|
|
private readonly IPlexCollectionScanner _scanner;
|
|
|
|
public SynchronizePlexCollectionsHandler(
|
|
IMediaSourceRepository mediaSourceRepository,
|
|
IPlexSecretStore plexSecretStore,
|
|
IPlexCollectionScanner scanner,
|
|
IConfigElementRepository configElementRepository,
|
|
IScannerProxy scannerProxy)
|
|
{
|
|
_mediaSourceRepository = mediaSourceRepository;
|
|
_plexSecretStore = plexSecretStore;
|
|
_scanner = scanner;
|
|
_configElementRepository = configElementRepository;
|
|
_scannerProxy = scannerProxy;
|
|
}
|
|
|
|
public async Task<Either<BaseError, Unit>> Handle(
|
|
SynchronizePlexCollections request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
Validation<BaseError, RequestParameters> validation = await Validate(request, cancellationToken);
|
|
return await validation.Match(
|
|
p => SynchronizeCollections(p, cancellationToken),
|
|
error => Task.FromResult<Either<BaseError, Unit>>(error.Join()));
|
|
}
|
|
|
|
private async Task<Validation<BaseError, RequestParameters>> Validate(
|
|
SynchronizePlexCollections request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
Task<Validation<BaseError, ConnectionParameters>> mediaSource = MediaSourceMustExist(request, cancellationToken)
|
|
.BindT(MediaSourceMustHaveActiveConnection)
|
|
.BindT(MediaSourceMustHaveToken);
|
|
|
|
return (await mediaSource, await ValidateLibraryRefreshInterval(cancellationToken))
|
|
.Apply((connectionParameters, libraryRefreshInterval) => new RequestParameters(
|
|
connectionParameters,
|
|
connectionParameters.PlexMediaSource,
|
|
request.ForceScan,
|
|
request.DeepScan,
|
|
libraryRefreshInterval,
|
|
request.BaseUrl));
|
|
}
|
|
|
|
private Task<Validation<BaseError, int>> ValidateLibraryRefreshInterval(CancellationToken cancellationToken) =>
|
|
_configElementRepository.GetValue<int>(ConfigElementKey.LibraryRefreshInterval, cancellationToken)
|
|
.FilterT(lri => lri is >= 0 and < 1_000_000)
|
|
.Map(lri => lri.ToValidation<BaseError>("Library refresh interval is invalid"));
|
|
|
|
private Task<Validation<BaseError, PlexMediaSource>> MediaSourceMustExist(
|
|
SynchronizePlexCollections request,
|
|
CancellationToken cancellationToken) =>
|
|
_mediaSourceRepository.GetPlex(request.PlexMediaSourceId, cancellationToken)
|
|
.Map(o => o.ToValidation<BaseError>("Plex media source does not exist."));
|
|
|
|
private static Validation<BaseError, ConnectionParameters> MediaSourceMustHaveActiveConnection(
|
|
PlexMediaSource plexMediaSource)
|
|
{
|
|
Option<PlexConnection> maybeConnection = plexMediaSource.Connections.SingleOrDefault(c => c.IsActive);
|
|
return maybeConnection.Map(connection => new ConnectionParameters(plexMediaSource, connection))
|
|
.ToValidation<BaseError>("Plex media source requires an active connection");
|
|
}
|
|
|
|
private async Task<Validation<BaseError, ConnectionParameters>> MediaSourceMustHaveToken(
|
|
ConnectionParameters connectionParameters)
|
|
{
|
|
Option<PlexServerAuthToken> maybeToken = await
|
|
_plexSecretStore.GetServerAuthToken(connectionParameters.PlexMediaSource.ClientIdentifier);
|
|
return maybeToken.Map(token => connectionParameters with { PlexServerAuthToken = token })
|
|
.ToValidation<BaseError>("Plex media source requires a token");
|
|
}
|
|
|
|
private async Task<Either<BaseError, Unit>> SynchronizeCollections(
|
|
RequestParameters parameters,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
_scannerProxy.SetBaseUrl(parameters.BaseUrl);
|
|
|
|
var lastScan = new DateTimeOffset(
|
|
parameters.MediaSource.LastCollectionsScan ?? SystemTime.MinValueUtc,
|
|
TimeSpan.Zero);
|
|
DateTimeOffset nextScan = lastScan + TimeSpan.FromHours(parameters.LibraryRefreshInterval);
|
|
if (parameters.ForceScan || parameters.LibraryRefreshInterval > 0 && nextScan < DateTimeOffset.Now)
|
|
{
|
|
Either<BaseError, Unit> result = await _scanner.ScanCollections(
|
|
parameters.ConnectionParameters.ActiveConnection,
|
|
parameters.ConnectionParameters.PlexServerAuthToken,
|
|
parameters.DeepScan,
|
|
cancellationToken);
|
|
|
|
if (result.IsRight)
|
|
{
|
|
parameters.MediaSource.LastCollectionsScan = DateTime.UtcNow;
|
|
await _mediaSourceRepository.UpdateLastCollectionScan(parameters.MediaSource);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return Unit.Default;
|
|
}
|
|
|
|
private record RequestParameters(
|
|
ConnectionParameters ConnectionParameters,
|
|
PlexMediaSource MediaSource,
|
|
bool ForceScan,
|
|
bool DeepScan,
|
|
int LibraryRefreshInterval,
|
|
string BaseUrl);
|
|
|
|
private record ConnectionParameters(PlexMediaSource PlexMediaSource, PlexConnection ActiveConnection)
|
|
{
|
|
public PlexServerAuthToken? PlexServerAuthToken { get; set; }
|
|
}
|
|
}
|