mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-19 16:01:46 +02:00
New: TMDb List Rework
This commit is contained in:
parent
bdc1adb2ed
commit
65287ec4f3
@ -16,17 +16,19 @@ public RadarrCloudRequestBuilder()
|
|||||||
Services = new HttpRequestBuilder("https://radarr.lidarr.audio/v1/")
|
Services = new HttpRequestBuilder("https://radarr.lidarr.audio/v1/")
|
||||||
.CreateFactory();
|
.CreateFactory();
|
||||||
|
|
||||||
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}")
|
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/{api}/{route}/{id}{secondaryRoute}")
|
||||||
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
|
.SetHeader("Authorization", $"Bearer {AuthToken}")
|
||||||
.CreateFactory();
|
.CreateFactory();
|
||||||
|
|
||||||
TMDBSingle = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}")
|
TMDBSingle = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}")
|
||||||
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
|
.SetHeader("Authorization", $"Bearer {AuthToken}")
|
||||||
.CreateFactory();
|
.CreateFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHttpRequestBuilderFactory Services { get; private set; }
|
public IHttpRequestBuilderFactory Services { get; private set; }
|
||||||
public IHttpRequestBuilderFactory TMDB { get; private set; }
|
public IHttpRequestBuilderFactory TMDB { get; private set; }
|
||||||
public IHttpRequestBuilderFactory TMDBSingle { get; private set; }
|
public IHttpRequestBuilderFactory TMDBSingle { get; private set; }
|
||||||
|
|
||||||
|
public string AuthToken => "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxYTczNzMzMDE5NjFkMDNmOTdmODUzYTg3NmRkMTIxMiIsInN1YiI6IjU4NjRmNTkyYzNhMzY4MGFiNjAxNzUzNCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.gh1BwogCCKOda6xj9FRMgAAj_RYKMMPC3oNlcBtlmwk";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,17 @@ public class MovieSearchRoot
|
|||||||
public int total_pages { get; set; }
|
public int total_pages { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AuthRefreshTokenResponse
|
||||||
|
{
|
||||||
|
public string request_token { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AuthAccessTokenResponse
|
||||||
|
{
|
||||||
|
public string access_token { get; set; }
|
||||||
|
public string account_id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class MovieResult
|
public class MovieResult
|
||||||
{
|
{
|
||||||
public string poster_path { get; set; }
|
public string poster_path { get; set; }
|
||||||
@ -37,6 +48,13 @@ public class MovieResult
|
|||||||
public string physical_release_note { get; set; }
|
public string physical_release_note { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CreditsResult : MovieResult
|
||||||
|
{
|
||||||
|
public string department { get; set; }
|
||||||
|
public string job { get; set; }
|
||||||
|
public string credit_id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class MovieResourceRoot
|
public class MovieResourceRoot
|
||||||
{
|
{
|
||||||
public bool adult { get; set; }
|
public bool adult { get; set; }
|
||||||
@ -181,17 +199,31 @@ public class Video
|
|||||||
|
|
||||||
public class ListResponseRoot
|
public class ListResponseRoot
|
||||||
{
|
{
|
||||||
public string created_by { get; set; }
|
|
||||||
public string description { get; set; }
|
|
||||||
public int favorite_count { get; set; }
|
|
||||||
public string id { get; set; }
|
public string id { get; set; }
|
||||||
public Item[] items { get; set; }
|
public Item[] results { get; set; }
|
||||||
public int item_count { get; set; }
|
public int total_results { get; set; }
|
||||||
public string iso_639_1 { get; set; }
|
public string iso_639_1 { get; set; }
|
||||||
public string name { get; set; }
|
public string name { get; set; }
|
||||||
public object poster_path { get; set; }
|
public object poster_path { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CollectionResponseRoot
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public string overview { get; set; }
|
||||||
|
public string poster_path { get; set; }
|
||||||
|
public string backdrop_path { get; set; }
|
||||||
|
public MovieResult[] parts { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PersonCreditsRoot
|
||||||
|
{
|
||||||
|
public CreditsResult[] cast { get; set; }
|
||||||
|
public CreditsResult[] crew { get; set; }
|
||||||
|
public int id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class Item : MovieResult
|
public class Item : MovieResult
|
||||||
{
|
{
|
||||||
public string media_type { get; set; }
|
public string media_type { get; set; }
|
||||||
|
@ -63,6 +63,7 @@ public HashSet<int> GetChangedMovies(DateTime startTime)
|
|||||||
var startDate = startTime.ToString("o");
|
var startDate = startTime.ToString("o");
|
||||||
|
|
||||||
var request = _movieBuilder.Create()
|
var request = _movieBuilder.Create()
|
||||||
|
.SetSegment("api", "3")
|
||||||
.SetSegment("route", "movie")
|
.SetSegment("route", "movie")
|
||||||
.SetSegment("id", "")
|
.SetSegment("id", "")
|
||||||
.SetSegment("secondaryRoute", "changes")
|
.SetSegment("secondaryRoute", "changes")
|
||||||
@ -82,6 +83,7 @@ public Tuple<Movie, List<Credit>> GetMovieInfo(int tmdbId, Profile profile, bool
|
|||||||
var langCode = profile != null ? IsoLanguages.Get(profile.Language)?.TwoLetterCode ?? "en" : "en";
|
var langCode = profile != null ? IsoLanguages.Get(profile.Language)?.TwoLetterCode ?? "en" : "en";
|
||||||
|
|
||||||
var request = _movieBuilder.Create()
|
var request = _movieBuilder.Create()
|
||||||
|
.SetSegment("api", "3")
|
||||||
.SetSegment("route", "movie")
|
.SetSegment("route", "movie")
|
||||||
.SetSegment("id", tmdbId.ToString())
|
.SetSegment("id", tmdbId.ToString())
|
||||||
.SetSegment("secondaryRoute", "")
|
.SetSegment("secondaryRoute", "")
|
||||||
@ -329,6 +331,7 @@ public Tuple<Movie, List<Credit>> GetMovieInfo(int tmdbId, Profile profile, bool
|
|||||||
public Movie GetMovieInfo(string imdbId)
|
public Movie GetMovieInfo(string imdbId)
|
||||||
{
|
{
|
||||||
var request = _movieBuilder.Create()
|
var request = _movieBuilder.Create()
|
||||||
|
.SetSegment("api", "3")
|
||||||
.SetSegment("route", "find")
|
.SetSegment("route", "find")
|
||||||
.SetSegment("id", imdbId)
|
.SetSegment("id", imdbId)
|
||||||
.SetSegment("secondaryRoute", "")
|
.SetSegment("secondaryRoute", "")
|
||||||
@ -504,6 +507,7 @@ public List<Movie> SearchForNewMovie(string title)
|
|||||||
var firstChar = searchTerm.First();
|
var firstChar = searchTerm.First();
|
||||||
|
|
||||||
var request = _movieBuilder.Create()
|
var request = _movieBuilder.Create()
|
||||||
|
.SetSegment("api", "3")
|
||||||
.SetSegment("route", "search")
|
.SetSegment("route", "search")
|
||||||
.SetSegment("id", "movie")
|
.SetSegment("id", "movie")
|
||||||
.SetSegment("secondaryRoute", "")
|
.SetSegment("secondaryRoute", "")
|
||||||
|
@ -51,7 +51,7 @@ public List<INetImport> Enabled()
|
|||||||
|
|
||||||
public List<INetImport> Discoverable()
|
public List<INetImport> Discoverable()
|
||||||
{
|
{
|
||||||
var enabledImporters = GetAvailableProviders().Where(n => (n.GetType() == typeof(Radarr.RadarrLists) || n.GetType() == typeof(TMDb.TMDbImport)));
|
var enabledImporters = GetAvailableProviders().Where(n => (n.GetType() == typeof(Radarr.RadarrLists) || n.GetType() == typeof(TMDb.Popular.TMDbPopularImport)));
|
||||||
var indexers = FilterBlockedIndexers(enabledImporters);
|
var indexers = FilterBlockedIndexers(enabledImporters);
|
||||||
return indexers.ToList();
|
return indexers.ToList();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cloud;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Collection
|
||||||
|
{
|
||||||
|
public class TMDbCollectionImport : TMDbNetImportBase<TMDbCollectionSettings>
|
||||||
|
{
|
||||||
|
public TMDbCollectionImport(IRadarrCloudRequestBuilder requestBuilder,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IParsingService parsingService,
|
||||||
|
ISearchForNewMovie searchForNewMovie,
|
||||||
|
Logger logger)
|
||||||
|
: base(requestBuilder, httpClient, configService, parsingService, searchForNewMovie, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "TMDb Collection";
|
||||||
|
public override bool Enabled => true;
|
||||||
|
public override bool EnableAuto => false;
|
||||||
|
|
||||||
|
public override IParseNetImportResponse GetParser()
|
||||||
|
{
|
||||||
|
return new TMDbCollectionParser(_skyhookProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override INetImportRequestGenerator GetRequestGenerator()
|
||||||
|
{
|
||||||
|
return new TMDbCollectionRequestGenerator()
|
||||||
|
{
|
||||||
|
RequestBuilder = _requestBuilder,
|
||||||
|
Settings = Settings,
|
||||||
|
Logger = _logger,
|
||||||
|
HttpClient = _httpClient
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Collection
|
||||||
|
{
|
||||||
|
public class TMDbCollectionParser : TMDbParser
|
||||||
|
{
|
||||||
|
private readonly ISearchForNewMovie _skyhookProxy;
|
||||||
|
|
||||||
|
public TMDbCollectionParser(ISearchForNewMovie skyhookProxy)
|
||||||
|
: base(skyhookProxy)
|
||||||
|
{
|
||||||
|
_skyhookProxy = skyhookProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IList<Movie> ParseResponse(NetImportResponse importResponse)
|
||||||
|
{
|
||||||
|
var movies = new List<Movie>();
|
||||||
|
|
||||||
|
if (!PreProcess(importResponse))
|
||||||
|
{
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonResponse = JsonConvert.DeserializeObject<CollectionResponseRoot>(importResponse.Content);
|
||||||
|
|
||||||
|
// no movies were return
|
||||||
|
if (jsonResponse == null)
|
||||||
|
{
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var movie in jsonResponse.parts)
|
||||||
|
{
|
||||||
|
// Movies with no Year Fix
|
||||||
|
if (string.IsNullOrWhiteSpace(movie.release_date))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
movies.AddIfNotNull(_skyhookProxy.MapMovie(movie));
|
||||||
|
}
|
||||||
|
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Collection
|
||||||
|
{
|
||||||
|
public class TMDbCollectionRequestGenerator : INetImportRequestGenerator
|
||||||
|
{
|
||||||
|
public TMDbCollectionSettings Settings { get; set; }
|
||||||
|
public IHttpClient HttpClient { get; set; }
|
||||||
|
public IHttpRequestBuilderFactory RequestBuilder { get; set; }
|
||||||
|
public Logger Logger { get; set; }
|
||||||
|
|
||||||
|
public TMDbCollectionRequestGenerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual NetImportPageableRequestChain GetMovies()
|
||||||
|
{
|
||||||
|
var pageableRequests = new NetImportPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetMoviesRequest());
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<NetImportRequest> GetMoviesRequest()
|
||||||
|
{
|
||||||
|
Logger.Info($"Importing TMDb movies from collection: {Settings.CollectionId}");
|
||||||
|
|
||||||
|
yield return new NetImportRequest(RequestBuilder.Create()
|
||||||
|
.SetSegment("api", "3")
|
||||||
|
.SetSegment("route", "collection")
|
||||||
|
.SetSegment("id", Settings.CollectionId)
|
||||||
|
.SetSegment("secondaryRoute", "")
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Collection
|
||||||
|
{
|
||||||
|
public class TMDbCollectionSettingsValidator : TMDbSettingsBaseValidator<TMDbCollectionSettings>
|
||||||
|
{
|
||||||
|
public TMDbCollectionSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.CollectionId).Matches(@"^[1-9][0-9]*$", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TMDbCollectionSettings : TMDbSettingsBase<TMDbCollectionSettings>
|
||||||
|
{
|
||||||
|
protected override AbstractValidator<TMDbCollectionSettings> Validator => new TMDbCollectionSettingsValidator();
|
||||||
|
|
||||||
|
public TMDbCollectionSettings()
|
||||||
|
{
|
||||||
|
CollectionId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Collection Id", Type = FieldType.Textbox, HelpText = "TMDb Id of Collection to Follow")]
|
||||||
|
public string CollectionId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
42
src/NzbDrone.Core/NetImport/TMDb/List/TMDbListImport.cs
Normal file
42
src/NzbDrone.Core/NetImport/TMDb/List/TMDbListImport.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cloud;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.List
|
||||||
|
{
|
||||||
|
public class TMDbListImport : TMDbNetImportBase<TMDbListSettings>
|
||||||
|
{
|
||||||
|
public TMDbListImport(IRadarrCloudRequestBuilder requestBuilder,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IParsingService parsingService,
|
||||||
|
ISearchForNewMovie searchForNewMovie,
|
||||||
|
Logger logger)
|
||||||
|
: base(requestBuilder, httpClient, configService, parsingService, searchForNewMovie, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "TMDb List";
|
||||||
|
public override bool Enabled => true;
|
||||||
|
public override bool EnableAuto => false;
|
||||||
|
|
||||||
|
public override IParseNetImportResponse GetParser()
|
||||||
|
{
|
||||||
|
return new TMDbListParser(_skyhookProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override INetImportRequestGenerator GetRequestGenerator()
|
||||||
|
{
|
||||||
|
return new TMDbListRequestGenerator()
|
||||||
|
{
|
||||||
|
RequestBuilder = _requestBuilder,
|
||||||
|
Settings = Settings,
|
||||||
|
Logger = _logger,
|
||||||
|
HttpClient = _httpClient
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/NzbDrone.Core/NetImport/TMDb/List/TMDbListParser.cs
Normal file
51
src/NzbDrone.Core/NetImport/TMDb/List/TMDbListParser.cs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.List
|
||||||
|
{
|
||||||
|
public class TMDbListParser : TMDbParser
|
||||||
|
{
|
||||||
|
private readonly ISearchForNewMovie _skyhookProxy;
|
||||||
|
|
||||||
|
public TMDbListParser(ISearchForNewMovie skyhookProxy)
|
||||||
|
: base(skyhookProxy)
|
||||||
|
{
|
||||||
|
_skyhookProxy = skyhookProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IList<Movie> ParseResponse(NetImportResponse importResponse)
|
||||||
|
{
|
||||||
|
var movies = new List<Movie>();
|
||||||
|
|
||||||
|
if (!PreProcess(importResponse))
|
||||||
|
{
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonResponse = JsonConvert.DeserializeObject<ListResponseRoot>(importResponse.Content);
|
||||||
|
|
||||||
|
// no movies were return
|
||||||
|
if (jsonResponse == null)
|
||||||
|
{
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var movie in jsonResponse.results)
|
||||||
|
{
|
||||||
|
// Movies with no Year Fix
|
||||||
|
if (string.IsNullOrWhiteSpace(movie.release_date))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
movies.AddIfNotNull(_skyhookProxy.MapMovie(movie));
|
||||||
|
}
|
||||||
|
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.List
|
||||||
|
{
|
||||||
|
public class TMDbListRequestGenerator : INetImportRequestGenerator
|
||||||
|
{
|
||||||
|
public TMDbListSettings Settings { get; set; }
|
||||||
|
public IHttpClient HttpClient { get; set; }
|
||||||
|
public IHttpRequestBuilderFactory RequestBuilder { get; set; }
|
||||||
|
public Logger Logger { get; set; }
|
||||||
|
|
||||||
|
public TMDbListRequestGenerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual NetImportPageableRequestChain GetMovies()
|
||||||
|
{
|
||||||
|
var pageableRequests = new NetImportPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetMoviesRequest());
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<NetImportRequest> GetMoviesRequest()
|
||||||
|
{
|
||||||
|
Logger.Info($"Importing TMDb movies from list: {Settings.ListId}");
|
||||||
|
|
||||||
|
var requestBuilder = RequestBuilder.Create()
|
||||||
|
.SetSegment("api", "4")
|
||||||
|
.SetSegment("route", "list")
|
||||||
|
.SetSegment("id", Settings.ListId)
|
||||||
|
.SetSegment("secondaryRoute", "");
|
||||||
|
|
||||||
|
yield return new NetImportRequest(requestBuilder.Accept(HttpAccept.Json)
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/NzbDrone.Core/NetImport/TMDb/List/TMDbListSettings.cs
Normal file
27
src/NzbDrone.Core/NetImport/TMDb/List/TMDbListSettings.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.List
|
||||||
|
{
|
||||||
|
public class TMDbListSettingsValidator : TMDbSettingsBaseValidator<TMDbListSettings>
|
||||||
|
{
|
||||||
|
public TMDbListSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.ListId).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TMDbListSettings : TMDbSettingsBase<TMDbListSettings>
|
||||||
|
{
|
||||||
|
protected override AbstractValidator<TMDbListSettings> Validator => new TMDbListSettingsValidator();
|
||||||
|
|
||||||
|
public TMDbListSettings()
|
||||||
|
{
|
||||||
|
ListId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "ListId", Type = FieldType.Textbox, HelpText = "TMDb Id of List to Follow")]
|
||||||
|
public string ListId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
42
src/NzbDrone.Core/NetImport/TMDb/Person/TMDbPersonImport.cs
Normal file
42
src/NzbDrone.Core/NetImport/TMDb/Person/TMDbPersonImport.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cloud;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Person
|
||||||
|
{
|
||||||
|
public class TMDbPersonImport : TMDbNetImportBase<TMDbPersonSettings>
|
||||||
|
{
|
||||||
|
public TMDbPersonImport(IRadarrCloudRequestBuilder requestBuilder,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IParsingService parsingService,
|
||||||
|
ISearchForNewMovie searchForNewMovie,
|
||||||
|
Logger logger)
|
||||||
|
: base(requestBuilder, httpClient, configService, parsingService, searchForNewMovie, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "TMDb Person";
|
||||||
|
public override bool Enabled => true;
|
||||||
|
public override bool EnableAuto => false;
|
||||||
|
|
||||||
|
public override IParseNetImportResponse GetParser()
|
||||||
|
{
|
||||||
|
return new TMDbPersonParser(Settings, _skyhookProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override INetImportRequestGenerator GetRequestGenerator()
|
||||||
|
{
|
||||||
|
return new TMDbPersonRequestGenerator()
|
||||||
|
{
|
||||||
|
RequestBuilder = _requestBuilder,
|
||||||
|
Settings = Settings,
|
||||||
|
Logger = _logger,
|
||||||
|
HttpClient = _httpClient
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
src/NzbDrone.Core/NetImport/TMDb/Person/TMDbPersonParser.cs
Normal file
102
src/NzbDrone.Core/NetImport/TMDb/Person/TMDbPersonParser.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Person
|
||||||
|
{
|
||||||
|
public class TMDbPersonParser : TMDbParser
|
||||||
|
{
|
||||||
|
private readonly TMDbPersonSettings _settings;
|
||||||
|
private readonly ISearchForNewMovie _skyhookProxy;
|
||||||
|
|
||||||
|
public TMDbPersonParser(TMDbPersonSettings settings, ISearchForNewMovie skyhookProxy)
|
||||||
|
: base(skyhookProxy)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
_skyhookProxy = skyhookProxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IList<Movie> ParseResponse(NetImportResponse importResponse)
|
||||||
|
{
|
||||||
|
var movies = new List<Movie>();
|
||||||
|
|
||||||
|
if (!PreProcess(importResponse))
|
||||||
|
{
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonResponse = JsonConvert.DeserializeObject<PersonCreditsRoot>(importResponse.Content);
|
||||||
|
|
||||||
|
// no movies were return
|
||||||
|
if (jsonResponse == null)
|
||||||
|
{
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
var crewTypes = GetCrewDepartments();
|
||||||
|
|
||||||
|
if (_settings.PersonCast)
|
||||||
|
{
|
||||||
|
foreach (var movie in jsonResponse.cast)
|
||||||
|
{
|
||||||
|
// Movies with no Year Fix
|
||||||
|
if (string.IsNullOrWhiteSpace(movie.release_date))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
movies.AddIfNotNull(_skyhookProxy.MapMovie(movie));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crewTypes.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var movie in jsonResponse.crew)
|
||||||
|
{
|
||||||
|
// Movies with no Year Fix
|
||||||
|
if (string.IsNullOrWhiteSpace(movie.release_date))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crewTypes.Contains(movie.department))
|
||||||
|
{
|
||||||
|
movies.AddIfNotNull(_skyhookProxy.MapMovie(movie));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return movies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<string> GetCrewDepartments()
|
||||||
|
{
|
||||||
|
var creditsDepartment = new List<string>();
|
||||||
|
|
||||||
|
if (_settings.PersonCastDirector)
|
||||||
|
{
|
||||||
|
creditsDepartment.Add("Directing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_settings.PersonCastProducer)
|
||||||
|
{
|
||||||
|
creditsDepartment.Add("Production");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_settings.PersonCastSound)
|
||||||
|
{
|
||||||
|
creditsDepartment.Add("Sound");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_settings.PersonCastWriting)
|
||||||
|
{
|
||||||
|
creditsDepartment.Add("Writing");
|
||||||
|
}
|
||||||
|
|
||||||
|
return creditsDepartment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Person
|
||||||
|
{
|
||||||
|
public class TMDbPersonRequestGenerator : INetImportRequestGenerator
|
||||||
|
{
|
||||||
|
public TMDbPersonSettings Settings { get; set; }
|
||||||
|
public IHttpClient HttpClient { get; set; }
|
||||||
|
public IHttpRequestBuilderFactory RequestBuilder { get; set; }
|
||||||
|
public Logger Logger { get; set; }
|
||||||
|
|
||||||
|
public TMDbPersonRequestGenerator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual NetImportPageableRequestChain GetMovies()
|
||||||
|
{
|
||||||
|
var pageableRequests = new NetImportPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetMoviesRequest());
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<NetImportRequest> GetMoviesRequest()
|
||||||
|
{
|
||||||
|
Logger.Info($"Importing TMDb movies from person: {Settings.PersonId}");
|
||||||
|
|
||||||
|
var requestBuilder = RequestBuilder.Create()
|
||||||
|
.SetSegment("api", "3")
|
||||||
|
.SetSegment("route", "person")
|
||||||
|
.SetSegment("id", Settings.PersonId)
|
||||||
|
.SetSegment("secondaryRoute", "/movie_credits");
|
||||||
|
|
||||||
|
yield return new NetImportRequest(requestBuilder.Accept(HttpAccept.Json)
|
||||||
|
.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Person
|
||||||
|
{
|
||||||
|
public class TMDbPersonSettingsValidator : TMDbSettingsBaseValidator<TMDbPersonSettings>
|
||||||
|
{
|
||||||
|
public TMDbPersonSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.PersonId).Matches(@"^[1-9][0-9]*$", RegexOptions.IgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TMDbPersonSettings : TMDbSettingsBase<TMDbPersonSettings>
|
||||||
|
{
|
||||||
|
protected override AbstractValidator<TMDbPersonSettings> Validator => new TMDbPersonSettingsValidator();
|
||||||
|
|
||||||
|
public TMDbPersonSettings()
|
||||||
|
{
|
||||||
|
PersonId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "PersonId", Type = FieldType.Textbox, HelpText = "TMDb Id of Person to Follow")]
|
||||||
|
public string PersonId { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Person Cast", HelpText = "Select if you want to include Cast credits", Type = FieldType.Checkbox)]
|
||||||
|
public bool PersonCast { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(3, Label = "Person Director Credits", HelpText = "Select if you want to include Director credits", Type = FieldType.Checkbox)]
|
||||||
|
public bool PersonCastDirector { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(4, Label = "Person Producer Credits", HelpText = "Select if you want to include Producer credits", Type = FieldType.Checkbox)]
|
||||||
|
public bool PersonCastProducer { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(5, Label = "Person Sound Credits", HelpText = "Select if you want to include Sound credits", Type = FieldType.Checkbox)]
|
||||||
|
public bool PersonCastSound { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(6, Label = "Person Writing Credits", HelpText = "Select if you want to include Writing credits", Type = FieldType.Checkbox)]
|
||||||
|
public bool PersonCastWriting { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cloud;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Popular
|
||||||
|
{
|
||||||
|
public class TMDbPopularImport : TMDbNetImportBase<TMDbPopularSettings>
|
||||||
|
{
|
||||||
|
public TMDbPopularImport(IRadarrCloudRequestBuilder requestBuilder,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IParsingService parsingService,
|
||||||
|
ISearchForNewMovie searchForNewMovie,
|
||||||
|
Logger logger)
|
||||||
|
: base(requestBuilder, httpClient, configService, parsingService, searchForNewMovie, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "TMDb Popular";
|
||||||
|
public override bool Enabled => true;
|
||||||
|
public override bool EnableAuto => false;
|
||||||
|
|
||||||
|
public override IParseNetImportResponse GetParser()
|
||||||
|
{
|
||||||
|
return new TMDbParser(_skyhookProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override INetImportRequestGenerator GetRequestGenerator()
|
||||||
|
{
|
||||||
|
return new TMDbPopularRequestGenerator()
|
||||||
|
{
|
||||||
|
RequestBuilder = _requestBuilder,
|
||||||
|
Settings = Settings,
|
||||||
|
Logger = _logger,
|
||||||
|
HttpClient = _httpClient
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,9 @@
|
|||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace NzbDrone.Core.NetImport.TMDb
|
namespace NzbDrone.Core.NetImport.TMDb.Popular
|
||||||
{
|
{
|
||||||
public enum TMDbListType
|
public enum TMDbPopularListType
|
||||||
{
|
{
|
||||||
[EnumMember(Value = "List")]
|
|
||||||
List = 0,
|
|
||||||
[EnumMember(Value = "In Theaters")]
|
[EnumMember(Value = "In Theaters")]
|
||||||
Theaters = 1,
|
Theaters = 1,
|
||||||
[EnumMember(Value = "Popular")]
|
[EnumMember(Value = "Popular")]
|
@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Popular
|
||||||
|
{
|
||||||
|
public class TMDbPopularRequestGenerator : INetImportRequestGenerator
|
||||||
|
{
|
||||||
|
public TMDbPopularSettings Settings { get; set; }
|
||||||
|
public IHttpClient HttpClient { get; set; }
|
||||||
|
public IHttpRequestBuilderFactory RequestBuilder { get; set; }
|
||||||
|
public Logger Logger { get; set; }
|
||||||
|
|
||||||
|
public int MaxPages { get; set; }
|
||||||
|
|
||||||
|
public TMDbPopularRequestGenerator()
|
||||||
|
{
|
||||||
|
MaxPages = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual NetImportPageableRequestChain GetMovies()
|
||||||
|
{
|
||||||
|
var pageableRequests = new NetImportPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetMoviesRequests());
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<NetImportRequest> GetMoviesRequests()
|
||||||
|
{
|
||||||
|
var minVoteCount = Settings.FilterCriteria.MinVotes;
|
||||||
|
var minVoteAverage = Settings.FilterCriteria.MinVoteAverage;
|
||||||
|
var ceritification = Settings.FilterCriteria.Ceritification;
|
||||||
|
var includeGenreIds = Settings.FilterCriteria.IncludeGenreIds;
|
||||||
|
var excludeGenreIds = Settings.FilterCriteria.ExcludeGenreIds;
|
||||||
|
var languageCode = (TMDbLanguageCodes)Settings.FilterCriteria.LanguageCode;
|
||||||
|
|
||||||
|
var todaysDate = DateTime.Now.ToString("yyyy-MM-dd");
|
||||||
|
var threeMonthsAgo = DateTime.Parse(todaysDate).AddMonths(-3).ToString("yyyy-MM-dd");
|
||||||
|
var threeMonthsFromNow = DateTime.Parse(todaysDate).AddMonths(3).ToString("yyyy-MM-dd");
|
||||||
|
|
||||||
|
var requestBuilder = RequestBuilder.Create()
|
||||||
|
.SetSegment("api", "3")
|
||||||
|
.SetSegment("route", "discover")
|
||||||
|
.SetSegment("id", "")
|
||||||
|
.SetSegment("secondaryRoute", "movie");
|
||||||
|
|
||||||
|
switch (Settings.ListType)
|
||||||
|
{
|
||||||
|
case (int)TMDbPopularListType.Theaters:
|
||||||
|
requestBuilder.AddQueryParam("primary_release.gte", threeMonthsAgo)
|
||||||
|
.AddQueryParam("primary_release_date.lte", todaysDate);
|
||||||
|
break;
|
||||||
|
case (int)TMDbPopularListType.Popular:
|
||||||
|
requestBuilder.AddQueryParam("sort_by", "popularity.desc");
|
||||||
|
break;
|
||||||
|
case (int)TMDbPopularListType.Top:
|
||||||
|
requestBuilder.AddQueryParam("sort_by", "vote_average.desc");
|
||||||
|
break;
|
||||||
|
case (int)TMDbPopularListType.Upcoming:
|
||||||
|
requestBuilder.AddQueryParam("primary_release.gte", todaysDate)
|
||||||
|
.AddQueryParam("primary_release_date.lte", threeMonthsFromNow);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ceritification.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
requestBuilder.AddQueryParam("certification", ceritification)
|
||||||
|
.AddQueryParam("certification_country", "US");
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBuilder
|
||||||
|
.AddQueryParam("vote_count.gte", minVoteCount)
|
||||||
|
.AddQueryParam("vote_average.gte", minVoteAverage)
|
||||||
|
.AddQueryParam("with_genres", includeGenreIds)
|
||||||
|
.AddQueryParam("without_genres", excludeGenreIds)
|
||||||
|
.AddQueryParam("with_original_language", languageCode)
|
||||||
|
.Accept(HttpAccept.Json);
|
||||||
|
|
||||||
|
for (var pageNumber = 1; pageNumber <= MaxPages; pageNumber++)
|
||||||
|
{
|
||||||
|
Logger.Info($"Importing TMDb movies from: {requestBuilder.BaseUrl}&page={pageNumber}");
|
||||||
|
|
||||||
|
requestBuilder.AddQueryParam("page", pageNumber, true);
|
||||||
|
|
||||||
|
yield return new NetImportRequest(requestBuilder.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.Popular
|
||||||
|
{
|
||||||
|
public class TMDbPopularSettingsValidator : TMDbSettingsBaseValidator<TMDbPopularSettings>
|
||||||
|
{
|
||||||
|
public TMDbPopularSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.ListType).NotEmpty();
|
||||||
|
|
||||||
|
RuleFor(c => c.FilterCriteria).SetValidator(_ => new TMDbFilterSettingsValidator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TMDbPopularSettings : TMDbSettingsBase<TMDbPopularSettings>
|
||||||
|
{
|
||||||
|
protected override AbstractValidator<TMDbPopularSettings> Validator => new TMDbPopularSettingsValidator();
|
||||||
|
|
||||||
|
public TMDbPopularSettings()
|
||||||
|
{
|
||||||
|
ListType = (int)TMDbPopularListType.Popular;
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TMDbPopularListType), HelpText = "Type of list your seeking to import from")]
|
||||||
|
public int ListType { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2)]
|
||||||
|
public TMDbFilterSettings FilterCriteria { get; } = new TMDbFilterSettings();
|
||||||
|
}
|
||||||
|
}
|
73
src/NzbDrone.Core/NetImport/TMDb/TMDbFilterSettings.cs
Normal file
73
src/NzbDrone.Core/NetImport/TMDb/TMDbFilterSettings.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb
|
||||||
|
{
|
||||||
|
public class TMDbFilterSettingsValidator : AbstractValidator<TMDbFilterSettings>
|
||||||
|
{
|
||||||
|
public TMDbFilterSettingsValidator()
|
||||||
|
{
|
||||||
|
// Range 0.0 - 10.0
|
||||||
|
RuleFor(c => c.MinVoteAverage)
|
||||||
|
.Matches(@"^(?!0\d)\d*(\.\d{1})?$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.MinVoteAverage.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Minimum vote average must be between 0 and 10");
|
||||||
|
|
||||||
|
// Greater than 0
|
||||||
|
RuleFor(c => c.MinVotes)
|
||||||
|
.Matches(@"^[1-9][0-9]*$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.MinVotes.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Minimum votes must be greater than 0");
|
||||||
|
|
||||||
|
// Any valid certification
|
||||||
|
RuleFor(c => c.Ceritification)
|
||||||
|
.Matches(@"^\bNR\b|\bG\b|\bPG\b|\bPG\-13\b|\bR\b|\bNC\-17\b$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.Ceritification.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Not a valid certification");
|
||||||
|
|
||||||
|
// CSV of numbers
|
||||||
|
RuleFor(c => c.IncludeGenreIds)
|
||||||
|
.Matches(@"^\d+([,|]\d+)*$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.IncludeGenreIds.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Genre Ids must be comma (,) or pipe (|) separated number ids");
|
||||||
|
|
||||||
|
// CSV of numbers
|
||||||
|
RuleFor(c => c.ExcludeGenreIds)
|
||||||
|
.Matches(@"^\d+([,|]\d+)*$", RegexOptions.IgnoreCase)
|
||||||
|
.When(c => c.ExcludeGenreIds.IsNotNullOrWhiteSpace())
|
||||||
|
.WithMessage("Genre Ids must be comma (,) or pipe (|) separated number ids");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TMDbFilterSettings
|
||||||
|
{
|
||||||
|
public TMDbFilterSettings()
|
||||||
|
{
|
||||||
|
MinVoteAverage = "5";
|
||||||
|
MinVotes = "1";
|
||||||
|
LanguageCode = (int)TMDbLanguageCodes.en;
|
||||||
|
ExcludeGenreIds = "";
|
||||||
|
IncludeGenreIds = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Minimum Vote Average", HelpText = "Filter movies by votes (0.0-10.0)")]
|
||||||
|
public string MinVoteAverage { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Minimum Number of Votes", HelpText = "Filter movies by number of votes")]
|
||||||
|
public string MinVotes { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(3, Label = "Certification", HelpText = "Filter movies by a single ceritification (NR,G,PG,PG-13,R,NC-17)")]
|
||||||
|
public string Ceritification { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(4, Label = "Include Genre Ids", HelpText = "Filter movies by TMDb Genre Ids (Comma Separated)")]
|
||||||
|
public string IncludeGenreIds { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(5, Label = "Exclude Genre Ids", HelpText = "Filter movies by TMDb Genre Ids (Comma Separated)")]
|
||||||
|
public string ExcludeGenreIds { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(6, Label = "Original Language", Type = FieldType.Select, SelectOptions = typeof(TMDbLanguageCodes), HelpText = "Filter by Language")]
|
||||||
|
public int LanguageCode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Http;
|
|
||||||
using NzbDrone.Core.Configuration;
|
|
||||||
using NzbDrone.Core.MetadataSource;
|
|
||||||
using NzbDrone.Core.Parser;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.NetImport.TMDb
|
|
||||||
{
|
|
||||||
public class TMDbImport : HttpNetImportBase<TMDbSettings>
|
|
||||||
{
|
|
||||||
public override string Name => "TMDb Lists";
|
|
||||||
|
|
||||||
public override NetImportType ListType => NetImportType.TMDB;
|
|
||||||
public override bool Enabled => true;
|
|
||||||
public override bool EnableAuto => false;
|
|
||||||
|
|
||||||
private readonly ISearchForNewMovie _skyhookProxy;
|
|
||||||
|
|
||||||
public TMDbImport(IHttpClient httpClient,
|
|
||||||
IConfigService configService,
|
|
||||||
IParsingService parsingService,
|
|
||||||
ISearchForNewMovie skyhookProxy,
|
|
||||||
Logger logger)
|
|
||||||
: base(httpClient, configService, parsingService, logger)
|
|
||||||
{
|
|
||||||
_skyhookProxy = skyhookProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override INetImportRequestGenerator GetRequestGenerator()
|
|
||||||
{
|
|
||||||
return new TMDbRequestGenerator()
|
|
||||||
{
|
|
||||||
Settings = Settings,
|
|
||||||
Logger = _logger,
|
|
||||||
HttpClient = _httpClient
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IParseNetImportResponse GetParser()
|
|
||||||
{
|
|
||||||
return new TMDbParser(Settings, _skyhookProxy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
30
src/NzbDrone.Core/NetImport/TMDb/TMDbImportBase.cs
Normal file
30
src/NzbDrone.Core/NetImport/TMDb/TMDbImportBase.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cloud;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb
|
||||||
|
{
|
||||||
|
public abstract class TMDbNetImportBase<TSettings> : HttpNetImportBase<TSettings>
|
||||||
|
where TSettings : TMDbSettingsBase<TSettings>, new()
|
||||||
|
{
|
||||||
|
public override NetImportType ListType => NetImportType.TMDB;
|
||||||
|
|
||||||
|
public readonly ISearchForNewMovie _skyhookProxy;
|
||||||
|
public readonly IHttpRequestBuilderFactory _requestBuilder;
|
||||||
|
|
||||||
|
protected TMDbNetImportBase(IRadarrCloudRequestBuilder requestBuilder,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IParsingService parsingService,
|
||||||
|
ISearchForNewMovie skyhookProxy,
|
||||||
|
Logger logger)
|
||||||
|
: base(httpClient, configService, parsingService, logger)
|
||||||
|
{
|
||||||
|
_skyhookProxy = skyhookProxy;
|
||||||
|
_requestBuilder = requestBuilder.TMDB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,36 +4,30 @@
|
|||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.MetadataSource;
|
using NzbDrone.Core.MetadataSource;
|
||||||
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
using NzbDrone.Core.NetImport.Exceptions;
|
using NzbDrone.Core.NetImport.Exceptions;
|
||||||
|
|
||||||
namespace NzbDrone.Core.NetImport.TMDb
|
namespace NzbDrone.Core.NetImport.TMDb
|
||||||
{
|
{
|
||||||
public class TMDbParser : IParseNetImportResponse
|
public class TMDbParser : IParseNetImportResponse
|
||||||
{
|
{
|
||||||
private readonly TMDbSettings _settings;
|
|
||||||
private readonly ISearchForNewMovie _skyhookProxy;
|
private readonly ISearchForNewMovie _skyhookProxy;
|
||||||
private NetImportResponse _importResponse;
|
|
||||||
|
|
||||||
public TMDbParser(TMDbSettings settings, ISearchForNewMovie skyhookProxy)
|
public TMDbParser(ISearchForNewMovie skyhookProxy)
|
||||||
{
|
{
|
||||||
_skyhookProxy = skyhookProxy;
|
_skyhookProxy = skyhookProxy;
|
||||||
_settings = settings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Movies.Movie> ParseResponse(NetImportResponse importResponse)
|
public virtual IList<Movie> ParseResponse(NetImportResponse importResponse)
|
||||||
{
|
{
|
||||||
_importResponse = importResponse;
|
var movies = new List<Movie>();
|
||||||
|
|
||||||
var movies = new List<Movies.Movie>();
|
if (!PreProcess(importResponse))
|
||||||
|
|
||||||
if (!PreProcess(_importResponse))
|
|
||||||
{
|
{
|
||||||
return movies;
|
return movies;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_settings.ListType != (int)TMDbListType.List)
|
var jsonResponse = JsonConvert.DeserializeObject<MovieSearchRoot>(importResponse.Content);
|
||||||
{
|
|
||||||
var jsonResponse = JsonConvert.DeserializeObject<MovieSearchRoot>(_importResponse.Content);
|
|
||||||
|
|
||||||
// no movies were return
|
// no movies were return
|
||||||
if (jsonResponse == null)
|
if (jsonResponse == null)
|
||||||
@ -43,48 +37,23 @@ public TMDbParser(TMDbSettings settings, ISearchForNewMovie skyhookProxy)
|
|||||||
|
|
||||||
return jsonResponse.results.SelectList(_skyhookProxy.MapMovie);
|
return jsonResponse.results.SelectList(_skyhookProxy.MapMovie);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
var jsonResponse = JsonConvert.DeserializeObject<ListResponseRoot>(_importResponse.Content);
|
|
||||||
|
|
||||||
// no movies were return
|
protected virtual bool PreProcess(NetImportResponse listResponse)
|
||||||
if (jsonResponse == null)
|
|
||||||
{
|
{
|
||||||
return movies;
|
if (listResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||||
|
{
|
||||||
|
throw new NetImportException(listResponse,
|
||||||
|
"TMDb API call resulted in an unexpected StatusCode [{0}]",
|
||||||
|
listResponse.HttpResponse.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var movie in jsonResponse.items)
|
if (listResponse.HttpResponse.Headers.ContentType != null &&
|
||||||
|
listResponse.HttpResponse.Headers.ContentType.Contains("text/json") &&
|
||||||
|
listResponse.HttpRequest.Headers.Accept != null &&
|
||||||
|
!listResponse.HttpRequest.Headers.Accept.Contains("text/json"))
|
||||||
{
|
{
|
||||||
// Skip non-movie things
|
throw new NetImportException(listResponse,
|
||||||
if (movie.media_type != "movie")
|
"TMDb responded with html content. Site is likely blocked or unavailable.");
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Movies with no Year Fix
|
|
||||||
if (string.IsNullOrWhiteSpace(movie.release_date))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
movies.AddIfNotNull(_skyhookProxy.MapMovie(movie));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return movies;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool PreProcess(NetImportResponse indexerResponse)
|
|
||||||
{
|
|
||||||
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
|
||||||
{
|
|
||||||
throw new NetImportException(indexerResponse, "Indexer API call resulted in an unexpected StatusCode [{0}]", indexerResponse.HttpResponse.StatusCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexerResponse.HttpResponse.Headers.ContentType != null && indexerResponse.HttpResponse.Headers.ContentType.Contains("text/json") &&
|
|
||||||
indexerResponse.HttpRequest.Headers.Accept != null && !indexerResponse.HttpRequest.Headers.Accept.Contains("text/json"))
|
|
||||||
{
|
|
||||||
throw new NetImportException(indexerResponse, "Indexer responded with html content. Site is likely blocked or unavailable.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using NLog;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Common.Http;
|
|
||||||
using NzbDrone.Common.Serializer;
|
|
||||||
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.NetImport.TMDb
|
|
||||||
{
|
|
||||||
public class TMDbRequestGenerator : INetImportRequestGenerator
|
|
||||||
{
|
|
||||||
public TMDbSettings Settings { get; set; }
|
|
||||||
public IHttpClient HttpClient { get; set; }
|
|
||||||
public Logger Logger { get; set; }
|
|
||||||
|
|
||||||
public int MaxPages { get; set; }
|
|
||||||
|
|
||||||
public TMDbRequestGenerator()
|
|
||||||
{
|
|
||||||
MaxPages = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual NetImportPageableRequestChain GetMovies()
|
|
||||||
{
|
|
||||||
var minVoteCount = Settings.MinVotes;
|
|
||||||
var minVoteAverage = Settings.MinVoteAverage;
|
|
||||||
var ceritification = Settings.Ceritification;
|
|
||||||
var includeGenreIds = Settings.IncludeGenreIds;
|
|
||||||
var excludeGenreIds = Settings.ExcludeGenreIds;
|
|
||||||
var languageCode = (TMDbLanguageCodes)Settings.LanguageCode;
|
|
||||||
|
|
||||||
var todaysDate = DateTime.Now.ToString("yyyy-MM-dd");
|
|
||||||
var threeMonthsAgo = DateTime.Parse(todaysDate).AddMonths(-3).ToString("yyyy-MM-dd");
|
|
||||||
var threeMonthsFromNow = DateTime.Parse(todaysDate).AddMonths(3).ToString("yyyy-MM-dd");
|
|
||||||
|
|
||||||
if (ceritification.IsNotNullOrWhiteSpace())
|
|
||||||
{
|
|
||||||
ceritification = $"&certification_country=US&certification={ceritification}";
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmdbParams = "";
|
|
||||||
switch (Settings.ListType)
|
|
||||||
{
|
|
||||||
case (int)TMDbListType.List:
|
|
||||||
tmdbParams = $"/3/list/{Settings.ListId}?api_key=1a7373301961d03f97f853a876dd1212";
|
|
||||||
break;
|
|
||||||
case (int)TMDbListType.Theaters:
|
|
||||||
tmdbParams = $"/3/discover/movie?api_key=1a7373301961d03f97f853a876dd1212&primary_release_date.gte={threeMonthsAgo}&primary_release_date.lte={todaysDate}&vote_count.gte={minVoteCount}&vote_average.gte={minVoteAverage}{ceritification}&with_genres={includeGenreIds}&without_genres={excludeGenreIds}&with_original_language={languageCode}";
|
|
||||||
break;
|
|
||||||
case (int)TMDbListType.Popular:
|
|
||||||
tmdbParams = $"/3/discover/movie?api_key=1a7373301961d03f97f853a876dd1212&sort_by=popularity.desc&vote_count.gte={minVoteCount}&vote_average.gte={minVoteAverage}{ceritification}&with_genres={includeGenreIds}&without_genres={excludeGenreIds}&with_original_language={languageCode}";
|
|
||||||
break;
|
|
||||||
case (int)TMDbListType.Top:
|
|
||||||
tmdbParams = $"/3/discover/movie?api_key=1a7373301961d03f97f853a876dd1212&sort_by=vote_average.desc&vote_count.gte={minVoteCount}&vote_average.gte={minVoteAverage}{ceritification}&with_genres={includeGenreIds}&without_genres={excludeGenreIds}&with_original_language={languageCode}";
|
|
||||||
break;
|
|
||||||
case (int)TMDbListType.Upcoming:
|
|
||||||
tmdbParams = $"/3/discover/movie?api_key=1a7373301961d03f97f853a876dd1212&primary_release_date.gte={todaysDate}&primary_release_date.lte={threeMonthsFromNow}&vote_count.gte={minVoteCount}&vote_average.gte={minVoteAverage}{ceritification}&with_genres={includeGenreIds}&without_genres={excludeGenreIds}&with_original_language={languageCode}";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pageableRequests = new NetImportPageableRequestChain();
|
|
||||||
if (Settings.ListType != (int)TMDbListType.List)
|
|
||||||
{
|
|
||||||
// First query to get the total_Pages
|
|
||||||
var requestBuilder = new HttpRequestBuilder($"{Settings.Link.TrimEnd("/")}")
|
|
||||||
{
|
|
||||||
LogResponseContent = true
|
|
||||||
};
|
|
||||||
|
|
||||||
requestBuilder.Method = HttpMethod.GET;
|
|
||||||
requestBuilder.Resource(tmdbParams);
|
|
||||||
|
|
||||||
var request = requestBuilder
|
|
||||||
|
|
||||||
// .AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
|
|
||||||
.Accept(HttpAccept.Json)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var response = HttpClient.Execute(request);
|
|
||||||
var result = Json.Deserialize<MovieSearchRoot>(response.Content);
|
|
||||||
|
|
||||||
// @TODO Prolly some error handling to do here
|
|
||||||
pageableRequests.Add(GetMovies(tmdbParams, result.total_pages));
|
|
||||||
return pageableRequests;
|
|
||||||
}
|
|
||||||
|
|
||||||
pageableRequests.Add(GetMovies(tmdbParams, 0));
|
|
||||||
return pageableRequests;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<NetImportRequest> GetMovies(string tmdbParams, int totalPages)
|
|
||||||
{
|
|
||||||
var baseUrl = $"{Settings.Link.TrimEnd("/")}{tmdbParams}";
|
|
||||||
if (Settings.ListType != (int)TMDbListType.List)
|
|
||||||
{
|
|
||||||
for (var pageNumber = 1; pageNumber <= totalPages; pageNumber++)
|
|
||||||
{
|
|
||||||
// Limit the amount of pages
|
|
||||||
if (pageNumber >= MaxPages + 1)
|
|
||||||
{
|
|
||||||
Logger.Info(
|
|
||||||
$"Found more than {MaxPages} pages, skipping the {totalPages - (MaxPages + 1)} remaining pages");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Info($"Importing TMDb movies from: {baseUrl}&page={pageNumber}");
|
|
||||||
yield return new NetImportRequest($"{baseUrl}&page={pageNumber}", HttpAccept.Json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Info($"Importing TMDb movies from: {baseUrl}");
|
|
||||||
yield return new NetImportRequest($"{baseUrl}", HttpAccept.Json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +1,28 @@
|
|||||||
using System.Text.RegularExpressions;
|
using FluentValidation;
|
||||||
using FluentValidation;
|
|
||||||
using NzbDrone.Common.Extensions;
|
|
||||||
using NzbDrone.Core.Annotations;
|
|
||||||
using NzbDrone.Core.ThingiProvider;
|
using NzbDrone.Core.ThingiProvider;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.NetImport.TMDb
|
namespace NzbDrone.Core.NetImport.TMDb
|
||||||
{
|
{
|
||||||
public class TMDbSettingsValidator : AbstractValidator<TMDbSettings>
|
public class TMDbSettingsBaseValidator<TSettings> : AbstractValidator<TSettings>
|
||||||
|
where TSettings : TMDbSettingsBase<TSettings>
|
||||||
{
|
{
|
||||||
public TMDbSettingsValidator()
|
public TMDbSettingsBaseValidator()
|
||||||
{
|
{
|
||||||
RuleFor(c => c.Link).ValidRootUrl();
|
|
||||||
|
|
||||||
// Greater than 0
|
|
||||||
RuleFor(c => c.ListId)
|
|
||||||
.Matches(@"^[1-9][0-9]*$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.ListType == (int)TMDbListType.List)
|
|
||||||
.WithMessage("List Id is required when using TMDb Lists");
|
|
||||||
|
|
||||||
// Range 0.0 - 10.0
|
|
||||||
RuleFor(c => c.MinVoteAverage)
|
|
||||||
.Matches(@"^(?!0\d)\d*(\.\d{1})?$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.MinVoteAverage.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Minimum vote average must be between 0 and 10");
|
|
||||||
|
|
||||||
// Greater than 0
|
|
||||||
RuleFor(c => c.MinVotes)
|
|
||||||
.Matches(@"^[1-9][0-9]*$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.MinVotes.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Minimum votes must be greater than 0");
|
|
||||||
|
|
||||||
// Any valid certification
|
|
||||||
RuleFor(c => c.Ceritification)
|
|
||||||
.Matches(@"^\bNR\b|\bG\b|\bPG\b|\bPG\-13\b|\bR\b|\bNC\-17\b$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.Ceritification.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Not a valid certification");
|
|
||||||
|
|
||||||
// CSV of numbers
|
|
||||||
RuleFor(c => c.IncludeGenreIds)
|
|
||||||
.Matches(@"^\d+([,|]\d+)*$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.IncludeGenreIds.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Genre Ids must be comma (,) or pipe (|) separated number ids");
|
|
||||||
|
|
||||||
// CSV of numbers
|
|
||||||
RuleFor(c => c.ExcludeGenreIds)
|
|
||||||
.Matches(@"^\d+([,|]\d+)*$", RegexOptions.IgnoreCase)
|
|
||||||
.When(c => c.ExcludeGenreIds.IsNotNullOrWhiteSpace())
|
|
||||||
.WithMessage("Genre Ids must be comma (,) or pipe (|) separated number ids");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TMDbSettings : IProviderConfig
|
public class TMDbSettingsBase<TSettings> : IProviderConfig
|
||||||
|
where TSettings : TMDbSettingsBase<TSettings>
|
||||||
{
|
{
|
||||||
private static readonly TMDbSettingsValidator Validator = new TMDbSettingsValidator();
|
protected virtual AbstractValidator<TSettings> Validator => new TMDbSettingsBaseValidator<TSettings>();
|
||||||
|
public TMDbSettingsBase()
|
||||||
public TMDbSettings()
|
|
||||||
{
|
{
|
||||||
Link = "https://api.themoviedb.org";
|
|
||||||
ListType = (int)TMDbListType.Popular;
|
|
||||||
MinVoteAverage = "5";
|
|
||||||
MinVotes = "1";
|
|
||||||
LanguageCode = (int)TMDbLanguageCodes.en;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[FieldDefinition(0, Label = "TMDb API URL", HelpText = "Link to to TMDb API URL, do not change unless you know what you are doing.")]
|
|
||||||
public string Link { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TMDbListType), HelpText = "Type of list your seeking to import from")]
|
|
||||||
public int ListType { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(2, Label = "Public List ID", HelpText = "Required for List (Ignores Filtering Options)")]
|
|
||||||
public string ListId { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(3, Label = "Minimum Vote Average", HelpText = "Filter movies by votes (0.0-10.0)")]
|
|
||||||
public string MinVoteAverage { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(4, Label = "Minimum Number of Votes", HelpText = "Filter movies by number of votes")]
|
|
||||||
public string MinVotes { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(5, Label = "Certification", HelpText = "Filter movies by a single ceritification (NR,G,PG,PG-13,R,NC-17)")]
|
|
||||||
public string Ceritification { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(6, Label = "Include Genre Ids", HelpText = "Filter movies by TMDb Genre Ids (Comma Separated)")]
|
|
||||||
public string IncludeGenreIds { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(7, Label = "Exclude Genre Ids", HelpText = "Filter movies by TMDb Genre Ids (Comma Separated)")]
|
|
||||||
public string ExcludeGenreIds { get; set; }
|
|
||||||
|
|
||||||
[FieldDefinition(8, Label = "Original Language", Type = FieldType.Select, SelectOptions = typeof(TMDbLanguageCodes), HelpText = "Filter by Language")]
|
|
||||||
public int LanguageCode { get; set; }
|
|
||||||
|
|
||||||
public NzbDroneValidationResult Validate()
|
public NzbDroneValidationResult Validate()
|
||||||
{
|
{
|
||||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
return new NzbDroneValidationResult(Validator.Validate((TSettings)this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
97
src/NzbDrone.Core/NetImport/TMDb/User/TMDbUserImport.cs
Normal file
97
src/NzbDrone.Core/NetImport/TMDb/User/TMDbUserImport.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cloud;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.MetadataSource;
|
||||||
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
using NzbDrone.Core.Parser;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.User
|
||||||
|
{
|
||||||
|
public class TMDbUserImport : TMDbNetImportBase<TMDbUserSettings>
|
||||||
|
{
|
||||||
|
public TMDbUserImport(IRadarrCloudRequestBuilder requestBuilder,
|
||||||
|
IHttpClient httpClient,
|
||||||
|
IConfigService configService,
|
||||||
|
IParsingService parsingService,
|
||||||
|
ISearchForNewMovie searchForNewMovie,
|
||||||
|
Logger logger)
|
||||||
|
: base(requestBuilder, httpClient, configService, parsingService, searchForNewMovie, logger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Name => "TMDb User";
|
||||||
|
public override bool Enabled => true;
|
||||||
|
public override bool EnableAuto => false;
|
||||||
|
|
||||||
|
public override IParseNetImportResponse GetParser()
|
||||||
|
{
|
||||||
|
return new TMDbParser(_skyhookProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override INetImportRequestGenerator GetRequestGenerator()
|
||||||
|
{
|
||||||
|
return new TMDbUserRequestGenerator()
|
||||||
|
{
|
||||||
|
RequestBuilder = _requestBuilder,
|
||||||
|
Settings = Settings,
|
||||||
|
Logger = _logger,
|
||||||
|
HttpClient = _httpClient
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||||
|
{
|
||||||
|
if (action == "startOAuth")
|
||||||
|
{
|
||||||
|
var requestBuilder = _requestBuilder.Create()
|
||||||
|
.SetSegment("api", "4")
|
||||||
|
.SetSegment("route", "auth")
|
||||||
|
.SetSegment("id", "")
|
||||||
|
.SetSegment("secondaryRoute", "request_token")
|
||||||
|
.AddQueryParam("redirect_to", query["callbackUrl"]);
|
||||||
|
|
||||||
|
requestBuilder.Method = HttpMethod.POST;
|
||||||
|
|
||||||
|
var request = requestBuilder.Build();
|
||||||
|
|
||||||
|
var response = Json.Deserialize<AuthRefreshTokenResponse>(_httpClient.Execute(request).Content);
|
||||||
|
|
||||||
|
var oAuthRequest = new HttpRequestBuilder(Settings.OAuthUrl)
|
||||||
|
.AddQueryParam("request_token", response.request_token)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
OauthUrl = oAuthRequest.Url.ToString(),
|
||||||
|
RequestToken = response.request_token
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (action == "getOAuthToken")
|
||||||
|
{
|
||||||
|
var requestBuilder = _requestBuilder.Create()
|
||||||
|
.SetSegment("api", "4")
|
||||||
|
.SetSegment("route", "auth")
|
||||||
|
.SetSegment("id", "")
|
||||||
|
.SetSegment("secondaryRoute", "access_token")
|
||||||
|
.AddQueryParam("request_token", query["requestToken"]);
|
||||||
|
|
||||||
|
requestBuilder.Method = HttpMethod.POST;
|
||||||
|
|
||||||
|
var request = requestBuilder.Build();
|
||||||
|
|
||||||
|
var response = Json.Deserialize<AuthAccessTokenResponse>(_httpClient.Execute(request).Content);
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
accountId = response.account_id,
|
||||||
|
accessToken = response.access_token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new { };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/NzbDrone.Core/NetImport/TMDb/User/TMDbUserListType.cs
Normal file
16
src/NzbDrone.Core/NetImport/TMDb/User/TMDbUserListType.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.User
|
||||||
|
{
|
||||||
|
public enum TMDbUserListType
|
||||||
|
{
|
||||||
|
[EnumMember(Value = "Watchlist")]
|
||||||
|
Watchlist = 1,
|
||||||
|
[EnumMember(Value = "Recommendations")]
|
||||||
|
Recommendations = 2,
|
||||||
|
[EnumMember(Value = "Rated")]
|
||||||
|
Rated = 3,
|
||||||
|
[EnumMember(Value = "Favorite")]
|
||||||
|
Favorite = 4
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.User
|
||||||
|
{
|
||||||
|
public class TMDbUserRequestGenerator : INetImportRequestGenerator
|
||||||
|
{
|
||||||
|
public TMDbUserSettings Settings { get; set; }
|
||||||
|
public IHttpClient HttpClient { get; set; }
|
||||||
|
public IHttpRequestBuilderFactory RequestBuilder { get; set; }
|
||||||
|
public Logger Logger { get; set; }
|
||||||
|
|
||||||
|
public int MaxPages { get; set; }
|
||||||
|
|
||||||
|
public TMDbUserRequestGenerator()
|
||||||
|
{
|
||||||
|
MaxPages = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual NetImportPageableRequestChain GetMovies()
|
||||||
|
{
|
||||||
|
var pageableRequests = new NetImportPageableRequestChain();
|
||||||
|
|
||||||
|
pageableRequests.Add(GetMoviesRequests());
|
||||||
|
|
||||||
|
return pageableRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<NetImportRequest> GetMoviesRequests()
|
||||||
|
{
|
||||||
|
var requestBuilder = RequestBuilder.Create()
|
||||||
|
.SetHeader("Authorization", $"Bearer {Settings.AccessToken}")
|
||||||
|
.SetSegment("api", "4")
|
||||||
|
.SetSegment("route", "account")
|
||||||
|
.SetSegment("id", Settings.AccountId);
|
||||||
|
|
||||||
|
switch (Settings.ListType)
|
||||||
|
{
|
||||||
|
case (int)TMDbUserListType.Watchlist:
|
||||||
|
requestBuilder.SetSegment("secondaryRoute", "/movie/watchlist");
|
||||||
|
break;
|
||||||
|
case (int)TMDbUserListType.Recommendations:
|
||||||
|
requestBuilder.SetSegment("secondaryRoute", "/movie/recommendations");
|
||||||
|
break;
|
||||||
|
case (int)TMDbUserListType.Rated:
|
||||||
|
requestBuilder.SetSegment("secondaryRoute", "/movie/rated");
|
||||||
|
break;
|
||||||
|
case (int)TMDbUserListType.Favorite:
|
||||||
|
requestBuilder.SetSegment("secondaryRoute", "/movie/favorites");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBuilder.Accept(HttpAccept.Json);
|
||||||
|
|
||||||
|
requestBuilder.Method = HttpMethod.GET;
|
||||||
|
|
||||||
|
yield return new NetImportRequest(requestBuilder.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
src/NzbDrone.Core/NetImport/TMDb/User/TMDbUserSettings.cs
Normal file
40
src/NzbDrone.Core/NetImport/TMDb/User/TMDbUserSettings.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.NetImport.TMDb.User
|
||||||
|
{
|
||||||
|
public class TMDbUserSettingsValidator : TMDbSettingsBaseValidator<TMDbUserSettings>
|
||||||
|
{
|
||||||
|
public TMDbUserSettingsValidator()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.ListType).NotEmpty();
|
||||||
|
RuleFor(c => c.AccessToken).NotEmpty();
|
||||||
|
RuleFor(c => c.AccountId).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TMDbUserSettings : TMDbSettingsBase<TMDbUserSettings>
|
||||||
|
{
|
||||||
|
protected override AbstractValidator<TMDbUserSettings> Validator => new TMDbUserSettingsValidator();
|
||||||
|
|
||||||
|
public TMDbUserSettings()
|
||||||
|
{
|
||||||
|
ListType = (int)TMDbUserListType.Watchlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OAuthUrl => "https://www.themoviedb.org/auth/access";
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Account Id", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string AccountId { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TMDbUserListType), HelpText = "Type of list your seeking to import from")]
|
||||||
|
public int ListType { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(99, Label = "Authenticate with TMDB", Type = FieldType.OAuth)]
|
||||||
|
public string SignIn { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Nancy;
|
using Nancy;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.MediaCover;
|
using NzbDrone.Core.MediaCover;
|
||||||
using NzbDrone.Core.MetadataSource;
|
using NzbDrone.Core.MetadataSource;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
@ -30,7 +31,12 @@ private object Search()
|
|||||||
|
|
||||||
foreach (var movie in results)
|
foreach (var movie in results)
|
||||||
{
|
{
|
||||||
var mapped = _movieSearch.MapMovieToTmdbMovie(movie);
|
var mapped = movie;
|
||||||
|
|
||||||
|
if (movie.TmdbId == 0 || !movie.Images.Any() || movie.Overview.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
mapped = _movieSearch.MapMovieToTmdbMovie(movie);
|
||||||
|
}
|
||||||
|
|
||||||
if (mapped != null)
|
if (mapped != null)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user