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

File diff suppressed because it is too large Load Diff

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,76 +18,67 @@ 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;
{ try {
var usehttpclient = false; Type monotype = Type.GetType ("Mono.Runtime");
try { if (monotype != null) {
Type monotype = Type.GetType("Mono.Runtime"); MethodInfo displayName = monotype.GetMethod ("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
if (monotype != null) if (displayName != null) {
{ var monoVersion = displayName.Invoke (null, null).ToString ();
MethodInfo displayName = monotype.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); var monoVersionO = new Version (monoVersion.Split (' ') [0]);
if (displayName != null) if ((monoVersionO.Major >= 4 && monoVersionO.Minor >= 8) || monoVersionO.Major >= 5) {
{ // check if btls is supported
var monoVersion = displayName.Invoke(null, null).ToString(); var monoSecurity = Assembly.Load ("Mono.Security");
var monoVersionO = new Version(monoVersion.Split(' ')[0]); Type monoTlsProviderFactory = monoSecurity.GetType ("Mono.Security.Interface.MonoTlsProviderFactory");
if ((monoVersionO.Major >= 4 && monoVersionO.Minor >= 8) || monoVersionO.Major >= 5) if (monoTlsProviderFactory != null) {
{ MethodInfo isProviderSupported = monoTlsProviderFactory.GetMethod ("IsProviderSupported");
// check if btls is supported if (isProviderSupported != null) {
var monoSecurity = Assembly.Load("Mono.Security"); var btlsSupported = (bool)isProviderSupported.Invoke (null, new string [] { "btls" });
Type monoTlsProviderFactory = monoSecurity.GetType("Mono.Security.Interface.MonoTlsProviderFactory"); if (btlsSupported) {
if (monoTlsProviderFactory != null) // initialize btls
{ MethodInfo initialize = monoTlsProviderFactory.GetMethod ("Initialize", new [] { typeof (string) });
MethodInfo isProviderSupported = monoTlsProviderFactory.GetMethod("IsProviderSupported"); if (initialize != null) {
if(isProviderSupported != null) initialize.Invoke (null, new string [] { "btls" });
{ usehttpclient = true;
var btlsSupported = (bool)isProviderSupported.Invoke(null, new string[] { "btls" });
if (btlsSupported)
{
// initialize btls
MethodInfo initialize = monoTlsProviderFactory.GetMethod("Initialize", new[] { typeof(string) });
if (initialize != null)
{
initialize.Invoke(null, new string[] { "btls" });
usehttpclient = true;
}
} }
} }
} }
@@ -95,56 +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
builder.RegisterType<UnixLibCurlWebClient>().As<IWebClient>();
}
else else
{ builder.RegisterType<UnixLibCurlWebClient> ().As<IWebClient> ();
builder.RegisterType<HttpWebClient>().As<IWebClient>(); } else {
} 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);