Refactor done
|
@ -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 ..
|
|
@ -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
|
||||
|
||||
|
|
@ -77,6 +77,10 @@
|
|||
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</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">
|
||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
|
|
@ -16,62 +16,34 @@ namespace JackettConsole
|
|||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
|
||||
Server.Start();
|
||||
Console.ReadKey();
|
||||
|
||||
|
||||
/* var serverTask = Task.Run(async () =>
|
||||
{
|
||||
ServerInstance = new Server();
|
||||
await ServerInstance.Start();
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
if (Program.IsWindows)
|
||||
if (args.Length > 0)
|
||||
{
|
||||
#if !__MonoCS__
|
||||
Application.Run(new Main());
|
||||
#endif
|
||||
switch (args[0].ToLowerInvariant())
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
}*/
|
||||
|
||||
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);
|
||||
Engine.Logger.Error(e, "Top level exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
<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="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" />
|
||||
</packages>
|
|
@ -1,6 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</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>
|
|
@ -36,8 +36,65 @@
|
|||
<ApplicationIcon>jackett.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<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.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.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
|
@ -58,10 +115,17 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="jackett.ico" />
|
||||
</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" />
|
||||
<!-- 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.
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
private void InitializeComponent()
|
||||
{
|
||||
components = new System.ComponentModel.Container();
|
||||
this.ServiceName = "Service1";
|
||||
this.ServiceName = "Jackett";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -19,10 +19,15 @@ namespace Jackett.Service
|
|||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
Engine.Logger.Info("Service starting");
|
||||
Engine.Server.Start();
|
||||
Engine.Logger.Info("Service started");
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
Engine.Logger.Info("Service stopping");
|
||||
Engine.Server.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 361 KiB |
|
@ -1,170 +1,170 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
|
||||
|
||||
<script src="jquery-2.1.3.min.js"></script>
|
||||
<script src="handlebars-v3.0.1.js"></script>
|
||||
<script src="bootstrap/bootstrap.min.js"></script>
|
||||
<script src="bootstrap-notify.js"></script>
|
||||
|
||||
<link href="bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="animate.css" rel="stylesheet">
|
||||
<link href="custom.css" rel="stylesheet">
|
||||
|
||||
<title>Jackett</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<img id="logo" src="jacket_medium.png" /><span id="header-title">Jackett</span>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="input-area">
|
||||
<span class="input-header">Sonarr API Host: </span>
|
||||
<input id="sonarr-host" class="form-control input-right" type="text" readonly />
|
||||
<button id="sonarr-settings" class="btn btn-primary btn-sm">
|
||||
Settings <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="sonarr-test" class="btn btn-warning btn-sm">
|
||||
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||
</button>
|
||||
<p id="sonarr-warning" class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
Sonarr API must be configured
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="input-area">
|
||||
<p>
|
||||
To add a Jackett indexer in Sonarr go to <b>Settings > Indexers > Add > Torznab > Custom</b>.
|
||||
</p>
|
||||
<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="">
|
||||
<p>Use this key when adding indexers to Sonarr. This key works for all indexers.</p>
|
||||
<span class="input-header">Jackett port: </span>
|
||||
<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">
|
||||
Configure <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<span title="Jackett will restart after changing the port" class="glyphicon glyphicon-info-sign"></span>
|
||||
</div>
|
||||
<hr />
|
||||
<h3>Configured Indexers</h3>
|
||||
<div id="indexers"> </div>
|
||||
<hr />
|
||||
<div id="footer">
|
||||
Jackett Version <span id="app-version"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="select-indexer-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Select an indexer to setup</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="unconfigured-indexers">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modals"></div>
|
||||
|
||||
<div id="templates">
|
||||
|
||||
<div class="config-setup-modal modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">{{title}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="config-setup-form"></form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary setup-indexer-go">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="indexer card add-indexer" data-toggle="modal" data-target="#select-indexer-modal">
|
||||
<div class="indexer-add-content">
|
||||
<span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
<div class="light-text">Add</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div class="configured-indexer indexer card">
|
||||
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
||||
<div class="indexer-buttons">
|
||||
<button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||
</button>
|
||||
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
|
||||
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
|
||||
</a>
|
||||
<button class="btn btn-warning btn-sm indexer-button-test" data-id="{{id}}">
|
||||
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="indexer-host">
|
||||
<b>Torznab Host:</b>
|
||||
<input class="form-control" type="text" value="{{torznab_host}}" placeholder="Torznab Host" readonly="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="unconfigured-indexer card">
|
||||
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
||||
<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>
|
||||
<button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<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-value">{{{value_element}}}</div>
|
||||
</div>
|
||||
|
||||
<input class="setup-item-inputstring form-control" type="text" value="{{{value}}}" />
|
||||
<div class="setup-item-inputbool">
|
||||
{{#if value}}
|
||||
<input type="checkbox" data-id="{{id}}" class="form-control" checked />
|
||||
{{else}}
|
||||
<input type="checkbox" data-id="{{id}}" class="form-control" />
|
||||
{{/if}}
|
||||
</div>
|
||||
<img class="setup-item-displayimage" src="{{{value}}}" />
|
||||
<div class="setup-item-displayinfo alert alert-info" role="alert">{{{value}}}</div>
|
||||
|
||||
<span class="spinner glyphicon glyphicon-refresh"></span>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="custom.js"></script>
|
||||
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
|
||||
|
||||
<script src="jquery-2.1.3.min.js"></script>
|
||||
<script src="handlebars-v3.0.1.js"></script>
|
||||
<script src="bootstrap/bootstrap.min.js"></script>
|
||||
<script src="bootstrap-notify.js"></script>
|
||||
|
||||
<link href="bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="animate.css" rel="stylesheet">
|
||||
<link href="custom.css" rel="stylesheet">
|
||||
|
||||
<title>Jackett</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="page">
|
||||
|
||||
<img id="logo" src="jacket_medium.png" /><span id="header-title">Jackett</span>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="input-area">
|
||||
<span class="input-header">Sonarr API Host: </span>
|
||||
<input id="sonarr-host" class="form-control input-right" type="text" readonly />
|
||||
<button id="sonarr-settings" class="btn btn-primary btn-sm">
|
||||
Settings <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button id="sonarr-test" class="btn btn-warning btn-sm">
|
||||
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||
</button>
|
||||
<p id="sonarr-warning" class="alert alert-danger" role="alert">
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
Sonarr API must be configured
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="input-area">
|
||||
<p>
|
||||
To add a Jackett indexer in Sonarr go to <b>Settings > Indexers > Add > Torznab > Custom</b>.
|
||||
</p>
|
||||
<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="">
|
||||
<p>Use this key when adding indexers to Sonarr. This key works for all indexers.</p>
|
||||
<span class="input-header">Jackett port: </span>
|
||||
<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">
|
||||
Configure <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<span title="Jackett will restart after changing the port" class="glyphicon glyphicon-info-sign"></span>
|
||||
</div>
|
||||
<hr />
|
||||
<h3>Configured Indexers</h3>
|
||||
<div id="indexers"> </div>
|
||||
<hr />
|
||||
<div id="footer">
|
||||
Jackett Version <span id="app-version"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="select-indexer-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">Select an indexer to setup</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="unconfigured-indexers">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modals"></div>
|
||||
|
||||
<div id="templates">
|
||||
|
||||
<div class="config-setup-modal modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title">{{title}}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="config-setup-form"></form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary setup-indexer-go">Okay</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="indexer card add-indexer" data-toggle="modal" data-target="#select-indexer-modal">
|
||||
<div class="indexer-add-content">
|
||||
<span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
<div class="light-text">Add</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div class="configured-indexer indexer card">
|
||||
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
||||
<div class="indexer-buttons">
|
||||
<button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||
</button>
|
||||
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
|
||||
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
|
||||
</a>
|
||||
<button class="btn btn-warning btn-sm indexer-button-test" data-id="{{id}}">
|
||||
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="indexer-host">
|
||||
<b>Torznab Host:</b>
|
||||
<input class="form-control" type="text" value="{{torznab_host}}" placeholder="Torznab Host" readonly="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="unconfigured-indexer card">
|
||||
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
||||
<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>
|
||||
<button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<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-value">{{{value_element}}}</div>
|
||||
</div>
|
||||
|
||||
<input class="setup-item-inputstring form-control" type="text" value="{{{value}}}" />
|
||||
<div class="setup-item-inputbool">
|
||||
{{#if value}}
|
||||
<input type="checkbox" data-id="{{id}}" class="form-control" checked />
|
||||
{{else}}
|
||||
<input type="checkbox" data-id="{{id}}" class="form-control" />
|
||||
{{/if}}
|
||||
</div>
|
||||
<img class="setup-item-displayimage" src="{{{value}}}" />
|
||||
<div class="setup-item-displayinfo alert alert-info" role="alert">{{{value}}}</div>
|
||||
|
||||
<span class="spinner glyphicon glyphicon-refresh"></span>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script src="custom.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
|
@ -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")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,12 +19,14 @@ namespace Jackett.Controllers
|
|||
private IConfigurationService config;
|
||||
private ISonarrApi sonarrApi;
|
||||
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;
|
||||
sonarrApi = s;
|
||||
indexerService = i;
|
||||
serverService = ss;
|
||||
}
|
||||
|
||||
private async Task<JToken> ReadPostDataJson()
|
||||
|
@ -92,7 +94,7 @@ namespace Jackett.Controllers
|
|||
try
|
||||
{
|
||||
jsonReply["result"] = "success";
|
||||
jsonReply["api_key"] = ApiKey.CurrentKey;
|
||||
jsonReply["api_key"] = serverService.Config.APIKey;
|
||||
jsonReply["app_version"] = config.GetVersion();
|
||||
JArray items = new JArray();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ namespace Jackett
|
|||
{
|
||||
public static class CookieContainerExtensions
|
||||
{
|
||||
|
||||
public static void FillFromJson(this CookieContainer cookies, Uri uri, JToken json, Logger logger)
|
||||
{
|
||||
if (json["cookies"] != null)
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace Jackett
|
|||
{
|
||||
public class DataUrl
|
||||
{
|
||||
|
||||
public static string ReadFileToDataUrl(string file)
|
||||
{
|
||||
string mime = MimeMapping.GetMimeMapping(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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
||||
}
|
||||
|
||||
}*/
|
||||
}
|
|
@ -86,7 +86,7 @@ namespace Jackett.Indexers
|
|||
|
||||
configSaveData = new JObject();
|
||||
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
// If Windows use .net http
|
||||
var response = await client.SendAsync(message);
|
||||
|
@ -155,7 +155,7 @@ namespace Jackett.Indexers
|
|||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
||||
|
||||
string results;
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
var request = CreateHttpRequest(new Uri(episodeSearchUrl));
|
||||
request.Method = HttpMethod.Get;
|
||||
|
@ -223,7 +223,7 @@ namespace Jackett.Indexers
|
|||
|
||||
public override async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
return await client.GetByteArrayAsync(link);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ using System.Web;
|
|||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class AnimeBytes : IndexerInterface
|
||||
public class AnimeBytes : IIndexer
|
||||
{
|
||||
class ConfigurationDataBasicLoginAnimeBytes : ConfigurationDataBasicLogin
|
||||
{
|
||||
|
@ -41,8 +41,8 @@ namespace Jackett.Indexers
|
|||
private static List<CachedResult> cache = new List<CachedResult>();
|
||||
private static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0);
|
||||
|
||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||
|
||||
static string chromeUserAgent = BrowserUtil.ChromeUserAgent;
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ using NLog;
|
|||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public abstract class BaseIndexer: IndexerInterface
|
||||
public abstract class BaseIndexer: IIndexer
|
||||
{
|
||||
public string DisplayDescription { get; }
|
||||
public string DisplayName { get; }
|
||||
public string DisplayDescription { get; private set; }
|
||||
public string DisplayName { get; private 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<byte[]> Download(Uri link);
|
||||
|
|
|
@ -14,11 +14,11 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -15,9 +15,9 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
@ -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; }
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ using System.Text.RegularExpressions;
|
|||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
namespace Jackett
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class BitMeTV : IndexerInterface
|
||||
public class BitMeTV : IIndexer
|
||||
{
|
||||
class BmtvConfig : ConfigurationData
|
||||
{
|
||||
|
@ -53,8 +53,8 @@ namespace Jackett
|
|||
HttpClient client;
|
||||
Logger logger;
|
||||
|
||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
||||
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||
|
||||
public BitMeTV(Logger l)
|
||||
{
|
||||
|
|
|
@ -12,11 +12,11 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using CsQuery;
|
||||
using Jackett.Indexers;
|
||||
using Jackett.Models;
|
||||
using Jackett.Utils;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
@ -15,11 +16,11 @@ using System.Threading.Tasks;
|
|||
using System.Web;
|
||||
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 LoginUrl = BaseUrl + "/login.php";
|
||||
|
@ -40,7 +41,7 @@ namespace Jackett
|
|||
|
||||
public Uri SiteLink { get { return new Uri(BaseUrl); } }
|
||||
|
||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||
Logger logger;
|
||||
|
||||
public Freshon(Logger l)
|
||||
|
|
|
@ -15,18 +15,17 @@ using System.Web;
|
|||
|
||||
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
|
||||
string BaseUrl = DefaultUrl;
|
||||
static string chromeUserAgent = BrowserUtil.ChromeUserAgent;
|
||||
private string SearchUrl = DefaultUrl + "/torrents.php?search={0}&active=1&options=0&category%5B%5D=59&category%5B%5D=60&category%5B%5D=30&category%5B%5D=38&page={1}";
|
||||
private static string LoginUrl = DefaultUrl + "/login.php";
|
||||
private static string LoginPostUrl = DefaultUrl + "/login.php?returnto=index.php";
|
||||
private const int MAXPAGES = 3;
|
||||
|
||||
CookieContainer cookies;
|
||||
|
|
|
@ -7,9 +7,9 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace Jackett
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public interface IndexerInterface
|
||||
public interface IIndexer
|
||||
{
|
||||
string DisplayName { get; }
|
||||
string DisplayDescription { get; }
|
|
@ -15,11 +15,11 @@ using System.Web;
|
|||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class IPTorrents : IndexerInterface
|
||||
public class IPTorrents : IIndexer
|
||||
{
|
||||
|
||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
||||
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||
|
||||
public string DisplayName { get { return "IPTorrents"; } }
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ using System.Web;
|
|||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class MoreThanTV : IndexerInterface
|
||||
public class MoreThanTV : IIndexer
|
||||
{
|
||||
public string DisplayName
|
||||
{
|
||||
|
@ -32,8 +32,8 @@ namespace Jackett.Indexers
|
|||
}
|
||||
|
||||
|
||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
||||
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||
|
||||
public bool IsConfigured { get; private set; }
|
||||
|
||||
|
@ -93,7 +93,7 @@ namespace Jackett.Indexers
|
|||
|
||||
var configSaveData = new JObject();
|
||||
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
// If Windows use .net http
|
||||
var response = await client.PostAsync(LoginUrl, content);
|
||||
|
@ -156,7 +156,7 @@ namespace Jackett.Indexers
|
|||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
||||
|
||||
string results;
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
results = await client.GetStringAsync(episodeSearchUrl, retries);
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ namespace Jackett.Indexers
|
|||
|
||||
public async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
return await client.GetByteArrayAsync(link);
|
||||
}
|
||||
|
|
|
@ -12,11 +12,11 @@ using System.Threading.Tasks;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -13,11 +13,11 @@ using System.Threading.Tasks;
|
|||
|
||||
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
|
||||
{
|
||||
|
@ -87,7 +87,7 @@ namespace Jackett.Indexers
|
|||
string responseContent;
|
||||
var configSaveData = new JObject();
|
||||
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
// If Windows use .net http
|
||||
var response = await client.PostAsync(LoginUrl, content);
|
||||
|
@ -139,7 +139,7 @@ namespace Jackett.Indexers
|
|||
var searchUrl = string.Format(SearchUrl, searchSection, searchCategory, searchString);
|
||||
|
||||
string results;
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
results = await client.GetStringAsync(searchUrl);
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ namespace Jackett.Indexers
|
|||
|
||||
public async Task<byte[]> Download(Uri link)
|
||||
{
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
return await client.GetByteArrayAsync(link);
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -14,11 +14,11 @@ using System.Xml;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -12,11 +12,11 @@ using System.Web;
|
|||
|
||||
namespace Jackett.Indexers
|
||||
{
|
||||
public class Strike : IndexerInterface
|
||||
public class Strike : IIndexer
|
||||
{
|
||||
|
||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
||||
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
|
|
|
@ -14,12 +14,12 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -15,12 +15,12 @@ using System.Web;
|
|||
|
||||
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"; } }
|
||||
|
||||
|
@ -110,7 +110,7 @@ namespace Jackett.Indexers
|
|||
|
||||
string results;
|
||||
|
||||
if (WebServer.IsWindows)
|
||||
if (Engine.IsWindows)
|
||||
{
|
||||
results = await client.GetStringAsync(episodeSearchUrl);
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -15,11 +15,11 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -15,11 +15,11 @@ using System.Web;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -13,11 +13,11 @@ using System.Xml;
|
|||
|
||||
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
|
||||
{
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
|
@ -110,6 +111,7 @@
|
|||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http.WebRequest" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Web" />
|
||||
<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>
|
||||
|
@ -133,8 +135,15 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Controllers\APIController.cs" />
|
||||
<Compile Include="Controllers\DownloadController.cs" />
|
||||
<Compile Include="Engine.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="Models\CachedResult.cs" />
|
||||
<Compile Include="Models\ChannelInfo.cs" />
|
||||
|
@ -147,8 +156,7 @@
|
|||
<Compile Include="DataUrl.cs" />
|
||||
<Compile Include="ExceptionWithConfigData.cs" />
|
||||
<Compile Include="HttpClientExtensions.cs" />
|
||||
<Compile Include="IndexerInterface.cs" />
|
||||
<Compile Include="IndexerManager.cs" />
|
||||
<Compile Include="Indexers\IIndexer.cs" />
|
||||
<Compile Include="Indexers\BeyondHD.cs" />
|
||||
<Compile Include="Indexers\BitHdtv.cs" />
|
||||
<Compile Include="Indexers\BitMeTV.cs" />
|
||||
|
@ -180,22 +188,20 @@
|
|||
</Compile>
|
||||
<Compile Include="Models\ReleaseInfo.cs" />
|
||||
<Compile Include="Models\ResultPage.cs" />
|
||||
<Compile Include="Server.cs" />
|
||||
<Compile Include="Services\ServerService.cs" />
|
||||
<Compile Include="Utils\ServerUtil.cs" />
|
||||
<Compile Include="Services\ConfigurationService.cs" />
|
||||
<Compile Include="Services\IndexerManagerService.cs" />
|
||||
<Compile Include="Services\SonarApi.cs" />
|
||||
<Compile Include="Startup.cs" />
|
||||
<Compile Include="Models\TorznabQuery.cs" />
|
||||
<Compile Include="WebApi.cs" />
|
||||
<Compile Include="CurlHelper.cs" />
|
||||
<Compile Include="Indexers\AlphaRatio.cs" />
|
||||
<Compile Include="WebServer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="WebContent\fonts\glyphicons-halflings-regular.woff">
|
||||
<None Include="Content\fonts\glyphicons-halflings-regular.woff">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
@ -209,116 +215,116 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="WebContent\custom.css">
|
||||
<Content Include="Content\custom.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\custom.js">
|
||||
<Content Include="Content\custom.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\animebytes.png">
|
||||
<Content Include="Content\logos\animebytes.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\beyondhd.png">
|
||||
<Content Include="Content\logos\beyondhd.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\frenchtorrentdb.png">
|
||||
<Content Include="Content\logos\frenchtorrentdb.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\hdtorrents.png">
|
||||
<Content Include="Content\logos\hdtorrents.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\sceneaccess.png">
|
||||
<Content Include="Content\logos\sceneaccess.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\scenetime.png">
|
||||
<Content Include="Content\logos\scenetime.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\showrss.png">
|
||||
<Content Include="Content\logos\showrss.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\t411.png">
|
||||
<Content Include="Content\logos\t411.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\torrentday.png">
|
||||
<Content Include="Content\logos\torrentday.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\torrentshack.png">
|
||||
<Content Include="Content\logos\torrentshack.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\animate.css">
|
||||
<Content Include="Content\animate.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\binding_dark.png">
|
||||
<Content Include="Content\binding_dark.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\bootstrap-notify.js">
|
||||
<Content Include="Content\bootstrap-notify.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\congruent_outline.png">
|
||||
<Content Include="Content\congruent_outline.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\crissXcross.png">
|
||||
<Content Include="Content\crissXcross.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\favicon.ico">
|
||||
<Content Include="Content\favicon.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\handlebars-v3.0.1.js">
|
||||
<Content Include="Content\handlebars-v3.0.1.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\jacket_medium.png">
|
||||
<Content Include="Content\jacket_medium.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\bithdtv.png">
|
||||
<Content Include="Content\logos\bithdtv.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\bitmetv.png">
|
||||
<Content Include="Content\logos\bitmetv.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\bootstrap\bootstrap.min.css">
|
||||
<Content Include="Content\bootstrap\bootstrap.min.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\bootstrap\bootstrap.min.js">
|
||||
<Content Include="Content\bootstrap\bootstrap.min.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\common.js">
|
||||
<Content Include="Content\common.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\index.html">
|
||||
<Content Include="Content\index.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\jquery-2.1.3.min.js">
|
||||
<Content Include="Content\jquery-2.1.3.min.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\freshon.png">
|
||||
<Content Include="Content\logos\freshon.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\iptorrents.png">
|
||||
<Content Include="Content\logos\iptorrents.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\morethantv.png">
|
||||
<Content Include="Content\logos\morethantv.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\rarbg.png">
|
||||
<Content Include="Content\logos\rarbg.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\strike.png">
|
||||
<Content Include="Content\logos\strike.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\thepiratebay.png">
|
||||
<Content Include="Content\logos\thepiratebay.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\torrentleech.png">
|
||||
<Content Include="Content\logos\torrentleech.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\logos\torrentz.png">
|
||||
<Content Include="Content\logos\torrentz.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="WebContent\setup_indexer.html">
|
||||
<Content Include="Content\setup_indexer.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Resources\validator_reply.xml" />
|
||||
<Content Include="WebContent\logos\alpharatio.png">
|
||||
<Content Include="Content\logos\alpharatio.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac.Integration.WebApi;
|
||||
using Jackett.Indexers;
|
||||
|
||||
namespace Jackett
|
||||
{
|
||||
|
@ -14,15 +15,15 @@ namespace Jackett
|
|||
{
|
||||
// Just register everything!
|
||||
var thisAssembly = typeof(JackettModule).Assembly;
|
||||
builder.RegisterAssemblyTypes(thisAssembly).AsImplementedInterfaces().SingleInstance();
|
||||
builder.RegisterAssemblyTypes(thisAssembly).Except<IIndexer>().AsImplementedInterfaces().SingleInstance();
|
||||
builder.RegisterApiControllers(thisAssembly).InstancePerRequest();
|
||||
|
||||
// Register indexers
|
||||
foreach(var indexer in thisAssembly.GetTypes()
|
||||
.Where(p => typeof(IndexerInterface).IsAssignableFrom(p) && !p.IsInterface)
|
||||
.Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface)
|
||||
.ToArray())
|
||||
{
|
||||
builder.RegisterType(indexer).Named<IndexerInterface>(indexer.Name.ToLowerInvariant()).SingleInstance();
|
||||
builder.RegisterType(indexer).Named<IIndexer>(indexer.Name.ToLowerInvariant()).SingleInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
@ -19,22 +20,115 @@ namespace Jackett.Services
|
|||
string GetAppDataFolder();
|
||||
JObject ReadServerSettingsFile();
|
||||
string GetSonarrConfigFile();
|
||||
T GetConfig<T>();
|
||||
void SaveConfig<T>(T config);
|
||||
string ApplicationFolder();
|
||||
}
|
||||
|
||||
public class ConfigurationService: IConfigurationService
|
||||
{
|
||||
private ISerializeService serializeService;
|
||||
private Logger logger;
|
||||
|
||||
public ConfigurationService(ISerializeService s, Logger l)
|
||||
{
|
||||
serializeService = s;
|
||||
logger = l;
|
||||
CreateOrMigrateSettings();
|
||||
}
|
||||
|
||||
private void CreateOrMigrateSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(GetAppDataFolder()))
|
||||
{
|
||||
Directory.CreateDirectory(GetAppDataFolder());
|
||||
}
|
||||
|
||||
logger.Debug("App config/log directory: " + GetAppDataFolder());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("Could not create settings directory. " + ex.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string oldDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jackett");
|
||||
if (Directory.Exists(oldDir))
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(oldDir, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
var path = file.Replace(oldDir, "");
|
||||
var destFolder = GetAppDataFolder()+ path;
|
||||
if (!Directory.Exists(Path.GetDirectoryName(destFolder)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(destFolder));
|
||||
}
|
||||
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()
|
||||
{
|
||||
var baseDir = Path.GetDirectoryName(Application.ExecutablePath);
|
||||
// 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");
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Combine(baseDir, "WebContent");
|
||||
dir = Path.Combine(ApplicationFolder(), "..\\..\\..\\Jackett\\Content");
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
public string GetVersion()
|
||||
|
@ -44,7 +138,16 @@ namespace Jackett.Services
|
|||
|
||||
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()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Autofac;
|
||||
using Jackett.Indexers;
|
||||
using Jackett.Models;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
@ -15,9 +16,10 @@ namespace Jackett.Services
|
|||
{
|
||||
void TestIndexer(string name);
|
||||
void DeleteIndexer(string name);
|
||||
IndexerInterface GetIndexer(string name);
|
||||
IEnumerable<IndexerInterface> GetAllIndexers();
|
||||
void SaveConfig(IndexerInterface indexer, JToken obj);
|
||||
IIndexer GetIndexer(string name);
|
||||
IEnumerable<IIndexer> GetAllIndexers();
|
||||
void SaveConfig(IIndexer indexer, JToken obj);
|
||||
void InitIndexers();
|
||||
}
|
||||
|
||||
public class IndexerManagerService : IIndexerManagerService
|
||||
|
@ -33,14 +35,35 @@ namespace Jackett.Services
|
|||
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)
|
||||
|
@ -62,12 +85,12 @@ namespace Jackett.Services
|
|||
//LoadMissingIndexers();
|
||||
}
|
||||
|
||||
private string GetIndexerConfigFilePath(IndexerInterface indexer)
|
||||
private string GetIndexerConfigFilePath(IIndexer indexer)
|
||||
{
|
||||
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);
|
||||
if (!Directory.Exists(configService.GetIndexerConfigDir()))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ namespace Jackett
|
|||
Task TestConnection();
|
||||
SonarrApi.ConfigurationSonarr GetConfiguration();
|
||||
Task ApplyConfiguration(JToken configJson);
|
||||
Task<string[]> GetShowTitle(int rid);
|
||||
}
|
||||
|
||||
public class SonarrApi: ISonarrApi
|
||||
|
@ -62,6 +63,8 @@ namespace Jackett
|
|||
|
||||
public SonarrApi(IConfigurationService c)
|
||||
{
|
||||
configService = c;
|
||||
|
||||
LoadSettings();
|
||||
|
||||
cookies = new CookieContainer();
|
||||
|
@ -74,8 +77,6 @@ namespace Jackett
|
|||
client = new HttpClient(handler);
|
||||
|
||||
IdNameMappings = new ConcurrentDictionary<int, string[]>();
|
||||
|
||||
configService = c;
|
||||
}
|
||||
|
||||
async Task ReloadNameMappings(string host, int port, string apiKey)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,30 +23,35 @@ namespace Jackett
|
|||
{
|
||||
// Configure Web API for self-host.
|
||||
var config = new HttpConfiguration();
|
||||
|
||||
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.DependencyResolver = new AutofacWebApiDependencyResolver(Engine.GetContainer());
|
||||
config.MapHttpAttributeRoutes();
|
||||
|
||||
config.Routes.MapHttpRoute(
|
||||
name: "Content",
|
||||
routeTemplate: "{controller}/{action}",
|
||||
defaults: new { controller = "Admin"}
|
||||
name: "Admin",
|
||||
routeTemplate: "admin/{action}",
|
||||
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
|
||||
{
|
||||
RequestPath = new PathString(string.Empty),
|
||||
FileSystem = new PhysicalFileSystem(Server.GetContainer().Resolve<IConfigurationService>().GetContentFolder()),
|
||||
FileSystem = new PhysicalFileSystem(Engine.ConfigService.GetContentFolder()),
|
||||
EnableDirectoryBrowsing = true,
|
||||
});
|
||||
|
||||
appBuilder.UseWebApi(config);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}*/
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}*/
|
||||
}
|