Feature/omdb api integration (#1509)

* Line endings...

* Refactoring how MetaIndexers handle fallbacks

Originally this modification was part of a much larger refactoring,
however for the sake of reviewability I split it into smaller chunks.
Sadly it is still quite large.
I wanted to split it even more, however after a certain point there was
really no value in creating smaller chunks. The biggest part of this
modification would be still huge.

So all in all, there're 3 aspects of this modification
- It modifies BaseIndexer so that it now implements IIndexer (will be
very useful later on)
- Resolving most of the warnings currently in Jackett (the only ones
remaining are related to Autofac, however if I could I would just burn
Autofac altogether rather than fix the warnings. Will open discussion on
this.)
- Biggest part: refactoring how MetaIndexers handle fallbacks and how
they provide the final result set

MetaIndexers now accept any kind of fallback and filtering mechanism
that implements the necessary interface, so that in the future IMDB
fallback and filtering won't be the only one. I know there are not a lot
of unit tests around Jackett at the moment, however this renders the
class much more unittestable as well.

* Integrate OMDB API for the fallback option

* Safeguarding when no API key is specified

* Autofac started complaining... I don't understand...

* How did that not make the previous commit?
This commit is contained in:
chibidev
2017-06-29 07:53:25 +02:00
committed by kaso17
parent 345602926e
commit 75e7ce81c2
7 changed files with 1208 additions and 1218 deletions

View File

@@ -62,6 +62,7 @@ function loadJackettSettings() {
$("#jackett-allowupdate").attr('checked', data.config.updatedisabled); $("#jackett-allowupdate").attr('checked', data.config.updatedisabled);
$("#jackett-prerelease").attr('checked', data.config.prerelease); $("#jackett-prerelease").attr('checked', data.config.prerelease);
$("#jackett-logging").attr('checked', data.config.logging); $("#jackett-logging").attr('checked', data.config.logging);
$("#jackett-omdbkey").val(data.config.omdbkey);
var password = data.config.password; var password = data.config.password;
$("#jackett-adminpwd").val(password); $("#jackett-adminpwd").val(password);
if (password != null && password != '') { if (password != null && password != '') {
@@ -1035,6 +1036,7 @@ function bindUIButtons() {
var jackett_update = $("#jackett-allowupdate").is(':checked'); var jackett_update = $("#jackett-allowupdate").is(':checked');
var jackett_prerelease = $("#jackett-prerelease").is(':checked'); var jackett_prerelease = $("#jackett-prerelease").is(':checked');
var jackett_logging = $("#jackett-logging").is(':checked'); var jackett_logging = $("#jackett-logging").is(':checked');
var jackett_omdb_key = $("#jackett-omdbkey").val();
var jsonObject = { var jsonObject = {
port: jackett_port, port: jackett_port,
external: jackett_external, external: jackett_external,
@@ -1042,7 +1044,8 @@ function bindUIButtons() {
prerelease: jackett_prerelease, prerelease: jackett_prerelease,
blackholedir: $("#jackett-savedir").val(), blackholedir: $("#jackett-savedir").val(),
logging: jackett_logging, logging: jackett_logging,
basepathoverride: jackett_basepathoverride basepathoverride: jackett_basepathoverride,
omdbkey: jackett_omdb_key
}; };
var jqxhr = $.post("set_config", JSON.stringify(jsonObject), function (data) { var jqxhr = $.post("set_config", JSON.stringify(jsonObject), function (data) {
if (data.result == "error") { if (data.result == "error") {

View File

@@ -140,6 +140,10 @@
<span class="input-header">Enhanced logging: </span> <span class="input-header">Enhanced logging: </span>
<input id="jackett-logging" class="form-control input-right" type="checkbox" /> <input id="jackett-logging" class="form-control input-right" type="checkbox" />
</div> </div>
<div class="input-area">
<span class="input-header">OMDB API key: </span>
<input id="jackett-omdbkey" class="form-control input-right" type="text" value="" placeholder="">
</div>
<hr /> <hr />
<div id="footer"> <div id="footer">
Jackett Version <span id="app-version"></span> Jackett Version <span id="app-version"></span>

View File

@@ -332,7 +332,7 @@ namespace Jackett.Controllers
cfg["password"] = string.IsNullOrEmpty(serverService.Config.AdminPassword) ? string.Empty : serverService.Config.AdminPassword.Substring(0, 10); cfg["password"] = string.IsNullOrEmpty(serverService.Config.AdminPassword) ? string.Empty : serverService.Config.AdminPassword.Substring(0, 10);
cfg["logging"] = Startup.TracingEnabled; cfg["logging"] = Startup.TracingEnabled;
cfg["basepathoverride"] = serverService.Config.BasePathOverride; cfg["basepathoverride"] = serverService.Config.BasePathOverride;
cfg["omdbkey"] = serverService.Config.OmdbApiKey;
jsonReply["config"] = cfg; jsonReply["config"] = cfg;
jsonReply["app_version"] = config.GetVersion(); jsonReply["app_version"] = config.GetVersion();
@@ -364,10 +364,12 @@ namespace Jackett.Controllers
bool preRelease = (bool)postData["prerelease"]; bool preRelease = (bool)postData["prerelease"];
bool logging = (bool)postData["logging"]; bool logging = (bool)postData["logging"];
string basePathOverride = (string)postData["basepathoverride"]; string basePathOverride = (string)postData["basepathoverride"];
string omdbApiKey = (string)postData["omdbkey"];
Engine.Server.Config.UpdateDisabled = updateDisabled; Engine.Server.Config.UpdateDisabled = updateDisabled;
Engine.Server.Config.UpdatePrerelease = preRelease; Engine.Server.Config.UpdatePrerelease = preRelease;
Engine.Server.Config.BasePathOverride = basePathOverride; Engine.Server.Config.BasePathOverride = basePathOverride;
Engine.Server.Config.OmdbApiKey = omdbApiKey;
Startup.BasePath = Engine.Server.BasePath(); Startup.BasePath = Engine.Server.BasePath();
Engine.Server.SaveConfig(); Engine.Server.SaveConfig();

View File

@@ -18,74 +18,66 @@ namespace Jackett
{ {
public class JackettModule : Autofac.Module public class JackettModule : Autofac.Module
{ {
protected override void Load(ContainerBuilder builder) protected override void Load (ContainerBuilder builder)
{ {
// Just register everything! // Just register everything!
var thisAssembly = typeof(JackettModule).Assembly; var thisAssembly = typeof (JackettModule).Assembly;
builder.RegisterAssemblyTypes(thisAssembly) builder.RegisterAssemblyTypes (thisAssembly)
.Except<IIndexer>() .Except<IIndexer> ()
.Except<IImdbResolver>() .Except<IImdbResolver> ()
.Except<IFallbackStrategyProvider>() .Except<OmdbResolver> ()
.Except<ImdbFallbackStrategyProvider>() .Except<IFallbackStrategyProvider> ()
.Except<IFallbackStrategy>() .Except<ImdbFallbackStrategyProvider> ()
.Except<ImdbFallbackStrategy>() .Except<IFallbackStrategy> ()
.Except<IResultFilterProvider>() .Except<ImdbFallbackStrategy> ()
.Except<ImdbTitleResultFilterProvider>() .Except<IResultFilterProvider> ()
.Except<IResultFilter>() .Except<ImdbTitleResultFilterProvider> ()
.Except<ImdbTitleResultFilterProvider>() .Except<IResultFilter> ()
.Except<BaseMetaIndexer>() .Except<ImdbTitleResultFilterProvider> ()
.Except<AggregateIndexer>() .Except<BaseMetaIndexer> ()
.AsImplementedInterfaces().SingleInstance(); .Except<AggregateIndexer> ()
builder.RegisterApiControllers(thisAssembly).InstancePerRequest(); .AsImplementedInterfaces ().SingleInstance ();
builder.RegisterType<HttpWebClient>(); builder.RegisterApiControllers (thisAssembly).InstancePerRequest ();
builder.RegisterType<HttpWebClient> ();
// Register the best web client for the platform or the override // Register the best web client for the platform or the override
switch (Startup.ClientOverride) switch (Startup.ClientOverride) {
{
case "httpclient": case "httpclient":
builder.RegisterType<HttpWebClient>().As<IWebClient>(); builder.RegisterType<HttpWebClient> ().As<IWebClient> ();
break; break;
case "httpclient2": case "httpclient2":
builder.RegisterType<HttpWebClient2>().As<IWebClient>(); builder.RegisterType<HttpWebClient2> ().As<IWebClient> ();
break; break;
case "safecurl": case "safecurl":
builder.RegisterType<UnixSafeCurlWebClient>().As<IWebClient>(); builder.RegisterType<UnixSafeCurlWebClient> ().As<IWebClient> ();
break; break;
case "libcurl": case "libcurl":
builder.RegisterType<UnixLibCurlWebClient>().As<IWebClient>(); builder.RegisterType<UnixLibCurlWebClient> ().As<IWebClient> ();
break; break;
case "automatic": case "automatic":
default: default:
if (System.Environment.OSVersion.Platform == PlatformID.Unix) if (System.Environment.OSVersion.Platform == PlatformID.Unix) {
{
var usehttpclient = false; var usehttpclient = false;
try { try {
Type monotype = Type.GetType("Mono.Runtime"); Type monotype = Type.GetType ("Mono.Runtime");
if (monotype != null) if (monotype != null) {
{ MethodInfo displayName = monotype.GetMethod ("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo displayName = monotype.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); if (displayName != null) {
if (displayName != null) var monoVersion = displayName.Invoke (null, null).ToString ();
{ var monoVersionO = new Version (monoVersion.Split (' ') [0]);
var monoVersion = displayName.Invoke(null, null).ToString(); if ((monoVersionO.Major >= 4 && monoVersionO.Minor >= 8) || monoVersionO.Major >= 5) {
var monoVersionO = new Version(monoVersion.Split(' ')[0]);
if ((monoVersionO.Major >= 4 && monoVersionO.Minor >= 8) || monoVersionO.Major >= 5)
{
// check if btls is supported // check if btls is supported
var monoSecurity = Assembly.Load("Mono.Security"); var monoSecurity = Assembly.Load ("Mono.Security");
Type monoTlsProviderFactory = monoSecurity.GetType("Mono.Security.Interface.MonoTlsProviderFactory"); Type monoTlsProviderFactory = monoSecurity.GetType ("Mono.Security.Interface.MonoTlsProviderFactory");
if (monoTlsProviderFactory != null) if (monoTlsProviderFactory != null) {
{ MethodInfo isProviderSupported = monoTlsProviderFactory.GetMethod ("IsProviderSupported");
MethodInfo isProviderSupported = monoTlsProviderFactory.GetMethod("IsProviderSupported"); if (isProviderSupported != null) {
if(isProviderSupported != null) var btlsSupported = (bool)isProviderSupported.Invoke (null, new string [] { "btls" });
{ if (btlsSupported) {
var btlsSupported = (bool)isProviderSupported.Invoke(null, new string[] { "btls" });
if (btlsSupported)
{
// initialize btls // initialize btls
MethodInfo initialize = monoTlsProviderFactory.GetMethod("Initialize", new[] { typeof(string) }); MethodInfo initialize = monoTlsProviderFactory.GetMethod ("Initialize", new [] { typeof (string) });
if (initialize != null) if (initialize != null) {
{ initialize.Invoke (null, new string [] { "btls" });
initialize.Invoke(null, new string[] { "btls" });
usehttpclient = true; usehttpclient = true;
} }
} }
@@ -94,57 +86,45 @@ namespace Jackett
} }
} }
} }
} } catch (Exception e) {
catch (Exception e) Console.Out.WriteLine ("Error while deciding which HttpWebClient to use: " + e);
{
Console.Out.WriteLine("Error while deciding which HttpWebClient to use: " + e);
} }
if (usehttpclient) if (usehttpclient)
builder.RegisterType<HttpWebClient>().As<IWebClient>(); builder.RegisterType<HttpWebClient> ().As<IWebClient> ();
else else
builder.RegisterType<UnixLibCurlWebClient>().As<IWebClient>(); builder.RegisterType<UnixLibCurlWebClient> ().As<IWebClient> ();
} } else {
else builder.RegisterType<HttpWebClient> ().As<IWebClient> ();
{
builder.RegisterType<HttpWebClient>().As<IWebClient>();
} }
break; break;
} }
// Register indexers // Register indexers
var indexerTypes = thisAssembly.GetTypes().Where(p => typeof (IIndexer).IsAssignableFrom (p) && !p.IsInterface && !p.IsInNamespace("Jackett.Indexers.Meta")); var indexerTypes = thisAssembly.GetTypes ().Where (p => typeof (IIndexer).IsAssignableFrom (p) && !p.IsInterface && !p.IsInNamespace ("Jackett.Indexers.Meta"));
foreach (var indexer in indexerTypes) foreach (var indexer in indexerTypes) {
{ builder.RegisterType (indexer).Named<IIndexer> (BaseIndexer.GetIndexerID (indexer));
builder.RegisterType(indexer).Named<IIndexer>(BaseIndexer.GetIndexerID(indexer));
} }
Mapper.CreateMap<WebClientByteResult, WebClientStringResult>().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((be, str) => Mapper.CreateMap<WebClientByteResult, WebClientStringResult> ().ForMember (x => x.Content, opt => opt.Ignore ()).AfterMap ((be, str) => {
{ str.Content = Encoding.UTF8.GetString (be.Content);
str.Content = Encoding.UTF8.GetString(be.Content);
}); });
Mapper.CreateMap<WebClientStringResult, WebClientByteResult>().ForMember(x => x.Content, opt => opt.Ignore()).AfterMap((str, be) => Mapper.CreateMap<WebClientStringResult, WebClientByteResult> ().ForMember (x => x.Content, opt => opt.Ignore ()).AfterMap ((str, be) => {
{ if (!string.IsNullOrEmpty (str.Content)) {
if (!string.IsNullOrEmpty(str.Content)) be.Content = Encoding.UTF8.GetBytes (str.Content);
{
be.Content = Encoding.UTF8.GetBytes(str.Content);
} }
}); });
Mapper.CreateMap<WebClientStringResult, WebClientStringResult>(); Mapper.CreateMap<WebClientStringResult, WebClientStringResult> ();
Mapper.CreateMap<WebClientByteResult, WebClientByteResult>(); Mapper.CreateMap<WebClientByteResult, WebClientByteResult> ();
Mapper.CreateMap<ReleaseInfo, ReleaseInfo>(); Mapper.CreateMap<ReleaseInfo, ReleaseInfo> ();
Mapper.CreateMap<ReleaseInfo, TrackerCacheResult>().AfterMap((r, t) => Mapper.CreateMap<ReleaseInfo, TrackerCacheResult> ().AfterMap ((r, t) => {
{ if (r.Category != null) {
if (r.Category != null) var CategoryDesc = string.Join (", ", r.Category.Select (x => TorznabCatType.GetCatDesc (x)).Where (x => !string.IsNullOrEmpty (x)));
{
var CategoryDesc = string.Join(", ", r.Category.Select(x => TorznabCatType.GetCatDesc(x)).Where(x => !string.IsNullOrEmpty(x)));
t.CategoryDesc = CategoryDesc; t.CategoryDesc = CategoryDesc;
} } else {
else
{
t.CategoryDesc = ""; t.CategoryDesc = "";
} }
}); });

View File

@@ -24,6 +24,7 @@ namespace Jackett.Models.Config
public bool UpdateDisabled { get; set; } public bool UpdateDisabled { get; set; }
public bool UpdatePrerelease { get; set; } public bool UpdatePrerelease { get; set; }
public string BasePathOverride { get; set; } public string BasePathOverride { get; set; }
public string OmdbApiKey { get; set; }
public string[] GetListenAddresses(bool? external = null) public string[] GetListenAddresses(bool? external = null)
{ {

View File

@@ -4,6 +4,8 @@ using System.Threading.Tasks;
using System.Web; using System.Web;
using CsQuery; using CsQuery;
using Jackett.Utils.Clients; using Jackett.Utils.Clients;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Jackett.Services namespace Jackett.Services
{ {
@@ -12,36 +14,34 @@ namespace Jackett.Services
Task<IEnumerable<string>> GetAllTitles(string imdbId); Task<IEnumerable<string>> GetAllTitles(string imdbId);
} }
public class ImdbResolver : IImdbResolver public struct Movie
{ {
public ImdbResolver(IWebClient webClient) public string Title;
}
public class OmdbResolver : IImdbResolver
{
public OmdbResolver(IWebClient webClient, string omdbApiKey)
{ {
WebClient = webClient; WebClient = webClient;
apiKey = omdbApiKey;
} }
public async Task<IEnumerable<string>> GetAllTitles(string imdbId) public async Task<IEnumerable<string>> GetAllTitles(string imdbId)
{ {
if (apiKey == null)
return new string[] { };
if (!imdbId.StartsWith("tt", StringComparison.Ordinal)) if (!imdbId.StartsWith("tt", StringComparison.Ordinal))
imdbId = "tt" + imdbId; imdbId = "tt" + imdbId;
var request = new WebRequest("http://www.imdb.com/title/" + imdbId + "/releaseinfo"); var request = new WebRequest("http://omdbapi.com/?apikey=" + apiKey + "&i=" + imdbId);
var result = await WebClient.GetString(request); var result = await WebClient.GetString(request);
var movie = JsonConvert.DeserializeObject<Movie>(result.Content);
CQ dom = result.Content; return new string[] { movie.Title };
var mainTitle = dom["h3[itemprop=name]"].Find("a")[0].InnerHTML.Replace("\"", "");
var akas = dom["table#akas"].Find("tbody").Find("tr");
var titleList = new List<string>();
titleList.Add(mainTitle);
foreach (var row in akas) {
string title = row.FirstElementChild.InnerHTML;
if (title == "(original title)" || title == "")
titleList.Add(HttpUtility.HtmlDecode(row.FirstElementChild.NextElementSibling.InnerHTML));
}
return titleList;
} }
private IWebClient WebClient; private IWebClient WebClient;
private string apiKey;
} }
} }

View File

@@ -131,7 +131,7 @@ namespace Jackett.Services
public void InitAggregateIndexer() public void InitAggregateIndexer()
{ {
var imdbResolver = new ImdbResolver(container.Resolve<IWebClient>()); var imdbResolver = new OmdbResolver(container.Resolve<IWebClient>(), container.Resolve<IServerService>().Config.OmdbApiKey);
var imdbFallbackStrategyProvider = new ImdbFallbackStrategyProvider(imdbResolver); var imdbFallbackStrategyProvider = new ImdbFallbackStrategyProvider(imdbResolver);
var imdbTitleResultFilterProvider = new ImdbTitleResultFilterProvider(imdbResolver); var imdbTitleResultFilterProvider = new ImdbTitleResultFilterProvider(imdbResolver);