using System.Text.RegularExpressions; using ErsatzTV.Core; using ErsatzTV.Core.Interfaces.Locking; using ErsatzTV.Core.Interfaces.Metadata; using ErsatzTV.Core.Interfaces.Repositories.Caching; using ErsatzTV.Core.Interfaces.Search; using ErsatzTV.Core.Interfaces.Trakt; using ErsatzTV.Infrastructure.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace ErsatzTV.Application.MediaCollections; public class AddTraktListHandler : TraktCommandBase, IRequestHandler> { private readonly IDbContextFactory _dbContextFactory; private readonly IEntityLocker _entityLocker; public AddTraktListHandler( ITraktApiClient traktApiClient, ICachingSearchRepository searchRepository, ISearchIndex searchIndex, IFallbackMetadataProvider fallbackMetadataProvider, IDbContextFactory dbContextFactory, ILogger logger, IEntityLocker entityLocker) : base(traktApiClient, searchRepository, searchIndex, fallbackMetadataProvider, logger) { _dbContextFactory = dbContextFactory; _entityLocker = entityLocker; } public async Task> Handle(AddTraktList request, CancellationToken cancellationToken) { try { Validation validation = ValidateUrl(request); return await validation.Match( DoAdd, error => Task.FromResult>(error.Join())); } finally { _entityLocker.UnlockTrakt(); } } private static Validation ValidateUrl(AddTraktList request) { const string PATTERN = @"(?:https:\/\/trakt\.tv\/users\/)?([\w\-_]+)\/(?:lists\/)?([\w\-_]+)"; Match match = Regex.Match(request.TraktListUrl, PATTERN); if (match.Success) { string user = match.Groups[1].Value; string list = match.Groups[2].Value; return new Parameters(user, list); } return BaseError.New("Invalid Trakt list url"); } private async Task> DoAdd(Parameters parameters) { await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync(); return await TraktApiClient.GetUserList(parameters.User, parameters.List) .BindT(list => SaveList(dbContext, list)) .BindT(list => SaveListItems(dbContext, list)) .BindT(list => MatchListItems(dbContext, list)) .MapT(_ => Unit.Default); // match list items (and update in search index) } private sealed record Parameters(string User, string List); }