mirror of
https://github.com/Radarr/Radarr
synced 2026-01-05 23:27:00 +01:00
Merge 9c3ef4f41e into b59ff0a3b1
This commit is contained in:
commit
d0ac535e37
9 changed files with 564 additions and 0 deletions
|
|
@ -0,0 +1,212 @@
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.ImportLists;
|
||||
using NzbDrone.Core.ImportLists.Filmweb;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ImportListTests.Filmweb
|
||||
{
|
||||
[TestFixture]
|
||||
public class FilmwebParserFixture : CoreTest<FilmwebParser>
|
||||
{
|
||||
private Mock<IHttpClient> _httpClient;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_httpClient = new Mock<IHttpClient>();
|
||||
}
|
||||
|
||||
private ImportListResponse CreateResponse(string url, string content, HttpStatusCode statusCode = HttpStatusCode.OK)
|
||||
{
|
||||
var httpRequest = new HttpRequest(url);
|
||||
var httpResponse = new HttpResponse(httpRequest, new HttpHeader(), Encoding.UTF8.GetBytes(content), statusCode);
|
||||
|
||||
return new ImportListResponse(new ImportListRequest(httpRequest), httpResponse);
|
||||
}
|
||||
|
||||
private void SetupMovieInfoResponse(long entityId, string title, string originalTitle, int year)
|
||||
{
|
||||
var movieInfoJson = $@"{{
|
||||
""id"": {entityId},
|
||||
""title"": ""{title}"",
|
||||
""originalTitle"": ""{originalTitle}"",
|
||||
""year"": {year},
|
||||
""type"": ""film"",
|
||||
""subType"": ""movie"",
|
||||
""posterPath"": ""/path/to/poster.jpg""
|
||||
}}";
|
||||
|
||||
var request = new HttpRequest($"https://www.filmweb.pl/api/v1/title/{entityId}/info");
|
||||
var response = new HttpResponse(request, new HttpHeader(), Encoding.UTF8.GetBytes(movieInfoJson), HttpStatusCode.OK);
|
||||
|
||||
_httpClient.Setup(c => c.Get(It.Is<HttpRequest>(r => r.Url.ToString().Contains($"/api/v1/title/{entityId}/info"))))
|
||||
.Returns(response);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_filmweb_want2see_list()
|
||||
{
|
||||
var listJson = @"[
|
||||
{""entity"": 123456, ""timestamp"": 1693737600, ""level"": 5},
|
||||
{""entity"": 789012, ""timestamp"": 1693824000, ""level"": 4}
|
||||
]";
|
||||
|
||||
SetupMovieInfoResponse(123456, "Blade Runner 2049", "Blade Runner 2049", 2017);
|
||||
SetupMovieInfoResponse(789012, "Dune", "Dune", 2021);
|
||||
|
||||
var parser = new FilmwebParser(_httpClient.Object, 100);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", listJson));
|
||||
|
||||
result.Should().HaveCount(2);
|
||||
|
||||
result.First().Title.Should().Be("Blade Runner 2049");
|
||||
result.First().Year.Should().Be(2017);
|
||||
|
||||
result[1].Title.Should().Be("Dune");
|
||||
result[1].Year.Should().Be(2021);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_respect_limit_parameter()
|
||||
{
|
||||
var listJson = @"[
|
||||
{""entity"": 111111, ""timestamp"": 1693737600, ""level"": 5},
|
||||
{""entity"": 222222, ""timestamp"": 1693824000, ""level"": 4},
|
||||
{""entity"": 333333, ""timestamp"": 1693910400, ""level"": 3}
|
||||
]";
|
||||
|
||||
SetupMovieInfoResponse(111111, "Movie 1", "Movie 1", 2020);
|
||||
SetupMovieInfoResponse(222222, "Movie 2", "Movie 2", 2021);
|
||||
|
||||
var parser = new FilmwebParser(_httpClient.Object, 2);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", listJson));
|
||||
|
||||
result.Should().HaveCount(2);
|
||||
result.First().Title.Should().Be("Movie 1");
|
||||
result[1].Title.Should().Be("Movie 2");
|
||||
|
||||
_httpClient.Verify(c => c.Get(It.Is<HttpRequest>(r => r.Url.ToString().Contains("/api/v1/title/333333/info"))), Times.Never);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_handle_empty_list()
|
||||
{
|
||||
var listJson = "[]";
|
||||
var parser = new FilmwebParser(_httpClient.Object, 100);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", listJson));
|
||||
|
||||
result.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_movies_with_failed_info_requests()
|
||||
{
|
||||
var listJson = @"[
|
||||
{""entity"": 123456, ""timestamp"": 1693737600, ""level"": 5},
|
||||
{""entity"": 789012, ""timestamp"": 1693824000, ""level"": 4}
|
||||
]";
|
||||
|
||||
SetupMovieInfoResponse(123456, "Working Movie", "Working Movie", 2020);
|
||||
|
||||
var failedRequest = new HttpRequest("https://www.filmweb.pl/api/v1/title/789012/info");
|
||||
var failedResponse = new HttpResponse(failedRequest, new HttpHeader(), System.Array.Empty<byte>(), HttpStatusCode.NotFound);
|
||||
_httpClient.Setup(c => c.Get(It.Is<HttpRequest>(r => r.Url.ToString().Contains("/api/v1/title/789012/info"))))
|
||||
.Returns(failedResponse);
|
||||
|
||||
var parser = new FilmwebParser(_httpClient.Object, 100);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", listJson));
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
result.First().Title.Should().Be("Working Movie");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_original_title_when_title_is_empty()
|
||||
{
|
||||
var listJson = @"[{""entity"": 123456, ""timestamp"": 1693737600, ""level"": 5}]";
|
||||
|
||||
var movieInfoJson = @"{
|
||||
""id"": 123456,
|
||||
""title"": """",
|
||||
""originalTitle"": ""Original Title"",
|
||||
""year"": 2020,
|
||||
""type"": ""film""
|
||||
}";
|
||||
|
||||
var request = new HttpRequest("https://www.filmweb.pl/api/v1/title/123456/info");
|
||||
var response = new HttpResponse(request, new HttpHeader(), Encoding.UTF8.GetBytes(movieInfoJson), HttpStatusCode.OK);
|
||||
_httpClient.Setup(c => c.Get(It.IsAny<HttpRequest>())).Returns(response);
|
||||
|
||||
var parser = new FilmwebParser(_httpClient.Object, 100);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", listJson));
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
result.First().Title.Should().Be("Original Title");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_enforce_limit_bounds()
|
||||
{
|
||||
var parser1 = new FilmwebParser(_httpClient.Object, -5);
|
||||
var parser2 = new FilmwebParser(_httpClient.Object, 1500);
|
||||
|
||||
parser1.Should().NotBeNull();
|
||||
parser2.Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_handle_invalid_json()
|
||||
{
|
||||
var invalidJson = "invalid json content";
|
||||
var parser = new FilmwebParser(_httpClient.Object, 100);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", invalidJson));
|
||||
result.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_handle_null_movie_info()
|
||||
{
|
||||
var listJson = @"[{""entity"": 123456, ""timestamp"": 1693737600, ""level"": 5}]";
|
||||
|
||||
var request = new HttpRequest("https://www.filmweb.pl/api/v1/title/123456/info");
|
||||
var response = new HttpResponse(request, new HttpHeader(), System.Array.Empty<byte>(), HttpStatusCode.InternalServerError);
|
||||
_httpClient.Setup(c => c.Get(It.IsAny<HttpRequest>())).Returns(response);
|
||||
|
||||
var parser = new FilmwebParser(_httpClient.Object, 100);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", listJson));
|
||||
|
||||
result.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_handle_malformed_entity_data()
|
||||
{
|
||||
var listJson = @"[
|
||||
{""timestamp"": 1693737600, ""level"": 5},
|
||||
{""entity"": 789012, ""timestamp"": 1693824000, ""level"": 4}
|
||||
]";
|
||||
|
||||
SetupMovieInfoResponse(789012, "Valid Movie", "Valid Movie", 2021);
|
||||
|
||||
var parser = new FilmwebParser(_httpClient.Object, 100);
|
||||
|
||||
var result = parser.ParseResponse(CreateResponse("https://www.filmweb.pl/api/v1/user/testuser/want2see/film", listJson));
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
result.First().Title.Should().Be("Valid Movie");
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/NzbDrone.Core/ImportLists/Filmweb/FilmwebImportBase.cs
Normal file
29
src/NzbDrone.Core/ImportLists/Filmweb/FilmwebImportBase.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Filmweb
|
||||
{
|
||||
public abstract class FilmwebImportBase<TSettings> : HttpImportListBase<TSettings>
|
||||
where TSettings : FilmwebSettingsBase<TSettings>, new()
|
||||
{
|
||||
public override ImportListType ListType => ImportListType.Filmweb;
|
||||
public override TimeSpan MinRefreshInterval => TimeSpan.FromHours(12);
|
||||
|
||||
protected FilmwebImportBase(IHttpClient httpClient,
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override IParseImportListResponse GetParser()
|
||||
{
|
||||
return new FilmwebParser(_httpClient, Settings.Limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
156
src/NzbDrone.Core/ImportLists/Filmweb/FilmwebParser.cs
Normal file
156
src/NzbDrone.Core/ImportLists/Filmweb/FilmwebParser.cs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.ImportLists.Exceptions;
|
||||
using NzbDrone.Core.ImportLists.ImportListMovies;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Filmweb
|
||||
{
|
||||
public class FilmwebParser : IParseImportListResponse
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly int _limit;
|
||||
private ImportListResponse _importResponse;
|
||||
|
||||
public FilmwebParser(IHttpClient httpClient, int limit)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
|
||||
if (limit <= 0)
|
||||
{
|
||||
limit = 100;
|
||||
}
|
||||
|
||||
if (limit > 1000)
|
||||
{
|
||||
limit = 1000;
|
||||
}
|
||||
|
||||
_limit = limit;
|
||||
}
|
||||
|
||||
public virtual IList<ImportListMovie> ParseResponse(ImportListResponse importResponse)
|
||||
{
|
||||
_importResponse = importResponse;
|
||||
|
||||
var movies = new List<ImportListMovie>();
|
||||
|
||||
if (!PreProcess(_importResponse))
|
||||
{
|
||||
return movies;
|
||||
}
|
||||
|
||||
var content = _importResponse.Content;
|
||||
|
||||
try
|
||||
{
|
||||
var movieEntities = JsonSerializer.Deserialize<List<FilmwebMovieEntity>>(content);
|
||||
|
||||
if (movieEntities != null)
|
||||
{
|
||||
var limitedEntities = movieEntities.Take(_limit).ToList();
|
||||
|
||||
foreach (var entity in limitedEntities)
|
||||
{
|
||||
try
|
||||
{
|
||||
var movieInfo = GetMovieInfo(entity.Entity);
|
||||
if (movieInfo != null)
|
||||
{
|
||||
var movie = new ImportListMovie()
|
||||
{
|
||||
Title = !string.IsNullOrEmpty(movieInfo.Title) ? movieInfo.Title : movieInfo.OriginalTitle,
|
||||
Year = movieInfo.Year
|
||||
};
|
||||
|
||||
movies.AddIfNotNull(movie);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
}
|
||||
|
||||
return movies;
|
||||
}
|
||||
|
||||
private FilmwebMovieInfo GetMovieInfo(long entityId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new HttpRequestBuilder("https://www.filmweb.pl")
|
||||
.Resource($"/api/v1/title/{entityId}/info")
|
||||
.Accept(HttpAccept.Json)
|
||||
.Build();
|
||||
|
||||
var response = _httpClient.Get(request);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return JsonSerializer.Deserialize<FilmwebMovieInfo>(response.Content);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual bool PreProcess(ImportListResponse importListResponse)
|
||||
{
|
||||
if (importListResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new ImportListException(importListResponse, "Filmweb call resulted in an unexpected StatusCode [{0}]", importListResponse.HttpResponse.StatusCode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class FilmwebMovieEntity
|
||||
{
|
||||
[JsonPropertyName("entity")]
|
||||
public long Entity { get; set; }
|
||||
|
||||
[JsonPropertyName("timestamp")]
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
[JsonPropertyName("level")]
|
||||
public int Level { get; set; }
|
||||
}
|
||||
|
||||
public class FilmwebMovieInfo
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[JsonPropertyName("originalTitle")]
|
||||
public string OriginalTitle { get; set; }
|
||||
|
||||
[JsonPropertyName("year")]
|
||||
public int Year { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonPropertyName("subType")]
|
||||
public string SubType { get; set; }
|
||||
|
||||
[JsonPropertyName("posterPath")]
|
||||
public string PosterPath { get; set; }
|
||||
}
|
||||
}
|
||||
45
src/NzbDrone.Core/ImportLists/Filmweb/FilmwebSettingsBase.cs
Normal file
45
src/NzbDrone.Core/ImportLists/Filmweb/FilmwebSettingsBase.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Filmweb
|
||||
{
|
||||
public class FilmwebSettingsBaseValidator<TSettings> : AbstractValidator<TSettings>
|
||||
where TSettings : FilmwebSettingsBase<TSettings>
|
||||
{
|
||||
public FilmwebSettingsBaseValidator()
|
||||
{
|
||||
RuleFor(c => c.Username).NotEmpty()
|
||||
.WithMessage("Username is required");
|
||||
|
||||
RuleFor(c => c.Limit)
|
||||
.GreaterThan(0)
|
||||
.LessThanOrEqualTo(1000)
|
||||
.WithMessage("Must be integer between 1 and 1000");
|
||||
}
|
||||
}
|
||||
|
||||
public class FilmwebSettingsBase<TSettings> : ImportListSettingsBase<TSettings>
|
||||
where TSettings : FilmwebSettingsBase<TSettings>
|
||||
{
|
||||
private static readonly FilmwebSettingsBaseValidator<TSettings> Validator = new ();
|
||||
|
||||
public FilmwebSettingsBase()
|
||||
{
|
||||
Limit = 100;
|
||||
}
|
||||
|
||||
public string Link => "https://www.filmweb.pl";
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Filmweb username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(98, Label = "Limit", HelpText = "Limit the number of movies to get")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate((TSettings)this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Filmweb.User
|
||||
{
|
||||
public class FilmwebUserImport : FilmwebImportBase<FilmwebUserSettings>
|
||||
{
|
||||
public FilmwebUserImport(IHttpClient httpClient,
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Filmweb Watchlist";
|
||||
public override bool Enabled => true;
|
||||
public override bool EnableAuto => false;
|
||||
|
||||
public override IImportListRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new FilmwebUserRequestGenerator
|
||||
{
|
||||
Settings = Settings,
|
||||
HttpClient = _httpClient,
|
||||
Logger = _logger
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Filmweb.User
|
||||
{
|
||||
public enum FilmwebUserListType
|
||||
{
|
||||
[FieldOption(Label = "Want to See")]
|
||||
WantToSee = 0,
|
||||
[FieldOption(Label = "Rated")]
|
||||
Rated = 1,
|
||||
[FieldOption(Label = "Favorites")]
|
||||
Favorites = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Filmweb.User
|
||||
{
|
||||
public class FilmwebUserRequestGenerator : IImportListRequestGenerator
|
||||
{
|
||||
public FilmwebUserSettings Settings { get; set; }
|
||||
public IHttpClient HttpClient { get; set; }
|
||||
public Logger Logger { get; set; }
|
||||
|
||||
public virtual ImportListPageableRequestChain GetMovies()
|
||||
{
|
||||
var pageableRequests = new ImportListPageableRequestChain();
|
||||
pageableRequests.Add(GetMoviesRequest());
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<ImportListRequest> GetMoviesRequest()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(Settings.Link.Trim())
|
||||
.Accept(HttpAccept.Json);
|
||||
|
||||
switch (Settings.FilmwebListType)
|
||||
{
|
||||
case (int)FilmwebUserListType.WantToSee:
|
||||
requestBuilder.Resource($"/api/v1/user/{Settings.Username.Trim()}/want2see/film");
|
||||
break;
|
||||
case (int)FilmwebUserListType.Rated:
|
||||
requestBuilder.Resource($"/api/v1/user/{Settings.Username.Trim()}/votes/film");
|
||||
break;
|
||||
case (int)FilmwebUserListType.Favorites:
|
||||
requestBuilder.Resource($"/api/v1/user/{Settings.Username.Trim()}/favorites/film");
|
||||
break;
|
||||
}
|
||||
|
||||
var request = new ImportListRequest(requestBuilder.Build());
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Filmweb.User
|
||||
{
|
||||
public class FilmwebUserSettingsValidator : FilmwebSettingsBaseValidator<FilmwebUserSettings>
|
||||
{
|
||||
public FilmwebUserSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.FilmwebListType).NotNull();
|
||||
}
|
||||
}
|
||||
|
||||
public class FilmwebUserSettings : FilmwebSettingsBase<FilmwebUserSettings>
|
||||
{
|
||||
private static readonly FilmwebUserSettingsValidator Validator = new ();
|
||||
|
||||
public FilmwebUserSettings()
|
||||
{
|
||||
FilmwebListType = (int)FilmwebUserListType.WantToSee;
|
||||
}
|
||||
|
||||
[FieldDefinition(2, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(FilmwebUserListType), HelpText = "Type of list to import from Filmweb")]
|
||||
public int FilmwebListType { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ public enum ImportListType
|
|||
Trakt,
|
||||
Plex,
|
||||
Simkl,
|
||||
Filmweb,
|
||||
Other,
|
||||
Advanced
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue