Newznab providers can be configured by the end user.

pull/4/head
Mark McDowall 13 years ago
parent f3f2691b4d
commit 6c86f1dfdd

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

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

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

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

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

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

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

@ -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,29 +279,52 @@ namespace NzbDrone.Web.Controllers
return PartialView("QualityProfileItem", profile); return PartialView("QualityProfileItem", profile);
} }
public ActionResult SubMenu() public JsonResult DeleteQualityProfile(int profileId)
{ {
return PartialView(); try
{
if (_seriesProvider.GetAllSeries().Where(s => s.QualityProfileId == profileId).Count() != 0)
return new JsonResult { Data = "Unable to delete Profile, it is still in use." };
_qualityProvider.Delete(profileId);
}
catch (Exception)
{
return new JsonResult { Data = "failed" };
}
return new JsonResult { Data = "ok" };
} }
public QualityModel GetUpdatedProfileList() public ViewResult AddNewznabProvider()
{ {
var profiles = _qualityProvider.All().ToList(); var newznab = new NewznabDefinition
var defaultQualityQualityProfileId = {
Convert.ToInt32(_configProvider.GetValue("DefaultQualityProfile", profiles[0].QualityProfileId)); Enable = false,
var selectList = new SelectList(profiles, "QualityProfileId", "Name"); Name = "Newznab Provider"
};
return new QualityModel { DefaultQualityProfileId = defaultQualityQualityProfileId, QualityProfileSelectList = selectList }; var id = _newznabProvider.Save(newznab);
newznab.Id = id;
ViewData["ProviderId"] = id;
return View("NewznabProvider", newznab);
} }
public JsonResult DeleteQualityProfile(int profileId) public ActionResult GetNewznabProviderView(NewznabDefinition provider)
{
ViewData["ProviderId"] = provider.Id;
return PartialView("NewznabProvider", provider);
}
public JsonResult DeleteNewznabProvider(int providerId)
{ {
try try
{ {
if (_seriesProvider.GetAllSeries().Where(s => s.QualityProfileId == profileId).Count() != 0) _newznabProvider.Delete(providerId);
return new JsonResult { Data = "Unable to delete Profile, it is still in use." };
_qualityProvider.Delete(profileId);
} }
catch (Exception) catch (Exception)
@ -307,6 +335,21 @@ namespace NzbDrone.Web.Controllers
return new JsonResult { Data = "ok" }; 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();
} }

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

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

@ -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
@ -32,7 +33,7 @@
<div> <div>
@{ Html.Telerik().PanelBar() @{ Html.Telerik().PanelBar()
.Name("PanelBar") .Name("PanelBar")
//.HtmlAttributes(new { style = "width: 500px; margin: 10px;" }) //.HtmlAttributes(new { style = "width: 500px; margin: 10px;" })
.ExpandMode(PanelBarExpandMode.Single) .ExpandMode(PanelBarExpandMode.Single)
.SelectedIndex(0) .SelectedIndex(0)
.Items(indexerItem => .Items(indexerItem =>
@ -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>
} }

@ -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>
}
Loading…
Cancel
Save