mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +01:00
Merge branch 'thingi-provider' into develop
This commit is contained in:
commit
2fc8123d6b
@ -20,14 +20,14 @@ namespace Marr.Data.Converters
|
||||
{
|
||||
public class BooleanIntConverter : IConverter
|
||||
{
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
int val = (int)dbValue;
|
||||
int val = (int)context.DbValue;
|
||||
|
||||
if (val == 1)
|
||||
{
|
||||
@ -40,7 +40,12 @@ public object FromDB(ColumnMap map, object dbValue)
|
||||
throw new ConversionException(
|
||||
string.Format(
|
||||
"The BooleanCharConverter could not convert the value '{0}' to a boolean.",
|
||||
dbValue));
|
||||
context.DbValue));
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
|
@ -20,14 +20,14 @@ namespace Marr.Data.Converters
|
||||
{
|
||||
public class BooleanYNConverter : IConverter
|
||||
{
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
string val = dbValue.ToString();
|
||||
string val = context.DbValue.ToString();
|
||||
|
||||
if (val == "Y")
|
||||
{
|
||||
@ -40,7 +40,12 @@ public object FromDB(ColumnMap map, object dbValue)
|
||||
throw new ConversionException(
|
||||
string.Format(
|
||||
"The BooleanYNConverter could not convert the value '{0}' to a boolean.",
|
||||
dbValue));
|
||||
context.DbValue));
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext {ColumnMap = map, DbValue = dbValue});
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
|
@ -30,10 +30,15 @@ public Type DbType
|
||||
get { return typeof(TDb); }
|
||||
}
|
||||
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
TDb val = (TDb)context.DbValue;
|
||||
return val.ToType(typeof(TClr), CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
TDb val = (TDb)dbValue;
|
||||
return val.ToType(typeof(TClr), CultureInfo.InvariantCulture);
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
|
13
Marr.Data/Converters/ConverterContext.cs
Normal file
13
Marr.Data/Converters/ConverterContext.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System.Data;
|
||||
using Marr.Data.Mapping;
|
||||
|
||||
namespace Marr.Data.Converters
|
||||
{
|
||||
public class ConverterContext
|
||||
{
|
||||
public ColumnMap ColumnMap { get; set; }
|
||||
public object DbValue { get; set; }
|
||||
public ColumnMapCollection MapCollection { get; set; }
|
||||
public IDataRecord DataRecord { get; set; }
|
||||
}
|
||||
}
|
@ -20,11 +20,16 @@ namespace Marr.Data.Converters
|
||||
{
|
||||
public class EnumIntConverter : IConverter
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (context.DbValue == null || context.DbValue == DBNull.Value)
|
||||
return null;
|
||||
return Enum.ToObject(context.ColumnMap.FieldType, (int)context.DbValue);
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
if (dbValue == null || dbValue == DBNull.Value)
|
||||
return null;
|
||||
return Enum.ToObject(map.FieldType, (int)dbValue);
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
|
@ -20,11 +20,16 @@ namespace Marr.Data.Converters
|
||||
{
|
||||
public class EnumStringConverter : IConverter
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (context.DbValue == null || context.DbValue == DBNull.Value)
|
||||
return null;
|
||||
return Enum.Parse(context.ColumnMap.FieldType, (string)context.DbValue);
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
if (dbValue == null || dbValue == DBNull.Value)
|
||||
return null;
|
||||
return Enum.Parse(map.FieldType, (string)dbValue);
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
|
@ -20,6 +20,9 @@ namespace Marr.Data.Converters
|
||||
{
|
||||
public interface IConverter
|
||||
{
|
||||
object FromDB(ConverterContext context);
|
||||
|
||||
[Obsolete("use FromDB(ConverterContext context) instead")]
|
||||
object FromDB(ColumnMap map, object dbValue);
|
||||
object ToDB(object clrValue);
|
||||
Type DbType { get; }
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Data.Common;
|
||||
using Marr.Data.Converters;
|
||||
|
||||
namespace Marr.Data.Mapping
|
||||
{
|
||||
@ -53,7 +54,15 @@ public object LoadExistingEntity(ColumnMapCollection mappings, DbDataReader read
|
||||
// Handle conversions
|
||||
if (dataMap.Converter != null)
|
||||
{
|
||||
dbValue = dataMap.Converter.FromDB(dataMap, dbValue);
|
||||
var convertContext = new ConverterContext
|
||||
{
|
||||
DbValue = dbValue,
|
||||
ColumnMap = dataMap,
|
||||
MapCollection = mappings,
|
||||
DataRecord = reader
|
||||
};
|
||||
|
||||
dbValue = dataMap.Converter.FromDB(convertContext);
|
||||
}
|
||||
|
||||
if (dbValue != DBNull.Value && dbValue != null)
|
||||
|
@ -52,6 +52,7 @@
|
||||
<Compile Include="Converters\BooleanYNConverter.cs" />
|
||||
<Compile Include="Converters\CastConverter.cs" />
|
||||
<Compile Include="Converters\ConversionException.cs" />
|
||||
<Compile Include="Converters\ConverterContext.cs" />
|
||||
<Compile Include="Converters\EnumIntConverter.cs" />
|
||||
<Compile Include="Converters\EnumStringConverter.cs" />
|
||||
<Compile Include="Converters\IConverter.cs" />
|
||||
|
@ -12,7 +12,7 @@ public class SchemaBuilderFixture : TestBase
|
||||
[Test]
|
||||
public void should_return_field_for_every_property()
|
||||
{
|
||||
var schema = SchemaBuilder.GenerateSchema(new TestModel());
|
||||
var schema = SchemaBuilder.ToSchema(new TestModel());
|
||||
schema.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ public void schema_should_have_proper_fields()
|
||||
LastName = "Poop"
|
||||
};
|
||||
|
||||
var schema = SchemaBuilder.GenerateSchema(model);
|
||||
var schema = SchemaBuilder.ToSchema(model);
|
||||
|
||||
schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && (string) c.Value == "Poop");
|
||||
schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && (string) c.Value == "Bob");
|
||||
|
@ -36,7 +36,7 @@ public class ResourceMappingFixture : TestBase
|
||||
[TestCase(typeof(Episode), typeof(EpisodeResource))]
|
||||
[TestCase(typeof(RootFolder), typeof(RootFolderResource))]
|
||||
[TestCase(typeof(NamingConfig), typeof(NamingConfigResource))]
|
||||
[TestCase(typeof(Indexer), typeof(IndexerResource))]
|
||||
[TestCase(typeof(IndexerDefinition), typeof(IndexerResource))]
|
||||
[TestCase(typeof(ReleaseInfo), typeof(ReleaseResource))]
|
||||
[TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))]
|
||||
[TestCase(typeof(DownloadDecision), typeof(ReleaseResource))]
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.EnsureThat;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
@ -8,8 +10,10 @@ namespace NzbDrone.Api.ClientSchema
|
||||
{
|
||||
public static class SchemaBuilder
|
||||
{
|
||||
public static List<Field> GenerateSchema(object model)
|
||||
public static List<Field> ToSchema(object model)
|
||||
{
|
||||
Ensure.That(() => model).IsNotNull();
|
||||
|
||||
var properties = model.GetType().GetSimpleProperties();
|
||||
|
||||
var result = new List<Field>(properties.Count);
|
||||
@ -50,10 +54,55 @@ public static List<Field> GenerateSchema(object model)
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static object ReadFormSchema(List<Field> fields, Type targetType)
|
||||
{
|
||||
Ensure.That(() => targetType).IsNotNull();
|
||||
|
||||
var properties = targetType.GetSimpleProperties();
|
||||
|
||||
var target = Activator.CreateInstance(targetType);
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
{
|
||||
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
|
||||
|
||||
if (fieldAttribute != null)
|
||||
{
|
||||
var field = fields.Find(f => f.Name == propertyInfo.Name);
|
||||
|
||||
if (propertyInfo.PropertyType == typeof(Int32))
|
||||
{
|
||||
var intValue = Convert.ToInt32(field.Value);
|
||||
propertyInfo.SetValue(target, intValue, null);
|
||||
}
|
||||
|
||||
else if (propertyInfo.PropertyType == typeof(Nullable<Int32>))
|
||||
{
|
||||
var intValue = field.Value.ToString().ParseInt32();
|
||||
propertyInfo.SetValue(target, intValue, null);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
propertyInfo.SetValue(target, field.Value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
public static T ReadFormSchema<T>(List<Field> fields)
|
||||
{
|
||||
return (T)ReadFormSchema(fields, typeof (T));
|
||||
}
|
||||
|
||||
private static List<SelectOption> GetSelectOptions(Type selectOptions)
|
||||
{
|
||||
var options = from Enum e in Enum.GetValues(selectOptions)
|
||||
select new SelectOption { Value = Convert.ToInt32(e), Name = e.ToString() };
|
||||
select new SelectOption { Value = Convert.ToInt32(e), Name = e.ToString() };
|
||||
|
||||
return options.OrderBy(o => o.Value).ToList();
|
||||
}
|
||||
|
@ -8,38 +8,6 @@ namespace NzbDrone.Api.ClientSchema
|
||||
{
|
||||
public static class SchemaDeserializer
|
||||
{
|
||||
public static T DeserializeSchema<T>(T model, List<Field> fields)
|
||||
{
|
||||
var properties = model.GetType().GetSimpleProperties();
|
||||
|
||||
foreach (var propertyInfo in properties)
|
||||
{
|
||||
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);
|
||||
|
||||
if (fieldAttribute != null)
|
||||
{
|
||||
var field = fields.Find(f => f.Name == propertyInfo.Name);
|
||||
|
||||
if (propertyInfo.PropertyType == typeof (Int32))
|
||||
{
|
||||
var intValue = Convert.ToInt32(field.Value);
|
||||
propertyInfo.SetValue(model, intValue, null);
|
||||
}
|
||||
|
||||
else if (propertyInfo.PropertyType == typeof(Nullable<Int32>))
|
||||
{
|
||||
var intValue = field.Value.ToString().ParseInt32();
|
||||
propertyInfo.SetValue(model, intValue, null);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
propertyInfo.SetValue(model, field.Value, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
16
NzbDrone.Api/IndexerResource.cs
Normal file
16
NzbDrone.Api/IndexerResource.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Api.REST;
|
||||
|
||||
namespace NzbDrone.Api
|
||||
{
|
||||
public class ProviderResource : RestResource
|
||||
{
|
||||
public Boolean Enable { get; set; }
|
||||
public String Name { get; set; }
|
||||
public List<Field> Fields { get; set; }
|
||||
public String Implementation { get; set; }
|
||||
public String ConfigContract { get; set; }
|
||||
}
|
||||
}
|
@ -1,113 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using Omu.ValueInjecter;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Core.Indexers;
|
||||
|
||||
namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
public class IndexerModule : NzbDroneRestModule<IndexerResource>
|
||||
public class IndexerModule : ProviderModuleBase<ProviderResource, IIndexer, IndexerDefinition>
|
||||
{
|
||||
private readonly IIndexerService _indexerService;
|
||||
|
||||
public IndexerModule(IIndexerService indexerService)
|
||||
public IndexerModule(IndexerFactory indexerFactory)
|
||||
: base(indexerFactory, "indexer")
|
||||
{
|
||||
_indexerService = indexerService;
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetIndexer;
|
||||
CreateResource = CreateIndexer;
|
||||
UpdateResource = UpdateIndexer;
|
||||
DeleteResource = DeleteIndexer;
|
||||
|
||||
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.Implementation).NotEmpty();
|
||||
|
||||
PostValidator.RuleFor(c => c.Fields).NotEmpty();
|
||||
}
|
||||
|
||||
private IndexerResource GetIndexer(int id)
|
||||
{
|
||||
return _indexerService.Get(id).InjectTo<IndexerResource>();
|
||||
}
|
||||
|
||||
private List<IndexerResource> GetAll()
|
||||
{
|
||||
var indexers = _indexerService.All();
|
||||
|
||||
var result = new List<IndexerResource>(indexers.Count);
|
||||
|
||||
foreach (var indexer in indexers)
|
||||
{
|
||||
var indexerResource = new IndexerResource();
|
||||
indexerResource.InjectFrom(indexer);
|
||||
indexerResource.Fields = SchemaBuilder.GenerateSchema(indexer.Settings);
|
||||
|
||||
result.Add(indexerResource);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int CreateIndexer(IndexerResource indexerResource)
|
||||
{
|
||||
var indexer = GetIndexer(indexerResource);
|
||||
indexer = _indexerService.Create(indexer);
|
||||
return indexer.Id;
|
||||
}
|
||||
|
||||
private void UpdateIndexer(IndexerResource indexerResource)
|
||||
{
|
||||
var indexer = _indexerService.Get(indexerResource.Id);
|
||||
indexer.InjectFrom(indexerResource);
|
||||
indexer.Settings = SchemaDeserializer.DeserializeSchema(indexer.Settings, indexerResource.Fields);
|
||||
|
||||
ValidateIndexer(indexer);
|
||||
|
||||
_indexerService.Update(indexer);
|
||||
}
|
||||
|
||||
|
||||
private static void ValidateIndexer(Indexer indexer)
|
||||
{
|
||||
if (indexer.Enable)
|
||||
{
|
||||
var validationResult = indexer.Settings.Validate();
|
||||
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Indexer GetIndexer(IndexerResource indexerResource)
|
||||
{
|
||||
var indexer = _indexerService.Schema()
|
||||
.SingleOrDefault(i =>
|
||||
i.Implementation.Equals(indexerResource.Implementation,
|
||||
StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (indexer == null)
|
||||
{
|
||||
throw new BadRequestException("Invalid Indexer Implementation");
|
||||
}
|
||||
|
||||
indexer.InjectFrom(indexerResource);
|
||||
indexer.Settings = SchemaDeserializer.DeserializeSchema(indexer.Settings, indexerResource.Fields);
|
||||
|
||||
ValidateIndexer(indexer);
|
||||
|
||||
return indexer;
|
||||
}
|
||||
|
||||
private void DeleteIndexer(int id)
|
||||
{
|
||||
_indexerService.Delete(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,5 +11,6 @@ public class IndexerResource : RestResource
|
||||
public String Name { get; set; }
|
||||
public List<Field> Fields { get; set; }
|
||||
public String Implementation { get; set; }
|
||||
public String ConfigContract { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using Omu.ValueInjecter;
|
||||
@ -7,26 +8,28 @@ namespace NzbDrone.Api.Indexers
|
||||
{
|
||||
public class IndexerSchemaModule : NzbDroneRestModule<IndexerResource>
|
||||
{
|
||||
private readonly IIndexerService _indexerService;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
|
||||
public IndexerSchemaModule(IIndexerService indexerService)
|
||||
public IndexerSchemaModule(IIndexerFactory indexerFactory)
|
||||
: base("indexer/schema")
|
||||
{
|
||||
_indexerService = indexerService;
|
||||
_indexerFactory = indexerFactory;
|
||||
GetResourceAll = GetSchema;
|
||||
}
|
||||
|
||||
private List<IndexerResource> GetSchema()
|
||||
{
|
||||
var indexers = _indexerService.Schema();
|
||||
|
||||
var result = new List<IndexerResource>(indexers.Count);
|
||||
var indexers = _indexerFactory.Templates().Where(c => c.Implementation =="Newznab");
|
||||
|
||||
|
||||
var result = new List<IndexerResource>(indexers.Count());
|
||||
|
||||
foreach (var indexer in indexers)
|
||||
{
|
||||
var indexerResource = new IndexerResource();
|
||||
indexerResource.InjectFrom(indexer);
|
||||
indexerResource.Fields = SchemaBuilder.GenerateSchema(indexer.Settings);
|
||||
indexerResource.Fields = SchemaBuilder.ToSchema(indexer.Settings);
|
||||
|
||||
result.Add(indexerResource);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.Notifications;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
@ -39,7 +40,7 @@ private List<NotificationResource> GetAll()
|
||||
{
|
||||
var notificationResource = new NotificationResource();
|
||||
notificationResource.InjectFrom(notification);
|
||||
notificationResource.Fields = SchemaBuilder.GenerateSchema(notification.Settings);
|
||||
notificationResource.Fields = SchemaBuilder.ToSchema(notification.Settings);
|
||||
notificationResource.TestCommand = String.Format("test{0}", notification.Implementation.ToLowerInvariant());
|
||||
|
||||
result.Add(notificationResource);
|
||||
@ -79,7 +80,10 @@ private Notification ConvertToNotification(NotificationResource notificationReso
|
||||
}
|
||||
|
||||
notification.InjectFrom(notificationResource);
|
||||
notification.Settings = SchemaDeserializer.DeserializeSchema(notification.Settings, notificationResource.Fields);
|
||||
|
||||
//var configType = ReflectionExtensions.CoreAssembly.FindTypeByName(notification)
|
||||
|
||||
//notification.Settings = SchemaBuilder.ReadFormSchema(notification.Settings, notificationResource.Fields);
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ private List<NotificationResource> GetSchema()
|
||||
{
|
||||
var notificationResource = new NotificationResource();
|
||||
notificationResource.InjectFrom(notification);
|
||||
notificationResource.Fields = SchemaBuilder.GenerateSchema(notification.Settings);
|
||||
notificationResource.Fields = SchemaBuilder.ToSchema(notification.Settings);
|
||||
notificationResource.TestCommand = String.Format("test{0}", notification.Implementation.ToLowerInvariant());
|
||||
|
||||
result.Add(notificationResource);
|
||||
|
@ -109,6 +109,8 @@
|
||||
<Compile Include="Frontend\StaticResourceModule.cs" />
|
||||
<Compile Include="History\HistoryResource.cs" />
|
||||
<Compile Include="History\HistoryModule.cs" />
|
||||
<Compile Include="IndexerResource.cs" />
|
||||
<Compile Include="ProviderModuleBase.cs" />
|
||||
<Compile Include="Indexers\IndexerSchemaModule.cs" />
|
||||
<Compile Include="Indexers\IndexerModule.cs" />
|
||||
<Compile Include="Indexers\IndexerResource.cs" />
|
||||
|
134
NzbDrone.Api/ProviderModuleBase.cs
Normal file
134
NzbDrone.Api/ProviderModuleBase.cs
Normal file
@ -0,0 +1,134 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Api.Indexers;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
namespace NzbDrone.Api
|
||||
{
|
||||
public abstract class ProviderModuleBase<TProviderResource, TProvider, TProviderDefinition> : NzbDroneRestModule<TProviderResource>
|
||||
where TProviderDefinition : ProviderDefinition, new()
|
||||
where TProvider : IProvider
|
||||
where TProviderResource : ProviderResource, new()
|
||||
{
|
||||
private readonly IProviderFactory<TProvider, TProviderDefinition> _providerFactory;
|
||||
|
||||
protected ProviderModuleBase(IProviderFactory<TProvider, TProviderDefinition> providerFactory, string resource)
|
||||
: base(resource)
|
||||
{
|
||||
_providerFactory = providerFactory;
|
||||
Get["templates"] = x => GetTemplates();
|
||||
GetResourceAll = GetAll;
|
||||
GetResourceById = GetProviderById;
|
||||
CreateResource = CreateProvider;
|
||||
UpdateResource = UpdateProvider;
|
||||
DeleteResource = DeleteProvider;
|
||||
|
||||
|
||||
|
||||
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.Implementation).NotEmpty();
|
||||
SharedValidator.RuleFor(c => c.ConfigContract).NotEmpty();
|
||||
|
||||
PostValidator.RuleFor(c => c.Fields).NotEmpty();
|
||||
}
|
||||
|
||||
private TProviderResource GetProviderById(int id)
|
||||
{
|
||||
return _providerFactory.Get(id).InjectTo<TProviderResource>();
|
||||
}
|
||||
|
||||
private List<TProviderResource> GetAll()
|
||||
{
|
||||
var indexerDefinitions = _providerFactory.All();
|
||||
|
||||
var result = new List<TProviderResource>(indexerDefinitions.Count);
|
||||
|
||||
foreach (var definition in indexerDefinitions)
|
||||
{
|
||||
var indexerResource = new TProviderResource();
|
||||
indexerResource.InjectFrom(definition);
|
||||
indexerResource.Fields = SchemaBuilder.ToSchema(definition.Settings);
|
||||
|
||||
result.Add(indexerResource);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int CreateProvider(TProviderResource indexerResource)
|
||||
{
|
||||
var indexer = GetDefinition(indexerResource);
|
||||
indexer = _providerFactory.Create(indexer);
|
||||
return indexer.Id;
|
||||
}
|
||||
|
||||
private void UpdateProvider(TProviderResource indexerResource)
|
||||
{
|
||||
var indexer = GetDefinition(indexerResource);
|
||||
|
||||
ValidateIndexer(indexer);
|
||||
|
||||
_providerFactory.Update(indexer);
|
||||
}
|
||||
|
||||
|
||||
private static void ValidateIndexer(ProviderDefinition definition)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
|
||||
var validationResult = definition.Settings.Validate();
|
||||
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
}
|
||||
|
||||
private TProviderDefinition GetDefinition(TProviderResource indexerResource)
|
||||
{
|
||||
|
||||
var definition = new TProviderDefinition();
|
||||
|
||||
definition.InjectFrom(indexerResource);
|
||||
|
||||
var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract);
|
||||
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(indexerResource.Fields, configContract);
|
||||
|
||||
ValidateIndexer(definition);
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
private void DeleteProvider(int id)
|
||||
{
|
||||
_providerFactory.Delete(id);
|
||||
}
|
||||
|
||||
private Response GetTemplates()
|
||||
{
|
||||
|
||||
var indexers = _providerFactory.Templates();
|
||||
|
||||
|
||||
var result = new List<IndexerResource>(indexers.Count());
|
||||
|
||||
foreach (var indexer in indexers)
|
||||
{
|
||||
var indexerResource = new IndexerResource();
|
||||
indexerResource.InjectFrom(indexer);
|
||||
indexerResource.Fields = SchemaBuilder.ToSchema(indexer.Settings);
|
||||
|
||||
result.Add(indexerResource);
|
||||
}
|
||||
|
||||
return result.AsResponse();
|
||||
}
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ public void container_should_inject_itself()
|
||||
{
|
||||
var factory = MainAppContainerBuilder.BuildContainer(args).Resolve<IServiceFactory>();
|
||||
|
||||
factory.Build<IIndexerService>().Should().NotBeNull();
|
||||
factory.Build<IIndexerFactory>().Should().NotBeNull();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -7,6 +7,8 @@ namespace NzbDrone.Common.Reflection
|
||||
{
|
||||
public static class ReflectionExtensions
|
||||
{
|
||||
public static readonly Assembly CoreAssembly = Assembly.Load("NzbDrone.Core");
|
||||
|
||||
public static List<PropertyInfo> GetSimpleProperties(this Type type)
|
||||
{
|
||||
var properties = type.GetProperties();
|
||||
@ -58,6 +60,11 @@ public static T GetAttribute<T>(this MemberInfo member, bool isRequired = true)
|
||||
return (T)attribute;
|
||||
}
|
||||
|
||||
public static Type FindTypeByName(this Assembly assembly, string name)
|
||||
{
|
||||
return assembly.GetTypes().SingleOrDefault(c => c.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public static bool HasAttribute<TAttribute>(this Type type)
|
||||
{
|
||||
return type.GetCustomAttributes(typeof(TAttribute), true).Any();
|
||||
|
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using FluentAssertions;
|
||||
using Marr.Data.Converters;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Test.Datastore.Converters
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProviderSettingConverterFixture : CoreTest<ProviderSettingConverter>
|
||||
{
|
||||
[Test]
|
||||
public void should_return_null_config_if_config_is_null()
|
||||
{
|
||||
var result = Subject.FromDB(new ConverterContext()
|
||||
{
|
||||
DbValue = DBNull.Value
|
||||
});
|
||||
|
||||
|
||||
result.Should().Be(NullConfig.Instance);
|
||||
}
|
||||
|
||||
[TestCase(null)]
|
||||
[TestCase("")]
|
||||
public void should_return_null_config_if_config_is_empty(object dbValue)
|
||||
{
|
||||
var result = Subject.FromDB(new ConverterContext()
|
||||
{
|
||||
DbValue = dbValue
|
||||
});
|
||||
|
||||
|
||||
result.Should().Be(NullConfig.Instance);
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
|
||||
namespace NzbDrone.Core.Test.IndexerTests
|
||||
{
|
||||
public class IndexerServiceFixture : DbTest<IndexerService, IndexerDefinition>
|
||||
public class IndexerServiceFixture : DbTest<IndexerFactory, IndexerDefinition>
|
||||
{
|
||||
private List<IIndexer> _indexers;
|
||||
|
||||
@ -57,10 +57,8 @@ public void getting_list_of_indexers()
|
||||
var indexers = Subject.All().ToList();
|
||||
indexers.Should().NotBeEmpty();
|
||||
indexers.Should().NotContain(c => c.Settings == null);
|
||||
indexers.Should().NotContain(c => c.Instance == null);
|
||||
indexers.Should().NotContain(c => c.Name == null);
|
||||
indexers.Select(c => c.Name).Should().OnlyHaveUniqueItems();
|
||||
indexers.Select(c => c.Instance).Should().OnlyHaveUniqueItems();
|
||||
}
|
||||
|
||||
|
||||
@ -73,6 +71,7 @@ public void should_remove_missing_indexers_on_startup()
|
||||
|
||||
|
||||
var existingIndexers = Builder<IndexerDefinition>.CreateNew().BuildNew();
|
||||
existingIndexers.ConfigContract = typeof (NewznabSettings).Name;
|
||||
|
||||
repo.Insert(existingIndexers);
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Test.Common.Categories;
|
||||
using System.Linq;
|
||||
|
||||
@ -27,6 +28,12 @@ public void wombles_rss()
|
||||
{
|
||||
var indexer = new Wombles();
|
||||
|
||||
indexer.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "Wombles",
|
||||
Settings = NullConfig.Instance
|
||||
};
|
||||
|
||||
var result = Subject.FetchRss(indexer);
|
||||
|
||||
ValidateResult(result, skipSize: true, skipInfo: true);
|
||||
@ -37,6 +44,11 @@ public void wombles_rss()
|
||||
public void extv_rss()
|
||||
{
|
||||
var indexer = new Eztv();
|
||||
indexer.Definition = new IndexerDefinition
|
||||
{
|
||||
Name = "Eztv",
|
||||
Settings = NullConfig.Instance
|
||||
};
|
||||
|
||||
var result = Subject.FetchRss(indexer);
|
||||
|
||||
@ -48,14 +60,14 @@ public void extv_rss()
|
||||
public void nzbsorg_rss()
|
||||
{
|
||||
var indexer = new Newznab();
|
||||
indexer.Settings = new NewznabSettings
|
||||
{
|
||||
ApiKey = "64d61d3cfd4b75e51d01cbc7c6a78275",
|
||||
Url = "http://nzbs.org"
|
||||
};
|
||||
|
||||
indexer.InstanceDefinition = new IndexerDefinition();
|
||||
indexer.InstanceDefinition.Name = "nzbs.org";
|
||||
indexer.Definition = new IndexerDefinition();
|
||||
indexer.Definition.Name = "nzbs.org";
|
||||
indexer.Definition.Settings = new NewznabSettings
|
||||
{
|
||||
ApiKey = "64d61d3cfd4b75e51d01cbc7c6a78275",
|
||||
Url = "http://nzbs.org"
|
||||
};
|
||||
|
||||
var result = Subject.FetchRss(indexer);
|
||||
|
||||
|
@ -102,6 +102,7 @@
|
||||
<Compile Include="DataAugmentationFixture\Scene\SceneMappingProxyFixture.cs" />
|
||||
<Compile Include="DataAugmentationFixture\Scene\SceneMappingServiceFixture.cs" />
|
||||
<Compile Include="Datastore\BasicRepositoryFixture.cs" />
|
||||
<Compile Include="Datastore\Converters\ProviderSettingConverterFixture.cs" />
|
||||
<Compile Include="Datastore\DatabaseRelationshipFixture.cs" />
|
||||
<Compile Include="Datastore\MappingExtentionFixture.cs" />
|
||||
<Compile Include="Datastore\ObjectDatabaseFixture.cs" />
|
||||
@ -181,6 +182,8 @@
|
||||
<Compile Include="ProviderTests\RecycleBinProviderTests\DeleteDirectoryFixture.cs" />
|
||||
<Compile Include="NotificationTests\PlexProviderTest.cs" />
|
||||
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedEpisodesFixture.cs" />
|
||||
<Compile Include="ThingiProviderTests\NullConfigFixture.cs" />
|
||||
<Compile Include="ThingiProvider\ProviderBaseFixture.cs" />
|
||||
<Compile Include="TvTests\RefreshEpisodeServiceFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeProviderTests\HandleEpisodeFileDeletedFixture.cs" />
|
||||
<Compile Include="TvTests\EpisodeRepositoryTests\FindEpisodeFixture.cs" />
|
||||
|
31
NzbDrone.Core.Test/ThingiProvider/ProviderBaseFixture.cs
Normal file
31
NzbDrone.Core.Test/ThingiProvider/ProviderBaseFixture.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Indexers.Newznab;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Test.ThingiProvider
|
||||
{
|
||||
|
||||
public class ProviderRepositoryFixture : DbTest<IndexerRepository, IndexerDefinition>
|
||||
{
|
||||
[Test]
|
||||
public void should_read_write_download_provider()
|
||||
{
|
||||
var model = Builder<IndexerDefinition>.CreateNew().BuildNew();
|
||||
var newznabSettings = Builder<NewznabSettings>.CreateNew().Build();
|
||||
model.Settings = newznabSettings;
|
||||
Subject.Insert(model);
|
||||
|
||||
var storedProvider = Subject.Single();
|
||||
|
||||
storedProvider.Settings.Should().BeOfType<NewznabSettings>();
|
||||
|
||||
var storedSetting = (NewznabSettings)storedProvider.Settings;
|
||||
|
||||
storedSetting.ShouldHave().AllProperties().EqualTo(newznabSettings);
|
||||
}
|
||||
}
|
||||
}
|
17
NzbDrone.Core.Test/ThingiProviderTests/NullConfigFixture.cs
Normal file
17
NzbDrone.Core.Test/ThingiProviderTests/NullConfigFixture.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Test.ThingiProviderTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class NullConfigFixture : CoreTest<NullConfig>
|
||||
{
|
||||
[Test]
|
||||
public void should_be_valid()
|
||||
{
|
||||
Subject.Validate().IsValid.Should().BeTrue();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,14 +6,14 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class BooleanIntConverter : IConverter
|
||||
{
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
var val = (Int64)dbValue;
|
||||
var val = (Int64)context.DbValue;
|
||||
|
||||
switch (val)
|
||||
{
|
||||
@ -22,10 +22,15 @@ public object FromDB(ColumnMap map, object dbValue)
|
||||
case 0:
|
||||
return false;
|
||||
default:
|
||||
throw new ConversionException(string.Format("The BooleanCharConverter could not convert the value '{0}' to a Boolean.", dbValue));
|
||||
throw new ConversionException(string.Format("The BooleanCharConverter could not convert the value '{0}' to a Boolean.", context.DbValue));
|
||||
}
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
var val = (Nullable<bool>)clrValue;
|
||||
|
@ -7,22 +7,26 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class EmbeddedDocumentConverter : IConverter
|
||||
{
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public virtual object FromDB(ConverterContext context)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
var stringValue = (string)dbValue;
|
||||
var stringValue = (string)context.DbValue;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(stringValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return Json.Deserialize(stringValue, map.FieldType);
|
||||
return Json.Deserialize(stringValue, context.ColumnMap.FieldType);
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
|
@ -14,16 +14,21 @@ public Type DbType
|
||||
}
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (dbValue != null && dbValue != DBNull.Value)
|
||||
if (context.DbValue != null && context.DbValue != DBNull.Value)
|
||||
{
|
||||
return Enum.ToObject(map.FieldType, (Int64)dbValue);
|
||||
return Enum.ToObject(context.ColumnMap.FieldType, (Int64)context.DbValue);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
if (clrValue != null)
|
||||
|
@ -6,6 +6,21 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class Int32Converter : IConverter
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return DBNull.Value;
|
||||
}
|
||||
|
||||
if (context.DbValue is Int32)
|
||||
{
|
||||
return context.DbValue;
|
||||
}
|
||||
|
||||
return Convert.ToInt32(context.DbValue);
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
|
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using Marr.Data.Converters;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class ProviderSettingConverter : EmbeddedDocumentConverter
|
||||
{
|
||||
public override object FromDB(ConverterContext context)
|
||||
{
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return NullConfig.Instance;
|
||||
}
|
||||
|
||||
var stringValue = (string)context.DbValue;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(stringValue))
|
||||
{
|
||||
return NullConfig.Instance;
|
||||
}
|
||||
|
||||
var ordinal = context.DataRecord.GetOrdinal("ConfigContract");
|
||||
var contract = context.DataRecord.GetString(ordinal);
|
||||
|
||||
|
||||
var impType = typeof (IProviderConfig).Assembly.FindTypeByName(contract);
|
||||
|
||||
if (impType == null)
|
||||
{
|
||||
throw new ConfigContractNotFoundException(contract);
|
||||
}
|
||||
|
||||
return Json.Deserialize(stringValue, impType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -7,18 +7,23 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class QualityIntConverter : IConverter
|
||||
{
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
if (dbValue == DBNull.Value)
|
||||
if (context.DbValue == DBNull.Value)
|
||||
{
|
||||
return Quality.Unknown;
|
||||
}
|
||||
|
||||
var val = Convert.ToInt32(dbValue);
|
||||
var val = Convert.ToInt32(context.DbValue);
|
||||
|
||||
return (Quality)val;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
{
|
||||
if(clrValue == null) return 0;
|
||||
|
@ -6,9 +6,14 @@ namespace NzbDrone.Core.Datastore.Converters
|
||||
{
|
||||
public class UtcConverter : IConverter
|
||||
{
|
||||
public object FromDB(ConverterContext context)
|
||||
{
|
||||
return context.DbValue;
|
||||
}
|
||||
|
||||
public object FromDB(ColumnMap map, object dbValue)
|
||||
{
|
||||
return dbValue;
|
||||
return FromDB(new ConverterContext { ColumnMap = map, DbValue = dbValue });
|
||||
}
|
||||
|
||||
public object ToDB(object clrValue)
|
||||
|
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(22)]
|
||||
public class move_indexer_to_generic_provider : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Indexers").AddColumn("ConfigContract").AsString().Nullable();
|
||||
|
||||
//Execute.WithConnection(ConvertSeasons);
|
||||
}
|
||||
|
||||
private void ConvertSeasons(IDbConnection conn, IDbTransaction tran)
|
||||
{
|
||||
using (IDbCommand allSeriesCmd = conn.CreateCommand())
|
||||
{
|
||||
allSeriesCmd.Transaction = tran;
|
||||
allSeriesCmd.CommandText = @"SELECT Id FROM Series";
|
||||
using (IDataReader allSeriesReader = allSeriesCmd.ExecuteReader())
|
||||
{
|
||||
while (allSeriesReader.Read())
|
||||
{
|
||||
int seriesId = allSeriesReader.GetInt32(0);
|
||||
var seasons = new List<dynamic>();
|
||||
|
||||
using (IDbCommand seasonsCmd = conn.CreateCommand())
|
||||
{
|
||||
seasonsCmd.Transaction = tran;
|
||||
seasonsCmd.CommandText = String.Format(@"SELECT SeasonNumber, Monitored FROM Seasons WHERE SeriesId = {0}", seriesId);
|
||||
|
||||
using (IDataReader seasonReader = seasonsCmd.ExecuteReader())
|
||||
{
|
||||
while (seasonReader.Read())
|
||||
{
|
||||
int seasonNumber = seasonReader.GetInt32(0);
|
||||
bool monitored = seasonReader.GetBoolean(1);
|
||||
|
||||
if (seasonNumber == 0)
|
||||
{
|
||||
monitored = false;
|
||||
}
|
||||
|
||||
seasons.Add(new { seasonNumber, monitored });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (IDbCommand updateCmd = conn.CreateCommand())
|
||||
{
|
||||
var text = String.Format("UPDATE Series SET Seasons = '{0}' WHERE Id = {1}", seasons.ToJson(), seriesId);
|
||||
|
||||
updateCmd.Transaction = tran;
|
||||
updateCmd.CommandText = text;
|
||||
updateCmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(23)]
|
||||
public class add_config_contract_to_indexers : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Update.Table("Indexers").Set(new { ConfigContract = "NewznabSettings" }).Where(new { Implementation = "Newznab" });
|
||||
Update.Table("Indexers").Set(new { ConfigContract = "OmgwtfnzbsSettings" }).Where(new { Implementation = "Omgwtfnzbs" });
|
||||
Update.Table("Indexers").Set(new { ConfigContract = "NullConfig" }).Where(new { Implementation = "Wombles" });
|
||||
Update.Table("Indexers").Set(new { ConfigContract = "NullConfig" }).Where(new { Implementation = "Eztv" });
|
||||
|
||||
Delete.FromTable("Indexers").IsNull("ConfigContract");
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
using System.Linq;
|
||||
using Marr.Data;
|
||||
using Marr.Data.Mapping;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.DataAugmentation.Scene;
|
||||
using NzbDrone.Core.Datastore.Converters;
|
||||
@ -15,6 +16,7 @@
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.RootFolders;
|
||||
using NzbDrone.Core.SeriesStats;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Datastore
|
||||
@ -69,6 +71,7 @@ public static void Map()
|
||||
private static void RegisterMappers()
|
||||
{
|
||||
RegisterEmbeddedConverter();
|
||||
RegisterProviderSettingConverter();
|
||||
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(Int32), new Int32Converter());
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(DateTime), new UtcConverter());
|
||||
@ -78,10 +81,20 @@ private static void RegisterMappers()
|
||||
MapRepository.Instance.RegisterTypeConverter(typeof(Dictionary<string, string>), new EmbeddedDocumentConverter());
|
||||
}
|
||||
|
||||
private static void RegisterProviderSettingConverter()
|
||||
{
|
||||
var settingTypes = typeof(IProviderConfig).Assembly.ImplementationsOf<IProviderConfig>();
|
||||
|
||||
var providerSettingConverter = new ProviderSettingConverter();
|
||||
foreach (var embeddedType in settingTypes)
|
||||
{
|
||||
MapRepository.Instance.RegisterTypeConverter(embeddedType, providerSettingConverter);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RegisterEmbeddedConverter()
|
||||
{
|
||||
var embeddedTypes = typeof(IEmbeddedDocument).Assembly.GetTypes()
|
||||
.Where(c => c.GetInterfaces().Any(i => i == typeof(IEmbeddedDocument)));
|
||||
var embeddedTypes = typeof(IEmbeddedDocument).Assembly.ImplementationsOf<IEmbeddedDocument>();
|
||||
|
||||
|
||||
var embeddedConvertor = new EmbeddedDocumentConverter();
|
||||
|
@ -24,7 +24,7 @@ public interface ISearchForNzb
|
||||
|
||||
public class NzbSearchService : ISearchForNzb
|
||||
{
|
||||
private readonly IIndexerService _indexerService;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly IFetchFeedFromIndexers _feedFetcher;
|
||||
private readonly ISceneMappingService _sceneMapping;
|
||||
private readonly ISeriesService _seriesService;
|
||||
@ -32,7 +32,7 @@ public class NzbSearchService : ISearchForNzb
|
||||
private readonly IMakeDownloadDecision _makeDownloadDecision;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public NzbSearchService(IIndexerService indexerService,
|
||||
public NzbSearchService(IIndexerFactory indexerFactory,
|
||||
IFetchFeedFromIndexers feedFetcher,
|
||||
ISceneMappingService sceneMapping,
|
||||
ISeriesService seriesService,
|
||||
@ -40,7 +40,7 @@ public NzbSearchService(IIndexerService indexerService,
|
||||
IMakeDownloadDecision makeDownloadDecision,
|
||||
Logger logger)
|
||||
{
|
||||
_indexerService = indexerService;
|
||||
_indexerFactory = indexerFactory;
|
||||
_feedFetcher = feedFetcher;
|
||||
_sceneMapping = sceneMapping;
|
||||
_seriesService = seriesService;
|
||||
@ -132,7 +132,7 @@ public List<DownloadDecision> SeasonSearch(int seriesId, int seasonNumber)
|
||||
|
||||
private List<DownloadDecision> Dispatch(Func<IIndexer, IEnumerable<ReleaseInfo>> searchAction, SearchCriteriaBase criteriaBase)
|
||||
{
|
||||
var indexers = _indexerService.GetAvailableIndexers().ToList();
|
||||
var indexers = _indexerFactory.GetAvailableProviders().ToList();
|
||||
var reports = new List<ReleaseInfo>();
|
||||
|
||||
_logger.ProgressInfo("Searching {0} indexers for {1}", indexers.Count, criteriaBase);
|
||||
|
@ -1,28 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Eztv
|
||||
{
|
||||
public class Eztv : IndexerBase
|
||||
public class Eztv : IndexerBase<NullConfig>
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "Eztv"; }
|
||||
}
|
||||
|
||||
public override IndexerKind Kind
|
||||
public override DownloadProtocol Protocol
|
||||
{
|
||||
get
|
||||
{
|
||||
return IndexerKind.Torrent;
|
||||
return DownloadProtocol.Torrent;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool EnableByDefault
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override IParseFeed Parser
|
||||
{
|
||||
get
|
||||
@ -35,10 +26,7 @@ public override IEnumerable<string> RecentFeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
"http://www.ezrss.it/feed/"
|
||||
};
|
||||
yield return "http://www.ezrss.it/feed/";
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +43,7 @@ public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int
|
||||
|
||||
public override IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date)
|
||||
{
|
||||
//EZTV doesn't support searching based on actual epidose airdate. they only support release date.
|
||||
//EZTV doesn't support searching based on actual episode airdate. they only support release date.
|
||||
return new string[0];
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,13 @@ public interface IFetchAndParseRss
|
||||
|
||||
public class FetchAndParseRssService : IFetchAndParseRss
|
||||
{
|
||||
private readonly IIndexerService _indexerService;
|
||||
private readonly IIndexerFactory _indexerFactory;
|
||||
private readonly IFetchFeedFromIndexers _feedFetcher;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public FetchAndParseRssService(IIndexerService indexerService, IFetchFeedFromIndexers feedFetcher, Logger logger)
|
||||
public FetchAndParseRssService(IIndexerFactory indexerFactory, IFetchFeedFromIndexers feedFetcher, Logger logger)
|
||||
{
|
||||
_indexerService = indexerService;
|
||||
_indexerFactory = indexerFactory;
|
||||
_feedFetcher = feedFetcher;
|
||||
_logger = logger;
|
||||
}
|
||||
@ -29,7 +29,7 @@ public List<ReleaseInfo> Fetch()
|
||||
{
|
||||
var result = new List<ReleaseInfo>();
|
||||
|
||||
var indexers = _indexerService.GetAvailableIndexers().ToList();
|
||||
var indexers = _indexerFactory.GetAvailableProviders().ToList();
|
||||
|
||||
if (!indexers.Any())
|
||||
{
|
||||
|
@ -1,23 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface IIndexer
|
||||
public interface IIndexer : IProvider
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
bool EnableByDefault { get; }
|
||||
|
||||
IEnumerable<IndexerDefinition> DefaultDefinitions { get; }
|
||||
|
||||
IndexerDefinition InstanceDefinition { get; set; }
|
||||
IParseFeed Parser { get; }
|
||||
DownloadProtocol Protocol { get; }
|
||||
|
||||
IEnumerable<string> RecentFeed { get; }
|
||||
|
||||
IParseFeed Parser { get; }
|
||||
IndexerKind Kind { get; }
|
||||
|
||||
IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber);
|
||||
IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date);
|
||||
IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int offset);
|
||||
|
@ -1,41 +1,62 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public abstract class IndexerBase : IIndexer
|
||||
public abstract class IndexerBase<TSettings> : IIndexer where TSettings : IProviderConfig, new()
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
|
||||
public abstract IndexerKind Kind { get; }
|
||||
|
||||
public virtual bool EnableByDefault { get { return true; } }
|
||||
|
||||
public IndexerDefinition InstanceDefinition { get; set; }
|
||||
|
||||
public virtual IEnumerable<IndexerDefinition> DefaultDefinitions
|
||||
public Type ConfigContract
|
||||
{
|
||||
get
|
||||
{
|
||||
return typeof(TSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
var config = (IProviderConfig)new TSettings();
|
||||
|
||||
yield return new IndexerDefinition
|
||||
{
|
||||
Name = Name,
|
||||
Enable = EnableByDefault,
|
||||
Name = GetType().Name,
|
||||
Enable = config.Validate().IsValid,
|
||||
Implementation = GetType().Name,
|
||||
Settings = String.Empty
|
||||
Settings = config
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public ProviderDefinition Definition { get; set; }
|
||||
|
||||
public abstract DownloadProtocol Protocol { get; }
|
||||
|
||||
protected TSettings Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
return (TSettings)Definition.Settings;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IParseFeed Parser { get; private set; }
|
||||
|
||||
public abstract IEnumerable<string> RecentFeed { get; }
|
||||
public abstract IEnumerable<string> GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber);
|
||||
public abstract IEnumerable<string> GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date);
|
||||
public abstract IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int offset);
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GetType().Name;
|
||||
}
|
||||
}
|
||||
|
||||
public enum IndexerKind
|
||||
public enum DownloadProtocol
|
||||
{
|
||||
Usenet,
|
||||
Torrent
|
||||
|
@ -1,13 +1,8 @@
|
||||
using System;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public class IndexerDefinition : ModelBase
|
||||
public class IndexerDefinition : ProviderDefinition
|
||||
{
|
||||
public Boolean Enable { get; set; }
|
||||
public String Name { get; set; }
|
||||
public String Settings { get; set; }
|
||||
public String Implementation { get; set; }
|
||||
}
|
||||
}
|
41
NzbDrone.Core/Indexers/IndexerFactory.cs
Normal file
41
NzbDrone.Core/Indexers/IndexerFactory.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface IIndexerFactory : IProviderFactory<IIndexer, IndexerDefinition>
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class IndexerFactory : ProviderFactory<IIndexer, IndexerDefinition>, IIndexerFactory
|
||||
{
|
||||
private readonly IIndexerRepository _providerRepository;
|
||||
private readonly IEnumerable<IIndexer> _providers;
|
||||
|
||||
public IndexerFactory(IIndexerRepository providerRepository, IEnumerable<IIndexer> providers, Logger logger)
|
||||
: base(providerRepository, providers, logger)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_providers = providers;
|
||||
}
|
||||
|
||||
protected override void InitializeProviders()
|
||||
{
|
||||
var definitions = _providers.Where(c => c.Protocol == DownloadProtocol.Usenet)
|
||||
.SelectMany(indexer => indexer.DefaultDefinitions);
|
||||
|
||||
var currentProviders = All();
|
||||
|
||||
var newProviders = definitions.Where(def => currentProviders.All(c => c.Implementation != def.Implementation)).ToList();
|
||||
|
||||
|
||||
if (newProviders.Any())
|
||||
{
|
||||
_providerRepository.InsertMany(newProviders.Cast<IndexerDefinition>().ToList());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -33,11 +33,11 @@ public FetchFeedService(IHttpProvider httpProvider, Logger logger)
|
||||
|
||||
public virtual IList<ReleaseInfo> FetchRss(IIndexer indexer)
|
||||
{
|
||||
_logger.Debug("Fetching feeds from " + indexer.Name);
|
||||
_logger.Debug("Fetching feeds from " + indexer);
|
||||
|
||||
var result = Fetch(indexer, indexer.RecentFeed);
|
||||
|
||||
_logger.Debug("Finished processing feeds from " + indexer.Name);
|
||||
_logger.Debug("Finished processing feeds from " + indexer);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -48,7 +48,7 @@ public IList<ReleaseInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCri
|
||||
|
||||
var result = Fetch(indexer, searchCriteria, 0).DistinctBy(c => c.DownloadUrl).ToList();
|
||||
|
||||
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count);
|
||||
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer, searchCriteria, result.Count);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -61,7 +61,7 @@ private IList<ReleaseInfo> Fetch(IIndexer indexer, SeasonSearchCriteria searchCr
|
||||
var result = Fetch(indexer, searchUrls);
|
||||
|
||||
|
||||
_logger.Info("{0} offset {1}. Found {2}", indexer.Name, searchCriteria, result.Count);
|
||||
_logger.Info("{0} offset {1}. Found {2}", indexer, searchCriteria, result.Count);
|
||||
|
||||
if (result.Count > 90)
|
||||
{
|
||||
@ -79,7 +79,7 @@ public IList<ReleaseInfo> Fetch(IIndexer indexer, SingleEpisodeSearchCriteria se
|
||||
var result = Fetch(indexer, searchUrls);
|
||||
|
||||
|
||||
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count);
|
||||
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer, searchCriteria, result.Count);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ public IList<ReleaseInfo> Fetch(IIndexer indexer, DailyEpisodeSearchCriteria sea
|
||||
var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.Airtime);
|
||||
var result = Fetch(indexer, searchUrls);
|
||||
|
||||
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count);
|
||||
_logger.Info("Finished searching {0} for {1}. Found {2}", indexer, searchCriteria, result.Count);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -119,17 +119,16 @@ private List<ReleaseInfo> Fetch(IIndexer indexer, IEnumerable<string> urls)
|
||||
if (webException.Message.Contains("502") || webException.Message.Contains("503") ||
|
||||
webException.Message.Contains("timed out"))
|
||||
{
|
||||
_logger.Warn("{0} server is currently unavailable. {1} {2}", indexer.Name, url,
|
||||
webException.Message);
|
||||
_logger.Warn("{0} server is currently unavailable. {1} {2}", indexer, url, webException.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Warn("{0} {1} {2}", indexer.Name, url, webException.Message);
|
||||
_logger.Warn("{0} {1} {2}", indexer, url, webException.Message);
|
||||
}
|
||||
}
|
||||
catch (ApiKeyException)
|
||||
{
|
||||
_logger.Warn("Invalid API Key for {0} {1}", indexer.Name, url);
|
||||
_logger.Warn("Invalid API Key for {0} {1}", indexer, url);
|
||||
}
|
||||
catch (Exception feedEx)
|
||||
{
|
||||
@ -138,7 +137,7 @@ private List<ReleaseInfo> Fetch(IIndexer indexer, IEnumerable<string> urls)
|
||||
}
|
||||
}
|
||||
|
||||
result.ForEach(c => c.Indexer = indexer.Name);
|
||||
result.ForEach(c => c.Indexer = indexer.Definition.Name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1,33 +1,20 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface IIndexerRepository : IBasicRepository<IndexerDefinition>
|
||||
public interface IIndexerRepository : IProviderRepository<IndexerDefinition>
|
||||
{
|
||||
IndexerDefinition Get(string name);
|
||||
IndexerDefinition Find(string name);
|
||||
|
||||
}
|
||||
|
||||
public class IndexerRepository : BasicRepository<IndexerDefinition>, IIndexerRepository
|
||||
public class IndexerRepository : ProviderRepository<IndexerDefinition>, IIndexerRepository
|
||||
{
|
||||
public IndexerRepository(IDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
|
||||
public IndexerDefinition Get(string name)
|
||||
{
|
||||
return Query.Single(i => i.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public IndexerDefinition Find(string name)
|
||||
{
|
||||
return Query.SingleOrDefault(i => i.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,201 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Indexers.Newznab;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public class Indexer
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public bool Enable { get; set; }
|
||||
public IIndexerSetting Settings { get; set; }
|
||||
public IIndexer Instance { get; set; }
|
||||
public string Implementation { get; set; }
|
||||
}
|
||||
|
||||
public interface IIndexerService
|
||||
{
|
||||
List<Indexer> All();
|
||||
List<IIndexer> GetAvailableIndexers();
|
||||
Indexer Get(int id);
|
||||
Indexer Get(string name);
|
||||
List<Indexer> Schema();
|
||||
Indexer Create(Indexer indexer);
|
||||
Indexer Update(Indexer indexer);
|
||||
void Delete(int id);
|
||||
}
|
||||
|
||||
public class IndexerService : IIndexerService, IHandle<ApplicationStartedEvent>
|
||||
{
|
||||
private readonly IIndexerRepository _indexerRepository;
|
||||
private readonly IConfigFileProvider _configFileProvider;
|
||||
private readonly INewznabTestService _newznabTestService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly List<IIndexer> _indexers;
|
||||
|
||||
public IndexerService(IIndexerRepository indexerRepository,
|
||||
IEnumerable<IIndexer> indexers,
|
||||
IConfigFileProvider configFileProvider,
|
||||
INewznabTestService newznabTestService,
|
||||
Logger logger)
|
||||
{
|
||||
_indexerRepository = indexerRepository;
|
||||
_configFileProvider = configFileProvider;
|
||||
_newznabTestService = newznabTestService;
|
||||
_logger = logger;
|
||||
|
||||
|
||||
if (!configFileProvider.Torrent)
|
||||
{
|
||||
_indexers = indexers.Where(c => c.Kind != IndexerKind.Torrent).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
_indexers = indexers.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Indexer> All()
|
||||
{
|
||||
return _indexerRepository.All().Select(ToIndexer).ToList();
|
||||
}
|
||||
|
||||
public List<IIndexer> GetAvailableIndexers()
|
||||
{
|
||||
return All().Where(c => c.Enable && c.Settings.Validate().IsValid).Select(c => c.Instance).ToList();
|
||||
}
|
||||
|
||||
public Indexer Get(int id)
|
||||
{
|
||||
return ToIndexer(_indexerRepository.Get(id));
|
||||
}
|
||||
|
||||
public Indexer Get(string name)
|
||||
{
|
||||
return ToIndexer(_indexerRepository.Get(name));
|
||||
}
|
||||
|
||||
public List<Indexer> Schema()
|
||||
{
|
||||
var indexers = new List<Indexer>();
|
||||
|
||||
var newznab = new Indexer();
|
||||
newznab.Instance = new Newznab.Newznab();
|
||||
newznab.Id = 1;
|
||||
newznab.Name = "Newznab";
|
||||
newznab.Settings = new NewznabSettings();
|
||||
newznab.Implementation = "Newznab";
|
||||
|
||||
indexers.Add(newznab);
|
||||
|
||||
return indexers;
|
||||
}
|
||||
|
||||
public Indexer Create(Indexer indexer)
|
||||
{
|
||||
var definition = new IndexerDefinition
|
||||
{
|
||||
Name = indexer.Name,
|
||||
Enable = indexer.Enable,
|
||||
Implementation = indexer.Implementation,
|
||||
Settings = indexer.Settings.ToJson()
|
||||
};
|
||||
|
||||
var instance = ToIndexer(definition).Instance;
|
||||
_newznabTestService.Test(instance);
|
||||
|
||||
definition = _indexerRepository.Insert(definition);
|
||||
indexer.Id = definition.Id;
|
||||
|
||||
return indexer;
|
||||
}
|
||||
|
||||
public Indexer Update(Indexer indexer)
|
||||
{
|
||||
var definition = _indexerRepository.Get(indexer.Id);
|
||||
definition.InjectFrom(indexer);
|
||||
definition.Settings = indexer.Settings.ToJson();
|
||||
_indexerRepository.Update(definition);
|
||||
|
||||
return indexer;
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
_indexerRepository.Delete(id);
|
||||
}
|
||||
|
||||
private Indexer ToIndexer(IndexerDefinition definition)
|
||||
{
|
||||
var indexer = new Indexer();
|
||||
indexer.Id = definition.Id;
|
||||
indexer.Enable = definition.Enable;
|
||||
indexer.Instance = GetInstance(definition);
|
||||
indexer.Name = definition.Name;
|
||||
indexer.Implementation = definition.Implementation;
|
||||
|
||||
if (indexer.Instance.GetType().GetMethod("ImportSettingsFromJson") != null)
|
||||
{
|
||||
indexer.Settings = ((dynamic)indexer.Instance).ImportSettingsFromJson(definition.Settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
indexer.Settings = NullSetting.Instance;
|
||||
}
|
||||
|
||||
return indexer;
|
||||
}
|
||||
|
||||
private IIndexer GetInstance(IndexerDefinition indexerDefinition)
|
||||
{
|
||||
var type = GetImplementation(indexerDefinition);
|
||||
var instance = (IIndexer)Activator.CreateInstance(type);
|
||||
instance.InstanceDefinition = indexerDefinition;
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Type GetImplementation(IndexerDefinition indexerDefinition)
|
||||
{
|
||||
return _indexers.Select(c => c.GetType()).SingleOrDefault(c => c.Name.Equals(indexerDefinition.Implementation, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public void Handle(ApplicationStartedEvent message)
|
||||
{
|
||||
_logger.Debug("Initializing indexers. Count {0}", _indexers.Count);
|
||||
|
||||
RemoveMissingImplementations();
|
||||
|
||||
var definitions = _indexers.SelectMany(indexer => indexer.DefaultDefinitions);
|
||||
|
||||
var currentIndexer = All();
|
||||
|
||||
var newIndexers = definitions.Where(def => currentIndexer.All(c => c.Implementation != def.Implementation)).ToList();
|
||||
|
||||
|
||||
if (newIndexers.Any())
|
||||
{
|
||||
_indexerRepository.InsertMany(newIndexers);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveMissingImplementations()
|
||||
{
|
||||
var storedIndexers = _indexerRepository.All();
|
||||
|
||||
foreach (var indexerDefinition in storedIndexers.Where(i => GetImplementation(i) == null))
|
||||
{
|
||||
_logger.Debug("Removing Indexer {0} ", indexerDefinition.Name);
|
||||
_indexerRepository.Delete(indexerDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public interface IProviderIndexerSetting
|
||||
{
|
||||
TSetting Get<TSetting>(IIndexer indexer) where TSetting : IIndexerSetting, new();
|
||||
}
|
||||
|
||||
public class IndexerSettingProvider : IProviderIndexerSetting
|
||||
{
|
||||
private readonly IIndexerRepository _indexerRepository;
|
||||
|
||||
public IndexerSettingProvider(IIndexerRepository indexerRepository)
|
||||
{
|
||||
_indexerRepository = indexerRepository;
|
||||
}
|
||||
|
||||
public TSetting Get<TSetting>(IIndexer indexer) where TSetting : IIndexerSetting, new()
|
||||
{
|
||||
var indexerDef = _indexerRepository.Find(indexer.Name);
|
||||
|
||||
if (indexerDef == null || string.IsNullOrWhiteSpace(indexerDef.Settings))
|
||||
{
|
||||
return new TSetting();
|
||||
}
|
||||
|
||||
return Json.Deserialize<TSetting>(indexerDef.Settings);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public class IndexerSettingUpdatedEvent : IEvent
|
||||
{
|
||||
public string IndexerName { get; private set; }
|
||||
public IIndexerSetting IndexerSetting { get; private set; }
|
||||
public IProviderConfig IndexerSetting { get; private set; }
|
||||
|
||||
public IndexerSettingUpdatedEvent(string indexerName, IIndexerSetting indexerSetting)
|
||||
public IndexerSettingUpdatedEvent(string indexerName, IProviderConfig indexerSetting)
|
||||
{
|
||||
IndexerName = indexerName;
|
||||
IndexerSetting = indexerSetting;
|
||||
|
@ -1,21 +0,0 @@
|
||||
using NzbDrone.Common.Serializer;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public abstract class IndexerWithSetting<TSetting> : IndexerBase where TSetting : class, IIndexerSetting, new()
|
||||
{
|
||||
public TSetting Settings { get; set; }
|
||||
|
||||
public override bool EnableByDefault
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public TSetting ImportSettingsFromJson(string json)
|
||||
{
|
||||
Settings = Json.Deserialize<TSetting>(json) ?? new TSetting();
|
||||
|
||||
return Settings;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
public class Newznab : IndexerWithSetting<NewznabSettings>
|
||||
public class Newznab : IndexerBase<NewznabSettings>
|
||||
{
|
||||
public override IParseFeed Parser
|
||||
{
|
||||
@ -15,7 +16,7 @@ public override IParseFeed Parser
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<IndexerDefinition> DefaultDefinitions
|
||||
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
@ -51,7 +52,7 @@ public override IEnumerable<IndexerDefinition> DefaultDefinitions
|
||||
}
|
||||
}
|
||||
|
||||
private string GetSettings(string url, List<int> categories)
|
||||
private NewznabSettings GetSettings(string url, List<int> categories)
|
||||
{
|
||||
var settings = new NewznabSettings { Url = url };
|
||||
|
||||
@ -60,7 +61,7 @@ private string GetSettings(string url, List<int> categories)
|
||||
settings.Categories = categories;
|
||||
}
|
||||
|
||||
return settings.ToJson();
|
||||
return settings;
|
||||
}
|
||||
|
||||
public override IEnumerable<string> RecentFeed
|
||||
@ -68,7 +69,7 @@ public override IEnumerable<string> RecentFeed
|
||||
get
|
||||
{
|
||||
//Todo: We should be able to update settings on start
|
||||
if (Name.Equals("nzbs.org", StringComparison.InvariantCultureIgnoreCase))
|
||||
if (Settings.Url.Contains("nzbs.org"))
|
||||
{
|
||||
Settings.Categories = new List<int> { 5000 };
|
||||
}
|
||||
@ -114,19 +115,11 @@ public override IEnumerable<string> GetSeasonSearchUrls(string seriesTitle, int
|
||||
return RecentFeed.Select(url => String.Format("{0}&limit=100&q={1}&season={2}&offset={3}", url, NewsnabifyTitle(seriesTitle), seasonNumber, offset));
|
||||
}
|
||||
|
||||
public override string Name
|
||||
public override DownloadProtocol Protocol
|
||||
{
|
||||
get
|
||||
{
|
||||
return InstanceDefinition.Name;
|
||||
}
|
||||
}
|
||||
|
||||
public override IndexerKind Kind
|
||||
{
|
||||
get
|
||||
{
|
||||
return IndexerKind.Usenet;
|
||||
return DownloadProtocol.Usenet;
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,4 +128,4 @@ private static string NewsnabifyTitle(string title)
|
||||
return title.Replace("+", "%20");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
@ -16,7 +17,7 @@ public NewznabSettingsValidator()
|
||||
}
|
||||
|
||||
|
||||
public class NewznabSettings : IIndexerSetting
|
||||
public class NewznabSettings : IProviderConfig
|
||||
{
|
||||
private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator();
|
||||
|
||||
|
@ -3,18 +3,13 @@
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Omgwtfnzbs
|
||||
{
|
||||
public class Omgwtfnzbs : IndexerWithSetting<OmgwtfnzbsSettings>
|
||||
public class Omgwtfnzbs : IndexerBase<OmgwtfnzbsSettings>
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "omgwtfnzbs"; }
|
||||
}
|
||||
|
||||
public override IndexerKind Kind
|
||||
public override DownloadProtocol Protocol
|
||||
{
|
||||
get
|
||||
{
|
||||
return IndexerKind.Usenet;
|
||||
return DownloadProtocol.Usenet;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Omgwtfnzbs
|
||||
{
|
||||
@ -14,7 +15,7 @@ public OmgwtfnzbsSettingsValidator()
|
||||
}
|
||||
}
|
||||
|
||||
public class OmgwtfnzbsSettings : IIndexerSetting
|
||||
public class OmgwtfnzbsSettings : IProviderConfig
|
||||
{
|
||||
private static readonly OmgwtfnzbsSettingsValidator Validator = new OmgwtfnzbsSettingsValidator();
|
||||
|
||||
|
@ -1,20 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Wombles
|
||||
{
|
||||
public class Wombles : IndexerBase
|
||||
public class Wombles : IndexerBase<NullConfig>
|
||||
{
|
||||
public override string Name
|
||||
{
|
||||
get { return "WomblesIndex"; }
|
||||
}
|
||||
|
||||
public override IndexerKind Kind
|
||||
public override DownloadProtocol Protocol
|
||||
{
|
||||
get
|
||||
{
|
||||
return IndexerKind.Usenet;
|
||||
return DownloadProtocol.Usenet;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Email
|
||||
{
|
||||
public class EmailSettings : INotifcationSettings
|
||||
public class EmailSettings : IProviderConfig
|
||||
{
|
||||
public EmailSettings()
|
||||
{
|
||||
@ -38,5 +40,10 @@ public bool IsValid
|
||||
return !string.IsNullOrWhiteSpace(Server) && Port > 0 && !string.IsNullOrWhiteSpace(From) && !string.IsNullOrWhiteSpace(To);
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Growl
|
||||
{
|
||||
public class GrowlSettings : INotifcationSettings
|
||||
public class GrowlSettings : IProviderConfig
|
||||
{
|
||||
public GrowlSettings()
|
||||
{
|
||||
@ -26,5 +28,10 @@ public bool IsValid
|
||||
return !string.IsNullOrWhiteSpace(Host) && !string.IsNullOrWhiteSpace(Password) && Port > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public interface INotifcationSettings
|
||||
{
|
||||
bool IsValid { get; }
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace NzbDrone.Core.Notifications
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public class Notification
|
||||
{
|
||||
@ -8,7 +10,7 @@ public class Notification
|
||||
public string Link { get; set; }
|
||||
public bool OnGrab { get; set; }
|
||||
public bool OnDownload { get; set; }
|
||||
public INotifcationSettings Settings { get; set; }
|
||||
public IProviderConfig Settings { get; set; }
|
||||
public INotification Instance { get; set; }
|
||||
public string Implementation { get; set; }
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public abstract class NotificationBase<TSetting> : INotification where TSetting : class, INotifcationSettings, new()
|
||||
public abstract class NotificationBase<TSetting> : INotification where TSetting : class, IProviderConfig, new()
|
||||
{
|
||||
public abstract string Name { get; }
|
||||
public abstract string ImplementationName { get; }
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
@ -11,4 +12,11 @@ public class NotificationDefinition : ModelBase
|
||||
public String Settings { get; set; }
|
||||
public String Implementation { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class NotificationProviderModel : ProviderDefinition
|
||||
{
|
||||
public Boolean OnGrab { get; set; }
|
||||
public Boolean OnDownload { get; set; }
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles.Events;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Tv;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
@ -18,7 +19,7 @@ public interface INotificationService
|
||||
Notification Get(int id);
|
||||
List<Notification> Schema();
|
||||
Notification Create(Notification notification);
|
||||
Notification Update(Notification notification);
|
||||
void Update(Notification notification);
|
||||
void Delete(int id);
|
||||
}
|
||||
|
||||
@ -71,7 +72,7 @@ public List<Notification> Schema()
|
||||
|
||||
var instanceType = newNotification.Instance.GetType();
|
||||
var baseGenArgs = instanceType.BaseType.GetGenericArguments();
|
||||
newNotification.Settings = (INotifcationSettings)Activator.CreateInstance(baseGenArgs[0]);
|
||||
newNotification.Settings = (IProviderConfig)Activator.CreateInstance(baseGenArgs[0]);
|
||||
newNotification.Implementation = type.Name;
|
||||
|
||||
notifications.Add(newNotification);
|
||||
@ -93,15 +94,13 @@ public Notification Create(Notification notification)
|
||||
return notification;
|
||||
}
|
||||
|
||||
public Notification Update(Notification notification)
|
||||
public void Update(Notification notification)
|
||||
{
|
||||
var definition = _notificationRepository.Get(notification.Id);
|
||||
definition.InjectFrom(notification);
|
||||
definition.Settings = notification.Settings.ToJson();
|
||||
|
||||
_notificationRepository.Update(definition);
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
|
@ -1,10 +1,11 @@
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications
|
||||
{
|
||||
public interface INotificationSettingsProvider
|
||||
{
|
||||
TSetting Get<TSetting>(INotification indexer) where TSetting : INotifcationSettings, new();
|
||||
TSetting Get<TSetting>(INotification indexer) where TSetting : IProviderConfig, new();
|
||||
}
|
||||
|
||||
public class NotificationSettingsProvider : INotificationSettingsProvider
|
||||
@ -16,7 +17,7 @@ public NotificationSettingsProvider(INotificationRepository notificationReposito
|
||||
_notificationRepository = notificationRepository;
|
||||
}
|
||||
|
||||
public TSetting Get<TSetting>(INotification indexer) where TSetting : INotifcationSettings, new()
|
||||
public TSetting Get<TSetting>(INotification indexer) where TSetting : IProviderConfig, new()
|
||||
{
|
||||
var indexerDef = _notificationRepository.Find(indexer.Name);
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.NotifyMyAndroid
|
||||
{
|
||||
public class NotifyMyAndroidSettings : INotifcationSettings
|
||||
public class NotifyMyAndroidSettings : IProviderConfig
|
||||
{
|
||||
[FieldDefinition(0, Label = "API Key", HelpLink = "http://www.notifymyandroid.com/")]
|
||||
public String ApiKey { get; set; }
|
||||
@ -18,5 +20,10 @@ public bool IsValid
|
||||
return !String.IsNullOrWhiteSpace(ApiKey) && Priority != null & Priority >= -1 && Priority <= 2;
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public class PlexClientSettings : INotifcationSettings
|
||||
public class PlexClientSettings : IProviderConfig
|
||||
{
|
||||
public PlexClientSettings()
|
||||
{
|
||||
@ -29,5 +31,10 @@ public bool IsValid
|
||||
return !string.IsNullOrWhiteSpace(Host);
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public class PlexServerSettings : INotifcationSettings
|
||||
public class PlexServerSettings : IProviderConfig
|
||||
{
|
||||
public PlexServerSettings()
|
||||
{
|
||||
@ -26,5 +28,10 @@ public bool IsValid
|
||||
return !string.IsNullOrWhiteSpace(Host);
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Prowl
|
||||
{
|
||||
public class ProwlSettings : INotifcationSettings
|
||||
public class ProwlSettings : IProviderConfig
|
||||
{
|
||||
[FieldDefinition(0, Label = "API Key", HelpLink = "https://www.prowlapp.com/api_settings.php")]
|
||||
public String ApiKey { get; set; }
|
||||
@ -18,5 +20,10 @@ public bool IsValid
|
||||
return !string.IsNullOrWhiteSpace(ApiKey) && Priority != null & Priority >= -2 && Priority <= 2;
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.PushBullet
|
||||
{
|
||||
public class PushBulletSettings : INotifcationSettings
|
||||
public class PushBulletSettings : IProviderConfig
|
||||
{
|
||||
[FieldDefinition(0, Label = "API Key", HelpLink = "https://www.pushbullet.com/")]
|
||||
public String ApiKey { get; set; }
|
||||
@ -18,5 +20,10 @@ public bool IsValid
|
||||
return !String.IsNullOrWhiteSpace(ApiKey) && DeviceId > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Pushover
|
||||
{
|
||||
public class PushoverSettings : INotifcationSettings
|
||||
public class PushoverSettings : IProviderConfig
|
||||
{
|
||||
[FieldDefinition(0, Label = "User Key", HelpLink = "https://pushover.net/")]
|
||||
public String UserKey { get; set; }
|
||||
@ -18,5 +20,10 @@ public bool IsValid
|
||||
return !string.IsNullOrWhiteSpace(UserKey) && Priority != null & Priority >= -1 && Priority <= 2;
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using FluentValidation.Results;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Xbmc
|
||||
{
|
||||
public class XbmcSettings : INotifcationSettings
|
||||
public class XbmcSettings : IProviderConfig
|
||||
{
|
||||
public XbmcSettings()
|
||||
{
|
||||
@ -51,5 +53,10 @@ public bool IsValid
|
||||
return !string.IsNullOrWhiteSpace(Host) && Port > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +141,7 @@
|
||||
<Compile Include="DataAugmentation\Xem\XemService.cs" />
|
||||
<Compile Include="Datastore\ConnectionStringFactory.cs" />
|
||||
<Compile Include="Datastore\Converters\BooleanIntConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\ProviderSettingConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\QualityIntConverter.cs" />
|
||||
<Compile Include="Datastore\Converters\Int32Converter.cs" />
|
||||
<Compile Include="Datastore\Converters\EmbeddedDocumentConverter.cs" />
|
||||
@ -172,6 +173,8 @@
|
||||
<Compile Include="Datastore\Migration\019_restore_unique_constraints.cs" />
|
||||
<Compile Include="Datastore\Migration\020_add_year_and_seasons_to_series.cs" />
|
||||
<Compile Include="Datastore\Migration\021_drop_seasons_table.cs" />
|
||||
<Compile Include="Datastore\Migration\022_move_notification_to_generic_provider.cs" />
|
||||
<Compile Include="Datastore\Migration\023_add_config_contract_to_indexers.cs" />
|
||||
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
|
||||
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
|
||||
<Compile Include="Datastore\Migration\Framework\MigrationExtension.cs" />
|
||||
@ -241,12 +244,10 @@
|
||||
<Compile Include="Indexers\IIndexer.cs" />
|
||||
<Compile Include="Indexers\IndexerSettingUpdatedEvent.cs" />
|
||||
<Compile Include="Indexers\NewznabTestService.cs" />
|
||||
<Compile Include="Indexers\IndexerWithSetting.cs" />
|
||||
<Compile Include="Indexers\IParseFeed.cs" />
|
||||
<Compile Include="Indexers\Newznab\NewznabException.cs" />
|
||||
<Compile Include="Indexers\Newznab\NewznabPreProcessor.cs" />
|
||||
<Compile Include="Indexers\Newznab\SizeParsingException.cs" />
|
||||
<Compile Include="Indexers\NullSetting.cs" />
|
||||
<Compile Include="Indexers\RssSyncCommand.cs" />
|
||||
<Compile Include="Indexers\XElementExtensions.cs" />
|
||||
<Compile Include="Instrumentation\Commands\ClearLogCommand.cs" />
|
||||
@ -343,13 +344,11 @@
|
||||
<Compile Include="Indexers\IndexerBase.cs" />
|
||||
<Compile Include="Indexers\IndexerDefinition.cs" />
|
||||
<Compile Include="Indexers\IndexerRepository.cs" />
|
||||
<Compile Include="Indexers\IndexerSettingProvider.cs" />
|
||||
<Compile Include="Indexers\Newznab\Newznab.cs" />
|
||||
<Compile Include="Indexers\Newznab\NewznabSettings.cs" />
|
||||
<Compile Include="Indexers\Newznab\NewznabParser.cs" />
|
||||
<Compile Include="Indexers\Omgwtfnzbs\Omgwtfnzbs.cs" />
|
||||
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsParser.cs" />
|
||||
<Compile Include="Indexers\IIndexerSetting.cs" />
|
||||
<Compile Include="Indexers\Omgwtfnzbs\OmgwtfnzbsSettings.cs" />
|
||||
<Compile Include="Indexers\Wombles\Wombles.cs" />
|
||||
<Compile Include="Indexers\Wombles\WomblesParser.cs" />
|
||||
@ -371,7 +370,6 @@
|
||||
<Compile Include="MetadataSource\Trakt\Images.cs" />
|
||||
<Compile Include="MetadataSource\Trakt\Season.cs" />
|
||||
<Compile Include="MetadataSource\Trakt\FullShow.cs" />
|
||||
<Compile Include="Notifications\INotifcationSettings.cs" />
|
||||
<Compile Include="Notifications\Plex\TestPlexServerCommand.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexServer.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexClientSettings.cs" />
|
||||
@ -417,6 +415,8 @@
|
||||
<Compile Include="Parser\Parser.cs" />
|
||||
<Compile Include="Parser\ParsingService.cs" />
|
||||
<Compile Include="Parser\QualityParser.cs" />
|
||||
<Compile Include="ThingiProvider\ConfigContractNotFoundException.cs" />
|
||||
<Compile Include="ThingiProvider\IProvider.cs" />
|
||||
<Compile Include="Qualities\QualityProfileInUseException.cs" />
|
||||
<Compile Include="Qualities\QualitySizeRepository.cs" />
|
||||
<Compile Include="Qualities\QualityProfileRepository.cs" />
|
||||
@ -427,6 +427,13 @@
|
||||
<Compile Include="Rest\RestSharpExtensions.cs" />
|
||||
<Compile Include="Rest\RestException.cs" />
|
||||
<Compile Include="SeriesStats\SeriesStatisticsService.cs" />
|
||||
<Compile Include="ThingiProvider\IProviderConfig.cs" />
|
||||
<Compile Include="ThingiProvider\IProviderFactory.cs" />
|
||||
<Compile Include="ThingiProvider\IProviderRepository.cs" />
|
||||
<Compile Include="ThingiProvider\NullConfig.cs" />
|
||||
<Compile Include="ThingiProvider\ProviderDefinition.cs" />
|
||||
<Compile Include="ThingiProvider\ProviderRepository.cs" />
|
||||
<Compile Include="ThingiProvider\ProviderFactory.cs" />
|
||||
<Compile Include="Tv\EpisodeService.cs" />
|
||||
<Compile Include="Tv\Events\EpisodeInfoDeletedEvent.cs" />
|
||||
<Compile Include="Tv\Events\EpisodeInfoUpdatedEvent.cs" />
|
||||
@ -505,7 +512,7 @@
|
||||
<Compile Include="History\HistoryService.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Indexers\IndexerService.cs">
|
||||
<Compile Include="Indexers\IndexerFactory.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Indexers\IndexerFetchService.cs">
|
||||
|
@ -13,7 +13,7 @@
|
||||
<AnalyseExecutionTimes>true</AnalyseExecutionTimes>
|
||||
<IncludeStaticReferencesInWorkspace>true</IncludeStaticReferencesInWorkspace>
|
||||
<DefaultTestTimeout>5000</DefaultTestTimeout>
|
||||
<UseBuildConfiguration>Debug</UseBuildConfiguration>
|
||||
<UseBuildConfiguration>RELEASE</UseBuildConfiguration>
|
||||
<UseBuildPlatform>x86</UseBuildPlatform>
|
||||
<ProxyProcessPath></ProxyProcessPath>
|
||||
<UseCPUArchitecture>x86</UseCPUArchitecture>
|
||||
|
@ -0,0 +1,13 @@
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public class ConfigContractNotFoundException : NzbDroneException
|
||||
{
|
||||
public ConfigContractNotFoundException(string contract)
|
||||
: base("Couldn't find config contract " + contract)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
14
NzbDrone.Core/ThingiProvider/IProvider.cs
Normal file
14
NzbDrone.Core/ThingiProvider/IProvider.cs
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public interface IProvider
|
||||
{
|
||||
Type ConfigContract { get; }
|
||||
|
||||
IEnumerable<ProviderDefinition> DefaultDefinitions { get; }
|
||||
ProviderDefinition Definition { get; set; }
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public interface IIndexerSetting
|
||||
public interface IProviderConfig
|
||||
{
|
||||
ValidationResult Validate();
|
||||
}
|
||||
}
|
||||
}
|
17
NzbDrone.Core/ThingiProvider/IProviderFactory.cs
Normal file
17
NzbDrone.Core/ThingiProvider/IProviderFactory.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public interface IProviderFactory<TProvider, TProviderDefinition>
|
||||
where TProviderDefinition : ProviderDefinition, new()
|
||||
where TProvider : IProvider
|
||||
{
|
||||
List<TProviderDefinition> All();
|
||||
List<TProvider> GetAvailableProviders();
|
||||
TProviderDefinition Get(int id);
|
||||
TProviderDefinition Create(TProviderDefinition indexer);
|
||||
void Update(TProviderDefinition indexer);
|
||||
void Delete(int id);
|
||||
List<TProviderDefinition> Templates();
|
||||
}
|
||||
}
|
8
NzbDrone.Core/ThingiProvider/IProviderRepository.cs
Normal file
8
NzbDrone.Core/ThingiProvider/IProviderRepository.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public interface IProviderRepository<TProvider> : IBasicRepository<TProvider> where TProvider : ModelBase, new()
|
||||
{
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public class NullSetting : IIndexerSetting
|
||||
public class NullConfig : IProviderConfig
|
||||
{
|
||||
public static readonly NullSetting Instance = new NullSetting();
|
||||
public static readonly NullConfig Instance = new NullConfig();
|
||||
|
||||
public ValidationResult Validate()
|
||||
{
|
30
NzbDrone.Core/ThingiProvider/ProviderDefinition.cs
Normal file
30
NzbDrone.Core/ThingiProvider/ProviderDefinition.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using NzbDrone.Core.Datastore;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public abstract class ProviderDefinition : ModelBase
|
||||
{
|
||||
private IProviderConfig _settings;
|
||||
public string Name { get; set; }
|
||||
public string Implementation { get; set; }
|
||||
public bool Enable { get; set; }
|
||||
|
||||
public string ConfigContract { get; set; }
|
||||
|
||||
public IProviderConfig Settings
|
||||
{
|
||||
get
|
||||
{
|
||||
return _settings;
|
||||
}
|
||||
set
|
||||
{
|
||||
_settings = value;
|
||||
if (value != null)
|
||||
{
|
||||
ConfigContract = value.GetType().Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
104
NzbDrone.Core/ThingiProvider/ProviderFactory.cs
Normal file
104
NzbDrone.Core/ThingiProvider/ProviderFactory.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public abstract class ProviderFactory<TProvider, TProviderDefinition> : IProviderFactory<TProvider, TProviderDefinition>, IHandle<ApplicationStartedEvent>
|
||||
where TProviderDefinition : ProviderDefinition, new()
|
||||
where TProvider : IProvider
|
||||
{
|
||||
private readonly IProviderRepository<TProviderDefinition> _providerRepository;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly List<TProvider> _providers;
|
||||
|
||||
protected ProviderFactory(IProviderRepository<TProviderDefinition> providerRepository, IEnumerable<TProvider> providers, Logger logger)
|
||||
{
|
||||
_providerRepository = providerRepository;
|
||||
_providers = providers.ToList();
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<TProviderDefinition> All()
|
||||
{
|
||||
return _providerRepository.All().ToList();
|
||||
}
|
||||
|
||||
public List<TProviderDefinition> Templates()
|
||||
{
|
||||
return _providers.Select(p => new TProviderDefinition()
|
||||
{
|
||||
ConfigContract = p.ConfigContract.Name,
|
||||
Implementation = p.GetType().Name,
|
||||
Settings = (IProviderConfig)Activator.CreateInstance(p.ConfigContract)
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public List<TProvider> GetAvailableProviders()
|
||||
{
|
||||
return All().Where(c => c.Enable && c.Settings.Validate().IsValid)
|
||||
.Select(GetInstance).ToList();
|
||||
}
|
||||
|
||||
public TProviderDefinition Get(int id)
|
||||
{
|
||||
return _providerRepository.Get(id);
|
||||
}
|
||||
|
||||
public TProviderDefinition Create(TProviderDefinition provider)
|
||||
{
|
||||
return _providerRepository.Insert(provider);
|
||||
}
|
||||
|
||||
public void Update(TProviderDefinition definition)
|
||||
{
|
||||
_providerRepository.Update(definition);
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
_providerRepository.Delete(id);
|
||||
}
|
||||
|
||||
private TProvider GetInstance(TProviderDefinition definition)
|
||||
{
|
||||
var type = GetImplementation(definition);
|
||||
var instance = (TProvider)Activator.CreateInstance(type);
|
||||
instance.Definition = definition;
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Type GetImplementation(TProviderDefinition definition)
|
||||
{
|
||||
return _providers.Select(c => c.GetType()).SingleOrDefault(c => c.Name.Equals(definition.Implementation, StringComparison.InvariantCultureIgnoreCase));
|
||||
}
|
||||
|
||||
public void Handle(ApplicationStartedEvent message)
|
||||
{
|
||||
_logger.Debug("Initializing Providers. Count {0}", _providers.Count);
|
||||
|
||||
RemoveMissingImplementations();
|
||||
|
||||
InitializeProviders();
|
||||
}
|
||||
|
||||
protected virtual void InitializeProviders()
|
||||
{
|
||||
}
|
||||
|
||||
private void RemoveMissingImplementations()
|
||||
{
|
||||
var storedProvider = _providerRepository.All();
|
||||
|
||||
foreach (var invalidDefinition in storedProvider.Where(def => GetImplementation(def) == null))
|
||||
{
|
||||
_logger.Debug("Removing {0} ", invalidDefinition.Name);
|
||||
_providerRepository.Delete(invalidDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
NzbDrone.Core/ThingiProvider/ProviderRepository.cs
Normal file
16
NzbDrone.Core/ThingiProvider/ProviderRepository.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.ThingiProvider
|
||||
{
|
||||
public class ProviderRepository<TProviderDefinition> : BasicRepository<TProviderDefinition>, IProviderRepository<TProviderDefinition>
|
||||
where TProviderDefinition : ModelBase,
|
||||
new()
|
||||
{
|
||||
protected ProviderRepository(IDatabase database, IEventAggregator eventAggregator)
|
||||
: base(database, eventAggregator)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
using FluentAssertions;
|
||||
using System.Linq;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Integration.Test
|
||||
{
|
||||
@ -13,6 +15,7 @@ public void should_have_built_in_indexer()
|
||||
|
||||
indexers.Should().NotBeEmpty();
|
||||
indexers.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name));
|
||||
indexers.Where(c => c.ConfigContract == typeof(NullConfig).Name).Should().OnlyContain(c => c.Enable);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using FluentAssertions;
|
||||
using System.Threading;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Api.Indexers;
|
||||
|
||||
@ -10,10 +11,13 @@ public class ReleaseIntegrationTest : IntegrationTest
|
||||
[Test]
|
||||
public void should_only_have_unknown_series_releases()
|
||||
{
|
||||
|
||||
var releases = Releases.All();
|
||||
var indexers = Indexers.All();
|
||||
|
||||
|
||||
releases.Should().OnlyContain(c => c.Rejections.Contains("Unknown Series"));
|
||||
releases.Should().OnlyContain(c=>BeValidRelease(c));
|
||||
releases.Should().OnlyContain(c => BeValidRelease(c));
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<FrameworkUtilisationTypeForGallio>Disabled</FrameworkUtilisationTypeForGallio>
|
||||
<FrameworkUtilisationTypeForMSpec>Disabled</FrameworkUtilisationTypeForMSpec>
|
||||
<FrameworkUtilisationTypeForMSTest>Disabled</FrameworkUtilisationTypeForMSTest>
|
||||
<EngineModes>Run all tests automatically:BFRydWU=;Run all tests manually:BUZhbHNl;Run impacted tests automatically, others manually (experimental!):CklzSW1wYWN0ZWQ=;Run pinned tests automatically, others manually:CElzUGlubmVk;Fast:DlN0cnVjdHVyYWxOb2RlBAAAABNEb2VzTm90SGF2ZUNhdGVnb3J5D0ludGVncmF0aW9uVGVzdBNEb2VzTm90SGF2ZUNhdGVnb3J5BkRiVGVzdApJc0ltcGFjdGVkE0RvZXNOb3RIYXZlQ2F0ZWdvcnkORGlza0FjY2Vzc1Rlc3QAAAAAAAAAAAAAAAA=</EngineModes>
|
||||
<EngineModes>Run all tests automatically:BFRydWU=;Run all tests manually:BUZhbHNl;Run impacted tests automatically, others manually (experimental!):CklzSW1wYWN0ZWQ=;Run pinned tests automatically, others manually:CElzUGlubmVk;Fast:DlN0cnVjdHVyYWxOb2RlBQAAABNEb2VzTm90SGF2ZUNhdGVnb3J5D0ludGVncmF0aW9uVGVzdBNEb2VzTm90SGF2ZUNhdGVnb3J5BkRiVGVzdApJc0ltcGFjdGVkE0RvZXNOb3RIYXZlQ2F0ZWdvcnkORGlza0FjY2Vzc1Rlc3QISXNQaW5uZWQAAAAAAAAAAAAAAAABAAAA</EngineModes>
|
||||
<MetricsExclusionList>
|
||||
</MetricsExclusionList>
|
||||
</SolutionConfiguration>
|
Loading…
Reference in New Issue
Block a user