Browse Source

improve trakt list url validation (#1824)

* improve trakt url validation and logging

* update changelog
pull/1825/head
Jason Dove 1 year ago committed by GitHub
parent
commit
5947555e86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 41
      ErsatzTV.Application/MediaCollections/Commands/AddTraktListHandler.cs
  3. 12
      ErsatzTV.Application/MediaCollections/Commands/TraktCommandBase.cs
  4. 2
      ErsatzTV.Infrastructure/Trakt/TraktApiClient.cs
  5. 9
      ErsatzTV/Services/WorkerService.cs

1
CHANGELOG.md

@ -51,6 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -51,6 +51,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Remove some unnecessary API calls related to media server scanning and paging
- Improve trakt list URL validation; non-trakt URLs will no longer be requested
## [0.8.7-beta] - 2024-06-26
### Added

41
ErsatzTV.Application/MediaCollections/Commands/AddTraktListHandler.cs

@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
using System.Text.RegularExpressions;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.Interfaces.Locking;
using ErsatzTV.Core.Interfaces.Metadata;
using ErsatzTV.Core.Interfaces.Repositories.Caching;
@ -11,7 +12,7 @@ using Microsoft.Extensions.Logging; @@ -11,7 +12,7 @@ using Microsoft.Extensions.Logging;
namespace ErsatzTV.Application.MediaCollections;
public class AddTraktListHandler : TraktCommandBase, IRequestHandler<AddTraktList, Either<BaseError, Unit>>
public partial class AddTraktListHandler : TraktCommandBase, IRequestHandler<AddTraktList, Either<BaseError, Unit>>
{
private readonly IDbContextFactory<TvContext> _dbContextFactory;
private readonly IEntityLocker _entityLocker;
@ -47,8 +48,11 @@ public class AddTraktListHandler : TraktCommandBase, IRequestHandler<AddTraktLis @@ -47,8 +48,11 @@ public class AddTraktListHandler : TraktCommandBase, IRequestHandler<AddTraktLis
private static Validation<BaseError, Parameters> ValidateUrl(AddTraktList request)
{
const string PATTERN = @"(?:https:\/\/trakt\.tv\/users\/)?([\w\-_]+)\/(?:lists\/)?([\w\-_]+)";
Match match = Regex.Match(request.TraktListUrl, PATTERN);
// if we get a url, ensure it's for trakt.tv
Match match = Uri.IsWellFormedUriString(request.TraktListUrl, UriKind.Absolute)
? UriTraktListRegex().Match(request.TraktListUrl)
: ShorthandTraktListRegex().Match(request.TraktListUrl);
if (match.Success)
{
string user = match.Groups[1].Value;
@ -63,14 +67,33 @@ public class AddTraktListHandler : TraktCommandBase, IRequestHandler<AddTraktLis @@ -63,14 +67,33 @@ public class AddTraktListHandler : TraktCommandBase, IRequestHandler<AddTraktLis
{
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);
Logger.LogDebug("Searching for trakt list: {User}/{List}", parameters.User, parameters.List);
Either<BaseError, TraktList> maybeList = await TraktApiClient.GetUserList(parameters.User, parameters.List);
foreach (TraktList list in maybeList.RightToSeq())
{
maybeList = await SaveList(dbContext, list);
}
foreach (TraktList list in maybeList.RightToSeq())
{
maybeList = await SaveListItems(dbContext, list);
}
foreach (TraktList list in maybeList.RightToSeq())
{
// match list items (and update in search index)
maybeList = await MatchListItems(dbContext, list);
}
// match list items (and update in search index)
return maybeList.Map(_ => Unit.Default);
}
private sealed record Parameters(string User, string List);
[GeneratedRegex(@"https:\/\/trakt\.tv\/users\/([\w\-_]+)\/(?:lists\/)?([\w\-_]+)")]
private static partial Regex UriTraktListRegex();
[GeneratedRegex(@"([\w\-_]+)\/(?:lists\/)?([\w\-_]+)")]
private static partial Regex ShorthandTraktListRegex();
}

12
ErsatzTV.Application/MediaCollections/Commands/TraktCommandBase.cs

@ -30,11 +30,15 @@ public abstract class TraktCommandBase @@ -30,11 +30,15 @@ public abstract class TraktCommandBase
_searchIndex = searchIndex;
_fallbackMetadataProvider = fallbackMetadataProvider;
_logger = logger;
TraktApiClient = traktApiClient;
Logger = logger;
}
protected ITraktApiClient TraktApiClient { get; }
protected ILogger Logger { get; }
protected static Task<Validation<BaseError, TraktList>>
TraktListMustExist(TvContext dbContext, int traktListId) =>
dbContext.TraktLists
@ -43,8 +47,10 @@ public abstract class TraktCommandBase @@ -43,8 +47,10 @@ public abstract class TraktCommandBase
.SelectOneAsync(c => c.Id, c => c.Id == traktListId)
.Map(o => o.ToValidation<BaseError>($"TraktList {traktListId} does not exist."));
protected static async Task<Either<BaseError, TraktList>> SaveList(TvContext dbContext, TraktList list)
protected async Task<Either<BaseError, TraktList>> SaveList(TvContext dbContext, TraktList list)
{
_logger.LogDebug("Saving trakt list to database: {User}/{List}", list.User, list.List);
Option<TraktList> maybeExisting = await dbContext.TraktLists
.Include(l => l.Items)
.ThenInclude(i => i.Guids)
@ -72,6 +78,8 @@ public abstract class TraktCommandBase @@ -72,6 +78,8 @@ public abstract class TraktCommandBase
protected async Task<Either<BaseError, TraktList>> SaveListItems(TvContext dbContext, TraktList list)
{
_logger.LogDebug("Saving trakt list items to database: {User}/{List}", list.User, list.List);
Either<BaseError, List<TraktListItemWithGuids>> maybeItems =
await TraktApiClient.GetUserListItems(list.User, list.List);
@ -118,6 +126,8 @@ public abstract class TraktCommandBase @@ -118,6 +126,8 @@ public abstract class TraktCommandBase
{
try
{
_logger.LogDebug("Matching trakt list items: {User}/{List}", list.User, list.List);
var ids = new System.Collections.Generic.HashSet<int>();
foreach (TraktListItem item in list.Items

2
ErsatzTV.Infrastructure/Trakt/TraktApiClient.cs

@ -43,7 +43,7 @@ public class TraktApiClient : ITraktApiClient @@ -43,7 +43,7 @@ public class TraktApiClient : ITraktApiClient
Name = response.Name,
Description = response.Description,
ItemCount = response.ItemCount,
Items = new List<TraktListItem>()
Items = []
};
}
catch (Exception ex)

9
ErsatzTV/Services/WorkerService.cs

@ -81,7 +81,14 @@ public class WorkerService : BackgroundService @@ -81,7 +81,14 @@ public class WorkerService : BackgroundService
await mediator.Send(deleteOrphanedSubtitles, stoppingToken);
break;
case AddTraktList addTraktList:
await mediator.Send(addTraktList, stoppingToken);
Either<BaseError, Unit> result = await mediator.Send(addTraktList, stoppingToken);
foreach (BaseError error in result.LeftToSeq())
{
_logger.LogWarning(
"Unable to add trakt list {Url}: {Error}",
addTraktList.TraktListUrl,
error.Value);
}
break;
case DeleteTraktList deleteTraktList:
await mediator.Send(deleteTraktList, stoppingToken);

Loading…
Cancel
Save