diff --git a/Build.bat b/Build.bat new file mode 100644 index 000000000..8ee0b9dc0 --- /dev/null +++ b/Build.bat @@ -0,0 +1,15 @@ + +rmdir /s /q build +cd src +Msbuild Jackett.sln /t:Clean,Build /p:Configuration=Release +cd .. + +xcopy src\Jackett.Console\bin\Release Build\ /e /y +copy /Y src\Jackett.Service\bin\Release\JackettService.exe build\JackettService.exe +copy /Y src\Jackett.Service\bin\Release\JackettService.exe.config build\JackettService.exe.config +copy /Y src\Jackett.Tray\bin\Release\JackettTray.exe build\JackettTray.exe +copy /Y src\Jackett.Tray\bin\Release\JackettTray.exe.config build\JackettTray.exe.config +cd build +del *.pdb +del *.xml +cd .. diff --git a/Installer.iss b/Installer.iss new file mode 100644 index 000000000..9c6361bc4 --- /dev/null +++ b/Installer.iss @@ -0,0 +1,58 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +#define MyAppName "Jackett" +#define MyAppVersion "0.5" +#define MyAppPublisher "Jackett Inc." +#define MyAppURL "https://github.com/zone117x/Jackett" +#define MyAppExeName "JackettTray.exe" + +[Setup] +; NOTE: The value of AppId uniquely identifies this application. +; Do not use the same AppId value in installers for other applications. +; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) +AppId={{C2A9FC00-AA48-4F17-9A72-62FBCEE2785B} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +;AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={pf}\{#MyAppName} +DefaultGroupName={#MyAppName} +DisableProgramGroupPage=yes +OutputBaseFilename=setup +SetupIconFile=O:\Documents\JackettKayo\src\Jackett.Console\jackett.ico +Compression=lzma +SolidCompression=yes + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "windowsService"; Description: "Install as a Windows Service" +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[Files] +Source: "O:\Documents\JackettKayo\Build\JackettTray.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: "O:\Documents\JackettKayo\Build\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" +Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}" +Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent + +[Run] +Filename: "{app}\JackettConsole.exe"; Parameters: "/u"; Flags: waituntilterminated; +Filename: "{app}\JackettConsole.exe"; Parameters: "/r"; Flags: waituntilterminated; +Filename: "{app}\JackettConsole.exe"; Parameters: "/i"; Flags: waituntilterminated; Tasks: windowsService + +[UninstallRun] +Filename: "{app}\JackettConsole.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist + + diff --git a/README.md b/README.md index 0d87fcb34..e3f76e552 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Download in the [Releases page](https://github.com/zone117x/Jackett/releases) * [BeyondHD](https://beyondhd.me/) * [T411](http://www.t411.io/) * [FrenchTorrentDb](http://www.frenchtorrentdb.com/) + * [bB](http://reddit.com/r/baconbits) ### Additional Trackers diff --git a/src/Jackett.Console/App.config b/src/Jackett.Console/App.config new file mode 100644 index 000000000..da396d090 --- /dev/null +++ b/src/Jackett.Console/App.config @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Jackett.Console/Jackett.Console.csproj b/src/Jackett.Console/Jackett.Console.csproj new file mode 100644 index 000000000..bb4bd0e92 --- /dev/null +++ b/src/Jackett.Console/Jackett.Console.csproj @@ -0,0 +1,155 @@ + + + + + Debug + AnyCPU + {4E2A81DA-E235-4A88-AD20-38AABBFBF33C} + Exe + Properties + Jackett.Console + JackettConsole + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + jackett.ico + + + JackettConsole.Program + + + + False + ..\packages\Autofac.3.5.0\lib\net40\Autofac.dll + + + ..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll + + + False + ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll + + + ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll + + + ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll + + + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + True + + + ..\packages\Microsoft.Owin.FileSystems.3.0.1\lib\net45\Microsoft.Owin.FileSystems.dll + True + + + ..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll + True + + + ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + + + ..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll + True + + + ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll + + + ..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll + + + ..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll + True + + + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + True + + + False + ..\packages\NLog.4.0.1\lib\net45\NLog.dll + + + ..\packages\Owin.1.0\lib\net40\Owin.dll + True + + + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Tracing.5.2.3\lib\net45\System.Web.Http.Tracing.dll + + + + + + + + + + + + + + + + Designer + + + + + {74420a79-cc16-442c-8b1e-7c1b913844f0} + CurlSharp + + + {e636d5f8-68b4-4903-b4ed-ccfd9c9e899f} + Jackett + + + + + + + + \ No newline at end of file diff --git a/src/Jackett.Console/Program.cs b/src/Jackett.Console/Program.cs new file mode 100644 index 000000000..6f6973c79 --- /dev/null +++ b/src/Jackett.Console/Program.cs @@ -0,0 +1,59 @@ +using Jackett; +using Jackett.Indexers; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace JackettConsole +{ + public class Program + { + static void Main(string[] args) + { + try + { + foreach (var arg in args) + { + switch (arg.ToLowerInvariant()) + { + case "/i": // Install + Engine.ServiceConfig.Install(); + return; + case "/r": // Reserve port/url & install + Engine.Server.ReserveUrls(doInstall: true); + return; + case "/c": // Change port + Engine.Server.ReserveUrls(doInstall: false); + return; + case "/u": // Uninstall + Engine.Server.ReserveUrls(doInstall: false); + Engine.ServiceConfig.Uninstall(); + return; + case "/l": // Logging + Engine.LogRequests = true; + break; + case "/t": // Tracing + Engine.TracingEnabled = true; + break; + } + } + + Engine.Server.Start(); + Engine.Logger.Info("Running in console mode."); + Engine.RunTime.Spin(); + Engine.Logger.Info("Server thread exit"); + } + catch (Exception e) + { + Engine.Logger.Error(e, "Top level exception"); + } + } + } +} + diff --git a/src/Jackett.Console/Properties/AssemblyInfo.cs b/src/Jackett.Console/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..0c11610e1 --- /dev/null +++ b/src/Jackett.Console/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Jackett.Console")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Jackett.Console")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4e2a81da-e235-4a88-ad20-38aabbfbf33c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Jackett/WebContent/favicon.ico b/src/Jackett.Console/jackett.ico similarity index 100% rename from src/Jackett/WebContent/favicon.ico rename to src/Jackett.Console/jackett.ico diff --git a/src/Jackett.Console/packages.config b/src/Jackett.Console/packages.config new file mode 100644 index 000000000..268d933cc --- /dev/null +++ b/src/Jackett.Console/packages.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Jackett.Service/App.config b/src/Jackett.Service/App.config new file mode 100644 index 000000000..e74e545f8 --- /dev/null +++ b/src/Jackett.Service/App.config @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Jackett.Service/Jackett.Service.csproj b/src/Jackett.Service/Jackett.Service.csproj new file mode 100644 index 000000000..a4c6c9940 --- /dev/null +++ b/src/Jackett.Service/Jackett.Service.csproj @@ -0,0 +1,144 @@ + + + + + Debug + AnyCPU + {BF611F7B-4658-4CB8-AA9E-0736FADAA3BA} + WinExe + Properties + Jackett.Service + JackettService + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + jackett.ico + + + + False + ..\packages\Autofac.3.5.0\lib\net40\Autofac.dll + + + ..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll + + + False + ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll + + + ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll + + + False + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + + + False + ..\packages\Microsoft.Owin.FileSystems.3.0.1\lib\net45\Microsoft.Owin.FileSystems.dll + + + ..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll + + + False + ..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll + + + False + ..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll + + + False + ..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + + + False + ..\packages\NLog.4.0.1\lib\net45\NLog.dll + + + False + ..\packages\Owin.1.0\lib\net40\Owin.dll + + + + + False + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + + False + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + + + False + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll + + + ..\packages\Microsoft.AspNet.WebApi.Tracing.5.2.3\lib\net45\System.Web.Http.Tracing.dll + + + + + + + + + + + + Component + + + Service.cs + + + + + + + + + + + + + + {74420a79-cc16-442c-8b1e-7c1b913844f0} + CurlSharp + + + {e636d5f8-68b4-4903-b4ed-ccfd9c9e899f} + Jackett + + + + + \ No newline at end of file diff --git a/src/Jackett.Service/Program.cs b/src/Jackett.Service/Program.cs new file mode 100644 index 000000000..22c25af4b --- /dev/null +++ b/src/Jackett.Service/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Service +{ + static class Program + { + /// + /// The main entry point for the application. + /// + static void Main() + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] + { + new Service() + }; + ServiceBase.Run(ServicesToRun); + } + } +} diff --git a/src/Jackett.Service/Properties/AssemblyInfo.cs b/src/Jackett.Service/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..246a632ba --- /dev/null +++ b/src/Jackett.Service/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Jackett.Service")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Jackett.Service")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bf611f7b-4658-4cb8-aa9e-0736fadaa3ba")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Jackett.Service/Service.Designer.cs b/src/Jackett.Service/Service.Designer.cs new file mode 100644 index 000000000..0e2f0f42d --- /dev/null +++ b/src/Jackett.Service/Service.Designer.cs @@ -0,0 +1,37 @@ +namespace Jackett.Service +{ + partial class Service + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + this.ServiceName = "Jackett"; + } + + #endregion + } +} diff --git a/src/Jackett.Service/Service.cs b/src/Jackett.Service/Service.cs new file mode 100644 index 000000000..47690ab21 --- /dev/null +++ b/src/Jackett.Service/Service.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Service +{ + public partial class Service : ServiceBase + { + public Service() + { + InitializeComponent(); + } + + protected override void OnStart(string[] args) + { + Engine.Logger.Info("Service starting"); + Engine.Server.Start(); + Engine.Logger.Info("Service started"); + } + + protected override void OnStop() + { + Engine.Logger.Info("Service stopping"); + Engine.Server.Stop(); + } + } +} diff --git a/src/Jackett/jacket_large.ico b/src/Jackett.Service/jackett.ico similarity index 100% rename from src/Jackett/jacket_large.ico rename to src/Jackett.Service/jackett.ico diff --git a/src/Jackett.Service/packages.config b/src/Jackett.Service/packages.config new file mode 100644 index 000000000..cfb04dcec --- /dev/null +++ b/src/Jackett.Service/packages.config @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Jackett.Tray/App.config b/src/Jackett.Tray/App.config new file mode 100644 index 000000000..88fa4027b --- /dev/null +++ b/src/Jackett.Tray/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Jackett.Tray/Jackett.Tray.csproj b/src/Jackett.Tray/Jackett.Tray.csproj new file mode 100644 index 000000000..0e3f38375 --- /dev/null +++ b/src/Jackett.Tray/Jackett.Tray.csproj @@ -0,0 +1,96 @@ + + + + + Debug + AnyCPU + {FF9025B1-EC14-4AA9-8081-9F69C5E35B63} + WinExe + Properties + Jackett.Tray + JackettTray + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + jackett.ico + + + + + + + + + + + + + + + + + Form + + + Main.cs + + + + + Main.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + \ No newline at end of file diff --git a/src/Jackett/Windows/Main.Designer.cs b/src/Jackett.Tray/Main.Designer.cs similarity index 98% rename from src/Jackett/Windows/Main.Designer.cs rename to src/Jackett.Tray/Main.Designer.cs index d881a2821..71457c20d 100644 --- a/src/Jackett/Windows/Main.Designer.cs +++ b/src/Jackett.Tray/Main.Designer.cs @@ -1,6 +1,4 @@ -#if !__MonoCS__ - -namespace Jackett +namespace JackettTray { partial class Main { @@ -99,5 +97,4 @@ namespace Jackett private System.Windows.Forms.ToolStripMenuItem toolStripMenuItemShutdown; } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/Jackett/Windows/Main.cs b/src/Jackett.Tray/Main.cs similarity index 88% rename from src/Jackett/Windows/Main.cs rename to src/Jackett.Tray/Main.cs index 250aff2ce..991f72f1a 100644 --- a/src/Jackett/Windows/Main.cs +++ b/src/Jackett.Tray/Main.cs @@ -1,5 +1,4 @@ -#if !__MonoCS__ -using Microsoft.Win32; +using Microsoft.Win32; using System; using System.Collections.Generic; using System.ComponentModel; @@ -13,7 +12,7 @@ using System.Text; using System.Threading.Tasks; using System.Windows.Forms; -namespace Jackett +namespace JackettTray { public partial class Main : Form { @@ -28,13 +27,13 @@ namespace Jackett toolStripMenuItemWebUI.Click += toolStripMenuItemWebUI_Click; toolStripMenuItemShutdown.Click += toolStripMenuItemShutdown_Click; - if (Program.IsFirstRun) - AutoStart = true; + //if (Server.IsFirstRun) + // AutoStart = true; } void toolStripMenuItemWebUI_Click(object sender, EventArgs e) { - Process.Start("http://127.0.0.1:" + Server.Port); + // Process.Start("http://127.0.0.1:" + Server.Port); } void toolStripMenuItemShutdown_Click(object sender, EventArgs e) @@ -84,13 +83,12 @@ namespace Jackett private void CreateShortcut() { - var appPath = Assembly.GetExecutingAssembly().Location; + /* var appPath = Assembly.GetExecutingAssembly().Location; var shell = new IWshRuntimeLibrary.WshShell(); var shortcut = (IWshRuntimeLibrary.IWshShortcut)shell.CreateShortcut(ShortcutPath); shortcut.Description = Assembly.GetExecutingAssembly().GetName().Name; shortcut.TargetPath = appPath; - shortcut.Save(); + shortcut.Save();*/ } } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/Jackett/Windows/Main.resx b/src/Jackett.Tray/Main.resx similarity index 100% rename from src/Jackett/Windows/Main.resx rename to src/Jackett.Tray/Main.resx diff --git a/src/Jackett.Tray/Program.cs b/src/Jackett.Tray/Program.cs new file mode 100644 index 000000000..b902536cd --- /dev/null +++ b/src/Jackett.Tray/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace JackettTray +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Main()); + } + } +} diff --git a/src/Jackett.Tray/Properties/AssemblyInfo.cs b/src/Jackett.Tray/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..0f6590c99 --- /dev/null +++ b/src/Jackett.Tray/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Jackett.Tray")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Jackett.Tray")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ff9025b1-ec14-4aa9-8081-9f69c5e35b63")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Jackett.Tray/Properties/Resources.Designer.cs b/src/Jackett.Tray/Properties/Resources.Designer.cs new file mode 100644 index 000000000..b6ce33f0b --- /dev/null +++ b/src/Jackett.Tray/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Jackett.Tray.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Jackett.Tray.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/src/Jackett.Tray/Properties/Resources.resx b/src/Jackett.Tray/Properties/Resources.resx new file mode 100644 index 000000000..af7dbebba --- /dev/null +++ b/src/Jackett.Tray/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Jackett.Tray/Properties/Settings.Designer.cs b/src/Jackett.Tray/Properties/Settings.Designer.cs new file mode 100644 index 000000000..c110e9e5e --- /dev/null +++ b/src/Jackett.Tray/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Jackett.Tray.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/src/Jackett.Tray/Properties/Settings.settings b/src/Jackett.Tray/Properties/Settings.settings new file mode 100644 index 000000000..39645652a --- /dev/null +++ b/src/Jackett.Tray/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Jackett.Tray/jackett.ico b/src/Jackett.Tray/jackett.ico new file mode 100644 index 000000000..6392acc61 Binary files /dev/null and b/src/Jackett.Tray/jackett.ico differ diff --git a/src/Jackett.sln b/src/Jackett.sln index a7c6e3384..bd24b6def 100644 --- a/src/Jackett.sln +++ b/src/Jackett.sln @@ -9,10 +9,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CurlSharp", "CurlSharp\Curl EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BE7B0C8A-6144-47CD-821E-B09BA1B7BADE}" ProjectSection(SolutionItems) = preProject + ..\Build.bat = ..\Build.bat + ..\Installer.iss = ..\Installer.iss ..\LICENSE = ..\LICENSE ..\README.md = ..\README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jackett.Console", "Jackett.Console\Jackett.Console.csproj", "{4E2A81DA-E235-4A88-AD20-38AABBFBF33C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jackett.Service", "Jackett.Service\Jackett.Service.csproj", "{BF611F7B-4658-4CB8-AA9E-0736FADAA3BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jackett.Tray", "Jackett.Tray\Jackett.Tray.csproj", "{FF9025B1-EC14-4AA9-8081-9F69C5E35B63}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +35,18 @@ Global {74420A79-CC16-442C-8B1E-7C1B913844F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {74420A79-CC16-442C-8B1E-7C1B913844F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {74420A79-CC16-442C-8B1E-7C1B913844F0}.Release|Any CPU.Build.0 = Release|Any CPU + {4E2A81DA-E235-4A88-AD20-38AABBFBF33C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E2A81DA-E235-4A88-AD20-38AABBFBF33C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E2A81DA-E235-4A88-AD20-38AABBFBF33C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E2A81DA-E235-4A88-AD20-38AABBFBF33C}.Release|Any CPU.Build.0 = Release|Any CPU + {BF611F7B-4658-4CB8-AA9E-0736FADAA3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BF611F7B-4658-4CB8-AA9E-0736FADAA3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BF611F7B-4658-4CB8-AA9E-0736FADAA3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BF611F7B-4658-4CB8-AA9E-0736FADAA3BA}.Release|Any CPU.Build.0 = Release|Any CPU + {FF9025B1-EC14-4AA9-8081-9F69C5E35B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF9025B1-EC14-4AA9-8081-9F69C5E35B63}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF9025B1-EC14-4AA9-8081-9F69C5E35B63}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF9025B1-EC14-4AA9-8081-9F69C5E35B63}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Jackett/App.config b/src/Jackett/App.config index 56ebf3f29..547fa70d5 100644 --- a/src/Jackett/App.config +++ b/src/Jackett/App.config @@ -9,7 +9,27 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Jackett/WebContent/animate.css b/src/Jackett/Content/animate.css similarity index 100% rename from src/Jackett/WebContent/animate.css rename to src/Jackett/Content/animate.css diff --git a/src/Jackett/WebContent/binding_dark.png b/src/Jackett/Content/binding_dark.png similarity index 100% rename from src/Jackett/WebContent/binding_dark.png rename to src/Jackett/Content/binding_dark.png diff --git a/src/Jackett/WebContent/bootstrap-notify.js b/src/Jackett/Content/bootstrap-notify.js similarity index 100% rename from src/Jackett/WebContent/bootstrap-notify.js rename to src/Jackett/Content/bootstrap-notify.js diff --git a/src/Jackett/WebContent/bootstrap/bootstrap.min.css b/src/Jackett/Content/bootstrap/bootstrap.min.css similarity index 100% rename from src/Jackett/WebContent/bootstrap/bootstrap.min.css rename to src/Jackett/Content/bootstrap/bootstrap.min.css diff --git a/src/Jackett/WebContent/bootstrap/bootstrap.min.js b/src/Jackett/Content/bootstrap/bootstrap.min.js similarity index 100% rename from src/Jackett/WebContent/bootstrap/bootstrap.min.js rename to src/Jackett/Content/bootstrap/bootstrap.min.js diff --git a/src/Jackett/WebContent/common.js b/src/Jackett/Content/common.js similarity index 100% rename from src/Jackett/WebContent/common.js rename to src/Jackett/Content/common.js diff --git a/src/Jackett/WebContent/congruent_outline.png b/src/Jackett/Content/congruent_outline.png similarity index 100% rename from src/Jackett/WebContent/congruent_outline.png rename to src/Jackett/Content/congruent_outline.png diff --git a/src/Jackett/WebContent/crissXcross.png b/src/Jackett/Content/crissXcross.png similarity index 100% rename from src/Jackett/WebContent/crissXcross.png rename to src/Jackett/Content/crissXcross.png diff --git a/src/Jackett/WebContent/custom.css b/src/Jackett/Content/custom.css similarity index 100% rename from src/Jackett/WebContent/custom.css rename to src/Jackett/Content/custom.css diff --git a/src/Jackett/WebContent/custom.js b/src/Jackett/Content/custom.js similarity index 81% rename from src/Jackett/WebContent/custom.js rename to src/Jackett/Content/custom.js index c79c6fa97..940018b69 100644 --- a/src/Jackett/WebContent/custom.js +++ b/src/Jackett/Content/custom.js @@ -5,22 +5,29 @@ loadJackettSettings(); function loadJackettSettings() { getJackettConfig(function (data) { + + $("#api-key-input").val(data.config.api_key); + $("#app-version").html(data.app_version); $("#jackett-port").val(data.config.port); + var password = data.config.password; + $("#jackett-adminpwd").val(password); + if (password != null && password != '') { + $("#logoutBtn").show(); + } }); } $("#change-jackett-port").click(function () { var jackett_port = $("#jackett-port").val(); - var jsonObject = JSON.parse('{"port":"' + jackett_port + '"}'); - - var jqxhr = $.post("apply_jackett_config", JSON.stringify(jsonObject), function (data) { + var jsonObject = { port: jackett_port}; + var jqxhr = $.post("/admin/apply_jackett_config", JSON.stringify(jsonObject), function (data) { if (data.result == "error") { doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert"); return; } else { doNotify("The port has been changed. Jackett will now restart...", "success", "glyphicon glyphicon-ok"); - var jqxhr0 = $.post("jackett_restart", null, function (data_restart) { }); + var jqxhr0 = $.post("admin/jackett_restart", null, function (data_restart) { }); window.setTimeout(function () { url = window.location.href; @@ -34,8 +41,30 @@ $("#change-jackett-port").click(function () { }); }); +$("#change-jackett-password").click(function () { + var password = $("#jackett-adminpwd").val(); + var jsonObject = { password: password }; + + var jqxhr = $.post("/admin/set_admin_password", JSON.stringify(jsonObject), function (data) { + + if (data.result == "error") { + doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert"); + return; + } else { + doNotify("Admin password has been set.", "success", "glyphicon glyphicon-ok"); + + window.setTimeout(function () { + window.location = window.location.pathname; + }, 1000); + + } + }).fail(function () { + doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert"); + }); +}); + function getJackettConfig(callback) { - var jqxhr = $.get("get_jackett_config", function (data) { + var jqxhr = $.get("/admin/get_jackett_config", function (data) { callback(data); }).fail(function () { @@ -47,9 +76,7 @@ function reloadIndexers() { $('#indexers').hide(); $('#indexers > .indexer').remove(); $('#unconfigured-indexers').empty(); - var jqxhr = $.get("get_indexers", function (data) { - $("#api-key-input").val(data.api_key); - $("#app-version").html(data.app_version); + var jqxhr = $.get("/admin/get_indexers", function (data) { displayIndexers(data.items); }).fail(function () { doNotify("Error loading indexers, request to Jackett server failed", "danger", "glyphicon glyphicon-alert"); @@ -82,7 +109,7 @@ function prepareDeleteButtons() { var $btn = $(btn); var id = $btn.data("id"); $btn.click(function () { - var jqxhr = $.post("delete_indexer", JSON.stringify({ indexer: id }), function (data) { + var jqxhr = $.post("/admin/delete_indexer", JSON.stringify({ indexer: id }), function (data) { if (data.result == "error") { doNotify("Delete error for " + id + "\n" + data.error, "danger", "glyphicon glyphicon-alert"); } @@ -114,7 +141,7 @@ function prepareTestButtons() { var id = $btn.data("id"); $btn.click(function () { doNotify("Test started for " + id, "info", "glyphicon glyphicon-transfer"); - var jqxhr = $.post("test_indexer", JSON.stringify({ indexer: id }), function (data) { + var jqxhr = $.post("/admin/test_indexer", JSON.stringify({ indexer: id }), function (data) { if (data.result == "error") { doNotify("Test failed for " + data.name + "\n" + data.error, "danger", "glyphicon glyphicon-alert"); } @@ -130,7 +157,7 @@ function prepareTestButtons() { function displayIndexerSetup(id) { - var jqxhr = $.post("get_config_form", JSON.stringify({ indexer: id }), function (data) { + var jqxhr = $.post("/admin/get_config_form", JSON.stringify({ indexer: id }), function (data) { if (data.result == "error") { doNotify("Error: " + data.error, "danger", "glyphicon glyphicon-alert"); return; @@ -200,7 +227,7 @@ function populateSetupForm(indexerId, name, config) { $goButton.prop('disabled', true); $goButton.html($('#templates > .spinner')[0].outerHTML); - var jqxhr = $.post("configure_indexer", JSON.stringify(data), function (data) { + var jqxhr = $.post("/admin/configure_indexer", JSON.stringify(data), function (data) { if (data.result == "error") { if (data.config) { populateConfigItems(configForm, data.config); diff --git a/src/Jackett/Content/favicon.ico b/src/Jackett/Content/favicon.ico new file mode 100644 index 000000000..6392acc61 Binary files /dev/null and b/src/Jackett/Content/favicon.ico differ diff --git a/src/Jackett/Content/fonts/glyphicons-halflings-regular.eot b/src/Jackett/Content/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..b93a4953f Binary files /dev/null and b/src/Jackett/Content/fonts/glyphicons-halflings-regular.eot differ diff --git a/src/Jackett/Content/fonts/glyphicons-halflings-regular.svg b/src/Jackett/Content/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/src/Jackett/Content/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Jackett/Content/fonts/glyphicons-halflings-regular.ttf b/src/Jackett/Content/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..1413fc609 Binary files /dev/null and b/src/Jackett/Content/fonts/glyphicons-halflings-regular.ttf differ diff --git a/src/Jackett/WebContent/fonts/glyphicons-halflings-regular.woff b/src/Jackett/Content/fonts/glyphicons-halflings-regular.woff similarity index 100% rename from src/Jackett/WebContent/fonts/glyphicons-halflings-regular.woff rename to src/Jackett/Content/fonts/glyphicons-halflings-regular.woff diff --git a/src/Jackett/Content/fonts/glyphicons-halflings-regular.woff2 b/src/Jackett/Content/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000..64539b54c Binary files /dev/null and b/src/Jackett/Content/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/src/Jackett/WebContent/handlebars-v3.0.1.js b/src/Jackett/Content/handlebars-v3.0.1.js similarity index 100% rename from src/Jackett/WebContent/handlebars-v3.0.1.js rename to src/Jackett/Content/handlebars-v3.0.1.js diff --git a/src/Jackett/WebContent/index.html b/src/Jackett/Content/index.html similarity index 82% rename from src/Jackett/WebContent/index.html rename to src/Jackett/Content/index.html index e63a95ac6..d3b31d560 100644 --- a/src/Jackett/WebContent/index.html +++ b/src/Jackett/Content/index.html @@ -6,21 +6,21 @@ - - - - + + + + - - - + + + Jackett
- Jackett + Jackett
@@ -35,6 +35,16 @@ Jackett API Key:
+
+ Admin Password: + + + +

Jackett port: @@ -101,7 +111,7 @@
- +
- +
Visit @@ -153,7 +163,7 @@
- + \ No newline at end of file diff --git a/src/Jackett/WebContent/jacket_medium.png b/src/Jackett/Content/jacket_medium.png similarity index 100% rename from src/Jackett/WebContent/jacket_medium.png rename to src/Jackett/Content/jacket_medium.png diff --git a/src/Jackett/WebContent/jquery-2.1.3.min.js b/src/Jackett/Content/jquery-2.1.3.min.js similarity index 100% rename from src/Jackett/WebContent/jquery-2.1.3.min.js rename to src/Jackett/Content/jquery-2.1.3.min.js diff --git a/src/Jackett/Content/login.html b/src/Jackett/Content/login.html new file mode 100644 index 000000000..96b63710e --- /dev/null +++ b/src/Jackett/Content/login.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + Jackett + + +
+ + Jackett + +
+

Login

+
+
+ Admin password + +
+
+ +
+
+
+ + \ No newline at end of file diff --git a/src/Jackett/WebContent/logos/alpharatio.png b/src/Jackett/Content/logos/alpharatio.png similarity index 100% rename from src/Jackett/WebContent/logos/alpharatio.png rename to src/Jackett/Content/logos/alpharatio.png diff --git a/src/Jackett/WebContent/logos/animebytes.png b/src/Jackett/Content/logos/animebytes.png similarity index 100% rename from src/Jackett/WebContent/logos/animebytes.png rename to src/Jackett/Content/logos/animebytes.png diff --git a/src/Jackett/Content/logos/bb.png b/src/Jackett/Content/logos/bb.png new file mode 100644 index 000000000..73c5d3d72 Binary files /dev/null and b/src/Jackett/Content/logos/bb.png differ diff --git a/src/Jackett/WebContent/logos/beyondhd.png b/src/Jackett/Content/logos/beyondhd.png similarity index 100% rename from src/Jackett/WebContent/logos/beyondhd.png rename to src/Jackett/Content/logos/beyondhd.png diff --git a/src/Jackett/WebContent/logos/bithdtv.png b/src/Jackett/Content/logos/bithdtv.png similarity index 100% rename from src/Jackett/WebContent/logos/bithdtv.png rename to src/Jackett/Content/logos/bithdtv.png diff --git a/src/Jackett/WebContent/logos/bitmetv.png b/src/Jackett/Content/logos/bitmetv.png similarity index 100% rename from src/Jackett/WebContent/logos/bitmetv.png rename to src/Jackett/Content/logos/bitmetv.png diff --git a/src/Jackett/WebContent/logos/frenchtorrentdb.png b/src/Jackett/Content/logos/frenchtorrentdb.png similarity index 100% rename from src/Jackett/WebContent/logos/frenchtorrentdb.png rename to src/Jackett/Content/logos/frenchtorrentdb.png diff --git a/src/Jackett/WebContent/logos/freshon.png b/src/Jackett/Content/logos/freshon.png similarity index 100% rename from src/Jackett/WebContent/logos/freshon.png rename to src/Jackett/Content/logos/freshon.png diff --git a/src/Jackett/WebContent/logos/hdtorrents.png b/src/Jackett/Content/logos/hdtorrents.png similarity index 100% rename from src/Jackett/WebContent/logos/hdtorrents.png rename to src/Jackett/Content/logos/hdtorrents.png diff --git a/src/Jackett/WebContent/logos/iptorrents.png b/src/Jackett/Content/logos/iptorrents.png similarity index 100% rename from src/Jackett/WebContent/logos/iptorrents.png rename to src/Jackett/Content/logos/iptorrents.png diff --git a/src/Jackett/WebContent/logos/morethantv.png b/src/Jackett/Content/logos/morethantv.png similarity index 100% rename from src/Jackett/WebContent/logos/morethantv.png rename to src/Jackett/Content/logos/morethantv.png diff --git a/src/Jackett/WebContent/logos/rarbg.png b/src/Jackett/Content/logos/rarbg.png similarity index 100% rename from src/Jackett/WebContent/logos/rarbg.png rename to src/Jackett/Content/logos/rarbg.png diff --git a/src/Jackett/WebContent/logos/sceneaccess.png b/src/Jackett/Content/logos/sceneaccess.png similarity index 100% rename from src/Jackett/WebContent/logos/sceneaccess.png rename to src/Jackett/Content/logos/sceneaccess.png diff --git a/src/Jackett/WebContent/logos/scenetime.png b/src/Jackett/Content/logos/scenetime.png similarity index 100% rename from src/Jackett/WebContent/logos/scenetime.png rename to src/Jackett/Content/logos/scenetime.png diff --git a/src/Jackett/WebContent/logos/showrss.png b/src/Jackett/Content/logos/showrss.png similarity index 100% rename from src/Jackett/WebContent/logos/showrss.png rename to src/Jackett/Content/logos/showrss.png diff --git a/src/Jackett/WebContent/logos/speedcd.png b/src/Jackett/Content/logos/speedcd.png similarity index 100% rename from src/Jackett/WebContent/logos/speedcd.png rename to src/Jackett/Content/logos/speedcd.png diff --git a/src/Jackett/WebContent/logos/strike.png b/src/Jackett/Content/logos/strike.png similarity index 100% rename from src/Jackett/WebContent/logos/strike.png rename to src/Jackett/Content/logos/strike.png diff --git a/src/Jackett/WebContent/logos/t411.png b/src/Jackett/Content/logos/t411.png similarity index 100% rename from src/Jackett/WebContent/logos/t411.png rename to src/Jackett/Content/logos/t411.png diff --git a/src/Jackett/WebContent/logos/thepiratebay.png b/src/Jackett/Content/logos/thepiratebay.png similarity index 100% rename from src/Jackett/WebContent/logos/thepiratebay.png rename to src/Jackett/Content/logos/thepiratebay.png diff --git a/src/Jackett/WebContent/logos/torrentday.png b/src/Jackett/Content/logos/torrentday.png similarity index 100% rename from src/Jackett/WebContent/logos/torrentday.png rename to src/Jackett/Content/logos/torrentday.png diff --git a/src/Jackett/WebContent/logos/torrentleech.png b/src/Jackett/Content/logos/torrentleech.png similarity index 100% rename from src/Jackett/WebContent/logos/torrentleech.png rename to src/Jackett/Content/logos/torrentleech.png diff --git a/src/Jackett/WebContent/logos/torrentshack.png b/src/Jackett/Content/logos/torrentshack.png similarity index 100% rename from src/Jackett/WebContent/logos/torrentshack.png rename to src/Jackett/Content/logos/torrentshack.png diff --git a/src/Jackett/WebContent/logos/torrentz.png b/src/Jackett/Content/logos/torrentz.png similarity index 100% rename from src/Jackett/WebContent/logos/torrentz.png rename to src/Jackett/Content/logos/torrentz.png diff --git a/src/Jackett/WebContent/setup_indexer.html b/src/Jackett/Content/setup_indexer.html similarity index 100% rename from src/Jackett/WebContent/setup_indexer.html rename to src/Jackett/Content/setup_indexer.html diff --git a/src/Jackett/Controllers/APIController.cs b/src/Jackett/Controllers/APIController.cs new file mode 100644 index 000000000..18eaad0d0 --- /dev/null +++ b/src/Jackett/Controllers/APIController.cs @@ -0,0 +1,73 @@ +using Jackett.Models; +using Jackett.Services; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; + +namespace Jackett.Controllers +{ + public class APIController : ApiController + { + private IIndexerManagerService indexerService; + private Logger logger; + + public APIController(IIndexerManagerService i, Logger l) + { + indexerService = i; + logger = l; + } + + [HttpGet] + public async Task Call(string indexerName) + { + var indexer = indexerService.GetIndexer(indexerName); + var torznabQuery = TorznabQuery.FromHttpQuery(HttpUtility.ParseQueryString(Request.RequestUri.Query)); + + if (torznabQuery.RageIDLookupEnabled && indexer.RequiresRageIDLookupDisabled) + { + throw new ArgumentException("This indexer requires RageID lookup disabled"); + } + + var releases = await indexer.PerformQuery(torznabQuery); + + logger.Debug(string.Format("Found {0} releases from {1}", releases.Length, indexer.DisplayName)); + var severUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port); + + var resultPage = new ResultPage(new ChannelInfo + { + Title = indexer.DisplayName, + Description = indexer.DisplayDescription, + Link = indexer.SiteLink, + ImageUrl = new Uri(severUrl + "logos/" + indexer.DisplayName + ".png"), + ImageTitle = indexer.DisplayName, + ImageLink = indexer.SiteLink, + ImageDescription = indexer.DisplayName + }); + + // add Jackett proxy to download links... + foreach (var release in releases) + { + if (release.Link == null || release.Link.Scheme == "magnet") + continue; + var originalLink = release.Link; + var encodedLink = HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(originalLink.ToString())) + "/download.torrent"; + var proxyLink = string.Format("{0}api/{1}/download/{2}", severUrl, indexer.DisplayName, encodedLink); + release.Link = new Uri(proxyLink); + } + + resultPage.Releases.AddRange(releases); + var xml = resultPage.ToXml(new Uri(severUrl)); + // Force the return as XML + return new HttpResponseMessage() + { + Content = new StringContent(xml, Encoding.UTF8, "application/rss+xml") + }; + } + } +} diff --git a/src/Jackett/Controllers/AdminController.cs b/src/Jackett/Controllers/AdminController.cs new file mode 100644 index 000000000..0adffb411 --- /dev/null +++ b/src/Jackett/Controllers/AdminController.cs @@ -0,0 +1,310 @@ +using Autofac; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; +using System.Web.Http.Results; +using System.Web.Security; + +namespace Jackett.Controllers +{ + [RoutePrefix("admin")] + [JackettAuthorized] + public class AdminController : ApiController + { + private IConfigurationService config; + private IIndexerManagerService indexerService; + private IServerService serverService; + private ISecuityService securityService; + + public AdminController(IConfigurationService config, IIndexerManagerService i, IServerService ss, ISecuityService s) + { + this.config = config; + indexerService = i; + serverService = ss; + securityService = s; + } + + private async Task ReadPostDataJson() + { + var content = await Request.Content.ReadAsStringAsync(); + return JObject.Parse(content); + } + + + private HttpResponseMessage GetFile(string path) + { + var result = new HttpResponseMessage(HttpStatusCode.OK); + var mappedPath = Path.Combine(config.GetContentFolder(), path); + var stream = new FileStream(mappedPath, FileMode.Open); + result.Content = new StreamContent(stream); + result.Content.Headers.ContentType = + new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(mappedPath)); + + return result; + } + + [HttpGet] + [AllowAnonymous] + public RedirectResult Logout() + { + var ctx = Request.GetOwinContext(); + var authManager = ctx.Authentication; + authManager.SignOut("ApplicationCookie"); + return Redirect("/Admin/Dashboard"); + } + + [HttpGet] + [HttpPost] + [AllowAnonymous] + public async Task Dashboard() + { + if(Request.RequestUri.Query!=null && Request.RequestUri.Query.Contains("logout")) + { + var file = GetFile("login.html"); + securityService.Logout(file); + return file; + } + + + if (securityService.CheckAuthorised(Request)) + { + return GetFile("index.html"); + + } else + { + var formData = await Request.Content.ReadAsFormDataAsync(); + + if (formData!=null && securityService.HashPassword(formData["password"]) == serverService.Config.AdminPassword) + { + var file = GetFile("index.html"); + securityService.Login(file); + return file; + } else + { + return GetFile("login.html"); + } + } + } + + [Route("set_admin_password")] + [HttpPost] + public async Task SetAdminPassword() + { + var jsonReply = new JObject(); + try + { + var postData = await ReadPostDataJson(); + var password = (string)postData["password"]; + if (string.IsNullOrEmpty(password)) + { + serverService.Config.AdminPassword = string.Empty; + } + else + { + serverService.Config.AdminPassword = securityService.HashPassword(password); + } + + serverService.SaveConfig(); + jsonReply["result"] = "success"; + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + return Json(jsonReply); + } + + [Route("get_config_form")] + [HttpPost] + public async Task GetConfigForm() + { + var jsonReply = new JObject(); + try + { + var postData = await ReadPostDataJson(); + var indexer = indexerService.GetIndexer((string)postData["indexer"]); + var config = await indexer.GetConfigurationForSetup(); + jsonReply["config"] = config.ToJson(); + jsonReply["name"] = indexer.DisplayName; + jsonReply["result"] = "success"; + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + return Json(jsonReply); + } + + [Route("configure_indexer")] + [HttpPost] + public async Task Configure() + { + JToken jsonReply = new JObject(); + try + { + var postData = await ReadPostDataJson(); + string indexerString = (string)postData["indexer"]; + var indexer = indexerService.GetIndexer((string)postData["indexer"]); + jsonReply["name"] = indexer.DisplayName; + await indexer.ApplyConfiguration(postData["config"]); + await indexerService.TestIndexer((string)postData["indexer"]); + jsonReply["result"] = "success"; + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + if (ex is ExceptionWithConfigData) + { + jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson(); + } + } + return Json(jsonReply); + } + + + + [Route("get_indexers")] + [HttpGet] + public IHttpActionResult Indexers() + { + var jsonReply = new JObject(); + try + { + jsonReply["result"] = "success"; + JArray items = new JArray(); + + foreach (var indexer in indexerService.GetAllIndexers()) + { + var item = new JObject(); + item["id"] = indexer.ID; + item["name"] = indexer.DisplayName; + item["description"] = indexer.DisplayDescription; + item["configured"] = indexer.IsConfigured; + item["site_link"] = indexer.SiteLink; + items.Add(item); + } + jsonReply["items"] = items; + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + return Json(jsonReply); + } + + [Route("test_indexer")] + [HttpPost] + public async Task Test() + { + JToken jsonReply = new JObject(); + try + { + var postData = await ReadPostDataJson(); + string indexerString = (string)postData["indexer"]; + await indexerService.TestIndexer(indexerString); + jsonReply["name"] = indexerService.GetIndexer(indexerString).DisplayName; + jsonReply["result"] = "success"; + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + return Json(jsonReply); + } + + [Route("delete_indexer")] + [HttpPost] + public async Task Delete() + { + var jsonReply = new JObject(); + try + { + var postData = await ReadPostDataJson(); + string indexerString = (string)postData["indexer"]; + indexerService.DeleteIndexer(indexerString); + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + return Json(jsonReply); + } + + [Route("get_jackett_config")] + [HttpGet] + public IHttpActionResult GetConfig() + { + var jsonReply = new JObject(); + try + { + var cfg = new JObject(); + cfg["port"] = serverService.Config.Port; + cfg["api_key"] = serverService.Config.APIKey; + cfg["password"] = string.IsNullOrEmpty(serverService.Config.AdminPassword )? string.Empty:serverService.Config.AdminPassword.Substring(0,10); + + jsonReply["config"] = cfg; + jsonReply["app_version"] = config.GetVersion(); + jsonReply["result"] = "success"; + } + catch (CustomException ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + return Json(jsonReply); + } + + [Route("apply_jackett_config")] + [HttpPost] + public async Task SetConfig() + { + var jsonReply = new JObject(); + try + { + var postData = await ReadPostDataJson(); + // int port = await WebServer.ApplyPortConfiguration(postData); + jsonReply["result"] = "success"; + // jsonReply["port"] = port; + } + catch (Exception ex) + { + jsonReply["result"] = "error"; + jsonReply["error"] = ex.Message; + } + return Json(jsonReply); + } + + + [Route("jackett_restart")] + [HttpPost] + public IHttpActionResult Restart() + { + return null; + } + } +} + diff --git a/src/Jackett/Controllers/DownloadController.cs b/src/Jackett/Controllers/DownloadController.cs new file mode 100644 index 000000000..a43fcb6ff --- /dev/null +++ b/src/Jackett/Controllers/DownloadController.cs @@ -0,0 +1,49 @@ +using Jackett.Services; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; + +namespace Jackett.Controllers +{ + [AllowAnonymous] + public class DownloadController : ApiController + { + private Logger logger; + private IIndexerManagerService indexerService; + + public DownloadController(IIndexerManagerService i, Logger l) + { + logger = l; + indexerService = i; + } + + [HttpGet] + public async Task Download(string indexerName, string path) + { + try + { + var indexer = indexerService.GetIndexer(indexerName); + var remoteFile = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(path)); + var downloadBytes = await indexer.Download(new Uri(remoteFile)); + + var result = new HttpResponseMessage(HttpStatusCode.OK); + result.Content = new ByteArrayContent(downloadBytes); + result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-bittorrent"); + return result; + } + catch (Exception e) + { + logger.Error(e, "Error downloading " + indexerName + " " + path); + return new HttpResponseMessage(HttpStatusCode.NotFound); + } + } + } +} diff --git a/src/Jackett/Http/CookieContainerExtensions.cs b/src/Jackett/CookieContainerExtensions.cs similarity index 82% rename from src/Jackett/Http/CookieContainerExtensions.cs rename to src/Jackett/CookieContainerExtensions.cs index 447c3be30..070a326fa 100644 --- a/src/Jackett/Http/CookieContainerExtensions.cs +++ b/src/Jackett/CookieContainerExtensions.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Linq; @@ -11,8 +12,7 @@ namespace Jackett { public static class CookieContainerExtensions { - - public static void FillFromJson(this CookieContainer cookies, Uri uri, JToken json) + public static void FillFromJson(this CookieContainer cookies, Uri uri, JToken json, Logger logger) { if (json["cookies"] != null) { @@ -43,12 +43,17 @@ namespace Jackett } catch (CookieException ex) { - Program.LoggerInstance.Info("(Non-critical) Problem loading cookie {0}, {1}, {2}", uri, c, ex.Message); + logger.Info("(Non-critical) Problem loading cookie {0}, {1}, {2}", uri, c, ex.Message); } } } } + public static void DumpToJson(this CookieContainer cookies, string uri, JToken json) + { + DumpToJson(cookies, new Uri(uri), json); + } + public static void DumpToJson(this CookieContainer cookies, Uri uri, JToken json) { json["cookie_header"] = cookies.GetCookieHeader(uri); diff --git a/src/Jackett/Http/CurlHelper.cs b/src/Jackett/CurlHelper.cs similarity index 100% rename from src/Jackett/Http/CurlHelper.cs rename to src/Jackett/CurlHelper.cs diff --git a/src/Jackett/Engine.cs b/src/Jackett/Engine.cs new file mode 100644 index 000000000..000484704 --- /dev/null +++ b/src/Jackett/Engine.cs @@ -0,0 +1,139 @@ +using Autofac; +using Jackett.Services; +using NLog; +using NLog.Config; +using NLog.Targets; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett +{ + public class Engine + { + private static IContainer container = null; + + static Engine() + { + +#if DEBUG + TracingEnabled = true; +#endif + + var builder = new ContainerBuilder(); + builder.RegisterModule(); + container = builder.Build(); + + // Register the container in itself to allow for late resolves + var secondaryBuilder = new ContainerBuilder(); + secondaryBuilder.RegisterInstance(container).SingleInstance(); + SetupLogging(secondaryBuilder); + secondaryBuilder.Update(container); + + Logger.Info("Starting Jackett " + ConfigService.GetVersion()); + } + + public static bool TracingEnabled + { + get; + set; + } + + public static bool LogRequests + { + get; + set; + } + + public static IContainer GetContainer() + { + return container; + } + + public static bool IsWindows + { + get + { + return Environment.OSVersion.Platform == PlatformID.Win32NT; + } + } + + public static IConfigurationService ConfigService + { + get + { + return container.Resolve(); + } + } + + public static IServiceConfigService ServiceConfig + { + get + { + return container.Resolve(); + } + } + + public static IServerService Server + { + get + { + return container.Resolve(); + } + } + + public static IRunTimeService RunTime + { + get + { + return container.Resolve(); + } + } + + public static Logger Logger + { + get + { + return container.Resolve(); + } + } + + public static ISecuityService SecurityService + { + get + { + return container.Resolve(); + } + } + + + private static void SetupLogging(ContainerBuilder builder) + { + var logConfig = new LoggingConfiguration(); + + var logFile = new FileTarget(); + logConfig.AddTarget("file", logFile); + logFile.Layout = "${longdate} ${level} ${message} ${exception:format=ToString}"; + logFile.FileName = Path.Combine(ConfigurationService.GetAppDataFolderStatic(), "log.txt"); + logFile.ArchiveFileName = "log.{#####}.txt"; + logFile.ArchiveAboveSize = 500000; + logFile.MaxArchiveFiles = 1; + logFile.KeepFileOpen = false; + logFile.ArchiveNumbering = ArchiveNumberingMode.DateAndSequence; + var logFileRule = new LoggingRule("*", LogLevel.Debug, logFile); + logConfig.LoggingRules.Add(logFileRule); + + var logConsole = new ConsoleTarget(); + logConfig.AddTarget("console", logConsole); + logConsole.Layout = "${longdate} ${level} ${message} ${exception:format=ToString}"; + var logConsoleRule = new LoggingRule("*", LogLevel.Debug, logConsole); + logConfig.LoggingRules.Add(logConsoleRule); + + LogManager.Configuration = logConfig; + builder.RegisterInstance(LogManager.GetCurrentClassLogger()).SingleInstance(); + } + } +} diff --git a/src/Jackett/Configurations/ExceptionWithConfigData.cs b/src/Jackett/ExceptionWithConfigData.cs similarity index 93% rename from src/Jackett/Configurations/ExceptionWithConfigData.cs rename to src/Jackett/ExceptionWithConfigData.cs index 60cf8deae..9280d5f14 100644 --- a/src/Jackett/Configurations/ExceptionWithConfigData.cs +++ b/src/Jackett/ExceptionWithConfigData.cs @@ -1,4 +1,5 @@ -using System; +using Jackett.Models; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/src/Jackett/Http/HttpClientExtensions.cs b/src/Jackett/HttpClientExtensions.cs similarity index 100% rename from src/Jackett/Http/HttpClientExtensions.cs rename to src/Jackett/HttpClientExtensions.cs diff --git a/src/Jackett/IndexerManager.cs b/src/Jackett/IndexerManager.cs deleted file mode 100644 index e5f3d8f62..000000000 --- a/src/Jackett/IndexerManager.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Jackett -{ - public class IndexerManager - { - - static string IndexerConfigDirectory = Path.Combine(Program.AppConfigDirectory, "Indexers"); - - public Dictionary Indexers { get; private set; } - - public IndexerManager() - { - Indexers = new Dictionary(); - LoadMissingIndexers(); - } - - void LoadMissingIndexers() - { - var implementedIndexerTypes = AppDomain.CurrentDomain.GetAssemblies() - .SelectMany(s => s.GetTypes()) - .Where(p => typeof(IndexerInterface).IsAssignableFrom(p) && !p.IsInterface) - .ToArray(); - - foreach (var t in implementedIndexerTypes) - { - LoadIndexer(t); - } - } - - void LoadIndexer(Type indexerType) - { - var name = indexerType.Name.Trim().ToLower(); - - if (Indexers.ContainsKey(name)) - return; - - IndexerInterface newIndexer = (IndexerInterface)Activator.CreateInstance(indexerType); - newIndexer.OnSaveConfigurationRequested += newIndexer_OnSaveConfigurationRequested; - newIndexer.OnResultParsingError += newIndexer_OnResultParsingError; - - var configFilePath = GetIndexerConfigFilePath(newIndexer); - if (File.Exists(configFilePath)) - { - var jsonString = JObject.Parse(File.ReadAllText(configFilePath)); - newIndexer.LoadFromSavedConfiguration(jsonString); - } - - Indexers.Add(name, newIndexer); - } - - void newIndexer_OnResultParsingError(IndexerInterface indexer, string results, Exception ex) - { - var fileName = string.Format("Error on {0} for {1}.txt", DateTime.Now.ToString("yyyyMMddHHmmss"), indexer.DisplayName); - var spacing = string.Join("", Enumerable.Repeat(Environment.NewLine, 5)); - var fileContents = string.Format("{0}{1}{2}", ex, spacing, results); - File.WriteAllText(Path.Combine(Program.AppConfigDirectory, fileName), fileContents); - } - - string GetIndexerConfigFilePath(IndexerInterface indexer) - { - return Path.Combine(IndexerConfigDirectory, indexer.GetType().Name.ToLower() + ".json"); - } - - void newIndexer_OnSaveConfigurationRequested(IndexerInterface indexer, JToken obj) - { - var configFilePath = GetIndexerConfigFilePath(indexer); - if (!Directory.Exists(IndexerConfigDirectory)) - Directory.CreateDirectory(IndexerConfigDirectory); - File.WriteAllText(configFilePath, obj.ToString()); - } - - public IndexerInterface GetIndexer(string name) - { - IndexerInterface indexer; - if (!Indexers.TryGetValue(name, out indexer)) - throw new Exception(string.Format("No indexer with ID '{0}'", name)); - return indexer; - } - - public void DeleteIndexer(string name) - { - var indexer = GetIndexer(name); - var configPath = GetIndexerConfigFilePath(indexer); - File.Delete(configPath); - Indexers.Remove(name); - LoadMissingIndexers(); - } - - public async Task TestIndexer(IndexerInterface indexer) - { - var browseQuery = new TorznabQuery(); - var results = await indexer.PerformQuery(browseQuery); - Program.LoggerInstance.Debug(string.Format("Found {0} releases from {1}", results.Length, indexer.DisplayName)); - if (results.Length == 0) - throw new Exception("Found no results while trying to browse this tracker"); - - } - - } -} diff --git a/src/Jackett/Indexers/AlphaRatio.cs b/src/Jackett/Indexers/AlphaRatio.cs index fe92ecbbd..8cf4eaf78 100644 --- a/src/Jackett/Indexers/AlphaRatio.cs +++ b/src/Jackett/Indexers/AlphaRatio.cs @@ -9,45 +9,19 @@ using System.Text; using System.Threading.Tasks; using System.Web; using System.Net.Http.Headers; +using Jackett.Models; +using Jackett.Utils; +using NLog; +using Jackett.Services; namespace Jackett.Indexers { - public class AlphaRatio : IndexerInterface + public class AlphaRatio : BaseIndexer, IIndexer { - public string DisplayName - { - get { return "AlphaRatio"; } - } - - public string DisplayDescription - { - get { return "Legendary"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - - public event Action OnSaveConfigurationRequested; - public event Action OnResultParsingError; - - public bool IsConfigured { get; private set; } - - static string BaseUrl = "https://alpharatio.cc"; - - static string LoginUrl = BaseUrl + "/login.php"; - - static string SearchUrl = BaseUrl + "/ajax.php?action=browse&searchstr="; - - static string DownloadUrl = BaseUrl + "/torrents.php?action=download&id="; - - static string GuidUrl = BaseUrl + "/torrents.php?torrentid="; - - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; + private readonly string DownloadUrl = ""; + private readonly string GuidUrl = ""; CookieContainer cookies; HttpClientHandler handler; @@ -55,9 +29,19 @@ namespace Jackett.Indexers string cookieHeader; - public AlphaRatio() + public AlphaRatio(IIndexerManagerService i, Logger l) + : base(name: "AlphaRatio", + description: "Legendary", + link: new Uri("https://alpharatio.cc"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/login.php"; + SearchUrl = SiteLink + "/ajax.php?action=browse&searchstr="; + DownloadUrl = SiteLink + "/torrents.php?action=download&id="; + GuidUrl = SiteLink + "/torrents.php?torrentid="; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -78,8 +62,7 @@ namespace Jackett.Indexers public async Task ApplyConfiguration(JToken configJson) { var configSaveData = new JObject(); - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); + SaveConfig(configSaveData); var config = new ConfigurationDataBasicLogin(); config.LoadValuesFromJson(configJson); @@ -100,7 +83,7 @@ namespace Jackett.Indexers configSaveData = new JObject(); - if (Program.IsWindows) + if (Engine.IsWindows) { // If Windows use .net http var response = await client.SendAsync(message); @@ -126,9 +109,7 @@ namespace Jackett.Indexers } else { - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } @@ -138,13 +119,13 @@ namespace Jackett.Indexers var message = new HttpRequestMessage(); message.Method = HttpMethod.Post; message.RequestUri = uri; - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + message.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); return message; } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(SiteLink, jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); cookieHeader = cookies.GetCookieHeader(SiteLink); IsConfigured = true; } @@ -167,7 +148,7 @@ namespace Jackett.Indexers var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString); string results; - if (Program.IsWindows) + if (Engine.IsWindows) { var request = CreateHttpRequest(new Uri(episodeSearchUrl)); request.Method = HttpMethod.Get; @@ -188,7 +169,7 @@ namespace Jackett.Indexers DateTime pubDate = DateTime.MinValue; double dateNum; if (double.TryParse((string)r["groupTime"], out dateNum)) - pubDate = DateTimeUtil.UnixTimestampToDateTime(dateNum); + pubDate = UnixTimestampToDateTime(dateNum); var groupName = (string)r["groupName"]; @@ -218,16 +199,22 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); } + static DateTime UnixTimestampToDateTime(double unixTime) + { + DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); + long unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond); + return new DateTime(unixStart.Ticks + unixTimeStampInTicks); + } + public async Task Download(Uri link) { - if (Program.IsWindows) + if (Engine.IsWindows) { return await client.GetByteArrayAsync(link); } diff --git a/src/Jackett/Indexers/AnimeBytes.cs b/src/Jackett/Indexers/AnimeBytes.cs index 739b45611..b65fe26c7 100644 --- a/src/Jackett/Indexers/AnimeBytes.cs +++ b/src/Jackett/Indexers/AnimeBytes.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -15,7 +19,7 @@ using System.Web; namespace Jackett.Indexers { - public class AnimeBytes : IndexerInterface + public class AnimeBytes : BaseIndexer, IIndexer { class ConfigurationDataBasicLoginAnimeBytes : ConfigurationDataBasicLogin { @@ -35,46 +39,28 @@ namespace Jackett.Indexers } } - private static List cache = new List(); - private static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0); - public event Action OnResultParsingError; - public event Action OnSaveConfigurationRequested; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; - - public string DisplayName - { - get { return "AnimeBytes"; } - } - - public string DisplayDescription - { - get { return "The web's best Chinese cartoons"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - const string BaseUrl = "https://animebytes.tv"; - const string LoginUrl = BaseUrl + "/user/login"; - const string SearchUrl = BaseUrl + "/torrents.php?filter_cat[1]=1"; - - public bool IsConfigured { get; private set; } public bool AllowRaws { get; private set; } - - CookieContainer cookieContainer; HttpClientHandler handler; HttpClient client; - public AnimeBytes() + public AnimeBytes(IIndexerManagerService i, Logger l) + : base(name: "AnimeBytes", + description: "The web's best Chinese cartoons", + link: new Uri("https://animebytes.tv"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + TorznabCaps.Categories.Clear(); + TorznabCaps.Categories.Add(new TorznabCategory { ID = "5070", Name = "TV/Anime" }); + LoginUrl = SiteLink + "/user/login"; + SearchUrl = SiteLink + "/torrents.php?filter_cat[1]=1"; + cookieContainer = new CookieContainer(); handler = new HttpClientHandler { @@ -83,7 +69,7 @@ namespace Jackett.Indexers UseCookies = true, }; client = new HttpClient(handler); - client.DefaultRequestHeaders.Add("User-Agent", chromeUserAgent); + client.DefaultRequestHeaders.Add("User-Agent", BrowserUtil.ChromeUserAgent); } public Task GetConfigurationForSetup() @@ -127,11 +113,11 @@ namespace Jackett.Indexers { foreach (var c in cookies) { - cookieContainer.SetCookies(new Uri(BaseUrl), c.Substring(0, c.LastIndexOf(';'))); + cookieContainer.SetCookies(SiteLink, c.Substring(0, c.LastIndexOf(';'))); } } - foreach (Cookie cookie in cookieContainer.GetCookies(new Uri(BaseUrl))) + foreach (Cookie cookie in cookieContainer.GetCookies(SiteLink)) { if (cookie.Name == "session") { @@ -141,7 +127,7 @@ namespace Jackett.Indexers } // Get the home page now we are logged in as AllowAutoRedirect is false as we needed to get the cookie manually. - response = await client.GetAsync(BaseUrl); + response = await client.GetAsync(SiteLink); responseContent = await response.Content.ReadAsStringAsync(); if (!responseContent.Contains("/user/logout")) @@ -155,16 +141,14 @@ namespace Jackett.Indexers cookieContainer.DumpToJson(SiteLink, configSaveData); configSaveData["raws"] = AllowRaws; - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookieContainer.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookieContainer.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; AllowRaws = jsonConfig["raws"].Value(); } @@ -186,13 +170,7 @@ namespace Jackett.Indexers return sb.ToString(); } - private void CleanCache() - { - foreach (var expired in cache.Where(i => i.Created - DateTime.Now > cacheTime).ToList()) - { - cache.Remove(expired); - } - } + public async Task PerformQuery(TorznabQuery query) @@ -339,9 +317,9 @@ namespace Jackett.Indexers release.PublishDate = release.PublishDate.AddDays(Math.Min(DateTime.Now.DayOfYear, 365) - 1); var infoLink = links.Get(1); - release.Comments = new Uri(BaseUrl + "/" + infoLink.Attributes.GetAttribute("href")); - release.Guid = new Uri(BaseUrl + "/" + infoLink.Attributes.GetAttribute("href") + "&nh=" + Hash(title)); // Sonarr should dedupe on this url - allow a url per name. - release.Link = new Uri(BaseUrl + "/" + downloadLink.Attributes.GetAttribute("href")); + release.Comments = new Uri(SiteLink + "/" + infoLink.Attributes.GetAttribute("href")); + release.Guid = new Uri(SiteLink + "/" + infoLink.Attributes.GetAttribute("href") + "&nh=" + Hash(title)); // Sonarr should dedupe on this url - allow a url per name. + release.Link = new Uri(SiteLink + "/" + downloadLink.Attributes.GetAttribute("href")); // We dont actually have a release name >.> so try to create one var releaseTags = infoLink.InnerText.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList(); @@ -401,8 +379,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, responseContent, ex); - throw ex; + OnParseError(responseContent, ex); } // Add to the cache diff --git a/src/Jackett/Indexers/BB.cs b/src/Jackett/Indexers/BB.cs new file mode 100644 index 000000000..e23c56f82 --- /dev/null +++ b/src/Jackett/Indexers/BB.cs @@ -0,0 +1,179 @@ +using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; +using Newtonsoft.Json.Linq; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace Jackett.Indexers +{ + // To comply with the rules for this tracker, only the acronym is used and no publicly displayed URLs to the site. + + public class BB : BaseIndexer, IIndexer + { + private readonly string BaseUrl = ""; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; + + CookieContainer cookies; + HttpClientHandler handler; + HttpClient client; + + public BB(IIndexerManagerService i, Logger l) + : base(name: "bB", + description: "bB", + link: new Uri("http://www.reddit.com/r/baconbits"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) + { + + BaseUrl = StringUtil.FromBase64("aHR0cHM6Ly9iYWNvbmJpdHMub3Jn"); + LoginUrl = BaseUrl + "/login.php"; + SearchUrl = BaseUrl + "/torrents.php?searchstr={0}&searchtags=&tags_type=0&order_by=s3&order_way=desc&disablegrouping=1&filter_cat%5B10%5D=1"; + cookies = new CookieContainer(); + handler = new HttpClientHandler + { + CookieContainer = cookies, + AllowAutoRedirect = true, + UseCookies = true, + }; + client = new HttpClient(handler); + } + + public Task GetConfigurationForSetup() + { + var config = new ConfigurationDataBasicLogin(); + return Task.FromResult(config); + } + + public async Task ApplyConfiguration(JToken configJson) + { + var config = new ConfigurationDataBasicLogin(); + config.LoadValuesFromJson(configJson); + + var pairs = new Dictionary { + { "username", config.Username.Value }, + { "password", config.Password.Value }, + { "keeplogged", "1" }, + { "login", "Log In!" } + }; + + var content = new FormUrlEncodedContent(pairs); + var response = await client.PostAsync(LoginUrl, content); + var responseContent = await response.Content.ReadAsStringAsync(); + + if (!responseContent.Contains("logout.php")) + { + CQ dom = responseContent; + var messageEl = dom["#loginform"]; + var messages = new List(); + for (var i = 0; i < 13; i++) + { + var child = messageEl[0].ChildNodes[i]; + messages.Add(child.Cq().Text().Trim()); + } + var message = string.Join(" ", messages); + throw new ExceptionWithConfigData(message, (ConfigurationData)config); + } + else + { + + var configSaveData = new JObject(); + cookies.DumpToJson(BaseUrl, configSaveData); + SaveConfig(configSaveData); + IsConfigured = true; + } + + } + + public void LoadFromSavedConfiguration(JToken jsonConfig) + { + cookies.FillFromJson(new Uri(BaseUrl), jsonConfig, logger); + IsConfigured = true; + } + + public async Task PerformQuery(TorznabQuery query) + { + List releases = new List(); + + var searchString = query.SanitizedSearchTerm + " " + query.GetEpisodeSearchString(); + var episodeSearchUrl = string.Format(SearchUrl, HttpUtility.UrlEncode(searchString)); + var results = await client.GetStringAsync(episodeSearchUrl); + try + { + CQ dom = results; + var rows = dom["#torrent_table > tbody > tr.torrent"]; + foreach (var row in rows) + { + CQ qRow = row.Cq(); + var release = new ReleaseInfo(); + + release.MinimumRatio = 1; + release.MinimumSeedTime = 172800; + + var qLink = row.ChildElements.ElementAt(1).Cq().Children("a")[0].Cq(); + var linkStr = qLink.Attr("href"); + release.Title = qLink.Text(); + release.Comments = new Uri(BaseUrl + "/" + linkStr); + release.Guid = release.Comments; + + var qDownload = row.ChildElements.ElementAt(1).Cq().Find("a[title='Download']")[0].Cq(); + release.Link = new Uri(BaseUrl + "/" + qDownload.Attr("href")); + + var dateStr = row.ChildElements.ElementAt(3).Cq().Text().Trim().Replace(" and", ""); + var dateParts = dateStr.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + TimeSpan timeAgo = TimeSpan.Zero; + for (var i = 0; i < dateParts.Length / 2; i++) + { + var val = ParseUtil.CoerceInt(dateParts[i * 2]); + var unit = dateParts[i * 2 + 1]; + if (unit.Contains("sec")) + timeAgo += TimeSpan.FromSeconds(val); + else if (unit.Contains("min")) + timeAgo += TimeSpan.FromMinutes(val); + else if (unit.Contains("hour")) + timeAgo += TimeSpan.FromHours(val); + else if (unit.Contains("day")) + timeAgo += TimeSpan.FromDays(val); + else if (unit.Contains("week")) + timeAgo += TimeSpan.FromDays(val * 7); + else if (unit.Contains("month")) + timeAgo += TimeSpan.FromDays(val * 30); + else if (unit.Contains("year")) + timeAgo += TimeSpan.FromDays(val * 365); + } + release.PublishDate = DateTime.SpecifyKind(DateTime.Now - timeAgo, DateTimeKind.Local); + + var sizeStr = row.ChildElements.ElementAt(4).Cq().Text().Trim(); + var sizeParts = sizeStr.Split(' '); + release.Size = ReleaseInfo.GetBytes(sizeParts[1], ParseUtil.CoerceFloat(sizeParts[0])); + + release.Seeders = ParseUtil.CoerceInt(row.ChildElements.ElementAt(7).Cq().Text().Trim()); + release.Peers = ParseUtil.CoerceInt(row.ChildElements.ElementAt(8).Cq().Text().Trim()) + release.Seeders; + + releases.Add(release); + } + } + catch (Exception ex) + { + OnParseError(results, ex); + } + return releases.ToArray(); + } + + public Task Download(Uri link) + { + return client.GetByteArrayAsync(link); + } + + } +} diff --git a/src/Jackett/Indexers/BaseIndexer.cs b/src/Jackett/Indexers/BaseIndexer.cs new file mode 100644 index 000000000..68b17033f --- /dev/null +++ b/src/Jackett/Indexers/BaseIndexer.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Jackett.Models; +using Newtonsoft.Json.Linq; +using NLog; +using Jackett.Services; +using Jackett.Utils; + +namespace Jackett.Indexers +{ + public abstract class BaseIndexer + { + public string DisplayDescription { get; private set; } + public string DisplayName { get; private set; } + public string ID { get { return GetIndexerID(GetType()); } } + + public bool IsConfigured { get; protected set; } + public Uri SiteLink { get; private set; } + + public TorznabCapabilities TorznabCaps { get; private set; } + + protected Logger logger; + protected IIndexerManagerService indexerService; + + protected static List cache = new List(); + protected static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0); + + public static string GetIndexerID(Type type) + { + return StringUtil.StripNonAlphaNumeric(type.Name.ToLowerInvariant()); + } + + public BaseIndexer(string name, string description, Uri link, TorznabCapabilities caps, IIndexerManagerService manager, Logger logger) + { + DisplayName = name; + DisplayDescription = description; + SiteLink = link; + TorznabCaps = caps; + this.logger = logger; + indexerService = manager; + } + + protected void SaveConfig(JToken config) + { + indexerService.SaveConfig(this as IIndexer, config); + } + + protected void OnParseError(string results, Exception ex) + { + var fileName = string.Format("Error on {0} for {1}.txt", DateTime.Now.ToString("yyyyMMddHHmmss"), DisplayName); + var spacing = string.Join("", Enumerable.Repeat(Environment.NewLine, 5)); + var fileContents = string.Format("{0}{1}{2}", ex, spacing, results); + logger.Error(fileName + fileContents); + throw ex; + } + + protected void CleanCache() + { + foreach (var expired in cache.Where(i => i.Created - DateTime.Now > cacheTime).ToList()) + { + cache.Remove(expired); + } + } + } +} diff --git a/src/Jackett/Indexers/BeyondHD.cs b/src/Jackett/Indexers/BeyondHD.cs index 25293f406..b8e082d00 100644 --- a/src/Jackett/Indexers/BeyondHD.cs +++ b/src/Jackett/Indexers/BeyondHD.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Linq; @@ -11,42 +15,26 @@ using System.Web; namespace Jackett.Indexers { - public class BeyondHD : IndexerInterface + public class BeyondHD : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "BeyondHD"; } - } - - public string DisplayDescription - { - get { return "Without BeyondHD, your HDTV is just a TV"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - - const string BaseUrl = "https://beyondhd.me"; - const string SearchUrl = BaseUrl + "/browse.php?c40=1&c44=1&c48=1&c89=1&c46=1&c45=1&searchin=title&incldead=0&search={0}"; - const string DownloadUrl = BaseUrl + "/download.php?torrent={0}"; + private readonly string SearchUrl = ""; + private readonly string DownloadUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public BeyondHD() + public BeyondHD(IIndexerManagerService i, Logger l) + : base(name: "BeyondHD", + description: "Without BeyondHD, your HDTV is just a TV", + link: new Uri("https://beyondhd.me"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + SearchUrl = SiteLink + "/browse.php?c40=1&c44=1&c48=1&c89=1&c46=1&c45=1&searchin=title&incldead=0&search={0}"; + DownloadUrl = SiteLink + "/download.php?torrent={0}"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -70,9 +58,9 @@ namespace Jackett.Indexers var jsonCookie = new JObject(); jsonCookie["cookie_header"] = config.CookieHeader; - cookies.FillFromJson(new Uri(BaseUrl), jsonCookie); + cookies.FillFromJson(SiteLink, jsonCookie, logger); - var responseContent = await client.GetStringAsync(BaseUrl); + var responseContent = await client.GetStringAsync(SiteLink); if (!responseContent.Contains("logout.php")) { @@ -83,18 +71,14 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } - } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -119,14 +103,14 @@ namespace Jackett.Indexers var qRow = row.Cq(); var qLink = row.ChildElements.ElementAt(2).FirstChild.Cq(); - release.Link = new Uri(BaseUrl + "/" + qLink.Attr("href")); + release.Link = new Uri(SiteLink + "/" + qLink.Attr("href")); var torrentID = qLink.Attr("href").Split('=').Last(); var descCol = row.ChildElements.ElementAt(3); var qCommentLink = descCol.FirstChild.Cq(); release.Title = qCommentLink.Text(); release.Description = release.Title; - release.Comments = new Uri(BaseUrl + "/" + qCommentLink.Attr("href")); + release.Comments = new Uri(SiteLink + "/" + qCommentLink.Attr("href")); release.Guid = release.Comments; var dateStr = descCol.ChildElements.Last().Cq().Text().Split('|').Last().ToLowerInvariant().Replace("ago.", "").Trim(); @@ -162,11 +146,9 @@ namespace Jackett.Indexers } } - catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); diff --git a/src/Jackett/Indexers/BitHdtv.cs b/src/Jackett/Indexers/BitHdtv.cs index 86a8f213c..76293c971 100644 --- a/src/Jackett/Indexers/BitHdtv.cs +++ b/src/Jackett/Indexers/BitHdtv.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,39 +16,28 @@ using System.Web; namespace Jackett.Indexers { - public class BitHdtv : IndexerInterface + public class BitHdtv : BaseIndexer, IIndexer { - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "BIT-HDTV"; } - } - - public string DisplayDescription - { - get { return "Home of high definition invites"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - static string BaseUrl = "https://www.bit-hdtv.com"; - static string LoginUrl = BaseUrl + "/takelogin.php"; - static string SearchUrl = BaseUrl + "/torrents.php?cat=0&search="; - static string DownloadUrl = BaseUrl + "/download.php?/{0}/dl.torrent"; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; + private readonly string DownloadUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public BitHdtv() + public BitHdtv(IIndexerManagerService i, Logger l) + : base(name: "BIT-HDTV", + description: "Home of high definition invites", + link: new Uri("https://www.bit-hdtv.com"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/takelogin.php"; + SearchUrl = SiteLink + "/torrents.php?cat=0&search="; + DownloadUrl = SiteLink + "/download.php?/{0}/dl.torrent"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -89,21 +82,14 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } - public event Action OnSaveConfigurationRequested; - - public bool IsConfigured { get; private set; } - public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -131,7 +117,7 @@ namespace Jackett.Indexers release.MinimumSeedTime = 172800; release.Title = qLink.Attr("title"); release.Description = release.Title; - release.Guid = new Uri(BaseUrl + qLink.Attr("href")); + release.Guid = new Uri(SiteLink + qLink.Attr("href")); release.Comments = release.Guid; release.Link = new Uri(string.Format(DownloadUrl, qLink.Attr("href").Split('=')[1])); @@ -152,8 +138,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); @@ -163,7 +148,5 @@ namespace Jackett.Indexers { return client.GetByteArrayAsync(link); } - - } } diff --git a/src/Jackett/Indexers/BitMeTV.cs b/src/Jackett/Indexers/BitMeTV.cs index 42d6f2bfc..eb53a887e 100644 --- a/src/Jackett/Indexers/BitMeTV.cs +++ b/src/Jackett/Indexers/BitMeTV.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -11,9 +15,9 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; -namespace Jackett +namespace Jackett.Indexers { - public class BitMeTV : IndexerInterface + public class BitMeTV : BaseIndexer, IIndexer { class BmtvConfig : ConfigurationData { @@ -39,22 +43,28 @@ namespace Jackett } } - static string BaseUrl = "http://www.bitmetv.org"; - static string LoginUrl = BaseUrl + "/login.php"; - static string LoginPost = BaseUrl + "/takelogin.php"; - static string CaptchaUrl = BaseUrl + "/visual.php"; - static string SearchUrl = BaseUrl + "/browse.php"; + private readonly string LoginUrl = ""; + private readonly string LoginPost = ""; + private readonly string CaptchaUrl = ""; + private readonly string SearchUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public event Action OnSaveConfigurationRequested; - public event Action OnResultParsingError; - - public BitMeTV() + public BitMeTV(IIndexerManagerService i, Logger l) + : base(name: "BitMeTV", + description: "TV Episode specialty tracker", + link: new Uri("http://www.bitmetv.org"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/login.php"; + LoginPost = SiteLink + "/takelogin.php"; + CaptchaUrl = SiteLink + "/visual.php"; + SearchUrl = SiteLink + "/browse.php"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -65,16 +75,6 @@ namespace Jackett client = new HttpClient(handler); } - public string DisplayName { get { return "BitMeTV"; } } - - public string DisplayDescription { get { return "TV Episode specialty tracker"; } } - - public Uri SiteLink { get { return new Uri(BaseUrl); } } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - public async Task GetConfigurationForSetup() { await client.GetAsync(LoginUrl); @@ -114,17 +114,14 @@ namespace Jackett { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -150,7 +147,7 @@ namespace Jackett release.MinimumRatio = 1; release.MinimumSeedTime = 172800; - release.Comments = new Uri(BaseUrl + "/" + qLink.Attr("href")); + release.Comments = new Uri(SiteLink + "/" + qLink.Attr("href")); release.Guid = release.Comments; release.Title = qLink.Attr("title"); release.Description = release.Title; @@ -164,7 +161,7 @@ namespace Jackett var date = DateTime.ParseExact(formattedTimeString, "dddd MMMM d yyyy hh:mm:ss tt", CultureInfo.InvariantCulture); release.PublishDate = DateTime.SpecifyKind(date, DateTimeKind.Utc).ToLocalTime(); - release.Link = new Uri(BaseUrl + "/" + row.ChildElements.ElementAt(2).Cq().Children("a.index").Attr("href")); + release.Link = new Uri(SiteLink + "/" + row.ChildElements.ElementAt(2).Cq().Children("a.index").Attr("href")); var sizeCol = row.ChildElements.ElementAt(6); var sizeVal = ParseUtil.CoerceFloat(sizeCol.ChildNodes[0].NodeValue); @@ -182,8 +179,7 @@ namespace Jackett } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); diff --git a/src/Jackett/Indexers/FrenchTorrentDb.cs b/src/Jackett/Indexers/FrenchTorrentDb.cs index 6eae903c6..128b0e674 100644 --- a/src/Jackett/Indexers/FrenchTorrentDb.cs +++ b/src/Jackett/Indexers/FrenchTorrentDb.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -10,11 +14,11 @@ using System.Web; namespace Jackett.Indexers { - class FrenchTorrentDb : IndexerInterface + class FrenchTorrentDb : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; + public event Action OnSaveConfigurationRequested; - public event Action OnResultParsingError; + public event Action OnResultParsingError; class ConfigurationDataBasicLoginFrenchTorrentDb : ConfigurationData { @@ -31,28 +35,9 @@ namespace Jackett.Indexers } } - public string DisplayName - { - get { return "FrenchTorrentDb"; } - } - public string DisplayDescription - { - get { return "One the biggest French Torrent Tracker"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - const string BaseUrl = "http://www.frenchtorrentdb.com/"; - const string MainUrl = BaseUrl + "?section=INDEX"; - const string SearchUrl = BaseUrl + "?section=TORRENTS&exact=1&name={0}&submit=GO"; - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; + private readonly string MainUrl = ""; + private readonly string SearchUrl = ""; string cookie = string.Empty; @@ -60,9 +45,17 @@ namespace Jackett.Indexers HttpClientHandler handler; HttpClient client; - public FrenchTorrentDb() + public FrenchTorrentDb(IIndexerManagerService i, Logger l) + : base(name: "FrenchTorrentDb", + description: "One the biggest French Torrent Tracker", + link: new Uri("http://www.frenchtorrentdb.com/"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + MainUrl = SiteLink + "?section=INDEX"; + SearchUrl = SiteLink + "?section=TORRENTS&exact=1&name={0}&submit=GO"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -71,12 +64,12 @@ namespace Jackett.Indexers UseCookies = true, }; client = new HttpClient(handler); - client.DefaultRequestHeaders.UserAgent.ParseAdd(chromeUserAgent); + client.DefaultRequestHeaders.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); } public Task GetConfigurationForSetup() { - var config = new ConfigurationDataUrl(BaseUrl); + var config = new ConfigurationDataUrl(SiteLink); return Task.FromResult(config); } @@ -84,7 +77,7 @@ namespace Jackett.Indexers { var config = new ConfigurationDataBasicLoginFrenchTorrentDb(); config.LoadValuesFromJson(configJson); - cookies.SetCookies(new Uri(BaseUrl), "WebsiteID=" + config.Cookie.Value); + cookies.SetCookies(SiteLink, "WebsiteID=" + config.Cookie.Value); var mainPage = await client.GetAsync(MainUrl); string responseContent = await mainPage.Content.ReadAsStringAsync(); @@ -107,7 +100,7 @@ namespace Jackett.Indexers public void LoadFromSavedConfiguration(Newtonsoft.Json.Linq.JToken jsonConfig) { cookie = (string)jsonConfig["cookie"]; - cookies.SetCookies(new Uri(BaseUrl), "WebsiteID=" + cookie); + cookies.SetCookies(SiteLink, "WebsiteID=" + cookie); IsConfigured = true; } @@ -140,9 +133,9 @@ namespace Jackett.Indexers release.MinimumSeedTime = 172800; release.Title = qLink.Text().Trim(); release.Description = release.Title; - release.Comments = new Uri(BaseUrl + "/" + qLink.Attr("href").TrimStart('/')); + release.Comments = new Uri(SiteLink + "/" + qLink.Attr("href").TrimStart('/')); release.Guid = release.Comments; - release.Link = new Uri(BaseUrl + "/" + qDlLink.Attr("href").TrimStart('/')); + release.Link = new Uri(SiteLink + "/" + qDlLink.Attr("href").TrimStart('/')); release.PublishDate = DateTime.Now; release.Seeders = ParseUtil.CoerceInt(qRow.Find("li.torrents_seeders").Text()); release.Peers = ParseUtil.CoerceInt(qRow.Find("li.torrents_leechers").Text()) + release.Seeders; diff --git a/src/Jackett/Indexers/Freshon.cs b/src/Jackett/Indexers/Freshon.cs index e2b18cc78..ca2a5ad03 100644 --- a/src/Jackett/Indexers/Freshon.cs +++ b/src/Jackett/Indexers/Freshon.cs @@ -1,5 +1,10 @@ using CsQuery; +using Jackett.Indexers; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,38 +17,31 @@ using System.Threading.Tasks; using System.Web; using System.Web.UI.WebControls; -namespace Jackett +namespace Jackett.Indexers { - public class Freshon : IndexerInterface + public class Freshon : BaseIndexer, IIndexer { - public event Action OnResultParsingError; - - static string BaseUrl = "https://freshon.tv"; - static string LoginUrl = BaseUrl + "/login.php"; - static string LoginPostUrl = BaseUrl + "/login.php?action=makelogin"; - static string SearchUrl = BaseUrl + "/browse.php"; - - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; + private readonly string LoginUrl = ""; + private readonly string LoginPostUrl = ""; + private readonly string SearchUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public bool IsConfigured { get; private set; } - - public string DisplayName { get { return "FreshOnTV"; } } - - public string DisplayDescription { get { return "Our goal is to provide the latest stuff in the TV show domain"; } } - - public Uri SiteLink { get { return new Uri(BaseUrl); } } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public event Action OnSaveConfigurationRequested; - - public Freshon() + public Freshon(IIndexerManagerService i, Logger l) + : base(name: "FreshOnTV", + description: "Our goal is to provide the latest stuff in the TV show domain", + link: new Uri("https://www.bit-hdtv.com"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + + LoginUrl = SiteLink + "/login.php"; + LoginPostUrl = SiteLink + "/login.php?action=makelogin"; + SearchUrl = SiteLink + "/browse.php"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -93,17 +91,14 @@ namespace Jackett { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -112,7 +107,7 @@ namespace Jackett var message = new HttpRequestMessage(); message.Method = HttpMethod.Get; message.RequestUri = uri; - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + message.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); return message; } @@ -150,9 +145,9 @@ namespace Jackett release.MinimumSeedTime = 172800; release.Title = qLink.Attr("title"); release.Description = release.Title; - release.Guid = new Uri(BaseUrl + qLink.Attr("href")); + release.Guid = new Uri(SiteLink + qLink.Attr("href")); release.Comments = release.Guid; - release.Link = new Uri(BaseUrl + qRow.Find("td.table_links > a").First().Attr("href")); + release.Link = new Uri(SiteLink + qRow.Find("td.table_links > a").First().Attr("href")); DateTime pubDate; var dateString = qRow.Find("td.table_added").Text().Trim(); @@ -177,8 +172,7 @@ namespace Jackett } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); diff --git a/src/Jackett/Indexers/HDTorrents.cs b/src/Jackett/Indexers/HDTorrents.cs index 991d78d7e..73ccdb38e 100644 --- a/src/Jackett/Indexers/HDTorrents.cs +++ b/src/Jackett/Indexers/HDTorrents.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,26 +16,27 @@ using System.Web; namespace Jackett.Indexers { - public class HDTorrents : IndexerInterface + public class HDTorrents : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - const string DefaultUrl = "http://hdts.ru"; // Of the accessible domains the .ru seems the most reliable. https://hdts.ru | https://hd-torrents.org | https://hd-torrents.net | https://hd-torrents.me - string BaseUrl = DefaultUrl; - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; - private string SearchUrl = DefaultUrl + "/torrents.php?search={0}&active=1&options=0&category%5B%5D=59&category%5B%5D=60&category%5B%5D=30&category%5B%5D=38&page={1}"; - private static string LoginUrl = DefaultUrl + "/login.php"; + private readonly string SearchUrl = ""; + private static string LoginUrl = ""; private const int MAXPAGES = 3; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public HDTorrents() + public HDTorrents(IIndexerManagerService i, Logger l) + : base(name: "HD-Torrents", + description: "HD-Torrents is a private torrent website with HD torrents and strict rules on their content.", + link: new Uri("http://hdts.ru"),// Of the accessible domains the .ru seems the most reliable. https://hdts.ru | https://hd-torrents.org | https://hd-torrents.net | https://hd-torrents.me + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + SearchUrl = SiteLink + "/torrents.php?search={0}&active=1&options=0&category%5B%5D=59&category%5B%5D=60&category%5B%5D=30&category%5B%5D=38&page={1}"; + LoginUrl = SiteLink + "/login.php"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -42,29 +47,6 @@ namespace Jackett.Indexers client = new HttpClient(handler); } - public string DisplayName - { - get { return "HD-Torrents"; } - } - - public string DisplayDescription - { - get { return "HD-Torrents is a private torrent website with HD torrents and strict rules on their content."; } - } - - public Uri SiteLink - { - get { return new Uri(DefaultUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured - { - get; - private set; - } - public Task GetConfigurationForSetup() { var config = new ConfigurationDataBasicLogin(); @@ -76,7 +58,7 @@ namespace Jackett.Indexers var message = new HttpRequestMessage(); message.Method = HttpMethod.Get; message.RequestUri = new Uri(url); - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + message.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); return message; } @@ -113,21 +95,18 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(SiteLink, jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } - async Task PerformQuery(TorznabQuery query, string baseUrl) + async Task PerformQuery(TorznabQuery query, Uri baseUrl) { List releases = new List(); List searchurls = new List(); @@ -207,9 +186,9 @@ namespace Jackett.Indexers } release.Size = size; - release.Guid = new Uri(DefaultUrl + "/" + qRow.Find("td.mainblockcontent b a").Attr("href")); - release.Link = new Uri(DefaultUrl + "/" + qRow.Find("td.mainblockcontent").Get(3).FirstChild.GetAttribute("href")); - release.Comments = new Uri(DefaultUrl + "/" + qRow.Find("td.mainblockcontent b a").Attr("href") + "#comments"); + release.Guid = new Uri(SiteLink + "/" + qRow.Find("td.mainblockcontent b a").Attr("href")); + release.Link = new Uri(SiteLink + "/" + qRow.Find("td.mainblockcontent").Get(3).FirstChild.GetAttribute("href")); + release.Comments = new Uri(SiteLink + "/" + qRow.Find("td.mainblockcontent b a").Attr("href") + "#comments"); string[] dateSplit = qRow.Find("td.mainblockcontent").Get(5).InnerHTML.Split(','); string dateString = dateSplit[1].Substring(0, dateSplit[1].IndexOf('>')); @@ -220,18 +199,16 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } } - return releases.ToArray(); } public async Task PerformQuery(TorznabQuery query) { - return await PerformQuery(query, BaseUrl); + return await PerformQuery(query, SiteLink); } public Task Download(Uri link) diff --git a/src/Jackett/IndexerInterface.cs b/src/Jackett/Indexers/IIndexer.cs similarity index 71% rename from src/Jackett/IndexerInterface.cs rename to src/Jackett/Indexers/IIndexer.cs index abbc9e8f0..cf6ba4c74 100644 --- a/src/Jackett/IndexerInterface.cs +++ b/src/Jackett/Indexers/IIndexer.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json.Linq; +using Jackett.Models; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; @@ -6,21 +7,17 @@ using System.Text; using System.Threading.Tasks; using System.Web.UI.WebControls; -namespace Jackett +namespace Jackett.Indexers { - public interface IndexerInterface + public interface IIndexer { - - // Invoked when the indexer configuration has been applied and verified so the cookie needs to be saved - event Action OnSaveConfigurationRequested; - - event Action OnResultParsingError; - string DisplayName { get; } string DisplayDescription { get; } + string ID { get; } + Uri SiteLink { get; } - bool RequiresRageIDLookupDisabled { get; } + TorznabCapabilities TorznabCaps { get; } // Whether this indexer has been configured, verified and saved in the past and has the settings required for functioning bool IsConfigured { get; } diff --git a/src/Jackett/Indexers/IPTorrents.cs b/src/Jackett/Indexers/IPTorrents.cs index b374366b3..23b26745a 100644 --- a/src/Jackett/Indexers/IPTorrents.cs +++ b/src/Jackett/Indexers/IPTorrents.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,36 +16,22 @@ using System.Web; namespace Jackett.Indexers { - public class IPTorrents : IndexerInterface + public class IPTorrents : BaseIndexer, IIndexer { - - public event Action OnSaveConfigurationRequested; - public event Action OnResultParsingError; - - public string DisplayName { get { return "IPTorrents"; } } - - public string DisplayDescription { get { return "Always a step ahead"; } } - - public Uri SiteLink { get { return new Uri(BaseUrl); } } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; - - static string BaseUrl = "https://iptorrents.com"; - - string SearchUrl = BaseUrl + "/t?q="; - - + private readonly string SearchUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public IPTorrents() + public IPTorrents(IIndexerManagerService i, Logger l) + : base(name: "IPTorrents", + description: "Always a step ahead.", + link: new Uri("https://iptorrents.com"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + SearchUrl = SiteLink + "t?q="; cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -54,7 +44,7 @@ namespace Jackett.Indexers public async Task GetConfigurationForSetup() { - await client.GetAsync(new Uri(BaseUrl)); + await client.GetAsync(SiteLink); var config = new ConfigurationDataBasicLogin(); return (ConfigurationData)config; } @@ -74,9 +64,9 @@ namespace Jackett.Indexers var message = new HttpRequestMessage(); message.Method = HttpMethod.Post; message.Content = content; - message.RequestUri = new Uri(BaseUrl); - message.Headers.Referrer = new Uri(BaseUrl); - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + message.RequestUri = SiteLink; + message.Headers.Referrer = SiteLink; + message.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); var response = await client.SendAsync(message); var responseContent = await response.Content.ReadAsStringAsync(); @@ -92,13 +82,9 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } - } HttpRequestMessage CreateHttpRequest(Uri uri) @@ -106,22 +92,19 @@ namespace Jackett.Indexers var message = new HttpRequestMessage(); message.Method = HttpMethod.Get; message.RequestUri = uri; - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + message.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); return message; } public void LoadFromSavedConfiguration(Newtonsoft.Json.Linq.JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } public async Task PerformQuery(TorznabQuery query) { - - List releases = new List(); - - + var releases = new List(); var searchString = query.SanitizedSearchTerm + " " + query.GetEpisodeSearchString(); var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString); @@ -143,7 +126,7 @@ namespace Jackett.Indexers var qTitleLink = qRow.Find("a.t_title").First(); release.Title = qTitleLink.Text().Trim(); release.Description = release.Title; - release.Guid = new Uri(BaseUrl + qTitleLink.Attr("href")); + release.Guid = new Uri(SiteLink + qTitleLink.Attr("href")); release.Comments = release.Guid; DateTime pubDate; @@ -169,7 +152,7 @@ namespace Jackett.Indexers release.PublishDate = pubDate; var qLink = row.ChildElements.ElementAt(3).Cq().Children("a"); - release.Link = new Uri(BaseUrl + qLink.Attr("href")); + release.Link = new Uri(SiteLink + qLink.Attr("href")); var sizeStr = row.ChildElements.ElementAt(5).Cq().Text().Trim(); var sizeVal = ParseUtil.CoerceFloat(sizeStr.Split(' ')[0]); @@ -184,12 +167,10 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); - } public async Task Download(Uri link) @@ -199,8 +180,5 @@ namespace Jackett.Indexers var bytes = await response.Content.ReadAsByteArrayAsync(); return bytes; } - - - } } diff --git a/src/Jackett/Indexers/MoreThanTV.cs b/src/Jackett/Indexers/MoreThanTV.cs index 82488708e..98c38c00f 100644 --- a/src/Jackett/Indexers/MoreThanTV.cs +++ b/src/Jackett/Indexers/MoreThanTV.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.IO; @@ -12,39 +16,12 @@ using System.Web; namespace Jackett.Indexers { - public class MoreThanTV : IndexerInterface + public class MoreThanTV : BaseIndexer, IIndexer { - public string DisplayName - { - get { return "MoreThanTV"; } - } - - public string DisplayDescription - { - get { return "ROMANIAN Private Torrent Tracker for TV / MOVIES, and the internal tracker for the release group DRACULA"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public event Action OnSaveConfigurationRequested; - public event Action OnResultParsingError; - - public bool IsConfigured { get; private set; } - - static string BaseUrl = "https://www.morethan.tv"; - - static string LoginUrl = BaseUrl + "/login.php"; - - static string SearchUrl = BaseUrl + "/ajax.php?action=browse&searchstr="; - - static string DownloadUrl = BaseUrl + "/torrents.php?action=download&id="; - - static string GuidUrl = BaseUrl + "/torrents.php?torrentid="; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; + private readonly string DownloadUrl = ""; + private readonly string GuidUrl = ""; CookieContainer cookies; HttpClientHandler handler; @@ -53,9 +30,19 @@ namespace Jackett.Indexers string cookieHeader; int retries = 3; - public MoreThanTV() + public MoreThanTV(IIndexerManagerService i, Logger l) + : base(name: "MoreThanTV", + description: "ROMANIAN Private Torrent Tracker for TV / MOVIES, and the internal tracker for the release group DRACULA.", + link: new Uri("https://www.morethan.tv"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/login.php"; + SearchUrl = SiteLink + "/ajax.php?action=browse&searchstr="; + DownloadUrl = SiteLink + "/torrents.php?action=download&id="; + GuidUrl = SiteLink + "/torrents.php?torrentid="; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -90,7 +77,7 @@ namespace Jackett.Indexers var configSaveData = new JObject(); - if (Program.IsWindows) + if (Engine.IsWindows) { // If Windows use .net http var response = await client.PostAsync(LoginUrl, content); @@ -117,21 +104,19 @@ namespace Jackett.Indexers } else { - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(SiteLink, jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); cookieHeader = cookies.GetCookieHeader(SiteLink); IsConfigured = true; } - static void FillReleaseInfoFromJson(ReleaseInfo release, JObject r) + private void FillReleaseInfoFromJson(ReleaseInfo release, JObject r) { var id = r["torrentId"]; release.Size = (long)r["size"]; @@ -150,7 +135,7 @@ namespace Jackett.Indexers var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString); string results; - if (Program.IsWindows) + if (Engine.IsWindows) { results = await client.GetStringAsync(episodeSearchUrl, retries); } @@ -201,8 +186,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); @@ -210,7 +194,7 @@ namespace Jackett.Indexers public async Task Download(Uri link) { - if (Program.IsWindows) + if (Engine.IsWindows) { return await client.GetByteArrayAsync(link); } @@ -219,7 +203,6 @@ namespace Jackett.Indexers var response = await CurlHelper.GetAsync(link.ToString(), cookieHeader); return response.Content; } - } } } diff --git a/src/Jackett/Indexers/Rarbg.cs b/src/Jackett/Indexers/Rarbg.cs index 45358119d..4d43927b1 100644 --- a/src/Jackett/Indexers/Rarbg.cs +++ b/src/Jackett/Indexers/Rarbg.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Linq; @@ -10,48 +14,27 @@ using System.Threading.Tasks; namespace Jackett.Indexers { - public class Rarbg : IndexerInterface + public class Rarbg : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "RARBG"; } - } - - public string DisplayDescription - { - get { return DisplayName; } - } - - public Uri SiteLink - { - get { return new Uri("https://rarbg.com"); } - } - - public bool RequiresRageIDLookupDisabled { get { return false; } } - - public bool IsConfigured { get; private set; } - const string DefaultUrl = "http://torrentapi.org"; - const string TokenUrl = "/pubapi.php?get_token=get_token&format=json"; const string SearchTVRageUrl = "/pubapi.php?mode=search&search_tvrage={0}&token={1}&format=json&min_seeders=1"; const string SearchQueryUrl = "/pubapi.php?mode=search&search_string={0}&token={1}&format=json&min_seeders=1"; - - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; - - string BaseUrl; + private string BaseUrl; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public Rarbg() + + public Rarbg(IIndexerManagerService i, Logger l) + : base(name: "RARBG", + description: "RARBG", + link: new Uri("https://rarbg.com"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -83,10 +66,7 @@ namespace Jackett.Indexers var configSaveData = new JObject(); configSaveData["base_url"] = BaseUrl; - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } @@ -101,7 +81,7 @@ namespace Jackett.Indexers var message = new HttpRequestMessage(); message.Method = HttpMethod.Get; message.RequestUri = new Uri(uri); - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + message.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); return message; } @@ -154,10 +134,9 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); + OnParseError(results, ex); } return releases.ToArray(); - } public Task Download(Uri link) diff --git a/src/Jackett/Indexers/SceneAccess.cs b/src/Jackett/Indexers/SceneAccess.cs index 75d29d1f9..0e065596c 100644 --- a/src/Jackett/Indexers/SceneAccess.cs +++ b/src/Jackett/Indexers/SceneAccess.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -10,48 +14,27 @@ using System.Threading.Tasks; namespace Jackett.Indexers { - class SceneAccess : IndexerInterface + class SceneAccess : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "SceneAccess"; } - } - - public string DisplayDescription - { - get { return "Your gateway to the scene"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - const string BaseUrl = "https://sceneaccess.eu"; - const string LoginUrl = BaseUrl + "/login"; - const string SearchUrl = BaseUrl + "/{0}?method=1&c{1}=1&search={2}"; - - - public bool IsConfigured - { - get; - private set; - } + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; string cookieHeader; - public SceneAccess() + public SceneAccess(IIndexerManagerService i, Logger l) + : base(name: "SceneAccess", + description: "Your gateway to the scene", + link: new Uri("https://sceneaccess.eu"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/login"; + SearchUrl = SiteLink + "/{0}?method=1&c{1}=1&search={2}"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -84,7 +67,7 @@ namespace Jackett.Indexers string responseContent; var configSaveData = new JObject(); - if (Program.IsWindows) + if (Engine.IsWindows) { // If Windows use .net http var response = await client.PostAsync(LoginUrl, content); @@ -109,16 +92,14 @@ namespace Jackett.Indexers } else { - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); cookieHeader = cookies.GetCookieHeader(SiteLink); IsConfigured = true; } @@ -134,7 +115,7 @@ namespace Jackett.Indexers var searchUrl = string.Format(SearchUrl, searchSection, searchCategory, searchString); string results; - if (Program.IsWindows) + if (Engine.IsWindows) { results = await client.GetStringAsync(searchUrl); } @@ -157,9 +138,9 @@ namespace Jackett.Indexers release.MinimumSeedTime = 129600; release.Title = qRow.Find(".ttr_name > a").Text(); release.Description = release.Title; - release.Guid = new Uri(BaseUrl + "/" + qRow.Find(".ttr_name > a").Attr("href")); + release.Guid = new Uri(SiteLink + "/" + qRow.Find(".ttr_name > a").Attr("href")); release.Comments = release.Guid; - release.Link = new Uri(BaseUrl + "/" + qRow.Find(".td_dl > a").Attr("href")); + release.Link = new Uri(SiteLink + "/" + qRow.Find(".td_dl > a").Attr("href")); var sizeStr = qRow.Find(".ttr_size").Contents()[0].NodeValue; var sizeParts = sizeStr.Split(' '); @@ -180,8 +161,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); @@ -189,7 +169,7 @@ namespace Jackett.Indexers public async Task Download(Uri link) { - if (Program.IsWindows) + if (Engine.IsWindows) { return await client.GetByteArrayAsync(link); } diff --git a/src/Jackett/Indexers/SceneTime.cs b/src/Jackett/Indexers/SceneTime.cs index c64742cec..00f83bb51 100644 --- a/src/Jackett/Indexers/SceneTime.cs +++ b/src/Jackett/Indexers/SceneTime.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,44 +16,28 @@ using System.Web; namespace Jackett.Indexers { - public class SceneTime : IndexerInterface + public class SceneTime : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "SceneTime"; } - } - - public string DisplayDescription - { - get { return "Always on time"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - - const string BaseUrl = "https://www.scenetime.com"; - const string LoginUrl = BaseUrl + "/takelogin.php"; - const string SearchUrl = BaseUrl + "/browse_API.php"; - const string DownloadUrl = BaseUrl + "/download.php/{0}/download.torrent"; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; + private readonly string DownloadUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - - public SceneTime() + public SceneTime(IIndexerManagerService i, Logger l) + : base(name: "SceneTime", + description: "Always on time", + link: new Uri("https://www.scenetime.com"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/takelogin.php"; + SearchUrl = SiteLink + "/browse_API.php"; + DownloadUrl = SiteLink + "/download.php/{0}/download.torrent"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -91,17 +79,14 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -141,7 +126,7 @@ namespace Jackett.Indexers var qLink = qDescCol.Find("a"); release.Title = qLink.Text(); release.Description = release.Title; - release.Comments = new Uri(BaseUrl + "/" + qLink.Attr("href")); + release.Comments = new Uri(SiteLink + "/" + qLink.Attr("href")); release.Guid = release.Comments; var torrentId = qLink.Attr("href").Split('=')[1]; release.Link = new Uri(string.Format(DownloadUrl, torrentId)); @@ -164,8 +149,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); } diff --git a/src/Jackett/Indexers/ShowRSS.cs b/src/Jackett/Indexers/ShowRSS.cs index 3f3ef5ca9..78ae0d230 100644 --- a/src/Jackett/Indexers/ShowRSS.cs +++ b/src/Jackett/Indexers/ShowRSS.cs @@ -1,4 +1,8 @@ -using Newtonsoft.Json.Linq; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; +using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,47 +16,25 @@ using System.Xml; namespace Jackett.Indexers { - public class ShowRSS : IndexerInterface + public class ShowRSS : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "ShowRSS"; } - } - - public string DisplayDescription - { - get { return "showRSS is a service that allows you to keep track of your favorite TV shows"; } - } - - public Uri SiteLink - { - get { return new Uri(DefaultUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - const string DefaultUrl = "http://showrss.info"; - const string searchAllUrl = DefaultUrl + "/feeds/all.rss"; + private readonly string searchAllUrl = ""; string BaseUrl; - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public bool IsConfigured + public ShowRSS(IIndexerManagerService i, Logger l) + : base(name: "ShowRSS", + description: "showRSS is a service that allows you to keep track of your favorite TV shows", + link: new Uri("http://showrss.info"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - get; - private set; - } + searchAllUrl = SiteLink + "/feeds/all.rss"; - public ShowRSS() - { - IsConfigured = false; cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -65,13 +47,13 @@ namespace Jackett.Indexers public Task GetConfigurationForSetup() { - var config = new ConfigurationDataUrl(DefaultUrl); + var config = new ConfigurationDataUrl(SiteLink); return Task.FromResult(config); } public async Task ApplyConfiguration(Newtonsoft.Json.Linq.JToken configJson) { - var config = new ConfigurationDataUrl(DefaultUrl); + var config = new ConfigurationDataUrl(SiteLink); config.LoadValuesFromJson(configJson); var formattedUrl = config.GetFormattedHostUrl(); @@ -83,10 +65,7 @@ namespace Jackett.Indexers var configSaveData = new JObject(); configSaveData["base_url"] = BaseUrl; - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } @@ -110,7 +89,7 @@ namespace Jackett.Indexers { WebClient wc = new WebClient(); WebHeaderCollection headers = new WebHeaderCollection(); - headers.Add("User-Agent", chromeUserAgent); + headers.Add("User-Agent", BrowserUtil.ChromeUserAgent); wc.Headers = headers; return wc; } @@ -164,8 +143,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, xml, ex); - throw ex; + OnParseError(xml, ex); } return releases.ToArray(); diff --git a/src/Jackett/Indexers/SpeedCD.cs b/src/Jackett/Indexers/SpeedCD.cs index 6e43d55e1..8122f66e9 100644 --- a/src/Jackett/Indexers/SpeedCD.cs +++ b/src/Jackett/Indexers/SpeedCD.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -13,36 +17,31 @@ using System.Web; namespace Jackett.Indexers { - public class SpeedCD : IndexerInterface + public class SpeedCD : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName { get { return "Speed.cd"; } } - - public string DisplayDescription { get { return "Your home now!"; } } - - public Uri SiteLink { get { return new Uri(BaseUrl); } } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - const string BaseUrl = "http://speed.cd"; - const string LoginUrl = BaseUrl + "/take_login.php"; - const string SearchUrl = BaseUrl + "/V3/API/API.php"; - const string SearchFormData = "c53=1&c49=1&c2=1&c52=1&c41=1&c50=1&c30=1&jxt=4&jxw=b"; - const string CommentsUrl = BaseUrl + "/t/{0}"; - const string DownloadUrl = BaseUrl + "/download.php?torrent={0}"; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; + private readonly string SearchFormData = "c53=1&c49=1&c2=1&c52=1&c41=1&c50=1&c30=1&jxt=4&jxw=b"; + private readonly string CommentsUrl = ""; + private readonly string DownloadUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public bool IsConfigured { get; private set; } - - public SpeedCD() + public SpeedCD(IIndexerManagerService i, Logger l) + : base(name: "Speed.cd", + description: "Your home now!", + link: new Uri("http://speed.cd"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/take_login.php"; + SearchUrl = SiteLink + "/V3/API/API.php"; + CommentsUrl = SiteLink + "/t/{0}"; + DownloadUrl = SiteLink + "/download.php?torrent={0}"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -84,17 +83,14 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -142,8 +138,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); } diff --git a/src/Jackett/Indexers/Strike.cs b/src/Jackett/Indexers/Strike.cs index b2418548c..5c63476f2 100644 --- a/src/Jackett/Indexers/Strike.cs +++ b/src/Jackett/Indexers/Strike.cs @@ -1,4 +1,8 @@ -using Newtonsoft.Json.Linq; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; +using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -11,47 +15,24 @@ using System.Web; namespace Jackett.Indexers { - public class Strike : IndexerInterface + public class Strike : BaseIndexer, IIndexer { + private readonly string DownloadUrl = "/torrents/api/download/{0}.torrent"; + private readonly string SearchUrl = "/api/v2/torrents/search/?category=TV&phrase={0}"; + private string BaseUrl; - public event Action OnSaveConfigurationRequested; - public event Action OnResultParsingError; + private CookieContainer cookies; + private HttpClientHandler handler; + private HttpClient client; - public string DisplayName + public Strike(IIndexerManagerService i, Logger l) + : base(name: "Strike", + description: "Torrent search engine", + link: new Uri("https://getstrike.net"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - get { return "Strike"; } - } - - public string DisplayDescription - { - get { return "Torrent search engine"; } - } - - public Uri SiteLink - { - get { return new Uri(DefaultUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - - const string DefaultUrl = "https://getstrike.net"; - - - //const string DownloadUrl = "/api/v2/torrents/download/?hash={0}"; - const string DownloadUrl = "/torrents/api/download/{0}.torrent"; - - const string SearchUrl = "/api/v2/torrents/search/?category=TV&phrase={0}"; - string BaseUrl; - - CookieContainer cookies; - HttpClientHandler handler; - HttpClient client; - - public Strike() - { - IsConfigured = false; cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -64,13 +45,13 @@ namespace Jackett.Indexers public Task GetConfigurationForSetup() { - var config = new ConfigurationDataUrl(DefaultUrl); + var config = new ConfigurationDataUrl(SiteLink); return Task.FromResult(config); } public async Task ApplyConfiguration(JToken configJson) { - var config = new ConfigurationDataUrl(DefaultUrl); + var config = new ConfigurationDataUrl(SiteLink); config.LoadValuesFromJson(configJson); var formattedUrl = config.GetFormattedHostUrl(); @@ -82,12 +63,8 @@ namespace Jackett.Indexers var configSaveData = new JObject(); configSaveData["base_url"] = BaseUrl; - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; - } public void LoadFromSavedConfiguration(JToken jsonConfig) @@ -122,8 +99,13 @@ namespace Jackett.Indexers release.Size = (long)result["size"]; // "Apr 2, 2015", "Apr 12, 2015" (note the spacing) + // some are unix timestamps, some are not.. :/ var dateString = string.Join(" ", ((string)result["upload_date"]).Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)); - release.PublishDate = DateTime.ParseExact(dateString, "MMM d, yyyy", CultureInfo.InvariantCulture); + float dateVal; + if (ParseUtil.TryCoerceFloat(dateString, out dateVal)) + release.PublishDate = DateTimeUtil.UnixTimestampToDateTime(dateVal); + else + release.PublishDate = DateTime.ParseExact(dateString, "MMM d, yyyy", CultureInfo.InvariantCulture); release.Guid = new Uri((string)result["page"]); release.Comments = release.Guid; @@ -137,11 +119,9 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } - return releases.ToArray(); } @@ -154,7 +134,5 @@ namespace Jackett.Indexers { throw new NotImplementedException(); } - - } } diff --git a/src/Jackett/Indexers/T411.cs b/src/Jackett/Indexers/T411.cs index 085971a09..814753558 100644 --- a/src/Jackett/Indexers/T411.cs +++ b/src/Jackett/Indexers/T411.cs @@ -1,4 +1,8 @@ -using Newtonsoft.Json.Linq; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; +using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,35 +16,9 @@ using System.Web; namespace Jackett.Indexers { - public class T411 : IndexerInterface + public class T411 : BaseIndexer, IIndexer { - - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "T411"; } - } - - public string DisplayDescription - { - get { return "French Torrent Tracker"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - - const string BaseUrl = "http://www.t411.io"; - const string CommentsUrl = BaseUrl + "/torrents/{0}"; - + private readonly string CommentsUrl = ""; const string ApiUrl = "http://api.t411.io"; const string AuthUrl = ApiUrl + "/auth"; const string SearchUrl = ApiUrl + "/torrents/search/{0}"; @@ -54,8 +32,15 @@ namespace Jackett.Indexers string token = string.Empty; DateTime lastTokenFetch = DateTime.MinValue; - public T411() + public T411(IIndexerManagerService i, Logger l) + : base(name: "T411", + description: "French Torrent Tracker", + link: new Uri("http://www.t411.io"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { + CommentsUrl = SiteLink + "/torrents/{0}"; IsConfigured = false; handler = new HttpClientHandler { @@ -118,10 +103,7 @@ namespace Jackett.Indexers configSaveData["password"] = password; configSaveData["token"] = token; configSaveData["last_token_fetch"] = lastTokenFetch; - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } @@ -180,8 +162,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); } diff --git a/src/Jackett/Indexers/ThePirateBay.cs b/src/Jackett/Indexers/ThePirateBay.cs index 8dd1070c5..281df8382 100644 --- a/src/Jackett/Indexers/ThePirateBay.cs +++ b/src/Jackett/Indexers/ThePirateBay.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -13,35 +17,24 @@ using System.Web; namespace Jackett.Indexers { - public class ThePirateBay : IndexerInterface + public class ThePirateBay : BaseIndexer, IIndexer { - - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName { get { return "The Pirate Bay"; } } - - public string DisplayDescription { get { return "The worlds largest bittorrent indexer"; } } - - public Uri SiteLink { get { return new Uri(DefaultUrl); } } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - - const string DefaultUrl = "https://thepiratebay.mn"; const string SearchUrl = "/search/{0}/0/99/208,205"; - string BaseUrl; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public ThePirateBay() + public ThePirateBay(IIndexerManagerService i, Logger l) + : base(name: "The Pirate Bay", + description: "The worlds largest bittorrent indexer", + link: new Uri("https://thepiratebay.mn"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - BaseUrl = DefaultUrl; + BaseUrl = SiteLink.ToString(); IsConfigured = false; cookies = new CookieContainer(); handler = new HttpClientHandler @@ -61,7 +54,7 @@ namespace Jackett.Indexers public async Task ApplyConfiguration(JToken configJson) { - var config = new ConfigurationDataUrl(DefaultUrl); + var config = new ConfigurationDataUrl(SiteLink); config.LoadValuesFromJson(configJson); var formattedUrl = config.GetFormattedHostUrl(); @@ -73,10 +66,7 @@ namespace Jackett.Indexers var configSaveData = new JObject(); configSaveData["base_url"] = BaseUrl; - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } @@ -101,7 +91,7 @@ namespace Jackett.Indexers string results; - if (Program.IsWindows) + if (Engine.IsWindows) { results = await client.GetStringAsync(episodeSearchUrl); } @@ -175,7 +165,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); + // OnResultParsingError(this, results, ex); throw ex; } return releases.ToArray(); diff --git a/src/Jackett/Indexers/TorrentDay.cs b/src/Jackett/Indexers/TorrentDay.cs index 5cbec6a05..ca4107c8d 100644 --- a/src/Jackett/Indexers/TorrentDay.cs +++ b/src/Jackett/Indexers/TorrentDay.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,45 +16,28 @@ using System.Web; namespace Jackett.Indexers { - public class TorrentDay : IndexerInterface + public class TorrentDay : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "TorrentDay"; } - } - - public string DisplayDescription - { - get { return DisplayName; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - public bool IsConfigured { get; private set; } - - const string BaseUrl = "https://torrentday.eu"; - const string StartPageUrl = BaseUrl + "/login.php"; - const string LoginUrl = BaseUrl + "/tak3login.php"; - const string SearchUrl = BaseUrl + "/browse.php?search={0}&cata=yes&c2=1&c7=1&c14=1&c24=1&c26=1&c31=1&c32=1&c33=1"; - - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; + private readonly string StartPageUrl = ""; + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public TorrentDay() + public TorrentDay(IIndexerManagerService i, Logger l) + : base(name: "TorrentDay", + description: "TorrentDay", + link: new Uri("https://torrentday.eu"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + StartPageUrl = SiteLink + "/login.php"; + LoginUrl = SiteLink + "/tak3login.php"; + SearchUrl = SiteLink + "/browse.php?search={0}&cata=yes&c2=1&c7=1&c14=1&c24=1&c26=1&c31=1&c32=1&c33=1"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -72,7 +59,7 @@ namespace Jackett.Indexers var message = new HttpRequestMessage(); message.Method = HttpMethod.Get; message.RequestUri = new Uri(uri); - message.Headers.UserAgent.ParseAdd(chromeUserAgent); + message.Headers.UserAgent.ParseAdd(BrowserUtil.ChromeUserAgent); return message; } @@ -110,17 +97,14 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -144,9 +128,9 @@ namespace Jackett.Indexers release.MinimumSeedTime = 172800; release.Title = qRow.Find(".torrentName").Text(); release.Description = release.Title; - release.Guid = new Uri(BaseUrl + "/" + qRow.Find(".torrentName").Attr("href")); + release.Guid = new Uri(SiteLink + "/" + qRow.Find(".torrentName").Attr("href")); release.Comments = release.Guid; - release.Link = new Uri(BaseUrl + "/" + qRow.Find(".dlLinksInfo > a").Attr("href")); + release.Link = new Uri(SiteLink + "/" + qRow.Find(".dlLinksInfo > a").Attr("href")); var sizeStr = qRow.Find(".sizeInfo").Text().Trim(); var sizeParts = sizeStr.Split(' '); @@ -180,8 +164,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); } diff --git a/src/Jackett/Indexers/TorrentLeech.cs b/src/Jackett/Indexers/TorrentLeech.cs index 15de57ff7..eac781cc3 100644 --- a/src/Jackett/Indexers/TorrentLeech.cs +++ b/src/Jackett/Indexers/TorrentLeech.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,43 +16,26 @@ using System.Web; namespace Jackett.Indexers { - public class TorrentLeech : IndexerInterface + public class TorrentLeech : BaseIndexer, IIndexer { - public event Action OnResultParsingError; - - public event Action OnSaveConfigurationRequested; - - public string DisplayName - { - get { return "TorrentLeech"; } - } - - public string DisplayDescription - { - get { return "This is what happens when you seed"; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - const string BaseUrl = "http://www.torrentleech.org"; - const string LoginUrl = BaseUrl + "/user/account/login/"; - const string SearchUrl = BaseUrl + "/torrents/browse/index/query/{0}/categories/2%2C26%2C27%2C32/orderby/added?"; - - public bool IsConfigured { get; private set; } - + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public TorrentLeech() + public TorrentLeech(IIndexerManagerService i, Logger l) + : base(name: "TorrentLeech", + description: "This is what happens when you seed", + link: new Uri("http://www.torrentleech.org"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/user/account/login/"; + SearchUrl = SiteLink + "/torrents/browse/index/query/{0}/categories/2%2C26%2C27%2C32/orderby/added?"; + cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -93,17 +80,14 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } } public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -132,12 +116,12 @@ namespace Jackett.Indexers release.MinimumSeedTime = 172800; CQ qLink = qRow.Find(".title > a").First(); - release.Guid = new Uri(BaseUrl + qLink.Attr("href")); + release.Guid = new Uri(SiteLink + qLink.Attr("href")); release.Comments = release.Guid; release.Title = qLink.Text(); release.Description = release.Title; - release.Link = new Uri(BaseUrl + qRow.Find(".quickdownload > a").Attr("href")); + release.Link = new Uri(SiteLink + qRow.Find(".quickdownload > a").Attr("href")); var dateString = qRow.Find(".name").First()[0].ChildNodes[4].NodeValue.Replace(" on", "").Trim(); //"2015-04-25 23:38:12" @@ -156,8 +140,7 @@ namespace Jackett.Indexers catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); diff --git a/src/Jackett/Indexers/TorrentShack.cs b/src/Jackett/Indexers/TorrentShack.cs index a6141c45d..b466f7568 100644 --- a/src/Jackett/Indexers/TorrentShack.cs +++ b/src/Jackett/Indexers/TorrentShack.cs @@ -1,5 +1,9 @@ using CsQuery; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -12,43 +16,25 @@ using System.Web; namespace Jackett.Indexers { - public class TorrentShack : IndexerInterface + public class TorrentShack : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "TorrentShack"; } - } - - public string DisplayDescription - { - get { return DisplayName; } - } - - public Uri SiteLink - { - get { return new Uri(BaseUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - const string BaseUrl = "http://torrentshack.me"; - const string LoginUrl = BaseUrl + "/login.php"; - const string SearchUrl = BaseUrl + "/torrents.php?searchstr={0}&release_type=both&searchtags=&tags_type=0&order_by=s3&order_way=desc&torrent_preset=all&filter_cat%5B600%5D=1&filter_cat%5B620%5D=1&filter_cat%5B700%5D=1&filter_cat%5B981%5D=1&filter_cat%5B980%5D=1"; - + private readonly string LoginUrl = ""; + private readonly string SearchUrl = ""; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public bool IsConfigured { get; private set; } - - public TorrentShack() + public TorrentShack(IIndexerManagerService i, Logger l) + : base(name: "TorrentShack", + description: "TorrentShack", + link: new Uri("http://torrentshack.me"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - IsConfigured = false; + LoginUrl = SiteLink + "/login.php"; + SearchUrl = SiteLink + "/torrents.php?searchstr={0}&release_type=both&searchtags=&tags_type=0&order_by=s3&order_way=desc&torrent_preset=all&filter_cat%5B600%5D=1&filter_cat%5B620%5D=1&filter_cat%5B700%5D=1&filter_cat%5B981%5D=1&filter_cat%5B980%5D=1"; cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -94,10 +80,7 @@ namespace Jackett.Indexers { var configSaveData = new JObject(); cookies.DumpToJson(SiteLink, configSaveData); - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } @@ -105,7 +88,7 @@ namespace Jackett.Indexers public void LoadFromSavedConfiguration(JToken jsonConfig) { - cookies.FillFromJson(new Uri(BaseUrl), jsonConfig); + cookies.FillFromJson(SiteLink, jsonConfig, logger); IsConfigured = true; } @@ -130,9 +113,9 @@ namespace Jackett.Indexers release.MinimumSeedTime = 172800; release.Title = qRow.Find(".torrent_name_link").Text(); release.Description = release.Title; - release.Guid = new Uri(BaseUrl + "/" + qRow.Find(".torrent_name_link").Parent().Attr("href")); + release.Guid = new Uri(SiteLink + "/" + qRow.Find(".torrent_name_link").Parent().Attr("href")); release.Comments = release.Guid; - release.Link = new Uri(BaseUrl + "/" + qRow.Find(".torrent_handle_links > a").First().Attr("href")); + release.Link = new Uri(SiteLink + "/" + qRow.Find(".torrent_handle_links > a").First().Attr("href")); var dateStr = qRow.Find(".time").Text().Trim(); if (dateStr.ToLower().Contains("just now")) @@ -172,8 +155,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, results, ex); - throw ex; + OnParseError(results, ex); } return releases.ToArray(); } diff --git a/src/Jackett/Indexers/Torrentz.cs b/src/Jackett/Indexers/Torrentz.cs index de45011c3..1d54b0bf9 100644 --- a/src/Jackett/Indexers/Torrentz.cs +++ b/src/Jackett/Indexers/Torrentz.cs @@ -1,4 +1,8 @@ -using Newtonsoft.Json.Linq; +using Jackett.Models; +using Jackett.Services; +using Jackett.Utils; +using Newtonsoft.Json.Linq; +using NLog; using System; using System.Collections.Generic; using System.Globalization; @@ -11,47 +15,25 @@ using System.Xml; namespace Jackett.Indexers { - public class Torrentz : IndexerInterface + public class Torrentz : BaseIndexer, IIndexer { - public event Action OnSaveConfigurationRequested; - - public event Action OnResultParsingError; - - public string DisplayName - { - get { return "Torrentz"; } - } - - public string DisplayDescription - { - get { return "Torrentz is a meta-search engine and a Multisearch. This means we just search other search engines."; } - } - - public Uri SiteLink - { - get { return new Uri(DefaultUrl); } - } - - public bool RequiresRageIDLookupDisabled { get { return true; } } - - const string DefaultUrl = "https://torrentz.eu"; - const string SearchUrl = DefaultUrl + "/feed_verifiedP?f={0}"; + private readonly string SearchUrl = ""; string BaseUrl; - static string chromeUserAgent = BrowserUtil.ChromeUserAgent; CookieContainer cookies; HttpClientHandler handler; HttpClient client; - public bool IsConfigured + public Torrentz(IIndexerManagerService i, Logger l) + : base(name: "Torrentz", + description: "Torrentz is a meta-search engine and a Multisearch. This means we just search other search engines.", + link: new Uri("https://torrentz.eu"), + caps: TorznabCapsUtil.CreateDefaultTorznabTVCaps(), + manager: i, + logger: l) { - get; - private set; - } - public Torrentz() - { - IsConfigured = false; + SearchUrl = SiteLink + "/feed_verifiedP?f={0}"; cookies = new CookieContainer(); handler = new HttpClientHandler { @@ -62,18 +44,16 @@ namespace Jackett.Indexers client = new HttpClient(handler); } - - public Task GetConfigurationForSetup() { - var config = new ConfigurationDataUrl(DefaultUrl); + var config = new ConfigurationDataUrl(SiteLink); return Task.FromResult(config); } public async Task ApplyConfiguration(JToken configJson) { - var config = new ConfigurationDataUrl(DefaultUrl); + var config = new ConfigurationDataUrl(SiteLink); config.LoadValuesFromJson(configJson); var formattedUrl = config.GetFormattedHostUrl(); @@ -85,10 +65,7 @@ namespace Jackett.Indexers var configSaveData = new JObject(); configSaveData["base_url"] = BaseUrl; - - if (OnSaveConfigurationRequested != null) - OnSaveConfigurationRequested(this, configSaveData); - + SaveConfig(configSaveData); IsConfigured = true; } @@ -97,7 +74,7 @@ namespace Jackett.Indexers { WebClient wc = new WebClient(); WebHeaderCollection headers = new WebHeaderCollection(); - headers.Add("User-Agent", chromeUserAgent); + headers.Add("User-Agent", BrowserUtil.ChromeUserAgent); wc.Headers = headers; return wc; } @@ -117,7 +94,7 @@ namespace Jackett.Indexers { using (wc) { - xml = wc.DownloadString(episodeSearchUrl); + xml = await wc.DownloadStringTaskAsync(new Uri(episodeSearchUrl)); xmlDoc.LoadXml(xml); } @@ -151,8 +128,7 @@ namespace Jackett.Indexers } catch (Exception ex) { - OnResultParsingError(this, xml, ex); - throw ex; + OnParseError(xml, ex); } return releases.ToArray(); @@ -174,7 +150,6 @@ namespace Jackett.Indexers { throw new NotImplementedException(); } - } public class TorrentzHelper diff --git a/src/Jackett/Jackett.csproj b/src/Jackett/Jackett.csproj index 7b9597c12..04dca9d65 100644 --- a/src/Jackett/Jackett.csproj +++ b/src/Jackett/Jackett.csproj @@ -5,7 +5,7 @@ Debug AnyCPU {E636D5F8-68B4-4903-B4ED-CCFD9C9E899F} - WinExe + Library Properties Jackett Jackett @@ -48,25 +48,94 @@ 4 - jacket_large.ico + + - Jackett.Program + + + + ..\packages\Autofac.3.5.2\lib\net40\Autofac.dll + True + + + ..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll + + + False + ..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll + + + ..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll + ..\packages\CsQuery.1.3.4\lib\net40\CsQuery.dll + + ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll + + + ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll + True + + + ..\packages\Microsoft.Owin.FileSystems.3.0.1\lib\net45\Microsoft.Owin.FileSystems.dll + True + + + ..\packages\Microsoft.Owin.Host.HttpListener.2.0.2\lib\net45\Microsoft.Owin.Host.HttpListener.dll + True + + + ..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll + + + ..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll + True + + + ..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll + + + ..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll + + + ..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll + True + ..\packages\NLog.Windows.Forms.2.0.0.0\lib\net35\NLog.Windows.Forms.dll True + + ..\packages\Owin.1.0\lib\net40\Owin.dll + True + + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + True + + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll + True + + + ..\packages\Microsoft.AspNet.WebApi.Tracing.5.2.3\lib\net45\System.Web.Http.Tracing.dll + @@ -81,22 +150,33 @@ + + + + + - + + + + + + + + - - - - - - - + + + + + + + + - - - - - + + + @@ -117,33 +197,45 @@ - - Form - - - Main.cs - + + + - True True Resources.resx - - - + + + - - - + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + - + PreserveNewest @@ -151,129 +243,134 @@ - - Main.cs - ResXFileCodeGenerator Resources.Designer.cs - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + PreserveNewest - + PreserveNewest diff --git a/src/Jackett/JackettModule.cs b/src/Jackett/JackettModule.cs new file mode 100644 index 000000000..5f1c43b3a --- /dev/null +++ b/src/Jackett/JackettModule.cs @@ -0,0 +1,30 @@ +using Autofac; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Autofac.Integration.WebApi; +using Jackett.Indexers; + +namespace Jackett +{ + public class JackettModule : Module + { + protected override void Load(ContainerBuilder builder) + { + // Just register everything! + var thisAssembly = typeof(JackettModule).Assembly; + builder.RegisterAssemblyTypes(thisAssembly).Except().AsImplementedInterfaces().SingleInstance(); + builder.RegisterApiControllers(thisAssembly).InstancePerRequest(); + + // Register indexers + foreach (var indexer in thisAssembly.GetTypes() + .Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface) + .ToArray()) + { + builder.RegisterType(indexer).Named(BaseIndexer.GetIndexerID(indexer)); + } + } + } +} diff --git a/src/Jackett/Torznab/CachedResult.cs b/src/Jackett/Models/CachedResult.cs similarity index 96% rename from src/Jackett/Torznab/CachedResult.cs rename to src/Jackett/Models/CachedResult.cs index 925d9a139..24feea319 100644 --- a/src/Jackett/Torznab/CachedResult.cs +++ b/src/Jackett/Models/CachedResult.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public class CachedResult { diff --git a/src/Jackett/Torznab/ChannelInfo.cs b/src/Jackett/Models/ChannelInfo.cs similarity index 96% rename from src/Jackett/Torznab/ChannelInfo.cs rename to src/Jackett/Models/ChannelInfo.cs index a42421e01..e9afa5112 100644 --- a/src/Jackett/Torznab/ChannelInfo.cs +++ b/src/Jackett/Models/ChannelInfo.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public class ChannelInfo { diff --git a/src/Jackett/Models/Config/ServerConfig.cs b/src/Jackett/Models/Config/ServerConfig.cs new file mode 100644 index 000000000..58a221a5f --- /dev/null +++ b/src/Jackett/Models/Config/ServerConfig.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Models.Config +{ + public class ServerConfig + { + public ServerConfig() + { + Port = 9117; + } + + public int Port { get; set; } + public bool AllowExternal { get; set; } + public string APIKey { get; set; } + public string AdminPassword { get; set; } + + public string[] GetListenAddresses(bool? external = null) + { + if (external == null) + { + external = AllowExternal; + } + if (external.Value) + { + return new string[] { "http://*:" + Port + "/" }; + } + else + { + return new string[] { + "http://127.0.0.1:" + Port + "/", + "http://localhost:" + Port + "/", + }; + } + } + + public string GenerateApi() + { + var chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + var randBytes = new byte[32]; + var rngCsp = new RNGCryptoServiceProvider(); + rngCsp.GetBytes(randBytes); + var key = ""; + foreach (var b in randBytes) + { + key += chars[b % chars.Length]; + } + return key; + } + } +} diff --git a/src/Jackett/Configurations/ConfigurationData.cs b/src/Jackett/Models/ConfigurationData.cs similarity index 94% rename from src/Jackett/Configurations/ConfigurationData.cs rename to src/Jackett/Models/ConfigurationData.cs index 220df37d7..21344cf9c 100644 --- a/src/Jackett/Configurations/ConfigurationData.cs +++ b/src/Jackett/Models/ConfigurationData.cs @@ -1,11 +1,12 @@ -using Newtonsoft.Json.Linq; +using Jackett.Utils; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public abstract class ConfigurationData { @@ -57,7 +58,7 @@ namespace Jackett jObject["value"] = ((BoolItem)item).Value; break; case ItemType.DisplayImage: - string dataUri = DataUrlUtil.BytesToDataUrl(((ImageItem)item).Value, "image/jpeg"); + string dataUri = DataUrlUtils.BytesToDataUrl(((ImageItem)item).Value, "image/jpeg"); jObject["value"] = dataUri; break; } diff --git a/src/Jackett/Configurations/ConfigurationDataBasicLogin.cs b/src/Jackett/Models/ConfigurationDataBasicLogin.cs similarity index 96% rename from src/Jackett/Configurations/ConfigurationDataBasicLogin.cs rename to src/Jackett/Models/ConfigurationDataBasicLogin.cs index 19a2ab0ff..66afbffe3 100644 --- a/src/Jackett/Configurations/ConfigurationDataBasicLogin.cs +++ b/src/Jackett/Models/ConfigurationDataBasicLogin.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public class ConfigurationDataBasicLogin : ConfigurationData { diff --git a/src/Jackett/Configurations/ConfigurationDataCookie.cs b/src/Jackett/Models/ConfigurationDataCookie.cs similarity index 98% rename from src/Jackett/Configurations/ConfigurationDataCookie.cs rename to src/Jackett/Models/ConfigurationDataCookie.cs index c184dfc5a..afa3e46e5 100644 --- a/src/Jackett/Configurations/ConfigurationDataCookie.cs +++ b/src/Jackett/Models/ConfigurationDataCookie.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public class ConfigurationDataCookie : ConfigurationData diff --git a/src/Jackett/Configurations/ConfigurationDataUrl.cs b/src/Jackett/Models/ConfigurationDataUrl.cs similarity index 78% rename from src/Jackett/Configurations/ConfigurationDataUrl.cs rename to src/Jackett/Models/ConfigurationDataUrl.cs index e302cf2fd..abf834d58 100644 --- a/src/Jackett/Configurations/ConfigurationDataUrl.cs +++ b/src/Jackett/Models/ConfigurationDataUrl.cs @@ -4,12 +4,17 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public class ConfigurationDataUrl : ConfigurationData { public StringItem Url { get; private set; } + public ConfigurationDataUrl(Uri defaultUrl) + { + Url = new StringItem { Name = "Url", Value = defaultUrl.ToString() }; + } + public ConfigurationDataUrl(string defaultUrl) { Url = new StringItem { Name = "Url", Value = defaultUrl }; diff --git a/src/Jackett/Torznab/ReleaseInfo.cs b/src/Jackett/Models/ReleaseInfo.cs similarity index 99% rename from src/Jackett/Torznab/ReleaseInfo.cs rename to src/Jackett/Models/ReleaseInfo.cs index a6845102a..b81d810e7 100644 --- a/src/Jackett/Torznab/ReleaseInfo.cs +++ b/src/Jackett/Models/ReleaseInfo.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public class ReleaseInfo: ICloneable diff --git a/src/Jackett/Torznab/ResultPage.cs b/src/Jackett/Models/ResultPage.cs similarity index 99% rename from src/Jackett/Torznab/ResultPage.cs rename to src/Jackett/Models/ResultPage.cs index f9d4b9b2e..fe238e225 100644 --- a/src/Jackett/Torznab/ResultPage.cs +++ b/src/Jackett/Models/ResultPage.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; -namespace Jackett +namespace Jackett.Models { public class ResultPage { diff --git a/src/Jackett/Models/TorznabCapabilities.cs b/src/Jackett/Models/TorznabCapabilities.cs new file mode 100644 index 000000000..012ad8e57 --- /dev/null +++ b/src/Jackett/Models/TorznabCapabilities.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Jackett.Models +{ + public class TorznabCapabilities + { + + public bool SearchAvailable { get; set; } + + public bool TVSearchAvailable { get; set; } + + public bool SupportsTVRageSearch { get; set; } + + public List Categories { get; private set; } + + public TorznabCapabilities() + { + Categories = new List(); + } + + string SupportedTVSearchParams + { + get + { + var parameters = new List() { "q", "season", "ep" }; + if (SupportsTVRageSearch) + parameters.Add("rid"); + return string.Join(",", parameters); + } + } + + public string ToXml() + { + var xdoc = new XDocument( + new XDeclaration("1.0", "UTF-8", null), + new XElement("caps", + new XElement("searching", + new XElement("search", + new XAttribute("available", SearchAvailable ? "yes" : "no"), + new XAttribute("supportedParams", "q") + ), + new XElement("tv-search", + new XAttribute("available", TVSearchAvailable ? "yes" : "no"), + new XAttribute("supportedParams", SupportedTVSearchParams) + ) + ), + new XElement("categories", + from c in Categories + select new XElement("category", + new XAttribute("id", c.ID), + new XAttribute("name", c.Name), + from sc in c.SubCategories + select new XElement("subcat", + new XAttribute("id", sc.ID), + new XAttribute("name", sc.Name) + ) + ) + ) + ) + ); + + return xdoc.Declaration.ToString() + Environment.NewLine + xdoc.ToString(); + } + } +} diff --git a/src/Jackett/Models/TorznabCategory.cs b/src/Jackett/Models/TorznabCategory.cs new file mode 100644 index 000000000..53425c540 --- /dev/null +++ b/src/Jackett/Models/TorznabCategory.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Models +{ + public class TorznabCategory + { + public string ID { get; set; } + public string Name { get; set; } + + public List SubCategories { get; private set; } + + public TorznabCategory() + { + SubCategories = new List(); + } + } +} diff --git a/src/Jackett/Torznab/TorznabQuery.cs b/src/Jackett/Models/TorznabQuery.cs similarity index 95% rename from src/Jackett/Torznab/TorznabQuery.cs rename to src/Jackett/Models/TorznabQuery.cs index ae3e18785..cbc3fdc75 100644 --- a/src/Jackett/Torznab/TorznabQuery.cs +++ b/src/Jackett/Models/TorznabQuery.cs @@ -1,4 +1,5 @@ -using System; +using Jackett.Utils; +using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; @@ -6,7 +7,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Models { public class TorznabQuery { @@ -17,7 +18,6 @@ namespace Jackett public int Limit { get; private set; } public int Offset { get; private set; } public int RageID { get; private set; } - public bool RageIDLookupEnabled { get; private set; } public int Season { get; private set; } public string Episode { get; private set; } @@ -72,8 +72,6 @@ namespace Jackett q.SanitizedSearchTerm = SanitizeSearchTerm(q.SearchTerm); } - q.RageIDLookupEnabled = query["rid_enabled"] != "0"; - if (query["cat"] != null) { q.Categories = query["cat"].Split(','); diff --git a/src/Jackett/Program.cs b/src/Jackett/Program.cs deleted file mode 100644 index 7dcd24db9..000000000 --- a/src/Jackett/Program.cs +++ /dev/null @@ -1,179 +0,0 @@ -using Jackett.Indexers; -using Newtonsoft.Json.Linq; -using NLog; -using NLog.Config; -using NLog.Targets; -using NLog.Windows.Forms; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace Jackett -{ - class Program - { - public static string AppConfigDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jackett"); - - public static Server ServerInstance { get; private set; } - - public static bool IsFirstRun { get; private set; } - - public static Logger LoggerInstance { get; private set; } - - public static ManualResetEvent ExitEvent { get; private set; } - - public static bool IsWindows { get { return Environment.OSVersion.Platform == PlatformID.Win32NT; } } - - - - static void Main(string[] args) - { - ExitEvent = new ManualResetEvent(false); - - MigrateSettingsDirectory(); - - try - { - if (!Directory.Exists(AppConfigDirectory)) - { - IsFirstRun = true; - Directory.CreateDirectory(AppConfigDirectory); - } - Console.WriteLine("App config/log directory: " + AppConfigDirectory); - } - catch (Exception ex) - { - Console.WriteLine("Missing settings directory: " + AppConfigDirectory); - Console.WriteLine("Could not create settings directory. " + ex.Message); - Application.Exit(); - return; - } - - var logConfig = new LoggingConfiguration(); - - var logFile = new FileTarget(); - logConfig.AddTarget("file", logFile); - logFile.Layout = "${longdate} ${level} ${message} \n ${exception:format=ToString}\n"; - logFile.FileName = Path.Combine(AppConfigDirectory, "log.txt"); - logFile.ArchiveFileName = "log.{#####}.txt"; - logFile.ArchiveAboveSize = 500000; - logFile.MaxArchiveFiles = 1; - logFile.KeepFileOpen = false; - logFile.ArchiveNumbering = ArchiveNumberingMode.DateAndSequence; - var logFileRule = new LoggingRule("*", LogLevel.Debug, logFile); - logConfig.LoggingRules.Add(logFileRule); - - if (Program.IsWindows) - { -#if !__MonoCS__ - var logAlert = new MessageBoxTarget(); - logConfig.AddTarget("alert", logAlert); - logAlert.Layout = "${message}"; - logAlert.Caption = "Alert"; - var logAlertRule = new LoggingRule("*", LogLevel.Fatal, logAlert); - logConfig.LoggingRules.Add(logAlertRule); -#endif - } - - var logConsole = new ConsoleTarget(); - logConfig.AddTarget("console", logConsole); - logConsole.Layout = "${longdate} ${level} ${message} ${exception:format=ToString}"; - var logConsoleRule = new LoggingRule("*", LogLevel.Debug, logConsole); - logConfig.LoggingRules.Add(logConsoleRule); - - LogManager.Configuration = logConfig; - LoggerInstance = LogManager.GetCurrentClassLogger(); - - ReadSettingsFile(); - - var serverTask = Task.Run(async () => - { - ServerInstance = new Server(); - await ServerInstance.Start(); - }); - - try - { - if (Program.IsWindows) - { -#if !__MonoCS__ - Application.Run(new Main()); -#endif - } - } - catch (Exception) - { - - } - - Console.WriteLine("Running in headless mode."); - - - - Task.WaitAll(serverTask); - Console.WriteLine("Server thread exit"); - } - - public static void RestartServer() - { - ServerInstance.Stop(); - ServerInstance = null; - var serverTask = Task.Run(async () => - { - ServerInstance = new Server(); - await ServerInstance.Start(); - }); - Task.WaitAll(serverTask); - } - - static void MigrateSettingsDirectory() - { - try - { - string oldDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Jackett"); - if (Directory.Exists(oldDir) && !Directory.Exists(AppConfigDirectory)) - { - Directory.Move(oldDir, AppConfigDirectory); - } - } - catch (Exception ex) - { - Console.WriteLine("ERROR could not migrate settings directory " + ex); - } - } - - static void ReadSettingsFile() - { - var path = Path.Combine(AppConfigDirectory, "config.json"); - if (!File.Exists(path)) - { - JObject f = new JObject(); - f.Add("port", Server.DefaultPort); - f.Add("public", true); - File.WriteAllText(path, f.ToString()); - } - - var configJson = JObject.Parse(File.ReadAllText(path)); - int port = (int)configJson.GetValue("port"); - Server.Port = port; - - Server.ListenPublic = (bool)configJson.GetValue("public"); - - Console.WriteLine("Config file path: " + path); - } - - static public void RestartAsAdmin() - { - var startInfo = new ProcessStartInfo(Application.ExecutablePath.ToString()) { Verb = "runas" }; - Process.Start(startInfo); - Environment.Exit(0); - } - } -} diff --git a/src/Jackett/Server.cs b/src/Jackett/Server.cs deleted file mode 100644 index ea70edf3c..000000000 --- a/src/Jackett/Server.cs +++ /dev/null @@ -1,301 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Web; -using System.Windows.Forms; - -namespace Jackett -{ - public class Server - { - public const int DefaultPort = 9117; - public static int Port = DefaultPort; - public static bool ListenPublic = true; - - HttpListener listener; - IndexerManager indexerManager; - WebApi webApi; - - - public Server() - { - // Allow all SSL.. sucks I know but mono on linux is having problems without it.. - ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; - - ReadServerSettingsFile(); - LoadApiKey(); - - indexerManager = new IndexerManager(); - webApi = new WebApi(indexerManager); - } - - void LoadApiKey() - { - var apiKeyFile = Path.Combine(Program.AppConfigDirectory, "api_key.txt"); - if (File.Exists(apiKeyFile)) - ApiKeyUtil.CurrentKey = File.ReadAllText(apiKeyFile).Trim(); - else - { - ApiKeyUtil.CurrentKey = ApiKeyUtil.Generate(); - File.WriteAllText(apiKeyFile, ApiKeyUtil.CurrentKey); - } - } - - public async Task Start() - { - Program.LoggerInstance.Info("Starting HTTP server on port " + Port + " listening " + (ListenPublic ? "publicly" : "privately")); - - try - { - listener = new HttpListener(); - - if (ListenPublic) - { - listener.Prefixes.Add(string.Format("http://*:{0}/", Port)); - } - else - { - listener.Prefixes.Add(string.Format("http://127.0.0.1:{0}/", Port)); - } - - listener.Start(); - webApi.server = this; - } - catch (HttpListenerException ex) - { - if (ex.ErrorCode == 5) - { - var errorStr = "App must be ran as admin for permission to use port " - + Port + Environment.NewLine + "Restart app with admin privileges?"; - if (Program.IsWindows) - { - var dialogResult = MessageBox.Show(errorStr, "Error", MessageBoxButtons.YesNo); - if (dialogResult == DialogResult.No) - { - Application.Exit(); - return; - } - else - { - Program.RestartAsAdmin(); - } - } - } - else - { - Program.LoggerInstance.Fatal(ex, "Failed to start HTTP server. " + ex.Message); - } - } - catch (Exception ex) - { - Program.LoggerInstance.Error(ex, "Error starting HTTP server: " + ex.Message); - return; - } - - Program.LoggerInstance.Info("Server started on port " + Port); - Program.LoggerInstance.Info("Accepting only requests from local system: " + (!ListenPublic)); - - while (true) - { - Exception error = null; - try - { - error = null; - var context = await listener.GetContextAsync(); - ProcessHttpRequest(context); - } - catch (ObjectDisposedException ex) - { - Program.LoggerInstance.Error(ex, "Critical error, HTTP listener was destroyed"); - Process.GetCurrentProcess().Kill(); - } - catch (Exception ex) - { - error = ex; - Program.LoggerInstance.Error(ex, "Error processing HTTP request"); - } - - if (error != null) - await Task.Delay(TimeSpan.FromSeconds(5)); - } - } - - public void Stop() - { - listener.Stop(); - listener.Abort(); - } - - async void ProcessHttpRequest(HttpListenerContext context) - { - Program.LoggerInstance.Trace("Received request: " + context.Request.Url.ToString()); - Exception exception = null; - try - { - if (await webApi.HandleRequest(context)) - { - - } - else if (context.Request.Url.AbsolutePath.StartsWith("/api/")) - { - await ProcessTorznab(context); - } - else - { - var responseBytes = Encoding.UTF8.GetBytes("Invalid request"); - await context.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); - } - } - catch (Exception ex) - { - exception = ex; - Program.LoggerInstance.Error(ex, ex.Message + ex.ToString()); - } - - if (exception != null) - { - try - { - var errorBytes = Encoding.UTF8.GetBytes(exception.Message); - await context.Response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length); - } - catch (Exception) - { - } - } - - try - { - context.Response.Close(); - } - catch (Exception) - { - } - - } - - async Task ProcessTorznab(HttpListenerContext context) - { - - var query = HttpUtility.ParseQueryString(context.Request.Url.Query); - var inputStream = context.Request.InputStream; - - var indexerId = context.Request.Url.Segments[2].TrimEnd('/').ToLower(); - var indexer = indexerManager.GetIndexer(indexerId); - - if (context.Request.Url.Segments.Length > 4 && context.Request.Url.Segments[3] == "download/") - { - var downloadSegment = HttpServerUtility.UrlTokenDecode(context.Request.Url.Segments[4].TrimEnd('/')); - var downloadLink = Encoding.UTF8.GetString(downloadSegment); - var downloadBytes = await indexer.Download(new Uri(downloadLink)); - await context.Response.OutputStream.WriteAsync(downloadBytes, 0, downloadBytes.Length); - return; - } - - var torznabQuery = TorznabQuery.FromHttpQuery(query); - - /*if (torznabQuery.RageIDLookupEnabled && indexer.RequiresRageIDLookupDisabled) - { - throw new ArgumentException("This indexer requires RageID lookup disabled"); - }*/ - - var releases = await indexer.PerformQuery(torznabQuery); - - Program.LoggerInstance.Debug(string.Format("Found {0} releases from {1}", releases.Length, indexer.DisplayName)); - - var severUrl = string.Format("{0}://{1}:{2}/", context.Request.Url.Scheme, context.Request.Url.Host, context.Request.Url.Port); - - var resultPage = new ResultPage(new ChannelInfo - { - Title = indexer.DisplayName, - Description = indexer.DisplayDescription, - Link = indexer.SiteLink, - ImageUrl = new Uri(severUrl + "logos/" + indexerId + ".png"), - ImageTitle = indexer.DisplayName, - ImageLink = indexer.SiteLink, - ImageDescription = indexer.DisplayName - }); - - // add Jackett proxy to download links... - foreach (var release in releases) - { - if (release.Link == null || release.Link.Scheme == "magnet") - continue; - var originalLink = release.Link; - var encodedLink = HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(originalLink.ToString())) + "/download.torrent"; - var proxyLink = string.Format("{0}api/{1}/download/{2}", severUrl, indexerId, encodedLink); - release.Link = new Uri(proxyLink); - } - - resultPage.Releases.AddRange(releases); - - var xml = resultPage.ToXml(new Uri(severUrl)); - - var responseBytes = Encoding.UTF8.GetBytes(xml); - context.Response.ContentEncoding = Encoding.UTF8; - context.Response.ContentLength64 = responseBytes.LongLength; - context.Response.ContentType = "application/rss+xml"; - await context.Response.OutputStream.WriteAsync(responseBytes, 0, responseBytes.Length); - - } - - - - - public JObject ReadServerSettingsFile() - { - var path = ServerConfigFile; - JObject jsonReply = new JObject(); - if (File.Exists(path)) - { - jsonReply = JObject.Parse(File.ReadAllText(path)); - Port = (int)jsonReply["port"]; - ListenPublic = (bool)jsonReply["public"]; - } - else - { - jsonReply["port"] = Port; - jsonReply["public"] = ListenPublic; - } - return jsonReply; - } - - public Task ApplyPortConfiguration(JToken json) - { - JObject jsonObject = (JObject)json; - JToken jJackettPort = jsonObject.GetValue("port"); - int jackettPort; - if (!ServerUtil.IsPort(jJackettPort.ToString())) - throw new CustomException("The value entered is not a valid port"); - else - jackettPort = int.Parse(jJackettPort.ToString()); - - if (jackettPort == Port) - throw new CustomException("The current port is the same as the one being used now."); - else if (ServerUtil.RestrictedPorts.Contains(jackettPort)) - throw new CustomException("This port is not allowed due to it not being safe."); - SaveSettings(jackettPort); - - return Task.FromResult(jackettPort); - } - - private static string ServerConfigFile = Path.Combine(Program.AppConfigDirectory, "config.json"); - - private void SaveSettings(int jacketPort) - { - JObject json = new JObject(); - json["port"] = jacketPort; - json["public"] = ListenPublic; - File.WriteAllText(ServerConfigFile, json.ToString()); - } - - } -} diff --git a/src/Jackett/Services/ConfigurationService.cs b/src/Jackett/Services/ConfigurationService.cs new file mode 100644 index 000000000..f9c59af48 --- /dev/null +++ b/src/Jackett/Services/ConfigurationService.cs @@ -0,0 +1,171 @@ +using Newtonsoft.Json.Linq; +using NLog; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Jackett.Services +{ + public interface IConfigurationService + { + string GetContentFolder(); + string GetVersion(); + string GetIndexerConfigDir(); + string GetAppDataFolder(); + string GetSonarrConfigFile(); + T GetConfig(); + void SaveConfig(T config); + string ApplicationFolder(); + } + + public class ConfigurationService : IConfigurationService + { + private ISerializeService serializeService; + private Logger logger; + + public ConfigurationService(ISerializeService s, Logger l) + { + serializeService = s; + logger = l; + CreateOrMigrateSettings(); + } + + private void CreateOrMigrateSettings() + { + try + { + if (!Directory.Exists(GetAppDataFolder())) + { + Directory.CreateDirectory(GetAppDataFolder()); + } + + logger.Debug("App config/log directory: " + GetAppDataFolder()); + } + catch (Exception ex) + { + throw new Exception("Could not create settings directory. " + ex.Message); + } + + try + { + string oldDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jackett"); + if (Directory.Exists(oldDir)) + { + foreach (var file in Directory.GetFiles(oldDir, "*", SearchOption.AllDirectories)) + { + var path = file.Replace(oldDir, ""); + var destFolder = GetAppDataFolder() + path; + if (!Directory.Exists(Path.GetDirectoryName(destFolder))) + { + Directory.CreateDirectory(Path.GetDirectoryName(destFolder)); + } + if (!File.Exists(destFolder)) + { + File.Move(file, destFolder); + } + } + Directory.Delete(oldDir, true); + } + } + catch (Exception ex) + { + logger.Error("ERROR could not migrate settings directory " + ex); + } + } + + public T GetConfig() + { + var type = typeof(T); + var fullPath = Path.Combine(GetAppDataFolder(), type.Name + ".json"); + try + { + if (!File.Exists(fullPath)) + { + logger.Debug("Config file does not exist: " + fullPath); + return default(T); + } + + return serializeService.DeSerialise(File.ReadAllText(fullPath)); + } + catch (Exception e) + { + logger.Error(e, "Error reading config file " + fullPath); + return default(T); + } + } + + public void SaveConfig(T config) + { + var type = typeof(T); + var fullPath = Path.Combine(GetAppDataFolder(), type.Name + ".json"); + try + { + var json = serializeService.Serialise(config); + if (!Directory.Exists(GetAppDataFolder())) + Directory.CreateDirectory(GetAppDataFolder()); + File.WriteAllText(fullPath, json); + } + catch (Exception e) + { + logger.Error(e, "Error reading config file " + fullPath); + } + } + + public string ApplicationFolder() + { + return Path.GetDirectoryName(Application.ExecutablePath); + } + + public string GetContentFolder() + { + // If we are debugging we can use the non copied content. + var dir = Path.Combine(ApplicationFolder(), "Content"); + if (!Directory.Exists(dir)) + { + dir = Path.Combine(ApplicationFolder(), "..\\..\\..\\Jackett\\Content"); + } + + return dir; + } + + public string GetVersion() + { + return Assembly.GetExecutingAssembly().GetName().Version.ToString(); + } + + public string GetAppDataFolder() + { + return GetAppDataFolderStatic(); + } + + /// + /// This is needed for the logger prior to ioc setup. + /// + /// + public static string GetAppDataFolderStatic() + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Jackett"); + } + + public string GetIndexerConfigDir() + { + return Path.Combine(GetAppDataFolder(), "Indexers"); + } + + public string GetConfigFile() + { + return Path.Combine(GetAppDataFolder(), "config.json"); + } + + public string GetSonarrConfigFile() + { + return Path.Combine(GetAppDataFolder(), "sonarr_api.json"); + } + } +} diff --git a/src/Jackett/Services/IndexerManagerService.cs b/src/Jackett/Services/IndexerManagerService.cs new file mode 100644 index 000000000..b6491fa78 --- /dev/null +++ b/src/Jackett/Services/IndexerManagerService.cs @@ -0,0 +1,104 @@ +using Autofac; +using Jackett.Indexers; +using Jackett.Models; +using Jackett.Utils; +using Newtonsoft.Json.Linq; +using NLog; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Services +{ + public interface IIndexerManagerService + { + Task TestIndexer(string name); + void DeleteIndexer(string name); + IIndexer GetIndexer(string name); + IEnumerable GetAllIndexers(); + void SaveConfig(IIndexer indexer, JToken obj); + void InitIndexers(); + } + + public class IndexerManagerService : IIndexerManagerService + { + private IContainer container; + private IConfigurationService configService; + private Logger logger; + private Dictionary indexers = new Dictionary(); + + public IndexerManagerService(IContainer c, IConfigurationService config, Logger l) + { + container = c; + configService = config; + logger = l; + } + + public void InitIndexers() + { + foreach (var idx in container.Resolve>().OrderBy(_ => _.DisplayName)) + { + indexers.Add(idx.ID, idx); + var configFilePath = GetIndexerConfigFilePath(idx); + if (File.Exists(configFilePath)) + { + var jsonString = JObject.Parse(File.ReadAllText(configFilePath)); + idx.LoadFromSavedConfiguration(jsonString); + } + } + } + + public IIndexer GetIndexer(string name) + { + var indexer = indexers.Values.Where(i => string.Equals(StringUtil.StripNonAlphaNumeric(i.DisplayName), name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault(); + if (indexer != null) + { + return indexer; + } + else + { + logger.Error("Request for unknown indexer: " + name); + throw new Exception("Unknown indexer: " + name); + } + } + + public IEnumerable GetAllIndexers() + { + return indexers.Values; + } + + public async Task TestIndexer(string name) + { + var indexer = GetIndexer(name); + var browseQuery = new TorznabQuery(); + var results = await indexer.PerformQuery(browseQuery); + logger.Debug(string.Format("Found {0} releases from {1}", results.Length, indexer.DisplayName)); + if (results.Length == 0) + throw new Exception("Found no results while trying to browse this tracker"); + } + + public void DeleteIndexer(string name) + { + var indexer = GetIndexer(name); + var configPath = GetIndexerConfigFilePath(indexer); + File.Delete(configPath); + indexers[name] = container.ResolveNamed(indexer.ID); + } + + private string GetIndexerConfigFilePath(IIndexer indexer) + { + return Path.Combine(configService.GetIndexerConfigDir(), indexer.ID + ".json"); + } + + public void SaveConfig(IIndexer indexer, JToken obj) + { + var configFilePath = GetIndexerConfigFilePath(indexer); + if (!Directory.Exists(configService.GetIndexerConfigDir())) + Directory.CreateDirectory(configService.GetIndexerConfigDir()); + File.WriteAllText(configFilePath, obj.ToString()); + } + } +} diff --git a/src/Jackett/Services/ProcessService.cs b/src/Jackett/Services/ProcessService.cs new file mode 100644 index 000000000..1ef3bac96 --- /dev/null +++ b/src/Jackett/Services/ProcessService.cs @@ -0,0 +1,64 @@ +using NLog; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Services +{ + public interface IProcessService + { + void StartProcessAndLog(string exe, string args); + } + + public class ProcessService : IProcessService + { + private Logger logger; + + public ProcessService(Logger l) + { + logger = l; + } + + public void StartProcessAndLog(string exe, string args) + { + var startInfo = new ProcessStartInfo() + { + CreateNoWindow = true, + UseShellExecute = false, + FileName = exe, + Arguments = args, + RedirectStandardError = true, + RedirectStandardOutput = true, + RedirectStandardInput = true + }; + + var proc = Process.Start(startInfo); + proc.OutputDataReceived += proc_OutputDataReceived; + proc.ErrorDataReceived += proc_ErrorDataReceived; + proc.BeginErrorReadLine(); + proc.BeginOutputReadLine(); + proc.WaitForExit(); + proc.OutputDataReceived -= proc_OutputDataReceived; + proc.ErrorDataReceived -= proc_ErrorDataReceived; + } + + void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e) + { + if (!string.IsNullOrWhiteSpace(e.Data)) + { + logger.Error(e.Data); + } + } + + void proc_OutputDataReceived(object sender, DataReceivedEventArgs e) + { + if (!string.IsNullOrWhiteSpace(e.Data)) + { + logger.Debug(e.Data); + } + } + } +} diff --git a/src/Jackett/Services/SecuityService.cs b/src/Jackett/Services/SecuityService.cs new file mode 100644 index 000000000..3ec85acaf --- /dev/null +++ b/src/Jackett/Services/SecuityService.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +namespace Jackett.Services +{ + public interface ISecuityService + { + bool CheckAuthorised(HttpRequestMessage request); + string HashPassword(string input); + void Login(HttpResponseMessage request); + void Logout(HttpResponseMessage request); + } + + class SecuityService : ISecuityService + { + private const string COOKIENAME = "JACKETT"; + private IServerService serverService; + + public SecuityService(IServerService ss) + { + serverService = ss; + } + + public string HashPassword(string input) + { + // Append key as salt + input += serverService.Config.APIKey; + + UnicodeEncoding UE = new UnicodeEncoding(); + byte[] hashValue; + byte[] message = UE.GetBytes(input); + + SHA512Managed hashString = new SHA512Managed(); + string hex = ""; + + hashValue = hashString.ComputeHash(message); + foreach (byte x in hashValue) + { + hex += String.Format("{0:x2}", x); + } + return hex; + } + + public void Login(HttpResponseMessage response) + { + // Login + response.Headers.Add("Set-Cookie", COOKIENAME + "=" + serverService.Config.AdminPassword + "; path=/"); + } + + public void Logout(HttpResponseMessage response) + { + // Logout + response.Headers.Add("Set-Cookie", COOKIENAME + "=; path=/"); + } + + public bool CheckAuthorised(HttpRequestMessage request) + { + if (string.IsNullOrEmpty(Engine.Server.Config.AdminPassword)) + return true; + + try + { + var cookie = request.Headers.GetCookies(COOKIENAME).FirstOrDefault(); + if (cookie != null) + { + return cookie[COOKIENAME].Value == serverService.Config.AdminPassword; + } + } + catch { } + + return false; + } + } +} diff --git a/src/Jackett/Services/SerializeService.cs b/src/Jackett/Services/SerializeService.cs new file mode 100644 index 000000000..dacbf9ebb --- /dev/null +++ b/src/Jackett/Services/SerializeService.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Services +{ + public interface ISerializeService + { + string Serialise(object obj); + T DeSerialise(string json); + } + + class SerializeService : ISerializeService + { + public string Serialise(object obj) + { + return JsonConvert.SerializeObject(obj,Formatting.Indented); + } + + public T DeSerialise(string json) + { + try + { + return JsonConvert.DeserializeObject(json); + } + catch + { + return default(T); + } + } + } +} diff --git a/src/Jackett/Services/ServerService.cs b/src/Jackett/Services/ServerService.cs new file mode 100644 index 000000000..d406b1e77 --- /dev/null +++ b/src/Jackett/Services/ServerService.cs @@ -0,0 +1,147 @@ +using Autofac; +using Jackett.Models.Config; +using Jackett.Services; +using Microsoft.Owin.Hosting; +using Newtonsoft.Json.Linq; +using NLog; +using NLog.Config; +using NLog.Targets; +using NLog.Windows.Forms; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Services +{ + public interface IServerService + { + void Start(); + void Stop(); + void ReserveUrls(bool doInstall = true); + ServerConfig Config { get; } + void SaveConfig(); + } + + public class ServerService : IServerService + { + private ServerConfig config; + + private IDisposable _server = null; + + private IIndexerManagerService indexerService; + private IProcessService processService; + private ISerializeService serializeService; + private IConfigurationService configService; + private Logger logger; + + public ServerService(IIndexerManagerService i, IProcessService p, ISerializeService s, IConfigurationService c, Logger l) + { + indexerService = i; + processService = p; + serializeService = s; + configService = c; + logger = l; + + LoadConfig(); + } + + public ServerConfig Config + { + get { return config; } + } + + private void LoadConfig() + { + // Load config + config = configService.GetConfig(); + if (config == null) + { + config = new ServerConfig(); + } + + if (string.IsNullOrWhiteSpace(config.APIKey)) + { + // Check for legacy key config + var apiKeyFile = Path.Combine(configService.GetAppDataFolder(), "api_key.txt"); + if (File.Exists(apiKeyFile)) + { + config.APIKey = File.ReadAllText(apiKeyFile); + } + + // Check for legacy settings + + var path = Path.Combine(configService.GetAppDataFolder(), "config.json"); ; + var jsonReply = new JObject(); + if (File.Exists(path)) + { + jsonReply = JObject.Parse(File.ReadAllText(path)); + config.Port = (int)jsonReply["port"]; + config.AllowExternal = (bool)jsonReply["public"]; + } + + if (string.IsNullOrWhiteSpace(config.APIKey)) + { + config.APIKey = config.GenerateApi(); + } + + configService.SaveConfig(config); + } + } + + public void SaveConfig() + { + configService.SaveConfig(config); + } + + public void Start() + { + CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US"); + + // Allow all SSL.. sucks I know but mono on linux is having problems without it.. + ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; + + // Load indexers + indexerService.InitIndexers(); + + // Start the server + logger.Debug("Starting web server at " + config.GetListenAddresses()[0]); + var startOptions = new StartOptions(); + config.GetListenAddresses().ToList().ForEach(u => startOptions.Urls.Add(u)); + _server = WebApp.Start(startOptions); + logger.Debug("Web server started"); + } + + public void ReserveUrls(bool doInstall = true) + { + logger.Debug("Unreserving Urls"); + config.GetListenAddresses(false).ToList().ForEach(u => RunNetSh(string.Format("http delete urlacl {0}", u))); + config.GetListenAddresses(true).ToList().ForEach(u => RunNetSh(string.Format("http delete urlacl {0}", u))); + if (doInstall) + { + logger.Debug("Reserving Urls"); + config.GetListenAddresses(true).ToList().ForEach(u => RunNetSh(string.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", u))); + logger.Debug("Urls reserved"); + } + } + + private void RunNetSh(string args) + { + processService.StartProcessAndLog("netsh.exe", args); + } + + public void Stop() + { + if (_server != null) + { + _server.Dispose(); + } + } + } +} diff --git a/src/Jackett/Services/ServiceConfigService.cs b/src/Jackett/Services/ServiceConfigService.cs new file mode 100644 index 000000000..12886e931 --- /dev/null +++ b/src/Jackett/Services/ServiceConfigService.cs @@ -0,0 +1,114 @@ +using NLog; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration.Install; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Services +{ + public interface IServiceConfigService + { + void Install(); + void Uninstall(); + } + + class ServiceConfigService : IServiceConfigService + { + private const string NAME = "Jackett"; + private const string DESCRIPTION = "Additional indexers for Sonarr"; + private const string SERVICEEXE = "JackettService.exe"; + + private IConfigurationService configService; + private Logger logger; + + public ServiceConfigService(IConfigurationService c, Logger l) + { + configService = c; + logger = l; + } + + public bool Exists() + { + return GetService(NAME) != null; + } + + public ServiceController GetService(string serviceName) + { + return ServiceController.GetServices().FirstOrDefault(c => String.Equals(c.ServiceName, serviceName, StringComparison.InvariantCultureIgnoreCase)); + } + + public void Install() + { + if (Exists()) + { + + } + else + { + var installer = new ServiceProcessInstaller + { + Account = ServiceAccount.NetworkService + }; + + var serviceInstaller = new ServiceInstaller(); + + var exePath = Path.Combine(configService.ApplicationFolder(), SERVICEEXE); + if (!File.Exists(exePath) && Debugger.IsAttached) + { + exePath = Path.Combine(configService.ApplicationFolder(), "..\\..\\..\\Jackett.Service\\bin\\Debug", SERVICEEXE); + } + + string[] cmdline = { @"/assemblypath=" + exePath}; + + var context = new InstallContext("jackettservice_install.log", cmdline); + serviceInstaller.Context = context; + serviceInstaller.DisplayName = NAME; + serviceInstaller.ServiceName = NAME; + serviceInstaller.Description = DESCRIPTION; + serviceInstaller.StartType = ServiceStartMode.Automatic; + serviceInstaller.Parent = installer; + + serviceInstaller.Install(new ListDictionary()); + } + } + + public void Uninstall() + { + Stop(); + + var serviceInstaller = new ServiceInstaller(); + + var context = new InstallContext("jackettservice_uninstall.log", null); + serviceInstaller.Context = context; + serviceInstaller.ServiceName = NAME; + serviceInstaller.Uninstall(null); + + logger.Info("The service was uninstalled."); + + } + + public void Stop() + { + var service = GetService(NAME); + if (service.Status != ServiceControllerStatus.Stopped) + { + service.Stop(); + service.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(60)); + + service.Refresh(); + if (service.Status == ServiceControllerStatus.Stopped) + logger.Info("Service stopped."); + else + logger.Error("Failed to stop the service"); + } + else + logger.Warn("The service was already stopped"); + } + } +} diff --git a/src/Jackett/Services/SpinService.cs b/src/Jackett/Services/SpinService.cs new file mode 100644 index 000000000..8d8e5d0b0 --- /dev/null +++ b/src/Jackett/Services/SpinService.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Jackett.Services +{ + public interface IRunTimeService + { + void Spin(); + } + + class RunTimeService : IRunTimeService + { + private bool isRunning = true; + + public void Spin() + { + while (isRunning) + { + Thread.Sleep(2000); + } + } + } +} diff --git a/src/Jackett/Startup.cs b/src/Jackett/Startup.cs new file mode 100644 index 000000000..56dbb8f59 --- /dev/null +++ b/src/Jackett/Startup.cs @@ -0,0 +1,88 @@ +using Owin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using Autofac.Integration.WebApi; +using Microsoft.Owin; +using Jackett; +using Microsoft.Owin.StaticFiles; +using Microsoft.Owin.FileSystems; +using Autofac; +using Jackett.Services; +using System.Web.Http.Tracing; +using Jackett.Utils; +using Microsoft.Owin.Security.Cookies; +using Microsoft.AspNet.Identity; + +[assembly: OwinStartup(typeof(Startup))] +namespace Jackett +{ + public class Startup + { + public void Configuration(IAppBuilder appBuilder) + { + // Configure Web API for self-host. + var config = new HttpConfiguration(); + + appBuilder.Use(); + + // Setup tracing if enabled + if (Engine.TracingEnabled) + { + config.EnableSystemDiagnosticsTracing(); + config.Services.Replace(typeof(ITraceWriter), new WebAPIToNLogTracer()); + } + // Add request logging if enabled + if (Engine.LogRequests) + { + config.MessageHandlers.Add(new WebAPIRequestLogger()); + } + config.DependencyResolver = new AutofacWebApiDependencyResolver(Engine.GetContainer()); + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "Admin", + routeTemplate: "admin/{action}", + defaults: new { controller = "Admin" } + ); + + config.Routes.MapHttpRoute( + name: "apiDefault", + routeTemplate: "api/{indexerName}", + defaults: new { controller = "API", action = "Call" } + ); + + config.Routes.MapHttpRoute( + name: "api", + routeTemplate: "api/{indexerName}/api", + defaults: new { controller = "API", action = "Call" } + ); + + config.Routes.MapHttpRoute( + name: "download", + routeTemplate: "api/{indexerName}/download/{path}/download.torrent", + defaults: new { controller = "Download", action = "Download" } + ); + + appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions + { + AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, + LoginPath = new PathString("/Admin/Login") + }); + + appBuilder.UseFileServer(new FileServerOptions + { + RequestPath = new PathString(string.Empty), + FileSystem = new PhysicalFileSystem(Engine.ConfigService.GetContentFolder()), + EnableDirectoryBrowsing = false, + + }); + + appBuilder.UseWebApi(config); + } + } +} diff --git a/src/Jackett/Utils/ApiKeyUtil.cs b/src/Jackett/Utils/ApiKeyUtil.cs deleted file mode 100644 index b6b449aab..000000000 --- a/src/Jackett/Utils/ApiKeyUtil.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; - -namespace Jackett -{ - public static class ApiKeyUtil - { - public static string CurrentKey; - - const string chars = "abcdefghijklmnopqrstuvwxyz0123456789"; - - public static string Generate() - { - var randBytes = new byte[32]; - var rngCsp = new RNGCryptoServiceProvider(); - rngCsp.GetBytes(randBytes); - var key = ""; - foreach (var b in randBytes) - { - key += chars[b % chars.Length]; - } - return key; - - } - } -} diff --git a/src/Jackett/Utils/BrowserUtil.cs b/src/Jackett/Utils/BrowserUtil.cs index 82154b176..669b1e346 100644 --- a/src/Jackett/Utils/BrowserUtil.cs +++ b/src/Jackett/Utils/BrowserUtil.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Utils { public static class BrowserUtil { diff --git a/src/Jackett/Utils/DataUrl.cs b/src/Jackett/Utils/DataUrl.cs index dff8bf509..07b08508b 100644 --- a/src/Jackett/Utils/DataUrl.cs +++ b/src/Jackett/Utils/DataUrl.cs @@ -6,9 +6,9 @@ using System.Text; using System.Threading.Tasks; using System.Web; -namespace Jackett +namespace Jackett.Utils { - public static class DataUrlUtil + public class DataUrlUtils { public static string ReadFileToDataUrl(string file) { diff --git a/src/Jackett/Utils/DateTimeUtil.cs b/src/Jackett/Utils/DateTimeUtil.cs index 7e5989661..093df7a76 100644 --- a/src/Jackett/Utils/DateTimeUtil.cs +++ b/src/Jackett/Utils/DateTimeUtil.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Utils { public static class DateTimeUtil { @@ -14,6 +14,5 @@ namespace Jackett long unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond); return new DateTime(unixStart.Ticks + unixTimeStampInTicks); } - } } diff --git a/src/Jackett/Utils/JackettAuthorizedAttribute.cs b/src/Jackett/Utils/JackettAuthorizedAttribute.cs new file mode 100644 index 000000000..3a0e77a78 --- /dev/null +++ b/src/Jackett/Utils/JackettAuthorizedAttribute.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http; +using System.Web.Http.Controllers; +using System.Web.Http.Filters; + +namespace Jackett.Utils +{ + public class JackettAuthorizedAttribute : AuthorizationFilterAttribute + { + public override void OnAuthorization(HttpActionContext actionContext) + { + // Skip authorisation on blank passwords + if (string.IsNullOrEmpty(Engine.Server.Config.AdminPassword)) + { + return; + } + + if (!Engine.SecurityService.CheckAuthorised(actionContext.Request)) + { + if(actionContext.ControllerContext.ControllerDescriptor.ControllerType.GetCustomAttributes(true).Where(a => a.GetType() == typeof(AllowAnonymousAttribute)).Any()) + { + return; + } + + if (actionContext.ControllerContext.ControllerDescriptor.ControllerType.GetMethod(actionContext.ActionDescriptor.ActionName).GetCustomAttributes(true).Where(a => a.GetType() == typeof(AllowAnonymousAttribute)).Any()) + { + return; + } + + + actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode + .Unauthorized); + } + } + } +} diff --git a/src/Jackett/Utils/ParseUtil.cs b/src/Jackett/Utils/ParseUtil.cs index 9b62c0027..c96e2be7a 100644 --- a/src/Jackett/Utils/ParseUtil.cs +++ b/src/Jackett/Utils/ParseUtil.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Utils { public static class ParseUtil { diff --git a/src/Jackett/Utils/ServerUtil.cs b/src/Jackett/Utils/ServerUtil.cs index 8f3e6c02f..a65f1f2f7 100644 --- a/src/Jackett/Utils/ServerUtil.cs +++ b/src/Jackett/Utils/ServerUtil.cs @@ -5,7 +5,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -namespace Jackett +namespace Jackett.Utils { public static class ServerUtil { diff --git a/src/Jackett/Utils/StringUtil.cs b/src/Jackett/Utils/StringUtil.cs new file mode 100644 index 000000000..6a40ca5f9 --- /dev/null +++ b/src/Jackett/Utils/StringUtil.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Jackett.Utils +{ + public static class StringUtil + { + public static string StripNonAlphaNumeric(string str) + { + Regex rgx = new Regex("[^a-zA-Z0-9 -]"); + str = rgx.Replace(str, ""); + return str; + } + + public static string FromBase64(string str) + { + return Encoding.UTF8.GetString(Convert.FromBase64String(str)); + } + + } +} diff --git a/src/Jackett/Utils/TorznabCapsUtil.cs b/src/Jackett/Utils/TorznabCapsUtil.cs new file mode 100644 index 000000000..789d38236 --- /dev/null +++ b/src/Jackett/Utils/TorznabCapsUtil.cs @@ -0,0 +1,27 @@ +using Jackett.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Utils +{ + public static class TorznabCapsUtil + { + public static TorznabCapabilities CreateDefaultTorznabTVCaps() + { + var caps = new TorznabCapabilities(); + caps.SearchAvailable = true; + caps.TVSearchAvailable = true; + caps.SupportsTVRageSearch = false; + caps.Categories.AddRange(new[] { + new TorznabCategory { ID = "5000", Name = "TV" }, + new TorznabCategory { ID = "5030", Name = "TV/SD" }, + new TorznabCategory { ID = "5040", Name = "TV/HD" } + }); + return caps; + } + + } +} diff --git a/src/Jackett/Utils/WebAPIRequestLogger.cs b/src/Jackett/Utils/WebAPIRequestLogger.cs new file mode 100644 index 000000000..9cda729c6 --- /dev/null +++ b/src/Jackett/Utils/WebAPIRequestLogger.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Jackett.Utils +{ + class WebAPIRequestLogger : DelegatingHandler + { + protected override async Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) + { + //logging request body + string requestBody = await request.Content.ReadAsStringAsync(); + Trace.WriteLine(requestBody); + Engine.Logger.Debug(request.Method + ": " + request.RequestUri); + Engine.Logger.Debug("Body: " + requestBody); + + //let other handlers process the request + return await base.SendAsync(request, cancellationToken) + .ContinueWith(task => + { + if (null != task.Result.Content) + { + //once response is ready, log it + var responseBody = task.Result.Content.ReadAsStringAsync().Result; + Trace.WriteLine(responseBody); + Engine.Logger.Debug("Response: " + responseBody); + } + return task.Result; + }); + } + } +} diff --git a/src/Jackett/Utils/WebAPIToNLogTracer.cs b/src/Jackett/Utils/WebAPIToNLogTracer.cs new file mode 100644 index 000000000..810bc60ec --- /dev/null +++ b/src/Jackett/Utils/WebAPIToNLogTracer.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http.Tracing; + +namespace Jackett.Utils +{ + public class WebAPIToNLogTracer : ITraceWriter + { + public void Trace(HttpRequestMessage request, string category, TraceLevel level, + Action traceAction) + { + if (Engine.TracingEnabled) + { + TraceRecord rec = new TraceRecord(request, category, level); + traceAction(rec); + WriteTrace(rec); + } + } + + protected void WriteTrace(TraceRecord rec) + { + var message = string.Format("{0} {1} {2}", rec.Operator, rec.Operation, rec.Message); + switch (rec.Level) + { + case TraceLevel.Debug: + Engine.Logger.Debug(message); + break; + case TraceLevel.Error: + Engine.Logger.Error(message); + break; + case TraceLevel.Fatal: + Engine.Logger.Fatal(message); + break; + case TraceLevel.Info: + Engine.Logger.Info(message); + break; + case TraceLevel.Off: + // Do nothing? + break; + case TraceLevel.Warn: + Engine.Logger.Warn(message); + break; + + } + + System.Diagnostics.Trace.WriteLine(message, rec.Category); + } + } +} diff --git a/src/Jackett/Utils/WebApiRootRedirectMiddleware.cs b/src/Jackett/Utils/WebApiRootRedirectMiddleware.cs new file mode 100644 index 000000000..68959f76d --- /dev/null +++ b/src/Jackett/Utils/WebApiRootRedirectMiddleware.cs @@ -0,0 +1,32 @@ +using Microsoft.Owin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Jackett.Utils +{ + public class WebApiRootRedirectMiddleware : OwinMiddleware + { + public WebApiRootRedirectMiddleware(OwinMiddleware next) + : base(next) + { + } + + public async override Task Invoke(IOwinContext context) + { + var url = context.Request.Uri; + if (string.IsNullOrWhiteSpace(url.AbsolutePath) || url.AbsolutePath == "/") + { + // 301 is the status code of permanent redirect + context.Response.StatusCode = 301; + context.Response.Headers.Set("Location", "/Admin/Dashboard"); + } + else + { + await Next.Invoke(context); + } + } + } +} \ No newline at end of file diff --git a/src/Jackett/WebApi.cs b/src/Jackett/WebApi.cs deleted file mode 100644 index 5c1d70ed5..000000000 --- a/src/Jackett/WebApi.cs +++ /dev/null @@ -1,325 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using System.Web; - -namespace Jackett -{ - public class WebApi - { - static string WebContentFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WebContent"); - static string[] StaticFiles = Directory.EnumerateFiles(WebContentFolder, "*", SearchOption.AllDirectories).ToArray(); - public Server server; - - public enum WebApiMethod - { - GetConfigForm, - ConfigureIndexer, - GetIndexers, - TestIndexer, - DeleteIndexer, - GetSonarrConfig, - ApplySonarrConfig, - GetJackettConfig, - ApplyJackettConfig, - JackettRestart, - } - - static Dictionary WebApiMethods = new Dictionary { - { "get_config_form", WebApiMethod.GetConfigForm }, - { "configure_indexer", WebApiMethod.ConfigureIndexer }, - { "get_indexers", WebApiMethod.GetIndexers }, - { "test_indexer", WebApiMethod.TestIndexer }, - { "delete_indexer", WebApiMethod.DeleteIndexer }, - { "get_sonarr_config", WebApiMethod.GetSonarrConfig }, - { "apply_sonarr_config", WebApiMethod.ApplySonarrConfig }, - { "get_jackett_config",WebApiMethod.GetJackettConfig}, - { "apply_jackett_config",WebApiMethod.ApplyJackettConfig}, - { "jackett_restart", WebApiMethod.JackettRestart }, - }; - - IndexerManager indexerManager; - - public WebApi(IndexerManager indexerManager) - { - this.indexerManager = indexerManager; - } - - public async Task HandleRequest(HttpListenerContext context) - { - string path = context.Request.Url.AbsolutePath.TrimStart('/'); - if (path == "") - path = "index.html"; - - var sysPath = Path.Combine(WebContentFolder, path.Replace("/", Path.DirectorySeparatorChar.ToString())); - if (Array.IndexOf(StaticFiles, sysPath) > -1) - { - await ServeStaticFile(context, path); - return true; - } - - WebApi.WebApiMethod apiMethod; - if (WebApi.WebApiMethods.TryGetValue(path, out apiMethod)) - { - await ProcessWebApiRequest(context, apiMethod); - return true; - } - - return false; - } - - async Task ServeStaticFile(HttpListenerContext context, string file) - { - var contentFile = File.ReadAllBytes(Path.Combine(WebContentFolder, file)); - context.Response.ContentType = MimeMapping.GetMimeMapping(file); - context.Response.StatusCode = (int)HttpStatusCode.OK; - try - { - await context.Response.OutputStream.WriteAsync(contentFile, 0, contentFile.Length); - } - catch (HttpListenerException) - { - } - } - - async Task ReadPostDataJson(Stream stream) - { - string postData = await new StreamReader(stream).ReadToEndAsync(); - return JObject.Parse(postData); - } - - delegate Task HandlerTask(HttpListenerContext context); - - async Task ProcessWebApiRequest(HttpListenerContext context, WebApiMethod method) - { - context.Response.ContentType = "text/json"; - context.Response.StatusCode = (int)HttpStatusCode.OK; - - HandlerTask handlerTask; - - switch (method) - { - case WebApiMethod.GetConfigForm: - handlerTask = HandleConfigForm; - break; - case WebApiMethod.ConfigureIndexer: - handlerTask = HandleConfigureIndexer; - break; - case WebApiMethod.GetIndexers: - handlerTask = HandleGetIndexers; - break; - case WebApiMethod.TestIndexer: - handlerTask = HandleTestIndexer; - break; - case WebApiMethod.DeleteIndexer: - handlerTask = HandleDeleteIndexer; - break; - case WebApiMethod.ApplyJackettConfig: - handlerTask = HandleApplyJackettConfig; - break; - case WebApiMethod.GetJackettConfig: - handlerTask = HandleJackettConfig; - break; - case WebApiMethod.JackettRestart: - handlerTask = HandleJackettRestart; - break; - default: - handlerTask = HandleInvalidApiMethod; - break; - } - JToken jsonReply = await handlerTask(context); - await ReplyWithJson(context, jsonReply, method.ToString()); - } - - async Task ReplyWithJson(HttpListenerContext context, JToken json, string apiCall) - { - try - { - byte[] jsonBytes = Encoding.UTF8.GetBytes(json.ToString()); - await context.Response.OutputStream.WriteAsync(jsonBytes, 0, jsonBytes.Length); - } - catch (Exception ex) - { - Console.WriteLine("Error writing json to stream for API call " + apiCall + Environment.NewLine + ex.ToString()); - } - } - - Task HandleInvalidApiMethod(HttpListenerContext context) - { - JToken jsonReply = new JObject(); - jsonReply["result"] = "error"; - jsonReply["error"] = "Invalid API method"; - return Task.FromResult(jsonReply); - } - - async Task HandleConfigForm(HttpListenerContext context) - { - JToken jsonReply = new JObject(); - try - { - var postData = await ReadPostDataJson(context.Request.InputStream); - string indexerString = (string)postData["indexer"]; - var indexer = indexerManager.GetIndexer(indexerString); - var config = await indexer.GetConfigurationForSetup(); - jsonReply["config"] = config.ToJson(); - jsonReply["name"] = indexer.DisplayName; - jsonReply["result"] = "success"; - } - catch (Exception ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - } - return jsonReply; - } - - async Task HandleConfigureIndexer(HttpListenerContext context) - { - JToken jsonReply = new JObject(); - try - { - var postData = await ReadPostDataJson(context.Request.InputStream); - string indexerString = (string)postData["indexer"]; - var indexer = indexerManager.GetIndexer(indexerString); - jsonReply["name"] = indexer.DisplayName; - await indexer.ApplyConfiguration(postData["config"]); - await indexerManager.TestIndexer(indexer); - jsonReply["result"] = "success"; - } - catch (Exception ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - if (ex is ExceptionWithConfigData) - { - jsonReply["config"] = ((ExceptionWithConfigData)ex).ConfigData.ToJson(); - } - } - return jsonReply; - } - - Task HandleGetIndexers(HttpListenerContext context) - { - JToken jsonReply = new JObject(); - try - { - jsonReply["result"] = "success"; - jsonReply["api_key"] = ApiKeyUtil.CurrentKey; - jsonReply["app_version"] = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - JArray items = new JArray(); - foreach (var i in indexerManager.Indexers.OrderBy(_ => _.Key)) - { - var indexer = i.Value; - var item = new JObject(); - item["id"] = i.Key; - item["name"] = indexer.DisplayName; - item["description"] = indexer.DisplayDescription; - item["configured"] = indexer.IsConfigured; - item["site_link"] = indexer.SiteLink; - items.Add(item); - } - jsonReply["items"] = items; - } - catch (Exception ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - } - return Task.FromResult(jsonReply); - } - - async Task HandleTestIndexer(HttpListenerContext context) - { - JToken jsonReply = new JObject(); - try - { - var postData = await ReadPostDataJson(context.Request.InputStream); - string indexerString = (string)postData["indexer"]; - var indexer = indexerManager.GetIndexer(indexerString); - jsonReply["name"] = indexer.DisplayName; - await indexerManager.TestIndexer(indexer); - jsonReply["result"] = "success"; - } - catch (Exception ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - } - return jsonReply; - } - - async Task HandleDeleteIndexer(HttpListenerContext context) - { - JToken jsonReply = new JObject(); - try - { - var postData = await ReadPostDataJson(context.Request.InputStream); - string indexerString = (string)postData["indexer"]; - indexerManager.DeleteIndexer(indexerString); - } - catch (Exception ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - } - return jsonReply; - } - - - //Jacket port functions - Task HandleJackettConfig(HttpListenerContext context) - { - JObject jsonReply = new JObject(); - try - { - jsonReply["config"] = server.ReadServerSettingsFile(); - jsonReply["result"] = "success"; - } - catch (CustomException ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - } - catch (Exception ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - } - return Task.FromResult(jsonReply); - } - - async Task HandleApplyJackettConfig(HttpListenerContext context) - { - JToken jsonReply = new JObject(); - - try - { - var postData = await ReadPostDataJson(context.Request.InputStream); - int port = await server.ApplyPortConfiguration(postData); - jsonReply["result"] = "success"; - jsonReply["port"] = port; - } - catch (Exception ex) - { - jsonReply["result"] = "error"; - jsonReply["error"] = ex.Message; - } - return jsonReply; - } - - async Task HandleJackettRestart(HttpListenerContext context) - { - Program.RestartServer(); - return null; - } - - - } -} diff --git a/src/Jackett/packages.config b/src/Jackett/packages.config index efab650c9..805887fbf 100644 --- a/src/Jackett/packages.config +++ b/src/Jackett/packages.config @@ -1,7 +1,27 @@  - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file