mirror of https://github.com/ErsatzTV/ErsatzTV.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
6.5 KiB
146 lines
6.5 KiB
using ErsatzTV.Core; |
|
using ErsatzTV.Core.Domain; |
|
using ErsatzTV.Core.Interfaces.Jellyfin; |
|
using ErsatzTV.Core.Interfaces.Repositories; |
|
using ErsatzTV.Core.Jellyfin; |
|
using ErsatzTV.Scanner.Core.Interfaces; |
|
using Microsoft.Extensions.Logging; |
|
|
|
namespace ErsatzTV.Scanner.Application.Jellyfin; |
|
|
|
public class |
|
SynchronizeJellyfinShowByIdHandler : IRequestHandler<SynchronizeJellyfinShowById, Either<BaseError, string>> |
|
{ |
|
private readonly IJellyfinSecretStore _jellyfinSecretStore; |
|
private readonly IJellyfinTelevisionLibraryScanner _jellyfinTelevisionLibraryScanner; |
|
private readonly IJellyfinTelevisionRepository _jellyfinTelevisionRepository; |
|
private readonly ILogger<SynchronizeJellyfinShowByIdHandler> _logger; |
|
private readonly IScannerProxy _scannerProxy; |
|
private readonly IMediaSourceRepository _mediaSourceRepository; |
|
|
|
public SynchronizeJellyfinShowByIdHandler( |
|
IScannerProxy scannerProxy, |
|
IMediaSourceRepository mediaSourceRepository, |
|
IJellyfinTelevisionRepository jellyfinTelevisionRepository, |
|
IJellyfinSecretStore jellyfinSecretStore, |
|
IJellyfinTelevisionLibraryScanner jellyfinTelevisionLibraryScanner, |
|
ILogger<SynchronizeJellyfinShowByIdHandler> logger) |
|
{ |
|
_scannerProxy = scannerProxy; |
|
_mediaSourceRepository = mediaSourceRepository; |
|
_jellyfinTelevisionRepository = jellyfinTelevisionRepository; |
|
_jellyfinSecretStore = jellyfinSecretStore; |
|
_jellyfinTelevisionLibraryScanner = jellyfinTelevisionLibraryScanner; |
|
_logger = logger; |
|
} |
|
|
|
public async Task<Either<BaseError, string>> Handle( |
|
SynchronizeJellyfinShowById request, |
|
CancellationToken cancellationToken) |
|
{ |
|
Validation<BaseError, RequestParameters> validation = await Validate(request, cancellationToken); |
|
return await validation.Match( |
|
parameters => Synchronize(parameters, cancellationToken), |
|
error => Task.FromResult<Either<BaseError, string>>(error.Join())); |
|
} |
|
|
|
private async Task<Either<BaseError, string>> Synchronize( |
|
RequestParameters parameters, |
|
CancellationToken cancellationToken) |
|
{ |
|
if (parameters.Library.MediaKind != LibraryMediaKind.Shows) |
|
{ |
|
return BaseError.New($"Library {parameters.Library.Name} is not a TV show library"); |
|
} |
|
|
|
_scannerProxy.SetBaseUrl(parameters.BaseUrl); |
|
|
|
_logger.LogInformation( |
|
"Starting targeted scan for show '{ShowTitle}' in Jellyfin library {LibraryName}", |
|
parameters.ShowTitle, |
|
parameters.Library.Name); |
|
|
|
Either<BaseError, Unit> result = await _jellyfinTelevisionLibraryScanner.ScanSingleShow( |
|
parameters.ConnectionParameters, |
|
parameters.Library, |
|
parameters.ItemId, |
|
parameters.ShowTitle, |
|
parameters.DeepScan, |
|
cancellationToken); |
|
|
|
foreach (BaseError error in result.LeftToSeq()) |
|
{ |
|
_logger.LogError("Error synchronizing Jellyfin show '{ShowTitle}': {Error}", parameters.ShowTitle, error); |
|
} |
|
|
|
return result.Map(_ => $"Show '{parameters.ShowTitle}' in {parameters.Library.Name}"); |
|
} |
|
|
|
private async Task<Validation<BaseError, RequestParameters>> Validate( |
|
SynchronizeJellyfinShowById request, |
|
CancellationToken cancellationToken) => |
|
(await ValidateConnection(request), await JellyfinLibraryMustExist(request), |
|
await JellyfinShowMustExist(request, cancellationToken)) |
|
.Apply((connectionParameters, jellyfinLibrary, showTitleItemId) => |
|
new RequestParameters( |
|
connectionParameters, |
|
jellyfinLibrary, |
|
showTitleItemId.ItemId, |
|
showTitleItemId.Title, |
|
request.DeepScan, |
|
request.BaseUrl |
|
)); |
|
|
|
private Task<Validation<BaseError, JellyfinConnectionParameters>> ValidateConnection( |
|
SynchronizeJellyfinShowById request) => |
|
JellyfinMediaSourceMustExist(request) |
|
.BindT(MediaSourceMustHaveActiveConnection) |
|
.BindT(MediaSourceMustHaveApiKey); |
|
|
|
private Task<Validation<BaseError, JellyfinMediaSource>> JellyfinMediaSourceMustExist( |
|
SynchronizeJellyfinShowById request) => |
|
_mediaSourceRepository.GetJellyfinByLibraryId(request.JellyfinLibraryId) |
|
.Map(v => v.ToValidation<BaseError>( |
|
$"Jellyfin media source for library {request.JellyfinLibraryId} does not exist.")); |
|
|
|
private Validation<BaseError, JellyfinConnectionParameters> MediaSourceMustHaveActiveConnection( |
|
JellyfinMediaSource jellyfinMediaSource) |
|
{ |
|
Option<JellyfinConnection> maybeConnection = jellyfinMediaSource.Connections.HeadOrNone(); |
|
return maybeConnection.Map(connection => new JellyfinConnectionParameters( |
|
connection.Address, |
|
string.Empty, |
|
connection.JellyfinMediaSourceId)) |
|
.ToValidation<BaseError>("Jellyfin media source requires an active connection"); |
|
} |
|
|
|
private async Task<Validation<BaseError, JellyfinConnectionParameters>> MediaSourceMustHaveApiKey( |
|
JellyfinConnectionParameters connectionParameters) |
|
{ |
|
JellyfinSecrets secrets = await _jellyfinSecretStore.ReadSecrets(); |
|
return Optional(secrets.Address == connectionParameters.Address) |
|
.Where(match => match) |
|
.Map(_ => connectionParameters with { ApiKey = secrets.ApiKey }) |
|
.ToValidation<BaseError>("Jellyfin media source requires an api key"); |
|
} |
|
|
|
private Task<Validation<BaseError, JellyfinLibrary>> JellyfinLibraryMustExist( |
|
SynchronizeJellyfinShowById request) => |
|
_mediaSourceRepository.GetJellyfinLibrary(request.JellyfinLibraryId) |
|
.Map(v => v.ToValidation<BaseError>($"Jellyfin library {request.JellyfinLibraryId} does not exist.")); |
|
|
|
private Task<Validation<BaseError, JellyfinShowTitleItemIdResult>> JellyfinShowMustExist( |
|
SynchronizeJellyfinShowById request, |
|
CancellationToken cancellationToken) => |
|
_jellyfinTelevisionRepository.GetShowTitleItemId(request.JellyfinLibraryId, request.ShowId, cancellationToken) |
|
.Map(v => v.ToValidation<BaseError>( |
|
$"Jellyfin show {request.ShowId} does not exist in library {request.JellyfinLibraryId}.")); |
|
|
|
private record RequestParameters( |
|
JellyfinConnectionParameters ConnectionParameters, |
|
JellyfinLibrary Library, |
|
string ItemId, |
|
string ShowTitle, |
|
bool DeepScan, |
|
string BaseUrl); |
|
}
|
|
|