mirror of
https://github.com/Jackett/Jackett.git
synced 2025-09-17 17:34:09 +02:00
Added Sonarr API
This commit is contained in:
@@ -73,19 +73,40 @@ namespace Jackett
|
|||||||
public string ID { get { return Name.Replace(" ", "").ToLower(); } }
|
public string ID { get { return Name.Replace(" ", "").ToLower(); } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DisplayItem : StringItem
|
||||||
|
{
|
||||||
|
public DisplayItem(string value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
ItemType = ItemType.DisplayInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class StringItem : Item
|
public class StringItem : Item
|
||||||
{
|
{
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
|
public StringItem()
|
||||||
|
{
|
||||||
|
ItemType = ConfigurationData.ItemType.InputString;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BoolItem : Item
|
public class BoolItem : Item
|
||||||
{
|
{
|
||||||
public bool Value { get; set; }
|
public bool Value { get; set; }
|
||||||
|
public BoolItem()
|
||||||
|
{
|
||||||
|
ItemType = ConfigurationData.ItemType.InputBool;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ImageItem : Item
|
public class ImageItem : Item
|
||||||
{
|
{
|
||||||
public byte[] Value { get; set; }
|
public byte[] Value { get; set; }
|
||||||
|
public ImageItem()
|
||||||
|
{
|
||||||
|
ItemType = ConfigurationData.ItemType.DisplayImage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Item[] GetItems();
|
public abstract Item[] GetItems();
|
||||||
|
@@ -13,8 +13,8 @@ namespace Jackett
|
|||||||
|
|
||||||
public ConfigurationDataBasicLogin()
|
public ConfigurationDataBasicLogin()
|
||||||
{
|
{
|
||||||
Username = new StringItem { Name = "Username", ItemType = ItemType.InputString };
|
Username = new StringItem { Name = "Username" };
|
||||||
Password = new StringItem { Name = "Password", ItemType = ItemType.InputString };
|
Password = new StringItem { Name = "Password" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Item[] GetItems()
|
public override Item[] GetItems()
|
||||||
|
@@ -24,10 +24,10 @@ namespace Jackett
|
|||||||
|
|
||||||
public BmtvConfig()
|
public BmtvConfig()
|
||||||
{
|
{
|
||||||
Username = new StringItem { Name = "Username", ItemType = ItemType.InputString };
|
Username = new StringItem { Name = "Username" };
|
||||||
Password = new StringItem { Name = "Password", ItemType = ItemType.InputString };
|
Password = new StringItem { Name = "Password" };
|
||||||
CaptchaImage = new ImageItem { Name = "Captcha Image", ItemType = ItemType.DisplayImage };
|
CaptchaImage = new ImageItem { Name = "Captcha Image" };
|
||||||
CaptchaText = new StringItem { Name = "Captcha Text", ItemType = ItemType.InputString };
|
CaptchaText = new StringItem { Name = "Captcha Text" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Item[] GetItems()
|
public override Item[] GetItems()
|
||||||
|
@@ -22,7 +22,7 @@ namespace Jackett.Indexers
|
|||||||
|
|
||||||
public ThePirateBayConfig()
|
public ThePirateBayConfig()
|
||||||
{
|
{
|
||||||
Url = new StringItem { Name = "Url", ItemType = ItemType.InputString, Value = "https://thepiratebay.se/" };
|
Url = new StringItem { Name = "Url", Value = "https://thepiratebay.se/" };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Item[] GetItems()
|
public override Item[] GetItems()
|
||||||
|
@@ -90,6 +90,7 @@
|
|||||||
<Compile Include="ReleaseInfo.cs" />
|
<Compile Include="ReleaseInfo.cs" />
|
||||||
<Compile Include="ResultPage.cs" />
|
<Compile Include="ResultPage.cs" />
|
||||||
<Compile Include="Server.cs" />
|
<Compile Include="Server.cs" />
|
||||||
|
<Compile Include="SonarApi.cs" />
|
||||||
<Compile Include="TorznabQuery.cs" />
|
<Compile Include="TorznabQuery.cs" />
|
||||||
<Compile Include="TVRage.cs" />
|
<Compile Include="TVRage.cs" />
|
||||||
<Compile Include="WebApi.cs" />
|
<Compile Include="WebApi.cs" />
|
||||||
|
@@ -16,13 +16,15 @@ namespace Jackett
|
|||||||
HttpListener listener;
|
HttpListener listener;
|
||||||
IndexerManager indexerManager;
|
IndexerManager indexerManager;
|
||||||
WebApi webApi;
|
WebApi webApi;
|
||||||
|
SonarrApi sonarrApi;
|
||||||
|
|
||||||
public Server()
|
public Server()
|
||||||
{
|
{
|
||||||
LoadApiKey();
|
LoadApiKey();
|
||||||
|
|
||||||
indexerManager = new IndexerManager();
|
indexerManager = new IndexerManager();
|
||||||
webApi = new WebApi(indexerManager);
|
sonarrApi = new SonarrApi();
|
||||||
|
webApi = new WebApi(indexerManager, sonarrApi);
|
||||||
|
|
||||||
listener = new HttpListener();
|
listener = new HttpListener();
|
||||||
listener.Prefixes.Add("http://*:9117/");
|
listener.Prefixes.Add("http://*:9117/");
|
||||||
|
122
src/Jackett/SonarApi.cs
Normal file
122
src/Jackett/SonarApi.cs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Jackett
|
||||||
|
{
|
||||||
|
public class SonarrApi
|
||||||
|
{
|
||||||
|
public class ConfigurationSonarr : ConfigurationData
|
||||||
|
{
|
||||||
|
public StringItem Host { get; private set; }
|
||||||
|
public StringItem Port { get; private set; }
|
||||||
|
public StringItem ApiKey { get; private set; }
|
||||||
|
|
||||||
|
DisplayItem ApiInfo;
|
||||||
|
|
||||||
|
public ConfigurationSonarr()
|
||||||
|
{
|
||||||
|
Host = new StringItem { Name = "Host", Value = "http://localhost" };
|
||||||
|
Port = new StringItem { Name = "Port", Value = "8989" };
|
||||||
|
ApiKey = new StringItem { Name = "API Key" };
|
||||||
|
ApiInfo = new DisplayItem("API Key can be found in Sonarr > Settings > General > Security") { Name = "API Info" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Item[] GetItems()
|
||||||
|
{
|
||||||
|
return new Item[] { Host, Port, ApiKey, ApiInfo };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static string SonarrConfigFile = Path.Combine(Program.AppConfigDirectory, "sonarr_api.json");
|
||||||
|
|
||||||
|
string Host;
|
||||||
|
int Port;
|
||||||
|
string ApiKey;
|
||||||
|
|
||||||
|
CookieContainer cookies;
|
||||||
|
HttpClientHandler handler;
|
||||||
|
HttpClient client;
|
||||||
|
|
||||||
|
public SonarrApi()
|
||||||
|
{
|
||||||
|
LoadSettings();
|
||||||
|
|
||||||
|
cookies = new CookieContainer();
|
||||||
|
handler = new HttpClientHandler
|
||||||
|
{
|
||||||
|
CookieContainer = cookies,
|
||||||
|
AllowAutoRedirect = true,
|
||||||
|
UseCookies = true,
|
||||||
|
};
|
||||||
|
client = new HttpClient(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(SonarrConfigFile))
|
||||||
|
{
|
||||||
|
var json = JObject.Parse(File.ReadAllText(SonarrConfigFile));
|
||||||
|
Host = (string)json["host"];
|
||||||
|
Port = (int)json["port"];
|
||||||
|
ApiKey = (string)json["api_key"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveSettings()
|
||||||
|
{
|
||||||
|
JObject json = new JObject();
|
||||||
|
json["host"] = Host;
|
||||||
|
json["port"] = Port;
|
||||||
|
json["api_key"] = ApiKey;
|
||||||
|
File.WriteAllText(SonarrConfigFile, json.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationSonarr GetConfiguration()
|
||||||
|
{
|
||||||
|
var config = new ConfigurationSonarr();
|
||||||
|
if (ApiKey != null)
|
||||||
|
{
|
||||||
|
config.Host.Value = Host;
|
||||||
|
config.Port.Value = Port.ToString();
|
||||||
|
config.ApiKey.Value = ApiKey;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ApplyConfiguration(JToken configJson)
|
||||||
|
{
|
||||||
|
var config = new ConfigurationSonarr();
|
||||||
|
config.LoadValuesFromJson(configJson);
|
||||||
|
await TestConnection(config.Host.Value, int.Parse(config.Port.Value), config.ApiKey.Value);
|
||||||
|
Host = "http://" + new Uri(config.Host.Value).Host;
|
||||||
|
Port = int.Parse(config.Port.Value);
|
||||||
|
ApiKey = config.ApiKey.Value;
|
||||||
|
SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task TestConnection()
|
||||||
|
{
|
||||||
|
await TestConnection(Host, Port, ApiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task TestConnection(string host, int port, string apiKey)
|
||||||
|
{
|
||||||
|
Uri hostUri = new Uri(host);
|
||||||
|
var queryUrl = string.Format("http://{0}:{1}/api/series?apikey={2}", hostUri.Host, port, apiKey);
|
||||||
|
var response = await client.GetStringAsync(queryUrl);
|
||||||
|
var json = JArray.Parse(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -22,7 +22,10 @@ namespace Jackett
|
|||||||
ConfigureIndexer,
|
ConfigureIndexer,
|
||||||
GetIndexers,
|
GetIndexers,
|
||||||
TestIndexer,
|
TestIndexer,
|
||||||
DeleteIndexer
|
DeleteIndexer,
|
||||||
|
GetSonarrConfig,
|
||||||
|
ApplySonarrConfig,
|
||||||
|
TestSonarr
|
||||||
}
|
}
|
||||||
static Dictionary<string, WebApiMethod> WebApiMethods = new Dictionary<string, WebApiMethod>
|
static Dictionary<string, WebApiMethod> WebApiMethods = new Dictionary<string, WebApiMethod>
|
||||||
{
|
{
|
||||||
@@ -30,14 +33,19 @@ namespace Jackett
|
|||||||
{ "configure_indexer", WebApiMethod.ConfigureIndexer },
|
{ "configure_indexer", WebApiMethod.ConfigureIndexer },
|
||||||
{ "get_indexers", WebApiMethod.GetIndexers },
|
{ "get_indexers", WebApiMethod.GetIndexers },
|
||||||
{ "test_indexer", WebApiMethod.TestIndexer },
|
{ "test_indexer", WebApiMethod.TestIndexer },
|
||||||
{ "delete_indexer", WebApiMethod.DeleteIndexer }
|
{ "delete_indexer", WebApiMethod.DeleteIndexer },
|
||||||
|
{ "get_sonarr_config", WebApiMethod.GetSonarrConfig },
|
||||||
|
{ "apply_sonarr_config", WebApiMethod.ApplySonarrConfig },
|
||||||
|
{ "test_sonarr", WebApiMethod.TestSonarr }
|
||||||
};
|
};
|
||||||
|
|
||||||
IndexerManager indexerManager;
|
IndexerManager indexerManager;
|
||||||
|
SonarrApi sonarrApi;
|
||||||
|
|
||||||
public WebApi(IndexerManager indexerManager)
|
public WebApi(IndexerManager indexerManager, SonarrApi sonarrApi)
|
||||||
{
|
{
|
||||||
this.indexerManager = indexerManager;
|
this.indexerManager = indexerManager;
|
||||||
|
this.sonarrApi = sonarrApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HandleRequest(HttpListenerContext context)
|
public bool HandleRequest(HttpListenerContext context)
|
||||||
@@ -110,6 +118,15 @@ namespace Jackett
|
|||||||
case WebApiMethod.DeleteIndexer:
|
case WebApiMethod.DeleteIndexer:
|
||||||
handlerTask = HandleDeleteIndexer;
|
handlerTask = HandleDeleteIndexer;
|
||||||
break;
|
break;
|
||||||
|
case WebApiMethod.GetSonarrConfig:
|
||||||
|
handlerTask = HandleGetSonarrConfig;
|
||||||
|
break;
|
||||||
|
case WebApiMethod.ApplySonarrConfig:
|
||||||
|
handlerTask = HandleApplySonarrConfig;
|
||||||
|
break;
|
||||||
|
case WebApiMethod.TestSonarr:
|
||||||
|
handlerTask = HandleTestSonarr;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
handlerTask = HandleInvalidApiMethod;
|
handlerTask = HandleInvalidApiMethod;
|
||||||
break;
|
break;
|
||||||
@@ -125,15 +142,61 @@ namespace Jackett
|
|||||||
context.Response.OutputStream.Close();
|
context.Response.OutputStream.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
Task<JToken> HandleInvalidApiMethod(HttpListenerContext context)
|
||||||
{
|
{
|
||||||
return Task<JToken>.Run(() =>
|
JToken jsonReply = new JObject();
|
||||||
{
|
jsonReply["result"] = "error";
|
||||||
JToken jsonReply = new JObject();
|
jsonReply["error"] = "Invalid API method";
|
||||||
jsonReply["result"] = "error";
|
return Task.FromResult<JToken>(jsonReply);
|
||||||
jsonReply["error"] = "Invalid API method";
|
|
||||||
return jsonReply;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<JToken> HandleConfigForm(HttpListenerContext context)
|
async Task<JToken> HandleConfigForm(HttpListenerContext context)
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
#page {
|
#page {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
max-width: 800px;
|
max-width: 900px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -35,26 +35,15 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#api-key-input {
|
|
||||||
width: 300px;
|
|
||||||
display: inline-block;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
#api-key-header {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
box-shadow: 1px 1px 5px 2px #cdcdcd;
|
box-shadow: 1px 1px 5px 2px #cdcdcd;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
width: 220px;
|
width: 260px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 30px;
|
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
margin-bottom: 30px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unconfigured-indexer {
|
.unconfigured-indexer {
|
||||||
@@ -62,11 +51,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.indexer {
|
.indexer {
|
||||||
height: 265px;
|
height: 230px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#add-indexer {
|
#add-indexer {
|
||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indexer-logo {
|
.indexer-logo {
|
||||||
@@ -87,13 +77,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.indexer-buttons > .btn {
|
.indexer-buttons > .btn {
|
||||||
width: 90px;
|
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.indexer-buttons > .btn:nth-child(even) {
|
|
||||||
margin-left: 5px;
|
.indexer-button-test {
|
||||||
}
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
.indexer-add-content {
|
.indexer-add-content {
|
||||||
color: gray;
|
color: gray;
|
||||||
@@ -161,18 +151,67 @@
|
|||||||
#setup-indexer-go {
|
#setup-indexer-go {
|
||||||
width: 70px;
|
width: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border-top-color: #cdcdcd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area {
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area > * {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area > p {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-header {
|
||||||
|
font-size: 18px;
|
||||||
|
width: 140px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-right {
|
||||||
|
width: 300px;
|
||||||
|
display: inline-block;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sonarr-warning {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="page">
|
<div id="page">
|
||||||
|
<h3><span>need a logo..</span></h3>
|
||||||
|
|
||||||
<div id="api-key">
|
<hr />
|
||||||
<span>need a logo..</span>
|
|
||||||
<span id="api-key-header">API Key: </span>
|
<div class="input-area">
|
||||||
<input id="api-key-input" class="form-control" type="text" value="" placeholder="API Key" readonly="">
|
<span class="input-header">Sonarr API Host: </span>
|
||||||
<span>(Same key works for all indexers)</span>
|
<input id="sonarr-host" class="form-control input-right" type="text" readonly />
|
||||||
|
<button id="sonarr-settings" class="btn btn-primary btn-sm">
|
||||||
|
Settings <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button id="sonarr-test" class="btn btn-warning btn-sm">
|
||||||
|
Test <span class="glyphicon glyphicon-screenshot" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<p id="sonarr-warning" class="alert alert-danger" role="alert">
|
||||||
|
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||||
|
Sonarr API must be configured
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="input-area">
|
||||||
|
<span class="input-header">Jackett API Key: </span>
|
||||||
|
<input id="api-key-input" class="form-control input-right" type="text" value="" placeholder="API Key" readonly="">
|
||||||
|
<p>Use this key when adding indexers to Sonarr. This key works for all indexers.</p>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
@@ -208,42 +247,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="setup-indexer-modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
<div id="modals"></div>
|
||||||
<div class="modal-dialog modal-sm">
|
|
||||||
<div class="modal-content">
|
<div id="templates">
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
<div class="config-setup-modal modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||||
<h4 class="modal-title">Setup indexer</h4>
|
<div class="modal-dialog">
|
||||||
</div>
|
<div class="modal-content">
|
||||||
<div class="modal-body">
|
<div class="modal-header">
|
||||||
<form id="indexer-setup-form"></form>
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
</div>
|
<h4 class="modal-title">{{title}}</h4>
|
||||||
<div class="modal-footer">
|
</div>
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
<div class="modal-body">
|
||||||
<button type="button" class="btn btn-primary" id="setup-indexer-go">Okay</button>
|
<form class="config-setup-form"></form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary setup-indexer-go">Okay</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="templates">
|
|
||||||
|
|
||||||
<div class="indexer card">
|
<div class="indexer card">
|
||||||
<div class="indexer-logo"><img src="logos/{{id}}.png" /></div>
|
<div class="indexer-logo"><img src="logos/{{id}}.png" /></div>
|
||||||
<div class="indexer-name"><h3>{{name}}</h3></div>
|
<div class="indexer-name"><h3>{{name}}</h3></div>
|
||||||
<div class="indexer-buttons">
|
<div class="indexer-buttons">
|
||||||
|
<button class="btn btn-primary btn-sm" data-id="{{id}}">
|
||||||
|
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
|
||||||
|
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
|
<a class="btn btn-info btn-sm" target="_blank" href="{{site_link}}">
|
||||||
Visit <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>
|
||||||
<button class="btn btn-danger btn-sm indexer-button-delete" data-id="{{id}}">
|
|
||||||
Delete <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary btn-sm" data-id="{{id}}">
|
|
||||||
Configure <span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="indexer-host">
|
<div class="indexer-host">
|
||||||
<b>Torznab Host:</b>
|
<b>Torznab Host:</b>
|
||||||
@@ -266,7 +307,6 @@
|
|||||||
<div class="setup-item-value">{{{value_element}}}</div>
|
<div class="setup-item-value">{{{value_element}}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="setup-item-header" data-type="indexer" data-name="{{name}}" data-indexer="{{indexer}}">{{name}}</h4>
|
|
||||||
<input class="setup-item-inputstring form-control" type="text" value="{{{value}}}"></input>
|
<input class="setup-item-inputstring form-control" type="text" value="{{{value}}}"></input>
|
||||||
<div class="setup-item-checkbox">
|
<div class="setup-item-checkbox">
|
||||||
{{#if value}}
|
{{#if value}}
|
||||||
@@ -276,7 +316,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<img class="setup-item-displayimage" src="{{{value}}}" />
|
<img class="setup-item-displayimage" src="{{{value}}}" />
|
||||||
<span class="setup-item-displayinfo">{{{value}}}</span>
|
<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>
|
||||||
|
|
||||||
@@ -284,6 +324,90 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
|
||||||
|
reloadIndexers();
|
||||||
|
loadSonarrInfo();
|
||||||
|
|
||||||
|
function loadSonarrInfo() {
|
||||||
|
getSonarrConfig(function (data) {
|
||||||
|
$("#sonarr-host").val("");
|
||||||
|
var host, port, apiKey;
|
||||||
|
for (var i = 0; i < data.config.length; i++) {
|
||||||
|
if (data.config[i].id == "host")
|
||||||
|
host = data.config[i].value;
|
||||||
|
if (data.config[i].id == "port")
|
||||||
|
port = data.config[i].value;
|
||||||
|
if (data.config[i].id == "apikey")
|
||||||
|
apiKey = data.config[i].value;
|
||||||
|
}
|
||||||
|
if (!apiKey)
|
||||||
|
$("#sonarr-warning").show();
|
||||||
|
else {
|
||||||
|
$("#sonarr-warning").hide();
|
||||||
|
$("#sonarr-host").val(host + ":" + port);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSonarrConfig(callback) {
|
||||||
|
var jqxhr = $.get("get_sonarr_config", function (data) {
|
||||||
|
callback(data);
|
||||||
|
}).fail(function () {
|
||||||
|
doNotify("Error loading Sonarr API configuration, request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#sonarr-test").click(function () {
|
||||||
|
var jqxhr = $.get("get_indexers", function (data) {
|
||||||
|
if (data.result == "error")
|
||||||
|
doNotify("Test failed for Sonarr API\n" + data.error, "danger", "glyphicon glyphicon-alert");
|
||||||
|
else
|
||||||
|
doNotify("Test successful for Sonarr API", "success", "glyphicon glyphicon-ok");
|
||||||
|
}).fail(function () {
|
||||||
|
doNotify("Error testing Sonarr, request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#sonarr-settings").click(function () {
|
||||||
|
getSonarrConfig(function (data) {
|
||||||
|
var config = data.config;
|
||||||
|
|
||||||
|
var configForm = newConfigModal("Sonarr API", config);
|
||||||
|
|
||||||
|
var $goButton = configForm.find(".setup-indexer-go");
|
||||||
|
$goButton.click(function () {
|
||||||
|
var data = getConfigModalJson(configForm);
|
||||||
|
|
||||||
|
var originalBtnText = $goButton.html();
|
||||||
|
$goButton.prop('disabled', true);
|
||||||
|
$goButton.html($('#templates > .spinner')[0].outerHTML);
|
||||||
|
|
||||||
|
var jqxhr = $.post("apply_sonarr_config", JSON.stringify(data), function (data) {
|
||||||
|
if (data.result == "error") {
|
||||||
|
if (data.config) {
|
||||||
|
populateSetupForm(data.indexer, data.name, data.config);
|
||||||
|
}
|
||||||
|
doNotify("Configuration failed: " + data.error, "danger", "glyphicon glyphicon-alert");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configForm.modal("hide");
|
||||||
|
loadSonarrInfo();
|
||||||
|
doNotify("Successfully configured Sonarr API", "success", "glyphicon glyphicon-ok");
|
||||||
|
}
|
||||||
|
}).fail(function () {
|
||||||
|
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||||
|
}).always(function () {
|
||||||
|
$goButton.html(originalBtnText);
|
||||||
|
$goButton.prop('disabled', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
configForm.modal("show");
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
function reloadIndexers() {
|
function reloadIndexers() {
|
||||||
$('#indexers').hide();
|
$('#indexers').hide();
|
||||||
$('#indexers > div.indexer').remove();
|
$('#indexers > div.indexer').remove();
|
||||||
@@ -372,7 +496,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
populateSetupForm(id, data.name, data.config);
|
populateSetupForm(id, data.name, data.config);
|
||||||
$("#setup-indexer-modal").modal("show");
|
|
||||||
}).fail(function () {
|
}).fail(function () {
|
||||||
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||||
});
|
});
|
||||||
@@ -380,17 +504,80 @@
|
|||||||
$("#select-indexer-modal").modal("hide");
|
$("#select-indexer-modal").modal("hide");
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateSetupForm(indexerId, name, config) {
|
|
||||||
$("#indexer-setup-form").empty();
|
function newConfigModal(title, config) {
|
||||||
var setupItemHeader = Handlebars.compile($("#templates > .setup-item-header")[0].outerHTML);
|
//config-setup-modal
|
||||||
$("#indexer-setup-form").append(setupItemHeader({ indexer: indexerId, name: name }));
|
var configTemplate = Handlebars.compile($("#templates > .config-setup-modal")[0].outerHTML);
|
||||||
|
var configForm = $(configTemplate({ title: title }));
|
||||||
|
|
||||||
|
$("#modals").append(configForm);
|
||||||
|
|
||||||
|
var $formItemContainer = configForm.find(".config-setup-form");
|
||||||
var setupItemTemplate = Handlebars.compile($("#templates > .setup-item")[0].outerHTML);
|
var setupItemTemplate = Handlebars.compile($("#templates > .setup-item")[0].outerHTML);
|
||||||
for (var i = 0; i < config.length; i++) {
|
for (var i = 0; i < config.length; i++) {
|
||||||
var item = config[i];
|
var item = config[i];
|
||||||
var setupValueTemplate = Handlebars.compile($("#templates > .setup-item-" + item.type)[0].outerHTML);
|
var setupValueTemplate = Handlebars.compile($("#templates > .setup-item-" + item.type)[0].outerHTML);
|
||||||
item.value_element = setupValueTemplate(item);
|
item.value_element = setupValueTemplate(item);
|
||||||
$("#indexer-setup-form").append(setupItemTemplate(item));
|
$formItemContainer.append(setupItemTemplate(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return configForm;
|
||||||
|
//modal.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConfigModalJson(configForm) {
|
||||||
|
var configJson = {};
|
||||||
|
configForm.find(".config-setup-form").children().each(function (i, el) {
|
||||||
|
$el = $(el);
|
||||||
|
var type = $el.data("type");
|
||||||
|
var id = $el.data("id");
|
||||||
|
switch (type) {
|
||||||
|
case "inputstring":
|
||||||
|
configJson[id] = $el.find(".setup-item-inputstring").val();
|
||||||
|
break;
|
||||||
|
case "inputbool":
|
||||||
|
configJson[id] = $el.find(".setup-item-checkbox").val();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return configJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
function populateSetupForm(indexerId, name, config) {
|
||||||
|
|
||||||
|
var configForm = newConfigModal(name, config);
|
||||||
|
|
||||||
|
var $goButton = configForm.find(".setup-indexer-go");
|
||||||
|
$goButton.click(function () {
|
||||||
|
var data = { indexer: indexerId, name: name };
|
||||||
|
data.config = getConfigModalJson(configForm);
|
||||||
|
|
||||||
|
var originalBtnText = $goButton.html();
|
||||||
|
$goButton.prop('disabled', true);
|
||||||
|
$goButton.html($('#templates > .spinner')[0].outerHTML);
|
||||||
|
|
||||||
|
var jqxhr = $.post("configure_indexer", JSON.stringify(data), function (data) {
|
||||||
|
if (data.result == "error") {
|
||||||
|
if (data.config) {
|
||||||
|
populateSetupForm(data.indexer, data.name, data.config);
|
||||||
|
}
|
||||||
|
doNotify("Configuration failed: " + data.error, "danger", "glyphicon glyphicon-alert");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configForm.modal("hide");
|
||||||
|
reloadIndexers();
|
||||||
|
doNotify("Successfully configured " + data.name, "success", "glyphicon glyphicon-ok");
|
||||||
|
}
|
||||||
|
}).fail(function () {
|
||||||
|
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
||||||
|
}).always(function () {
|
||||||
|
$goButton.html(originalBtnText);
|
||||||
|
$goButton.prop('disabled', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
configForm.modal("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveUrl(url) {
|
function resolveUrl(url) {
|
||||||
@@ -400,55 +587,7 @@
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#setup-indexer-go").click(function () {
|
|
||||||
var data = { config: {} };
|
|
||||||
$("#indexer-setup-form").children().each(function (i, el) {
|
|
||||||
$el = $(el);
|
|
||||||
var type = $el.data("type");
|
|
||||||
var id = $el.data("id");
|
|
||||||
switch (type) {
|
|
||||||
case "indexer":
|
|
||||||
data.indexer = $el.data("indexer");
|
|
||||||
data.name = $el.data("name")
|
|
||||||
return;
|
|
||||||
case "inputstring":
|
|
||||||
data.config[id] = $el.find(".setup-item-inputstring").val();
|
|
||||||
break;
|
|
||||||
case "inputbool":
|
|
||||||
data.config[id] = $el.find(".setup-item-checkbox").val();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
sendSetupData(data.indexer, data.name, data);
|
|
||||||
});
|
|
||||||
|
|
||||||
function sendSetupData(indexerId, name, data) {
|
|
||||||
var originalBtnText = $('#setup-indexer-go').html();
|
|
||||||
|
|
||||||
$('#setup-indexer-go').prop('disabled', true);
|
|
||||||
$('#setup-indexer-go').html($('#templates > .spinner')[0].outerHTML);
|
|
||||||
|
|
||||||
var jqxhr = $.post("configure_indexer", JSON.stringify(data), function (data) {
|
|
||||||
if (data.result == "error") {
|
|
||||||
if (data.config) {
|
|
||||||
populateSetupForm(indexerId, name, data.config);
|
|
||||||
}
|
|
||||||
doNotify("Configuration failed: " + data.error, "danger", "glyphicon glyphicon-alert");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$("#setup-indexer-modal").modal("hide");
|
|
||||||
reloadIndexers();
|
|
||||||
doNotify("Successfully configured " + name, "success", "glyphicon glyphicon-ok");
|
|
||||||
}
|
|
||||||
}).fail(function () {
|
|
||||||
doNotify("Request to Jackett server failed", "danger", "glyphicon glyphicon-alert");
|
|
||||||
}).always(function () {
|
|
||||||
$('#setup-indexer-go').html(originalBtnText);
|
|
||||||
$('#setup-indexer-go').prop('disabled', false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadIndexers();
|
|
||||||
|
|
||||||
function doNotify(message, type, icon) {
|
function doNotify(message, type, icon) {
|
||||||
$.notify({
|
$.notify({
|
||||||
|
Reference in New Issue
Block a user