New: TMDb List Rework

pull/2/head
Qstick 5 years ago
parent bdc1adb2ed
commit 65287ec4f3

@ -16,17 +16,19 @@ namespace NzbDrone.Common.Cloud
Services = new HttpRequestBuilder("https://radarr.lidarr.audio/v1/")
.CreateFactory();
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}/{id}{secondaryRoute}")
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
TMDB = new HttpRequestBuilder("https://api.themoviedb.org/{api}/{route}/{id}{secondaryRoute}")
.SetHeader("Authorization", $"Bearer {AuthToken}")
.CreateFactory();
TMDBSingle = new HttpRequestBuilder("https://api.themoviedb.org/3/{route}")
.AddQueryParam("api_key", "1a7373301961d03f97f853a876dd1212")
.SetHeader("Authorization", $"Bearer {AuthToken}")
.CreateFactory();
}
public IHttpRequestBuilderFactory Services { get; private set; }
public IHttpRequestBuilderFactory TMDB { get; private set; }
public IHttpRequestBuilderFactory TMDBSingle { get; private set; }
public string AuthToken => "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxYTczNzMzMDE5NjFkMDNmOTdmODUzYTg3NmRkMTIxMiIsInN1YiI6IjU4NjRmNTkyYzNhMzY4MGFiNjAxNzUzNCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.gh1BwogCCKOda6xj9FRMgAAj_RYKMMPC3oNlcBtlmwk";
}
}

@ -15,6 +15,17 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
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 string poster_path { get; set; }
@ -37,6 +48,13 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
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 bool adult { get; set; }
@ -181,17 +199,31 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
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 Item[] items { get; set; }
public int item_count { get; set; }
public Item[] results { get; set; }
public int total_results { get; set; }
public string iso_639_1 { get; set; }
public string name { 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 string media_type { get; set; }

@ -63,6 +63,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
var startDate = startTime.ToString("o");
var request = _movieBuilder.Create()
.SetSegment("api", "3")
.SetSegment("route", "movie")
.SetSegment("id", "")
.SetSegment("secondaryRoute", "changes")
@ -82,6 +83,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
var langCode = profile != null ? IsoLanguages.Get(profile.Language)?.TwoLetterCode ?? "en" : "en";
var request = _movieBuilder.Create()
.SetSegment("api", "3")
.SetSegment("route", "movie")
.SetSegment("id", tmdbId.ToString())
.SetSegment("secondaryRoute", "")
@ -329,6 +331,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
public Movie GetMovieInfo(string imdbId)
{
var request = _movieBuilder.Create()
.SetSegment("api", "3")
.SetSegment("route", "find")
.SetSegment("id", imdbId)
.SetSegment("secondaryRoute", "")
@ -504,6 +507,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
var firstChar = searchTerm.First();
var request = _movieBuilder.Create()
.SetSegment("api", "3")
.SetSegment("route", "search")
.SetSegment("id", "movie")
.SetSegment("secondaryRoute", "")

@ -51,7 +51,7 @@ namespace NzbDrone.Core.NetImport
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);
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; }
}
}

@ -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
};
}
}
}

@ -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());
}
}
}

@ -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; }
}
}

@ -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
};
}
}
}

@ -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;
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")]
Theaters = 1,
[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();
}
}

@ -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);
}
}
}

@ -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 Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
using NzbDrone.Core.Movies;
using NzbDrone.Core.NetImport.Exceptions;
namespace NzbDrone.Core.NetImport.TMDb
{
public class TMDbParser : IParseNetImportResponse
{
private readonly TMDbSettings _settings;
private readonly ISearchForNewMovie _skyhookProxy;
private NetImportResponse _importResponse;
public TMDbParser(TMDbSettings settings, ISearchForNewMovie skyhookProxy)
public TMDbParser(ISearchForNewMovie 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;
}
if (_settings.ListType != (int)TMDbListType.List)
{
var jsonResponse = JsonConvert.DeserializeObject<MovieSearchRoot>(_importResponse.Content);
var jsonResponse = JsonConvert.DeserializeObject<MovieSearchRoot>(importResponse.Content);
// no movies were return
if (jsonResponse == null)
@ -43,48 +37,23 @@ namespace NzbDrone.Core.NetImport.TMDb
return jsonResponse.results.SelectList(_skyhookProxy.MapMovie);
}
else
{
var jsonResponse = JsonConvert.DeserializeObject<ListResponseRoot>(_importResponse.Content);
// no movies were return
if (jsonResponse == null)
{
return movies;
}
foreach (var movie in jsonResponse.items)
{
// Skip non-movie things
if (movie.media_type != "movie")
{
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)
protected virtual bool PreProcess(NetImportResponse listResponse)
{
if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
if (listResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
{
throw new NetImportException(indexerResponse, "Indexer API call resulted in an unexpected StatusCode [{0}]", indexerResponse.HttpResponse.StatusCode);
throw new NetImportException(listResponse,
"TMDb API call resulted in an unexpected StatusCode [{0}]",
listResponse.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"))
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"))
{
throw new NetImportException(indexerResponse, "Indexer responded with html content. Site is likely blocked or unavailable.");
throw new NetImportException(listResponse,
"TMDb responded with html content. Site is likely blocked or unavailable.");
}
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 NzbDrone.Common.Extensions;
using NzbDrone.Core.Annotations;
using FluentValidation;
using NzbDrone.Core.ThingiProvider;
using NzbDrone.Core.Validation;
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();
public TMDbSettings()
protected virtual AbstractValidator<TSettings> Validator => new TMDbSettingsBaseValidator<TSettings>();
public TMDbSettingsBase()
{
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()
{
return new NzbDroneValidationResult(Validator.Validate(this));
return new NzbDroneValidationResult(Validator.Validate((TSettings)this));
}
}
}

@ -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 { };
}
}
}

@ -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());
}
}
}

@ -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.Linq;
using Nancy;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MetadataSource;
using NzbDrone.Core.Movies;
@ -30,7 +31,12 @@ namespace Radarr.Api.V3.Movies
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)
{

Loading…
Cancel
Save