Refactor done
15
Build.bat
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
rmdir /s /q build
|
||||||
|
cd src
|
||||||
|
Msbuild Jackett.sln /t:Clean,Build /p:Configuration=Release
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
xcopy src\Jackett.Console\bin\Release Build\ /e /y
|
||||||
|
copy /Y src\Jackett.Service\bin\Release\JackettService.exe build\JackettService.exe
|
||||||
|
copy /Y src\Jackett.Service\bin\Release\JackettService.exe.config build\JackettService.exe.config
|
||||||
|
copy /Y src\Jackett.Tray\bin\Release\JackettTray.exe build\JackettTray.exe
|
||||||
|
copy /Y src\Jackett.Tray\bin\Release\JackettTray.exe.config build\JackettTray.exe.config
|
||||||
|
cd build
|
||||||
|
del *.pdb
|
||||||
|
del *.xml
|
||||||
|
cd ..
|
58
Installer.iss
Normal file
@@ -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>
|
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\NLog.4.0.1\lib\net45\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
|
@@ -16,62 +16,34 @@ namespace JackettConsole
|
|||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
Server.Start();
|
|
||||||
Console.ReadKey();
|
|
||||||
|
|
||||||
|
|
||||||
/* var serverTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
ServerInstance = new Server();
|
|
||||||
await ServerInstance.Start();
|
|
||||||
});
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Program.IsWindows)
|
if (args.Length > 0)
|
||||||
{
|
{
|
||||||
#if !__MonoCS__
|
switch (args[0].ToLowerInvariant())
|
||||||
Application.Run(new Main());
|
{
|
||||||
#endif
|
case "/i":
|
||||||
|
Engine.ServiceConfig.Install();
|
||||||
|
return;
|
||||||
|
case "/r":
|
||||||
|
Engine.Server.ReserveUrls();
|
||||||
|
return;
|
||||||
|
case "/u":
|
||||||
|
Engine.Server.ReserveUrls(false);
|
||||||
|
Engine.ServiceConfig.Uninstall();
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Engine.Server.Start();
|
||||||
|
Engine.Logger.Info("Running in headless mode.");
|
||||||
|
Engine.RunTime.Spin();
|
||||||
|
Engine.Logger.Info("Server thread exit");
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
Engine.Logger.Error(e, "Top level exception");
|
||||||
}*/
|
}
|
||||||
|
|
||||||
Console.WriteLine("Running in headless mode.");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Task.WaitAll(serverTask);
|
|
||||||
Console.WriteLine("Server thread exit");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* public static void RestartServer()
|
|
||||||
{
|
|
||||||
|
|
||||||
ServerInstance.Stop();
|
|
||||||
ServerInstance = null;
|
|
||||||
var serverTask = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
ServerInstance = new Server();
|
|
||||||
await ServerInstance.Start();
|
|
||||||
});
|
|
||||||
Task.WaitAll(serverTask);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static public void RestartAsAdmin()
|
|
||||||
{
|
|
||||||
// var startInfo = new ProcessStartInfo(Application.ExecutablePath.ToString()) { Verb = "runas" };
|
|
||||||
// Process.Start(startInfo);
|
|
||||||
Environment.Exit(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,5 +14,6 @@
|
|||||||
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" userInstalled="true" />
|
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" userInstalled="true" />
|
||||||
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" userInstalled="true" />
|
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" userInstalled="true" />
|
||||||
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" userInstalled="true" />
|
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" userInstalled="true" />
|
||||||
|
<package id="NLog" version="4.0.1" targetFramework="net452" />
|
||||||
<package id="Owin" version="1.0" targetFramework="net452" userInstalled="true" />
|
<package id="Owin" version="1.0" targetFramework="net452" userInstalled="true" />
|
||||||
</packages>
|
</packages>
|
@@ -1,6 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||||
</startup>
|
</startup>
|
||||||
|
<runtime>
|
||||||
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.Web.Http.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-7.0.0.0" newVersion="7.0.0.0" />
|
||||||
|
</dependentAssembly>
|
||||||
|
</assemblyBinding>
|
||||||
|
</runtime>
|
||||||
</configuration>
|
</configuration>
|
@@ -36,8 +36,65 @@
|
|||||||
<ApplicationIcon>jackett.ico</ApplicationIcon>
|
<ApplicationIcon>jackett.ico</ApplicationIcon>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Autofac.3.5.0\lib\net40\Autofac.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Autofac.Integration.Owin">
|
||||||
|
<HintPath>..\packages\Autofac.Owin.3.1.0\lib\net45\Autofac.Integration.Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Autofac.Integration.WebApi, Version=3.4.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Autofac.WebApi2.3.4.0\lib\net45\Autofac.Integration.WebApi.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Autofac.Integration.WebApi.Owin">
|
||||||
|
<HintPath>..\packages\Autofac.WebApi2.Owin.3.2.0\lib\net45\Autofac.Integration.WebApi.Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.FileSystems, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.FileSystems.3.0.1\lib\net45\Microsoft.Owin.FileSystems.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Host.HttpListener">
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Host.HttpListener.3.0.1\lib\net45\Microsoft.Owin.Host.HttpListener.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.Hosting, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.Hosting.2.0.2\lib\net45\Microsoft.Owin.Hosting.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.Owin.StaticFiles, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Microsoft.Owin.StaticFiles.3.0.1\lib\net45\Microsoft.Owin.StaticFiles.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\NLog.4.0.1\lib\net45\NLog.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Web.Http.Owin, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.Owin.5.2.3\lib\net45\System.Web.Http.Owin.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@@ -58,10 +115,17 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="App.config" />
|
<None Include="App.config" />
|
||||||
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="jackett.ico" />
|
<Content Include="jackett.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Jackett\Jackett.csproj">
|
||||||
|
<Project>{e636d5f8-68b4-4903-b4ed-ccfd9c9e899f}</Project>
|
||||||
|
<Name>Jackett</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
2
src/Jackett.Service/Service.Designer.cs
generated
@@ -29,7 +29,7 @@
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
components = new System.ComponentModel.Container();
|
components = new System.ComponentModel.Container();
|
||||||
this.ServiceName = "Service1";
|
this.ServiceName = "Jackett";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
@@ -19,10 +19,15 @@ namespace Jackett.Service
|
|||||||
|
|
||||||
protected override void OnStart(string[] args)
|
protected override void OnStart(string[] args)
|
||||||
{
|
{
|
||||||
|
Engine.Logger.Info("Service starting");
|
||||||
|
Engine.Server.Start();
|
||||||
|
Engine.Logger.Info("Service started");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnStop()
|
protected override void OnStop()
|
||||||
{
|
{
|
||||||
|
Engine.Logger.Info("Service stopping");
|
||||||
|
Engine.Server.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
src/Jackett.Service/packages.config
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Autofac" version="3.5.0" targetFramework="net452" />
|
||||||
|
<package id="Autofac.Owin" version="3.1.0" targetFramework="net452" />
|
||||||
|
<package id="Autofac.WebApi2" version="3.4.0" targetFramework="net452" />
|
||||||
|
<package id="Autofac.WebApi2.Owin" version="3.2.0" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.AspNet.WebApi.OwinSelfHost" version="5.2.3" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.Owin.FileSystems" version="3.0.1" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.Owin.Host.HttpListener" version="3.0.1" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" />
|
||||||
|
<package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net452" />
|
||||||
|
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net452" />
|
||||||
|
<package id="NLog" version="4.0.1" targetFramework="net452" />
|
||||||
|
<package id="Owin" version="1.0" targetFramework="net452" />
|
||||||
|
</packages>
|
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>
|
<!DOCTYPE html>
|
||||||
|
|
||||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
|
<link rel='shortcut icon' type='image/x-icon' href='/favicon.ico' />
|
||||||
|
|
||||||
<script src="jquery-2.1.3.min.js"></script>
|
<script src="jquery-2.1.3.min.js"></script>
|
||||||
<script src="handlebars-v3.0.1.js"></script>
|
<script src="handlebars-v3.0.1.js"></script>
|
||||||
<script src="bootstrap/bootstrap.min.js"></script>
|
<script src="bootstrap/bootstrap.min.js"></script>
|
||||||
<script src="bootstrap-notify.js"></script>
|
<script src="bootstrap-notify.js"></script>
|
||||||
|
|
||||||
<link href="bootstrap/bootstrap.min.css" rel="stylesheet">
|
<link href="bootstrap/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="animate.css" rel="stylesheet">
|
<link href="animate.css" rel="stylesheet">
|
||||||
<link href="custom.css" rel="stylesheet">
|
<link href="custom.css" rel="stylesheet">
|
||||||
|
|
||||||
<title>Jackett</title>
|
<title>Jackett</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="page">
|
<div id="page">
|
||||||
|
|
||||||
<img id="logo" src="jacket_medium.png" /><span id="header-title">Jackett</span>
|
<img id="logo" src="jacket_medium.png" /><span id="header-title">Jackett</span>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="input-area">
|
<div class="input-area">
|
||||||
<span class="input-header">Sonarr API Host: </span>
|
<span class="input-header">Sonarr API Host: </span>
|
||||||
<input id="sonarr-host" class="form-control input-right" type="text" readonly />
|
<input id="sonarr-host" class="form-control input-right" type="text" readonly />
|
||||||
<button id="sonarr-settings" class="btn btn-primary btn-sm">
|
<button id="sonarr-settings" class="btn btn-primary btn-sm">
|
||||||
Settings <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
Settings <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<button id="sonarr-test" class="btn btn-warning btn-sm">
|
<button id="sonarr-test" class="btn btn-warning btn-sm">
|
||||||
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<p id="sonarr-warning" class="alert alert-danger" role="alert">
|
<p id="sonarr-warning" class="alert alert-danger" role="alert">
|
||||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||||
Sonarr API must be configured
|
Sonarr API must be configured
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="input-area">
|
<div class="input-area">
|
||||||
<p>
|
<p>
|
||||||
To add a Jackett indexer in Sonarr go to <b>Settings > Indexers > Add > Torznab > Custom</b>.
|
To add a Jackett indexer in Sonarr go to <b>Settings > Indexers > Add > Torznab > Custom</b>.
|
||||||
</p>
|
</p>
|
||||||
<span class="input-header">Jackett API Key: </span>
|
<span class="input-header">Jackett API Key: </span>
|
||||||
<input id="api-key-input" class="form-control input-right" type="text" value="" placeholder="API Key" readonly="">
|
<input id="api-key-input" class="form-control input-right" type="text" value="" placeholder="API Key" readonly="">
|
||||||
<p>Use this key when adding indexers to Sonarr. This key works for all indexers.</p>
|
<p>Use this key when adding indexers to Sonarr. This key works for all indexers.</p>
|
||||||
<span class="input-header">Jackett port: </span>
|
<span class="input-header">Jackett port: </span>
|
||||||
<input id="jackett-port" class="form-control input-right" type="text" value="" placeholder="9117">
|
<input id="jackett-port" class="form-control input-right" type="text" value="" placeholder="9117">
|
||||||
<button id="change-jackett-port" class="btn btn-primary btn-sm">
|
<button id="change-jackett-port" class="btn btn-primary btn-sm">
|
||||||
Configure <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
Configure <span class="glyphicon glyphicon-ok-wrench" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<span title="Jackett will restart after changing the port" class="glyphicon glyphicon-info-sign"></span>
|
<span title="Jackett will restart after changing the port" class="glyphicon glyphicon-info-sign"></span>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<h3>Configured Indexers</h3>
|
<h3>Configured Indexers</h3>
|
||||||
<div id="indexers"> </div>
|
<div id="indexers"> </div>
|
||||||
<hr />
|
<hr />
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
Jackett Version <span id="app-version"></span>
|
Jackett Version <span id="app-version"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="select-indexer-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
<div id="select-indexer-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<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>
|
<h4 class="modal-title">Select an indexer to setup</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div id="unconfigured-indexers">
|
<div id="unconfigured-indexers">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="modals"></div>
|
<div id="modals"></div>
|
||||||
|
|
||||||
<div id="templates">
|
<div id="templates">
|
||||||
|
|
||||||
<div class="config-setup-modal modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
<div class="config-setup-modal modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title">{{title}}</h4>
|
<h4 class="modal-title">{{title}}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form class="config-setup-form"></form>
|
<form class="config-setup-form"></form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
<button type="button" class="btn btn-primary setup-indexer-go">Okay</button>
|
<button type="button" class="btn btn-primary setup-indexer-go">Okay</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="indexer card add-indexer" data-toggle="modal" data-target="#select-indexer-modal">
|
<button class="indexer card add-indexer" data-toggle="modal" data-target="#select-indexer-modal">
|
||||||
<div class="indexer-add-content">
|
<div class="indexer-add-content">
|
||||||
<span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
<div class="light-text">Add</div>
|
<div class="light-text">Add</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="configured-indexer indexer card">
|
<div class="configured-indexer indexer card">
|
||||||
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
||||||
<div class="indexer-buttons">
|
<div class="indexer-buttons">
|
||||||
<button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}">
|
<button class="btn btn-primary btn-sm indexer-setup" data-id="{{id}}">
|
||||||
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
|
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
|
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
|
||||||
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
|
<span class="glyphicon glyphicon-new-window" aria-hidden="true"></span>
|
||||||
</a>
|
</a>
|
||||||
<button class="btn btn-warning btn-sm indexer-button-test" data-id="{{id}}">
|
<button class="btn btn-warning btn-sm indexer-button-test" data-id="{{id}}">
|
||||||
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="indexer-host">
|
<div class="indexer-host">
|
||||||
<b>Torznab Host:</b>
|
<b>Torznab Host:</b>
|
||||||
<input class="form-control" type="text" value="{{torznab_host}}" placeholder="Torznab Host" readonly="">
|
<input class="form-control" type="text" value="{{torznab_host}}" placeholder="Torznab Host" readonly="">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="unconfigured-indexer card">
|
<div class="unconfigured-indexer card">
|
||||||
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
<div class="indexer-logo"><img alt="{{name}}" title="{{name}}" src="logos/{{id}}.png" /></div>
|
||||||
<div class="indexer-buttons">
|
<div class="indexer-buttons">
|
||||||
<a class="btn btn-info" target="_blank" href="{{site_link}}">Visit <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>
|
<a class="btn btn-info" target="_blank" href="{{site_link}}">Visit <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a>
|
||||||
<button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
<button class="indexer-setup btn btn-success" data-id="{{id}}">Setup <span class="glyphicon glyphicon-ok" aria-hidden="true"></span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="setup-item form-group" data-id="{{id}}" data-value="{{value}}" data-type="{{type}}">
|
<div class="setup-item form-group" data-id="{{id}}" data-value="{{value}}" data-type="{{type}}">
|
||||||
<div class="setup-item-label">{{name}}</div>
|
<div class="setup-item-label">{{name}}</div>
|
||||||
<div class="setup-item-value">{{{value_element}}}</div>
|
<div class="setup-item-value">{{{value_element}}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input class="setup-item-inputstring form-control" type="text" value="{{{value}}}" />
|
<input class="setup-item-inputstring form-control" type="text" value="{{{value}}}" />
|
||||||
<div class="setup-item-inputbool">
|
<div class="setup-item-inputbool">
|
||||||
{{#if value}}
|
{{#if value}}
|
||||||
<input type="checkbox" data-id="{{id}}" class="form-control" checked />
|
<input type="checkbox" data-id="{{id}}" class="form-control" checked />
|
||||||
{{else}}
|
{{else}}
|
||||||
<input type="checkbox" data-id="{{id}}" class="form-control" />
|
<input type="checkbox" data-id="{{id}}" class="form-control" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<img class="setup-item-displayimage" src="{{{value}}}" />
|
<img class="setup-item-displayimage" src="{{{value}}}" />
|
||||||
<div class="setup-item-displayinfo alert alert-info" role="alert">{{{value}}}</div>
|
<div class="setup-item-displayinfo alert alert-info" role="alert">{{{value}}}</div>
|
||||||
|
|
||||||
<span class="spinner glyphicon glyphicon-refresh"></span>
|
<span class="spinner glyphicon glyphicon-refresh"></span>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script src="custom.js"></script>
|
<script src="custom.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
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 |
75
src/Jackett/Controllers/APIController.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using Jackett.Models;
|
||||||
|
using Jackett.Services;
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Http;
|
||||||
|
|
||||||
|
namespace Jackett.Controllers
|
||||||
|
{
|
||||||
|
public class APIController : ApiController
|
||||||
|
{
|
||||||
|
private IIndexerManagerService indexerService;
|
||||||
|
private ISonarrApi sonarrService;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
public APIController(IIndexerManagerService i, ISonarrApi s, Logger l)
|
||||||
|
{
|
||||||
|
indexerService = i;
|
||||||
|
sonarrService = s;
|
||||||
|
logger = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<HttpResponseMessage> Call(string indexerName)
|
||||||
|
{
|
||||||
|
var indexer = indexerService.GetIndexer(indexerName);
|
||||||
|
var torznabQuery = TorznabQuery.FromHttpQuery(HttpUtility.ParseQueryString(Request.RequestUri.Query));
|
||||||
|
|
||||||
|
if (torznabQuery.RageID != 0)
|
||||||
|
torznabQuery.ShowTitles = await sonarrService.GetShowTitle(torznabQuery.RageID);
|
||||||
|
else if (!string.IsNullOrEmpty(torznabQuery.SearchTerm))
|
||||||
|
torznabQuery.ShowTitles = new string[] { torznabQuery.SearchTerm };
|
||||||
|
|
||||||
|
var releases = await indexer.PerformQuery(torznabQuery);
|
||||||
|
|
||||||
|
logger.Debug(string.Format("Found {0} releases from {1}", releases.Length, indexer.DisplayName));
|
||||||
|
var severUrl = string.Format("{0}://{1}:{2}/", Request.RequestUri.Scheme, Request.RequestUri.Host, Request.RequestUri.Port);
|
||||||
|
|
||||||
|
var resultPage = new ResultPage(new ChannelInfo
|
||||||
|
{
|
||||||
|
Title = indexer.DisplayName,
|
||||||
|
Description = indexer.DisplayDescription,
|
||||||
|
Link = indexer.SiteLink,
|
||||||
|
ImageUrl = new Uri(severUrl + "logos/" + indexer.DisplayName + ".png"),
|
||||||
|
ImageTitle = indexer.DisplayName,
|
||||||
|
ImageLink = indexer.SiteLink,
|
||||||
|
ImageDescription = indexer.DisplayName
|
||||||
|
});
|
||||||
|
|
||||||
|
// add Jackett proxy to download links...
|
||||||
|
foreach (var release in releases)
|
||||||
|
{
|
||||||
|
if (release.Link == null || release.Link.Scheme == "magnet")
|
||||||
|
continue;
|
||||||
|
var originalLink = release.Link;
|
||||||
|
var encodedLink = HttpServerUtility.UrlTokenEncode(Encoding.UTF8.GetBytes(originalLink.ToString())) + "/download.torrent";
|
||||||
|
var proxyLink = string.Format("{0}api/{1}/download/{2}", severUrl, indexer.DisplayName.ToLowerInvariant(), encodedLink);
|
||||||
|
release.Link = new Uri(proxyLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
resultPage.Releases.AddRange(releases);
|
||||||
|
var xml = resultPage.ToXml(new Uri(severUrl));
|
||||||
|
// Force the return as XML
|
||||||
|
return new HttpResponseMessage()
|
||||||
|
{
|
||||||
|
Content = new StringContent(xml, Encoding.UTF8, "application/rss+xml")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -19,12 +19,14 @@ namespace Jackett.Controllers
|
|||||||
private IConfigurationService config;
|
private IConfigurationService config;
|
||||||
private ISonarrApi sonarrApi;
|
private ISonarrApi sonarrApi;
|
||||||
private IIndexerManagerService indexerService;
|
private IIndexerManagerService indexerService;
|
||||||
|
private IServerService serverService;
|
||||||
|
|
||||||
public AdminController(IConfigurationService config, ISonarrApi s, IIndexerManagerService i)
|
public AdminController(IConfigurationService config, ISonarrApi s, IIndexerManagerService i, IServerService ss)
|
||||||
{
|
{
|
||||||
this.config = config;
|
this.config = config;
|
||||||
sonarrApi = s;
|
sonarrApi = s;
|
||||||
indexerService = i;
|
indexerService = i;
|
||||||
|
serverService = ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<JToken> ReadPostDataJson()
|
private async Task<JToken> ReadPostDataJson()
|
||||||
@@ -92,7 +94,7 @@ namespace Jackett.Controllers
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
jsonReply["result"] = "success";
|
jsonReply["result"] = "success";
|
||||||
jsonReply["api_key"] = ApiKey.CurrentKey;
|
jsonReply["api_key"] = serverService.Config.APIKey;
|
||||||
jsonReply["app_version"] = config.GetVersion();
|
jsonReply["app_version"] = config.GetVersion();
|
||||||
JArray items = new JArray();
|
JArray items = new JArray();
|
||||||
|
|
||||||
|
48
src/Jackett/Controllers/DownloadController.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using Jackett.Services;
|
||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Web;
|
||||||
|
using System.Web.Http;
|
||||||
|
|
||||||
|
namespace Jackett.Controllers
|
||||||
|
{
|
||||||
|
public class DownloadController : ApiController
|
||||||
|
{
|
||||||
|
private Logger logger;
|
||||||
|
private IIndexerManagerService indexerService;
|
||||||
|
|
||||||
|
public DownloadController(IIndexerManagerService i, Logger l)
|
||||||
|
{
|
||||||
|
logger = l;
|
||||||
|
indexerService = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<HttpResponseMessage> Download(string indexerName, string path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var indexer = indexerService.GetIndexer(indexerName);
|
||||||
|
var remoteFile = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(path));
|
||||||
|
var downloadBytes = await indexer.Download(new Uri(remoteFile));
|
||||||
|
|
||||||
|
var result = new HttpResponseMessage(HttpStatusCode.OK);
|
||||||
|
result.Content = new ByteArrayContent(downloadBytes);
|
||||||
|
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-bittorrent");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error(e, "Error downloading " + indexerName + " " + path);
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.NotFound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -12,7 +12,6 @@ namespace Jackett
|
|||||||
{
|
{
|
||||||
public static class CookieContainerExtensions
|
public static class CookieContainerExtensions
|
||||||
{
|
{
|
||||||
|
|
||||||
public static void FillFromJson(this CookieContainer cookies, Uri uri, JToken json, Logger logger)
|
public static void FillFromJson(this CookieContainer cookies, Uri uri, JToken json, Logger logger)
|
||||||
{
|
{
|
||||||
if (json["cookies"] != null)
|
if (json["cookies"] != null)
|
||||||
|
@@ -10,7 +10,6 @@ namespace Jackett
|
|||||||
{
|
{
|
||||||
public class DataUrl
|
public class DataUrl
|
||||||
{
|
{
|
||||||
|
|
||||||
public static string ReadFileToDataUrl(string file)
|
public static string ReadFileToDataUrl(string file)
|
||||||
{
|
{
|
||||||
string mime = MimeMapping.GetMimeMapping(file);
|
string mime = MimeMapping.GetMimeMapping(file);
|
||||||
|
111
src/Jackett/Engine.cs
Normal file
@@ -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();
|
configSaveData = new JObject();
|
||||||
|
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
// If Windows use .net http
|
// If Windows use .net http
|
||||||
var response = await client.SendAsync(message);
|
var response = await client.SendAsync(message);
|
||||||
@@ -155,7 +155,7 @@ namespace Jackett.Indexers
|
|||||||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
||||||
|
|
||||||
string results;
|
string results;
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
var request = CreateHttpRequest(new Uri(episodeSearchUrl));
|
var request = CreateHttpRequest(new Uri(episodeSearchUrl));
|
||||||
request.Method = HttpMethod.Get;
|
request.Method = HttpMethod.Get;
|
||||||
@@ -223,7 +223,7 @@ namespace Jackett.Indexers
|
|||||||
|
|
||||||
public override async Task<byte[]> Download(Uri link)
|
public override async Task<byte[]> Download(Uri link)
|
||||||
{
|
{
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
return await client.GetByteArrayAsync(link);
|
return await client.GetByteArrayAsync(link);
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class AnimeBytes : IndexerInterface
|
public class AnimeBytes : IIndexer
|
||||||
{
|
{
|
||||||
class ConfigurationDataBasicLoginAnimeBytes : ConfigurationDataBasicLogin
|
class ConfigurationDataBasicLoginAnimeBytes : ConfigurationDataBasicLogin
|
||||||
{
|
{
|
||||||
@@ -41,8 +41,8 @@ namespace Jackett.Indexers
|
|||||||
private static List<CachedResult> cache = new List<CachedResult>();
|
private static List<CachedResult> cache = new List<CachedResult>();
|
||||||
private static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0);
|
private static readonly TimeSpan cacheTime = new TimeSpan(0, 9, 0);
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
static string chromeUserAgent = BrowserUtil.ChromeUserAgent;
|
static string chromeUserAgent = BrowserUtil.ChromeUserAgent;
|
||||||
|
|
||||||
|
@@ -9,12 +9,12 @@ using NLog;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public abstract class BaseIndexer: IndexerInterface
|
public abstract class BaseIndexer: IIndexer
|
||||||
{
|
{
|
||||||
public string DisplayDescription { get; }
|
public string DisplayDescription { get; private set; }
|
||||||
public string DisplayName { get; }
|
public string DisplayName { get; private set; }
|
||||||
public bool IsConfigured { get; protected set; }
|
public bool IsConfigured { get; protected set; }
|
||||||
public Uri SiteLink { get; }
|
public Uri SiteLink { get; private set; }
|
||||||
|
|
||||||
public abstract Task ApplyConfiguration(JToken configJson);
|
public abstract Task ApplyConfiguration(JToken configJson);
|
||||||
public abstract Task<byte[]> Download(Uri link);
|
public abstract Task<byte[]> Download(Uri link);
|
||||||
|
@@ -14,11 +14,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class BeyondHD : IndexerInterface
|
public class BeyondHD : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -15,9 +15,9 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class BitHdtv : IndexerInterface
|
public class BitHdtv : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
@@ -100,7 +100,7 @@ namespace Jackett.Indexers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public bool IsConfigured { get; private set; }
|
public bool IsConfigured { get; private set; }
|
||||||
|
|
||||||
|
@@ -14,9 +14,9 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
|
||||||
namespace Jackett
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class BitMeTV : IndexerInterface
|
public class BitMeTV : IIndexer
|
||||||
{
|
{
|
||||||
class BmtvConfig : ConfigurationData
|
class BmtvConfig : ConfigurationData
|
||||||
{
|
{
|
||||||
@@ -53,8 +53,8 @@ namespace Jackett
|
|||||||
HttpClient client;
|
HttpClient client;
|
||||||
Logger logger;
|
Logger logger;
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public BitMeTV(Logger l)
|
public BitMeTV(Logger l)
|
||||||
{
|
{
|
||||||
|
@@ -12,11 +12,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
class FrenchTorrentDb : IndexerInterface
|
class FrenchTorrentDb : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
class ConfigurationDataBasicLoginFrenchTorrentDb : ConfigurationData
|
class ConfigurationDataBasicLoginFrenchTorrentDb : ConfigurationData
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using CsQuery;
|
using CsQuery;
|
||||||
|
using Jackett.Indexers;
|
||||||
using Jackett.Models;
|
using Jackett.Models;
|
||||||
using Jackett.Utils;
|
using Jackett.Utils;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@@ -15,11 +16,11 @@ using System.Threading.Tasks;
|
|||||||
using System.Web;
|
using System.Web;
|
||||||
using System.Web.UI.WebControls;
|
using System.Web.UI.WebControls;
|
||||||
|
|
||||||
namespace Jackett
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class Freshon : IndexerInterface
|
public class Freshon : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
static string BaseUrl = "https://freshon.tv";
|
static string BaseUrl = "https://freshon.tv";
|
||||||
static string LoginUrl = BaseUrl + "/login.php";
|
static string LoginUrl = BaseUrl + "/login.php";
|
||||||
@@ -40,7 +41,7 @@ namespace Jackett
|
|||||||
|
|
||||||
public Uri SiteLink { get { return new Uri(BaseUrl); } }
|
public Uri SiteLink { get { return new Uri(BaseUrl); } }
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
Logger logger;
|
Logger logger;
|
||||||
|
|
||||||
public Freshon(Logger l)
|
public Freshon(Logger l)
|
||||||
|
@@ -15,18 +15,17 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class HDTorrents : IndexerInterface
|
public class HDTorrents : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
const string DefaultUrl = "http://hdts.ru"; // Of the accessible domains the .ru seems the most reliable. https://hdts.ru | https://hd-torrents.org | https://hd-torrents.net | https://hd-torrents.me
|
const string DefaultUrl = "http://hdts.ru"; // Of the accessible domains the .ru seems the most reliable. https://hdts.ru | https://hd-torrents.org | https://hd-torrents.net | https://hd-torrents.me
|
||||||
string BaseUrl = DefaultUrl;
|
string BaseUrl = DefaultUrl;
|
||||||
static string chromeUserAgent = BrowserUtil.ChromeUserAgent;
|
static string chromeUserAgent = BrowserUtil.ChromeUserAgent;
|
||||||
private string SearchUrl = DefaultUrl + "/torrents.php?search={0}&active=1&options=0&category%5B%5D=59&category%5B%5D=60&category%5B%5D=30&category%5B%5D=38&page={1}";
|
private string SearchUrl = DefaultUrl + "/torrents.php?search={0}&active=1&options=0&category%5B%5D=59&category%5B%5D=60&category%5B%5D=30&category%5B%5D=38&page={1}";
|
||||||
private static string LoginUrl = DefaultUrl + "/login.php";
|
private static string LoginUrl = DefaultUrl + "/login.php";
|
||||||
private static string LoginPostUrl = DefaultUrl + "/login.php?returnto=index.php";
|
|
||||||
private const int MAXPAGES = 3;
|
private const int MAXPAGES = 3;
|
||||||
|
|
||||||
CookieContainer cookies;
|
CookieContainer cookies;
|
||||||
|
@@ -7,9 +7,9 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web.UI.WebControls;
|
using System.Web.UI.WebControls;
|
||||||
|
|
||||||
namespace Jackett
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public interface IndexerInterface
|
public interface IIndexer
|
||||||
{
|
{
|
||||||
string DisplayName { get; }
|
string DisplayName { get; }
|
||||||
string DisplayDescription { get; }
|
string DisplayDescription { get; }
|
@@ -15,11 +15,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class IPTorrents : IndexerInterface
|
public class IPTorrents : IIndexer
|
||||||
{
|
{
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName { get { return "IPTorrents"; } }
|
public string DisplayName { get { return "IPTorrents"; } }
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class MoreThanTV : IndexerInterface
|
public class MoreThanTV : IIndexer
|
||||||
{
|
{
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
@@ -32,8 +32,8 @@ namespace Jackett.Indexers
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public bool IsConfigured { get; private set; }
|
public bool IsConfigured { get; private set; }
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ namespace Jackett.Indexers
|
|||||||
|
|
||||||
var configSaveData = new JObject();
|
var configSaveData = new JObject();
|
||||||
|
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
// If Windows use .net http
|
// If Windows use .net http
|
||||||
var response = await client.PostAsync(LoginUrl, content);
|
var response = await client.PostAsync(LoginUrl, content);
|
||||||
@@ -156,7 +156,7 @@ namespace Jackett.Indexers
|
|||||||
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
var episodeSearchUrl = SearchUrl + HttpUtility.UrlEncode(searchString);
|
||||||
|
|
||||||
string results;
|
string results;
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
results = await client.GetStringAsync(episodeSearchUrl, retries);
|
results = await client.GetStringAsync(episodeSearchUrl, retries);
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ namespace Jackett.Indexers
|
|||||||
|
|
||||||
public async Task<byte[]> Download(Uri link)
|
public async Task<byte[]> Download(Uri link)
|
||||||
{
|
{
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
return await client.GetByteArrayAsync(link);
|
return await client.GetByteArrayAsync(link);
|
||||||
}
|
}
|
||||||
|
@@ -12,11 +12,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class Rarbg : IndexerInterface
|
public class Rarbg : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -13,11 +13,11 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
class SceneAccess : IndexerInterface
|
class SceneAccess : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
@@ -87,7 +87,7 @@ namespace Jackett.Indexers
|
|||||||
string responseContent;
|
string responseContent;
|
||||||
var configSaveData = new JObject();
|
var configSaveData = new JObject();
|
||||||
|
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
// If Windows use .net http
|
// If Windows use .net http
|
||||||
var response = await client.PostAsync(LoginUrl, content);
|
var response = await client.PostAsync(LoginUrl, content);
|
||||||
@@ -139,7 +139,7 @@ namespace Jackett.Indexers
|
|||||||
var searchUrl = string.Format(SearchUrl, searchSection, searchCategory, searchString);
|
var searchUrl = string.Format(SearchUrl, searchSection, searchCategory, searchString);
|
||||||
|
|
||||||
string results;
|
string results;
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
results = await client.GetStringAsync(searchUrl);
|
results = await client.GetStringAsync(searchUrl);
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ namespace Jackett.Indexers
|
|||||||
|
|
||||||
public async Task<byte[]> Download(Uri link)
|
public async Task<byte[]> Download(Uri link)
|
||||||
{
|
{
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
return await client.GetByteArrayAsync(link);
|
return await client.GetByteArrayAsync(link);
|
||||||
}
|
}
|
||||||
|
@@ -15,11 +15,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class SceneTime : IndexerInterface
|
public class SceneTime : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -14,11 +14,11 @@ using System.Xml;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class ShowRSS : IndexerInterface
|
public class ShowRSS : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, Newtonsoft.Json.Linq.JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -12,11 +12,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class Strike : IndexerInterface
|
public class Strike : IIndexer
|
||||||
{
|
{
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -14,12 +14,12 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class T411 : IndexerInterface
|
public class T411 : IIndexer
|
||||||
{
|
{
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -15,12 +15,12 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class ThePirateBay : IndexerInterface
|
public class ThePirateBay : IIndexer
|
||||||
{
|
{
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName { get { return "The Pirate Bay"; } }
|
public string DisplayName { get { return "The Pirate Bay"; } }
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ namespace Jackett.Indexers
|
|||||||
|
|
||||||
string results;
|
string results;
|
||||||
|
|
||||||
if (WebServer.IsWindows)
|
if (Engine.IsWindows)
|
||||||
{
|
{
|
||||||
results = await client.GetStringAsync(episodeSearchUrl);
|
results = await client.GetStringAsync(episodeSearchUrl);
|
||||||
}
|
}
|
||||||
|
@@ -15,11 +15,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class TorrentDay : IndexerInterface
|
public class TorrentDay : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -15,11 +15,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class TorrentLeech : IndexerInterface
|
public class TorrentLeech : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -15,11 +15,11 @@ using System.Web;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class TorrentShack : IndexerInterface
|
public class TorrentShack : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -13,11 +13,11 @@ using System.Xml;
|
|||||||
|
|
||||||
namespace Jackett.Indexers
|
namespace Jackett.Indexers
|
||||||
{
|
{
|
||||||
public class Torrentz : IndexerInterface
|
public class Torrentz : IIndexer
|
||||||
{
|
{
|
||||||
public event Action<IndexerInterface, JToken> OnSaveConfigurationRequested;
|
public event Action<IIndexer, JToken> OnSaveConfigurationRequested;
|
||||||
|
|
||||||
public event Action<IndexerInterface, string, Exception> OnResultParsingError;
|
public event Action<IIndexer, string, Exception> OnResultParsingError;
|
||||||
|
|
||||||
public string DisplayName
|
public string DisplayName
|
||||||
{
|
{
|
||||||
|
@@ -102,6 +102,7 @@
|
|||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Configuration.Install" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Drawing" />
|
<Reference Include="System.Drawing" />
|
||||||
<Reference Include="System.Net.Http" />
|
<Reference Include="System.Net.Http" />
|
||||||
@@ -110,6 +111,7 @@
|
|||||||
<Private>True</Private>
|
<Private>True</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System.Net.Http.WebRequest" />
|
<Reference Include="System.Net.Http.WebRequest" />
|
||||||
|
<Reference Include="System.ServiceProcess" />
|
||||||
<Reference Include="System.Web" />
|
<Reference Include="System.Web" />
|
||||||
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
||||||
@@ -133,8 +135,15 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Controllers\APIController.cs" />
|
||||||
|
<Compile Include="Controllers\DownloadController.cs" />
|
||||||
|
<Compile Include="Engine.cs" />
|
||||||
<Compile Include="Indexers\BaseIndexer.cs" />
|
<Compile Include="Indexers\BaseIndexer.cs" />
|
||||||
<Compile Include="Models\ApiKey.cs" />
|
<Compile Include="Models\Config\ServerConfig.cs" />
|
||||||
|
<Compile Include="Services\ProcessService.cs" />
|
||||||
|
<Compile Include="Services\SerializeService.cs" />
|
||||||
|
<Compile Include="Services\ServiceConfigService.cs" />
|
||||||
|
<Compile Include="Services\SpinService.cs" />
|
||||||
<Compile Include="Utils\BrowserUtil.cs" />
|
<Compile Include="Utils\BrowserUtil.cs" />
|
||||||
<Compile Include="Models\CachedResult.cs" />
|
<Compile Include="Models\CachedResult.cs" />
|
||||||
<Compile Include="Models\ChannelInfo.cs" />
|
<Compile Include="Models\ChannelInfo.cs" />
|
||||||
@@ -147,8 +156,7 @@
|
|||||||
<Compile Include="DataUrl.cs" />
|
<Compile Include="DataUrl.cs" />
|
||||||
<Compile Include="ExceptionWithConfigData.cs" />
|
<Compile Include="ExceptionWithConfigData.cs" />
|
||||||
<Compile Include="HttpClientExtensions.cs" />
|
<Compile Include="HttpClientExtensions.cs" />
|
||||||
<Compile Include="IndexerInterface.cs" />
|
<Compile Include="Indexers\IIndexer.cs" />
|
||||||
<Compile Include="IndexerManager.cs" />
|
|
||||||
<Compile Include="Indexers\BeyondHD.cs" />
|
<Compile Include="Indexers\BeyondHD.cs" />
|
||||||
<Compile Include="Indexers\BitHdtv.cs" />
|
<Compile Include="Indexers\BitHdtv.cs" />
|
||||||
<Compile Include="Indexers\BitMeTV.cs" />
|
<Compile Include="Indexers\BitMeTV.cs" />
|
||||||
@@ -180,22 +188,20 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Models\ReleaseInfo.cs" />
|
<Compile Include="Models\ReleaseInfo.cs" />
|
||||||
<Compile Include="Models\ResultPage.cs" />
|
<Compile Include="Models\ResultPage.cs" />
|
||||||
<Compile Include="Server.cs" />
|
<Compile Include="Services\ServerService.cs" />
|
||||||
<Compile Include="Utils\ServerUtil.cs" />
|
<Compile Include="Utils\ServerUtil.cs" />
|
||||||
<Compile Include="Services\ConfigurationService.cs" />
|
<Compile Include="Services\ConfigurationService.cs" />
|
||||||
<Compile Include="Services\IndexerManagerService.cs" />
|
<Compile Include="Services\IndexerManagerService.cs" />
|
||||||
<Compile Include="Services\SonarApi.cs" />
|
<Compile Include="Services\SonarApi.cs" />
|
||||||
<Compile Include="Startup.cs" />
|
<Compile Include="Startup.cs" />
|
||||||
<Compile Include="Models\TorznabQuery.cs" />
|
<Compile Include="Models\TorznabQuery.cs" />
|
||||||
<Compile Include="WebApi.cs" />
|
|
||||||
<Compile Include="CurlHelper.cs" />
|
<Compile Include="CurlHelper.cs" />
|
||||||
<Compile Include="Indexers\AlphaRatio.cs" />
|
<Compile Include="Indexers\AlphaRatio.cs" />
|
||||||
<Compile Include="WebServer.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="App.config" />
|
<None Include="App.config" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
<None Include="WebContent\fonts\glyphicons-halflings-regular.woff">
|
<None Include="Content\fonts\glyphicons-halflings-regular.woff">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -209,116 +215,116 @@
|
|||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="WebContent\custom.css">
|
<Content Include="Content\custom.css">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\custom.js">
|
<Content Include="Content\custom.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\animebytes.png">
|
<Content Include="Content\logos\animebytes.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\beyondhd.png">
|
<Content Include="Content\logos\beyondhd.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\frenchtorrentdb.png">
|
<Content Include="Content\logos\frenchtorrentdb.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\hdtorrents.png">
|
<Content Include="Content\logos\hdtorrents.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\sceneaccess.png">
|
<Content Include="Content\logos\sceneaccess.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\scenetime.png">
|
<Content Include="Content\logos\scenetime.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\showrss.png">
|
<Content Include="Content\logos\showrss.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\t411.png">
|
<Content Include="Content\logos\t411.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\torrentday.png">
|
<Content Include="Content\logos\torrentday.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\torrentshack.png">
|
<Content Include="Content\logos\torrentshack.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\animate.css">
|
<Content Include="Content\animate.css">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\binding_dark.png">
|
<Content Include="Content\binding_dark.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\bootstrap-notify.js">
|
<Content Include="Content\bootstrap-notify.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\congruent_outline.png">
|
<Content Include="Content\congruent_outline.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\crissXcross.png">
|
<Content Include="Content\crissXcross.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\favicon.ico">
|
<Content Include="Content\favicon.ico">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\handlebars-v3.0.1.js">
|
<Content Include="Content\handlebars-v3.0.1.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\jacket_medium.png">
|
<Content Include="Content\jacket_medium.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\bithdtv.png">
|
<Content Include="Content\logos\bithdtv.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\bitmetv.png">
|
<Content Include="Content\logos\bitmetv.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\bootstrap\bootstrap.min.css">
|
<Content Include="Content\bootstrap\bootstrap.min.css">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\bootstrap\bootstrap.min.js">
|
<Content Include="Content\bootstrap\bootstrap.min.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\common.js">
|
<Content Include="Content\common.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\index.html">
|
<Content Include="Content\index.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\jquery-2.1.3.min.js">
|
<Content Include="Content\jquery-2.1.3.min.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\freshon.png">
|
<Content Include="Content\logos\freshon.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\iptorrents.png">
|
<Content Include="Content\logos\iptorrents.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\morethantv.png">
|
<Content Include="Content\logos\morethantv.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\rarbg.png">
|
<Content Include="Content\logos\rarbg.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\strike.png">
|
<Content Include="Content\logos\strike.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\thepiratebay.png">
|
<Content Include="Content\logos\thepiratebay.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\torrentleech.png">
|
<Content Include="Content\logos\torrentleech.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\logos\torrentz.png">
|
<Content Include="Content\logos\torrentz.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="WebContent\setup_indexer.html">
|
<Content Include="Content\setup_indexer.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="Resources\validator_reply.xml" />
|
<Content Include="Resources\validator_reply.xml" />
|
||||||
<Content Include="WebContent\logos\alpharatio.png">
|
<Content Include="Content\logos\alpharatio.png">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@@ -5,6 +5,7 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Autofac.Integration.WebApi;
|
using Autofac.Integration.WebApi;
|
||||||
|
using Jackett.Indexers;
|
||||||
|
|
||||||
namespace Jackett
|
namespace Jackett
|
||||||
{
|
{
|
||||||
@@ -14,15 +15,15 @@ namespace Jackett
|
|||||||
{
|
{
|
||||||
// Just register everything!
|
// Just register everything!
|
||||||
var thisAssembly = typeof(JackettModule).Assembly;
|
var thisAssembly = typeof(JackettModule).Assembly;
|
||||||
builder.RegisterAssemblyTypes(thisAssembly).AsImplementedInterfaces().SingleInstance();
|
builder.RegisterAssemblyTypes(thisAssembly).Except<IIndexer>().AsImplementedInterfaces().SingleInstance();
|
||||||
builder.RegisterApiControllers(thisAssembly).InstancePerRequest();
|
builder.RegisterApiControllers(thisAssembly).InstancePerRequest();
|
||||||
|
|
||||||
// Register indexers
|
// Register indexers
|
||||||
foreach(var indexer in thisAssembly.GetTypes()
|
foreach(var indexer in thisAssembly.GetTypes()
|
||||||
.Where(p => typeof(IndexerInterface).IsAssignableFrom(p) && !p.IsInterface)
|
.Where(p => typeof(IIndexer).IsAssignableFrom(p) && !p.IsInterface)
|
||||||
.ToArray())
|
.ToArray())
|
||||||
{
|
{
|
||||||
builder.RegisterType(indexer).Named<IndexerInterface>(indexer.Name.ToLowerInvariant()).SingleInstance();
|
builder.RegisterType(indexer).Named<IIndexer>(indexer.Name.ToLowerInvariant()).SingleInstance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
45
src/Jackett/Models/Config/ServerConfig.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jackett.Models.Config
|
||||||
|
{
|
||||||
|
public class ServerConfig
|
||||||
|
{
|
||||||
|
public ServerConfig()
|
||||||
|
{
|
||||||
|
Port = 9117;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Port { get; set; }
|
||||||
|
public bool AllowExternal { get; set; }
|
||||||
|
public string APIKey { get; set; }
|
||||||
|
|
||||||
|
public string GetListenAddress(bool? external = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (external == null)
|
||||||
|
{
|
||||||
|
external = AllowExternal;
|
||||||
|
}
|
||||||
|
return "http://" + (external.Value ? "*" : "localhost") + ":" + Port + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateApi()
|
||||||
|
{
|
||||||
|
var chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
var randBytes = new byte[32];
|
||||||
|
var rngCsp = new RNGCryptoServiceProvider();
|
||||||
|
rngCsp.GetBytes(randBytes);
|
||||||
|
var key = "";
|
||||||
|
foreach (var b in randBytes)
|
||||||
|
{
|
||||||
|
key += chars[b % chars.Length];
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -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 Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -19,22 +20,115 @@ namespace Jackett.Services
|
|||||||
string GetAppDataFolder();
|
string GetAppDataFolder();
|
||||||
JObject ReadServerSettingsFile();
|
JObject ReadServerSettingsFile();
|
||||||
string GetSonarrConfigFile();
|
string GetSonarrConfigFile();
|
||||||
|
T GetConfig<T>();
|
||||||
|
void SaveConfig<T>(T config);
|
||||||
|
string ApplicationFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ConfigurationService: IConfigurationService
|
public class ConfigurationService: IConfigurationService
|
||||||
{
|
{
|
||||||
|
private ISerializeService serializeService;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
public ConfigurationService(ISerializeService s, Logger l)
|
||||||
|
{
|
||||||
|
serializeService = s;
|
||||||
|
logger = l;
|
||||||
|
CreateOrMigrateSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateOrMigrateSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(GetAppDataFolder()))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(GetAppDataFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug("App config/log directory: " + GetAppDataFolder());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("Could not create settings directory. " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string oldDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jackett");
|
||||||
|
if (Directory.Exists(oldDir))
|
||||||
|
{
|
||||||
|
foreach (var file in Directory.GetFiles(oldDir, "*", SearchOption.AllDirectories))
|
||||||
|
{
|
||||||
|
var path = file.Replace(oldDir, "");
|
||||||
|
var destFolder = GetAppDataFolder()+ path;
|
||||||
|
if (!Directory.Exists(Path.GetDirectoryName(destFolder)))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(destFolder));
|
||||||
|
}
|
||||||
|
File.Move(file, destFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error("ERROR could not migrate settings directory " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetConfig<T>()
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
var fullPath = Path.Combine(GetAppDataFolder(), type.Name + ".json");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!File.Exists(fullPath))
|
||||||
|
{
|
||||||
|
logger.Debug("Config file does not exist: " + fullPath);
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
return serializeService.DeSerialise<T>(File.ReadAllText(fullPath));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
logger.Error(e, "Error reading config file " + fullPath);
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveConfig<T>(T config)
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
var fullPath = Path.Combine(GetAppDataFolder(), type.Name + ".json");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var json = serializeService.Serialise(config);
|
||||||
|
if (!Directory.Exists(GetAppDataFolder()))
|
||||||
|
Directory.CreateDirectory(GetAppDataFolder());
|
||||||
|
File.WriteAllText(fullPath, json);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error(e, "Error reading config file " + fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ApplicationFolder()
|
||||||
|
{
|
||||||
|
return Path.GetDirectoryName(Application.ExecutablePath);
|
||||||
|
}
|
||||||
|
|
||||||
public string GetContentFolder()
|
public string GetContentFolder()
|
||||||
{
|
{
|
||||||
var baseDir = Path.GetDirectoryName(Application.ExecutablePath);
|
|
||||||
// If we are debugging we can use the non copied content.
|
// If we are debugging we can use the non copied content.
|
||||||
if (Debugger.IsAttached)
|
var dir = Path.Combine(ApplicationFolder(), "Content");
|
||||||
|
if (!Directory.Exists(dir))
|
||||||
{
|
{
|
||||||
return Path.Combine(baseDir, "..\\..\\..\\Jackett\\WebContent");
|
dir = Path.Combine(ApplicationFolder(), "..\\..\\..\\Jackett\\Content");
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Path.Combine(baseDir, "WebContent");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetVersion()
|
public string GetVersion()
|
||||||
@@ -44,7 +138,16 @@ namespace Jackett.Services
|
|||||||
|
|
||||||
public string GetAppDataFolder()
|
public string GetAppDataFolder()
|
||||||
{
|
{
|
||||||
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Jackett"); ;
|
return GetAppDataFolderStatic();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is needed for the logger prior to ioc setup.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetAppDataFolderStatic()
|
||||||
|
{
|
||||||
|
return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Jackett");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetIndexerConfigDir()
|
public string GetIndexerConfigDir()
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Autofac;
|
using Autofac;
|
||||||
|
using Jackett.Indexers;
|
||||||
using Jackett.Models;
|
using Jackett.Models;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
@@ -15,9 +16,10 @@ namespace Jackett.Services
|
|||||||
{
|
{
|
||||||
void TestIndexer(string name);
|
void TestIndexer(string name);
|
||||||
void DeleteIndexer(string name);
|
void DeleteIndexer(string name);
|
||||||
IndexerInterface GetIndexer(string name);
|
IIndexer GetIndexer(string name);
|
||||||
IEnumerable<IndexerInterface> GetAllIndexers();
|
IEnumerable<IIndexer> GetAllIndexers();
|
||||||
void SaveConfig(IndexerInterface indexer, JToken obj);
|
void SaveConfig(IIndexer indexer, JToken obj);
|
||||||
|
void InitIndexers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IndexerManagerService : IIndexerManagerService
|
public class IndexerManagerService : IIndexerManagerService
|
||||||
@@ -33,14 +35,35 @@ namespace Jackett.Services
|
|||||||
logger = l;
|
logger = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexerInterface GetIndexer(string name)
|
public void InitIndexers()
|
||||||
{
|
{
|
||||||
return container.ResolveNamed<IndexerInterface>(name.ToLowerInvariant());
|
// Load the existing config for each indexer
|
||||||
|
foreach (var indexer in GetAllIndexers())
|
||||||
|
{
|
||||||
|
var configFilePath = GetIndexerConfigFilePath(indexer);
|
||||||
|
if (File.Exists(configFilePath))
|
||||||
|
{
|
||||||
|
var jsonString = JObject.Parse(File.ReadAllText(configFilePath));
|
||||||
|
indexer.LoadFromSavedConfiguration(jsonString);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IndexerInterface> GetAllIndexers()
|
public IIndexer GetIndexer(string name)
|
||||||
{
|
{
|
||||||
return container.Resolve<IEnumerable<IndexerInterface>>().OrderBy(_ => _.DisplayName);
|
var indexer = GetAllIndexers().Where(i => string.Equals(i.DisplayName, name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
|
||||||
|
if (indexer == null)
|
||||||
|
{
|
||||||
|
logger.Error("Request for unknown indexer: " + name);
|
||||||
|
throw new Exception("Unknown indexer: " + name);
|
||||||
|
}
|
||||||
|
return indexer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<IIndexer> GetAllIndexers()
|
||||||
|
{
|
||||||
|
|
||||||
|
return container.Resolve<IEnumerable<IIndexer>>().OrderBy(_ => _.DisplayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void TestIndexer(string name)
|
public async void TestIndexer(string name)
|
||||||
@@ -62,12 +85,12 @@ namespace Jackett.Services
|
|||||||
//LoadMissingIndexers();
|
//LoadMissingIndexers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetIndexerConfigFilePath(IndexerInterface indexer)
|
private string GetIndexerConfigFilePath(IIndexer indexer)
|
||||||
{
|
{
|
||||||
return Path.Combine(configService.GetIndexerConfigDir(), indexer.GetType().Name.ToLower() + ".json");
|
return Path.Combine(configService.GetIndexerConfigDir(), indexer.GetType().Name.ToLower() + ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveConfig(IndexerInterface indexer, JToken obj)
|
public void SaveConfig(IIndexer indexer, JToken obj)
|
||||||
{
|
{
|
||||||
var configFilePath = GetIndexerConfigFilePath(indexer);
|
var configFilePath = GetIndexerConfigFilePath(indexer);
|
||||||
if (!Directory.Exists(configService.GetIndexerConfigDir()))
|
if (!Directory.Exists(configService.GetIndexerConfigDir()))
|
||||||
|
64
src/Jackett/Services/ProcessService.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jackett.Services
|
||||||
|
{
|
||||||
|
public interface IProcessService
|
||||||
|
{
|
||||||
|
void StartProcessAndLog(string exe, string args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProcessService : IProcessService
|
||||||
|
{
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
public ProcessService(Logger l)
|
||||||
|
{
|
||||||
|
logger = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartProcessAndLog(string exe, string args)
|
||||||
|
{
|
||||||
|
var startInfo = new ProcessStartInfo()
|
||||||
|
{
|
||||||
|
CreateNoWindow = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
FileName = exe,
|
||||||
|
Arguments = args,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardInput = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var proc = Process.Start(startInfo);
|
||||||
|
proc.OutputDataReceived += proc_OutputDataReceived;
|
||||||
|
proc.ErrorDataReceived += proc_ErrorDataReceived;
|
||||||
|
proc.BeginErrorReadLine();
|
||||||
|
proc.BeginOutputReadLine();
|
||||||
|
proc.WaitForExit();
|
||||||
|
proc.OutputDataReceived -= proc_OutputDataReceived;
|
||||||
|
proc.ErrorDataReceived -= proc_ErrorDataReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(e.Data))
|
||||||
|
{
|
||||||
|
logger.Error(e.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(e.Data))
|
||||||
|
{
|
||||||
|
logger.Debug(e.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
src/Jackett/Services/SerializeService.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jackett.Services
|
||||||
|
{
|
||||||
|
public interface ISerializeService
|
||||||
|
{
|
||||||
|
string Serialise(object obj);
|
||||||
|
T DeSerialise<T>(string json);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SerializeService : ISerializeService
|
||||||
|
{
|
||||||
|
public string Serialise(object obj)
|
||||||
|
{
|
||||||
|
return JsonConvert.SerializeObject(obj,Formatting.Indented);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T DeSerialise<T>(string json)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return default(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
136
src/Jackett/Services/ServerService.cs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
using Autofac;
|
||||||
|
using Jackett.Models.Config;
|
||||||
|
using Jackett.Services;
|
||||||
|
using Microsoft.Owin.Hosting;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Config;
|
||||||
|
using NLog.Targets;
|
||||||
|
using NLog.Windows.Forms;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jackett.Services
|
||||||
|
{
|
||||||
|
public interface IServerService
|
||||||
|
{
|
||||||
|
void Start();
|
||||||
|
void Stop();
|
||||||
|
void ReserveUrls(bool doInstall = true);
|
||||||
|
ServerConfig Config { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ServerService : IServerService
|
||||||
|
{
|
||||||
|
private ServerConfig config;
|
||||||
|
|
||||||
|
private IDisposable _server = null;
|
||||||
|
|
||||||
|
private IIndexerManagerService indexerService;
|
||||||
|
private IProcessService processService;
|
||||||
|
private ISerializeService serializeService;
|
||||||
|
private IConfigurationService configService;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
public ServerService(IIndexerManagerService i, IProcessService p, ISerializeService s, IConfigurationService c, Logger l)
|
||||||
|
{
|
||||||
|
indexerService = i;
|
||||||
|
processService = p;
|
||||||
|
serializeService = s;
|
||||||
|
configService = c;
|
||||||
|
logger = l;
|
||||||
|
|
||||||
|
LoadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerConfig Config
|
||||||
|
{
|
||||||
|
get { return config; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadConfig()
|
||||||
|
{
|
||||||
|
// Load config
|
||||||
|
config = configService.GetConfig<ServerConfig>();
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
config = new ServerConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(config.APIKey))
|
||||||
|
{
|
||||||
|
// Check for legacy key config
|
||||||
|
var apiKeyFile = Path.Combine(configService.GetAppDataFolder(), "api_key.txt");
|
||||||
|
if (File.Exists(apiKeyFile))
|
||||||
|
{
|
||||||
|
config.APIKey = File.ReadAllText(apiKeyFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for legacy settings
|
||||||
|
|
||||||
|
var path = Path.Combine(configService.GetAppDataFolder(), "config.json"); ;
|
||||||
|
var jsonReply = new JObject();
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
jsonReply = JObject.Parse(File.ReadAllText(path));
|
||||||
|
config.Port = (int)jsonReply["port"];
|
||||||
|
config.AllowExternal = (bool)jsonReply["public"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(config.APIKey))
|
||||||
|
{
|
||||||
|
config.APIKey = config.GenerateApi();
|
||||||
|
}
|
||||||
|
|
||||||
|
configService.SaveConfig<ServerConfig>(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
// Allow all SSL.. sucks I know but mono on linux is having problems without it..
|
||||||
|
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
|
||||||
|
|
||||||
|
// Load indexers
|
||||||
|
indexerService.InitIndexers();
|
||||||
|
|
||||||
|
// Start the server
|
||||||
|
logger.Debug("Starting web server at " + config.GetListenAddress());
|
||||||
|
_server = WebApp.Start<Startup>(url: config.GetListenAddress());
|
||||||
|
logger.Debug("Web server started");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReserveUrls(bool doInstall = true)
|
||||||
|
{
|
||||||
|
logger.Debug("Unreserving Urls");
|
||||||
|
RunNetSh(string.Format("http delete urlacl {0}", config.GetListenAddress(false)));
|
||||||
|
RunNetSh(string.Format("http delete urlacl {0}", config.GetListenAddress(true)));
|
||||||
|
if (doInstall)
|
||||||
|
{
|
||||||
|
logger.Debug("Reserving Urls");
|
||||||
|
RunNetSh(string.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", config.GetListenAddress()));
|
||||||
|
logger.Debug("Urls reserved");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunNetSh(string args)
|
||||||
|
{
|
||||||
|
processService.StartProcessAndLog("netsh.exe", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
if (_server != null)
|
||||||
|
{
|
||||||
|
_server.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
114
src/Jackett/Services/ServiceConfigService.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using NLog;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.Configuration.Install;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ServiceProcess;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jackett.Services
|
||||||
|
{
|
||||||
|
public interface IServiceConfigService
|
||||||
|
{
|
||||||
|
void Install();
|
||||||
|
void Uninstall();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServiceConfigService : IServiceConfigService
|
||||||
|
{
|
||||||
|
private const string NAME = "Jackett";
|
||||||
|
private const string DESCRIPTION = "Additional indexers for Sonarr";
|
||||||
|
private const string SERVICEEXE = "JackettService.exe";
|
||||||
|
|
||||||
|
private IConfigurationService configService;
|
||||||
|
private Logger logger;
|
||||||
|
|
||||||
|
public ServiceConfigService(IConfigurationService c, Logger l)
|
||||||
|
{
|
||||||
|
configService = c;
|
||||||
|
logger = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Exists()
|
||||||
|
{
|
||||||
|
return GetService(NAME) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceController GetService(string serviceName)
|
||||||
|
{
|
||||||
|
return ServiceController.GetServices().FirstOrDefault(c => String.Equals(c.ServiceName, serviceName, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Install()
|
||||||
|
{
|
||||||
|
if (Exists())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var installer = new ServiceProcessInstaller
|
||||||
|
{
|
||||||
|
Account = ServiceAccount.NetworkService
|
||||||
|
};
|
||||||
|
|
||||||
|
var serviceInstaller = new ServiceInstaller();
|
||||||
|
|
||||||
|
var exePath = Path.Combine(configService.ApplicationFolder(), SERVICEEXE);
|
||||||
|
if (!File.Exists(exePath) && Debugger.IsAttached)
|
||||||
|
{
|
||||||
|
exePath = Path.Combine(configService.ApplicationFolder(), "..\\..\\..\\Jackett.Service\\bin\\Debug", SERVICEEXE);
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] cmdline = { @"/assemblypath=" + exePath};
|
||||||
|
|
||||||
|
var context = new InstallContext("jackettservice_install.log", cmdline);
|
||||||
|
serviceInstaller.Context = context;
|
||||||
|
serviceInstaller.DisplayName = NAME;
|
||||||
|
serviceInstaller.ServiceName = NAME;
|
||||||
|
serviceInstaller.Description = DESCRIPTION;
|
||||||
|
serviceInstaller.StartType = ServiceStartMode.Automatic;
|
||||||
|
serviceInstaller.Parent = installer;
|
||||||
|
|
||||||
|
serviceInstaller.Install(new ListDictionary());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Uninstall()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
var serviceInstaller = new ServiceInstaller();
|
||||||
|
|
||||||
|
var context = new InstallContext("jackettservice_uninstall.log", null);
|
||||||
|
serviceInstaller.Context = context;
|
||||||
|
serviceInstaller.ServiceName = NAME;
|
||||||
|
serviceInstaller.Uninstall(null);
|
||||||
|
|
||||||
|
logger.Info("The service was uninstalled.");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop()
|
||||||
|
{
|
||||||
|
var service = GetService(NAME);
|
||||||
|
if (service.Status != ServiceControllerStatus.Stopped)
|
||||||
|
{
|
||||||
|
service.Stop();
|
||||||
|
service.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(60));
|
||||||
|
|
||||||
|
service.Refresh();
|
||||||
|
if (service.Status == ServiceControllerStatus.Stopped)
|
||||||
|
logger.Info("Service stopped.");
|
||||||
|
else
|
||||||
|
logger.Error("Failed to stop the service");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
logger.Warn("The service was already stopped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -19,6 +19,7 @@ namespace Jackett
|
|||||||
Task TestConnection();
|
Task TestConnection();
|
||||||
SonarrApi.ConfigurationSonarr GetConfiguration();
|
SonarrApi.ConfigurationSonarr GetConfiguration();
|
||||||
Task ApplyConfiguration(JToken configJson);
|
Task ApplyConfiguration(JToken configJson);
|
||||||
|
Task<string[]> GetShowTitle(int rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SonarrApi: ISonarrApi
|
public class SonarrApi: ISonarrApi
|
||||||
@@ -62,6 +63,8 @@ namespace Jackett
|
|||||||
|
|
||||||
public SonarrApi(IConfigurationService c)
|
public SonarrApi(IConfigurationService c)
|
||||||
{
|
{
|
||||||
|
configService = c;
|
||||||
|
|
||||||
LoadSettings();
|
LoadSettings();
|
||||||
|
|
||||||
cookies = new CookieContainer();
|
cookies = new CookieContainer();
|
||||||
@@ -74,8 +77,6 @@ namespace Jackett
|
|||||||
client = new HttpClient(handler);
|
client = new HttpClient(handler);
|
||||||
|
|
||||||
IdNameMappings = new ConcurrentDictionary<int, string[]>();
|
IdNameMappings = new ConcurrentDictionary<int, string[]>();
|
||||||
|
|
||||||
configService = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task ReloadNameMappings(string host, int port, string apiKey)
|
async Task ReloadNameMappings(string host, int port, string apiKey)
|
||||||
|
27
src/Jackett/Services/SpinService.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jackett.Services
|
||||||
|
{
|
||||||
|
public interface IRunTimeService
|
||||||
|
{
|
||||||
|
void Spin();
|
||||||
|
}
|
||||||
|
|
||||||
|
class RunTimeService : IRunTimeService
|
||||||
|
{
|
||||||
|
private bool isRunning = true;
|
||||||
|
|
||||||
|
public void Spin()
|
||||||
|
{
|
||||||
|
while (isRunning)
|
||||||
|
{
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -23,30 +23,35 @@ namespace Jackett
|
|||||||
{
|
{
|
||||||
// Configure Web API for self-host.
|
// Configure Web API for self-host.
|
||||||
var config = new HttpConfiguration();
|
var config = new HttpConfiguration();
|
||||||
|
config.DependencyResolver = new AutofacWebApiDependencyResolver(Engine.GetContainer());
|
||||||
config.DependencyResolver = new AutofacWebApiDependencyResolver(Server.GetContainer());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Enable attribute based routing
|
|
||||||
// http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
|
|
||||||
config.MapHttpAttributeRoutes();
|
config.MapHttpAttributeRoutes();
|
||||||
|
|
||||||
config.Routes.MapHttpRoute(
|
config.Routes.MapHttpRoute(
|
||||||
name: "Content",
|
name: "Admin",
|
||||||
routeTemplate: "{controller}/{action}",
|
routeTemplate: "admin/{action}",
|
||||||
defaults: new { controller = "Admin"}
|
defaults: new { controller = "Admin" }
|
||||||
|
);
|
||||||
|
|
||||||
|
config.Routes.MapHttpRoute(
|
||||||
|
name: "api",
|
||||||
|
routeTemplate: "api/{indexerName}",
|
||||||
|
defaults: new { controller = "API", action = "Call" }
|
||||||
|
);
|
||||||
|
|
||||||
|
config.Routes.MapHttpRoute(
|
||||||
|
name: "download",
|
||||||
|
routeTemplate: "api/{indexerName}/download/{path}/download.torrent",
|
||||||
|
defaults: new { controller = "Download", action = "Download" }
|
||||||
);
|
);
|
||||||
|
|
||||||
appBuilder.UseFileServer(new FileServerOptions
|
appBuilder.UseFileServer(new FileServerOptions
|
||||||
{
|
{
|
||||||
RequestPath = new PathString(string.Empty),
|
RequestPath = new PathString(string.Empty),
|
||||||
FileSystem = new PhysicalFileSystem(Server.GetContainer().Resolve<IConfigurationService>().GetContentFolder()),
|
FileSystem = new PhysicalFileSystem(Engine.ConfigService.GetContentFolder()),
|
||||||
EnableDirectoryBrowsing = true,
|
EnableDirectoryBrowsing = true,
|
||||||
});
|
});
|
||||||
|
|
||||||
appBuilder.UseWebApi(config);
|
appBuilder.UseWebApi(config);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}*/
|
|
||||||
}
|
|