Refactor done

This commit is contained in:
Kayomani
2015-07-19 14:22:50 +01:00
parent e6c2138b32
commit e50e937a69
94 changed files with 1338 additions and 1407 deletions

15
Build.bat Normal file
View File

@@ -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 ..

58
Installer.iss Normal file
View File

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

View File

@@ -77,6 +77,10 @@
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.4.0.1\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL"> <Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath> <HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private> <Private>True</Private>

View File

@@ -16,62 +16,34 @@ namespace JackettConsole
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
Server.Start();
Console.ReadKey();
/* var serverTask = Task.Run(async () =>
{
ServerInstance = new Server();
await ServerInstance.Start();
});
try try
{ {
if (Program.IsWindows) if (args.Length > 0)
{ {
#if !__MonoCS__ switch (args[0].ToLowerInvariant())
Application.Run(new Main()); {
#endif case "/i":
Engine.ServiceConfig.Install();
return;
case "/r":
Engine.Server.ReserveUrls();
return;
case "/u":
Engine.Server.ReserveUrls(false);
Engine.ServiceConfig.Uninstall();
return;
}
} }
Engine.Server.Start();
Engine.Logger.Info("Running in headless mode.");
Engine.RunTime.Spin();
Engine.Logger.Info("Server thread exit");
} }
catch (Exception) catch(Exception e)
{ {
Engine.Logger.Error(e, "Top level 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 public void RestartAsAdmin()
{
// var startInfo = new ProcessStartInfo(Application.ExecutablePath.ToString()) { Verb = "runas" };
// Process.Start(startInfo);
Environment.Exit(0);
} }
} }
} }

View File

@@ -14,5 +14,6 @@
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" userInstalled="true" /> <package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" userInstalled="true" />
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" userInstalled="true" /> <package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" userInstalled="true" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" userInstalled="true" /> <package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" userInstalled="true" />
<package id="NLog" version="4.0.1" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" userInstalled="true" /> <package id="Owin" version="1.0" targetFramework="net452" userInstalled="true" />
</packages> </packages>

View File

@@ -1,6 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup> </startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration> </configuration>

View File

@@ -36,8 +36,65 @@
<ApplicationIcon>jackett.ico</ApplicationIcon> <ApplicationIcon>jackett.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Autofac.3.5.0\lib\net40\Autofac.dll</HintPath>
</Reference>
<Reference Include="Autofac.Integration.Owin">
<HintPath>..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll</HintPath>
</Reference>
<Reference Include="Autofac.Integration.WebApi, Version=3.4.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll</HintPath>
</Reference>
<Reference Include="Autofac.Integration.WebApi.Owin">
<HintPath>..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.FileSystems, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.FileSystems.3.0.1\lib\net45\Microsoft.Owin.FileSystems.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Host.HttpListener">
<HintPath>..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Hosting, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.StaticFiles, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.4.0.1\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.Owin, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
@@ -58,10 +115,17 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
<None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="jackett.ico" /> <Content Include="jackett.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jackett\Jackett.csproj">
<Project>{e636d5f8-68b4-4903-b4ed-ccfd9c9e899f}</Project>
<Name>Jackett</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -29,7 +29,7 @@
private void InitializeComponent() private void InitializeComponent()
{ {
components = new System.ComponentModel.Container(); components = new System.ComponentModel.Container();
this.ServiceName = "Service1"; this.ServiceName = "Jackett";
} }
#endregion #endregion

View File

@@ -19,10 +19,15 @@ namespace Jackett.Service
protected override void OnStart(string[] args) protected override void OnStart(string[] args)
{ {
Engine.Logger.Info("Service starting");
Engine.Server.Start();
Engine.Logger.Info("Service started");
} }
protected override void OnStop() protected override void OnStop()
{ {
Engine.Logger.Info("Service stopping");
Engine.Server.Stop();
} }
} }
} }

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="3.5.0" targetFramework="net452" />
<package id="Autofac.Owin" version="3.1.0" targetFramework="net452" />
<package id="Autofac.WebApi2" version="3.4.0" targetFramework="net452" />
<package id="Autofac.WebApi2.Owin" version="3.2.0" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.OwinSelfHost" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.FileSystems" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net452" />
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" />
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" />
<package id="NLog" version="4.0.1" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" />
</packages>

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 361 KiB

View File

@@ -1,170 +1,170 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' /> <link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
<script src="jquery-2.1.3.min.js"></script> <script src="jquery-2.1.3.min.js"></script>
<script src="handlebars-v3.0.1.js"></script> <script src="handlebars-v3.0.1.js"></script>
<script src="bootstrap/bootstrap.min.js"></script> <script src="bootstrap/bootstrap.min.js"></script>
<script src="bootstrap-notify.js"></script> <script src="bootstrap-notify.js"></script>
<link href="bootstrap/bootstrap.min.css" rel="stylesheet"> <link href="bootstrap/bootstrap.min.css" rel="stylesheet">
<link href="animate.css" rel="stylesheet"> <link href="animate.css" rel="stylesheet">
<link href="custom.css" rel="stylesheet"> <link href="custom.css" rel="stylesheet">
<title>Jackett</title> <title>Jackett</title>
</head> </head>
<body> <body>
<div id="page"> <div id="page">
<img id="logo" src="jacket_medium.png" /><span id="header-title">Jackett</span> <img id="logo" src="jacket_medium.png" /><span id="header-title">Jackett</span>
<hr /> <hr />
<div class="input-area"> <div class="input-area">
<span class="input-header">Sonarr API Host: </span> <span class="input-header">Sonarr API Host: </span>
<input id="sonarr-host" class="form-control input-right" type="text" readonly /> <input id="sonarr-host" class="form-control input-right" type="text" readonly />
<button id="sonarr-settings" class="btn btn-primary btn-sm"> <button id="sonarr-settings" class="btn btn-primary btn-sm">
Settings <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> Settings <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
</button> </button>
<button id="sonarr-test" class="btn btn-warning btn-sm"> <button id="sonarr-test" class="btn btn-warning btn-sm">
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span> Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
</button> </button>
<p id="sonarr-warning" class="alert alert-danger" role="alert"> <p id="sonarr-warning" class="alert alert-danger" role="alert">
<span class="glyphicon glyphicon-exclamation-sign"></span> <span class="glyphicon glyphicon-exclamation-sign"></span>
Sonarr API must be configured Sonarr API must be configured
</p> </p>
</div> </div>
<hr /> <hr />
<div class="input-area"> <div class="input-area">
<p> <p>
To add a Jackett indexer in Sonarr go to <b>Settings > Indexers > Add > Torznab > Custom</b>. To add a Jackett indexer in Sonarr go to <b>Settings > Indexers > Add > Torznab > Custom</b>.
</p> </p>
<span class="input-header">Jackett API Key: </span> <span class="input-header">Jackett API Key: </span>
<input id="api-key-input" class="form-control input-right" type="text" value="" placeholder="API Key" readonly=""> <input id="api-key-input" class="form-control input-right" type="text" value="" placeholder="API Key" readonly="">
<p>Use this key when adding indexers to Sonarr. This key works for all indexers.</p> <p>Use this key when adding indexers to Sonarr. This key works for all indexers.</p>
<span class="input-header">Jackett port: </span> <span class="input-header">Jackett port: </span>
<input id="jackett-port" class="form-control input-right" type="text" value="" placeholder="9117"> <input id="jackett-port" class="form-control input-right" type="text" value="" placeholder="9117">
<button id="change-jackett-port" class="btn btn-primary btn-sm"> <button id="change-jackett-port" class="btn btn-primary btn-sm">
Configure <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span> Configure <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
</button> </button>
<span title="Jackett will restart after changing the port" class="glyphicon glyphicon-info-sign"></span> <span title="Jackett will restart after changing the port" class="glyphicon glyphicon-info-sign"></span>
</div> </div>
<hr /> <hr />
<h3>Configured Indexers</h3> <h3>Configured Indexers</h3>
<div id="indexers"> </div> <div id="indexers"> </div>
<hr /> <hr />
<div id="footer"> <div id="footer">
Jackett Version <span id="app-version"></span> Jackett Version <span id="app-version"></span>
</div> </div>
</div> </div>
<div id="select-indexer-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div id="select-indexer-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">Select an indexer to setup</h4> <h4 class="modal-title">Select an indexer to setup</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="unconfigured-indexers"> <div id="unconfigured-indexers">
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div id="modals"></div> <div id="modals"></div>
<div id="templates"> <div id="templates">
<div class="config-setup-modal modal fade" tabindex="-1" role="dialog" aria-hidden="true"> <div class="config-setup-modal modal fade" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{title}}</h4> <h4 class="modal-title">{{title}}</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="config-setup-form"></form> <form class="config-setup-form"></form>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary setup-indexer-go">Okay</button> <button type="button" class="btn btn-primary setup-indexer-go">Okay</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<button class="indexer card add-indexer" data-toggle="modal" data-target="#select-indexer-modal"> <button class="indexer card add-indexer" data-toggle="modal" data-target="#select-indexer-modal">
<div class="indexer-add-content"> <div class="indexer-add-content">
<span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span> <span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span>
<div class="light-text">Add</div> <div class="light-text">Add</div>
</div> </div>
</button> </button>
<div class="configured-indexer indexer card"> <div class="configured-indexer indexer card">
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div> <div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
<div class="indexer-buttons"> <div class="indexer-buttons">
<button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}"> <button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}">
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span> <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
</button> </button>
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}"> <button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</button> </button>
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}"> <a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
</a> </a>
<button class="btn btn-warning btn-sm indexer-button-test" data-id="{{id}}"> <button class="btn btn-warning btn-sm indexer-button-test" data-id="{{id}}">
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span> Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
</button> </button>
</div> </div>
<div class="indexer-host"> <div class="indexer-host">
<b>Torznab Host:</b> <b>Torznab Host:</b>
<input class="form-control" type="text" value="{{torznab_host}}" placeholder="Torznab Host" readonly=""> <input class="form-control" type="text" value="{{torznab_host}}" placeholder="Torznab Host" readonly="">
</div> </div>
</div> </div>
<div class="unconfigured-indexer card"> <div class="unconfigured-indexer card">
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div> <div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
<div class="indexer-buttons"> <div class="indexer-buttons">
<a class="btn btn-info" target="_blank" href="{{site_link}}">Visit <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a> <a class="btn btn-info" target="_blank" href="{{site_link}}">Visit <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>
<button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button> <button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
</div> </div>
</div> </div>
<div class="setup-item form-group" data-id="{{id}}" data-value="{{value}}" data-type="{{type}}"> <div class="setup-item form-group" data-id="{{id}}" data-value="{{value}}" data-type="{{type}}">
<div class="setup-item-label">{{name}}</div> <div class="setup-item-label">{{name}}</div>
<div class="setup-item-value">{{{value_element}}}</div> <div class="setup-item-value">{{{value_element}}}</div>
</div> </div>
<input class="setup-item-inputstring form-control" type="text" value="{{{value}}}" /> <input class="setup-item-inputstring form-control" type="text" value="{{{value}}}" />
<div class="setup-item-inputbool"> <div class="setup-item-inputbool">
{{#if value}} {{#if value}}
<input type="checkbox" data-id="{{id}}" class="form-control" checked /> <input type="checkbox" data-id="{{id}}" class="form-control" checked />
{{else}} {{else}}
<input type="checkbox" data-id="{{id}}" class="form-control" /> <input type="checkbox" data-id="{{id}}" class="form-control" />
{{/if}} {{/if}}
</div> </div>
<img class="setup-item-displayimage" src="{{{value}}}" /> <img class="setup-item-displayimage" src="{{{value}}}" />
<div class="setup-item-displayinfo alert alert-info" role="alert">{{{value}}}</div> <div class="setup-item-displayinfo alert alert-info" role="alert">{{{value}}}</div>
<span class="spinner glyphicon glyphicon-refresh"></span> <span class="spinner glyphicon glyphicon-refresh"></span>
</div> </div>
<script src="custom.js"></script> <script src="custom.js"></script>
</body> </body>
</html> </html>

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@@ -0,0 +1,75 @@
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 ISonarrApi sonarrService;
private Logger logger;
public APIController(IIndexerManagerService i, ISonarrApi s, Logger l)
{
indexerService = i;
sonarrService = s;
logger = l;
}
[HttpGet]
public async Task<HttpResponseMessage> Call(string indexerName)
{
var indexer = indexerService.GetIndexer(indexerName);
var torznabQuery = TorznabQuery.FromHttpQuery(HttpUtility.ParseQueryString(Request.RequestUri.Query));
if (torznabQuery.RageID != 0)
torznabQuery.ShowTitles = await sonarrService.GetShowTitle(torznabQuery.RageID);
else if (!string.IsNullOrEmpty(torznabQuery.SearchTerm))
torznabQuery.ShowTitles = new string[] { torznabQuery.SearchTerm };
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.ToLowerInvariant(), 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")
};
}
}
}

View File

@@ -19,12 +19,14 @@ namespace Jackett.Controllers
private IConfigurationService config; private IConfigurationService config;
private ISonarrApi sonarrApi; private ISonarrApi sonarrApi;
private IIndexerManagerService indexerService; private IIndexerManagerService indexerService;
private IServerService serverService;
public AdminController(IConfigurationService config, ISonarrApi s, IIndexerManagerService i) public AdminController(IConfigurationService config, ISonarrApi s, IIndexerManagerService i, IServerService ss)
{ {
this.config = config; this.config = config;
sonarrApi = s; sonarrApi = s;
indexerService = i; indexerService = i;
serverService = ss;
} }
private async Task<JToken> ReadPostDataJson() private async Task<JToken> ReadPostDataJson()
@@ -92,7 +94,7 @@ namespace Jackett.Controllers
try try
{ {
jsonReply["result"] = "success"; jsonReply["result"] = "success";
jsonReply["api_key"] = ApiKey.CurrentKey; jsonReply["api_key"] = serverService.Config.APIKey;
jsonReply["app_version"] = config.GetVersion(); jsonReply["app_version"] = config.GetVersion();
JArray items = new JArray(); JArray items = new JArray();

View File

@@ -0,0 +1,48 @@
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
{
public class DownloadController : ApiController
{
private Logger logger;
private IIndexerManagerService indexerService;
public DownloadController(IIndexerManagerService i, Logger l)
{
logger = l;
indexerService = i;
}
[HttpGet]
public async Task<HttpResponseMessage> 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);
}
}
}
}

View File

@@ -12,7 +12,6 @@ namespace Jackett
{ {
public static class CookieContainerExtensions public static class CookieContainerExtensions
{ {
public static void FillFromJson(this CookieContainer cookies, Uri uri, JToken json, Logger logger) public static void FillFromJson(this CookieContainer cookies, Uri uri, JToken json, Logger logger)
{ {
if (json["cookies"] != null) if (json["cookies"] != null)

View File

@@ -10,7 +10,6 @@ namespace Jackett
{ {
public class DataUrl public class DataUrl
{ {
public static string ReadFileToDataUrl(string file) public static string ReadFileToDataUrl(string file)
{ {
string mime = MimeMapping.GetMimeMapping(file); string mime = MimeMapping.GetMimeMapping(file);

111
src/Jackett/Engine.cs Normal file
View File

@@ -0,0 +1,111 @@
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()
{
var builder = new ContainerBuilder();
builder.RegisterModule<JackettModule>();
container = builder.Build();
// Register the container in itself to allow for late resolves
var secondaryBuilder = new ContainerBuilder();
secondaryBuilder.RegisterInstance<IContainer>(container).SingleInstance();
SetupLogging(secondaryBuilder);
secondaryBuilder.Update(container);
Logger.Info("Starting Jackett " + ConfigService.GetVersion());
}
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<IConfigurationService>();
}
}
public static IServiceConfigService ServiceConfig
{
get
{
return container.Resolve<IServiceConfigService>();
}
}
public static IServerService Server
{
get
{
return container.Resolve<IServerService>();
}
}
public static IRunTimeService RunTime
{
get
{
return container.Resolve<IRunTimeService>();
}
}
public static Logger Logger
{
get
{
return container.Resolve<Logger>();
}
}
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<Logger>(LogManager.GetCurrentClassLogger()).SingleInstance();
}
}
}

View File

@@ -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(WebServer.AppConfigDirectory, "Indexers");
public Dictionary<string, IndexerInterface> Indexers { get; private set; }
public IndexerManager()
{
Indexers = new Dictionary<string, IndexerInterface>();
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(WebServer.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);
WebServer.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");
}
}*/
}

View File

@@ -86,7 +86,7 @@ namespace Jackett.Indexers
configSaveData = new JObject(); configSaveData = new JObject();
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
// If Windows use .net http // If Windows use .net http
var response = await client.SendAsync(message); var response = await client.SendAsync(message);
@@ -155,7 +155,7 @@ namespace Jackett.Indexers
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString); var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
string results; string results;
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
var request = CreateHttpRequest(new Uri(episodeSearchUrl)); var request = CreateHttpRequest(new Uri(episodeSearchUrl));
request.Method = HttpMethod.Get; request.Method = HttpMethod.Get;
@@ -223,7 +223,7 @@ namespace Jackett.Indexers
public override async Task<byte[]> Download(Uri link) public override async Task<byte[]> Download(Uri link)
{ {
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
return await client.GetByteArrayAsync(link); return await client.GetByteArrayAsync(link);
} }

View File

@@ -18,7 +18,7 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class AnimeBytes : IndexerInterface public class AnimeBytes : IIndexer
{ {
class ConfigurationDataBasicLoginAnimeBytes : ConfigurationDataBasicLogin class ConfigurationDataBasicLoginAnimeBytes : ConfigurationDataBasicLogin
{ {
@@ -41,8 +41,8 @@ namespace Jackett.Indexers
private static List<CachedResult> cache = new List<CachedResult>(); private static List<CachedResult> cache = new List<CachedResult>();
private static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0); private static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0);
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
static string chromeUserAgent = BrowserUtil.ChromeUserAgent; static string chromeUserAgent = BrowserUtil.ChromeUserAgent;

View File

@@ -9,12 +9,12 @@ using NLog;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public abstract class BaseIndexer: IndexerInterface public abstract class BaseIndexer: IIndexer
{ {
public string DisplayDescription { get; } public string DisplayDescription { get; private set; }
public string DisplayName { get; } public string DisplayName { get; private set; }
public bool IsConfigured { get; protected set; } public bool IsConfigured { get; protected set; }
public Uri SiteLink { get; } public Uri SiteLink { get; private set; }
public abstract Task ApplyConfiguration(JToken configJson); public abstract Task ApplyConfiguration(JToken configJson);
public abstract Task<byte[]> Download(Uri link); public abstract Task<byte[]> Download(Uri link);

View File

@@ -14,11 +14,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class BeyondHD : IndexerInterface public class BeyondHD : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -15,9 +15,9 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class BitHdtv : IndexerInterface public class BitHdtv : IIndexer
{ {
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {
@@ -100,7 +100,7 @@ namespace Jackett.Indexers
} }
} }
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public bool IsConfigured { get; private set; } public bool IsConfigured { get; private set; }

View File

@@ -14,9 +14,9 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web; using System.Web;
namespace Jackett namespace Jackett.Indexers
{ {
public class BitMeTV : IndexerInterface public class BitMeTV : IIndexer
{ {
class BmtvConfig : ConfigurationData class BmtvConfig : ConfigurationData
{ {
@@ -53,8 +53,8 @@ namespace Jackett
HttpClient client; HttpClient client;
Logger logger; Logger logger;
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public BitMeTV(Logger l) public BitMeTV(Logger l)
{ {

View File

@@ -12,11 +12,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
class FrenchTorrentDb : IndexerInterface class FrenchTorrentDb : IIndexer
{ {
public event Action<IndexerInterface, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested; public event Action<IIndexer, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
class ConfigurationDataBasicLoginFrenchTorrentDb : ConfigurationData class ConfigurationDataBasicLoginFrenchTorrentDb : ConfigurationData
{ {

View File

@@ -1,4 +1,5 @@
using CsQuery; using CsQuery;
using Jackett.Indexers;
using Jackett.Models; using Jackett.Models;
using Jackett.Utils; using Jackett.Utils;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -15,11 +16,11 @@ using System.Threading.Tasks;
using System.Web; using System.Web;
using System.Web.UI.WebControls; using System.Web.UI.WebControls;
namespace Jackett namespace Jackett.Indexers
{ {
public class Freshon : IndexerInterface public class Freshon : IIndexer
{ {
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
static string BaseUrl = "https://freshon.tv"; static string BaseUrl = "https://freshon.tv";
static string LoginUrl = BaseUrl + "/login.php"; static string LoginUrl = BaseUrl + "/login.php";
@@ -40,7 +41,7 @@ namespace Jackett
public Uri SiteLink { get { return new Uri(BaseUrl); } } public Uri SiteLink { get { return new Uri(BaseUrl); } }
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
Logger logger; Logger logger;
public Freshon(Logger l) public Freshon(Logger l)

View File

@@ -15,18 +15,17 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class HDTorrents : IndexerInterface public class HDTorrents : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> 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 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; string BaseUrl = DefaultUrl;
static string chromeUserAgent = BrowserUtil.ChromeUserAgent; 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 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 static string LoginUrl = DefaultUrl + "/login.php";
private static string LoginPostUrl = DefaultUrl + "/login.php?returnto=index.php";
private const int MAXPAGES = 3; private const int MAXPAGES = 3;
CookieContainer cookies; CookieContainer cookies;

View File

@@ -7,9 +7,9 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Web.UI.WebControls; using System.Web.UI.WebControls;
namespace Jackett namespace Jackett.Indexers
{ {
public interface IndexerInterface public interface IIndexer
{ {
string DisplayName { get; } string DisplayName { get; }
string DisplayDescription { get; } string DisplayDescription { get; }

View File

@@ -15,11 +15,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class IPTorrents : IndexerInterface public class IPTorrents : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName { get { return "IPTorrents"; } } public string DisplayName { get { return "IPTorrents"; } }

View File

@@ -14,7 +14,7 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class MoreThanTV : IndexerInterface public class MoreThanTV : IIndexer
{ {
public string DisplayName public string DisplayName
{ {
@@ -32,8 +32,8 @@ namespace Jackett.Indexers
} }
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public bool IsConfigured { get; private set; } public bool IsConfigured { get; private set; }
@@ -93,7 +93,7 @@ namespace Jackett.Indexers
var configSaveData = new JObject(); var configSaveData = new JObject();
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
// If Windows use .net http // If Windows use .net http
var response = await client.PostAsync(LoginUrl, content); var response = await client.PostAsync(LoginUrl, content);
@@ -156,7 +156,7 @@ namespace Jackett.Indexers
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString); var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
string results; string results;
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
results = await client.GetStringAsync(episodeSearchUrl, retries); results = await client.GetStringAsync(episodeSearchUrl, retries);
} }
@@ -224,7 +224,7 @@ namespace Jackett.Indexers
public async Task<byte[]> Download(Uri link) public async Task<byte[]> Download(Uri link)
{ {
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
return await client.GetByteArrayAsync(link); return await client.GetByteArrayAsync(link);
} }

View File

@@ -12,11 +12,11 @@ using System.Threading.Tasks;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class Rarbg : IndexerInterface public class Rarbg : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -13,11 +13,11 @@ using System.Threading.Tasks;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
class SceneAccess : IndexerInterface class SceneAccess : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {
@@ -87,7 +87,7 @@ namespace Jackett.Indexers
string responseContent; string responseContent;
var configSaveData = new JObject(); var configSaveData = new JObject();
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
// If Windows use .net http // If Windows use .net http
var response = await client.PostAsync(LoginUrl, content); var response = await client.PostAsync(LoginUrl, content);
@@ -139,7 +139,7 @@ namespace Jackett.Indexers
var searchUrl = string.Format(SearchUrl, searchSection, searchCategory, searchString); var searchUrl = string.Format(SearchUrl, searchSection, searchCategory, searchString);
string results; string results;
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
results = await client.GetStringAsync(searchUrl); results = await client.GetStringAsync(searchUrl);
} }
@@ -195,7 +195,7 @@ namespace Jackett.Indexers
public async Task<byte[]> Download(Uri link) public async Task<byte[]> Download(Uri link)
{ {
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
return await client.GetByteArrayAsync(link); return await client.GetByteArrayAsync(link);
} }

View File

@@ -15,11 +15,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class SceneTime : IndexerInterface public class SceneTime : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -14,11 +14,11 @@ using System.Xml;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class ShowRSS : IndexerInterface public class ShowRSS : IIndexer
{ {
public event Action<IndexerInterface, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested; public event Action<IIndexer, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -12,11 +12,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class Strike : IndexerInterface public class Strike : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -14,12 +14,12 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class T411 : IndexerInterface public class T411 : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -15,12 +15,12 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class ThePirateBay : IndexerInterface public class ThePirateBay : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName { get { return "The Pirate Bay"; } } public string DisplayName { get { return "The Pirate Bay"; } }
@@ -110,7 +110,7 @@ namespace Jackett.Indexers
string results; string results;
if (WebServer.IsWindows) if (Engine.IsWindows)
{ {
results = await client.GetStringAsync(episodeSearchUrl); results = await client.GetStringAsync(episodeSearchUrl);
} }

View File

@@ -15,11 +15,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class TorrentDay : IndexerInterface public class TorrentDay : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -15,11 +15,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class TorrentLeech : IndexerInterface public class TorrentLeech : IIndexer
{ {
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public string DisplayName public string DisplayName
{ {

View File

@@ -15,11 +15,11 @@ using System.Web;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class TorrentShack : IndexerInterface public class TorrentShack : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -13,11 +13,11 @@ using System.Xml;
namespace Jackett.Indexers namespace Jackett.Indexers
{ {
public class Torrentz : IndexerInterface public class Torrentz : IIndexer
{ {
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested; public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
public event Action<IndexerInterface, string, Exception> OnResultParsingError; public event Action<IIndexer, string, Exception> OnResultParsingError;
public string DisplayName public string DisplayName
{ {

View File

@@ -102,6 +102,7 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
@@ -110,6 +111,7 @@
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Web" /> <Reference Include="System.Web" />
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath> <HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
@@ -133,8 +135,15 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Controllers\APIController.cs" />
<Compile Include="Controllers\DownloadController.cs" />
<Compile Include="Engine.cs" />
<Compile Include="Indexers\BaseIndexer.cs" /> <Compile Include="Indexers\BaseIndexer.cs" />
<Compile Include="Models\ApiKey.cs" /> <Compile Include="Models\Config\ServerConfig.cs" />
<Compile Include="Services\ProcessService.cs" />
<Compile Include="Services\SerializeService.cs" />
<Compile Include="Services\ServiceConfigService.cs" />
<Compile Include="Services\SpinService.cs" />
<Compile Include="Utils\BrowserUtil.cs" /> <Compile Include="Utils\BrowserUtil.cs" />
<Compile Include="Models\CachedResult.cs" /> <Compile Include="Models\CachedResult.cs" />
<Compile Include="Models\ChannelInfo.cs" /> <Compile Include="Models\ChannelInfo.cs" />
@@ -147,8 +156,7 @@
<Compile Include="DataUrl.cs" /> <Compile Include="DataUrl.cs" />
<Compile Include="ExceptionWithConfigData.cs" /> <Compile Include="ExceptionWithConfigData.cs" />
<Compile Include="HttpClientExtensions.cs" /> <Compile Include="HttpClientExtensions.cs" />
<Compile Include="IndexerInterface.cs" /> <Compile Include="Indexers\IIndexer.cs" />
<Compile Include="IndexerManager.cs" />
<Compile Include="Indexers\BeyondHD.cs" /> <Compile Include="Indexers\BeyondHD.cs" />
<Compile Include="Indexers\BitHdtv.cs" /> <Compile Include="Indexers\BitHdtv.cs" />
<Compile Include="Indexers\BitMeTV.cs" /> <Compile Include="Indexers\BitMeTV.cs" />
@@ -180,22 +188,20 @@
</Compile> </Compile>
<Compile Include="Models\ReleaseInfo.cs" /> <Compile Include="Models\ReleaseInfo.cs" />
<Compile Include="Models\ResultPage.cs" /> <Compile Include="Models\ResultPage.cs" />
<Compile Include="Server.cs" /> <Compile Include="Services\ServerService.cs" />
<Compile Include="Utils\ServerUtil.cs" /> <Compile Include="Utils\ServerUtil.cs" />
<Compile Include="Services\ConfigurationService.cs" /> <Compile Include="Services\ConfigurationService.cs" />
<Compile Include="Services\IndexerManagerService.cs" /> <Compile Include="Services\IndexerManagerService.cs" />
<Compile Include="Services\SonarApi.cs" /> <Compile Include="Services\SonarApi.cs" />
<Compile Include="Startup.cs" /> <Compile Include="Startup.cs" />
<Compile Include="Models\TorznabQuery.cs" /> <Compile Include="Models\TorznabQuery.cs" />
<Compile Include="WebApi.cs" />
<Compile Include="CurlHelper.cs" /> <Compile Include="CurlHelper.cs" />
<Compile Include="Indexers\AlphaRatio.cs" /> <Compile Include="Indexers\AlphaRatio.cs" />
<Compile Include="WebServer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="WebContent\fonts\glyphicons-halflings-regular.woff"> <None Include="Content\fonts\glyphicons-halflings-regular.woff">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
@@ -209,116 +215,116 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="WebContent\custom.css"> <Content Include="Content\custom.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\custom.js"> <Content Include="Content\custom.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\animebytes.png"> <Content Include="Content\logos\animebytes.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\beyondhd.png"> <Content Include="Content\logos\beyondhd.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\frenchtorrentdb.png"> <Content Include="Content\logos\frenchtorrentdb.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\hdtorrents.png"> <Content Include="Content\logos\hdtorrents.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\sceneaccess.png"> <Content Include="Content\logos\sceneaccess.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\scenetime.png"> <Content Include="Content\logos\scenetime.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\showrss.png"> <Content Include="Content\logos\showrss.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\t411.png"> <Content Include="Content\logos\t411.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\torrentday.png"> <Content Include="Content\logos\torrentday.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\torrentshack.png"> <Content Include="Content\logos\torrentshack.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\animate.css"> <Content Include="Content\animate.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\binding_dark.png"> <Content Include="Content\binding_dark.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\bootstrap-notify.js"> <Content Include="Content\bootstrap-notify.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\congruent_outline.png"> <Content Include="Content\congruent_outline.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\crissXcross.png"> <Content Include="Content\crissXcross.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\favicon.ico"> <Content Include="Content\favicon.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\handlebars-v3.0.1.js"> <Content Include="Content\handlebars-v3.0.1.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\jacket_medium.png"> <Content Include="Content\jacket_medium.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\bithdtv.png"> <Content Include="Content\logos\bithdtv.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\bitmetv.png"> <Content Include="Content\logos\bitmetv.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\bootstrap\bootstrap.min.css"> <Content Include="Content\bootstrap\bootstrap.min.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\bootstrap\bootstrap.min.js"> <Content Include="Content\bootstrap\bootstrap.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\common.js"> <Content Include="Content\common.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\index.html"> <Content Include="Content\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\jquery-2.1.3.min.js"> <Content Include="Content\jquery-2.1.3.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\freshon.png"> <Content Include="Content\logos\freshon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\iptorrents.png"> <Content Include="Content\logos\iptorrents.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\morethantv.png"> <Content Include="Content\logos\morethantv.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\rarbg.png"> <Content Include="Content\logos\rarbg.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\strike.png"> <Content Include="Content\logos\strike.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\thepiratebay.png"> <Content Include="Content\logos\thepiratebay.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\torrentleech.png"> <Content Include="Content\logos\torrentleech.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\logos\torrentz.png"> <Content Include="Content\logos\torrentz.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="WebContent\setup_indexer.html"> <Content Include="Content\setup_indexer.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="Resources\validator_reply.xml" /> <Content Include="Resources\validator_reply.xml" />
<Content Include="WebContent\logos\alpharatio.png"> <Content Include="Content\logos\alpharatio.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Autofac.Integration.WebApi; using Autofac.Integration.WebApi;
using Jackett.Indexers;
namespace Jackett namespace Jackett
{ {
@@ -14,15 +15,15 @@ namespace Jackett
{ {
// Just register everything! // Just register everything!
var thisAssembly = typeof(JackettModule).Assembly; var thisAssembly = typeof(JackettModule).Assembly;
builder.RegisterAssemblyTypes(thisAssembly).AsImplementedInterfaces().SingleInstance(); builder.RegisterAssemblyTypes(thisAssembly).Except<IIndexer>().AsImplementedInterfaces().SingleInstance();
builder.RegisterApiControllers(thisAssembly).InstancePerRequest(); builder.RegisterApiControllers(thisAssembly).InstancePerRequest();
// Register indexers // Register indexers
foreach(var indexer in thisAssembly.GetTypes() foreach(var indexer in thisAssembly.GetTypes()
.Where(p => typeof(IndexerInterface).IsAssignableFrom(p) && !p.IsInterface) .Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface)
.ToArray()) .ToArray())
{ {
builder.RegisterType(indexer).Named<IndexerInterface>(indexer.Name.ToLowerInvariant()).SingleInstance(); builder.RegisterType(indexer).Named<IIndexer>(indexer.Name.ToLowerInvariant()).SingleInstance();
} }
} }
} }

View File

@@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Jackett.Models
{
public class ApiKey
{
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;
}
}
}

View File

@@ -0,0 +1,45 @@
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 GetListenAddress(bool? external = null)
{
if (external == null)
{
external = AllowExternal;
}
return "http://" + (external.Value ? "*" : "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;
}
}
}

View File

@@ -1,93 +0,0 @@
using Autofac;
using Jackett.Services;
using Microsoft.Owin.Hosting;
using NLog;
using NLog.Config;
using NLog.Targets;
using NLog.Windows.Forms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Jackett
{
public class Server
{
private static IContainer container = null;
private static string baseAddress = "http://localhost:9000/";
private static IDisposable _server = null;
static Server()
{
var builder = new ContainerBuilder();
builder.RegisterModule<JackettModule>();
container = builder.Build();
// Register the container in itself to allow for late resolves
var secondaryBuilder = new ContainerBuilder();
secondaryBuilder.RegisterInstance<IContainer>(container);
SetupLogging(secondaryBuilder, container.Resolve<IConfigurationService>());
secondaryBuilder.Update(container);
}
private static void SetupLogging(ContainerBuilder builder, IConfigurationService config)
{
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(config.GetAppDataFolder(), "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 (WebServer.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;
builder.RegisterInstance<Logger>(LogManager.GetCurrentClassLogger()).SingleInstance();
}
public static void Start()
{
_server = WebApp.Start<Startup>(url: baseAddress);
}
public static void Stop()
{
if (_server != null)
{
_server.Dispose();
}
}
public static IContainer GetContainer()
{
return container;
}
}
}

View File

@@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -19,22 +20,115 @@ namespace Jackett.Services
string GetAppDataFolder(); string GetAppDataFolder();
JObject ReadServerSettingsFile(); JObject ReadServerSettingsFile();
string GetSonarrConfigFile(); string GetSonarrConfigFile();
T GetConfig<T>();
void SaveConfig<T>(T config);
string ApplicationFolder();
} }
public class ConfigurationService: IConfigurationService 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));
}
File.Move(file, destFolder);
}
}
}
catch (Exception ex)
{
logger.Error("ERROR could not migrate settings directory " + ex);
}
}
public T GetConfig<T>()
{
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<T>(File.ReadAllText(fullPath));
}
catch(Exception e)
{
logger.Error(e, "Error reading config file " + fullPath);
return default(T);
}
}
public void SaveConfig<T>(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() public string GetContentFolder()
{ {
var baseDir = Path.GetDirectoryName(Application.ExecutablePath);
// If we are debugging we can use the non copied content. // If we are debugging we can use the non copied content.
if (Debugger.IsAttached) var dir = Path.Combine(ApplicationFolder(), "Content");
if (!Directory.Exists(dir))
{ {
return Path.Combine(baseDir, "..\\..\\..\\Jackett\\WebContent"); dir = Path.Combine(ApplicationFolder(), "..\\..\\..\\Jackett\\Content");
}
else
{
return Path.Combine(baseDir, "WebContent");
} }
return dir;
} }
public string GetVersion() public string GetVersion()
@@ -44,7 +138,16 @@ namespace Jackett.Services
public string GetAppDataFolder() public string GetAppDataFolder()
{ {
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jackett"); ; return GetAppDataFolderStatic();
}
/// <summary>
/// This is needed for the logger prior to ioc setup.
/// </summary>
/// <returns></returns>
public static string GetAppDataFolderStatic()
{
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Jackett");
} }
public string GetIndexerConfigDir() public string GetIndexerConfigDir()

View File

@@ -1,4 +1,5 @@
using Autofac; using Autofac;
using Jackett.Indexers;
using Jackett.Models; using Jackett.Models;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
@@ -15,9 +16,10 @@ namespace Jackett.Services
{ {
void TestIndexer(string name); void TestIndexer(string name);
void DeleteIndexer(string name); void DeleteIndexer(string name);
IndexerInterface GetIndexer(string name); IIndexer GetIndexer(string name);
IEnumerable<IndexerInterface> GetAllIndexers(); IEnumerable<IIndexer> GetAllIndexers();
void SaveConfig(IndexerInterface indexer, JToken obj); void SaveConfig(IIndexer indexer, JToken obj);
void InitIndexers();
} }
public class IndexerManagerService : IIndexerManagerService public class IndexerManagerService : IIndexerManagerService
@@ -33,14 +35,35 @@ namespace Jackett.Services
logger = l; logger = l;
} }
public IndexerInterface GetIndexer(string name) public void InitIndexers()
{ {
return container.ResolveNamed<IndexerInterface>(name.ToLowerInvariant()); // Load the existing config for each indexer
foreach (var indexer in GetAllIndexers())
{
var configFilePath = GetIndexerConfigFilePath(indexer);
if (File.Exists(configFilePath))
{
var jsonString = JObject.Parse(File.ReadAllText(configFilePath));
indexer.LoadFromSavedConfiguration(jsonString);
}
}
} }
public IEnumerable<IndexerInterface> GetAllIndexers() public IIndexer GetIndexer(string name)
{ {
return container.Resolve<IEnumerable<IndexerInterface>>().OrderBy(_ => _.DisplayName); var indexer = GetAllIndexers().Where(i => string.Equals(i.DisplayName, name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if (indexer == null)
{
logger.Error("Request for unknown indexer: " + name);
throw new Exception("Unknown indexer: " + name);
}
return indexer;
}
public IEnumerable<IIndexer> GetAllIndexers()
{
return container.Resolve<IEnumerable<IIndexer>>().OrderBy(_ => _.DisplayName);
} }
public async void TestIndexer(string name) public async void TestIndexer(string name)
@@ -62,12 +85,12 @@ namespace Jackett.Services
//LoadMissingIndexers(); //LoadMissingIndexers();
} }
private string GetIndexerConfigFilePath(IndexerInterface indexer) private string GetIndexerConfigFilePath(IIndexer indexer)
{ {
return Path.Combine(configService.GetIndexerConfigDir(), indexer.GetType().Name.ToLower() + ".json"); return Path.Combine(configService.GetIndexerConfigDir(), indexer.GetType().Name.ToLower() + ".json");
} }
public void SaveConfig(IndexerInterface indexer, JToken obj) public void SaveConfig(IIndexer indexer, JToken obj)
{ {
var configFilePath = GetIndexerConfigFilePath(indexer); var configFilePath = GetIndexerConfigFilePath(indexer);
if (!Directory.Exists(configService.GetIndexerConfigDir())) if (!Directory.Exists(configService.GetIndexerConfigDir()))

View File

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

View File

@@ -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<T>(string json);
}
class SerializeService : ISerializeService
{
public string Serialise(object obj)
{
return JsonConvert.SerializeObject(obj,Formatting.Indented);
}
public T DeSerialise<T>(string json)
{
try
{
return JsonConvert.DeserializeObject<T>(json);
}
catch
{
return default(T);
}
}
}
}

View File

@@ -0,0 +1,136 @@
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.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; }
}
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<ServerConfig>();
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<ServerConfig>(config);
}
}
public void Start()
{
// 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.GetListenAddress());
_server = WebApp.Start<Startup>(url: config.GetListenAddress());
logger.Debug("Web server started");
}
public void ReserveUrls(bool doInstall = true)
{
logger.Debug("Unreserving Urls");
RunNetSh(string.Format("http delete urlacl {0}", config.GetListenAddress(false)));
RunNetSh(string.Format("http delete urlacl {0}", config.GetListenAddress(true)));
if (doInstall)
{
logger.Debug("Reserving Urls");
RunNetSh(string.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", config.GetListenAddress()));
logger.Debug("Urls reserved");
}
}
private void RunNetSh(string args)
{
processService.StartProcessAndLog("netsh.exe", args);
}
public void Stop()
{
if (_server != null)
{
_server.Dispose();
}
}
}
}

View File

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

View File

@@ -19,6 +19,7 @@ namespace Jackett
Task TestConnection(); Task TestConnection();
SonarrApi.ConfigurationSonarr GetConfiguration(); SonarrApi.ConfigurationSonarr GetConfiguration();
Task ApplyConfiguration(JToken configJson); Task ApplyConfiguration(JToken configJson);
Task<string[]> GetShowTitle(int rid);
} }
public class SonarrApi: ISonarrApi public class SonarrApi: ISonarrApi
@@ -62,6 +63,8 @@ namespace Jackett
public SonarrApi(IConfigurationService c) public SonarrApi(IConfigurationService c)
{ {
configService = c;
LoadSettings(); LoadSettings();
cookies = new CookieContainer(); cookies = new CookieContainer();
@@ -74,8 +77,6 @@ namespace Jackett
client = new HttpClient(handler); client = new HttpClient(handler);
IdNameMappings = new ConcurrentDictionary<int, string[]>(); IdNameMappings = new ConcurrentDictionary<int, string[]>();
configService = c;
} }
async Task ReloadNameMappings(string host, int port, string apiKey) async Task ReloadNameMappings(string host, int port, string apiKey)

View File

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

View File

@@ -23,30 +23,35 @@ namespace Jackett
{ {
// Configure Web API for self-host. // Configure Web API for self-host.
var config = new HttpConfiguration(); var config = new HttpConfiguration();
config.DependencyResolver = new AutofacWebApiDependencyResolver(Engine.GetContainer());
config.DependencyResolver = new AutofacWebApiDependencyResolver(Server.GetContainer());
// Enable attribute based routing
// http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
config.MapHttpAttributeRoutes(); config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute( config.Routes.MapHttpRoute(
name: "Content", name: "Admin",
routeTemplate: "{controller}/{action}", routeTemplate: "admin/{action}",
defaults: new { controller = "Admin"} defaults: new { controller = "Admin" }
);
config.Routes.MapHttpRoute(
name: "api",
routeTemplate: "api/{indexerName}",
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.UseFileServer(new FileServerOptions appBuilder.UseFileServer(new FileServerOptions
{ {
RequestPath = new PathString(string.Empty), RequestPath = new PathString(string.Empty),
FileSystem = new PhysicalFileSystem(Server.GetContainer().Resolve<IConfigurationService>().GetContentFolder()), FileSystem = new PhysicalFileSystem(Engine.ConfigService.GetContentFolder()),
EnableDirectoryBrowsing = true, EnableDirectoryBrowsing = true,
}); });
appBuilder.UseWebApi(config); appBuilder.UseWebApi(config);
} }
} }
} }

View File

@@ -1,385 +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 WebServer server;
public enum WebApiMethod
{
GetConfigForm,
ConfigureIndexer,
GetIndexers,
TestIndexer,
DeleteIndexer,
GetSonarrConfig,
ApplySonarrConfig,
TestSonarr,
GetJackettConfig,
ApplyJackettConfig,
JackettRestart,
}
static Dictionary<string, WebApiMethod> WebApiMethods = new Dictionary<string, WebApiMethod> {
{ "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 },
{ "test_sonarr", WebApiMethod.TestSonarr },
{ "get_jackett_config",WebApiMethod.GetJackettConfig},
{ "apply_jackett_config",WebApiMethod.ApplyJackettConfig},
{ "jackett_restart", WebApiMethod.JackettRestart },
};
IndexerManager indexerManager;
SonarrApi sonarrApi;
public WebApi(IndexerManager indexerManager, SonarrApi sonarrApi)
{
this.indexerManager = indexerManager;
this.sonarrApi = sonarrApi;
}
public async Task<bool> 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<JToken> ReadPostDataJson(Stream stream)
{
string postData = await new StreamReader(stream).ReadToEndAsync();
return JObject.Parse(postData);
}
delegate Task<JToken> 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.GetSonarrConfig:
handlerTask = HandleGetSonarrConfig;
break;
case WebApiMethod.ApplySonarrConfig:
handlerTask = HandleApplySonarrConfig;
break;
case WebApiMethod.TestSonarr:
handlerTask = HandleTestSonarr;
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());
}
}
async Task<JToken> HandleTestSonarr(HttpListenerContext context)
{
JToken jsonReply = new JObject();
try
{
await sonarrApi.TestConnection();
jsonReply["result"] = "success";
}
catch (Exception ex)
{
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
return jsonReply;
}
async Task<JToken> HandleApplySonarrConfig(HttpListenerContext context)
{
JToken jsonReply = new JObject();
try
{
var postData = await ReadPostDataJson(context.Request.InputStream);
await sonarrApi.ApplyConfiguration(postData);
jsonReply["result"] = "success";
}
catch (Exception ex)
{
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
return jsonReply;
}
Task<JToken> HandleGetSonarrConfig(HttpListenerContext context)
{
JObject jsonReply = new JObject();
try
{
jsonReply["config"] = sonarrApi.GetConfiguration().ToJson();
jsonReply["result"] = "success";
}
catch (Exception ex)
{
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
return Task.FromResult<JToken>(jsonReply);
}
Task<JToken> HandleInvalidApiMethod(HttpListenerContext context)
{
JToken jsonReply = new JObject();
jsonReply["result"] = "error";
jsonReply["error"] = "Invalid API method";
return Task.FromResult<JToken>(jsonReply);
}
async Task<JToken> 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<JToken> 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<JToken> HandleGetIndexers(HttpListenerContext context)
{
JToken jsonReply = new JObject();
try
{
jsonReply["result"] = "success";
jsonReply["api_key"] = ApiKey.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<JToken>(jsonReply);
}
async Task<JToken> 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<JToken> 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<JToken> HandleJackettConfig(HttpListenerContext context)
{
JObject jsonReply = new JObject();
try
{
jsonReply["config"] = WebServer.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<JToken>(jsonReply);
}
async Task<JToken> HandleApplyJackettConfig(HttpListenerContext context)
{
JToken jsonReply = new JObject();
try
{
var postData = await ReadPostDataJson(context.Request.InputStream);
int port = await WebServer.ApplyPortConfiguration(postData);
jsonReply["result"] = "success";
jsonReply["port"] = port;
}
catch (Exception ex)
{
jsonReply["result"] = "error";
jsonReply["error"] = ex.Message;
}
return jsonReply;
}
async Task<JToken> HandleJackettRestart(HttpListenerContext context)
{
// WebServer.RestartServer();
* null;
}
}*/
}

View File

@@ -1,409 +0,0 @@
using Newtonsoft.Json.Linq;
using NLog;
using NLog.Config;
using NLog.Targets;
using NLog.Windows.Forms;
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 WebServer
{
public static bool IsWindows { get { return Environment.OSVersion.Platform == PlatformID.Win32NT; } }
}
/*
public const int DefaultPort = 9117;
public static int Port = DefaultPort;
public static bool ListenPublic = true;
public static Server ServerInstance { get; private set; }
public static bool IsFirstRun { get; private set; }
public static Logger LoggerInstance { get; private set; }
public static bool IsWindows { get { return Environment.OSVersion.Platform == PlatformID.Win32NT; } }
public static string AppConfigDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jackett");
HttpListener listener;
IndexerManager indexerManager;
WebApi webApi;
SonarrApi sonarrApi;
public WebServer()
{
// 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();
sonarrApi = new SonarrApi();
webApi = new WebApi(indexerManager, sonarrApi);
}
public static void SetupLogging()
{
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 (WebServer.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();
}
void LoadApiKey()
{
var apiKeyFile = Path.Combine(WebServer.AppConfigDirectory, "api_key.txt");
if (File.Exists(apiKeyFile))
ApiKey.CurrentKey = File.ReadAllText(apiKeyFile).Trim();
else
{
ApiKey.CurrentKey = ApiKey.Generate();
File.WriteAllText(apiKeyFile, ApiKey.CurrentKey);
}
}
static void CreateOrMigrateSettings()
{
try
{
if (!Directory.Exists(AppConfigDirectory))
{
IsFirstRun = true;
Directory.CreateDirectory(AppConfigDirectory);
}
Console.WriteLine("App config/log directory: " + AppConfigDirectory);
}
catch (Exception ex)
{
MessageBox.Show("Could not create settings directory. " + ex.Message);
Application.Exit();
return;
}
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);
}
}
public async Task Start()
{
CreateOrMigrateSettings();
WebServer.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 (WebServer.IsWindows)
{
var dialogResult = MessageBox.Show(errorStr, "Error", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.No)
{
Application.Exit();
return;
}
else
{
// WebServer.RestartAsAdmin();
}
}
}
else
{
WebServer.LoggerInstance.Fatal("Failed to start HTTP WebServer. " + ex.Message, ex);
}
}
catch (Exception ex)
{
WebServer.LoggerInstance.Error(ex, "Error starting HTTP server: " + ex.Message);
return;
}
WebServer.LoggerInstance.Info("Server started on port " + Port);
WebServer.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)
{
WebServer.LoggerInstance.Error(ex, "Critical error, HTTP listener was destroyed");
Process.GetCurrentProcess().Kill();
}
catch (Exception ex)
{
error = ex;
WebServer.LoggerInstance.Error(ex, "Error processing HTTP request");
}
if (error != null)
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
static void ReadSettingsFile()
{
var path = Path.Combine(AppConfigDirectory, "config.json");
if (!File.Exists(path))
{
JObject f = new JObject();
f.Add("port", WebServer.DefaultPort);
f.Add("public", true);
File.WriteAllText(path, f.ToString());
}
var configJson = JObject.Parse(File.ReadAllText(path));
int port = (int)configJson.GetValue("port");
WebServer.Port = port;
WebServer.ListenPublic = (bool)configJson.GetValue("public");
Console.WriteLine("Config file path: " + path);
}
public void Stop()
{
listener.Stop();
listener.Abort();
}
async void ProcessHttpRequest(HttpListenerContext context)
{
WebServer.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;
WebServer.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.RageID != 0)
torznabQuery.ShowTitles = await sonarrApi.GetShowTitle(torznabQuery.RageID);
else if (!string.IsNullOrEmpty(torznabQuery.SearchTerm))
torznabQuery.ShowTitles = new string[] { torznabQuery.SearchTerm };
var releases = await indexer.PerformQuery(torznabQuery);
WebServer.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 static 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 static Task<int> 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(WebServer.AppConfigDirectory, "config.json");
private static void SaveSettings(int jacketPort)
{
JObject json = new JObject();
json["port"] = jacketPort;
json["public"] = ListenPublic;
File.WriteAllText(ServerConfigFile, json.ToString());
}
}*/
}