mirror of https://github.com/ErsatzTV/ErsatzTV.git
13 changed files with 25 additions and 341 deletions
@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
|
||||
using ErsatzTV.Core; |
||||
|
||||
namespace ErsatzTV.Application.Jellyfin; |
||||
|
||||
public record SynchronizeJellyfinAdminUserId(int JellyfinMediaSourceId) : IRequest<Either<BaseError, Unit>>, |
||||
IScannerBackgroundServiceRequest; |
@ -1,107 +0,0 @@
@@ -1,107 +0,0 @@
|
||||
using ErsatzTV.Core; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Interfaces.Jellyfin; |
||||
using ErsatzTV.Core.Interfaces.Repositories; |
||||
using ErsatzTV.Core.Jellyfin; |
||||
using Microsoft.Extensions.Caching.Memory; |
||||
using Microsoft.Extensions.Logging; |
||||
|
||||
namespace ErsatzTV.Application.Jellyfin; |
||||
|
||||
public class |
||||
SynchronizeJellyfinAdminUserIdHandler : IRequestHandler<SynchronizeJellyfinAdminUserId, |
||||
Either<BaseError, Unit>> |
||||
{ |
||||
private readonly IJellyfinApiClient _jellyfinApiClient; |
||||
private readonly IJellyfinSecretStore _jellyfinSecretStore; |
||||
private readonly ILogger<SynchronizeJellyfinAdminUserIdHandler> _logger; |
||||
private readonly IMediaSourceRepository _mediaSourceRepository; |
||||
private readonly IMemoryCache _memoryCache; |
||||
|
||||
public SynchronizeJellyfinAdminUserIdHandler( |
||||
IMemoryCache memoryCache, |
||||
IMediaSourceRepository mediaSourceRepository, |
||||
IJellyfinSecretStore jellyfinSecretStore, |
||||
IJellyfinApiClient jellyfinApiClient, |
||||
ILogger<SynchronizeJellyfinAdminUserIdHandler> logger) |
||||
{ |
||||
_memoryCache = memoryCache; |
||||
_mediaSourceRepository = mediaSourceRepository; |
||||
_jellyfinSecretStore = jellyfinSecretStore; |
||||
_jellyfinApiClient = jellyfinApiClient; |
||||
_logger = logger; |
||||
} |
||||
|
||||
public Task<Either<BaseError, Unit>> Handle( |
||||
SynchronizeJellyfinAdminUserId request, |
||||
CancellationToken cancellationToken) => |
||||
Validate(request) |
||||
.Map(v => v.ToEither<ConnectionParameters>()) |
||||
.BindT(PerformSync); |
||||
|
||||
private async Task<Either<BaseError, Unit>> PerformSync(ConnectionParameters parameters) |
||||
{ |
||||
if (_memoryCache.TryGetValue($"jellyfin_admin_user_id.{parameters.JellyfinMediaSource.Id}", out string _)) |
||||
{ |
||||
return Unit.Default; |
||||
} |
||||
|
||||
Either<BaseError, string> maybeUserId = await _jellyfinApiClient.GetAdminUserId( |
||||
parameters.ActiveConnection.Address, |
||||
parameters.ApiKey); |
||||
|
||||
return await maybeUserId.Match( |
||||
userId => |
||||
{ |
||||
// _logger.LogDebug("Jellyfin admin user id is {UserId}", userId);
|
||||
_memoryCache.Set($"jellyfin_admin_user_id.{parameters.JellyfinMediaSource.Id}", userId); |
||||
return Task.FromResult<Either<BaseError, Unit>>(Unit.Default); |
||||
}, |
||||
async error => |
||||
{ |
||||
// clear api key if unable to sync with jellyfin
|
||||
if (error.Value.Contains("Unauthorized")) |
||||
{ |
||||
await _jellyfinSecretStore.SaveSecrets( |
||||
new JellyfinSecrets { Address = parameters.ActiveConnection.Address, ApiKey = null }); |
||||
} |
||||
|
||||
return Left<BaseError, Unit>(error); |
||||
}); |
||||
} |
||||
|
||||
private Task<Validation<BaseError, ConnectionParameters>> Validate(SynchronizeJellyfinAdminUserId request) => |
||||
MediaSourceMustExist(request) |
||||
.BindT(MediaSourceMustHaveActiveConnection) |
||||
.BindT(MediaSourceMustHaveApiKey); |
||||
|
||||
private Task<Validation<BaseError, JellyfinMediaSource>> MediaSourceMustExist( |
||||
SynchronizeJellyfinAdminUserId request) => |
||||
_mediaSourceRepository.GetJellyfin(request.JellyfinMediaSourceId) |
||||
.Map(o => o.ToValidation<BaseError>("Jellyfin media source does not exist.")); |
||||
|
||||
private Validation<BaseError, ConnectionParameters> MediaSourceMustHaveActiveConnection( |
||||
JellyfinMediaSource jellyfinMediaSource) |
||||
{ |
||||
Option<JellyfinConnection> maybeConnection = jellyfinMediaSource.Connections.HeadOrNone(); |
||||
return maybeConnection.Map(connection => new ConnectionParameters(jellyfinMediaSource, connection)) |
||||
.ToValidation<BaseError>("Jellyfin media source requires an active connection"); |
||||
} |
||||
|
||||
private async Task<Validation<BaseError, ConnectionParameters>> MediaSourceMustHaveApiKey( |
||||
ConnectionParameters connectionParameters) |
||||
{ |
||||
JellyfinSecrets secrets = await _jellyfinSecretStore.ReadSecrets(); |
||||
return Optional(secrets.Address == connectionParameters.ActiveConnection.Address) |
||||
.Where(match => match) |
||||
.Map(_ => connectionParameters with { ApiKey = secrets.ApiKey }) |
||||
.ToValidation<BaseError>("Jellyfin media source requires an api key"); |
||||
} |
||||
|
||||
private sealed record ConnectionParameters( |
||||
JellyfinMediaSource JellyfinMediaSource, |
||||
JellyfinConnection ActiveConnection) |
||||
{ |
||||
public string ApiKey { get; set; } |
||||
} |
||||
} |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
using ErsatzTV.Core; |
||||
|
||||
namespace ErsatzTV.Scanner.Application.Jellyfin; |
||||
|
||||
public record SynchronizeJellyfinAdminUserId(int JellyfinMediaSourceId) : IRequest<Either<BaseError, Unit>>; |
@ -1,103 +0,0 @@
@@ -1,103 +0,0 @@
|
||||
using ErsatzTV.Core; |
||||
using ErsatzTV.Core.Domain; |
||||
using ErsatzTV.Core.Interfaces.Jellyfin; |
||||
using ErsatzTV.Core.Interfaces.Repositories; |
||||
using ErsatzTV.Core.Jellyfin; |
||||
using Microsoft.Extensions.Caching.Memory; |
||||
|
||||
namespace ErsatzTV.Scanner.Application.Jellyfin; |
||||
|
||||
public class |
||||
SynchronizeJellyfinAdminUserIdHandler : IRequestHandler<SynchronizeJellyfinAdminUserId, |
||||
Either<BaseError, Unit>> |
||||
{ |
||||
private readonly IJellyfinApiClient _jellyfinApiClient; |
||||
private readonly IJellyfinSecretStore _jellyfinSecretStore; |
||||
private readonly IMediaSourceRepository _mediaSourceRepository; |
||||
private readonly IMemoryCache _memoryCache; |
||||
|
||||
public SynchronizeJellyfinAdminUserIdHandler( |
||||
IMemoryCache memoryCache, |
||||
IMediaSourceRepository mediaSourceRepository, |
||||
IJellyfinSecretStore jellyfinSecretStore, |
||||
IJellyfinApiClient jellyfinApiClient) |
||||
{ |
||||
_memoryCache = memoryCache; |
||||
_mediaSourceRepository = mediaSourceRepository; |
||||
_jellyfinSecretStore = jellyfinSecretStore; |
||||
_jellyfinApiClient = jellyfinApiClient; |
||||
} |
||||
|
||||
public Task<Either<BaseError, Unit>> Handle( |
||||
SynchronizeJellyfinAdminUserId request, |
||||
CancellationToken cancellationToken) => |
||||
Validate(request) |
||||
.Map(v => v.ToEither<ConnectionParameters>()) |
||||
.BindT(PerformSync); |
||||
|
||||
private async Task<Either<BaseError, Unit>> PerformSync(ConnectionParameters parameters) |
||||
{ |
||||
if (_memoryCache.TryGetValue($"jellyfin_admin_user_id.{parameters.JellyfinMediaSource.Id}", out string? _)) |
||||
{ |
||||
return Unit.Default; |
||||
} |
||||
|
||||
Either<BaseError, string> maybeUserId = await _jellyfinApiClient.GetAdminUserId( |
||||
parameters.ActiveConnection.Address, |
||||
parameters.ApiKey); |
||||
|
||||
return await maybeUserId.Match( |
||||
userId => |
||||
{ |
||||
// _logger.LogDebug("Jellyfin admin user id is {UserId}", userId);
|
||||
_memoryCache.Set($"jellyfin_admin_user_id.{parameters.JellyfinMediaSource.Id}", userId); |
||||
return Task.FromResult<Either<BaseError, Unit>>(Unit.Default); |
||||
}, |
||||
async error => |
||||
{ |
||||
// clear api key if unable to sync with jellyfin
|
||||
if (error.Value.Contains("Unauthorized")) |
||||
{ |
||||
await _jellyfinSecretStore.SaveSecrets( |
||||
new JellyfinSecrets { Address = parameters.ActiveConnection.Address, ApiKey = null }); |
||||
} |
||||
|
||||
return Left<BaseError, Unit>(error); |
||||
}); |
||||
} |
||||
|
||||
private Task<Validation<BaseError, ConnectionParameters>> Validate(SynchronizeJellyfinAdminUserId request) => |
||||
MediaSourceMustExist(request) |
||||
.BindT(MediaSourceMustHaveActiveConnection) |
||||
.BindT(MediaSourceMustHaveApiKey); |
||||
|
||||
private Task<Validation<BaseError, JellyfinMediaSource>> MediaSourceMustExist( |
||||
SynchronizeJellyfinAdminUserId request) => |
||||
_mediaSourceRepository.GetJellyfin(request.JellyfinMediaSourceId) |
||||
.Map(o => o.ToValidation<BaseError>("Jellyfin media source does not exist.")); |
||||
|
||||
private Validation<BaseError, ConnectionParameters> MediaSourceMustHaveActiveConnection( |
||||
JellyfinMediaSource jellyfinMediaSource) |
||||
{ |
||||
Option<JellyfinConnection> maybeConnection = jellyfinMediaSource.Connections.HeadOrNone(); |
||||
return maybeConnection.Map(connection => new ConnectionParameters(jellyfinMediaSource, connection)) |
||||
.ToValidation<BaseError>("Jellyfin media source requires an active connection"); |
||||
} |
||||
|
||||
private async Task<Validation<BaseError, ConnectionParameters>> MediaSourceMustHaveApiKey( |
||||
ConnectionParameters connectionParameters) |
||||
{ |
||||
JellyfinSecrets secrets = await _jellyfinSecretStore.ReadSecrets(); |
||||
return Optional(secrets.Address == connectionParameters.ActiveConnection.Address) |
||||
.Where(match => match) |
||||
.Map(_ => connectionParameters with { ApiKey = secrets.ApiKey }) |
||||
.ToValidation<BaseError>("Jellyfin media source requires an api key"); |
||||
} |
||||
|
||||
private record ConnectionParameters( |
||||
JellyfinMediaSource JellyfinMediaSource, |
||||
JellyfinConnection ActiveConnection) |
||||
{ |
||||
public string? ApiKey { get; init; } |
||||
} |
||||
} |
Loading…
Reference in new issue