1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2024-10-30 07:22:35 +01:00

Newznab providers can be configured by the end user.

This commit is contained in:
Mark McDowall 2011-11-13 12:51:15 -08:00
parent f3f2691b4d
commit 6c86f1dfdd
13 changed files with 503 additions and 18 deletions

View File

@ -70,6 +70,7 @@ namespace NzbDrone.Core
Kernel.Bind<IndexerBase>().To<NzbMatrix>(); Kernel.Bind<IndexerBase>().To<NzbMatrix>();
Kernel.Bind<IndexerBase>().To<NzbsRUs>(); Kernel.Bind<IndexerBase>().To<NzbsRUs>();
Kernel.Bind<IndexerBase>().To<Newzbin>(); Kernel.Bind<IndexerBase>().To<Newzbin>();
Kernel.Bind<IndexerBase>().To<Newznab>();
var indexers = Kernel.GetAll<IndexerBase>(); var indexers = Kernel.GetAll<IndexerBase>();
Kernel.Get<IndexerProvider>().InitializeIndexers(indexers.ToList()); Kernel.Get<IndexerProvider>().InitializeIndexers(indexers.ToList());

View File

@ -0,0 +1,28 @@
using System;
using System.Data;
using Migrator.Framework;
namespace NzbDrone.Core.Datastore.Migrations
{
[Migration(20111112)]
public class Migration2011112 : Migration
{
public override void Up()
{
Database.AddTable("NewznabDefinitions", new[]
{
new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity),
new Column("Enable", DbType.Boolean, ColumnProperty.NotNull),
new Column("Name", DbType.String, ColumnProperty.Null),
new Column("Url", DbType.String, ColumnProperty.Null),
new Column("ApiKey", DbType.String, ColumnProperty.Null)
});
}
public override void Down()
{
throw new NotImplementedException();
}
}
}

View File

@ -197,6 +197,7 @@
<Compile Include="Datastore\MigrationLogger.cs" /> <Compile Include="Datastore\MigrationLogger.cs" />
<Compile Include="Datastore\MigrationsHelper.cs" /> <Compile Include="Datastore\MigrationsHelper.cs" />
<Compile Include="Datastore\CustomeMapper.cs" /> <Compile Include="Datastore\CustomeMapper.cs" />
<Compile Include="Datastore\Migrations\Migration20111112.cs" />
<Compile Include="Datastore\Migrations\Migration20111011.cs" /> <Compile Include="Datastore\Migrations\Migration20111011.cs" />
<Compile Include="Datastore\Migrations\Migration20110909.cs" /> <Compile Include="Datastore\Migrations\Migration20110909.cs" />
<Compile Include="Datastore\Migrations\Migration20110726.cs" /> <Compile Include="Datastore\Migrations\Migration20110726.cs" />
@ -231,6 +232,8 @@
<Compile Include="Model\Xbmc\IconType.cs" /> <Compile Include="Model\Xbmc\IconType.cs" />
<Compile Include="Providers\Converting\AtomicParsleyProvider.cs" /> <Compile Include="Providers\Converting\AtomicParsleyProvider.cs" />
<Compile Include="Providers\Converting\HandbrakeProvider.cs" /> <Compile Include="Providers\Converting\HandbrakeProvider.cs" />
<Compile Include="Providers\Indexer\Newznab.cs" />
<Compile Include="Providers\NewznzbProvider.cs" />
<Compile Include="Providers\ExternalNotification\Prowl.cs" /> <Compile Include="Providers\ExternalNotification\Prowl.cs" />
<Compile Include="Providers\ProwlProvider.cs" /> <Compile Include="Providers\ProwlProvider.cs" />
<Compile Include="Providers\Core\ConfigFileProvider.cs" /> <Compile Include="Providers\Core\ConfigFileProvider.cs" />
@ -282,6 +285,7 @@
<Compile Include="Providers\Jobs\UpdateInfoJob.cs" /> <Compile Include="Providers\Jobs\UpdateInfoJob.cs" />
<Compile Include="Providers\SceneMappingProvider.cs" /> <Compile Include="Providers\SceneMappingProvider.cs" />
<Compile Include="Providers\Xbmc\EventClientProvider.cs" /> <Compile Include="Providers\Xbmc\EventClientProvider.cs" />
<Compile Include="Repository\NewznabDefinition.cs" />
<Compile Include="Repository\ExternalNotificationDefinition.cs" /> <Compile Include="Repository\ExternalNotificationDefinition.cs" />
<Compile Include="Repository\JobDefinition.cs" /> <Compile Include="Repository\JobDefinition.cs" />
<Compile Include="Repository\IndexerDefinition.cs" /> <Compile Include="Repository\IndexerDefinition.cs" />

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.ServiceModel.Syndication;
using System.Text.RegularExpressions;
using Ninject;
using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Search;
using NzbDrone.Core.Providers.Core;
namespace NzbDrone.Core.Providers.Indexer
{
public class Newznab : IndexerBase
{
private readonly NewznabProvider _newznabProvider;
[Inject]
public Newznab(HttpProvider httpProvider, ConfigProvider configProvider, NewznabProvider newznabProvider)
: base(httpProvider, configProvider)
{
_newznabProvider = newznabProvider;
}
protected override string[] Urls
{
get { return GetUrls(); }
}
public override string Name
{
get { return "Newznab"; }
}
protected override string NzbDownloadUrl(SyndicationItem item)
{
return item.Id;
}
protected override IList<string> GetSearchUrls(SearchModel searchModel)
{
var searchUrls = new List<String>();
foreach (var url in Urls)
{
if (searchModel.SearchType == SearchType.EpisodeSearch)
{
searchUrls.Add(String.Format("{0}&q={1}&season{2}&ep{3}", url,
searchModel.SeriesTitle, searchModel.SeasonNumber, searchModel.EpisodeNumber));
}
if (searchModel.SearchType == SearchType.SeasonSearch)
{
searchUrls.Add(String.Format("{0}&q={1}&season{2}", url, searchModel.SeriesTitle, searchModel.SeasonNumber));
}
}
return searchUrls;
}
protected override EpisodeParseResult CustomParser(SyndicationItem item, EpisodeParseResult currentResult)
{
if (currentResult != null)
{
var sizeString = Regex.Match(item.Summary.Text, @">\d+\.\d{1,2} \w{2}</a>", RegexOptions.IgnoreCase).Value;
currentResult.Size = Parser.GetReportSize(sizeString);
}
return currentResult;
}
private string[] GetUrls()
{
var urls = new List<string>();
var newznzbIndexers = _newznabProvider.Enabled();
foreach(var newznabDefinition in newznzbIndexers)
{
if (!String.IsNullOrWhiteSpace(newznabDefinition.ApiKey))
urls.Add(String.Format("{0}/api?t=tvsearch&cat=5030,5040&apikey={1}", newznabDefinition.Url,
newznabDefinition.ApiKey));
else
urls.Add(String.Format("{0}/api?t=tvsearch&cat=5030,5040", newznabDefinition.Url));
}
return urls.ToArray();
}
}
}

View File

@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Ninject;
using NLog;
using NzbDrone.Core.Providers.Indexer;
using NzbDrone.Core.Repository;
using PetaPoco;
namespace NzbDrone.Core.Providers
{
public class NewznabProvider
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly IDatabase _database;
[Inject]
public NewznabProvider(IDatabase database)
{
_database = database;
}
public NewznabProvider()
{
}
public virtual List<NewznabDefinition> Enabled()
{
return _database.Fetch<NewznabDefinition>("WHERE Enable = 1");
}
public virtual List<NewznabDefinition> All()
{
return _database.Fetch<NewznabDefinition>();
}
public virtual int Save(NewznabDefinition definition)
{
//Cleanup the URL
definition.Url = (new Uri(definition.Url).ParentUriString());
if (definition.Id == 0)
{
Logger.Debug("Adding Newznab definitions for {0}", definition.Name);
return Convert.ToInt32(_database.Insert(definition));
}
else
{
Logger.Debug("Updating Newznab definitions for {0}", definition.Name);
return _database.Update(definition);
}
}
public virtual void SaveAll(IEnumerable<NewznabDefinition> definitions)
{
var definitionsList = definitions.ToList();
//Cleanup the URL for each definition
definitionsList.ForEach(p => p.Url = (new Uri(p.Url).ParentUriString()));
_database.UpdateMany(definitionsList);
}
public virtual void InitializeNewznabIndexers(IList<NewznabDefinition> indexers)
{
Logger.Info("Initializing Newznab indexers. Count {0}", indexers.Count);
var currentIndexers = All();
foreach (var feedProvider in indexers)
{
NewznabDefinition indexerLocal = feedProvider;
if (!currentIndexers.Exists(c => c.Name == indexerLocal.Name))
{
var settings = new NewznabDefinition
{
Enable = false,
Name = indexerLocal.Name,
Url = indexerLocal.Url,
ApiKey = indexerLocal.ApiKey
};
Save(settings);
}
}
}
public virtual void Delete(int id)
{
_database.Delete<NewznabDefinition>(id);
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using PetaPoco;
namespace NzbDrone.Core.Repository
{
[TableName("NewznabDefinitions")]
[PrimaryKey("Id", autoIncrement = true)]
public class NewznabDefinition
{
public int Id { get; set; }
public Boolean Enable { get; set; }
public String Name { get; set; }
public String Url { get; set; }
public String ApiKey { get; set; }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

View File

@ -0,0 +1,86 @@
#bottom
{
margin-top: -10px;
margin-left: 15px;
}
#addItem
{
text-decoration: none;
font-size:16px;
color: black;
font-weight:bold;
}
.titleText
{
font-size: 1.5em;
line-height: 1;
margin-bottom: 1em;
display:inline;
position: absolute;
top: -1px;
left: 2px;
padding-bottom: 0px;
white-space:nowrap;
}
.providerHeader
{
min-height: 23px;
position: relative;
}
.providerSection
{
float: left;
width: 270px;
margin: 2px;
border:solid 1px #CCCCCD;
display: inline-block;
overflow: hidden;
padding: 5px 5px 5px 5px;
}
.providerOptions label
{
margin-top: 10px;
margin-left: 7px;
margin-right: 25px;
float: left;
font-weight: bold;
width: 50px;
}
.providerOptions input, .providerOptions select
{
font-size:12px;
padding:4px 2px;
border:solid 1px #aacfe4;
width:170px;
margin-right: 2px;
}
.providerOptions select
{
width:176px;
}
.deleteProvider
{
position: absolute;
top: 0px;
right: 0px;
}
input[type="checkbox"]
{
height: 25px;
}
#newznabProviders
{
overflow: hidden;
margin-top: 5px;
margin-bottom: 10px;
}

View File

@ -30,17 +30,19 @@ namespace NzbDrone.Web.Controllers
private readonly QualityTypeProvider _qualityTypeProvider; private readonly QualityTypeProvider _qualityTypeProvider;
private readonly RootDirProvider _rootDirProvider; private readonly RootDirProvider _rootDirProvider;
private readonly ConfigFileProvider _configFileProvider; private readonly ConfigFileProvider _configFileProvider;
private readonly NewznabProvider _newznabProvider;
public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider, public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider,
QualityProvider qualityProvider, AutoConfigureProvider autoConfigureProvider, QualityProvider qualityProvider, AutoConfigureProvider autoConfigureProvider,
SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider, SeriesProvider seriesProvider, ExternalNotificationProvider externalNotificationProvider,
QualityTypeProvider qualityTypeProvider, RootDirProvider rootDirProvider, QualityTypeProvider qualityTypeProvider, RootDirProvider rootDirProvider,
ConfigFileProvider configFileProvider) ConfigFileProvider configFileProvider, NewznabProvider newznabProvider)
{ {
_externalNotificationProvider = externalNotificationProvider; _externalNotificationProvider = externalNotificationProvider;
_qualityTypeProvider = qualityTypeProvider; _qualityTypeProvider = qualityTypeProvider;
_rootDirProvider = rootDirProvider; _rootDirProvider = rootDirProvider;
_configFileProvider = configFileProvider; _configFileProvider = configFileProvider;
_newznabProvider = newznabProvider;
_configProvider = configProvider; _configProvider = configProvider;
_indexerProvider = indexerProvider; _indexerProvider = indexerProvider;
_qualityProvider = qualityProvider; _qualityProvider = qualityProvider;
@ -86,7 +88,10 @@ namespace NzbDrone.Web.Controllers
NzbsOrgEnabled = _indexerProvider.GetSettings(typeof(NzbsOrg)).Enable, NzbsOrgEnabled = _indexerProvider.GetSettings(typeof(NzbsOrg)).Enable,
NzbMatrixEnabled = _indexerProvider.GetSettings(typeof(NzbMatrix)).Enable, NzbMatrixEnabled = _indexerProvider.GetSettings(typeof(NzbMatrix)).Enable,
NzbsRUsEnabled = _indexerProvider.GetSettings(typeof(NzbsRUs)).Enable, NzbsRUsEnabled = _indexerProvider.GetSettings(typeof(NzbsRUs)).Enable,
NewzbinEnabled = _indexerProvider.GetSettings(typeof(Newzbin)).Enable NewzbinEnabled = _indexerProvider.GetSettings(typeof(Newzbin)).Enable,
NewznabEnabled = _indexerProvider.GetSettings(typeof(Newzbin)).Enable,
NewznabDefinitions = _newznabProvider.All(),
}); });
} }
@ -274,21 +279,6 @@ namespace NzbDrone.Web.Controllers
return PartialView("QualityProfileItem", profile); return PartialView("QualityProfileItem", profile);
} }
public ActionResult SubMenu()
{
return PartialView();
}
public QualityModel GetUpdatedProfileList()
{
var profiles = _qualityProvider.All().ToList();
var defaultQualityQualityProfileId =
Convert.ToInt32(_configProvider.GetValue("DefaultQualityProfile", profiles[0].QualityProfileId));
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
return new QualityModel { DefaultQualityProfileId = defaultQualityQualityProfileId, QualityProfileSelectList = selectList };
}
public JsonResult DeleteQualityProfile(int profileId) public JsonResult DeleteQualityProfile(int profileId)
{ {
try try
@ -307,6 +297,59 @@ namespace NzbDrone.Web.Controllers
return new JsonResult { Data = "ok" }; return new JsonResult { Data = "ok" };
} }
public ViewResult AddNewznabProvider()
{
var newznab = new NewznabDefinition
{
Enable = false,
Name = "Newznab Provider"
};
var id = _newznabProvider.Save(newznab);
newznab.Id = id;
ViewData["ProviderId"] = id;
return View("NewznabProvider", newznab);
}
public ActionResult GetNewznabProviderView(NewznabDefinition provider)
{
ViewData["ProviderId"] = provider.Id;
return PartialView("NewznabProvider", provider);
}
public JsonResult DeleteNewznabProvider(int providerId)
{
try
{
_newznabProvider.Delete(providerId);
}
catch (Exception)
{
return new JsonResult { Data = "failed" };
}
return new JsonResult { Data = "ok" };
}
public ActionResult SubMenu()
{
return PartialView();
}
public QualityModel GetUpdatedProfileList()
{
var profiles = _qualityProvider.All().ToList();
var defaultQualityQualityProfileId =
Convert.ToInt32(_configProvider.GetValue("DefaultQualityProfile", profiles[0].QualityProfileId));
var selectList = new SelectList(profiles, "QualityProfileId", "Name");
return new QualityModel { DefaultQualityProfileId = defaultQualityQualityProfileId, QualityProfileSelectList = selectList };
}
public JsonResult AutoConfigureSab() public JsonResult AutoConfigureSab()
{ {
try try
@ -354,6 +397,9 @@ namespace NzbDrone.Web.Controllers
_configProvider.NewzbinUsername = data.NewzbinUsername; _configProvider.NewzbinUsername = data.NewzbinUsername;
_configProvider.NewzbinPassword = data.NewzbinPassword; _configProvider.NewzbinPassword = data.NewzbinPassword;
if (data.NewznabDefinitions != null)
_newznabProvider.SaveAll(data.NewznabDefinitions);
return GetSuccessResult(); return GetSuccessResult();
} }

View File

@ -71,5 +71,11 @@ namespace NzbDrone.Web.Models
[DisplayName("Newzbin")] [DisplayName("Newzbin")]
[Description("Enable downloading episodes from Newzbin")] [Description("Enable downloading episodes from Newzbin")]
public bool NewzbinEnabled { get; set; } public bool NewzbinEnabled { get; set; }
[DisplayName("Newznab")]
[Description("Enable downloading episodes from Newznab Providers")]
public bool NewznabEnabled { get; set; }
public List<NewznabDefinition> NewznabDefinitions { get; set; }
} }
} }

View File

@ -327,6 +327,7 @@
<Content Include="Content\2011.2.712\Windows7\slider-v.gif" /> <Content Include="Content\2011.2.712\Windows7\slider-v.gif" />
<Content Include="Content\2011.2.712\Windows7\sprite-vertical.png" /> <Content Include="Content\2011.2.712\Windows7\sprite-vertical.png" />
<Content Include="Content\2011.2.712\Windows7\sprite.png" /> <Content Include="Content\2011.2.712\Windows7\sprite.png" />
<Content Include="Content\IndexerSettings.css" />
<Content Include="Content\Slider.css" /> <Content Include="Content\Slider.css" />
<Content Include="Content\Grid.css" /> <Content Include="Content\Grid.css" />
<Content Include="Content\Images\close.png" /> <Content Include="Content\Images\close.png" />
@ -961,6 +962,9 @@
<ItemGroup> <ItemGroup>
<Content Include="Views\Settings\Prowl.cshtml" /> <Content Include="Views\Settings\Prowl.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="Views\Settings\NewznabProvider.cshtml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" /> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.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.

View File

@ -3,6 +3,7 @@
@section HeaderContent{ @section HeaderContent{
<link rel="stylesheet" type="text/css" href="../../Content/Settings.css" /> <link rel="stylesheet" type="text/css" href="../../Content/Settings.css" />
<link rel="stylesheet" type="text/css" href="../../Content/IndexerSettings.css" />
<style> <style>
.indexerPanel .indexerPanel
@ -121,6 +122,27 @@
@Html.TextBoxFor(m => m.NewzbinPassword, new { @class = "inputClass" }) @Html.TextBoxFor(m => m.NewzbinPassword, new { @class = "inputClass" })
</div> </div>
</text>); </text>);
indexerItem.Add()
.Text("Newznzb")
.ImageUrl("~/Content/Images/Indexers/Newznab.png")
.Content(@<text>
<div class="indexerPanel clearfix">
<label class="labelClass">Enable
<span class="small">@Html.DescriptionFor(m => m.NewznabEnabled)</span>
</label>
@Html.CheckBoxFor(m => m.NewznabEnabled, new { @class = "inputClass checkClass" })
</div>
<p></p>
<a id="addItem" href="@Url.Action("AddNewznabProvider", "Settings")">
<img src="../../Content/Images/Plus.png" alt="Add Newznab Provider" width="20px" height="20px" />
Add Newznab Provider</a>
<div id="newznabProviders">
@foreach (var provider in Model.NewznabDefinitions)
{
Html.RenderAction("GetNewznabProviderView", provider);
}
</div>
</text>);
}).Render(); }).Render();
} }
</div> </div>
@ -133,4 +155,51 @@
@section Scripts{ @section Scripts{
<script src="../../Scripts/NzbDrone/settingsForm.js" type="text/javascript"></script> <script src="../../Scripts/NzbDrone/settingsForm.js" type="text/javascript"></script>
<script type="text/javascript">
$("#addItem").live('click', function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) {
$("#newznabProviders").append(html);
}
});
return false;
});
var deleteNewznabProviderUrl = '@Url.Action("DeleteNewznabProvider", "Settings")';
function deleteProvider(id) {
$.ajax({
type: "POST",
url: deleteNewznabProviderUrl,
data: jQuery.param({ providerId: id }),
error: function (req, status, error) {
alert("Sorry! We could not delete your Provider at this time. " + error);
},
success: function (data, textStatus, jqXHR) {
if (data == "ok") {
$("#provider_" + id).remove();
}
else {
alert(data);
}
}
});
}
function getProviderId(obj) {
var parentProviderSection = $(obj).parents('.providerSection');
return parentProviderSection.children('.newznabProviderId').val();
}
$(".providerName_textbox").live('keyup', function () {
var value = $(this).val();
var profileId = getProviderId(this);
$("#title_" + profileId).text(value);
}).keyup();
</script>
} }

View File

@ -0,0 +1,38 @@
@model NzbDrone.Core.Repository.NewznabDefinition
@using System.Collections
@using NzbDrone.Core.Repository.Quality
@using NzbDrone.Web.Helpers
@{
Layout = null;
}
@using (Html.BeginCollectionItem("NewznabDefinitions"))
{
var idClean = ViewData.TemplateInfo.HtmlFieldPrefix.Replace('[', '_').Replace(']', '_');
<div class="providerSection" id="provider_@(Model.Id)">
<div class="providerHeader">
<span class="titleText" id="title_@(Model.Id)">
@Model.Name
</span>
<a href="#" id="@Model.Id" class="deleteProvider" onclick="deleteProvider('@(Model.Id)'); return false;">
<img src="../../Content/Images/X.png" alt="Delete" width="22px" height="22px" /></a>
</div>
<div class="providerOptions">
@Html.Label("Enabled")
@Html.CheckBoxFor(m => m.Enable)
@Html.LabelFor(x => x.Name)
@Html.TextBoxFor(x => x.Name, new {@class = "providerName_textbox"})
@Html.LabelFor(x => x.Url)
@Html.TextBoxFor(m => m.Url)
@Html.LabelFor(x => x.ApiKey)
@Html.TextBoxFor(m => m.ApiKey)
</div>
@Html.HiddenFor(x => x.Id, new {@class = "newznabProviderId"})
@Html.Hidden("cleanId", idClean, new {@class = "cleanId"})
</div>
}