diff --git a/Gruntfile.js b/Gruntfile.js index f78680b09..d78fac4da 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -52,9 +52,10 @@ module.exports = function (grunt) { options:{ dumpLineNumbers : 'false', - compress : false, + compress : true, yuicompress : false, - ieCompat : false + ieCompat : true, + strictImports : true }, bootstrap: { diff --git a/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs b/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs index f68bcb2ef..bf3d72d11 100644 --- a/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs +++ b/NzbDrone.Api.Test/ClientSchemaTests/SchemaBuilderFixture.cs @@ -28,8 +28,8 @@ public void schema_should_have_proper_fields() var schema = SchemaBuilder.GenerateSchema(model); - schema.Should().Contain(c => c.Order == 1 && c.Name == "LastName" && c.Label == "Last Name" && c.HelpText == "Your Last Name" && c.Value == "Poop"); - schema.Should().Contain(c => c.Order == 0 && c.Name == "FirstName" && c.Label == "First Name" && c.HelpText == "Your First Name" && c.Value == "Bob"); + 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"); } } diff --git a/NzbDrone.Api.Test/DirectoryLookupServiceFixture.cs b/NzbDrone.Api.Test/DirectoryLookupServiceFixture.cs index 7399a8f82..849773ddb 100644 --- a/NzbDrone.Api.Test/DirectoryLookupServiceFixture.cs +++ b/NzbDrone.Api.Test/DirectoryLookupServiceFixture.cs @@ -37,9 +37,6 @@ public void Setup() "Windows" }; - Mocker.GetMock() - .SetupGet(s => s.SpecialFolders) - .Returns(new HashSet { "$recycle.bin", "system volume information", "recycler" }); } private void SetupFolders(string root) diff --git a/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs b/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs index 2aaf97b79..82fb9425a 100644 --- a/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs +++ b/NzbDrone.Api.Test/MappingTests/ResourceMappingFixture.cs @@ -4,6 +4,7 @@ using FluentAssertions; using Marr.Data; using NUnit.Framework; +using NzbDrone.Api.Commands; using NzbDrone.Api.Config; using NzbDrone.Api.Episodes; using NzbDrone.Api.History; @@ -13,16 +14,16 @@ using NzbDrone.Api.Qualities; using NzbDrone.Api.RootFolders; using NzbDrone.Api.Series; -using NzbDrone.Api.Update; -using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Indexers; using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; using NzbDrone.Core.RootFolders; using NzbDrone.Core.Tv; -using NzbDrone.Core.Update; +using NzbDrone.Core.Update.Commands; using NzbDrone.Test.Common; using System.Linq; @@ -36,13 +37,13 @@ public class ResourceMappingFixture : TestBase [TestCase(typeof(RootFolder), typeof(RootFolderResource))] [TestCase(typeof(NamingConfig), typeof(NamingConfigResource))] [TestCase(typeof(Indexer), typeof(IndexerResource))] - [TestCase(typeof(ReportInfo), typeof(ReleaseResource))] + [TestCase(typeof(ReleaseInfo), typeof(ReleaseResource))] [TestCase(typeof(ParsedEpisodeInfo), typeof(ReleaseResource))] [TestCase(typeof(DownloadDecision), typeof(ReleaseResource))] [TestCase(typeof(Core.History.History), typeof(HistoryResource))] - [TestCase(typeof(UpdatePackage), typeof(UpdateResource))] [TestCase(typeof(Quality), typeof(QualityResource))] [TestCase(typeof(Log), typeof(LogResource))] + [TestCase(typeof(Command), typeof(CommandResource))] public void matching_fields(Type modelType, Type resourceType) { MappingValidation.ValidateMapping(modelType, resourceType); @@ -116,6 +117,15 @@ public void should_map_qualityprofile() profileResource.InjectTo(); } + + + + [Test] + public void should_map_tracked_command() + { + var profileResource = new ApplicationUpdateCommand(); + profileResource.InjectTo(); + } } public class ModelWithLazy diff --git a/NzbDrone.Api/Commands/CommandModule.cs b/NzbDrone.Api/Commands/CommandModule.cs index 371483cc3..231089734 100644 --- a/NzbDrone.Api/Commands/CommandModule.cs +++ b/NzbDrone.Api/Commands/CommandModule.cs @@ -1,37 +1,69 @@ using System; +using System.Collections.Generic; using System.Linq; -using Nancy; using NzbDrone.Api.Extensions; +using NzbDrone.Api.Mapping; +using NzbDrone.Api.Validation; using NzbDrone.Common.Composition; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Datastore.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Commands.Tracking; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.ProgressMessaging; + namespace NzbDrone.Api.Commands { - public class CommandModule : NzbDroneRestModule + public class CommandModule : NzbDroneRestModuleWithSignalR, IHandle { - private readonly IMessageAggregator _messageAggregator; + private readonly ICommandExecutor _commandExecutor; private readonly IContainer _container; + private readonly ITrackCommands _trackCommands; - public CommandModule(IMessageAggregator messageAggregator, IContainer container) + public CommandModule(ICommandExecutor commandExecutor, IContainer container, ITrackCommands trackCommands) + : base(commandExecutor) { - _messageAggregator = messageAggregator; + _commandExecutor = commandExecutor; _container = container; + _trackCommands = trackCommands; - Post["/"] = x => RunCommand(ReadResourceFromRequest()); + GetResourceById = GetCommand; + CreateResource = StartCommand; + GetResourceAll = GetAllCommands; + PostValidator.RuleFor(c => c.Name).NotBlank(); } - private Response RunCommand(CommandResource resource) + private CommandResource GetCommand(int id) + { + return _trackCommands.GetById(id).InjectTo(); + } + + private int StartCommand(CommandResource commandResource) { var commandType = - _container.GetImplementations(typeof(ICommand)) - .Single(c => c.Name.Replace("Command", "") - .Equals(resource.Command, StringComparison.InvariantCultureIgnoreCase)); + _container.GetImplementations(typeof(Command)) + .Single(c => c.Name.Replace("Command", "") + .Equals(commandResource.Name, StringComparison.InvariantCultureIgnoreCase)); dynamic command = Request.Body.FromJson(commandType); - _messageAggregator.PublishCommand(command); - return resource.AsResponse(HttpStatusCode.Created); + var trackedCommand = (Command)_commandExecutor.PublishCommandAsync(command); + return trackedCommand.Id; + } + + private List GetAllCommands() + { + return ToListResource(_trackCommands.RunningCommands); + } + + public void Handle(CommandUpdatedEvent message) + { + if (message.Command.SendUpdatesToClient) + { + BroadcastResourceChange(ModelAction.Updated, message.Command.Id); + } } } } \ No newline at end of file diff --git a/NzbDrone.Api/Commands/CommandResource.cs b/NzbDrone.Api/Commands/CommandResource.cs index e71a0d08f..86c1f4b15 100644 --- a/NzbDrone.Api/Commands/CommandResource.cs +++ b/NzbDrone.Api/Commands/CommandResource.cs @@ -1,9 +1,16 @@ -using NzbDrone.Api.REST; +using System; +using NzbDrone.Api.REST; +using NzbDrone.Core.Messaging.Commands.Tracking; namespace NzbDrone.Api.Commands { public class CommandResource : RestResource { - public string Command { get; set; } + public String Name { get; set; } + public String Message { get; set; } + public DateTime StartedOn { get; set; } + public DateTime StateChangeTime { get; set; } + public Boolean SendUpdatesToClient { get; set; } + public CommandStatus State { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Api/Episodes/EpisodeConnection.cs b/NzbDrone.Api/Episodes/EpisodeConnection.cs deleted file mode 100644 index 814c6dc84..000000000 --- a/NzbDrone.Api/Episodes/EpisodeConnection.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Microsoft.AspNet.SignalR; -using Microsoft.AspNet.SignalR.Infrastructure; -using NzbDrone.Api.SignalR; -using NzbDrone.Common.Messaging; -using NzbDrone.Core.Download; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Api.Episodes -{ - public class EpisodeConnection : BasicResourceConnection, IHandleAsync - { - public override string Resource - { - get { return "/Episodes"; } - } - - public void HandleAsync(EpisodeGrabbedEvent message) - { - var context = ((ConnectionManager)GlobalHost.ConnectionManager).GetConnection(GetType()); - context.Connection.Broadcast(message); - } - } -} diff --git a/NzbDrone.Api/ErrorManagement/ApiException.cs b/NzbDrone.Api/ErrorManagement/ApiException.cs index 0d5842d19..2a9f2678f 100644 --- a/NzbDrone.Api/ErrorManagement/ApiException.cs +++ b/NzbDrone.Api/ErrorManagement/ApiException.cs @@ -9,7 +9,6 @@ public abstract class ApiException : Exception { public object Content { get; private set; } - public HttpStatusCode StatusCode { get; private set; } protected ApiException(HttpStatusCode statusCode, object content = null) diff --git a/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs b/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs index 9db64b37d..47d79c849 100644 --- a/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs +++ b/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs @@ -3,6 +3,7 @@ using NLog; using Nancy; using NzbDrone.Api.Extensions; +using NzbDrone.Core; using HttpStatusCode = Nancy.HttpStatusCode; namespace NzbDrone.Api.ErrorManagement @@ -35,9 +36,20 @@ public Response HandleException(NancyContext context, Exception exception) return validationException.Errors.AsResponse(HttpStatusCode.BadRequest); } + var clientException = exception as NzbDroneClientException; + + if (clientException != null) + { + return new ErrorModel + { + Message = exception.Message, + Description = exception.ToString() + }.AsResponse((HttpStatusCode)clientException.StatusCode); + } + _logger.FatalException("Request Failed", exception); - return new ErrorModel() + return new ErrorModel { Message = exception.Message, Description = exception.ToString() diff --git a/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs b/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs index 70f7fc0bf..183326415 100644 --- a/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs +++ b/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs @@ -1,5 +1,4 @@ -using System; -using Nancy; +using Nancy; using Nancy.Bootstrapper; using NzbDrone.Api.Frontend; diff --git a/NzbDrone.Api/Extensions/ReqResExtensions.cs b/NzbDrone.Api/Extensions/ReqResExtensions.cs index fd9980347..1f1d89180 100644 --- a/NzbDrone.Api/Extensions/ReqResExtensions.cs +++ b/NzbDrone.Api/Extensions/ReqResExtensions.cs @@ -12,7 +12,6 @@ public static class ReqResExtensions { private static readonly NancyJsonSerializer NancySerializer = new NancyJsonSerializer(); - public static readonly string LastModified = BuildInfo.BuildDateTime.ToString("r"); public static T FromJson(this Stream body) where T : class, new() @@ -25,7 +24,6 @@ public static T FromJson(this Stream body, Type type) return (T)FromJson(body, type); } - public static object FromJson(this Stream body, Type type) { var reader = new StreamReader(body, true); diff --git a/NzbDrone.Api/Indexers/IndexerModule.cs b/NzbDrone.Api/Indexers/IndexerModule.cs index 8a4439b87..02215d3ed 100644 --- a/NzbDrone.Api/Indexers/IndexerModule.cs +++ b/NzbDrone.Api/Indexers/IndexerModule.cs @@ -6,7 +6,6 @@ using NzbDrone.Core.Indexers; using Omu.ValueInjecter; using FluentValidation; -using NzbDrone.Api.Extensions; using NzbDrone.Api.Mapping; namespace NzbDrone.Api.Indexers diff --git a/NzbDrone.Api/Indexers/ReleaseModule.cs b/NzbDrone.Api/Indexers/ReleaseModule.cs index aaf452261..f00d30542 100644 --- a/NzbDrone.Api/Indexers/ReleaseModule.cs +++ b/NzbDrone.Api/Indexers/ReleaseModule.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using Nancy; +using NLog; using NzbDrone.Api.Mapping; using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Download; using NzbDrone.Core.IndexerSearch; using NzbDrone.Core.Indexers; @@ -23,10 +25,10 @@ public class ReleaseModule : NzbDroneRestModule private readonly IParsingService _parsingService; public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, - ISearchForNzb nzbSearchService, - IMakeDownloadDecision downloadDecisionMaker, - IDownloadService downloadService, - IParsingService parsingService) + ISearchForNzb nzbSearchService, + IMakeDownloadDecision downloadDecisionMaker, + IDownloadService downloadService, + IParsingService parsingService) { _rssFetcherAndParser = rssFetcherAndParser; _nzbSearchService = nzbSearchService; @@ -40,7 +42,7 @@ public ReleaseModule(IFetchAndParseRss rssFetcherAndParser, private Response DownloadRelease(ReleaseResource release) { var remoteEpisode = _parsingService.Map(release.InjectTo(), 0); - remoteEpisode.Report = release.InjectTo(); + remoteEpisode.Release = release.InjectTo(); _downloadService.DownloadReport(remoteEpisode); @@ -60,6 +62,7 @@ private List GetReleases() private List GetEpisodeReleases(int episodeId) { var decisions = _nzbSearchService.EpisodeSearch(episodeId); + return MapDecisions(decisions); } @@ -79,7 +82,7 @@ private static List MapDecisions(IEnumerable { var release = new ReleaseResource(); - release.InjectFrom(downloadDecision.RemoteEpisode.Report); + release.InjectFrom(downloadDecision.RemoteEpisode.Release); release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo); release.InjectFrom(downloadDecision); release.Rejections = downloadDecision.Rejections.ToList(); diff --git a/NzbDrone.Api/Indexers/ReleaseResource.cs b/NzbDrone.Api/Indexers/ReleaseResource.cs index 3db66d39f..3cc4a054a 100644 --- a/NzbDrone.Api/Indexers/ReleaseResource.cs +++ b/NzbDrone.Api/Indexers/ReleaseResource.cs @@ -12,8 +12,6 @@ public class ReleaseResource : RestResource public Int32 Age { get; set; } public Int64 Size { get; set; } public String Indexer { get; set; } - public String NzbInfoUrl { get; set; } - public String NzbUrl { get; set; } public String ReleaseGroup { get; set; } public String Title { get; set; } public Boolean FullSeason { get; set; } @@ -26,5 +24,9 @@ public class ReleaseResource : RestResource public Boolean Approved { get; set; } public Int32 TvRageId { get; set; } public List Rejections { get; set; } + public DateTime PublishDate { get; set; } + public String CommentUrl { get; set; } + public String DownloadUrl { get; set; } + public String InfoUrl { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Api/Logs/LogModule.cs b/NzbDrone.Api/Logs/LogModule.cs index e1c39d92b..59ea4975d 100644 --- a/NzbDrone.Api/Logs/LogModule.cs +++ b/NzbDrone.Api/Logs/LogModule.cs @@ -17,6 +17,12 @@ public LogModule(ILogService logService) private PagingResource GetLogs(PagingResource pagingResource) { var pageSpec = pagingResource.InjectTo>(); + + if (pageSpec.SortKey == "time") + { + pageSpec.SortKey = "id"; + } + return ApplyToPage(_logService.Paged, pageSpec); } } diff --git a/NzbDrone.Api/Mapping/CloneInjection.cs b/NzbDrone.Api/Mapping/CloneInjection.cs index 71bbd4bbe..1266ebd0b 100644 --- a/NzbDrone.Api/Mapping/CloneInjection.cs +++ b/NzbDrone.Api/Mapping/CloneInjection.cs @@ -17,9 +17,10 @@ protected override bool Match(ConventionInfo conventionInfo) protected override object SetValue(ConventionInfo conventionInfo) { - if (conventionInfo.SourceProp.Type.IsValueType || conventionInfo.SourceProp.Type == typeof(string)) + if (conventionInfo.SourceProp.Type == conventionInfo.TargetProp.Type) return conventionInfo.SourceProp.Value; + if (conventionInfo.SourceProp.Type.IsArray) { var array = (Array)conventionInfo.SourceProp.Value; diff --git a/NzbDrone.Api/NancyBootstrapper.cs b/NzbDrone.Api/NancyBootstrapper.cs index 5f65c80e2..263c4f265 100644 --- a/NzbDrone.Api/NancyBootstrapper.cs +++ b/NzbDrone.Api/NancyBootstrapper.cs @@ -1,14 +1,14 @@ -using System; -using NLog; +using NLog; using Nancy.Bootstrapper; using Nancy.Diagnostics; using NzbDrone.Api.Authentication; using NzbDrone.Api.ErrorManagement; -using NzbDrone.Api.Extensions; using NzbDrone.Api.Extensions.Pipelines; -using NzbDrone.Common.Messaging; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using TinyIoC; namespace NzbDrone.Api @@ -21,20 +21,18 @@ public class NancyBootstrapper : TinyIoCNancyBootstrapper public NancyBootstrapper(TinyIoCContainer tinyIoCContainer) { _tinyIoCContainer = tinyIoCContainer; - _logger = LogManager.GetCurrentClassLogger(); + _logger = NzbDroneLogger.GetLogger(); } protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { _logger.Info("Starting NzbDrone API"); - RegisterPipelines(pipelines); container.Resolve().Register(); container.Resolve().Register(pipelines); - container.Resolve().PublishEvent(new ApplicationStartedEvent()); - + container.Resolve().PublishEvent(new ApplicationStartedEvent()); ApplicationPipelines.OnError.AddItemToEndOfPipeline(container.Resolve().HandleException); } @@ -47,10 +45,8 @@ private void RegisterPipelines(IPipelines pipelines) { registerNancyPipeline.Register(pipelines); } - } - protected override TinyIoCContainer GetApplicationContainer() { return _tinyIoCContainer; diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index d0a03bd17..a4fb4ee50 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -91,7 +91,6 @@ - @@ -123,6 +122,8 @@ + + @@ -135,10 +136,6 @@ - - - - @@ -156,11 +153,6 @@ - - - - - @@ -183,6 +175,10 @@ {ff5ee3b6-913b-47ce-9ceb-11c51b4e1205} NzbDrone.Core + + {7c2cc69f-5ca0-4e5c-85cb-983f9f6c3b36} + NzbDrone.SignalR + @@ -194,4 +190,4 @@ --> - \ No newline at end of file + diff --git a/NzbDrone.Api/NzbDrone.Api.ncrunchproject b/NzbDrone.Api/NzbDrone.Api.ncrunchproject index 575717ac8..1a2228e7f 100644 --- a/NzbDrone.Api/NzbDrone.Api.ncrunchproject +++ b/NzbDrone.Api/NzbDrone.Api.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,9 +12,11 @@ false true true - 60000 - - - - AutoDetect + 5000 + Debug + x86 + + x86 + STA + x86 \ No newline at end of file diff --git a/NzbDrone.Api/NzbDroneRestModule.cs b/NzbDrone.Api/NzbDroneRestModule.cs index b4b01aee5..5c297e331 100644 --- a/NzbDrone.Api/NzbDroneRestModule.cs +++ b/NzbDrone.Api/NzbDroneRestModule.cs @@ -9,6 +9,8 @@ namespace NzbDrone.Api { public abstract class NzbDroneRestModule : RestModule where TResource : RestResource, new() { + protected string Resource { get; private set; } + protected NzbDroneRestModule() : this(new TResource().ResourceName) { @@ -17,6 +19,7 @@ protected NzbDroneRestModule() protected NzbDroneRestModule(string resource) : base("/api/" + resource.Trim('/')) { + Resource = resource; PostValidator.RuleFor(r => r.Id).IsZero(); PutValidator.RuleFor(r => r.Id).ValidId(); } @@ -28,7 +31,7 @@ protected NzbDroneRestModule(string resource) return model.Id; } - protected List ToListResource(Func> function) where TModel : ModelBase, new() + protected List ToListResource(Func> function) where TModel : class { var modelList = function(); return modelList.InjectTo>(); diff --git a/NzbDrone.Api/NzbDroneRestModuleWithSignalR.cs b/NzbDrone.Api/NzbDroneRestModuleWithSignalR.cs new file mode 100644 index 000000000..d8350b8ae --- /dev/null +++ b/NzbDrone.Api/NzbDroneRestModuleWithSignalR.cs @@ -0,0 +1,56 @@ +using NzbDrone.Api.REST; +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.SignalR; + +namespace NzbDrone.Api +{ + public abstract class NzbDroneRestModuleWithSignalR : NzbDroneRestModule, IHandle> + where TResource : RestResource, new() + where TModel : ModelBase + { + private readonly ICommandExecutor _commandExecutor; + + protected NzbDroneRestModuleWithSignalR(ICommandExecutor commandExecutor) + { + _commandExecutor = commandExecutor; + } + + public void Handle(ModelEvent message) + { + if (message.Action == ModelAction.Deleted || message.Action == ModelAction.Sync) + { + BroadcastResourceChange(message.Action); + } + + BroadcastResourceChange(message.Action, message.Model.Id); + } + + protected void BroadcastResourceChange(ModelAction action, int id) + { + var resource = GetResourceById(id); + + var signalRMessage = new SignalRMessage + { + Name = Resource, + Body = new ResourceChangeMessage(resource, action) + }; + + _commandExecutor.PublishCommand(new BroadcastSignalRMessage(signalRMessage)); + } + + protected void BroadcastResourceChange(ModelAction action) + { + var signalRMessage = new SignalRMessage + { + Name = Resource, + Body = new ResourceChangeMessage(action) + }; + + _commandExecutor.PublishCommand(new BroadcastSignalRMessage(signalRMessage)); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/REST/RestModule.cs b/NzbDrone.Api/REST/RestModule.cs index 2b19bcc17..9a576d360 100644 --- a/NzbDrone.Api/REST/RestModule.cs +++ b/NzbDrone.Api/REST/RestModule.cs @@ -61,7 +61,7 @@ protected Action DeleteResource protected Func GetResourceById { - private get { return _getResourceById; } + get { return _getResourceById; } set { _getResourceById = value; diff --git a/NzbDrone.Api/ResourceChangeMessage.cs b/NzbDrone.Api/ResourceChangeMessage.cs new file mode 100644 index 000000000..6b7efb50a --- /dev/null +++ b/NzbDrone.Api/ResourceChangeMessage.cs @@ -0,0 +1,29 @@ +using System; +using NzbDrone.Api.REST; +using NzbDrone.Core.Datastore.Events; + +namespace NzbDrone.Api +{ + public class ResourceChangeMessage where TResource : RestResource + { + public TResource Resource { get; private set; } + public ModelAction Action { get; private set; } + + public ResourceChangeMessage(ModelAction action) + { + if (action != ModelAction.Deleted || action != ModelAction.Sync) + { + throw new InvalidOperationException("Resource message without a resource needs to have Delete or Sync as action"); + } + + Action = action; + } + + public ResourceChangeMessage(TResource resource, ModelAction action) + { + Resource = resource; + Action = action; + } + } + +} \ No newline at end of file diff --git a/NzbDrone.Api/RootFolders/RootFolderConnection.cs b/NzbDrone.Api/RootFolders/RootFolderConnection.cs deleted file mode 100644 index 0068cc972..000000000 --- a/NzbDrone.Api/RootFolders/RootFolderConnection.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NzbDrone.Api.SignalR; -using NzbDrone.Core.RootFolders; - -namespace NzbDrone.Api.RootFolders -{ - public class RootFolderConnection : BasicResourceConnection - { - public override string Resource - { - get { return "RootFolder"; } - } - } -} diff --git a/NzbDrone.Api/RootFolders/RootFolderModule.cs b/NzbDrone.Api/RootFolders/RootFolderModule.cs index 7bcc4aa52..dd346d5de 100644 --- a/NzbDrone.Api/RootFolders/RootFolderModule.cs +++ b/NzbDrone.Api/RootFolders/RootFolderModule.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.RootFolders; using NzbDrone.Api.Mapping; using NzbDrone.Api.Validation; diff --git a/NzbDrone.Api/Seasons/SeasonModule.cs b/NzbDrone.Api/Seasons/SeasonModule.cs deleted file mode 100644 index 0521b8518..000000000 --- a/NzbDrone.Api/Seasons/SeasonModule.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using NzbDrone.Api.Mapping; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Api.Seasons -{ - public class SeasonModule : NzbDroneRestModule - { - private readonly ISeasonService _seasonService; - - public SeasonModule(ISeasonService seasonService) - : base("/season") - { - _seasonService = seasonService; - - GetResourceAll = GetSeasons; - GetResourceById = GetSeason; - UpdateResource = Update; - - Post["/pass"] = x => SetSeasonPass(); - } - - private List GetSeasons() - { - var seriesId = Request.Query.SeriesId; - - if (seriesId.HasValue) - { - return ToListResource(() => _seasonService.GetSeasonsBySeries(seriesId)); - } - - return ToListResource(() => _seasonService.GetAllSeasons()); - } - - private SeasonResource GetSeason(int id) - { - return _seasonService.Get(id).InjectTo(); - } - - private void Update(SeasonResource seasonResource) - { - _seasonService.SetMonitored(seasonResource.SeriesId, seasonResource.SeasonNumber, seasonResource.Monitored); - } - - private List SetSeasonPass() - { - var seriesId = Request.Form.SeriesId; - var seasonNumber = Request.Form.SeasonNumber; - - return ToListResource(() => _seasonService.SetSeasonPass(seriesId, seasonNumber)); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Api/Seasons/SeasonResource.cs b/NzbDrone.Api/Seasons/SeasonResource.cs deleted file mode 100644 index 46e14be9d..000000000 --- a/NzbDrone.Api/Seasons/SeasonResource.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using NzbDrone.Api.REST; - -namespace NzbDrone.Api.Seasons -{ - public class SeasonResource : RestResource - { - public int SeriesId { get; set; } - public int SeasonNumber { get; set; } - public Boolean Monitored { get; set; } - } -} diff --git a/NzbDrone.Api/Series/SeriesConnection.cs b/NzbDrone.Api/Series/SeriesConnection.cs deleted file mode 100644 index 4de76e7e6..000000000 --- a/NzbDrone.Api/Series/SeriesConnection.cs +++ /dev/null @@ -1,12 +0,0 @@ -using NzbDrone.Api.SignalR; - -namespace NzbDrone.Api.Series -{ - public class SeriesConnection : BasicResourceConnection - { - public override string Resource - { - get { return "/Series"; } - } - } -} diff --git a/NzbDrone.Api/Series/SeriesModule.cs b/NzbDrone.Api/Series/SeriesModule.cs index fe7b820a6..ea01b1a06 100644 --- a/NzbDrone.Api/Series/SeriesModule.cs +++ b/NzbDrone.Api/Series/SeriesModule.cs @@ -117,7 +117,6 @@ private void LinkSeriesStatistics(SeriesResource resource, SeriesStatistics seri { resource.EpisodeCount = seriesStatistics.EpisodeCount; resource.EpisodeFileCount = seriesStatistics.EpisodeFileCount; - resource.SeasonCount = seriesStatistics.SeasonCount; resource.NextAiring = seriesStatistics.NextAiring; } } diff --git a/NzbDrone.Api/Series/SeriesResource.cs b/NzbDrone.Api/Series/SeriesResource.cs index 34f7dcead..170fa8301 100644 --- a/NzbDrone.Api/Series/SeriesResource.cs +++ b/NzbDrone.Api/Series/SeriesResource.cs @@ -14,7 +14,17 @@ public class SeriesResource : RestResource //View Only public String Title { get; set; } - public Int32 SeasonCount { get; set; } + + public Int32 SeasonCount + { + get + { + if (Seasons != null) return Seasons.Count; + + return 0; + } + } + public Int32 EpisodeCount { get; set; } public Int32 EpisodeFileCount { get; set; } public SeriesStatusType Status { get; set; } @@ -26,7 +36,8 @@ public class SeriesResource : RestResource public List Images { get; set; } public String RemotePoster { get; set; } - + public List Seasons { get; set; } + public Int32 Year { get; set; } //View & Edit public String Path { get; set; } diff --git a/NzbDrone.Api/SignalR/BasicResourceConnection.cs b/NzbDrone.Api/SignalR/BasicResourceConnection.cs deleted file mode 100644 index e56ae4fd2..000000000 --- a/NzbDrone.Api/SignalR/BasicResourceConnection.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNet.SignalR; -using Microsoft.AspNet.SignalR.Infrastructure; -using NLog; -using NzbDrone.Common.Messaging; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Datastore.Events; - -namespace NzbDrone.Api.SignalR -{ - public abstract class BasicResourceConnection : - NzbDronePersistentConnection, - IHandleAsync> - where T : ModelBase - { - private readonly Logger _logger; - - - public BasicResourceConnection() - { - _logger = LogManager.GetCurrentClassLogger(); - } - - protected override Task OnConnected(IRequest request, string connectionId) - { - _logger.Trace("SignalR client connected. ID:{0}", connectionId); - return base.OnConnected(request, connectionId); - } - - public void HandleAsync(ModelEvent message) - { - var context = ((ConnectionManager)GlobalHost.ConnectionManager).GetConnection(GetType()); - context.Connection.Broadcast(message); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Api/SignalR/NzbDronePersistentConnection.cs b/NzbDrone.Api/SignalR/NzbDronePersistentConnection.cs deleted file mode 100644 index 2e4c8444d..000000000 --- a/NzbDrone.Api/SignalR/NzbDronePersistentConnection.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.AspNet.SignalR; - -namespace NzbDrone.Api.SignalR -{ - public abstract class NzbDronePersistentConnection : PersistentConnection - { - public abstract string Resource { get; } - } -} \ No newline at end of file diff --git a/NzbDrone.Api/System/SystemModule.cs b/NzbDrone.Api/System/SystemModule.cs index e0e9b9177..a013b15af 100644 --- a/NzbDrone.Api/System/SystemModule.cs +++ b/NzbDrone.Api/System/SystemModule.cs @@ -40,6 +40,7 @@ private Response GetStatus() OsVersion = OsInfo.Version.ToString(), IsMono = OsInfo.IsMono, IsLinux = OsInfo.IsLinux, + IsWindows = OsInfo.IsWindows, Branch = _configFileProvider.Branch, Authentication = _configFileProvider.AuthenticationEnabled }.AsResponse(); diff --git a/NzbDrone.Api/Update/UpdateModule.cs b/NzbDrone.Api/Update/UpdateModule.cs index 8a95efa4b..fbe13bd8e 100644 --- a/NzbDrone.Api/Update/UpdateModule.cs +++ b/NzbDrone.Api/Update/UpdateModule.cs @@ -33,8 +33,6 @@ private List GetAvailableUpdate() public class UpdateResource : RestResource { - public String Id { get; set; } - [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))] public Version Version { get; set; } diff --git a/NzbDrone.Api/Validation/RuleBuilderExtensions.cs b/NzbDrone.Api/Validation/RuleBuilderExtensions.cs index b142f5a56..a13cbf52a 100644 --- a/NzbDrone.Api/Validation/RuleBuilderExtensions.cs +++ b/NzbDrone.Api/Validation/RuleBuilderExtensions.cs @@ -1,6 +1,4 @@ -using System; -using System.Linq.Expressions; -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using FluentValidation; using FluentValidation.Validators; @@ -27,5 +25,10 @@ public static IRuleBuilderOptions IsValidPath(this IRuleBuilder NotBlank(this IRuleBuilder ruleBuilder) + { + return ruleBuilder.SetValidator(new NotNullValidator()).SetValidator(new NotEmptyValidator("")); + } } } \ No newline at end of file diff --git a/NzbDrone.App.Test/ContainerFixture.cs b/NzbDrone.App.Test/ContainerFixture.cs index f4bf6af6c..244901c0a 100644 --- a/NzbDrone.App.Test/ContainerFixture.cs +++ b/NzbDrone.App.Test/ContainerFixture.cs @@ -2,12 +2,13 @@ using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Messaging; -using NzbDrone.Core.Datastore; using NzbDrone.Core.Download; using NzbDrone.Core.Indexers; using NzbDrone.Core.Jobs; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Host; using NzbDrone.Test.Common; using FluentAssertions; @@ -45,7 +46,6 @@ public void should_resolve_command_executor_by_name() { var genericExecutor = typeof(IExecute<>).MakeGenericType(typeof(RssSyncCommand)); var container = MainAppContainerBuilder.BuildContainer(args); - DbFactory.RegisterDatabase(container); var executor = container.Resolve(genericExecutor); diff --git a/NzbDrone.Common.Test/CacheTests/CachedFixture.cs b/NzbDrone.Common.Test/CacheTests/CachedFixture.cs index 4a1cf9021..1d91558a6 100644 --- a/NzbDrone.Common.Test/CacheTests/CachedFixture.cs +++ b/NzbDrone.Common.Test/CacheTests/CachedFixture.cs @@ -48,6 +48,23 @@ public void should_be_able_to_update_key() _cachedString.Find("Key").Should().Be("New"); } + + [Test] + public void should_be_able_to_remove_key() + { + _cachedString.Set("Key", "Value"); + + _cachedString.Remove("Key"); + + _cachedString.Find("Key").Should().BeNull(); + } + + [Test] + public void should_be_able_to_remove_non_existing_key() + { + _cachedString.Remove("Key"); + } + [Test] public void should_store_null() { diff --git a/NzbDrone.Common.Test/DiskProviderTests/DiskProviderFixture.cs b/NzbDrone.Common.Test/DiskProviderTests/DiskProviderFixture.cs index 1bcb330f1..4de222365 100644 --- a/NzbDrone.Common.Test/DiskProviderTests/DiskProviderFixture.cs +++ b/NzbDrone.Common.Test/DiskProviderTests/DiskProviderFixture.cs @@ -23,6 +23,10 @@ public void Setup() if (_binFolderCopy.Exists) { + foreach (var file in _binFolderCopy.GetFiles("*", SearchOption.AllDirectories)) + { + file.Attributes = FileAttributes.Normal; + } _binFolderCopy.Delete(true); } @@ -83,11 +87,7 @@ public void moveFile_should_not_move_overwrite_itself() [Test] public void CopyFolder_should_copy_folder() { - - Subject.CopyFolder(_binFolder.FullName, _binFolderCopy.FullName); - - VerifyCopy(); } @@ -127,6 +127,22 @@ public void MoveFolder_should_overwrite_existing_folder() } + [Test] + public void move_read_only_file() + { + var source = GetTestFilePath(); + var destination = GetTestFilePath(); + + Subject.WriteAllText(source, "SourceFile"); + Subject.WriteAllText(destination, "DestinationFile"); + + File.SetAttributes(source, FileAttributes.ReadOnly); + File.SetAttributes(destination, FileAttributes.ReadOnly); + + Subject.MoveFile(source, destination); + } + + [Test] @@ -139,16 +155,60 @@ public void empty_folder_should_return_folder_modified_date() [Test] public void folder_should_return_correct_value_for_last_write() { - var testFile = Path.Combine(SandboxFolder, "newfile.txt"); + var testFile = GetTestFilePath(); TestLogger.Info("Path is: {0}", testFile); - Subject.WriteAllText(testFile, ""); + Subject.WriteAllText(testFile, "Test"); Subject.GetLastFolderWrite(SandboxFolder).Should().BeOnOrAfter(DateTime.UtcNow.AddMinutes(-1)); Subject.GetLastFolderWrite(SandboxFolder).Should().BeBefore(DateTime.UtcNow); } + [Test] + public void should_return_false_for_unlocked_file() + { + var testFile = GetTestFilePath(); + Subject.WriteAllText(testFile, new Guid().ToString()); + + Subject.IsFileLocked(testFile).Should().BeFalse(); + } + + [Test] + public void should_return_false_for_unlocked_and_readonly_file() + { + var testFile = GetTestFilePath(); + Subject.WriteAllText(testFile, new Guid().ToString()); + + File.SetAttributes(testFile, FileAttributes.ReadOnly); + + Subject.IsFileLocked(testFile).Should().BeFalse(); + } + + + [Test] + public void should_return_true_for_unlocked_file() + { + var testFile = GetTestFilePath(); + Subject.WriteAllText(testFile, new Guid().ToString()); + + using (var file = File.OpenWrite(testFile)) + { + Subject.IsFileLocked(testFile).Should().BeTrue(); + } + } + + + [Test] + public void should_be_able_to_set_permission_from_parrent() + { + var testFile = GetTestFilePath(); + Subject.WriteAllText(testFile, new Guid().ToString()); + + Subject.InheritFolderPermissions(testFile); + } + + [Test] [Explicit] public void check_last_write() diff --git a/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs b/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs index 8359ad481..f3ede03eb 100644 --- a/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs +++ b/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; using NzbDrone.Test.Common; diff --git a/NzbDrone.Common.Test/EnvironmentProviderTest.cs b/NzbDrone.Common.Test/EnvironmentProviderTest.cs index 8dfaf87b5..588fb06ad 100644 --- a/NzbDrone.Common.Test/EnvironmentProviderTest.cs +++ b/NzbDrone.Common.Test/EnvironmentProviderTest.cs @@ -29,7 +29,7 @@ public void ApplicationPath_should_not_be_empty() [Test] public void IsProduction_should_return_false_when_run_within_nunit() { - RuntimeInfo.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName); + RuntimeInfo.IsProduction.Should().BeFalse("Process name is " + Process.GetCurrentProcess().ProcessName + " Folder is " + Directory.GetCurrentDirectory()); } [Test] diff --git a/NzbDrone.Common.Test/EventingTests/MessageAggregatorEventTests.cs b/NzbDrone.Common.Test/MessagingTests/MessageAggregatorEventTests.cs similarity index 94% rename from NzbDrone.Common.Test/EventingTests/MessageAggregatorEventTests.cs rename to NzbDrone.Common.Test/MessagingTests/MessageAggregatorEventTests.cs index b41f81124..30a177c81 100644 --- a/NzbDrone.Common.Test/EventingTests/MessageAggregatorEventTests.cs +++ b/NzbDrone.Common.Test/MessagingTests/MessageAggregatorEventTests.cs @@ -1,16 +1,18 @@ using System; using System.Collections.Generic; using System.Threading; +using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Test.Common; -using FluentAssertions; -namespace NzbDrone.Common.Test.EventingTests +namespace NzbDrone.Common.Test.MessagingTests { [TestFixture] - public class MessageAggregatorEventTests : TestBase + public class MessageAggregatorEventTests : TestBase { private Mock> HandlerA1; private Mock> HandlerA2; @@ -127,7 +129,7 @@ public void should_queue_multiple_async_events() counter.WaitForAllItems(); - counter.MaxThreads.Should().Be(2); + counter.MaxThreads.Should().Be(3); } } diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj index 4c22a7aeb..2347a37bf 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj @@ -67,8 +67,7 @@ - - + diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject index 12a3cf5f9..21e25ddd5 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.ncrunchproject @@ -2,7 +2,7 @@ false true false - true + false false false false @@ -12,11 +12,11 @@ false true true - 60000 - - + 5000 + Debug + x86 - AutoDetect + x86 STA x86 @@ -26,11 +26,35 @@ NzbDrone\.Common\.Test\.EventingTests\.ServiceNameFixture\..* - - NzbDrone\.Common\.Test\.ProcessProviderTests\..* - NzbDrone\.Common\.Test\.ServiceFactoryFixture\..* + + NzbDrone.Common.Test.ProcessProviderTests.ToString_on_new_processInfo + + + NzbDrone.Common.Test.ProcessProviderTests.Should_be_able_to_start_process + + + NzbDrone.Common.Test.ProcessProviderTests.kill_all_should_kill_all_process_with_name + + + NzbDrone.Common.Test.ProcessProviderTests.GetProcessById_should_return_null_for_invalid_process(9999) + + + NzbDrone.Common.Test.ProcessProviderTests.GetProcessById_should_return_null_for_invalid_process(-1) + + + NzbDrone.Common.Test.ProcessProviderTests.GetProcessById_should_return_null_for_invalid_process(0) + + + NzbDrone\.Common\.Test\.ServiceProviderTests\..* + + + NzbDrone.Common.Test.DiskProviderTests.DiskProviderFixture.folder_should_return_correct_value_for_last_write + + + NzbDrone\.Common\.Test\.DiskProviderTests\.DiskProviderFixture\..* + \ No newline at end of file diff --git a/NzbDrone.Common.Test/PathExtensionFixture.cs b/NzbDrone.Common.Test/PathExtensionFixture.cs index 907d13d4c..fbc2fb6b6 100644 --- a/NzbDrone.Common.Test/PathExtensionFixture.cs +++ b/NzbDrone.Common.Test/PathExtensionFixture.cs @@ -126,17 +126,16 @@ public void get_actual_casing_should_return_actual_casing_for_local_file_in_wind } - [Test] public void get_actual_casing_should_return_actual_casing_for_local_dir_in_windows() { WindowsOnly(); - var path = Directory.GetCurrentDirectory(); + var path = Directory.GetCurrentDirectory().Replace("c:\\","C:\\"); + path.ToUpper().GetActualCasing().Should().Be(path); path.ToLower().GetActualCasing().Should().Be(path); } - [Test] public void get_actual_casing_should_return_original_value_in_linux() { diff --git a/NzbDrone.Common.Test/ProcessProviderTests.cs b/NzbDrone.Common.Test/ProcessProviderTests.cs index dab923782..4155ab4d0 100644 --- a/NzbDrone.Common.Test/ProcessProviderTests.cs +++ b/NzbDrone.Common.Test/ProcessProviderTests.cs @@ -73,7 +73,7 @@ public void kill_all_should_kill_all_process_with_name() var dummy1 = StartDummyProcess(); var dummy2 = StartDummyProcess(); - Subject.KillAll(dummy1.ProcessName); + Subject.KillAll(DummyApp.DUMMY_PROCCESS_NAME); dummy1.HasExited.Should().BeTrue(); dummy2.HasExited.Should().BeTrue(); diff --git a/NzbDrone.Common.Test/ServiceFactoryFixture.cs b/NzbDrone.Common.Test/ServiceFactoryFixture.cs index 4d83d0b3c..7777476ff 100644 --- a/NzbDrone.Common.Test/ServiceFactoryFixture.cs +++ b/NzbDrone.Common.Test/ServiceFactoryFixture.cs @@ -2,8 +2,9 @@ using FluentAssertions; using NUnit.Framework; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Host; using NzbDrone.Test.Common; diff --git a/NzbDrone.Common/Cache/CacheManger.cs b/NzbDrone.Common/Cache/CacheManger.cs index d264eeb40..b5da3047c 100644 --- a/NzbDrone.Common/Cache/CacheManger.cs +++ b/NzbDrone.Common/Cache/CacheManger.cs @@ -28,7 +28,6 @@ public ICached GetCache(Type host) return GetCache(host, host.FullName); } - public void Clear() { _cache.Clear(); diff --git a/NzbDrone.Common/Cache/Cached.cs b/NzbDrone.Common/Cache/Cached.cs index eef89f317..7269a9a53 100644 --- a/NzbDrone.Common/Cache/Cached.cs +++ b/NzbDrone.Common/Cache/Cached.cs @@ -61,6 +61,12 @@ public T Find(string key) return value.Object; } + public void Remove(string key) + { + CacheItem value; + _store.TryRemove(key, out value); + } + public T Get(string key, Func function, TimeSpan? lifeTime = null) { Ensure.That(() => key).IsNotNullOrWhiteSpace(); @@ -81,7 +87,6 @@ public T Get(string key, Func function, TimeSpan? lifeTime = null) return value; } - public void Clear() { _store.Clear(); diff --git a/NzbDrone.Common/Cache/ICached.cs b/NzbDrone.Common/Cache/ICached.cs index 3708b72af..1c9e50812 100644 --- a/NzbDrone.Common/Cache/ICached.cs +++ b/NzbDrone.Common/Cache/ICached.cs @@ -13,6 +13,7 @@ public interface ICached : ICached void Set(string key, T value, TimeSpan? lifetime = null); T Get(string key, Func function, TimeSpan? lifeTime = null); T Find(string key); + void Remove(string key); ICollection Values { get; } } diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 851ba4550..56180cdda 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -8,12 +7,12 @@ using NLog; using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Common { public interface IDiskProvider { - HashSet SpecialFolders { get; } DateTime GetLastFolderWrite(string path); DateTime GetLastFileWrite(string path); void EnsureFolder(string path); @@ -24,8 +23,8 @@ public interface IDiskProvider string[] GetFiles(string path, SearchOption searchOption); long GetFolderSize(string path); long GetFileSize(string path); - String CreateFolder(string path); - void CopyFolder(string source, string target); + void CreateFolder(string path); + void CopyFolder(string source, string destination); void MoveFolder(string source, string destination); void DeleteFile(string path); void MoveFile(string source, string destination); @@ -36,11 +35,12 @@ public interface IDiskProvider void WriteAllText(string filename, string contents); void FileSetLastWriteTimeUtc(string path, DateTime dateTime); void FolderSetLastWriteTimeUtc(string path, DateTime dateTime); - bool IsFileLocked(FileInfo file); + bool IsFileLocked(string path); string GetPathRoot(string path); void SetPermissions(string filename, WellKnownSidType accountSid, FileSystemRights rights, AccessControlType controlType); bool IsParent(string parentPath, string childPath); FileAttributes GetFileAttributes(string path); + void EmptyFolder(string path); } public class DiskProvider : IDiskProvider @@ -58,15 +58,7 @@ static extern bool GetDiskFreeSpaceEx(string lpDirectoryName, out ulong lpTotalNumberOfBytes, out ulong lpTotalNumberOfFreeBytes); - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - public HashSet SpecialFolders - { - get - { - return new HashSet { "$recycle.bin", "system volume information", "recycler" }; - } - } + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public DateTime GetLastFolderWrite(string path) { @@ -93,7 +85,9 @@ public DateTime GetLastFileWrite(string path) Ensure.That(() => path).IsValidPath(); if (!FileExists(path)) + { throw new FileNotFoundException("File doesn't exist: " + path); + } return new FileInfo(path).LastWriteTimeUtc; } @@ -154,25 +148,26 @@ public long GetFileSize(string path) Ensure.That(() => path).IsValidPath(); if (!FileExists(path)) + { throw new FileNotFoundException("File doesn't exist: " + path); + } var fi = new FileInfo(path); return fi.Length; } - public String CreateFolder(string path) + public void CreateFolder(string path) { Ensure.That(() => path).IsValidPath(); - - return Directory.CreateDirectory(path).FullName; + Directory.CreateDirectory(path); } - public void CopyFolder(string source, string target) + public void CopyFolder(string source, string destination) { Ensure.That(() => source).IsValidPath(); - Ensure.That(() => target).IsValidPath(); + Ensure.That(() => destination).IsValidPath(); - TransferFolder(source, target, TransferAction.Copy); + TransferFolder(source, destination, TransferAction.Copy); } public void MoveFolder(string source, string destination) @@ -183,7 +178,7 @@ public void MoveFolder(string source, string destination) try { TransferFolder(source, destination, TransferAction.Move); - Directory.Delete(source, true); + DeleteFolder(source, true); } catch (Exception e) { @@ -238,8 +233,10 @@ private void TransferFolder(string source, string target, TransferAction transfe public void DeleteFile(string path) { Ensure.That(() => path).IsValidPath(); - Logger.Trace("Deleting file: {0}", path); + + RemoveReadOnly(path); + File.Delete(path); } @@ -259,6 +256,7 @@ public void MoveFile(string source, string destination) DeleteFile(destination); } + RemoveReadOnly(source); File.Move(source, destination); } @@ -273,9 +271,19 @@ public void InheritFolderPermissions(string filename) { Ensure.That(() => filename).IsValidPath(); - var fs = File.GetAccessControl(filename); - fs.SetAccessRuleProtection(false, false); - File.SetAccessControl(filename, fs); + try + { + var fs = File.GetAccessControl(filename); + fs.SetAccessRuleProtection(false, false); + File.SetAccessControl(filename, fs); + } + catch (NotImplementedException) + { + if (!OsInfo.IsLinux) + { + throw; + } + } } public long? GetAvailableSpace(string path) @@ -347,11 +355,10 @@ public string ReadAllText(string filePath) public void WriteAllText(string filename, string contents) { Ensure.That(() => filename).IsValidPath(); - + RemoveReadOnly(filename); File.WriteAllText(filename, contents); } - public void FileSetLastWriteTimeUtc(string path, DateTime dateTime) { Ensure.That(() => path).IsValidPath(); @@ -366,26 +373,19 @@ public void FolderSetLastWriteTimeUtc(string path, DateTime dateTime) Directory.SetLastWriteTimeUtc(path, dateTime); } - public bool IsFileLocked(FileInfo file) + public bool IsFileLocked(string file) { - FileStream stream = null; - try { - stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None); + using (File.Open(file, FileMode.Open, FileAccess.Read, FileShare.None)) + { + return false; + } } catch (IOException) { return true; } - finally - { - if (stream != null) - stream.Close(); - } - - //file is not locked - return false; } public string GetPathRoot(string path) @@ -441,9 +441,34 @@ public bool IsParent(string parentPath, string childPath) return false; } + + private static void RemoveReadOnly(string path) + { + if (File.Exists(path)) + { + var newAttributes = File.GetAttributes(path) & ~(FileAttributes.ReadOnly); + File.SetAttributes(path, newAttributes); + } + } + public FileAttributes GetFileAttributes(string path) { return File.GetAttributes(path); } + + public void EmptyFolder(string path) + { + Ensure.That(() => path).IsValidPath(); + + foreach (var file in GetFiles(path, SearchOption.TopDirectoryOnly)) + { + DeleteFile(file); + } + + foreach (var directory in GetDirectories(path)) + { + DeleteFolder(directory, true); + } + } } } \ No newline at end of file diff --git a/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs index 0cd595464..b3b840491 100644 --- a/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs +++ b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.IO; using System.Text.RegularExpressions; using NzbDrone.Common.EnsureThat.Resources; using NzbDrone.Common.EnvironmentInfo; diff --git a/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs b/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs index e93395756..04f7b29ab 100644 --- a/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs +++ b/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs @@ -4,6 +4,7 @@ using System.Security.AccessControl; using System.Security.Principal; using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Common.EnvironmentInfo { @@ -30,7 +31,7 @@ public AppFolderInfo(IDiskProvider diskProvider, IStartupArguments startupArgume DATA_SPECIAL_FOLDER = Environment.SpecialFolder.ApplicationData; } - _logger = LogManager.GetCurrentClassLogger(); + _logger = NzbDroneLogger.GetLogger(this); if (startupArguments.Args.ContainsKey(StartupArguments.APPDATA)) { diff --git a/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs b/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs index 7bd6e8d3c..858db57b0 100644 --- a/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs +++ b/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.IO; using System.Security.Principal; -using System.ServiceProcess; using NLog; namespace NzbDrone.Common.EnvironmentInfo @@ -65,7 +64,9 @@ private static bool InternalIsProduction() if (lowerProcessName.Contains("jetbrain")) return false; if (lowerProcessName.Contains("resharper")) return false; - if (Directory.GetCurrentDirectory().ToLower().Contains("teamcity")) return false; + string lowerCurrentDir = Directory.GetCurrentDirectory().ToLower(); + if (lowerCurrentDir.Contains("teamcity")) return false; + if (lowerCurrentDir.StartsWith("/run/")) return false; return true; } diff --git a/NzbDrone.Common/EnvironmentInfo/StartupArguments.cs b/NzbDrone.Common/EnvironmentInfo/StartupArguments.cs index 9075d64e0..51c587bd0 100644 --- a/NzbDrone.Common/EnvironmentInfo/StartupArguments.cs +++ b/NzbDrone.Common/EnvironmentInfo/StartupArguments.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; namespace NzbDrone.Common.EnvironmentInfo { @@ -17,14 +16,6 @@ public class StartupArguments : IStartupArguments public const string UNINSTALL_SERVICE = "u"; public const string HELP = "?"; - static StartupArguments() - { - if (RuntimeInfo.IsProduction) - { - Instance = new StartupArguments(""); - } - } - public StartupArguments(params string[] args) { Flags = new HashSet(); @@ -45,13 +36,9 @@ public StartupArguments(params string[] args) Flags.Add(flag); } } - - Instance = this; } public HashSet Flags { get; private set; } public Dictionary Args { get; private set; } - - public static IStartupArguments Instance { get; private set; } } } \ No newline at end of file diff --git a/NzbDrone.Common/Exceptions/NzbDroneException.cs b/NzbDrone.Common/Exceptions/NzbDroneException.cs index 09b431fc6..7e0be7312 100644 --- a/NzbDrone.Common/Exceptions/NzbDroneException.cs +++ b/NzbDrone.Common/Exceptions/NzbDroneException.cs @@ -2,8 +2,6 @@ namespace NzbDrone.Common.Exceptions { - - public abstract class NzbDroneException : ApplicationException { protected NzbDroneException(string message, params object[] args) @@ -17,5 +15,16 @@ protected NzbDroneException(string message) { } + + protected NzbDroneException(string message, Exception innerException, params object[] args) + : base(string.Format(message, args), innerException) + { + + } + + protected NzbDroneException(string message, Exception innerException) + : base(message, innerException) + { + } } } diff --git a/NzbDrone.Common/HashUtil.cs b/NzbDrone.Common/HashUtil.cs index 0a127ee63..abeb9496d 100644 --- a/NzbDrone.Common/HashUtil.cs +++ b/NzbDrone.Common/HashUtil.cs @@ -1,5 +1,4 @@ using System; -using System.Security.Cryptography; using System.Text; using System.Threading; @@ -34,31 +33,9 @@ public static string CalculateCrc(string input) return String.Format("{0:x8}", mCrc); } - public static string GenerateUserId() + public static string GenerateCommandId() { - return GenerateId("u"); - } - - public static string GenerateAppId() - { - return GenerateId("a"); - } - - public static string GenerateApiToken() - { - return Guid.NewGuid().ToString().Replace("-", ""); - } - - public static string GenerateSecurityToken(int length) - { - var byteSize = (length / 4) * 3; - - var linkBytes = new byte[byteSize]; - var rngCrypto = new RNGCryptoServiceProvider(); - rngCrypto.GetBytes(linkBytes); - var base64String = Convert.ToBase64String(linkBytes); - - return base64String; + return GenerateId("c"); } private static string GenerateId(string prefix) diff --git a/NzbDrone.Common/Instrumentation/ApplicationLogLayoutRenderer.cs b/NzbDrone.Common/Instrumentation/ApplicationLogLayoutRenderer.cs deleted file mode 100644 index 2a2082d18..000000000 --- a/NzbDrone.Common/Instrumentation/ApplicationLogLayoutRenderer.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.IO; -using System.Text; -using NLog; -using NLog.Config; -using NLog.LayoutRenderers; -using NzbDrone.Common.EnvironmentInfo; - -namespace NzbDrone.Common.Instrumentation -{ - [ThreadAgnostic] - [LayoutRenderer("appLog")] - public class ApplicationLogLayoutRenderer : LayoutRenderer - { - private readonly string _appData; - - public ApplicationLogLayoutRenderer() - { - _appData = Path.Combine(new AppFolderInfo(new DiskProvider(), StartupArguments.Instance ).GetLogFolder(), "nzbdrone.txt"); - } - - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - builder.Append(_appData); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Instrumentation/DirSeparatorLayoutRenderer.cs b/NzbDrone.Common/Instrumentation/DirSeparatorLayoutRenderer.cs deleted file mode 100644 index bb5658326..000000000 --- a/NzbDrone.Common/Instrumentation/DirSeparatorLayoutRenderer.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.IO; -using System.Text; -using NLog; -using NLog.Config; -using NLog.LayoutRenderers; - -namespace NzbDrone.Common.Instrumentation -{ - [ThreadAgnostic] - [LayoutRenderer("dirSeparator")] - public class DirSeparatorLayoutRenderer : LayoutRenderer - { - - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - builder.Append(Path.DirectorySeparatorChar); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Instrumentation/ExceptronTarget.cs b/NzbDrone.Common/Instrumentation/ExceptronTarget.cs index 59ff5e918..f824a5ac4 100644 --- a/NzbDrone.Common/Instrumentation/ExceptronTarget.cs +++ b/NzbDrone.Common/Instrumentation/ExceptronTarget.cs @@ -4,7 +4,6 @@ using Exceptron.Client.Configuration; using NLog; using NLog.Common; -using NLog.Config; using NLog.Layouts; using NLog.Targets; using NzbDrone.Common.EnvironmentInfo; @@ -23,19 +22,6 @@ public class ExceptronTarget : Target /// public IExceptronClient ExceptronClient { get; internal set; } - - private static ExceptronTarget _instance = new ExceptronTarget(); - - public static void Register() - { - var rule = new LoggingRule("*", LogLevel.Warn, _instance); - - LogManager.Configuration.AddTarget("ExceptronTarget", _instance); - LogManager.Configuration.LoggingRules.Add(rule); - LogManager.ConfigurationReloaded += (sender, args) => Register(); - LogManager.ReconfigExistingLoggers(); - } - protected override void InitializeTarget() { var config = new ExceptronConfiguration diff --git a/NzbDrone.Common/Instrumentation/GlobalExceptionHandlers.cs b/NzbDrone.Common/Instrumentation/GlobalExceptionHandlers.cs index 24fb64c61..ed32997eb 100644 --- a/NzbDrone.Common/Instrumentation/GlobalExceptionHandlers.cs +++ b/NzbDrone.Common/Instrumentation/GlobalExceptionHandlers.cs @@ -6,12 +6,9 @@ namespace NzbDrone.Common.Instrumentation { public static class GlobalExceptionHandlers { - private static readonly Logger Logger = LogManager.GetLogger("Global"); - + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public static void Register() { - ExceptronTarget.Register(); - AppDomain.CurrentDomain.UnhandledException += ((s, e) => AppDomainException(e.ExceptionObject as Exception)); TaskScheduler.UnobservedTaskException += ((s, e) => TaskException(e.Exception)); } @@ -24,6 +21,15 @@ private static void TaskException(Exception exception) private static void AppDomainException(Exception exception) { + if (exception == null) return; + + if (exception is NullReferenceException && + exception.ToString().Contains("Microsoft.AspNet.SignalR.Transports.TransportHeartbeat.ProcessServerCommand")) + { + Logger.Warn("SignalR Heartbeat error."); + return; + } + Console.WriteLine("EPIC FAIL: {0}", exception); Logger.FatalException("EPIC FAIL: " + exception.Message, exception); } diff --git a/NzbDrone.Common/Instrumentation/LogEventExtensions.cs b/NzbDrone.Common/Instrumentation/LogEventExtensions.cs index cb2fe44f2..373aa9201 100644 --- a/NzbDrone.Common/Instrumentation/LogEventExtensions.cs +++ b/NzbDrone.Common/Instrumentation/LogEventExtensions.cs @@ -13,7 +13,6 @@ public static string GetHash(this LogEventInfo logEvent) return HashUtil.CalculateCrc(hashSeed); } - public static string GetFormattedMessage(this LogEventInfo logEvent) { var message = logEvent.FormattedMessage; diff --git a/NzbDrone.Common/Instrumentation/LogTargets.cs b/NzbDrone.Common/Instrumentation/LogTargets.cs new file mode 100644 index 000000000..d63520e80 --- /dev/null +++ b/NzbDrone.Common/Instrumentation/LogTargets.cs @@ -0,0 +1,127 @@ +using System; +using System.IO; +using NLog; +using NLog.Config; +using NLog.Targets; +using NzbDrone.Common.EnvironmentInfo; + +namespace NzbDrone.Common.Instrumentation +{ + public static class LogTargets + { + public static void Register(IStartupArguments startupArguments, bool updateApp, bool inConsole) + { + var appFolderInfo = new AppFolderInfo(new DiskProvider(), startupArguments); + + LogManager.Configuration = new LoggingConfiguration(); + + RegisterExceptron(); + + if (updateApp) + { + RegisterLoggly(); + RegisterUpdateFile(appFolderInfo); + } + else + { + if (inConsole && (OsInfo.IsLinux || new RuntimeInfo(null).IsUserInteractive)) + { + RegisterConsole(); + } + + RegisterAppFile(appFolderInfo); + } + + LogManager.ReconfigExistingLoggers(); + } + + private static void RegisterConsole() + { + var level = LogLevel.Trace; + + if (RuntimeInfo.IsProduction) + { + level = LogLevel.Info; + } + + var coloredConsoleTarget = new ColoredConsoleTarget(); + + coloredConsoleTarget.Name = "consoleLogger"; + coloredConsoleTarget.Layout = "[${level}] ${logger}: ${message} ${onexception:inner=${newline}${newline}${exception:format=ToString}${newline}}"; + + var loggingRule = new LoggingRule("*", level, coloredConsoleTarget); + + LogManager.Configuration.AddTarget("console", coloredConsoleTarget); + LogManager.Configuration.LoggingRules.Add(loggingRule); + } + + + const string FileLogLayout = @"${date:format=yy-M-d HH\:mm\:ss.f}|${level}|${logger}|${message}${onexception:inner=${newline}${newline}${exception:format=ToString}${newline}}"; + + private static void RegisterAppFile(IAppFolderInfo appFolderInfo) + { + var fileTarget = new FileTarget(); + + fileTarget.Name = "rollingFileLogger"; + fileTarget.FileName = Path.Combine(appFolderInfo.GetLogFolder(), "nzbdrone.txt"); + fileTarget.AutoFlush = true; + fileTarget.KeepFileOpen = false; + fileTarget.ConcurrentWrites = false; + fileTarget.ConcurrentWriteAttemptDelay = 50; + fileTarget.ConcurrentWriteAttempts = 10; + fileTarget.ArchiveAboveSize = 1024000; + fileTarget.MaxArchiveFiles = 5; + fileTarget.EnableFileDelete = true; + fileTarget.ArchiveNumbering = ArchiveNumberingMode.Rolling; + fileTarget.Layout = FileLogLayout; + + var loggingRule = new LoggingRule("*", LogLevel.Info, fileTarget); + + LogManager.Configuration.AddTarget("appfile", fileTarget); + LogManager.Configuration.LoggingRules.Add(loggingRule); + } + + + + private static void RegisterUpdateFile(IAppFolderInfo appFolderInfo) + { + var fileTarget = new FileTarget(); + + fileTarget.Name = "updateFileLogger"; + fileTarget.FileName = Path.Combine(appFolderInfo.GetUpdateLogFolder(), DateTime.Now.ToString("yy.MM.d-HH.mm") + ".txt"); + fileTarget.AutoFlush = true; + fileTarget.KeepFileOpen = false; + fileTarget.ConcurrentWrites = false; + fileTarget.ConcurrentWriteAttemptDelay = 50; + fileTarget.ConcurrentWriteAttempts = 100; + fileTarget.Layout = FileLogLayout; + + var loggingRule = new LoggingRule("*", LogLevel.Trace, fileTarget); + + LogManager.Configuration.AddTarget("updateFile", fileTarget); + LogManager.Configuration.LoggingRules.Add(loggingRule); + } + + private static void RegisterExceptron() + { + + var exceptronTarget = new ExceptronTarget(); + var rule = new LoggingRule("*", LogLevel.Warn, exceptronTarget); + + LogManager.Configuration.AddTarget("ExceptronTarget", exceptronTarget); + LogManager.Configuration.LoggingRules.Add(rule); + } + + + private static void RegisterLoggly() + { + var logglyTarger = new LogglyTarget(); + + var rule = new LoggingRule("*", LogLevel.Trace, logglyTarger); + + LogManager.Configuration.AddTarget("LogglyLogger", logglyTarger); + LogManager.Configuration.LoggingRules.Add(rule); + } + + } +} \ No newline at end of file diff --git a/NzbDrone.Common/Instrumentation/LogglyTarget.cs b/NzbDrone.Common/Instrumentation/LogglyTarget.cs index 33d953390..8f2817581 100644 --- a/NzbDrone.Common/Instrumentation/LogglyTarget.cs +++ b/NzbDrone.Common/Instrumentation/LogglyTarget.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using NLog; -using NLog.Config; using NLog.Layouts; using NLog.Targets; using NzbDrone.Common.EnvironmentInfo; @@ -13,16 +12,10 @@ public class LogglyTarget : TargetWithLayout { private Logger _logger; - public void Register(LogLevel minLevel) + public LogglyTarget() { Layout = new SimpleLayout("${callsite:className=false:fileName=false:includeSourcePath=false:methodName=true}"); - - var rule = new LoggingRule("*", minLevel, this); - - LogManager.Configuration.AddTarget("LogglyLogger", this); - LogManager.Configuration.LoggingRules.Add(rule); - LogManager.ConfigurationReloaded += (sender, args) => Register(minLevel); - LogManager.ReconfigExistingLoggers(); + } protected override void InitializeTarget() diff --git a/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs b/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs new file mode 100644 index 000000000..c722d5160 --- /dev/null +++ b/NzbDrone.Common/Instrumentation/NzbDroneLogger.cs @@ -0,0 +1,42 @@ +using System; +using System.Diagnostics; +using NLog; + +namespace NzbDrone.Common.Instrumentation +{ + public static class NzbDroneLogger + { + public static Logger GetLogger() + { + string loggerName; + Type declaringType; + int framesToSkip = 1; + do + { + var frame = new StackFrame(framesToSkip, false); + var method = frame.GetMethod(); + declaringType = method.DeclaringType; + if (declaringType == null) + { + loggerName = method.Name; + break; + } + + framesToSkip++; + loggerName = declaringType.Name; + } while (declaringType.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase)); + + return LogManager.GetLogger(loggerName); + } + + public static Logger GetLogger(object obj) + { + return LogManager.GetLogger(obj.GetType().Name); + } + + public static Logger GetLogger() + { + return LogManager.GetLogger(typeof(T).Name); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/Instrumentation/UpdateLogLayoutRenderer.cs b/NzbDrone.Common/Instrumentation/UpdateLogLayoutRenderer.cs deleted file mode 100644 index 2f55d36de..000000000 --- a/NzbDrone.Common/Instrumentation/UpdateLogLayoutRenderer.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.IO; -using System.Text; -using NLog; -using NLog.Config; -using NLog.LayoutRenderers; -using NzbDrone.Common.EnvironmentInfo; - -namespace NzbDrone.Common.Instrumentation -{ - [ThreadAgnostic] - [LayoutRenderer("updateLog")] - public class UpdateLogLayoutRenderer : LayoutRenderer - { - private readonly string _appData; - - public UpdateLogLayoutRenderer() - { - _appData = Path.Combine(new AppFolderInfo(new DiskProvider(), StartupArguments.Instance).GetUpdateLogFolder(), DateTime.Now.ToString("yy.MM.d-HH.mm") + ".txt"); - - } - - protected override void Append(StringBuilder builder, LogEventInfo logEvent) - { - builder.Append(_appData); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/CommandCompletedEvent.cs b/NzbDrone.Common/Messaging/CommandCompletedEvent.cs deleted file mode 100644 index 613800ae0..000000000 --- a/NzbDrone.Common/Messaging/CommandCompletedEvent.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace NzbDrone.Common.Messaging -{ - public class CommandCompletedEvent : IEvent - { - public ICommand Command { get; private set; } - - public CommandCompletedEvent(ICommand command) - { - Command = command; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/CommandFailedEvent.cs b/NzbDrone.Common/Messaging/CommandFailedEvent.cs deleted file mode 100644 index d33ab79f8..000000000 --- a/NzbDrone.Common/Messaging/CommandFailedEvent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace NzbDrone.Common.Messaging -{ - public class CommandFailedEvent : IEvent - { - public ICommand Command { get; private set; } - public Exception Exception { get; private set; } - - public CommandFailedEvent(ICommand command, Exception exception) - { - Command = command; - Exception = exception; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/CommandStartedEvent.cs b/NzbDrone.Common/Messaging/CommandStartedEvent.cs deleted file mode 100644 index 3cb4e7f55..000000000 --- a/NzbDrone.Common/Messaging/CommandStartedEvent.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace NzbDrone.Common.Messaging -{ - public class CommandExecutedEvent : IEvent - { - public ICommand Command { get; private set; } - - public CommandExecutedEvent(ICommand command) - { - Command = command; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/ICommand.cs b/NzbDrone.Common/Messaging/ICommand.cs deleted file mode 100644 index d9f8049ba..000000000 --- a/NzbDrone.Common/Messaging/ICommand.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace NzbDrone.Common.Messaging -{ - public interface ICommand : IMessage - { - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/IEvent.cs b/NzbDrone.Common/Messaging/IEvent.cs index 953709c13..00f40b449 100644 --- a/NzbDrone.Common/Messaging/IEvent.cs +++ b/NzbDrone.Common/Messaging/IEvent.cs @@ -1,4 +1,4 @@ -namespace NzbDrone.Common.Messaging +namespace NzbDrone.Common.Messaging { public interface IEvent : IMessage { diff --git a/NzbDrone.Common/Messaging/IMessageAggregator.cs b/NzbDrone.Common/Messaging/IMessageAggregator.cs deleted file mode 100644 index 6de5ac3c8..000000000 --- a/NzbDrone.Common/Messaging/IMessageAggregator.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace NzbDrone.Common.Messaging -{ - /// - /// Enables loosely-coupled publication of events. - /// - public interface IMessageAggregator - { - void PublishEvent(TEvent @event) where TEvent : class, IEvent; - void PublishCommand(TCommand command) where TCommand : class, ICommand; - void PublishCommand(string commandType); - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/MessageExtensions.cs b/NzbDrone.Common/Messaging/MessageExtensions.cs deleted file mode 100644 index 302aad869..000000000 --- a/NzbDrone.Common/Messaging/MessageExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace NzbDrone.Common.Messaging -{ - public static class MessageExtensions - { - public static string GetExecutorName(this Type commandType) - { - if (!typeof(ICommand).IsAssignableFrom(commandType)) - { - throw new ArgumentException("commandType must implement ICommand"); - } - - return string.Format("I{0}Executor", commandType.Name); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/TestCommand.cs b/NzbDrone.Common/Messaging/TestCommand.cs deleted file mode 100644 index 1a54d5764..000000000 --- a/NzbDrone.Common/Messaging/TestCommand.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace NzbDrone.Common.Messaging -{ - public class TestCommand : ICommand - { - public TestCommand() - { - Duration = 4000; - } - - public int Duration { get; set; } - - } -} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/TestCommandExecutor.cs b/NzbDrone.Common/Messaging/TestCommandExecutor.cs deleted file mode 100644 index be0ea4ea4..000000000 --- a/NzbDrone.Common/Messaging/TestCommandExecutor.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading; - -namespace NzbDrone.Common.Messaging -{ - public class TestCommandExecutor : IExecute - { - public void Execute(TestCommand message) - { - Thread.Sleep(message.Duration); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index c1b17236f..d55670a61 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -41,7 +41,7 @@ False - ..\packages\loggly-csharp.2.2\lib\Loggly.dll + ..\packages\loggly-csharp.2.3\lib\net35\Loggly.dll @@ -92,6 +92,11 @@ + + + + + @@ -99,23 +104,8 @@ - - - - - - - - - - - - - - - @@ -123,9 +113,6 @@ - - - diff --git a/NzbDrone.Common/NzbDrone.Common.ncrunchproject b/NzbDrone.Common/NzbDrone.Common.ncrunchproject index 8641d3614..1a2228e7f 100644 --- a/NzbDrone.Common/NzbDrone.Common.ncrunchproject +++ b/NzbDrone.Common/NzbDrone.Common.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,8 +12,11 @@ false true true - 60000 - - - AutoDetect + 5000 + Debug + x86 + + x86 + STA + x86 \ No newline at end of file diff --git a/NzbDrone.Common/PathEqualityComparer.cs b/NzbDrone.Common/PathEqualityComparer.cs index 2bf854727..32d6bf07c 100644 --- a/NzbDrone.Common/PathEqualityComparer.cs +++ b/NzbDrone.Common/PathEqualityComparer.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using NzbDrone.Common.EnvironmentInfo; namespace NzbDrone.Common diff --git a/NzbDrone.Common/ProcessProvider.cs b/NzbDrone.Common/ProcessProvider.cs index 8df4198e8..e93edd781 100644 --- a/NzbDrone.Common/ProcessProvider.cs +++ b/NzbDrone.Common/ProcessProvider.cs @@ -6,6 +6,7 @@ using System.Linq; using NLog; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Model; namespace NzbDrone.Common @@ -26,7 +27,7 @@ public interface IProcessProvider public class ProcessProvider : IProcessProvider { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public const string NZB_DRONE_PROCESS_NAME = "NzbDrone"; public const string NZB_DRONE_CONSOLE_PROCESS_NAME = "NzbDrone.Console"; @@ -212,7 +213,7 @@ private static ProcessInfo ConvertToProcessInfo(Process process) return new ProcessInfo { Id = process.Id, - StartPath = process.MainModule.FileName, + StartPath = GetExeFileName(process), Name = process.ProcessName }; } @@ -224,6 +225,17 @@ private static ProcessInfo ConvertToProcessInfo(Process process) return null; } + + private static string GetExeFileName(Process process) + { + if (process.MainModule.FileName != "mono.exe") + { + return process.MainModule.FileName; + } + + return process.Modules.Cast().FirstOrDefault(module => module.ModuleName.ToLower().EndsWith(".exe")).FileName; + } + private void Kill(int processId) { var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId); diff --git a/NzbDrone.Common/Security/IgnoreCertErrorPolicy.cs b/NzbDrone.Common/Security/IgnoreCertErrorPolicy.cs index 1be964380..809b0fe74 100644 --- a/NzbDrone.Common/Security/IgnoreCertErrorPolicy.cs +++ b/NzbDrone.Common/Security/IgnoreCertErrorPolicy.cs @@ -3,12 +3,13 @@ using System.Security.Cryptography.X509Certificates; using NLog; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Common.Security { public static class IgnoreCertErrorPolicy { - private static readonly Logger Logger = LogManager.GetLogger("CertPolicy"); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public static void Register() { diff --git a/NzbDrone.Common/ServiceProvider.cs b/NzbDrone.Common/ServiceProvider.cs index 7287e36df..8c2206a63 100644 --- a/NzbDrone.Common/ServiceProvider.cs +++ b/NzbDrone.Common/ServiceProvider.cs @@ -5,6 +5,7 @@ using System.Linq; using System.ServiceProcess; using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Common { @@ -25,7 +26,7 @@ public class ServiceProvider : IServiceProvider { public const string NZBDRONE_SERVICE_NAME = "NzbDrone"; - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public virtual bool ServiceExist(string name) { diff --git a/NzbDrone.Common/TPL/TaskExtensions.cs b/NzbDrone.Common/TPL/TaskExtensions.cs index e655ac86f..855ae225c 100644 --- a/NzbDrone.Common/TPL/TaskExtensions.cs +++ b/NzbDrone.Common/TPL/TaskExtensions.cs @@ -1,11 +1,12 @@ using System.Threading.Tasks; using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Common.TPL { public static class TaskExtensions { - private static readonly Logger Logger = LogManager.GetLogger("TaskExtensions"); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public static Task LogExceptions(this Task task) { diff --git a/NzbDrone.Common/TinyIoC.cs b/NzbDrone.Common/TinyIoC.cs index 90fff373f..70ba36762 100644 --- a/NzbDrone.Common/TinyIoC.cs +++ b/NzbDrone.Common/TinyIoC.cs @@ -70,7 +70,6 @@ namespace TinyIoC { using System; using System.Collections.Generic; - using System.Collections.ObjectModel; using System.Linq; using System.Reflection; diff --git a/NzbDrone.Common/packages.config b/NzbDrone.Common/packages.config index c1ce89767..7252d18f7 100644 --- a/NzbDrone.Common/packages.config +++ b/NzbDrone.Common/packages.config @@ -1,6 +1,6 @@  - + diff --git a/NzbDrone.Console/ConsoleApp.cs b/NzbDrone.Console/ConsoleApp.cs index 3119cc811..68de86f25 100644 --- a/NzbDrone.Console/ConsoleApp.cs +++ b/NzbDrone.Console/ConsoleApp.cs @@ -2,23 +2,29 @@ using System.Threading; using NLog; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation; using NzbDrone.Host; namespace NzbDrone.Console { public static class ConsoleApp { + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); + public static void Main(string[] args) { try { - Bootstrap.Start(new StartupArguments(args), new ConsoleAlerts()); + var startupArgs = new StartupArguments(args); + LogTargets.Register(startupArgs, false, true); + Bootstrap.Start(startupArgs, new ConsoleAlerts()); } catch (TerminateApplicationException) { } catch (Exception e) { + Logger.FatalException("EPIC FAIL!", e); System.Console.ReadLine(); } @@ -28,4 +34,4 @@ public static void Main(string[] args) } } } -} \ No newline at end of file +} diff --git a/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs b/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs index 98ad8b504..587e70697 100644 --- a/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs +++ b/NzbDrone.Core.Test/DataAugmentationFixture/Scene/SceneMappingProxyFixture.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using Newtonsoft.Json; using NzbDrone.Common; -using NzbDrone.Core.Configuration; using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.Test.Framework; @@ -13,7 +12,7 @@ namespace NzbDrone.Core.Test.DataAugmentationFixture.Scene public class SceneMappingProxyFixture : CoreTest { - private const string SCENE_MAPPING_URL = "http://services.nzbdrone.com/SceneMapping/Active"; + private const string SCENE_MAPPING_URL = "http://services.nzbdrone.com/v1/SceneMapping"; [Test] public void fetch_should_return_list_of_mappings() @@ -31,7 +30,6 @@ public void fetch_should_return_list_of_mappings() mappings.Should().NotContain(c => c.TvdbId == 0); } - [Test] public void should_throw_on_server_error() { diff --git a/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperFixture.cs b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperFixture.cs deleted file mode 100644 index c940ac7b2..000000000 --- a/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperFixture.cs +++ /dev/null @@ -1,84 +0,0 @@ -using FizzWare.NBuilder; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Datastore.Migration.Framework; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.Datastore -{ - [TestFixture] - public class SQLiteMigrationHelperFixture : DbTest - { - private SQLiteMigrationHelper _subject; - - [SetUp] - public void SetUp() - { - _subject = Mocker.Resolve(); - } - - - - [Test] - public void should_parse_existing_columns() - { - var columns = _subject.GetColumns("Series"); - - columns.Should().NotBeEmpty(); - - columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name)); - columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Schema)); - } - - [Test] - public void should_create_table_from_column_list() - { - var columns = _subject.GetColumns("Series"); - columns.Remove("Title"); - - _subject.CreateTable("Series_New", columns.Values); - - var newColumns = _subject.GetColumns("Series_New"); - - newColumns.Values.Should().HaveSameCount(columns.Values); - newColumns.Should().NotContainKey("Title"); - } - - [Test] - public void should_get_zero_count_on_empty_table() - { - _subject.GetRowCount("Series").Should().Be(0); - } - - - [Test] - public void should_be_able_to_transfer_empty_tables() - { - var columns = _subject.GetColumns("Series"); - columns.Remove("Title"); - - _subject.CreateTable("Series_New", columns.Values); - - - _subject.CopyData("Series", "Series_New", columns.Values); - } - - [Test] - public void should_transfer_table_with_data() - { - var originalEpisodes = Builder.CreateListOfSize(10).BuildListOfNew(); - - Mocker.Resolve().InsertMany(originalEpisodes); - - var columns = _subject.GetColumns("Episodes"); - columns.Remove("Title"); - - _subject.CreateTable("Episodes_New", columns.Values); - - _subject.CopyData("Episodes", "Episodes_New", columns.Values); - - _subject.GetRowCount("Episodes_New").Should().Be(originalEpisodes.Count); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/AlterFixture.cs b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/AlterFixture.cs new file mode 100644 index 000000000..7e1510259 --- /dev/null +++ b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/AlterFixture.cs @@ -0,0 +1,127 @@ +using System.Collections.Generic; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; +using System.Linq; + +namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests +{ + [TestFixture] + public class AlterFixture : DbTest + { + private SqLiteMigrationHelper _subject; + + [SetUp] + public void SetUp() + { + _subject = Mocker.Resolve(); + } + + [Test] + public void should_parse_existing_columns() + { + var columns = _subject.GetColumns("Series"); + + columns.Should().NotBeEmpty(); + + columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Name)); + columns.Values.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Schema)); + } + + [Test] + public void should_create_table_from_column_list() + { + var columns = _subject.GetColumns("Series"); + columns.Remove("Title"); + + _subject.CreateTable("Series_New", columns.Values, new List()); + + var newColumns = _subject.GetColumns("Series_New"); + + newColumns.Values.Should().HaveSameCount(columns.Values); + newColumns.Should().NotContainKey("Title"); + } + + + [Test] + public void should_be_able_to_transfer_empty_tables() + { + var columns = _subject.GetColumns("Series"); + var indexes = _subject.GetIndexes("Series"); + columns.Remove("Title"); + + _subject.CreateTable("Series_New", columns.Values, indexes); + + + _subject.CopyData("Series", "Series_New", columns.Values); + } + + [Test] + public void should_transfer_table_with_data() + { + var originalEpisodes = Builder.CreateListOfSize(10).BuildListOfNew(); + + Mocker.Resolve().InsertMany(originalEpisodes); + + var columns = _subject.GetColumns("Episodes"); + var indexes = _subject.GetIndexes("Episodes"); + + columns.Remove("Title"); + + _subject.CreateTable("Episodes_New", columns.Values, indexes); + + _subject.CopyData("Episodes", "Episodes_New", columns.Values); + + _subject.GetRowCount("Episodes_New").Should().Be(originalEpisodes.Count); + } + + [Test] + public void should_read_existing_indexes() + { + var indexes = _subject.GetIndexes("QualitySizes"); + + indexes.Should().NotBeEmpty(); + + indexes.Should().OnlyContain(c => c != null); + indexes.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Column)); + indexes.Should().OnlyContain(c => c.Table == "QualitySizes"); + indexes.Should().OnlyContain(c => c.Unique); + } + + [Test] + public void should_add_indexes_when_creating_new_table() + { + var columns = _subject.GetColumns("QualitySizes"); + var indexes = _subject.GetIndexes("QualitySizes"); + + + _subject.CreateTable("QualityB", columns.Values, indexes); + + + var newIndexes = _subject.GetIndexes("QualityB"); + + newIndexes.Should().HaveSameCount(indexes); + newIndexes.Select(c=>c.Column).Should().BeEquivalentTo(indexes.Select(c=>c.Column)); + } + + + [Test] + public void should_be_able_to_create_table_with_new_indexes() + { + var columns = _subject.GetColumns("Series"); + columns.Remove("Title"); + + _subject.CreateTable("Series_New", columns.Values, new List{new SQLiteIndex{Column = "AirTime", Table = "Series_New", Unique = true}}); + + var newColumns = _subject.GetColumns("Series_New"); + var newIndexes = _subject.GetIndexes("Series_New"); + + newColumns.Values.Should().HaveSameCount(columns.Values); + newIndexes.Should().Contain(i=>i.Column == "AirTime"); + } + + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/DuplicateFixture.cs b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/DuplicateFixture.cs new file mode 100644 index 000000000..d193fb1ec --- /dev/null +++ b/NzbDrone.Core.Test/Datastore/SQLiteMigrationHelperTests/DuplicateFixture.cs @@ -0,0 +1,41 @@ +using System.Linq; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.Datastore.SQLiteMigrationHelperTests +{ + [TestFixture] + public class DuplicateFixture : DbTest + { + private SqLiteMigrationHelper _subject; + + [SetUp] + public void SetUp() + { + _subject = Mocker.Resolve(); + } + + + [Test] + public void get_duplicates() + { + var series = Builder.CreateListOfSize(10) + .Random(3) + .With(c => c.QualityProfileId = 100) + .BuildListOfNew(); + + Db.InsertMany(series); + + var duplicates = _subject.GetDuplicates("series", "QualityProfileId").ToList(); + + + duplicates.Should().HaveCount(1); + duplicates.First().Should().HaveCount(3); + } + + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs index 32528beaf..5f511e3dc 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs @@ -26,14 +26,14 @@ public void Setup() { parseResultMulti = new RemoteEpisode { - Report = new ReportInfo(), + Release = new ReleaseInfo(), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) }, Episodes = new List { new Episode(), new Episode() } }; parseResultSingle = new RemoteEpisode { - Report = new ReportInfo(), + Release = new ReleaseInfo(), ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) }, Episodes = new List { new Episode() } @@ -59,7 +59,7 @@ public void Setup() public void IsAcceptableSize_true_single_episode_not_first_or_last_30_minute() { parseResultSingle.Series = series30minutes; - parseResultSingle.Report.Size = 184572800; + parseResultSingle.Release.Size = 184572800; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -78,7 +78,7 @@ public void IsAcceptableSize_true_single_episode_not_first_or_last_30_minute() public void IsAcceptableSize_true_single_episode_not_first_or_last_60_minute() { parseResultSingle.Series = series60minutes; - parseResultSingle.Report.Size = 368572800; + parseResultSingle.Release.Size = 368572800; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -97,7 +97,7 @@ public void IsAcceptableSize_true_single_episode_not_first_or_last_60_minute() public void IsAcceptableSize_false_single_episode_not_first_or_last_30_minute() { parseResultSingle.Series = series30minutes; - parseResultSingle.Report.Size = 1.Gigabytes(); + parseResultSingle.Release.Size = 1.Gigabytes(); Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -116,7 +116,7 @@ public void IsAcceptableSize_false_single_episode_not_first_or_last_30_minute() public void IsAcceptableSize_false_single_episode_not_first_or_last_60_minute() { parseResultSingle.Series = series60minutes; - parseResultSingle.Report.Size = 1.Gigabytes(); + parseResultSingle.Release.Size = 1.Gigabytes(); Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -133,7 +133,7 @@ public void IsAcceptableSize_false_single_episode_not_first_or_last_60_minute() public void IsAcceptableSize_true_multi_episode_not_first_or_last_30_minute() { parseResultMulti.Series = series30minutes; - parseResultMulti.Report.Size = 184572800; + parseResultMulti.Release.Size = 184572800; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -152,7 +152,7 @@ public void IsAcceptableSize_true_multi_episode_not_first_or_last_30_minute() public void IsAcceptableSize_true_multi_episode_not_first_or_last_60_minute() { parseResultMulti.Series = series60minutes; - parseResultMulti.Report.Size = 368572800; + parseResultMulti.Release.Size = 368572800; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -171,7 +171,7 @@ public void IsAcceptableSize_true_multi_episode_not_first_or_last_60_minute() public void IsAcceptableSize_false_multi_episode_not_first_or_last_30_minute() { parseResultMulti.Series = series30minutes; - parseResultMulti.Report.Size = 1.Gigabytes(); + parseResultMulti.Release.Size = 1.Gigabytes(); Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -190,7 +190,7 @@ public void IsAcceptableSize_false_multi_episode_not_first_or_last_30_minute() public void IsAcceptableSize_false_multi_episode_not_first_or_last_60_minute() { parseResultMulti.Series = series60minutes; - parseResultMulti.Report.Size = 10.Gigabytes(); + parseResultMulti.Release.Size = 10.Gigabytes(); Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -209,7 +209,7 @@ public void IsAcceptableSize_false_multi_episode_not_first_or_last_60_minute() public void IsAcceptableSize_true_single_episode_first_30_minute() { parseResultSingle.Series = series30minutes; - parseResultSingle.Report.Size = 184572800; + parseResultSingle.Release.Size = 184572800; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -228,7 +228,7 @@ public void IsAcceptableSize_true_single_episode_first_30_minute() public void IsAcceptableSize_true_single_episode_first_60_minute() { parseResultSingle.Series = series60minutes; - parseResultSingle.Report.Size = 368572800; + parseResultSingle.Release.Size = 368572800; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -247,7 +247,7 @@ public void IsAcceptableSize_true_single_episode_first_60_minute() public void IsAcceptableSize_false_single_episode_first_30_minute() { parseResultSingle.Series = series30minutes; - parseResultSingle.Report.Size = 1.Gigabytes(); + parseResultSingle.Release.Size = 1.Gigabytes(); Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -268,7 +268,7 @@ public void IsAcceptableSize_false_single_episode_first_60_minute() parseResultSingle.Series = series60minutes; - parseResultSingle.Report.Size = 10.Gigabytes(); + parseResultSingle.Release.Size = 10.Gigabytes(); Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -289,7 +289,7 @@ public void IsAcceptableSize_true_unlimited_30_minute() parseResultSingle.Series = series30minutes; - parseResultSingle.Report.Size = 18457280000; + parseResultSingle.Release.Size = 18457280000; qualityType.MaxSize = 0; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -311,7 +311,7 @@ public void IsAcceptableSize_true_unlimited_60_minute() parseResultSingle.Series = series60minutes; - parseResultSingle.Report.Size = 36857280000; + parseResultSingle.Release.Size = 36857280000; qualityType.MaxSize = 0; Mocker.GetMock().Setup(s => s.Get(1)).Returns(qualityType); @@ -334,7 +334,7 @@ public void IsAcceptableSize_should_treat_daily_series_as_single_episode() parseResultSingle.Series = series60minutes; parseResultSingle.Series.SeriesType = SeriesTypes.Daily; - parseResultSingle.Report.Size = 300.Megabytes(); + parseResultSingle.Release.Size = 300.Megabytes(); qualityType.MaxSize = (int)600.Megabytes(); diff --git a/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs new file mode 100644 index 000000000..7ff36a03b --- /dev/null +++ b/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs @@ -0,0 +1,48 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Tv; +using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.DecisionEngineTests +{ + [TestFixture] + public class CutoffSpecificationFixture : CoreTest + { + [Test] + public void should_return_true_if_current_episode_is_less_than_cutoff() + { + Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.Bluray1080p }, + new QualityModel(Quality.DVD, true)).Should().BeTrue(); + } + + [Test] + public void should_return_false_if_current_episode_is_equal_to_cutoff() + { + Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, + new QualityModel(Quality.HDTV720p, true)).Should().BeFalse(); + } + + [Test] + public void should_return_false_if_current_episode_is_greater_than_cutoff() + { + Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, + new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); + } + + [Test] + public void should_return_true_when_new_episode_is_proper_but_existing_is_not() + { + Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, + new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue(); + } + + [Test] + public void should_return_false_if_cutoff_is_met_and_quality_is_higher() + { + Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, + new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs index 171387772..d250c4d98 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs @@ -4,6 +4,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; @@ -15,7 +16,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [TestFixture] public class DownloadDecisionMakerFixture : CoreTest { - private List _reports; + private List _reports; private RemoteEpisode _remoteEpisode; private Mock _pass1; @@ -56,10 +57,11 @@ public void Setup() _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny(), null)).Returns(false); _fail3.Setup(c => c.RejectionReason).Returns("_fail3"); - _reports = new List { new ReportInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } }; + _reports = new List { new ReleaseInfo { Title = "The.Office.S03E115.DVDRip.XviD-OSiTV" } }; _remoteEpisode = new RemoteEpisode { Series = new Series() }; - Mocker.GetMock().Setup(c => c.Map(It.IsAny(), It.IsAny())) + Mocker.GetMock() + .Setup(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(_remoteEpisode); } @@ -130,7 +132,7 @@ public void should_not_attempt_to_map_episode_if_not_parsable() var results = Subject.GetRssDecision(_reports).ToList(); - Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny()), Times.Never()); + Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); @@ -146,7 +148,7 @@ [Test] public void should_not_attempt_to_map_episode_series_title_is_blank() var results = Subject.GetRssDecision(_reports).ToList(); - Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny()), Times.Never()); + Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); _pass1.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); _pass2.Verify(c => c.IsSatisfiedBy(It.IsAny(), null), Times.Never()); @@ -174,19 +176,19 @@ public void broken_report_shouldnt_blowup_the_process() { GivenSpecifications(_pass1); - Mocker.GetMock().Setup(c => c.Map(It.IsAny(), It.IsAny())) + Mocker.GetMock().Setup(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(); - _reports = new List + _reports = new List { - new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, - new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, - new ReportInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"} + new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, + new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"}, + new ReleaseInfo{Title = "The.Office.S03E115.DVDRip.XviD-OSiTV"} }; Subject.GetRssDecision(_reports); - Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny()), Times.Exactly(_reports.Count)); + Mocker.GetMock().Verify(c => c.Map(It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(_reports.Count)); ExceptionVerification.ExpectedErrors(3); } diff --git a/NzbDrone.Core.Test/DecisionEngineTests/NotRestrictedReleaseSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/NotRestrictedReleaseSpecificationFixture.cs index f9bcf475e..cdbce8077 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/NotRestrictedReleaseSpecificationFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/NotRestrictedReleaseSpecificationFixture.cs @@ -17,7 +17,7 @@ public void Setup() { _parseResult = new RemoteEpisode { - Report = new ReportInfo + Release = new ReleaseInfo { Title = "Dexter.S08E01.EDITED.WEBRip.x264-KYR" } diff --git a/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradableSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradableSpecificationFixture.cs deleted file mode 100644 index c7eb4bf4b..000000000 --- a/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradableSpecificationFixture.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.DecisionEngineTests -{ - [TestFixture] - public class QualityUpgradableSpecificationFixture : CoreTest - { - [Test] - public void IsUpgradePossible_should_return_true_if_current_episode_is_less_than_cutoff() - { - Subject.IsUpgradable(new QualityProfile { Cutoff = Quality.Bluray1080p }, - new QualityModel(Quality.DVD, true)).Should().BeTrue(); - } - - [Test] - public void IsUpgradePossible_should_return_false_if_current_episode_is_equal_to_cutoff() - { - Subject.IsUpgradable(new QualityProfile { Cutoff = Quality.HDTV720p }, - new QualityModel(Quality.HDTV720p, true)).Should().BeFalse(); - } - - [Test] - public void IsUpgradePossible_should_return_false_if_current_episode_is_greater_than_cutoff() - { - Subject.IsUpgradable(new QualityProfile { Cutoff = Quality.HDTV720p }, - new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs index 2a489ee8a..cc631b871 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs @@ -17,7 +17,6 @@ public class QualityUpgradeSpecificationFixture : CoreTest + { + private RemoteEpisode _parseResultMulti; + private RemoteEpisode _parseResultSingle; + private EpisodeFile _firstFile; + private EpisodeFile _secondFile; + + [SetUp] + public void Setup() + { + Mocker.Resolve(); + + _firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now }; + _secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now }; + + var singleEpisodeList = new List { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; + var doubleEpisodeList = new List { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; + + var fakeSeries = Builder.CreateNew() + .With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p }) + .Build(); + + _parseResultMulti = new RemoteEpisode + { + Series = fakeSeries, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + Episodes = doubleEpisodeList + }; + + _parseResultSingle = new RemoteEpisode + { + Series = fakeSeries, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + Episodes = singleEpisodeList + }; + } + + private void WithFirstFileUpgradable() + { + _firstFile.Quality = new QualityModel(Quality.SDTV); + } + + private void GivenAutoDownloadPropers() + { + Mocker.GetMock() + .Setup(s => s.AutoDownloadPropers) + .Returns(true); + } + + [Test] + public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago() + { + _firstFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_first_episodeFile_was_added_more_than_7_days_ago() + { + _firstFile.Quality.Quality = Quality.DVD; + _secondFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_second_episodeFile_was_added_more_than_7_days_ago() + { + _firstFile.Quality.Quality = Quality.DVD; + _secondFile.Quality.Quality = Quality.DVD; + + _secondFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); + } + + [Test] + public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_proper_is_for_better_quality() + { + WithFirstFileUpgradable(); + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue(); + } + + [Test] + public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_is_for_search() + { + WithFirstFileUpgradable(); + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria()).Should().BeTrue(); + } + + [Test] + public void should_return_false_when_proper_but_auto_download_propers_is_false() + { + _firstFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today; + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); + } + + [Test] + public void should_return_true_when_episodeFile_was_added_today() + { + GivenAutoDownloadPropers(); + + _firstFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today; + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs index db083a6b9..eb85c757d 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs @@ -4,7 +4,6 @@ using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.DecisionEngine.Specifications; -using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -124,52 +123,5 @@ public void should_not_be_upgradable_if_qualities_are_the_same() _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false); _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); } - - [Test] - public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago() - { - _firstFile.Quality.Quality = Quality.DVD; - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); - } - - [Test] - public void should_return_false_when_first_episodeFile_was_added_more_than_7_days_ago() - { - _firstFile.Quality.Quality = Quality.DVD; - _secondFile.Quality.Quality = Quality.DVD; - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); - } - - [Test] - public void should_return_false_when_second_episodeFile_was_added_more_than_7_days_ago() - { - _firstFile.Quality.Quality = Quality.DVD; - _secondFile.Quality.Quality = Quality.DVD; - - _secondFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); - } - - [Test] - public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_proper_is_for_better_quality() - { - WithFirstFileUpgradable(); - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue(); - } - - [Test] - public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_is_for_search() - { - WithFirstFileUpgradable(); - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria()).Should().BeTrue(); - } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs b/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs index 8c5c5ecb7..f13c81ccb 100644 --- a/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/DownloadApprovedFixture.cs @@ -4,7 +4,7 @@ using FluentAssertions; using Moq; using NUnit.Framework; -using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -34,8 +34,8 @@ private RemoteEpisode GetRemoteEpisode(List episodes, QualityModel qual remoteEpisode.Episodes = new List(); remoteEpisode.Episodes.AddRange(episodes); - remoteEpisode.Report = new ReportInfo(); - remoteEpisode.Report.Age = 0; + remoteEpisode.Release = new ReleaseInfo(); + remoteEpisode.Release.PublishDate = DateTime.UtcNow; return remoteEpisode; } diff --git a/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs b/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs index d1be02648..370142d75 100644 --- a/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadApprovedReportsTests/GetQualifiedReportsFixture.cs @@ -1,9 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; -using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Download; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; @@ -32,9 +33,9 @@ private RemoteEpisode GetRemoteEpisode(List episodes, QualityModel qual remoteEpisode.Episodes = new List(); remoteEpisode.Episodes.AddRange(episodes); - remoteEpisode.Report = new ReportInfo(); - remoteEpisode.Report.Age = Age; - remoteEpisode.Report.Size = size; + remoteEpisode.Release = new ReleaseInfo(); + remoteEpisode.Release.PublishDate = DateTime.Now.AddDays(-Age); + remoteEpisode.Release.Size = size; return remoteEpisode; } @@ -110,9 +111,9 @@ public void should_order_by_lowest_number_of_episodes_with_multiple_episodes() public void should_order_by_smallest_rounded_to_200mb_then_age() { var remoteEpisodeSd = GetRemoteEpisode(new List { GetEpisode(1) }, new QualityModel(Quality.SDTV), size: 100.Megabytes(), Age: 1); - var remoteEpisodeHdSmallOld = GetRemoteEpisode(new List { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size:1200.Megabytes(), Age:1000); - var remoteEpisodeHdSmallYounge = GetRemoteEpisode(new List { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size:1250.Megabytes(), Age:10); - var remoteEpisodeHdLargeYounge = GetRemoteEpisode(new List { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size:3000.Megabytes(), Age:1); + var remoteEpisodeHdSmallOld = GetRemoteEpisode(new List { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 1200.Megabytes(), Age: 1000); + var remoteEpisodeHdSmallYounge = GetRemoteEpisode(new List { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 1250.Megabytes(), Age: 10); + var remoteEpisodeHdLargeYounge = GetRemoteEpisode(new List { GetEpisode(1) }, new QualityModel(Quality.HDTV720p), size: 3000.Megabytes(), Age: 1); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisodeSd)); diff --git a/NzbDrone.Core.Test/Download/DownloadClientTests/BlackholeProviderFixture.cs b/NzbDrone.Core.Test/Download/DownloadClientTests/BlackholeProviderFixture.cs index 6229aaf8a..9de3178b9 100644 --- a/NzbDrone.Core.Test/Download/DownloadClientTests/BlackholeProviderFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadClientTests/BlackholeProviderFixture.cs @@ -30,9 +30,9 @@ public void Setup() Mocker.GetMock().SetupGet(c => c.BlackholeFolder).Returns(_blackHoleFolder); _remoteEpisode = new RemoteEpisode(); - _remoteEpisode.Report = new ReportInfo(); - _remoteEpisode.Report.Title = _title; - _remoteEpisode.Report.NzbUrl = _nzbUrl; + _remoteEpisode.Release = new ReleaseInfo(); + _remoteEpisode.Release.Title = _title; + _remoteEpisode.Release.DownloadUrl = _nzbUrl; } private void WithExistingFile() @@ -58,7 +58,7 @@ public void should_replace_illegal_characters_in_title() { var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]"; var expectedFilename = Path.Combine(_blackHoleFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb"); - _remoteEpisode.Report.Title = illegalTitle; + _remoteEpisode.Release.Title = illegalTitle; Subject.DownloadNzb(_remoteEpisode); diff --git a/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs b/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs index 638613108..c897df251 100644 --- a/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using FizzWare.NBuilder; -using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Common; @@ -31,9 +30,9 @@ public void Setup() fakeConfig.SetupGet(c => c.NzbgetRecentTvPriority).Returns(PriorityType.High); _remoteEpisode = new RemoteEpisode(); - _remoteEpisode.Report = new ReportInfo(); - _remoteEpisode.Report.Title = _title; - _remoteEpisode.Report.NzbUrl = _url; + _remoteEpisode.Release = new ReleaseInfo(); + _remoteEpisode.Release.Title = _title; + _remoteEpisode.Release.DownloadUrl = _url; _remoteEpisode.Episodes = Builder.CreateListOfSize(1) .All() diff --git a/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs b/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs index 66b3695cc..cfac3a99a 100644 --- a/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadClientTests/PneumaticProviderFixture.cs @@ -34,9 +34,9 @@ public void Setup() Mocker.GetMock().SetupGet(c => c.DownloadedEpisodesFolder).Returns(_sabDrop); _remoteEpisode = new RemoteEpisode(); - _remoteEpisode.Report = new ReportInfo(); - _remoteEpisode.Report.Title = _title; - _remoteEpisode.Report.NzbUrl = _nzbUrl; + _remoteEpisode.Release = new ReleaseInfo(); + _remoteEpisode.Release.Title = _title; + _remoteEpisode.Release.DownloadUrl = _nzbUrl; _remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); _remoteEpisode.ParsedEpisodeInfo.FullSeason = false; @@ -72,7 +72,7 @@ public void should_throw_on_failed_download() [Test] public void should_throw_if_full_season_download() { - _remoteEpisode.Report.Title = "30 Rock - Season 1"; + _remoteEpisode.Release.Title = "30 Rock - Season 1"; _remoteEpisode.ParsedEpisodeInfo.FullSeason = true; Assert.Throws(() => Subject.DownloadNzb(_remoteEpisode)); @@ -83,7 +83,7 @@ public void should_replace_illegal_characters_in_title() { var illegalTitle = "Saturday Night Live - S38E08 - Jeremy Renner/Maroon 5 [SDTV]"; var expectedFilename = Path.Combine(_pneumaticFolder, "Saturday Night Live - S38E08 - Jeremy Renner+Maroon 5 [SDTV].nzb"); - _remoteEpisode.Report.Title = illegalTitle; + _remoteEpisode.Release.Title = illegalTitle; Subject.DownloadNzb(_remoteEpisode); diff --git a/NzbDrone.Core.Test/Download/DownloadClientTests/SabProviderTests/SabProviderFixture.cs b/NzbDrone.Core.Test/Download/DownloadClientTests/SabProviderTests/SabProviderFixture.cs index 111347602..f23a2b762 100644 --- a/NzbDrone.Core.Test/Download/DownloadClientTests/SabProviderTests/SabProviderFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadClientTests/SabProviderTests/SabProviderFixture.cs @@ -11,7 +11,6 @@ using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; -using NzbDrone.Test.Common; namespace NzbDrone.Core.Test.Download.DownloadClientTests.SabProviderTests { @@ -36,9 +35,9 @@ public void Setup() fakeConfig.SetupGet(c => c.SabTvCategory).Returns("tv"); _remoteEpisode = new RemoteEpisode(); - _remoteEpisode.Report = new ReportInfo(); - _remoteEpisode.Report.Title = TITLE; - _remoteEpisode.Report.NzbUrl = URL; + _remoteEpisode.Release = new ReleaseInfo(); + _remoteEpisode.Release.Title = TITLE; + _remoteEpisode.Release.DownloadUrl = URL; _remoteEpisode.Episodes = Builder.CreateListOfSize(1) .All() diff --git a/NzbDrone.Core.Test/Download/DownloadServiceFixture.cs b/NzbDrone.Core.Test/Download/DownloadServiceFixture.cs index cc0285b78..86413bcc4 100644 --- a/NzbDrone.Core.Test/Download/DownloadServiceFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadServiceFixture.cs @@ -30,7 +30,7 @@ public void Setup() _parseResult = Builder.CreateNew() .With(c => c.Series = Builder.CreateNew().Build()) - .With(c => c.Report = Builder.CreateNew().Build()) + .With(c => c.Release = Builder.CreateNew().Build()) .With(c => c.Episodes = episodes) .Build(); diff --git a/NzbDrone.Core.Test/Files/Media/H264_sample.mp4 b/NzbDrone.Core.Test/Files/Media/H264_sample.mp4 new file mode 100644 index 000000000..35bc6b353 Binary files /dev/null and b/NzbDrone.Core.Test/Files/Media/H264_sample.mp4 differ diff --git a/NzbDrone.Core.Test/Files/SceneMappings.json b/NzbDrone.Core.Test/Files/SceneMappings.json index a914ecdc9..71e1f5937 100644 --- a/NzbDrone.Core.Test/Files/SceneMappings.json +++ b/NzbDrone.Core.Test/Files/SceneMappings.json @@ -1,27 +1,32 @@ [ { - "CleanTitle": "csinewyork", - "Id": "73696", - "Title": "CSI" + "title": "Adventure Time", + "searchTitle": "Adventure Time", + "season": -1, + "tvdbId": 152831 }, { - "CleanTitle": "csiny", - "Id": "73696", - "Title": "CSI" + "title": "Americas Funniest Home Videos", + "searchTitle": "Americas Funniest Home Videos", + "season": -1, + "tvdbId": 76235 }, { - "CleanTitle": "csi", - "Id": "72546", - "Title": "CSI" + "title": "Antiques Roadshow UK", + "searchTitle": "Antiques Roadshow UK", + "season": -1, + "tvdbId": 83774 }, { - "CleanTitle": "csilasvegas", - "Id": "72546", - "Title": "CSI" + "title": "Aqua Something You Know Whatever", + "searchTitle": "Aqua Something You Know Whatever", + "season": 9, + "tvdbId": 77120 }, { - "CleanTitle": "archer", - "Id": "110381", - "Title": "Archer" + "title": "Aqua Teen Hunger Force", + "searchTitle": "Aqua Teen Hunger Force", + "season": -1, + "tvdbId": 77120 } ] \ No newline at end of file diff --git a/NzbDrone.Core.Test/Framework/DbTest.cs b/NzbDrone.Core.Test/Framework/DbTest.cs index ec94f7418..f8d3a0979 100644 --- a/NzbDrone.Core.Test/Framework/DbTest.cs +++ b/NzbDrone.Core.Test/Framework/DbTest.cs @@ -6,13 +6,15 @@ using Marr.Data; using Moq; using NUnit.Framework; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Test.Framework { - [Category("DbTest")] + public abstract class DbTest : DbTest where TSubject : class where TModel : ModelBase, new() @@ -61,7 +63,7 @@ protected TSubject Subject - + [Category("DbTest")] public abstract class DbTest : CoreTest { private ITestDatabase _db; @@ -93,7 +95,7 @@ private void WithTestDb() Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); - Mocker.SetConstant(Mocker.Resolve()); + Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); Mocker.SetConstant(Mocker.Resolve()); @@ -147,27 +149,27 @@ public interface ITestDatabase public class TestDatabase : ITestDatabase { private readonly IDatabase _dbConnection; - private IMessageAggregator _messageAggregator; + private IEventAggregator _eventAggregator; public TestDatabase(IDatabase dbConnection) { - _messageAggregator = new Mock().Object; + _eventAggregator = new Mock().Object; _dbConnection = dbConnection; } public void InsertMany(IEnumerable items) where T : ModelBase, new() { - new BasicRepository(_dbConnection, _messageAggregator).InsertMany(items.ToList()); + new BasicRepository(_dbConnection, _eventAggregator).InsertMany(items.ToList()); } public T Insert(T item) where T : ModelBase, new() { - return new BasicRepository(_dbConnection, _messageAggregator).Insert(item); + return new BasicRepository(_dbConnection, _eventAggregator).Insert(item); } public List All() where T : ModelBase, new() { - return new BasicRepository(_dbConnection, _messageAggregator).All().ToList(); + return new BasicRepository(_dbConnection, _eventAggregator).All().ToList(); } public T Single() where T : ModelBase, new() @@ -177,12 +179,12 @@ public TestDatabase(IDatabase dbConnection) public void Update(T childModel) where T : ModelBase, new() { - new BasicRepository(_dbConnection, _messageAggregator).Update(childModel); + new BasicRepository(_dbConnection, _eventAggregator).Update(childModel); } public void Delete(T childModel) where T : ModelBase, new() { - new BasicRepository(_dbConnection, _messageAggregator).Delete(childModel); + new BasicRepository(_dbConnection, _eventAggregator).Delete(childModel); } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs b/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs index 4ac080e2a..2eca3586b 100644 --- a/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs +++ b/NzbDrone.Core.Test/IndexerTests/BasicRssParserFixture.cs @@ -5,7 +5,7 @@ namespace NzbDrone.Core.Test.IndexerTests { - public class BasicRssParserFixture : CoreTest + public class BasicRssParserFixture : CoreTest { [TestCase("Castle.2009.S01E14.English.HDTV.XviD-LOL", "LOL")] @@ -16,7 +16,7 @@ public class BasicRssParserFixture : CoreTest [TestCase("The.Office.S03E115.DVDRip.XviD-OSiTV", "OSiTV")] public void parse_releaseGroup(string title, string expected) { - BasicRssParser.ParseReleaseGroup(title).Should().Be(expected); + RssParserBase.ParseReleaseGroup(title).Should().Be(expected); } @@ -29,7 +29,7 @@ public void parse_releaseGroup(string title, string expected) [TestCase("845 MB", 886046720)] public void parse_size(string sizeString, long expectedSize) { - var result = BasicRssParser.GetReportSize(sizeString); + var result = RssParserBase.ParseSize(sizeString); result.Should().Be(expectedSize); } diff --git a/NzbDrone.Core.Test/IndexerTests/IndexerServiceFixture.cs b/NzbDrone.Core.Test/IndexerTests/IndexerServiceFixture.cs index 3e6239823..2de52fb5f 100644 --- a/NzbDrone.Core.Test/IndexerTests/IndexerServiceFixture.cs +++ b/NzbDrone.Core.Test/IndexerTests/IndexerServiceFixture.cs @@ -6,7 +6,6 @@ using NUnit.Framework; using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers.Newznab; -using NzbDrone.Core.Indexers.NzbClub; using NzbDrone.Core.Indexers.Omgwtfnzbs; using NzbDrone.Core.Indexers.Wombles; using NzbDrone.Core.Lifecycle; @@ -24,7 +23,6 @@ public void Setup() _indexers = new List(); _indexers.Add(new Newznab()); - _indexers.Add(new NzbClub()); _indexers.Add(new Omgwtfnzbs()); _indexers.Add(new Wombles()); diff --git a/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs b/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs index 813c1a45f..1be142824 100644 --- a/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs +++ b/NzbDrone.Core.Test/IndexerTests/IntegrationTests/IndexerIntegrationTests.cs @@ -1,13 +1,14 @@ using System.Collections.Generic; using FluentAssertions; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Indexers.Eztv; using NzbDrone.Core.Indexers.Newznab; -using NzbDrone.Core.Indexers.NzbClub; using NzbDrone.Core.Indexers.Wombles; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; using NUnit.Framework; using NzbDrone.Test.Common.Categories; +using System.Linq; namespace NzbDrone.Core.Test.IndexerTests.IntegrationTests { @@ -21,17 +22,6 @@ public void SetUp() } - [Test] - [Explicit] - public void nzbclub_rss() - { - var indexer = new NzbClub(); - - var result = Subject.FetchRss(indexer); - - ValidateResult(result); - } - [Test] public void wombles_rss() { @@ -43,6 +33,17 @@ public void wombles_rss() } + [Test] + public void extv_rss() + { + var indexer = new Eztv(); + + var result = Subject.FetchRss(indexer); + + ValidateTorrentResult(result, skipSize: false, skipInfo: true); + } + + [Test] public void nzbsorg_rss() { @@ -63,15 +64,17 @@ public void nzbsorg_rss() - private void ValidateResult(IList reports, bool skipSize = false, bool skipInfo = false) + private void ValidateResult(IList reports, bool skipSize = false, bool skipInfo = false) { reports.Should().NotBeEmpty(); - reports.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.Title)); - reports.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.NzbUrl)); + reports.Should().NotContain(c => string.IsNullOrWhiteSpace(c.Title)); + reports.Should().NotContain(c => string.IsNullOrWhiteSpace(c.DownloadUrl)); + reports.Should().OnlyContain(c => c.PublishDate.Year > 2000); + reports.Should().OnlyContain(c => c.DownloadUrl.StartsWith("http")); if (!skipInfo) { - reports.Should().OnlyContain(c => !string.IsNullOrWhiteSpace(c.NzbInfoUrl)); + reports.Should().NotContain(c => string.IsNullOrWhiteSpace(c.InfoUrl)); } if (!skipSize) @@ -80,5 +83,18 @@ private void ValidateResult(IList reports, bool skipSize = false, bo } } + private void ValidateTorrentResult(IList reports, bool skipSize = false, bool skipInfo = false) + { + + reports.Should().OnlyContain(c => c.GetType() == typeof(TorrentInfo)); + + ValidateResult(reports, skipSize, skipInfo); + + reports.Should().OnlyContain(c => c.DownloadUrl.EndsWith(".torrent")); + + reports.Cast().Should().OnlyContain(c => c.MagnetUrl.StartsWith("magnet:")); + reports.Cast().Should().NotContain(c => string.IsNullOrWhiteSpace(c.InfoHash)); + } + } -} \ No newline at end of file +} diff --git a/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs b/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs index 6bb9644a9..2b88348aa 100644 --- a/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs +++ b/NzbDrone.Core.Test/InstrumentationTests/DatabaseTargetFixture.cs @@ -3,6 +3,7 @@ using FluentAssertions; using NLog; using NUnit.Framework; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Datastore; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Instrumentation; @@ -14,8 +15,6 @@ namespace NzbDrone.Core.Test.InstrumentationTests [TestFixture] public class DatabaseTargetFixture : DbTest { - string _loggerName; - private static string _uniqueMessage; Logger _logger; @@ -36,8 +35,7 @@ public void Setup() LogManager.ReconfigExistingLoggers(); - _logger = LogManager.GetCurrentClassLogger(); - _loggerName = _logger.Name.Replace("NzbDrone.",""); + _logger = NzbDroneLogger.GetLogger(); _uniqueMessage = "Unique message: " + Guid.NewGuid().ToString(); } @@ -119,7 +117,7 @@ public void Teardown() private void VerifyLog(Log logItem, LogLevel level) { logItem.Time.Should().BeWithin(TimeSpan.FromSeconds(2)); - logItem.Logger.Should().Be(_loggerName); + logItem.Logger.Should().Be(this.GetType().Name); logItem.Level.Should().Be(level.Name); logItem.Method.Should().Be(new StackTrace().GetFrame(1).GetMethod().Name); _logger.Name.Should().EndWith(logItem.Logger); diff --git a/NzbDrone.Core.Test/MediaFileTests/DropFolderImportServiceFixture.cs b/NzbDrone.Core.Test/MediaFiles/DownloadedEpisodesImportServiceFixture.cs similarity index 97% rename from NzbDrone.Core.Test/MediaFileTests/DropFolderImportServiceFixture.cs rename to NzbDrone.Core.Test/MediaFiles/DownloadedEpisodesImportServiceFixture.cs index 870e5025a..09e51ac3a 100644 --- a/NzbDrone.Core.Test/MediaFileTests/DropFolderImportServiceFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/DownloadedEpisodesImportServiceFixture.cs @@ -15,10 +15,10 @@ using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests +namespace NzbDrone.Core.Test.MediaFiles { [TestFixture] - public class DropFolderImportServiceFixture : CoreTest + public class DownloadedEpisodesImportServiceFixture : CoreTest { private string[] _subFolders = new[] { "c:\\root\\foldername".AsOsAgnostic() }; private string[] _videoFiles = new[] { "c:\\root\\foldername\\video.ext".AsOsAgnostic() }; @@ -75,7 +75,7 @@ public void should_skip_import_if_dropfolder_doesnt_exist() [Test] public void should_skip_if_file_is_in_use_by_another_process() { - Mocker.GetMock().Setup(c => c.IsFileLocked(It.IsAny())) + Mocker.GetMock().Setup(c => c.IsFileLocked(It.IsAny())) .Returns(true); Subject.Execute(new DownloadedEpisodesScanCommand()); diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/ImportDecisionMakerFixture.cs b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs similarity index 97% rename from NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/ImportDecisionMakerFixture.cs rename to NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs index 386c2a9ff..68dfdc5db 100644 --- a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/ImportDecisionMakerFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/ImportDecisionMakerFixture.cs @@ -13,12 +13,12 @@ using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport { [TestFixture] public class ImportDecisionMakerFixture : CoreTest { - private List _videoFiles; + private List _videoFiles; private LocalEpisode _localEpisode; private Series _series; @@ -60,7 +60,7 @@ public void Setup() _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny())).Returns(false); _fail3.Setup(c => c.RejectionReason).Returns("_fail3"); - _videoFiles = new List { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" }; + _videoFiles = new List { @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" }; _series = new Series(); _localEpisode = new LocalEpisode { Series = _series, Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi" }; diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/FreeSpaceSpecificationFixture.cs b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecificationFixture.cs similarity index 65% rename from NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/FreeSpaceSpecificationFixture.cs rename to NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecificationFixture.cs index 47804bde0..4f1f545a4 100644 --- a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/FreeSpaceSpecificationFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecificationFixture.cs @@ -8,12 +8,11 @@ using NzbDrone.Common; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Providers; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications { [TestFixture] public class FreeSpaceSpecificationFixture : CoreTest @@ -40,7 +39,7 @@ public void Setup() _localEpisode = new LocalEpisode { - Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", + Path = @"C:\Test\Unsorted\30 Rock\30.rock.s01e01.avi".AsOsAgnostic(), Episodes = episodes, Series = _series }; @@ -51,7 +50,7 @@ private void GivenFileSize(long size) _localEpisode.Size = size; } - private void GivenFreeSpace(long size) + private void GivenFreeSpace(long? size) { Mocker.GetMock() .Setup(s => s.GetAvailableSpace(It.IsAny())) @@ -98,5 +97,50 @@ public void should_use_series_paths_parent_for_free_space_check() Mocker.GetMock() .Verify(v => v.GetAvailableSpace(_rootFolder), Times.Once()); } + + [Test] + public void should_pass_if_free_space_is_null() + { + GivenFileSize(100.Megabytes()); + GivenFreeSpace(null); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + } + + [Test] + public void should_pass_if_exception_is_thrown() + { + GivenFileSize(100.Megabytes()); + + Mocker.GetMock() + .Setup(s => s.GetAvailableSpace(It.IsAny())) + .Throws(new TestException()); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + ExceptionVerification.ExpectedErrors(1); + } + + [Test] + public void should_skip_check_for_files_under_series_folder() + { + _localEpisode.ExistingFile = true; + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + + Mocker.GetMock() + .Verify(s => s.GetAvailableSpace(It.IsAny()), Times.Never()); + } + + [Test] + public void should_return_true_if_free_space_is_null() + { + long? freeSpace = null; + + Mocker.GetMock() + .Setup(s => s.GetAvailableSpace(It.IsAny())) + .Returns(freeSpace); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + } } } diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotInUseSpecificationFixture.cs b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotInUseSpecificationFixture.cs similarity index 77% rename from NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotInUseSpecificationFixture.cs rename to NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotInUseSpecificationFixture.cs index 98080014b..7887e40aa 100644 --- a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotInUseSpecificationFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotInUseSpecificationFixture.cs @@ -1,20 +1,15 @@ -using System.IO; -using System.Linq; -using FizzWare.NBuilder; +using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using Moq; -using Newtonsoft.Json.Serialization; using NUnit.Framework; using NzbDrone.Common; -using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications { [TestFixture] public class NotInUseSpecificationFixture : CoreTest @@ -34,9 +29,7 @@ public void Setup() private void GivenChildOfSeries() { - Mocker.GetMock() - .Setup(s => s.IsParent(_localEpisode.Series.Path, _localEpisode.Path)) - .Returns(true); + _localEpisode.ExistingFile = true; } private void GivenNewFile() @@ -62,7 +55,7 @@ public void should_not_check_for_file_in_use_if_child_of_series_folder() Subject.IsSatisfiedBy(_localEpisode); Mocker.GetMock() - .Verify(v => v.IsFileLocked(It.IsAny()), Times.Never()); + .Verify(v => v.IsFileLocked(It.IsAny()), Times.Never()); } [Test] @@ -71,7 +64,7 @@ public void should_return_false_if_file_is_in_use() GivenNewFile(); Mocker.GetMock() - .Setup(s => s.IsFileLocked(It.IsAny())) + .Setup(s => s.IsFileLocked(It.IsAny())) .Returns(true); Subject.IsSatisfiedBy(_localEpisode).Should().BeFalse(); @@ -83,7 +76,7 @@ public void should_return_true_if_file_is_not_in_use() GivenNewFile(); Mocker.GetMock() - .Setup(s => s.IsFileLocked(It.IsAny())) + .Setup(s => s.IsFileLocked(It.IsAny())) .Returns(false); Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotSampleSpecificationFixture.cs b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs similarity index 97% rename from NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotSampleSpecificationFixture.cs rename to NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs index f89cfa48d..c3f5b0429 100644 --- a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotSampleSpecificationFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotSampleSpecificationFixture.cs @@ -5,13 +5,14 @@ using Moq; using NUnit.Framework; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; +using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Providers; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications { [TestFixture] public class NotSampleSpecificationFixture : CoreTest diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotUnpackingSpecificationFixture.cs b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecificationFixture.cs similarity index 91% rename from NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotUnpackingSpecificationFixture.cs rename to NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecificationFixture.cs index 102f7a4f1..05a45a1f6 100644 --- a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/NotUnpackingSpecificationFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecificationFixture.cs @@ -1,22 +1,17 @@ using System; -using System.IO; -using System.Linq; using FizzWare.NBuilder; using FluentAssertions; -using Marr.Data; using Moq; -using Newtonsoft.Json.Serialization; using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Core.Configuration; -using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications { [TestFixture] public class NotUnpackingSpecificationFixture : CoreTest diff --git a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/UpgradeSpecificationFixture.cs b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs similarity index 99% rename from NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/UpgradeSpecificationFixture.cs rename to NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs index 9ac31aa1b..a0d0f7aef 100644 --- a/NzbDrone.Core.Test/MediaFileTests/EpisodeImportTests/UpgradeSpecificationFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs @@ -10,7 +10,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; -namespace NzbDrone.Core.Test.MediaFileTests.EpisodeImportTests +namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications { [TestFixture] public class UpgradeSpecificationFixture : CoreTest diff --git a/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs b/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs similarity index 95% rename from NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs rename to NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs index a7d13ff0c..b1cab5f58 100644 --- a/NzbDrone.Core.Test/MediaFileTests/ImportApprovedEpisodesFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/ImportApprovedEpisodesFixture.cs @@ -4,17 +4,18 @@ using FluentAssertions; using Moq; using NUnit.Framework; -using NzbDrone.Common.Messaging; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests +namespace NzbDrone.Core.Test.MediaFiles { [TestFixture] public class ImportApprovedEpisodesFixture : CoreTest @@ -103,7 +104,7 @@ public void should_publish_EpisodeImportedEvent_for_new_downloads() { Subject.Import(new List { _approvedDecisions.First() }, true); - Mocker.GetMock() + Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Once()); } @@ -122,7 +123,7 @@ public void should_not_trigger_EpisodeImportedEvent_for_existing_files() { Subject.Import(new List { _approvedDecisions.First() }); - Mocker.GetMock() + Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Never()); } } diff --git a/NzbDrone.Core.Test/MediaFileTests/MediaFileRepositoryFixture.cs b/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs similarity index 97% rename from NzbDrone.Core.Test/MediaFileTests/MediaFileRepositoryFixture.cs rename to NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs index 567a55949..2b54fff52 100644 --- a/NzbDrone.Core.Test/MediaFileTests/MediaFileRepositoryFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs @@ -8,7 +8,7 @@ using NzbDrone.Core.Tv; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests +namespace NzbDrone.Core.Test.MediaFiles { [TestFixture] public class MediaFileRepositoryFixture : DbTest diff --git a/NzbDrone.Core.Test/MediaFileTests/MediaFileServiceTest.cs b/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTest.cs similarity index 99% rename from NzbDrone.Core.Test/MediaFileTests/MediaFileServiceTest.cs rename to NzbDrone.Core.Test/MediaFiles/MediaFileServiceTest.cs index 1e1060239..2135e773a 100644 --- a/NzbDrone.Core.Test/MediaFileTests/MediaFileServiceTest.cs +++ b/NzbDrone.Core.Test/MediaFiles/MediaFileServiceTest.cs @@ -8,7 +8,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; -namespace NzbDrone.Core.Test.MediaFileTests +namespace NzbDrone.Core.Test.MediaFiles { [TestFixture] public class MediaFileServiceTest : CoreTest diff --git a/NzbDrone.Core.Test/MediaFileTests/MediaFileTableCleanupServiceFixture.cs b/NzbDrone.Core.Test/MediaFiles/MediaFileTableCleanupServiceFixture.cs similarity index 99% rename from NzbDrone.Core.Test/MediaFileTests/MediaFileTableCleanupServiceFixture.cs rename to NzbDrone.Core.Test/MediaFiles/MediaFileTableCleanupServiceFixture.cs index 0b61e29d0..343a03158 100644 --- a/NzbDrone.Core.Test/MediaFileTests/MediaFileTableCleanupServiceFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/MediaFileTableCleanupServiceFixture.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using FizzWare.NBuilder; using Moq; using NUnit.Framework; @@ -8,9 +9,8 @@ using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; -using System.Linq; -namespace NzbDrone.Core.Test.MediaFileTests +namespace NzbDrone.Core.Test.MediaFiles { public class MediaFileTableCleanupServiceFixture : CoreTest { diff --git a/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs b/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs new file mode 100644 index 000000000..c3267089b --- /dev/null +++ b/NzbDrone.Core.Test/MediaFiles/MediaInfo/VideoFileInfoReaderFixture.cs @@ -0,0 +1,22 @@ +using System.IO; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles.MediaInfo; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.Categories; + +namespace NzbDrone.Core.Test.MediaFiles.MediaInfo +{ + [TestFixture] + [DiskAccessTest] + public class VideoFileInfoReaderFixture : CoreTest + { + [Test] + public void get_runtime() + { + var path = Path.Combine(Directory.GetCurrentDirectory(), "Files", "Media", "H264_sample.mp4"); + + Subject.GetRunTime(path).Seconds.Should().Be(10); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/MediaFileTests/RenameEpisodeFileServiceFixture.cs b/NzbDrone.Core.Test/MediaFiles/RenameEpisodeFileServiceFixture.cs similarity index 95% rename from NzbDrone.Core.Test/MediaFileTests/RenameEpisodeFileServiceFixture.cs rename to NzbDrone.Core.Test/MediaFiles/RenameEpisodeFileServiceFixture.cs index 80b4dc855..070b4eb03 100644 --- a/NzbDrone.Core.Test/MediaFileTests/RenameEpisodeFileServiceFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/RenameEpisodeFileServiceFixture.cs @@ -3,14 +3,15 @@ using FizzWare.NBuilder; using Moq; using NUnit.Framework; -using NzbDrone.Common.Messaging; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.Events; -using NzbDrone.Core.Tv; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Tv; -namespace NzbDrone.Core.Test.MediaFileTests +namespace NzbDrone.Core.Test.MediaFiles { public class RenameEpisodeFileServiceFixture : CoreTest { @@ -70,7 +71,7 @@ public void should_not_publish_event_if_no_files_to_rename() Subject.Execute(new RenameSeriesCommand(_series.Id)); - Mocker.GetMock() + Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Never()); } @@ -85,7 +86,7 @@ public void should_not_publish_event_if_no_files_are_renamed() Subject.Execute(new RenameSeriesCommand(_series.Id)); - Mocker.GetMock() + Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Never()); } @@ -97,7 +98,7 @@ public void should_publish_event_if_files_are_renamed() Subject.Execute(new RenameSeriesCommand(_series.Id)); - Mocker.GetMock() + Mocker.GetMock() .Verify(v => v.PublishEvent(It.IsAny()), Times.Once()); } diff --git a/NzbDrone.Core.Test/MediaFileTests/UpgradeMediaFileServiceFixture.cs b/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs similarity index 98% rename from NzbDrone.Core.Test/MediaFileTests/UpgradeMediaFileServiceFixture.cs rename to NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs index 210eaff99..6594a3c77 100644 --- a/NzbDrone.Core.Test/MediaFileTests/UpgradeMediaFileServiceFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/UpgradeMediaFileServiceFixture.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using FizzWare.NBuilder; using Marr.Data; using Moq; @@ -10,7 +9,7 @@ using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; -namespace NzbDrone.Core.Test.MediaFileTests +namespace NzbDrone.Core.Test.MediaFiles { public class UpgradeMediaFileServiceFixture : CoreTest { diff --git a/NzbDrone.Core.Test/Messaging/Commands/CommandEqualityComparerFixture.cs b/NzbDrone.Core.Test/Messaging/Commands/CommandEqualityComparerFixture.cs new file mode 100644 index 000000000..34d4b1d30 --- /dev/null +++ b/NzbDrone.Core.Test/Messaging/Commands/CommandEqualityComparerFixture.cs @@ -0,0 +1,97 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.IndexerSearch; +using NzbDrone.Core.MediaFiles.Commands; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Update.Commands; +using NzbDrone.SignalR; + +namespace NzbDrone.Core.Test.Messaging.Commands +{ + [TestFixture] + public class CommandEqualityComparerFixture + { + [Test] + public void should_return_true_when_there_are_no_properties() + { + var command1 = new DownloadedEpisodesScanCommand(); + var command2 = new DownloadedEpisodesScanCommand(); + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeTrue(); + } + + [Test] + public void should_return_true_when_single_property_matches() + { + var command1 = new EpisodeSearchCommand { EpisodeId = 1 }; + var command2 = new EpisodeSearchCommand { EpisodeId = 1 }; + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeTrue(); + } + + [Test] + public void should_return_true_when_multiple_properties_match() + { + var command1 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 }; + var command2 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 }; + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeTrue(); + } + + [Test] + public void should_return_false_when_single_property_doesnt_match() + { + var command1 = new EpisodeSearchCommand { EpisodeId = 1 }; + var command2 = new EpisodeSearchCommand { EpisodeId = 2 }; + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_only_one_property_matches() + { + var command1 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 }; + var command2 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 2 }; + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_no_properties_match() + { + var command1 = new SeasonSearchCommand { SeriesId = 1, SeasonNumber = 1 }; + var command2 = new SeasonSearchCommand { SeriesId = 2, SeasonNumber = 2 }; + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_only_one_has_properties() + { + var command1 = new SeasonSearchCommand(); + var command2 = new SeasonSearchCommand { SeriesId = 2, SeasonNumber = 2 }; + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeFalse(); + } + + + [Test] + public void should_return_false_when_only_one_has_null_property() + { + var command1 = new BroadcastSignalRMessage(null); + var command2 = new BroadcastSignalRMessage(new SignalRMessage()); + + CommandEqualityComparer.Instance.Equals(command1, command2).Should().BeFalse(); + } + + + [Test] + public void should_return_false_when_commands_are_diffrent_types() + { + CommandEqualityComparer.Instance.Equals(new RssSyncCommand(), new ApplicationUpdateCommand()).Should().BeFalse(); + } + + + } +} diff --git a/NzbDrone.Common.Test/EventingTests/MessageAggregatorCommandTests.cs b/NzbDrone.Core.Test/Messaging/Commands/CommandExecutorFixture.cs similarity index 64% rename from NzbDrone.Common.Test/EventingTests/MessageAggregatorCommandTests.cs rename to NzbDrone.Core.Test/Messaging/Commands/CommandExecutorFixture.cs index 1e450ae21..650f81224 100644 --- a/NzbDrone.Common.Test/EventingTests/MessageAggregatorCommandTests.cs +++ b/NzbDrone.Core.Test/Messaging/Commands/CommandExecutorFixture.cs @@ -2,13 +2,16 @@ using System.Collections.Generic; using Moq; using NUnit.Framework; -using NzbDrone.Common.Messaging; +using NzbDrone.Common; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Commands.Tracking; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Test.Common; -namespace NzbDrone.Common.Test.EventingTests +namespace NzbDrone.Core.Test.Messaging.Commands { [TestFixture] - public class MessageAggregatorCommandTests : TestBase + public class CommandExecutorFixture : TestBase { private Mock> _executorA; private Mock> _executorB; @@ -27,6 +30,10 @@ public void Setup() .Setup(c => c.Build(typeof(IExecute))) .Returns(_executorB.Object); + + Mocker.GetMock() + .Setup(c => c.FindExisting(It.IsAny())) + .Returns(null); } [Test] @@ -42,7 +49,7 @@ public void should_publish_command_to_executor() [Test] public void should_publish_command_by_with_optional_arg_using_name() { - Mocker.GetMock().Setup(c => c.GetImplementations(typeof(ICommand))) + Mocker.GetMock().Setup(c => c.GetImplementations(typeof(Command))) .Returns(new List { typeof(CommandA), typeof(CommandB) }); Subject.PublishCommand(typeof(CommandA).FullName); @@ -55,7 +62,6 @@ public void should_not_publish_to_incompatible_executor() { var commandA = new CommandA(); - Subject.PublishCommand(commandA); _executorA.Verify(c => c.Execute(commandA), Times.Once()); @@ -72,21 +78,44 @@ public void broken_executor_should_throw_the_exception() Assert.Throws(() => Subject.PublishCommand(commandA)); } - } - public class CommandA : ICommand - { -// ReSharper disable UnusedParameter.Local - public CommandA(int id = 0) -// ReSharper restore UnusedParameter.Local + + [Test] + public void broken_executor_should_publish_executed_event() { + var commandA = new CommandA(); + _executorA.Setup(c => c.Execute(It.IsAny())) + .Throws(new NotImplementedException()); + + Assert.Throws(() => Subject.PublishCommand(commandA)); + + VerifyEventPublished(); + } + + [Test] + public void should_publish_executed_event_on_success() + { + var commandA = new CommandA(); + Subject.PublishCommand(commandA); + + VerifyEventPublished(); } } - public class CommandB : ICommand + public class CommandA : Command + { + public CommandA(int id = 0) + { + } + } + + public class CommandB : Command { + public CommandB() + { + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/Messaging/Commands/CommandFixture.cs b/NzbDrone.Core.Test/Messaging/Commands/CommandFixture.cs new file mode 100644 index 000000000..a75fc2224 --- /dev/null +++ b/NzbDrone.Core.Test/Messaging/Commands/CommandFixture.cs @@ -0,0 +1,19 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Update.Commands; + +namespace NzbDrone.Core.Test.Messaging.Commands +{ + [TestFixture] + public class CommandFixture + { + [Test] + public void default_values() + { + var command = new ApplicationUpdateCommand(); + + command.Id.Should().NotBe(0); + command.Name.Should().Be("ApplicationUpdate"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs b/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs index 4a2883253..412239c0b 100644 --- a/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs +++ b/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs @@ -47,11 +47,11 @@ public void should_be_able_to_get_series_detail(int tvdbId) ValidateEpisodes(details.Item2); } - [Test] public void getting_details_of_invalid_series() { Assert.Throws(() => Subject.GetSeriesInfo(Int32.MaxValue)); + ExceptionVerification.ExpectedWarns(1); } @@ -88,12 +88,16 @@ private void ValidateEpisodes(List episodes) foreach (var episode in episodes) { ValidateEpisode(episode); + + //if atleast one episdoe has title it means parse it working. + episodes.Should().Contain(c => !string.IsNullOrWhiteSpace(c.Title)); } } private void ValidateEpisode(Episode episode) { episode.Should().NotBeNull(); + episode.Title.Should().NotBeBlank(); //episode.Title.Should().NotBeBlank(); episode.EpisodeNumber.Should().NotBe(0); episode.TvDbEpisodeId.Should().BeGreaterThan(0); diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 524d84b1d..5035ea37e 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -108,8 +108,11 @@ - + + + + @@ -132,17 +135,21 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -155,6 +162,8 @@ + + @@ -174,22 +183,18 @@ - - - + - - - + @@ -205,14 +210,14 @@ - + - + @@ -229,6 +234,10 @@ {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205} NzbDrone.Core + + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36} + NzbDrone.SignalR + {CADDFCE0-7509-4430-8364-2074E1EEFCA2} NzbDrone.Test.Common @@ -242,6 +251,9 @@ Always + + Always + Always diff --git a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index 192ff7380..ace78f50e 100644 --- a/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -4,11 +4,10 @@ using Moq; using NUnit.Framework; using NzbDrone.Common.Contract; -using NzbDrone.Core.Indexers; +using NzbDrone.Common.Expansive; using NzbDrone.Core.Parser; using NzbDrone.Core.Test.Framework; using NzbDrone.Test.Common; -using NzbDrone.Common.Expansive; namespace NzbDrone.Core.Test.ParserTests { @@ -80,6 +79,8 @@ public class ParserFixture : CoreTest [TestCase("Top_Gear.19x06.720p_HDTV_x264-FoV", "Top Gear", 19, 6)] [TestCase("Portlandia.S03E10.Alexandra.720p.WEB-DL.AAC2.0.H.264-CROM.mkv", "Portlandia", 3, 10)] [TestCase("(Game of Thrones s03 e - \"Game of Thrones Season 3 Episode 10\"", "Game of Thrones", 3, 10)] + [TestCase("House.Hunters.International.S05E607.720p.hdtv.x264", "House.Hunters.International", 5, 607)] + [TestCase("Adventure.Time.With.Finn.And.Jake.S01E20.720p.BluRay.x264-DEiMOS", "Adventure.Time.With.Finn.And.Jake", 1, 20)] public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber) { var result = Parser.Parser.ParseTitle(postTitle); @@ -103,6 +104,8 @@ public void ParseTitle_single(string postTitle, string title, int seasonNumber, [TestCase(@"/TV Drop/King of the Hill - 10x12 - 24 Hour Propane People [SDTV]/1012 - 24 Hour Propane People.avi", 10, 12)] [TestCase(@"S:\TV Drop\King of the Hill - 10x12 - 24 Hour Propane People [SDTV]\Hour Propane People.avi", 10, 12)] [TestCase(@"/TV Drop/King of the Hill - 10x12 - 24 Hour Propane People [SDTV]/Hour Propane People.avi", 10, 12)] + [TestCase(@"E:\Downloads\tv\The.Big.Bang.Theory.S01E01.720p.HDTV\ajifajjjeaeaeqwer_eppj.avi", 1, 1)] + [TestCase(@"C:\Test\Unsorted\The.Big.Bang.Theory.S01E01.720p.HDTV\tbbt101.avi", 1, 1)] public void PathParse_tests(string path, int season, int episode) { var result = Parser.Parser.ParsePath(path); @@ -396,14 +399,6 @@ public void parse_season_subpack(string postTitle) result.Should().BeNull(); } - [TestCase("[112461]-[FULL]-[#a.b.teevee@EFNet]-[ 666.Park.Avenue.S01E03.720p.HDTV.X264-DIMENSION ]-[02/31] - \"the.devils.address.103.720p-dimension.par2\" yEnc", "666.Park.Avenue.S01E03.720p.HDTV.X264-DIMENSION")] - [TestCase("[112438]-[FULL]-[#a.b.teevee@EFNet]-[ Downton_Abbey.3x05.HDTV_x264-FoV ]-[01/26] - \"downton_abbey.3x05.hdtv_x264-fov.nfo\" yEnc", "Downton_Abbey.3x05.HDTV_x264-FoV")] - [TestCase("[ 21154 ] - [ TrollHD ] - [ 00/73 ] - \"MythBusters S03E20 Escape Slide Parachute 1080i HDTV-UPSCALE DD5.1 MPEG2-TrollHD.nzb\" yEnc", "MythBusters S03E20 Escape Slide Parachute 1080i HDTV-UPSCALE DD5.1 MPEG2-TrollHD.nzb")] - public void parse_header(string title, string expected) - { - BasicRssParser.ParseHeader(title).Should().Be(expected); - } - [TestCase("76El6LcgLzqb426WoVFg1vVVVGx4uCYopQkfjmLe")] [TestCase("Vrq6e1Aba3U amCjuEgV5R2QvdsLEGYF3YQAQkw8")] [TestCase("TDAsqTea7k4o6iofVx3MQGuDK116FSjPobMuh8oB")] diff --git a/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs new file mode 100644 index 000000000..b0692b6fb --- /dev/null +++ b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/GetEpisodesFixture.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests +{ + [TestFixture] + public class GetEpisodesFixture : TestBase + { + private Series _series; + private List _episodes; + private ParsedEpisodeInfo _parsedEpisodeInfo; + private SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria; + + [SetUp] + public void Setup() + { + _series = Builder.CreateNew() + .With(s => s.Title = "30 Rock") + .With(s => s.CleanTitle = "rock") + .Build(); + + _episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT)) + .Build() + .ToList(); + + _parsedEpisodeInfo = new ParsedEpisodeInfo + { + SeriesTitle = _series.Title, + SeasonNumber = 1, + EpisodeNumbers = new[] { 1 } + }; + + _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria + { + Series = _series, + EpisodeNumber = _episodes.First().EpisodeNumber, + SeasonNumber = _episodes.First().SeasonNumber, + Episodes = _episodes + }; + + Mocker.GetMock() + .Setup(s => s.FindByTitle(It.IsAny())) + .Returns(_series); + } + + private void GivenDailySeries() + { + _series.SeriesType = SeriesTypes.Daily; + } + + private void GivenDailyParseResult() + { + _parsedEpisodeInfo.AirDate = DateTime.Today; + } + + private void GivenSceneNumberingSeries() + { + _series.UseSceneNumbering = true; + } + + [Test] + public void should_get_daily_episode_episode_when_search_criteria_is_null() + { + GivenDailySeries(); + GivenDailyParseResult(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_search_criteria_episode_when_it_matches_daily() + { + GivenDailySeries(); + GivenDailyParseResult(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Never()); + } + + [Test] + public void should_fallback_to_daily_episode_lookup_when_search_criteria_episode_doesnt_match() + { + GivenDailySeries(); + _parsedEpisodeInfo.AirDate = DateTime.Today.AddDays(-5); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_scene_numbering_when_series_uses_scene_numbering() + { + GivenSceneNumberingSeries(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Once()); + } + + [Test] + public void should_match_search_criteria_by_scene_numbering() + { + GivenSceneNumberingSeries(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Never()); + } + + [Test] + public void should_fallback_to_findEpisode_when_search_criteria_match_fails_for_scene_numbering() + { + GivenSceneNumberingSeries(); + _episodes.First().SceneEpisodeNumber = 10; + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), true), Times.Once()); + } + + [Test] + public void should_find_episode() + { + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Once()); + } + + [Test] + public void should_match_episode_with_search_criteria() + { + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Never()); + } + + [Test] + public void should_fallback_to_findEpisode_when_search_criteria_match_fails() + { + _episodes.First().EpisodeNumber = 10; + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindEpisode(It.IsAny(), It.IsAny(), It.IsAny(), false), Times.Once()); + } + } +} diff --git a/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs new file mode 100644 index 000000000..efa6c5b3d --- /dev/null +++ b/NzbDrone.Core.Test/ParserTests/ParsingServiceTests/MapFixture.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FizzWare.NBuilder; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Tv; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.ParserTests.ParsingServiceTests +{ + [TestFixture] + public class MapFixture : TestBase + { + private Series _series; + private List _episodes; + private ParsedEpisodeInfo _parsedEpisodeInfo; + private SingleEpisodeSearchCriteria _singleEpisodeSearchCriteria; + + [SetUp] + public void Setup() + { + _series = Builder.CreateNew() + .With(s => s.Title = "30 Rock") + .With(s => s.CleanTitle = "rock") + .Build(); + + _episodes = Builder.CreateListOfSize(1) + .All() + .With(e => e.AirDate = DateTime.Today.ToString(Episode.AIR_DATE_FORMAT)) + .Build() + .ToList(); + + _parsedEpisodeInfo = new ParsedEpisodeInfo + { + SeriesTitle = _series.Title, + SeasonNumber = 1, + EpisodeNumbers = new[] { 1 } + }; + + _singleEpisodeSearchCriteria = new SingleEpisodeSearchCriteria + { + Series = _series, + EpisodeNumber = _episodes.First().EpisodeNumber, + SeasonNumber = _episodes.First().SeasonNumber, + Episodes = _episodes + }; + } + + private void GivenMatchBySeriesTitle() + { + Mocker.GetMock() + .Setup(s => s.FindByTitle(It.IsAny())) + .Returns(_series); + } + + private void GivenMatchByTvRageId() + { + Mocker.GetMock() + .Setup(s => s.FindByTvRageId(It.IsAny())) + .Returns(_series); + } + + private void GivenParseResultSeriesDoesntMatchSearchCriteria() + { + _parsedEpisodeInfo.SeriesTitle = "Another Name"; + } + + [Test] + public void should_lookup_series_by_name() + { + GivenMatchBySeriesTitle(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_tvrageid_when_series_title_lookup_fails() + { + GivenMatchByTvRageId(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId); + + Mocker.GetMock() + .Verify(v => v.FindByTvRageId(It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_search_criteria_series_title() + { + GivenMatchBySeriesTitle(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Never()); + } + + [Test] + public void should_FindByTitle_when_search_criteria_matching_fails() + { + GivenParseResultSeriesDoesntMatchSearchCriteria(); + + Subject.Map(_parsedEpisodeInfo, 10, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Once()); + } + + [Test] + public void should_FindByTvRageId_when_search_criteria_and_FIndByTitle_matching_fails() + { + GivenParseResultSeriesDoesntMatchSearchCriteria(); + + Subject.Map(_parsedEpisodeInfo, 10, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTvRageId(It.IsAny()), Times.Once()); + } + + [Test] + public void should_use_tvdbid_matching_when_alias_is_found() + { + Mocker.GetMock() + .Setup(s => s.GetTvDbId(It.IsAny())) + .Returns(_series.TvdbId); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Never()); + } + + [Test] + public void should_use_tvrageid_match_from_search_criteria_when_title_match_fails() + { + GivenParseResultSeriesDoesntMatchSearchCriteria(); + + Subject.Map(_parsedEpisodeInfo, _series.TvRageId, _singleEpisodeSearchCriteria); + + Mocker.GetMock() + .Verify(v => v.FindByTitle(It.IsAny()), Times.Never()); + } + } +} diff --git a/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs b/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs index a1a8c60f0..3eb98a5b9 100644 --- a/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs +++ b/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs @@ -1,6 +1,4 @@ - - -using System; +using System; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Qualities; @@ -12,120 +10,6 @@ namespace NzbDrone.Core.Test.ParserTests public class QualityParserFixture : CoreTest { - public static object[] SdtvCases = - { - new object[] { "S07E23 .avi ", false }, - new object[] {"The.Shield.S01E13.x264-CtrlSD", false}, - new object[] { "Nikita S02E01 HDTV XviD 2HD", false }, - new object[] { "Gossip Girl S05E11 PROPER HDTV XviD 2HD", true }, - new object[] { "The Jonathan Ross Show S02E08 HDTV x264 FTP", false }, - new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-TLA", false }, - new object[] { "White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA", true }, - new object[] { "The Real Housewives of Vancouver S01E04 DSR x264 2HD", false }, - new object[] { "Vanguard S01E04 Mexicos Death Train DSR x264 MiNDTHEGAP", false }, - new object[] { "Chuck S11E03 has no periods or extension HDTV", false }, - new object[] { "Chuck.S04E05.HDTV.XviD-LOL", false }, - new object[] { "Sonny.With.a.Chance.S02E15.avi", false }, - new object[] { "Sonny.With.a.Chance.S02E15.xvid", false }, - new object[] { "Sonny.With.a.Chance.S02E15.divx", false }, - new object[] { "The.Girls.Next.Door.S03E06.HDTV-WiDE", false }, - new object[] { "Degrassi.S10E27.WS.DSR.XviD-2HD", false }, - }; - - public static object[] DvdCases = - { - new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", true }, - new object[] { "The.Shield.S01E13.NTSC.x264-CtrlSD", false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false }, - new object[] { "WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false }, - new object[] { "WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", false }, - new object[] { "The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false }, - new object[] { "The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false }, - new object[] { "the.shield.1x13.circles.ws.xvidvd-tns", false} - }; - - public static object[] Webdl480pCases = - { - new object[] { "Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", false }, - new object[] { "Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", false }, - new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", false }, - }; - - public static object[] Hdtv720pCases = - { - new object[] { "Dexter - S01E01 - Title [HDTV]", false }, - new object[] { "Dexter - S01E01 - Title [HDTV-720p]", false }, - new object[] { "Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", true }, - new object[] { "Sonny.With.a.Chance.S02E15.720p", false }, - new object[] { "S07E23 - [HDTV-720p].mkv ", false }, - new object[] { "Chuck - S22E03 - MoneyBART - HD TV.mkv", false }, - new object[] { "S07E23.mkv ", false }, - new object[] { "Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", false }, - new object[] { "Sonny.With.a.Chance.S02E15.mkv", false }, - }; - - public static object[] Hdtv1080pCases = - { - new object[] { "Under the Dome S01E10 Let the Games Begin 1080p", false }, - new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.X264-QCF", false }, - new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.x264-QCF", false }, - new object[] { "DEXTER.S07E01.ARE.YOU.1080P.HDTV.proper.X264-QCF", true }, - new object[] { "Dexter - S01E01 - Title [HDTV-1080p]", false }, - }; - - public static object[] Webdl720pCases = - { - new object[] { "Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false }, - new object[] { "Vanguard S01E04 Mexicos Death Train 720p WEB DL", false }, - new object[] { "Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false }, - new object[] { "Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", false }, - new object[] { "Chuck - S11E06 - D-Yikes! - 720p WEB-DL.mkv", false }, - new object[] { "Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", false }, - new object[] { "S07E23 - [WEBDL].mkv ", false }, - new object[] { "Fringe S04E22 720p WEB-DL DD5.1 H264-EbP.mkv", false }, - }; - - public static object[] Webdl1080pCases = - { - new object[] { "Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false }, - new object[] { "CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", false }, - new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", false }, - new object[] { "Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", false }, - new object[] { "Its.Always.Sunny.in.Philadelphia.S08E01.1080p.WEB-DL.proper.AAC2.0.H.264", true }, - new object[] { "Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 REPACK NFHD", true }, - new object[] { "Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", false }, - new object[] { "The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", false }, - }; - - public static object[] Bluray720pCases = - { - new object[] { "WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false }, - new object[] { "Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", false }, - }; - - public static object[] Bluray1080pCases = - { - new object[] { "Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", false }, - new object[] { "Sons.Of.Anarchy.S02E13.1080p.BluRay.x264-AVCDVD", false }, - }; - - public static object[] RawCases = - { - new object[] { "POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", false }, - new object[] { "How I Met Your Mother S01E18 Nothing Good Happens After 2 A.M. 720p HDTV DD5.1 MPEG2-TrollHD", false }, - new object[] { "The Voice S01E11 The Finals 1080i HDTV DD5.1 MPEG2-TrollHD", false }, - }; - - public static object[] UnknownCases = - { - new object[] { "Sonny.With.a.Chance.S02E15", false }, - new object[] { "Law & Order: Special Victims Unit - 11x11 - Quickie", false }, - - }; - public static object[] SelfQualityParserCases = { new object[] { Quality.SDTV }, @@ -138,92 +22,130 @@ public class QualityParserFixture : CoreTest new object[] { Quality.Bluray1080p } }; - [Test, TestCaseSource("SdtvCases")] - public void should_parse_sdtv_quality(string postTitle, bool proper) + [TestCase("S07E23 .avi ", false)] + [TestCase("The.Shield.S01E13.x264-CtrlSD", false)] + [TestCase("Nikita S02E01 HDTV XviD 2HD", false)] + [TestCase("Gossip Girl S05E11 PROPER HDTV XviD 2HD", true)] + [TestCase("The Jonathan Ross Show S02E08 HDTV x264 FTP", false)] + [TestCase("White.Van.Man.2011.S02E01.WS.PDTV.x264-TLA", false)] + [TestCase("White.Van.Man.2011.S02E01.WS.PDTV.x264-REPACK-TLA", true)] + [TestCase("The Real Housewives of Vancouver S01E04 DSR x264 2HD", false)] + [TestCase("Vanguard S01E04 Mexicos Death Train DSR x264 MiNDTHEGAP", false)] + [TestCase("Chuck S11E03 has no periods or extension HDTV", false)] + [TestCase("Chuck.S04E05.HDTV.XviD-LOL", false)] + [TestCase("Sonny.With.a.Chance.S02E15.avi", false)] + [TestCase("Sonny.With.a.Chance.S02E15.xvid", false)] + [TestCase("Sonny.With.a.Chance.S02E15.divx", false)] + [TestCase("The.Girls.Next.Door.S03E06.HDTV-WiDE", false)] + [TestCase("Degrassi.S10E27.WS.DSR.XviD-2HD", false)] + public void should_parse_sdtv_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.SDTV); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.SDTV, proper); } - [Test, TestCaseSource("DvdCases")] - public void should_parse_dvd_quality(string postTitle, bool proper) + [TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", true)] + [TestCase("The.Shield.S01E13.NTSC.x264-CtrlSD", false)] + [TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", false)] + [TestCase("WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", false)] + [TestCase("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", false)] + [TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false)] + [TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false)] + [TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", false)] + [TestCase("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false)] + [TestCase("The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false)] + [TestCase("the.shield.1x13.circles.ws.xvidvd-tns", false)] + public void should_parse_dvd_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.DVD); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.DVD, proper); } - [Test, TestCaseSource("Webdl480pCases")] - public void should_parse_webdl480p_quality(string postTitle, bool proper) + [TestCase("Elementary.S01E10.The.Leviathan.480p.WEB-DL.x264-mSD", false)] + [TestCase("Glee.S04E10.Glee.Actually.480p.WEB-DL.x264-mSD", false)] + [TestCase("The.Big.Bang.Theory.S06E11.The.Santa.Simulation.480p.WEB-DL.x264-mSD", false)] + public void should_parse_webdl480p_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.WEBDL480p); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.WEBDL480p, proper); } - [Test, TestCaseSource("Hdtv720pCases")] - public void should_parse_hdtv720p_quality(string postTitle, bool proper) + [TestCase("Dexter - S01E01 - Title [HDTV]", false)] + [TestCase("Dexter - S01E01 - Title [HDTV-720p]", false)] + [TestCase("Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", true)] + [TestCase("Sonny.With.a.Chance.S02E15.720p", false)] + [TestCase("S07E23 - [HDTV-720p].mkv ", false)] + [TestCase("Chuck - S22E03 - MoneyBART - HD TV.mkv", false)] + [TestCase("S07E23.mkv ", false)] + [TestCase("Two.and.a.Half.Men.S08E05.720p.HDTV.X264-DIMENSION", false)] + [TestCase("Sonny.With.a.Chance.S02E15.mkv", false)] + [TestCase(@"E:\Downloads\tv\The.Big.Bang.Theory.S01E01.720p.HDTV\ajifajjjeaeaeqwer_eppj.avi", false)] + public void should_parse_hdtv720p_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.HDTV720p); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.HDTV720p, proper); } - [Test, TestCaseSource("Hdtv1080pCases")] - public void should_parse_hdtv1080p_quality(string postTitle, bool proper) + [TestCase("Under the Dome S01E10 Let the Games Begin 1080p", false)] + [TestCase("DEXTER.S07E01.ARE.YOU.1080P.HDTV.X264-QCF", false)] + [TestCase("DEXTER.S07E01.ARE.YOU.1080P.HDTV.x264-QCF", false)] + [TestCase("DEXTER.S07E01.ARE.YOU.1080P.HDTV.proper.X264-QCF", true)] + [TestCase("Dexter - S01E01 - Title [HDTV-1080p]", false)] + public void should_parse_hdtv1080p_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.HDTV1080p); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.HDTV1080p, proper); } - [Test, TestCaseSource("Webdl720pCases")] - public void should_parse_webdl720p_quality(string postTitle, bool proper) + [TestCase("Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false)] + [TestCase("Vanguard S01E04 Mexicos Death Train 720p WEB DL", false)] + [TestCase("Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false)] + [TestCase("Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", false)] + [TestCase("Chuck - S11E06 - D-Yikes! - 720p WEB-DL.mkv", false)] + [TestCase("Sonny.With.a.Chance.S02E15.720p.WEB-DL.DD5.1.H.264-SURFER", false)] + [TestCase("S07E23 - [WEBDL].mkv ", false)] + [TestCase("Fringe S04E22 720p WEB-DL DD5.1 H264-EbP.mkv", false)] + public void should_parse_webdl720p_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.WEBDL720p); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.WEBDL720p, proper); } - [Test, TestCaseSource("Webdl1080pCases")] - public void should_parse_webdl1080p_quality(string postTitle, bool proper) + [TestCase("Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false)] + [TestCase("CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", false)] + [TestCase("Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", false)] + [TestCase("Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", false)] + [TestCase("Its.Always.Sunny.in.Philadelphia.S08E01.1080p.WEB-DL.proper.AAC2.0.H.264", true)] + [TestCase("Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 REPACK NFHD", true)] + [TestCase("Glee.S04E09.Swan.Song.1080p.WEB-DL.DD5.1.H.264-ECI", false)] + [TestCase("The.Big.Bang.Theory.S06E11.The.Santa.Simulation.1080p.WEB-DL.DD5.1.H.264", false)] + public void should_parse_webdl1080p_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.WEBDL1080p); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.WEBDL1080p, proper); } - [Test, TestCaseSource("Bluray720pCases")] - public void should_parse_bluray720p_quality(string postTitle, bool proper) + [TestCase("WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false)] + [TestCase("Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", false)] + [TestCase("The Big Bang Theory.S03E01.The Electric Can Opener Fluctuation.m2ts", false)] + public void should_parse_bluray720p_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.Bluray720p); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.Bluray720p, proper); } - [Test, TestCaseSource("Bluray1080pCases")] - public void should_parse_bluray1080p_quality(string postTitle, bool proper) + [TestCase("Chuck - S01E03 - Come Fly With Me - 1080p BluRay.mkv", false)] + [TestCase("Sons.Of.Anarchy.S02E13.1080p.BluRay.x264-AVCDVD", false)] + public void should_parse_bluray1080p_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.Bluray1080p); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.Bluray1080p, proper); } - [Test, TestCaseSource("RawCases")] - public void should_parse_raw_quality(string postTitle, bool proper) + [TestCase("POI S02E11 1080i HDTV DD5.1 MPEG2-TrollHD", false)] + [TestCase("How I Met Your Mother S01E18 Nothing Good Happens After 2 A.M. 720p HDTV DD5.1 MPEG2-TrollHD", false)] + [TestCase("The Voice S01E11 The Finals 1080i HDTV DD5.1 MPEG2-TrollHD", false)] + public void should_parse_raw_quality(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.RAWHD); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.RAWHD, proper); } - [Test, TestCaseSource("UnknownCases")] - public void quality_parse(string postTitle, bool proper) + [TestCase("Sonny.With.a.Chance.S02E15", false)] + [TestCase("Law & Order: Special Victims Unit - 11x11 - Quickie", false)] + public void quality_parse(string title, bool proper) { - var result = Parser.QualityParser.ParseQuality(postTitle); - result.Quality.Should().Be(Quality.Unknown); - result.Proper.Should().Be(proper); + ParseAndVerifyQuality(title, Quality.Unknown, proper); } [Test, TestCaseSource("SelfQualityParserCases")] @@ -233,5 +155,12 @@ public void parsing_our_own_quality_enum(Quality quality) var result = Parser.QualityParser.ParseQuality(fileName); result.Quality.Should().Be(quality); } + + private void ParseAndVerifyQuality(string title, Quality quality, bool proper) + { + var result = Parser.QualityParser.ParseQuality(title); + result.Quality.Should().Be(quality); + result.Proper.Should().Be(proper); + } } } diff --git a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/GetVideoFilesFixture.cs b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/GetVideoFilesFixture.cs index b73e09984..bb2bb4f93 100644 --- a/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/GetVideoFilesFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/DiskScanProviderTests/GetVideoFilesFixture.cs @@ -69,7 +69,7 @@ public void should_check_top_level_directory_only_when_allDirectories_is_false() public void should_return_video_files_only() { var path = @"C:\Test\"; - + var test = Subject.GetVideoFiles(path); Subject.GetVideoFiles(path).Should().HaveCount(4); } } diff --git a/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs b/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs index 2589fe5df..1b689c53c 100644 --- a/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs +++ b/NzbDrone.Core.Test/SeriesStatsTests/SeriesStatisticsFixture.cs @@ -12,34 +12,88 @@ namespace NzbDrone.Core.Test.SeriesStatsTests [TestFixture] public class SeriesStatisticsFixture : DbTest { + private Series _series; private Episode _episode; [SetUp] public void Setup() { - var series = Builder.CreateNew() + _series = Builder.CreateNew() .With(s => s.Id = 0) .With(s => s.Runtime = 30) .Build(); - series.Id = Db.Insert(series).Id; + _series.Id = Db.Insert(_series).Id; _episode = Builder.CreateNew() .With(e => e.Id = 0) - .With(e => e.SeriesId = series.Id) + .With(e => e.EpisodeFileId = 0) + .With(e => e.Monitored = false) + .With(e => e.SeriesId = _series.Id) .With(e => e.AirDateUtc = DateTime.Today.AddDays(5)) .Build(); + } + private void GivenEpisodeWithFile() + { + _episode.EpisodeFileId = 1; + } + + private void GivenMonitoredEpisode() + { + _episode.Monitored = true; + } + + private void GivenFile() + { Db.Insert(_episode); } [Test] public void should_get_stats_for_series() { + GivenMonitoredEpisode(); + GivenFile(); + var stats = Subject.SeriesStatistics(); stats.Should().HaveCount(1); stats.First().NextAiring.Should().Be(_episode.AirDateUtc); } + + [Test] + public void should_not_have_next_airing_for_episode_with_file() + { + GivenEpisodeWithFile(); + GivenFile(); + + var stats = Subject.SeriesStatistics(); + + stats.Should().HaveCount(1); + stats.First().NextAiring.Should().NotHaveValue(); + } + + [Test] + public void should_not_include_unmonitored_episode_in_episode_count() + { + GivenFile(); + + var stats = Subject.SeriesStatistics(); + + stats.Should().HaveCount(1); + stats.First().EpisodeCount.Should().Be(0); + } + + [Test] + public void should_include_unmonitored_episode_with_file_in_episode_count() + { + GivenEpisodeWithFile(); + GivenFile(); + + var stats = Subject.SeriesStatistics(); + + stats.Should().HaveCount(1); + stats.First().EpisodeCount.Should().Be(1); + } } } diff --git a/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithoutFilesFixture.cs b/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithoutFilesFixture.cs index 496bcf77e..e08d77b23 100644 --- a/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithoutFilesFixture.cs +++ b/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/EpisodesWithoutFilesFixture.cs @@ -14,20 +14,24 @@ public class EpisodesWithoutFilesFixture : DbTest private Series _monitoredSeries; private Series _unmonitoredSeries; private PagingSpec _pagingSpec; - + [SetUp] public void Setup() { _monitoredSeries = Builder.CreateNew() .With(s => s.Id = 0) + .With(s => s.TvRageId = RandomNumber) .With(s => s.Runtime = 30) .With(s => s.Monitored = true) + .With(s => s.TitleSlug = "Title3") .Build(); _unmonitoredSeries = Builder.CreateNew() .With(s => s.Id = 0) + .With(s => s.TvdbId = RandomNumber) .With(s => s.Runtime = 30) .With(s => s.Monitored = false) + .With(s => s.TitleSlug = "Title2") .Build(); _monitoredSeries.Id = Db.Insert(_monitoredSeries).Id; @@ -44,6 +48,7 @@ public void Setup() var monitoredSeriesEpisodes = Builder.CreateListOfSize(3) .All() .With(e => e.Id = 0) + .With(e => e.TvDbEpisodeId = RandomNumber) .With(e => e.SeriesId = _monitoredSeries.Id) .With(e => e.EpisodeFileId = 0) .With(e => e.AirDateUtc = DateTime.Now.AddDays(-5)) @@ -57,6 +62,7 @@ public void Setup() var unmonitoredSeriesEpisodes = Builder.CreateListOfSize(3) .All() .With(e => e.Id = 0) + .With(e => e.TvDbEpisodeId = RandomNumber) .With(e => e.SeriesId = _unmonitoredSeries.Id) .With(e => e.EpisodeFileId = 0) .With(e => e.AirDateUtc = DateTime.Now.AddDays(-5)) @@ -67,6 +73,7 @@ public void Setup() .With(e => e.SeasonNumber = 0) .Build(); + Db.InsertMany(monitoredSeriesEpisodes); Db.InsertMany(unmonitoredSeriesEpisodes); } diff --git a/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs b/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs index 428568e8d..a3cd0b10a 100644 Binary files a/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs and b/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs differ diff --git a/NzbDrone.Core.Test/TvTests/SeasonProviderTest.cs b/NzbDrone.Core.Test/TvTests/SeasonProviderTest.cs deleted file mode 100644 index efb11c7bb..000000000 --- a/NzbDrone.Core.Test/TvTests/SeasonProviderTest.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Linq; -using FizzWare.NBuilder; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.TvTests -{ - public class SeasonProviderTest : DbTest - { - [TestCase(true)] - [TestCase(false)] - public void Ismonitored_should_return_monitored_status_of_season(bool monitored) - { - var fakeSeason = Builder.CreateNew() - .With(s => s.Monitored = monitored) - .BuildNew(); - - Db.Insert(fakeSeason); - - var result = Subject.IsMonitored(fakeSeason.SeriesId, fakeSeason.SeasonNumber); - - result.Should().Be(monitored); - } - - [Test] - public void Monitored_should_return_true_if_not_in_db() - { - Subject.IsMonitored(10, 0).Should().BeTrue(); - } - - [Test] - public void GetSeason_should_return_seasons_for_specified_series_only() - { - var seriesA = new[] { 1, 2, 3 }; - var seriesB = new[] { 4, 5, 6 }; - - var seasonsA = seriesA.Select(c => new Season {SeasonNumber = c, SeriesId = 1}).ToList(); - var seasonsB = seriesB.Select(c => new Season {SeasonNumber = c, SeriesId = 2}).ToList(); - - Subject.InsertMany(seasonsA); - Subject.InsertMany(seasonsB); - - Subject.GetSeasonNumbers(1).Should().Equal(seriesA); - Subject.GetSeasonNumbers(2).Should().Equal(seriesB); - } - - - [Test] - public void GetSeason_should_return_emptylist_if_series_doesnt_exist() - { - Subject.GetSeasonNumbers(1).Should().BeEmpty(); - } - - } -} diff --git a/NzbDrone.Core.Test/TvTests/SeasonServiceTests/HandleEpisodeInfoDeletedEventFixture.cs b/NzbDrone.Core.Test/TvTests/SeasonServiceTests/HandleEpisodeInfoDeletedEventFixture.cs deleted file mode 100644 index d31c2b679..000000000 --- a/NzbDrone.Core.Test/TvTests/SeasonServiceTests/HandleEpisodeInfoDeletedEventFixture.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using FizzWare.NBuilder; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; -using NzbDrone.Core.Tv.Events; - -namespace NzbDrone.Core.Test.TvTests.SeasonServiceTests -{ - [TestFixture] - public class HandleEpisodeInfoDeletedEventFixture : CoreTest - { - private List _seasons; - private List _episodes; - - [SetUp] - public void Setup() - { - _seasons = Builder - .CreateListOfSize(1) - .All() - .With(s => s.SeriesId = 1) - .Build() - .ToList(); - - _episodes = Builder - .CreateListOfSize(1) - .All() - .With(e => e.SeasonNumber = _seasons.First().SeasonNumber) - .With(s => s.SeriesId = _seasons.First().SeasonNumber) - .Build() - .ToList(); - - Mocker.GetMock() - .Setup(s => s.GetSeasonBySeries(It.IsAny())) - .Returns(_seasons); - - Mocker.GetMock() - .Setup(s => s.GetEpisodesBySeason(It.IsAny(), _seasons.First().SeasonNumber)) - .Returns(_episodes); - } - - private void GivenAbandonedSeason() - { - Mocker.GetMock() - .Setup(s => s.GetEpisodesBySeason(It.IsAny(), _seasons.First().SeasonNumber)) - .Returns(new List()); - } - - [Test] - public void should_not_delete_when_season_is_still_valid() - { - Subject.Handle(new EpisodeInfoDeletedEvent(_episodes)); - - Mocker.GetMock() - .Verify(v => v.Delete(It.IsAny()), Times.Never()); - } - - [Test] - public void should_delete_season_if_no_episodes_exist_in_that_season() - { - GivenAbandonedSeason(); - - Subject.Handle(new EpisodeInfoDeletedEvent(_episodes)); - - Mocker.GetMock() - .Verify(v => v.Delete(It.IsAny()), Times.Once()); - } - - [Test] - public void should_only_delete_a_season_once() - { - _episodes = Builder - .CreateListOfSize(5) - .All() - .With(e => e.SeasonNumber = _seasons.First().SeasonNumber) - .With(s => s.SeriesId = _seasons.First().SeasonNumber) - .Build() - .ToList(); - - GivenAbandonedSeason(); - - Subject.Handle(new EpisodeInfoDeletedEvent(_episodes)); - - Mocker.GetMock() - .Verify(v => v.Delete(It.IsAny()), Times.Once()); - } - } -} diff --git a/NzbDrone.Core.Test/TvTests/SeasonServiceTests/SetMonitoredFixture.cs b/NzbDrone.Core.Test/TvTests/SeasonServiceTests/SetMonitoredFixture.cs deleted file mode 100644 index 775d7d41c..000000000 --- a/NzbDrone.Core.Test/TvTests/SeasonServiceTests/SetMonitoredFixture.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.TvTests.SeasonServiceTests -{ - [TestFixture] - public class SetMonitoredFixture : CoreTest - { - private Season _season; - - [SetUp] - public void Setup() - { - _season = new Season - { - Id = 1, - SeasonNumber = 1, - SeriesId = 1, - Monitored = false - }; - - Mocker.GetMock() - .Setup(s => s.Get(It.IsAny(), It.IsAny())) - .Returns(_season); - } - - [TestCase(true)] - [TestCase(false)] - public void should_update_season(bool monitored) - { - Subject.SetMonitored(_season.SeriesId, _season.SeasonNumber, monitored); - - Mocker.GetMock() - .Verify(v => v.Update(_season), Times.Once()); - } - - [TestCase(true)] - [TestCase(false)] - public void should_update_episodes_in_season(bool monitored) - { - Subject.SetMonitored(_season.SeriesId, _season.SeasonNumber, monitored); - - Mocker.GetMock() - .Verify(v => v.SetEpisodeMonitoredBySeason(_season.SeriesId, _season.SeasonNumber, monitored), Times.Once()); - } - } -} diff --git a/NzbDrone.Core.Test/TvTests/SeasonServiceTests/SetSeasonPassFixture.cs b/NzbDrone.Core.Test/TvTests/SeasonServiceTests/SetSeasonPassFixture.cs deleted file mode 100644 index b8ac4a9ea..000000000 --- a/NzbDrone.Core.Test/TvTests/SeasonServiceTests/SetSeasonPassFixture.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using FizzWare.NBuilder; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Core.Test.Framework; -using NzbDrone.Core.Tv; - -namespace NzbDrone.Core.Test.TvTests.SeasonServiceTests -{ - [TestFixture] - public class SetSeasonPassFixture : CoreTest - { - private const Int32 SERIES_ID = 1; - private List _seasons; - - [SetUp] - public void Setup() - { - _seasons = Builder.CreateListOfSize(5) - .All() - .With(s => s.SeriesId = SERIES_ID) - .Build() - .ToList(); - - Mocker.GetMock() - .Setup(s => s.GetSeasonBySeries(It.IsAny())) - .Returns(_seasons); - } - - [Test] - public void should_updateMany() - { - Subject.SetSeasonPass(SERIES_ID, 1); - - Mocker.GetMock() - .Verify(v => v.UpdateMany(It.IsAny>()), Times.Once()); - } - - [Test] - public void should_set_lower_seasons_to_false() - { - const int seasonNumber = 3; - - var result = Subject.SetSeasonPass(SERIES_ID, seasonNumber); - - result.Where(s => s.SeasonNumber < seasonNumber).Should().OnlyContain(s => s.Monitored == false); - } - - [Test] - public void should_set_equal_or_higher_seasons_to_false() - { - const int seasonNumber = 3; - - var result = Subject.SetSeasonPass(SERIES_ID, seasonNumber); - - result.Where(s => s.SeasonNumber >= seasonNumber).Should().OnlyContain(s => s.Monitored == true); - } - - [Test] - public void should_set_episodes_in_lower_seasons_to_false() - { - const int seasonNumber = 3; - - Subject.SetSeasonPass(SERIES_ID, seasonNumber); - - Mocker.GetMock() - .Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is(i => i < seasonNumber), false), Times.AtLeastOnce()); - - Mocker.GetMock() - .Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is(i => i < seasonNumber), true), Times.Never()); - } - - [Test] - public void should_set_episodes_in_equal_or_higher_seasons_to_false() - { - const int seasonNumber = 3; - - Subject.SetSeasonPass(SERIES_ID, seasonNumber); - - Mocker.GetMock() - .Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is(i => i >= seasonNumber), true), Times.AtLeastOnce()); - - Mocker.GetMock() - .Verify(v => v.SetEpisodeMonitoredBySeason(SERIES_ID, It.Is(i => i >= seasonNumber), false), Times.Never()); - } - } -} diff --git a/NzbDrone.Core.Test/TvTests/SeriesServiceFixture.cs b/NzbDrone.Core.Test/TvTests/SeriesServiceTests/AddSeriesFixture.cs similarity index 77% rename from NzbDrone.Core.Test/TvTests/SeriesServiceFixture.cs rename to NzbDrone.Core.Test/TvTests/SeriesServiceTests/AddSeriesFixture.cs index 99ead2647..ab33ff072 100644 Binary files a/NzbDrone.Core.Test/TvTests/SeriesServiceFixture.cs and b/NzbDrone.Core.Test/TvTests/SeriesServiceTests/AddSeriesFixture.cs differ diff --git a/NzbDrone.Core.Test/TvTests/SeriesServiceTests/UpdateSeriesFixture.cs b/NzbDrone.Core.Test/TvTests/SeriesServiceTests/UpdateSeriesFixture.cs new file mode 100644 index 000000000..903e98102 Binary files /dev/null and b/NzbDrone.Core.Test/TvTests/SeriesServiceTests/UpdateSeriesFixture.cs differ diff --git a/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs b/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs index 138e3b404..aa58b0bbd 100644 --- a/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs +++ b/NzbDrone.Core.Test/UpdateTests/UpdatePackageProviderFixture.cs @@ -1,22 +1,25 @@ +using System; using FluentAssertions; using NUnit.Framework; -using NzbDrone.Core.Configuration; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Update; -using System.Linq; namespace NzbDrone.Core.Test.UpdateTests { public class UpdatePackageProviderFixture : CoreTest { [Test] - public void should_get_list_of_available_updates() + public void no_update_when_version_higher() { UseRealHttp(); + Subject.GetLatestUpdate("master", new Version(10,0)).Should().BeNull(); + } - Mocker.GetMock().SetupGet(c => c.Branch).Returns("master"); - - Subject.GetLatestUpdate().Should().BeNull(); + [Test] + public void finds_update_when_version_lower() + { + UseRealHttp(); + Subject.GetLatestUpdate("master", new Version(1, 0)).Should().NotBeNull(); } } } diff --git a/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs b/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs index 2a485146c..31ebee3e5 100644 --- a/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs +++ b/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.IO; using FluentAssertions; using Moq; diff --git a/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/NzbDrone.Core/Configuration/ConfigFileProvider.cs index fa694700d..8389ec658 100644 --- a/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -2,13 +2,16 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Xml; using System.Xml.Linq; using NzbDrone.Common; using NzbDrone.Common.Cache; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Configuration { @@ -24,24 +27,23 @@ public interface IConfigFileProvider : IHandleAsync string Password { get; } string LogLevel { get; } string Branch { get; } + bool Torrent { get; } } public class ConfigFileProvider : IConfigFileProvider { private const string CONFIG_ELEMENT_NAME = "Config"; - private readonly IAppFolderInfo _appFolderInfo; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly ICached _cache; private readonly string _configFile; - public ConfigFileProvider(IAppFolderInfo appFolderInfo, ICacheManger cacheManger, IMessageAggregator messageAggregator) + public ConfigFileProvider(IAppFolderInfo appFolderInfo, ICacheManger cacheManger, IEventAggregator eventAggregator) { - _appFolderInfo = appFolderInfo; _cache = cacheManger.GetCache(GetType()); - _messageAggregator = messageAggregator; - _configFile = _appFolderInfo.GetConfigPath(); + _eventAggregator = eventAggregator; + _configFile = appFolderInfo.GetConfigPath(); } public Dictionary GetConfigDictionary() @@ -81,7 +83,7 @@ public void SaveConfigDictionary(Dictionary configValues) } } - _messageAggregator.PublishEvent(new ConfigFileSavedEvent()); + _eventAggregator.PublishEvent(new ConfigFileSavedEvent()); } public int Port @@ -94,6 +96,11 @@ public bool LaunchBrowser get { return GetValueBoolean("LaunchBrowser", true); } } + public bool Torrent + { + get { return GetValueBoolean("Torrent", false, persist: false); } + } + public bool AuthenticationEnabled { get { return GetValueBoolean("AuthenticationEnabled", false); } @@ -124,9 +131,9 @@ public int GetValueInt(string key, int defaultValue) return Convert.ToInt32(GetValue(key, defaultValue)); } - public bool GetValueBoolean(string key, bool defaultValue) + public bool GetValueBoolean(string key, bool defaultValue, bool persist = true) { - return Convert.ToBoolean(GetValue(key, defaultValue)); + return Convert.ToBoolean(GetValue(key, defaultValue, persist)); } public T GetValueEnum(string key, T defaultValue) @@ -134,13 +141,13 @@ public T GetValueEnum(string key, T defaultValue) return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), true); } - public string GetValue(string key, object defaultValue) + public string GetValue(string key, object defaultValue, bool persist = true) { return _cache.Get(key, () => { EnsureDefaultConfigFile(); - var xDoc = XDocument.Load(_configFile); + var xDoc = LoadConfigFile(); var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); var parentContainer = config; @@ -151,7 +158,10 @@ public string GetValue(string key, object defaultValue) return valueHolder.First().Value; //Save the value - SetValue(key, defaultValue); + if (persist) + { + SetValue(key, defaultValue); + } //return the default value return defaultValue.ToString(); @@ -162,7 +172,7 @@ public void SetValue(string key, object value) { EnsureDefaultConfigFile(); - var xDoc = XDocument.Load(_configFile); + var xDoc = LoadConfigFile(); var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); var parentContainer = config; @@ -203,7 +213,7 @@ private void DeleteOldValues() { EnsureDefaultConfigFile(); - var xDoc = XDocument.Load(_configFile); + var xDoc = LoadConfigFile(); var config = xDoc.Descendants(CONFIG_ELEMENT_NAME).Single(); var type = GetType(); @@ -222,6 +232,19 @@ private void DeleteOldValues() xDoc.Save(_configFile); } + private XDocument LoadConfigFile() + { + try + { + return XDocument.Load(_configFile); + } + + catch (XmlException ex) + { + throw new InvalidConfigFileException(_configFile + " is invalid, please see the http://wiki.nzbdrone.com for steps to resolve this issue.", ex); + } + } + public void HandleAsync(ApplicationStartedEvent message) { DeleteOldValues(); diff --git a/NzbDrone.Core/Configuration/ConfigRepository.cs b/NzbDrone.Core/Configuration/ConfigRepository.cs index b0665445e..0b8c3fffb 100644 --- a/NzbDrone.Core/Configuration/ConfigRepository.cs +++ b/NzbDrone.Core/Configuration/ConfigRepository.cs @@ -1,6 +1,8 @@ using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Configuration { @@ -12,8 +14,8 @@ public interface IConfigRepository : IBasicRepository public class ConfigRepository : BasicRepository, IConfigRepository { - public ConfigRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public ConfigRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/Configuration/ConfigService.cs b/NzbDrone.Core/Configuration/ConfigService.cs index e3ca0317b..1e1af6182 100644 --- a/NzbDrone.Core/Configuration/ConfigService.cs +++ b/NzbDrone.Core/Configuration/ConfigService.cs @@ -2,11 +2,13 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Download; using NzbDrone.Core.Download.Clients.Nzbget; using NzbDrone.Core.Download.Clients.Sabnzbd; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Configuration { @@ -18,14 +20,14 @@ public enum ConfigKey public class ConfigService : IConfigService { private readonly IConfigRepository _repository; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; private static Dictionary _cache; - public ConfigService(IConfigRepository repository, IMessageAggregator messageAggregator, Logger logger) + public ConfigService(IConfigRepository repository, IEventAggregator eventAggregator, Logger logger) { _repository = repository; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _logger = logger; _cache = new Dictionary(); } @@ -68,7 +70,7 @@ public void SaveValues(Dictionary configValues) SetValue(configValue.Key, configValue.Value.ToString()); } - _messageAggregator.PublishEvent(new ConfigSavedEvent()); + _eventAggregator.PublishEvent(new ConfigSavedEvent()); } public String SabHost diff --git a/NzbDrone.Core/Configuration/InvalidConfigFileException.cs b/NzbDrone.Core/Configuration/InvalidConfigFileException.cs new file mode 100644 index 000000000..7069749ae --- /dev/null +++ b/NzbDrone.Core/Configuration/InvalidConfigFileException.cs @@ -0,0 +1,12 @@ +using System; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Configuration +{ + public class InvalidConfigFileException : NzbDroneException + { + public InvalidConfigFileException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs b/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs index f59ff9ee9..b5977e053 100644 --- a/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs +++ b/NzbDrone.Core/DataAugmentation/DailySeries/DailySeriesDataProxy.cs @@ -28,7 +28,7 @@ public IEnumerable GetDailySeriesIds() { try { - var dailySeriesIds = _httpProvider.DownloadString(Services.RootUrl + "/DailySeries/AllIds"); + var dailySeriesIds = _httpProvider.DownloadString(Services.RootUrl + "/v1/DailySeries"); var seriesIds = Json.Deserialize>(dailySeriesIds); @@ -46,7 +46,7 @@ public bool IsDailySeries(int tvdbid) { try { - var result = _httpProvider.DownloadString(Services.RootUrl + "/DailySeries/Check?seriesId=" + tvdbid); + var result = _httpProvider.DownloadString(Services.RootUrl + "/v1/DailySeries?seriesId=" + tvdbid); return Convert.ToBoolean(result); } catch (Exception ex) diff --git a/NzbDrone.Core/DataAugmentation/Scene/SceneMapping.cs b/NzbDrone.Core/DataAugmentation/Scene/SceneMapping.cs index 011096f15..07bdfc065 100644 --- a/NzbDrone.Core/DataAugmentation/Scene/SceneMapping.cs +++ b/NzbDrone.Core/DataAugmentation/Scene/SceneMapping.cs @@ -5,15 +5,15 @@ namespace NzbDrone.Core.DataAugmentation.Scene { public class SceneMapping : ModelBase { - [JsonProperty("CleanTitle")] + [JsonProperty("title")] public string ParseTerm { get; set; } - [JsonProperty("Title")] + [JsonProperty("searchTitle")] public string SearchTerm { get; set; } - [JsonProperty("Id")] public int TvdbId { get; set; } + [JsonProperty("season")] public int SeasonNumber { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs index 2447a96ec..9bda36efc 100644 --- a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs +++ b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingProxy.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using NzbDrone.Common; using NzbDrone.Common.Serializer; -using NzbDrone.Core.Configuration; namespace NzbDrone.Core.DataAugmentation.Scene { @@ -21,7 +20,7 @@ public SceneMappingProxy(IHttpProvider httpProvider) public List Fetch() { - var mappingsJson = _httpProvider.DownloadString(Services.RootUrl + "/SceneMapping/Active"); + var mappingsJson = _httpProvider.DownloadString(Services.RootUrl + "/v1/SceneMapping"); return Json.Deserialize>(mappingsJson); } } diff --git a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingRepository.cs b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingRepository.cs index 417b6cf11..5f1ade5c2 100644 --- a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingRepository.cs +++ b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingRepository.cs @@ -1,5 +1,7 @@ -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.DataAugmentation.Scene { @@ -10,8 +12,8 @@ public interface ISceneMappingRepository : IBasicRepository public class SceneMappingRepository : BasicRepository, ISceneMappingRepository { - public SceneMappingRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public SceneMappingRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs index 3c84d378c..d05dd69fd 100644 --- a/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs +++ b/NzbDrone.Core/DataAugmentation/Scene/SceneMappingService.cs @@ -2,8 +2,10 @@ using System.Linq; using NLog; using NzbDrone.Common.Cache; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser; namespace NzbDrone.Core.DataAugmentation.Scene @@ -18,7 +20,6 @@ public class SceneMappingService : ISceneMappingService, IHandleAsync, IExecute { - private readonly ISceneMappingRepository _repository; private readonly ISceneMappingProxy _sceneMappingProxy; private readonly Logger _logger; @@ -44,7 +45,6 @@ public string GetSceneName(int tvdbId) return mapping.SearchTerm; } - public Nullable GetTvDbId(string cleanName) { var mapping = _gettvdbIdCache.Find(cleanName.CleanSeriesTitle()); @@ -55,7 +55,6 @@ public Nullable GetTvDbId(string cleanName) return mapping.TvdbId; } - private void UpdateMappings() { _logger.Info("Updating Scene mapping"); @@ -74,7 +73,6 @@ private void UpdateMappings() } _repository.InsertMany(mappings); - } else { diff --git a/NzbDrone.Core/DataAugmentation/Scene/UpdateSceneMappingCommand.cs b/NzbDrone.Core/DataAugmentation/Scene/UpdateSceneMappingCommand.cs index 68356ab6e..215f8e033 100644 --- a/NzbDrone.Core/DataAugmentation/Scene/UpdateSceneMappingCommand.cs +++ b/NzbDrone.Core/DataAugmentation/Scene/UpdateSceneMappingCommand.cs @@ -1,8 +1,9 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.DataAugmentation.Scene { - public class UpdateSceneMappingCommand : ICommand + public class UpdateSceneMappingCommand : Command { + } } \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/BasicRepository.cs b/NzbDrone.Core/Datastore/BasicRepository.cs index daefd7eac..8552930fd 100644 --- a/NzbDrone.Core/Datastore/BasicRepository.cs +++ b/NzbDrone.Core/Datastore/BasicRepository.cs @@ -4,9 +4,10 @@ using System.Linq.Expressions; using Marr.Data; using Marr.Data.QGen; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore.Events; using NzbDrone.Common; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Datastore @@ -38,17 +39,17 @@ namespace NzbDrone.Core.Datastore public class BasicRepository : IBasicRepository where TModel : ModelBase, new() { private readonly IDatabase _database; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private IDataMapper DataMapper { get { return _database.GetDataMapper(); } } - public BasicRepository(IDatabase database, IMessageAggregator messageAggregator) + public BasicRepository(IDatabase database, IEventAggregator eventAggregator) { _database = database; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; } protected QueryBuilder Query @@ -115,7 +116,6 @@ public TModel Insert(TModel model) } DataMapper.Insert(model); - PublishModelEvent(model, RepositoryAction.Created); return model; } @@ -222,24 +222,29 @@ public void DeleteAll() DataMapper.Delete(c => c.Id > 0); } - private void PublishModelEvent(TModel model, RepositoryAction action) + protected void ModelCreated(TModel model) + { + PublishModelEvent(model, ModelAction.Created); + } + + protected void ModelUpdated(TModel model) + { + PublishModelEvent(model, ModelAction.Updated); + } + + protected void ModelDeleted(TModel model) + { + PublishModelEvent(model, ModelAction.Deleted); + } + + private void PublishModelEvent(TModel model, ModelAction action) { if (PublishModelEvents) { - _messageAggregator.PublishEvent(new ModelEvent(model, action)); + _eventAggregator.PublishEvent(new ModelEvent(model, action)); } } - protected virtual void OnModelChanged(IEnumerable models) - { - - } - - protected virtual void OnModelDeleted(IEnumerable models) - { - - } - protected virtual bool PublishModelEvents { get { return false; } diff --git a/NzbDrone.Core/Datastore/CachedBasicRepository.cs b/NzbDrone.Core/Datastore/CachedBasicRepository.cs deleted file mode 100644 index 1938af390..000000000 --- a/NzbDrone.Core/Datastore/CachedBasicRepository.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Generic; -using NzbDrone.Common.Cache; -using NzbDrone.Common.Messaging; - -namespace NzbDrone.Core.Datastore -{ - public abstract class CachedBasicRepository : BasicRepository where TModel : ModelBase, new() - { - private readonly ICacheManger _cacheManger; - - protected CachedBasicRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) - { - _cacheManger = new CacheManger(); - } - - protected ICached GetCache(string name) - { - return _cacheManger.GetCache(GetType(), name); - } - - protected override void OnModelChanged(IEnumerable models) - { - PurgeCache(); - } - - protected override void OnModelDeleted(IEnumerable models) - { - PurgeCache(); - } - - private void PurgeCache() - { - foreach (var model in _cacheManger.Caches) - { - model.Clear(); - } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/DbFactory.cs b/NzbDrone.Core/Datastore/DbFactory.cs index fb8d43a37..537fec3b0 100644 --- a/NzbDrone.Core/Datastore/DbFactory.cs +++ b/NzbDrone.Core/Datastore/DbFactory.cs @@ -3,9 +3,10 @@ using Marr.Data; using Marr.Data.Reflection; using NzbDrone.Common.Composition; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore.Migration.Framework; using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Datastore @@ -29,12 +30,16 @@ static DbFactory() public static void RegisterDatabase(IContainer container) { + container.Resolve().Create(); + container.Register(c => c.Resolve().Create()); + container.Resolve().Create(MigrationType.Log); + container.Register(c => { var db = c.Resolve().Create(MigrationType.Log); - return new LogRepository(db, c.Resolve()); + return new LogRepository(db, c.Resolve()); }); } diff --git a/NzbDrone.Core/Datastore/Events/ModelEvent.cs b/NzbDrone.Core/Datastore/Events/ModelEvent.cs index 30b9af580..3cabd3a69 100644 --- a/NzbDrone.Core/Datastore/Events/ModelEvent.cs +++ b/NzbDrone.Core/Datastore/Events/ModelEvent.cs @@ -2,23 +2,25 @@ namespace NzbDrone.Core.Datastore.Events { - public class ModelEvent : IEvent where T : ModelBase + public class ModelEvent : IEvent { - public T Model { get; set; } - public RepositoryAction Action { get; set; } + public TModel Model { get; set; } + public ModelAction Action { get; set; } - public ModelEvent(T model, RepositoryAction action) + public ModelEvent(TModel model, ModelAction action) { Model = model; Action = action; } } - public enum RepositoryAction + public enum ModelAction { + Unknow = 0, Created = 1, Updated = 2, - Deleted = 3 + Deleted = 3, + Sync = 4 } diff --git a/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs b/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs index 6614e2c14..83fd62478 100644 --- a/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs +++ b/NzbDrone.Core/Datastore/Migration/008_remove_backlog.cs @@ -8,8 +8,8 @@ public class remove_backlog : NzbDroneMigrationBase { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Series", new[] { "BacklogSetting" }); - SQLiteAlter.DropColumns("NamingConfig", new[] { "UseSceneName" }); + SqLiteAlter.DropColumns("Series", new[] { "BacklogSetting" }); + SqLiteAlter.DropColumns("NamingConfig", new[] { "UseSceneName" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs b/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs index d52799a03..f5ef5b2d1 100644 --- a/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs +++ b/NzbDrone.Core/Datastore/Migration/009_fix_renameEpisodes.cs @@ -8,7 +8,7 @@ public class fix_rename_episodes : NzbDroneMigrationBase { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("NamingConfig", new[] { "SeasonFolderFormat" }); + SqLiteAlter.DropColumns("NamingConfig", new[] { "SeasonFolderFormat" }); Execute.Sql("UPDATE NamingConfig SET RenameEpisodes = 1 WHERE RenameEpisodes = -1"); Execute.Sql("UPDATE NamingConfig SET RenameEpisodes = 0 WHERE RenameEpisodes = -2"); diff --git a/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs b/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs index 103f0648a..8f62c3b46 100644 --- a/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs +++ b/NzbDrone.Core/Datastore/Migration/011_remove_ignored.cs @@ -8,8 +8,8 @@ public class remove_ignored : NzbDroneMigrationBase { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Episodes", new[] { "Ignored" }); - SQLiteAlter.DropColumns("Seasons", new[] { "Ignored" }); + SqLiteAlter.DropColumns("Episodes", new[] { "Ignored" }); + SqLiteAlter.DropColumns("Seasons", new[] { "Ignored" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs b/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs index 417e73013..a7f156c87 100644 --- a/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs +++ b/NzbDrone.Core/Datastore/Migration/012_remove_custom_start_date.cs @@ -8,7 +8,7 @@ public class remove_custom_start_date : NzbDroneMigrationBase { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Series", new[] { "CustomStartDate" }); + SqLiteAlter.DropColumns("Series", new[] { "CustomStartDate" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs b/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs index 434ccc8a2..b88282202 100644 --- a/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs +++ b/NzbDrone.Core/Datastore/Migration/014_drop_air_date.cs @@ -8,7 +8,7 @@ public class drop_air_date : NzbDroneMigrationBase { protected override void MainDbUpgrade() { - SQLiteAlter.DropColumns("Episodes", new []{ "AirDate" }); + SqLiteAlter.DropColumns("Episodes", new []{ "AirDate" }); } } } diff --git a/NzbDrone.Core/Datastore/Migration/018_remove_duplicates.cs b/NzbDrone.Core/Datastore/Migration/018_remove_duplicates.cs new file mode 100644 index 000000000..7fb11e5e7 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/018_remove_duplicates.cs @@ -0,0 +1,60 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; +using System.Linq; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(18)] + public class remove_duplicates : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + using (var transaction = MigrationHelper.BeginTransaction()) + { + RemoveDuplicateSeries("TvdbId"); + RemoveDuplicateSeries("TitleSlug"); + + var duplicatedEpisodes = MigrationHelper.GetDuplicates("Episodes", "TvDbEpisodeId"); + + foreach (var duplicate in duplicatedEpisodes) + { + foreach (var episodeId in duplicate.OrderBy(c => c.Key).Skip(1).Select(c => c.Key)) + { + RemoveEpisodeRows(episodeId); + } + } + + transaction.Commit(); + } + } + + private void RemoveDuplicateSeries(string field) + { + var duplicatedSeries = MigrationHelper.GetDuplicates("Series", field); + + foreach (var duplicate in duplicatedSeries) + { + foreach (var seriesId in duplicate.OrderBy(c => c.Key).Skip(1).Select(c => c.Key)) + { + RemoveSeriesRows(seriesId); + } + } + } + + private void RemoveSeriesRows(int seriesId) + { + MigrationHelper.ExecuteNonQuery("DELETE FROM Series WHERE Id = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM Episodes WHERE SeriesId = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM Seasons WHERE SeriesId = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM History WHERE SeriesId = {0}", seriesId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM EpisodeFiles WHERE SeriesId = {0}", seriesId.ToString()); + } + + private void RemoveEpisodeRows(int episodeId) + { + MigrationHelper.ExecuteNonQuery("DELETE FROM Episodes WHERE Id = {0}", episodeId.ToString()); + MigrationHelper.ExecuteNonQuery("DELETE FROM History WHERE EpisodeId = {0}", episodeId.ToString()); + } + + } +} diff --git a/NzbDrone.Core/Datastore/Migration/019_restore_unique_constraints.cs b/NzbDrone.Core/Datastore/Migration/019_restore_unique_constraints.cs new file mode 100644 index 000000000..c4e304df7 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/019_restore_unique_constraints.cs @@ -0,0 +1,20 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(19)] + public class restore_unique_constraints : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + SqLiteAlter.AddIndexes("Series", + new SQLiteIndex { Column = "TvdbId", Table = "Series", Unique = true }, + new SQLiteIndex { Column = "TitleSlug", Table = "Series", Unique = true }); + + SqLiteAlter.AddIndexes("Episodes", + new SQLiteIndex { Column = "TvDbEpisodeId", Table = "Episodes", Unique = true }); + } + + } +} diff --git a/NzbDrone.Core/Datastore/Migration/020_add_year_and_seasons_to_series.cs b/NzbDrone.Core/Datastore/Migration/020_add_year_and_seasons_to_series.cs new file mode 100644 index 000000000..0521d3bdb --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/020_add_year_and_seasons_to_series.cs @@ -0,0 +1,69 @@ +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(20)] + public class add_year_and_seasons_to_series : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Alter.Table("Series").AddColumn("Year").AsInt32().Nullable(); + Alter.Table("Series").AddColumn("Seasons").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(); + + 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(); + } + } + } + } + } + } +} diff --git a/NzbDrone.Core/Datastore/Migration/021_drop_seasons_table.cs b/NzbDrone.Core/Datastore/Migration/021_drop_seasons_table.cs new file mode 100644 index 000000000..d2527c755 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/021_drop_seasons_table.cs @@ -0,0 +1,14 @@ +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(21)] + public class drop_seasons_table : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Delete.Table("Seasons"); + } + } +} diff --git a/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs b/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs index ebf634c15..90ac77a1e 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/MigrationContext.cs @@ -4,5 +4,6 @@ public class MigrationContext { public MigrationType MigrationType { get; set; } public ISQLiteAlter SQLiteAlter { get; set; } + public ISqLiteMigrationHelper MigrationHelper { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs b/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs index e2a0480f2..a930511c3 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/MigrationController.cs @@ -15,13 +15,15 @@ public class MigrationController : IMigrationController { private readonly IAnnouncer _announcer; private readonly ISQLiteAlter _sqLiteAlter; + private readonly ISqLiteMigrationHelper _migrationHelper; private static readonly HashSet MigrationCache = new HashSet(); - public MigrationController(IAnnouncer announcer, ISQLiteAlter sqLiteAlter) + public MigrationController(IAnnouncer announcer, ISQLiteAlter sqLiteAlter, ISqLiteMigrationHelper migrationHelper) { _announcer = announcer; _sqLiteAlter = sqLiteAlter; + _migrationHelper = migrationHelper; } public void MigrateToLatest(string connectionString, MigrationType migrationType) @@ -40,7 +42,8 @@ public void MigrateToLatest(string connectionString, MigrationType migrationType ApplicationContext = new MigrationContext { MigrationType = migrationType, - SQLiteAlter = _sqLiteAlter + SQLiteAlter = _sqLiteAlter, + MigrationHelper = _migrationHelper, } }; diff --git a/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs b/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs index c641d5f55..da9dde57a 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/NzbDroneMigrationBase.cs @@ -1,9 +1,18 @@ using System; +using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Core.Datastore.Migration.Framework { public abstract class NzbDroneMigrationBase : FluentMigrator.Migration { + private Logger _logger; + + protected NzbDroneMigrationBase() + { + _logger = NzbDroneLogger.GetLogger(); + } + protected virtual void MainDbUpgrade() { } @@ -16,7 +25,8 @@ public override void Up() { var context = (MigrationContext)ApplicationContext; - SQLiteAlter = context.SQLiteAlter; + SqLiteAlter = context.SQLiteAlter; + MigrationHelper = context.MigrationHelper; switch (context.MigrationType) { @@ -33,7 +43,8 @@ public override void Up() } } - public ISQLiteAlter SQLiteAlter { get; private set; } + protected ISQLiteAlter SqLiteAlter { get; private set; } + protected ISqLiteMigrationHelper MigrationHelper { get; private set; } public override void Down() { diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteColumn.cs b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteColumn.cs new file mode 100644 index 000000000..976916885 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteColumn.cs @@ -0,0 +1,13 @@ +namespace NzbDrone.Core.Datastore.Migration.Framework +{ + public class SQLiteColumn + { + public string Name { get; set; } + public string Schema { get; set; } + + public override string ToString() + { + return string.Format("[{0}] {1}", Name, Schema); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteIndex.cs b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteIndex.cs new file mode 100644 index 000000000..cbf18dbd9 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteIndex.cs @@ -0,0 +1,39 @@ +using System; + +namespace NzbDrone.Core.Datastore.Migration.Framework +{ + public class SQLiteIndex : IEquatable + { + public string Column { get; set; } + public string Table { get; set; } + public bool Unique { get; set; } + + public bool Equals(SQLiteIndex other) + { + return IndexName == other.IndexName; + } + + public override int GetHashCode() + { + return IndexName.GetHashCode(); + } + + public override string ToString() + { + return string.Format("[{0}] Unique: {1}", Column, Unique); + } + + public string IndexName + { + get + { + return string.Format("IX_{0}_{1}", Table, Column); + } + } + + public string CreateSql(string tableName) + { + return string.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper (vaio's conflicted copy 2013-09-04).cs b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper (vaio's conflicted copy 2013-09-04).cs new file mode 100644 index 000000000..0cbf55e4c --- /dev/null +++ b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper (vaio's conflicted copy 2013-09-04).cs @@ -0,0 +1,241 @@ +using System; +using System.Collections.Generic; +using System.Data.SQLite; +using System.Linq; +using System.Text.RegularExpressions; +using NLog; + +namespace NzbDrone.Core.Datastore.Migration.Framework +{ + public interface ISQLiteMigrationHelper + { + Dictionary GetColumns(string tableName); + void CreateTable(string tableName, IEnumerable values, IEnumerable indexes); + void CopyData(string sourceTable, string destinationTable, IEnumerable columns); + void DropTable(string tableName); + void RenameTable(string tableName, string newName); + List GetDuplicates(string tableName, string columnName); + SQLiteTransaction BeginTransaction(); + List GetIndexes(string tableName); + } + + public class SQLiteMigrationHelper : ISQLiteMigrationHelper + { + private readonly SQLiteConnection _connection; + + private static readonly Regex SchemaRegex = new Regex(@"['\""\[](?\w+)['\""\]]\s(?[\w-\s]+)", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); + + private static readonly Regex IndexRegex = new Regex(@"\(""(?.*)""\s(?ASC|DESC)\)$", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); + + public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory, Logger logger) + { + try + { + _connection = new SQLiteConnection(connectionStringFactory.MainDbConnectionString); + _connection.Open(); + } + catch (Exception e) + { + logger.ErrorException("Couldn't open database " + connectionStringFactory.MainDbConnectionString, e); + throw; + } + + } + + private string GetOriginalSql(string tableName) + { + var command = + new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='table' AND name ='{0}'", + tableName)); + + command.Connection = _connection; + + return (string)command.ExecuteScalar(); + } + + public Dictionary GetColumns(string tableName) + { + var originalSql = GetOriginalSql(tableName); + + var matches = SchemaRegex.Matches(originalSql); + + return matches.Cast().ToDictionary( + match => match.Groups["name"].Value.Trim(), + match => new SQLiteColumn + { + Name = match.Groups["name"].Value.Trim(), + Schema = match.Groups["schema"].Value.Trim() + }); + } + + + private static IEnumerable ReadArray(SQLiteDataReader reader) + { + while (reader.Read()) + { + yield return (T)Convert.ChangeType(reader[0], typeof(T)); + } + } + + public List GetIndexes(string tableName) + { + var command = new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name ='{0}'", tableName)); + command.Connection = _connection; + + var reader = command.ExecuteReader(); + var sqls = ReadArray(reader).ToList(); + + + var indexes = new List(); + + foreach (var indexSql in sqls) + { + var newIndex = new SQLiteIndex(); + var matches = IndexRegex.Match(indexSql); + + newIndex.Column = matches.Groups["col"].Value; + newIndex.Unique = indexSql.Contains("UNIQUE"); + newIndex.Table = tableName; + + indexes.Add(newIndex); + } + + return indexes; + } + + public void CreateTable(string tableName, IEnumerable values, IEnumerable indexes) + { + var columns = String.Join(",", values.Select(c => c.ToString())); + + ExecuteNonQuery("CREATE TABLE [{0}] ({1})", tableName, columns); + + foreach (var index in indexes) + { + ExecuteNonQuery("DROP INDEX {0}", index.IndexName); + ExecuteNonQuery(index.CreateSql(tableName)); + } + } + + public void CopyData(string sourceTable, string destinationTable, IEnumerable columns) + { + var originalCount = GetRowCount(sourceTable); + + var columnsToTransfer = String.Join(",", columns.Select(c => c.Name)); + + var transferCommand = BuildCommand("INSERT INTO {0} SELECT {1} FROM {2};", destinationTable, columnsToTransfer, sourceTable); + + transferCommand.ExecuteNonQuery(); + + var transferredRows = GetRowCount(destinationTable); + + + if (transferredRows != originalCount) + { + throw new ApplicationException(string.Format("Expected {0} rows to be copied from [{1}] to [{2}]. But only copied {3}", originalCount, sourceTable, destinationTable, transferredRows)); + } + } + + + public void DropTable(string tableName) + { + var dropCommand = BuildCommand("DROP TABLE {0};", tableName); + dropCommand.ExecuteNonQuery(); + } + + + public void RenameTable(string tableName, string newName) + { + var renameCommand = BuildCommand("ALTER TABLE {0} RENAME TO {1};", tableName, newName); + renameCommand.ExecuteNonQuery(); + } + + public Dictionary GetDuplicates(string tableName, string columnName) + { + var dupCommand = BuildCommand("select id, {0} from {1}", columnName, tableName); + + var result = new Dictionary(); + using (var reader = dupCommand.ExecuteReader()) + { + while (reader.Read()) + { + + } + } + + + return ReadArray().ToList(); + } + + public int GetRowCount(string tableName) + { + var countCommand = BuildCommand("SELECT COUNT(*) FROM {0};", tableName); + return Convert.ToInt32(countCommand.ExecuteScalar()); + } + + + public SQLiteTransaction BeginTransaction() + { + return _connection.BeginTransaction(); + } + + private SQLiteCommand BuildCommand(string format, params string[] args) + { + var command = new SQLiteCommand(string.Format(format, args)); + command.Connection = _connection; + return command; + } + + + + private void ExecuteNonQuery(string command, params string[] args) + { + var sqLiteCommand = new SQLiteCommand(string.Format(command, args)) + { + Connection = _connection + }; + + sqLiteCommand.ExecuteNonQuery(); + } + + + public class SQLiteColumn + { + public string Name { get; set; } + public string Schema { get; set; } + + public override string ToString() + { + return string.Format("[{0}] {1}", Name, Schema); + } + } + + public class SQLiteIndex + { + public string Column { get; set; } + public string Table { get; set; } + public bool Unique { get; set; } + + public override string ToString() + { + return string.Format("[{0}] Unique: {1}", Column, Unique); + } + + public string IndexName + { + get + { + return string.Format("IX_{0}_{1}", Table, Column); + } + } + + public string CreateSql(string tableName) + { + return string.Format(@"CREATE UNIQUE INDEX ""{2}"" ON ""{0}"" (""{1}"" ASC)", tableName, Column, IndexName); + } + } + } + + +} \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs index 80b4e25e8..42f8f8e7b 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/SQLiteMigrationHelper.cs @@ -4,28 +4,35 @@ using System.Linq; using System.Text.RegularExpressions; using NLog; +using NzbDrone.Common.Exceptions; namespace NzbDrone.Core.Datastore.Migration.Framework { - public interface ISQLiteMigrationHelper + public interface ISqLiteMigrationHelper { - Dictionary GetColumns(string tableName); - void CreateTable(string tableName, IEnumerable values); - void CopyData(string sourceTable, string destinationTable, IEnumerable columns); - int GetRowCount(string tableName); + Dictionary GetColumns(string tableName); + void CreateTable(string tableName, IEnumerable values, IEnumerable indexes); + void CopyData(string sourceTable, string destinationTable, IEnumerable columns); void DropTable(string tableName); void RenameTable(string tableName, string newName); + IEnumerable>> GetDuplicates(string tableName, string columnName); SQLiteTransaction BeginTransaction(); + List GetIndexes(string tableName); + int ExecuteScalar(string command, params string[] args); + void ExecuteNonQuery(string command, params string[] args); } - public class SQLiteMigrationHelper : ISQLiteMigrationHelper + public class SqLiteMigrationHelper : ISqLiteMigrationHelper { private readonly SQLiteConnection _connection; private static readonly Regex SchemaRegex = new Regex(@"['\""\[](?\w+)['\""\]]\s(?[\w-\s]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); - public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory,Logger logger) + private static readonly Regex IndexRegex = new Regex(@"\(""(?.*)""\s(?ASC|DESC)\)$", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline); + + public SqLiteMigrationHelper(IConnectionStringFactory connectionStringFactory, Logger logger) { try { @@ -34,7 +41,7 @@ public SQLiteMigrationHelper(IConnectionStringFactory connectionStringFactory,Lo } catch (Exception e) { - logger.ErrorException("Couldn't open databse " + connectionStringFactory.MainDbConnectionString, e); + logger.ErrorException("Couldn't open database " + connectionStringFactory.MainDbConnectionString, e); throw; } @@ -47,7 +54,15 @@ private string GetOriginalSql(string tableName) tableName)); command.Connection = _connection; - return (string)command.ExecuteScalar(); + + var sql = (string)command.ExecuteScalar(); + + if (string.IsNullOrWhiteSpace(sql)) + { + throw new TableNotFoundException(tableName); + } + + return sql; } public Dictionary GetColumns(string tableName) @@ -65,14 +80,52 @@ public Dictionary GetColumns(string tableName) }); } - public void CreateTable(string tableName, IEnumerable values) + + private static IEnumerable ReadArray(SQLiteDataReader reader) + { + while (reader.Read()) + { + yield return (T)Convert.ChangeType(reader[0], typeof(T)); + } + } + + public List GetIndexes(string tableName) + { + var command = new SQLiteCommand(string.Format("SELECT sql FROM sqlite_master WHERE type='index' AND tbl_name ='{0}'", tableName)); + command.Connection = _connection; + + var reader = command.ExecuteReader(); + var sqls = ReadArray(reader).ToList(); + + + var indexes = new List(); + + foreach (var indexSql in sqls) + { + var newIndex = new SQLiteIndex(); + var matches = IndexRegex.Match(indexSql); + + newIndex.Column = matches.Groups["col"].Value; + newIndex.Unique = indexSql.Contains("UNIQUE"); + newIndex.Table = tableName; + + indexes.Add(newIndex); + } + + return indexes; + } + + public void CreateTable(string tableName, IEnumerable values, IEnumerable indexes) { var columns = String.Join(",", values.Select(c => c.ToString())); - var command = new SQLiteCommand(string.Format("CREATE TABLE [{0}] ({1})", tableName, columns)); - command.Connection = _connection; + ExecuteNonQuery("CREATE TABLE [{0}] ({1})", tableName, columns); - command.ExecuteNonQuery(); + foreach (var index in indexes) + { + ExecuteNonQuery("DROP INDEX IF EXISTS {0}", index.IndexName); + ExecuteNonQuery(index.CreateSql(tableName)); + } } public void CopyData(string sourceTable, string destinationTable, IEnumerable columns) @@ -108,6 +161,23 @@ public void RenameTable(string tableName, string newName) renameCommand.ExecuteNonQuery(); } + public IEnumerable>> GetDuplicates(string tableName, string columnName) + { + var getDuplicates = BuildCommand("select id, {0} from {1}", columnName, tableName); + + var result = new List>(); + + using (var reader = getDuplicates.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(new KeyValuePair(reader.GetInt32(0), (T)Convert.ChangeType(reader[1], typeof(T)))); + } + } + + return result.GroupBy(c => c.Value).Where(g => g.Count() > 1); + } + public int GetRowCount(string tableName) { var countCommand = BuildCommand("SELECT COUNT(*) FROM {0};", tableName); @@ -128,17 +198,35 @@ private SQLiteCommand BuildCommand(string format, params string[] args) } - public class SQLiteColumn + public void ExecuteNonQuery(string command, params string[] args) { - public string Name { get; set; } - public string Schema { get; set; } - - public override string ToString() + var sqLiteCommand = new SQLiteCommand(string.Format(command, args)) { - return string.Format("[{0}] {1}", Name, Schema); + Connection = _connection + }; + + sqLiteCommand.ExecuteNonQuery(); + } + + public int ExecuteScalar(string command, params string[] args) + { + var sqLiteCommand = new SQLiteCommand(string.Format(command, args)) + { + Connection = _connection + }; + + return (int)sqLiteCommand.ExecuteScalar(); + } + + private class TableNotFoundException : NzbDroneException + { + public TableNotFoundException(string tableName) + : base("Table [{0}] not found", tableName) + { + } } + + } - - } \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs b/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs index ce2372d69..c22670588 100644 --- a/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs +++ b/NzbDrone.Core/Datastore/Migration/Framework/SqliteAlter.cs @@ -6,13 +6,14 @@ namespace NzbDrone.Core.Datastore.Migration.Framework public interface ISQLiteAlter { void DropColumns(string tableName, IEnumerable columns); + void AddIndexes(string tableName, params SQLiteIndex[] indexes); } public class SQLiteAlter : ISQLiteAlter { - private readonly ISQLiteMigrationHelper _sqLiteMigrationHelper; + private readonly ISqLiteMigrationHelper _sqLiteMigrationHelper; - public SQLiteAlter(ISQLiteMigrationHelper sqLiteMigrationHelper) + public SQLiteAlter(ISqLiteMigrationHelper sqLiteMigrationHelper) { _sqLiteMigrationHelper = sqLiteMigrationHelper; } @@ -22,21 +23,44 @@ public void DropColumns(string tableName, IEnumerable columns) using (var transaction = _sqLiteMigrationHelper.BeginTransaction()) { var originalColumns = _sqLiteMigrationHelper.GetColumns(tableName); + var originalIndexes = _sqLiteMigrationHelper.GetIndexes(tableName); var newColumns = originalColumns.Where(c => !columns.Contains(c.Key)).Select(c => c.Value).ToList(); + var newIndexes = originalIndexes.Where(c => !columns.Contains(c.Column)); - var tempTableName = tableName + "_temp"; - - _sqLiteMigrationHelper.CreateTable(tempTableName, newColumns); - - _sqLiteMigrationHelper.CopyData(tableName, tempTableName, newColumns); - - _sqLiteMigrationHelper.DropTable(tableName); - - _sqLiteMigrationHelper.RenameTable(tempTableName, tableName); + CreateTable(tableName, newColumns, newIndexes); transaction.Commit(); } } + + public void AddIndexes(string tableName, params SQLiteIndex[] indexes) + { + using (var transaction = _sqLiteMigrationHelper.BeginTransaction()) + { + var columns = _sqLiteMigrationHelper.GetColumns(tableName).Select(c => c.Value).ToList(); + var originalIndexes = _sqLiteMigrationHelper.GetIndexes(tableName); + + var newIndexes = originalIndexes.Union(indexes); + + + CreateTable(tableName, columns, newIndexes); + + transaction.Commit(); + } + } + + private void CreateTable(string tableName, List newColumns, IEnumerable newIndexes) + { + var tempTableName = tableName + "_temp"; + + _sqLiteMigrationHelper.CreateTable(tempTableName, newColumns, newIndexes); + + _sqLiteMigrationHelper.CopyData(tableName, tempTableName, newColumns); + + _sqLiteMigrationHelper.DropTable(tableName); + + _sqLiteMigrationHelper.RenameTable(tempTableName, tableName); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Datastore/TableMapping.cs b/NzbDrone.Core/Datastore/TableMapping.cs index 80242f783..533b3ab70 100644 --- a/NzbDrone.Core/Datastore/TableMapping.cs +++ b/NzbDrone.Core/Datastore/TableMapping.cs @@ -45,8 +45,6 @@ public static void Map() .Relationship() .HasOne(s => s.QualityProfile, s => s.QualityProfileId); - Mapper.Entity().RegisterModel("Seasons"); - Mapper.Entity().RegisterModel("Episodes") .Ignore(e => e.SeriesTitle) .Ignore(e => e.Series) diff --git a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index d94036901..f2b1404cf 100644 --- a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Core.DecisionEngine.Specifications.Search; +using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Common.Serializer; @@ -12,8 +13,8 @@ namespace NzbDrone.Core.DecisionEngine { public interface IMakeDownloadDecision { - List GetRssDecision(IEnumerable reports); - List GetSearchDecision(IEnumerable reports, SearchCriteriaBase searchCriteriaBase); + List GetRssDecision(List reports); + List GetSearchDecision(List reports, SearchCriteriaBase searchCriteriaBase); } public class DownloadDecisionMaker : IMakeDownloadDecision @@ -29,21 +30,34 @@ public DownloadDecisionMaker(IEnumerable specifications, IPar _logger = logger; } - public List GetRssDecision(IEnumerable reports) + public List GetRssDecision(List reports) { return GetDecisions(reports).ToList(); } - public List GetSearchDecision(IEnumerable reports, SearchCriteriaBase searchCriteriaBase) + public List GetSearchDecision(List reports, SearchCriteriaBase searchCriteriaBase) { return GetDecisions(reports, searchCriteriaBase).ToList(); } - private IEnumerable GetDecisions(IEnumerable reports, SearchCriteriaBase searchCriteria = null) + private IEnumerable GetDecisions(List reports, SearchCriteriaBase searchCriteria = null) { + if (reports.Any()) + { + _logger.ProgressInfo("Processing {0} reports", reports.Count); + } + + else + { + _logger.ProgressInfo("No reports found"); + } + + var reportNumber = 1; + foreach (var report in reports) { DownloadDecision decision = null; + _logger.ProgressTrace("Processing report {0}/{1}", reportNumber, reports.Count); try { @@ -51,8 +65,8 @@ private IEnumerable GetDecisions(IEnumerable repor if (parsedEpisodeInfo != null && !string.IsNullOrWhiteSpace(parsedEpisodeInfo.SeriesTitle)) { - var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvRageId); - remoteEpisode.Report = report; + var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, report.TvRageId, searchCriteria); + remoteEpisode.Release = report; if (remoteEpisode.Series != null) { @@ -69,6 +83,8 @@ private IEnumerable GetDecisions(IEnumerable repor _logger.ErrorException("Couldn't process report.", e); } + reportNumber++; + if (decision != null) { yield return decision; @@ -102,13 +118,13 @@ private string EvaluateSpec(IRejectWithReason spec, RemoteEpisode remoteEpisode, } catch (Exception e) { - e.Data.Add("report", remoteEpisode.Report.ToJson()); + e.Data.Add("report", remoteEpisode.Release.ToJson()); e.Data.Add("parsed", remoteEpisode.ParsedEpisodeInfo.ToJson()); - _logger.ErrorException("Couldn't evaluate decision on " + remoteEpisode.Report.Title, e); + _logger.ErrorException("Couldn't evaluate decision on " + remoteEpisode.Release.Title, e); return string.Format("{0}: {1}", spec.GetType().Name, e.Message); } return null; } } -} \ No newline at end of file +} diff --git a/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs b/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs index 8fbd65d1a..8bec31bc1 100644 --- a/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs @@ -1,5 +1,4 @@ using NLog; -using NzbDrone.Core.Configuration; using NzbDrone.Core.Qualities; using NzbDrone.Core.Tv; @@ -7,21 +6,21 @@ namespace NzbDrone.Core.DecisionEngine { public interface IQualityUpgradableSpecification { - bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null); + bool IsUpgradable(QualityModel currentQuality, QualityModel newQuality = null); + bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null); + bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality); } public class QualityUpgradableSpecification : IQualityUpgradableSpecification { - private readonly IConfigService _configService; private readonly Logger _logger; - public QualityUpgradableSpecification(IConfigService configService, Logger logger) + public QualityUpgradableSpecification(Logger logger) { - _configService = configService; _logger = logger; } - public bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) + public bool IsUpgradable(QualityModel currentQuality, QualityModel newQuality = null) { if (newQuality != null) { @@ -31,20 +30,40 @@ public bool IsUpgradable(QualityProfile profile, QualityModel currentQuality, Qu return false; } - if (currentQuality.Quality == newQuality.Quality && newQuality.Proper && _configService.AutoDownloadPropers) + if (IsProperUpgrade(currentQuality, newQuality)) { - _logger.Trace("Upgrading existing item to proper."); return true; } } + return true; + } + + public bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null) + { if (currentQuality.Quality >= profile.Cutoff) { + if (newQuality != null && IsProperUpgrade(currentQuality, newQuality)) + { + return true; + } + _logger.Trace("Existing item meets cut-off. skipping."); return false; } return true; } + + public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality) + { + if (currentQuality.Quality == newQuality.Quality && newQuality > currentQuality) + { + _logger.Trace("New quality is a proper for existing quality"); + return true; + } + + return false; + } } } diff --git a/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs index ada1817e5..1d6f5af7f 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/AcceptableSizeSpecification.cs @@ -57,17 +57,16 @@ public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase sear //Multiply maxSize by Series.Runtime maxSize = maxSize * subject.Series.Runtime * subject.Episodes.Count; - //Check if there was only one episode parsed - //and it is the first or last episode of the season - if (subject.Episodes.Count == 1 && _episodeService.IsFirstOrLastEpisodeOfSeason(subject.Episodes.Single().Id)) + //Check if there was only one episode parsed and it is the first + if (subject.Episodes.Count == 1 && subject.Episodes.First().EpisodeNumber == 1) { maxSize = maxSize * 2; } //If the parsed size is greater than maxSize we don't want it - if (subject.Report.Size > maxSize) + if (subject.Release.Size > maxSize) { - _logger.Trace("Item: {0}, Size: {1} is greater than maximum allowed size ({2}), rejecting.", subject, subject.Report.Size, maxSize); + _logger.Trace("Item: {0}, Size: {1} is greater than maximum allowed size ({2}), rejecting.", subject, subject.Release.Size, maxSize); return false; } diff --git a/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs new file mode 100644 index 000000000..f43df2836 --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/CutoffSpecification.cs @@ -0,0 +1,43 @@ +using System.Linq; +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications +{ + public class CutoffSpecification : IDecisionEngineSpecification + { + private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; + private readonly Logger _logger; + + public CutoffSpecification(QualityUpgradableSpecification qualityUpgradableSpecification, Logger logger) + { + _qualityUpgradableSpecification = qualityUpgradableSpecification; + _logger = logger; + } + + public string RejectionReason + { + get + { + return "Cutoff has already been met"; + } + } + + public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + { + foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) + { + _logger.Trace("Comparing file quality with report. Existing file is {0}", file.Quality); + + + if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.QualityProfile, file.Quality, subject.ParsedEpisodeInfo.Quality)) + { + return false; + } + } + + return true; + } + } +} diff --git a/NzbDrone.Core/DecisionEngine/DownloadDecision.cs b/NzbDrone.Core/DecisionEngine/Specifications/DownloadDecision.cs similarity index 93% rename from NzbDrone.Core/DecisionEngine/DownloadDecision.cs rename to NzbDrone.Core/DecisionEngine/Specifications/DownloadDecision.cs index b3037395c..22616a735 100644 --- a/NzbDrone.Core/DecisionEngine/DownloadDecision.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/DownloadDecision.cs @@ -2,7 +2,7 @@ using System.Linq; using NzbDrone.Core.Parser.Model; -namespace NzbDrone.Core.DecisionEngine +namespace NzbDrone.Core.DecisionEngine.Specifications { public class DownloadDecision { diff --git a/NzbDrone.Core/DecisionEngine/Specifications/NotRestrictedReleaseSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/NotRestrictedReleaseSpecification.cs index 698c1b32b..628b8183e 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/NotRestrictedReleaseSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/NotRestrictedReleaseSpecification.cs @@ -21,7 +21,7 @@ public string RejectionReason { get { - return "Contrains restricted term."; + return "Contains restricted term."; } } @@ -41,7 +41,7 @@ public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase sear foreach (var restriction in restrictions) { - if (subject.Report.Title.ToLowerInvariant().Contains(restriction.ToLowerInvariant())) + if (subject.Release.Title.ToLowerInvariant().Contains(restriction.ToLowerInvariant())) { _logger.Trace("{0} is restricted: {1}", subject, restriction); return false; diff --git a/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs new file mode 100644 index 000000000..afcb7149a --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/NotSampleSpecification.cs @@ -0,0 +1,20 @@ +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications +{ + public class NotSampleSpecification : IDecisionEngineSpecification + { + public string RejectionReason { get { return "Sample"; } } + + public bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + { + if (subject.Release.Title.ToLower().Contains("sample") && subject.Release.Size < 70.Megabytes()) + { + return false; + } + + return true; + } + } +} diff --git a/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs index 01b692adf..a2f4dee15 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/RetentionSpecification.cs @@ -27,7 +27,7 @@ public string RejectionReason public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) { - var age = subject.Report.Age; + var age = subject.Release.Age; _logger.Trace("Checking if report meets retention requirements. {0}", age); if (_configService.Retention > 0 && age > _configService.Retention) diff --git a/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs new file mode 100644 index 000000000..f0a133409 --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using NLog; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync +{ + public class ProperSpecification : IDecisionEngineSpecification + { + private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; + private readonly IConfigService _configService; + private readonly Logger _logger; + + public ProperSpecification(QualityUpgradableSpecification qualityUpgradableSpecification, IConfigService configService, Logger logger) + { + _qualityUpgradableSpecification = qualityUpgradableSpecification; + _configService = configService; + _logger = logger; + } + + public string RejectionReason + { + get + { + return "Proper for old episode"; + } + } + + public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + { + if (searchCriteria != null) + { + return true; + } + + foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) + { + if (_qualityUpgradableSpecification.IsProperUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality)) + { + if (file.DateAdded < DateTime.Today.AddDays(-7)) + { + _logger.Trace("Proper for old file, skipping: {0}", subject); + return false; + } + + if (!_configService.AutoDownloadPropers) + { + _logger.Trace("Auto downloading of propers is disabled"); + return false; + } + } + } + + return true; + } + } +} diff --git a/NzbDrone.Core/DecisionEngine/Specifications/RssSync/UpgradeHistorySpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/RssSync/UpgradeHistorySpecification.cs index 21611c161..e844cb91d 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/RssSync/UpgradeHistorySpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/RssSync/UpgradeHistorySpecification.cs @@ -22,7 +22,7 @@ public string RejectionReason { get { - return "Higher quality report exists in history"; + return "Existing file in history is of equal or higher quality"; } } @@ -40,7 +40,7 @@ public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase sear if (bestQualityInHistory != null) { _logger.Trace("Comparing history quality with report. History is {0}", bestQualityInHistory); - if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, bestQualityInHistory, subject.ParsedEpisodeInfo.Quality)) + if (!_qualityUpgradableSpecification.IsUpgradable(bestQualityInHistory, subject.ParsedEpisodeInfo.Quality)) return false; } } diff --git a/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs index df4bb37b0..daaf3146c 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/Search/DailyEpisodeMatchSpecification.cs @@ -34,7 +34,7 @@ public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase search if (dailySearchSpec == null) return true; - var episode = _episodeService.GetEpisode(dailySearchSpec.SeriesId, dailySearchSpec.Airtime); + var episode = _episodeService.GetEpisode(dailySearchSpec.Series.Id, dailySearchSpec.Airtime); if (!remoteEpisode.ParsedEpisodeInfo.AirDate.HasValue || remoteEpisode.ParsedEpisodeInfo.AirDate.Value.ToString(Episode.AIR_DATE_FORMAT) != episode.AirDate) { diff --git a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs index 8c0f07450..004f20134 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using NLog; using NzbDrone.Core.IndexerSearch.Definitions; @@ -21,7 +20,7 @@ public string RejectionReason { get { - return "Higher quality exists on disk"; + return "Existing file on disk is of equal or higher quality"; } } @@ -31,19 +30,10 @@ public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase sear { _logger.Trace("Comparing file quality with report. Existing file is {0}", file.Quality); - if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.QualityProfile, file.Quality, subject.ParsedEpisodeInfo.Quality)) + if (!_qualityUpgradableSpecification.IsUpgradable(file.Quality, subject.ParsedEpisodeInfo.Quality)) { return false; } - - if (searchCriteria == null && - subject.ParsedEpisodeInfo.Quality.Quality == file.Quality.Quality && - subject.ParsedEpisodeInfo.Quality.Proper && - file.DateAdded < DateTime.Today.AddDays(-7)) - { - _logger.Trace("Proper for old file, skipping: {0}", subject); - return false; - } } return true; diff --git a/NzbDrone.Core/Download/Clients/BlackholeProvider.cs b/NzbDrone.Core/Download/Clients/BlackholeProvider.cs index 520e06943..1598ee647 100644 --- a/NzbDrone.Core/Download/Clients/BlackholeProvider.cs +++ b/NzbDrone.Core/Download/Clients/BlackholeProvider.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using NLog; using NzbDrone.Common; @@ -13,29 +12,20 @@ public class BlackholeProvider : IDownloadClient { private readonly IConfigService _configService; private readonly IHttpProvider _httpProvider; - private readonly IDiskProvider _diskProvider; private readonly Logger _logger; - public BlackholeProvider(IConfigService configService, IHttpProvider httpProvider, - IDiskProvider diskProvider, Logger logger) + public BlackholeProvider(IConfigService configService, IHttpProvider httpProvider, Logger logger) { _configService = configService; _httpProvider = httpProvider; - _diskProvider = diskProvider; _logger = logger; } - - public bool IsInQueue(RemoteEpisode newEpisode) - { - throw new NotImplementedException(); - } - public void DownloadNzb(RemoteEpisode remoteEpisode) { - var url = remoteEpisode.Report.NzbUrl; - var title = remoteEpisode.Report.Title; + var url = remoteEpisode.Release.DownloadUrl; + var title = remoteEpisode.Release.Title; title = FileNameBuilder.CleanFilename(title); diff --git a/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs b/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs index 6fd7a9061..144805113 100644 --- a/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs +++ b/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Net; using Newtonsoft.Json; using NLog; using NzbDrone.Common; @@ -24,8 +23,8 @@ public NzbgetClient(IConfigService configService, IHttpProvider httpProvider, Lo public void DownloadNzb(RemoteEpisode remoteEpisode) { - var url = remoteEpisode.Report.NzbUrl; - var title = remoteEpisode.Report.Title + ".nzb"; + var url = remoteEpisode.Release.DownloadUrl; + var title = remoteEpisode.Release.Title + ".nzb"; string cat = _configService.NzbgetTvCategory; int priority = remoteEpisode.IsRecentEpisode() ? (int)_configService.NzbgetRecentTvPriority : (int)_configService.NzbgetOlderTvPriority; diff --git a/NzbDrone.Core/Download/Clients/PneumaticClient.cs b/NzbDrone.Core/Download/Clients/PneumaticClient.cs index a7e59b6a4..537683243 100644 --- a/NzbDrone.Core/Download/Clients/PneumaticClient.cs +++ b/NzbDrone.Core/Download/Clients/PneumaticClient.cs @@ -3,6 +3,7 @@ using System.IO; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Configuration; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; @@ -15,7 +16,7 @@ public class PneumaticClient : IDownloadClient private readonly IHttpProvider _httpProvider; private readonly IDiskProvider _diskProvider; - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger logger = NzbDroneLogger.GetLogger(); public PneumaticClient(IConfigService configService, IHttpProvider httpProvider, IDiskProvider diskProvider) @@ -27,8 +28,8 @@ public PneumaticClient(IConfigService configService, IHttpProvider httpProvider, public void DownloadNzb(RemoteEpisode remoteEpisode) { - var url = remoteEpisode.Report.NzbUrl; - var title = remoteEpisode.Report.Title; + var url = remoteEpisode.Release.DownloadUrl; + var title = remoteEpisode.Release.Title; if (remoteEpisode.ParsedEpisodeInfo.FullSeason) { diff --git a/NzbDrone.Core/Download/Clients/Sabnzbd/SabAutoConfigureService.cs b/NzbDrone.Core/Download/Clients/Sabnzbd/SabAutoConfigureService.cs index 6ff5a666a..224cae5c5 100644 --- a/NzbDrone.Core/Download/Clients/Sabnzbd/SabAutoConfigureService.cs +++ b/NzbDrone.Core/Download/Clients/Sabnzbd/SabAutoConfigureService.cs @@ -6,12 +6,13 @@ using System.Net.NetworkInformation; using System.Text.RegularExpressions; using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Core.Download.Clients.Sabnzbd { public class SabAutoConfigureService { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public SabModel AutoConfigureSab() { diff --git a/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs b/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs index 281096945..40a241a1c 100644 --- a/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs +++ b/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; -using System.Net; using System.Web; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Cache; using NzbDrone.Core.Configuration; using NzbDrone.Core.Parser.Model; using RestSharp; @@ -26,8 +26,8 @@ public IRestRequest AddToQueueRequest(RemoteEpisode remoteEpisode) string cat = _configService.SabTvCategory; int priority = (int)_configService.SabRecentTvPriority; - string name = remoteEpisode.Report.NzbUrl.Replace("&", "%26"); - string nzbName = HttpUtility.UrlEncode(remoteEpisode.Report.Title); + string name = remoteEpisode.Release.DownloadUrl.Replace("&", "%26"); + string nzbName = HttpUtility.UrlEncode(remoteEpisode.Release.Title); string action = string.Format("mode=addurl&name={0}&priority={1}&pp=3&cat={2}&nzbname={3}&output=json", name, priority, cat, nzbName); @@ -53,19 +53,21 @@ public class SabnzbdClient : IDownloadClient { private readonly IConfigService _configService; private readonly IHttpProvider _httpProvider; + private readonly ICached> _queueCache; private readonly Logger _logger; - public SabnzbdClient(IConfigService configService, IHttpProvider httpProvider, Logger logger) + public SabnzbdClient(IConfigService configService, IHttpProvider httpProvider, ICacheManger cacheManger, Logger logger) { _configService = configService; _httpProvider = httpProvider; + _queueCache = cacheManger.GetCache>(GetType(), "queue"); _logger = logger; } public void DownloadNzb(RemoteEpisode remoteEpisode) { - var url = remoteEpisode.Report.NzbUrl; - var title = remoteEpisode.Report.Title; + var url = remoteEpisode.Release.DownloadUrl; + var title = remoteEpisode.Release.Title; string cat = _configService.SabTvCategory; int priority = remoteEpisode.IsRecentEpisode() ? (int)_configService.SabRecentTvPriority : (int)_configService.SabOlderTvPriority; @@ -97,24 +99,31 @@ public bool IsConfigured public IEnumerable GetQueue() { - string action = String.Format("mode=queue&output=json&start={0}&limit={1}", 0, 0); - string request = GetSabRequest(action); - string response = _httpProvider.DownloadString(request); - - CheckForError(response); - - var sabQueue = JsonConvert.DeserializeObject(JObject.Parse(response).SelectToken("queue").ToString()).Items; - - foreach (var sabQueueItem in sabQueue) + return _queueCache.Get("queue", () => { - var queueItem = new QueueItem(); - queueItem.Id = sabQueueItem.Id; - queueItem.Title = sabQueueItem.Title; - queueItem.Size = sabQueueItem.Size; - queueItem.SizeLeft = sabQueueItem.Size; + string action = String.Format("mode=queue&output=json&start={0}&limit={1}", 0, 0); + string request = GetSabRequest(action); + string response = _httpProvider.DownloadString(request); - yield return queueItem; - } + CheckForError(response); + + var sabQueue = JsonConvert.DeserializeObject(JObject.Parse(response).SelectToken("queue").ToString()).Items; + + var queueItems = new List(); + + foreach (var sabQueueItem in sabQueue) + { + var queueItem = new QueueItem(); + queueItem.Id = sabQueueItem.Id; + queueItem.Title = sabQueueItem.Title; + queueItem.Size = sabQueueItem.Size; + queueItem.SizeLeft = sabQueueItem.Size; + + queueItems.Add( queueItem); + } + + return queueItems; + }, TimeSpan.FromSeconds(10)); } public virtual List GetHistory(int start = 0, int limit = 0) diff --git a/NzbDrone.Core/Download/DownloadApprovedReports.cs b/NzbDrone.Core/Download/DownloadApprovedReports.cs index 0da8ff3d9..46f54d1aa 100644 --- a/NzbDrone.Core/Download/DownloadApprovedReports.cs +++ b/NzbDrone.Core/Download/DownloadApprovedReports.cs @@ -2,8 +2,7 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Core.DecisionEngine; -using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.DecisionEngine.Specifications; namespace NzbDrone.Core.Download { @@ -60,8 +59,8 @@ public List GetQualifiedReports(IEnumerable return decisions.Where(c => c.Approved && c.RemoteEpisode.Episodes.Any()) .OrderByDescending(c => c.RemoteEpisode.ParsedEpisodeInfo.Quality) .ThenBy(c => c.RemoteEpisode.Episodes.Select(e => e.EpisodeNumber).MinOrDefault()) - .ThenBy(c => c.RemoteEpisode.Report.Size.Round(200.Megabytes()) / c.RemoteEpisode.Episodes.Count) - .ThenBy(c => c.RemoteEpisode.Report.Age) + .ThenBy(c => c.RemoteEpisode.Release.Size.Round(200.Megabytes()) / c.RemoteEpisode.Episodes.Count) + .ThenBy(c => c.RemoteEpisode.Release.Age) .ToList(); } } diff --git a/NzbDrone.Core/Download/DownloadService.cs b/NzbDrone.Core/Download/DownloadService.cs index 583fdfa1f..a1c4f3bf1 100644 --- a/NzbDrone.Core/Download/DownloadService.cs +++ b/NzbDrone.Core/Download/DownloadService.cs @@ -1,5 +1,7 @@ using NLog; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Download @@ -12,21 +14,21 @@ public interface IDownloadService public class DownloadService : IDownloadService { private readonly IProvideDownloadClient _downloadClientProvider; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; public DownloadService(IProvideDownloadClient downloadClientProvider, - IMessageAggregator messageAggregator, Logger logger) + IEventAggregator eventAggregator, Logger logger) { _downloadClientProvider = downloadClientProvider; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _logger = logger; } public void DownloadReport(RemoteEpisode remoteEpisode) { - var downloadTitle = remoteEpisode.Report.Title; + var downloadTitle = remoteEpisode.Release.Title; var downloadClient = _downloadClientProvider.GetDownloadClient(); if (!downloadClient.IsConfigured) @@ -37,8 +39,8 @@ public void DownloadReport(RemoteEpisode remoteEpisode) downloadClient.DownloadNzb(remoteEpisode); - _logger.Info("Report sent to download client. {0}", downloadTitle); - _messageAggregator.PublishEvent(new EpisodeGrabbedEvent(remoteEpisode)); + _logger.ProgressInfo("Report sent to download client. {0}", downloadTitle); + _eventAggregator.PublishEvent(new EpisodeGrabbedEvent(remoteEpisode)); } } } \ No newline at end of file diff --git a/NzbDrone.Core/History/HistoryRepository.cs b/NzbDrone.Core/History/HistoryRepository.cs index ca436d153..096397248 100644 --- a/NzbDrone.Core/History/HistoryRepository.cs +++ b/NzbDrone.Core/History/HistoryRepository.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using Marr.Data.QGen; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; namespace NzbDrone.Core.History @@ -16,8 +17,8 @@ public interface IHistoryRepository : IBasicRepository public class HistoryRepository : BasicRepository, IHistoryRepository { - public HistoryRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public HistoryRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/History/HistoryService.cs b/NzbDrone.Core/History/HistoryService.cs index c0752eb2a..6053541d0 100644 --- a/NzbDrone.Core/History/HistoryService.cs +++ b/NzbDrone.Core/History/HistoryService.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; namespace NzbDrone.Core.History @@ -65,15 +65,15 @@ public void Handle(EpisodeGrabbedEvent message) EventType = HistoryEventType.Grabbed, Date = DateTime.UtcNow, Quality = message.Episode.ParsedEpisodeInfo.Quality, - SourceTitle = message.Episode.Report.Title, + SourceTitle = message.Episode.Release.Title, SeriesId = episode.SeriesId, EpisodeId = episode.Id, }; - history.Data.Add("Indexer", message.Episode.Report.Indexer); - history.Data.Add("NzbInfoUrl", message.Episode.Report.NzbInfoUrl); - history.Data.Add("ReleaseGroup", message.Episode.Report.ReleaseGroup); - history.Data.Add("Age", message.Episode.Report.Age.ToString()); + history.Data.Add("Indexer", message.Episode.Release.Indexer); + history.Data.Add("NzbInfoUrl", message.Episode.Release.InfoUrl); + history.Data.Add("ReleaseGroup", message.Episode.Release.ReleaseGroup); + history.Data.Add("Age", message.Episode.Release.Age.ToString()); _historyRepository.Insert(history); } diff --git a/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs b/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs index 11d3d4d79..20a6e8a39 100644 --- a/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs +++ b/NzbDrone.Core/IndexerSearch/Definitions/SearchCriteriaBase.cs @@ -1,17 +1,19 @@ using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using NzbDrone.Common.EnsureThat; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.IndexerSearch.Definitions { public abstract class SearchCriteriaBase { - private static readonly Regex NoneWord = new Regex(@"[\W]", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex NonWord = new Regex(@"[\W]", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex BeginningThe = new Regex(@"^the\s", RegexOptions.IgnoreCase | RegexOptions.Compiled); - public int SeriesId { get; set; } - public int SeriesTvRageId { get; set; } + public Series Series { get; set; } public string SceneTitle { get; set; } + public List Episodes { get; set; } public string QueryTitle { @@ -32,7 +34,7 @@ private static string GetQueryTitle(string title) .Replace("`", "") .Replace("'", ""); - cleanTitle = NoneWord.Replace(cleanTitle, "+"); + cleanTitle = NonWord.Replace(cleanTitle, "+"); //remove any repeating +s cleanTitle = Regex.Replace(cleanTitle, @"\+{2,}", "+"); diff --git a/NzbDrone.Core/IndexerSearch/Definitions/SingleEpisodeSearchCriteria.cs b/NzbDrone.Core/IndexerSearch/Definitions/SingleEpisodeSearchCriteria.cs index 371f66bc6..56d110079 100644 --- a/NzbDrone.Core/IndexerSearch/Definitions/SingleEpisodeSearchCriteria.cs +++ b/NzbDrone.Core/IndexerSearch/Definitions/SingleEpisodeSearchCriteria.cs @@ -7,7 +7,7 @@ public class SingleEpisodeSearchCriteria : SearchCriteriaBase public override string ToString() { - return string.Format("[{0} : S{1:00}E{2:00} ]", SceneTitle, SeasonNumber, EpisodeNumber); + return string.Format("[{0} : S{1:00}E{2:00}]", SceneTitle, SeasonNumber, EpisodeNumber); } } } \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/EpisodeSearchCommand.cs b/NzbDrone.Core/IndexerSearch/EpisodeSearchCommand.cs index b0dce2fd1..b69659b77 100644 --- a/NzbDrone.Core/IndexerSearch/EpisodeSearchCommand.cs +++ b/NzbDrone.Core/IndexerSearch/EpisodeSearchCommand.cs @@ -1,9 +1,17 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.IndexerSearch { - public class EpisodeSearchCommand : ICommand + public class EpisodeSearchCommand : Command { public int EpisodeId { get; set; } + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs b/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs index e62a99836..4f2860eb4 100644 --- a/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs +++ b/NzbDrone.Core/IndexerSearch/EpisodeSearchService.cs @@ -1,5 +1,8 @@ -using NzbDrone.Common.Messaging; +using NLog; using NzbDrone.Core.Download; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.IndexerSearch { @@ -7,17 +10,23 @@ public class EpisodeSearchService : IExecute { private readonly ISearchForNzb _nzbSearchService; private readonly IDownloadApprovedReports _downloadApprovedReports; + private readonly Logger _logger; - public EpisodeSearchService(ISearchForNzb nzbSearchService, IDownloadApprovedReports downloadApprovedReports) + public EpisodeSearchService(ISearchForNzb nzbSearchService, + IDownloadApprovedReports downloadApprovedReports, + Logger logger) { _nzbSearchService = nzbSearchService; _downloadApprovedReports = downloadApprovedReports; + _logger = logger; } public void Execute(EpisodeSearchCommand message) { var decisions = _nzbSearchService.EpisodeSearch(message.EpisodeId); - _downloadApprovedReports.DownloadApproved(decisions); + var downloaded = _downloadApprovedReports.DownloadApproved(decisions); + + _logger.ProgressInfo("Episode search completed. {0} reports downloaded.", downloaded.Count); } } } diff --git a/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index 7386341d2..5770687b8 100644 --- a/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -5,8 +5,10 @@ using NLog; using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.DecisionEngine; +using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Indexers; +using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; using System.Linq; @@ -59,7 +61,7 @@ public List EpisodeSearch(int episodeId) throw new InvalidOperationException("Daily episode is missing AirDate. Try to refresh series info."); } - return SearchDaily(series, DateTime.ParseExact(episode.AirDate, Episode.AIR_DATE_FORMAT, CultureInfo.InvariantCulture)); + return SearchDaily(series, episode); } return SearchSingle(series, episode); @@ -67,7 +69,7 @@ public List EpisodeSearch(int episodeId) private List SearchSingle(Series series, Episode episode) { - var searchSpec = Get(series, episode.SeasonNumber); + var searchSpec = Get(series, new List{episode}); if (series.UseSceneNumbering) { @@ -92,9 +94,10 @@ private List SearchSingle(Series series, Episode episode) return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); } - private List SearchDaily(Series series, DateTime airDate) + private List SearchDaily(Series series, Episode episode) { - var searchSpec = Get(series); + var airDate = DateTime.ParseExact(episode.AirDate, Episode.AIR_DATE_FORMAT, CultureInfo.InvariantCulture); + var searchSpec = Get(series, new List{ episode }); searchSpec.Airtime = airDate; return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); @@ -103,20 +106,21 @@ private List SearchDaily(Series series, DateTime airDate) public List SeasonSearch(int seriesId, int seasonNumber) { var series = _seriesService.GetSeries(seriesId); + var episodes = _episodeService.GetEpisodesBySeason(seriesId, seasonNumber); - var searchSpec = Get(series, seasonNumber); + var searchSpec = Get(series, episodes); searchSpec.SeasonNumber = seasonNumber; return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); } - private TSpec Get(Series series, int seasonNumber = -1) where TSpec : SearchCriteriaBase, new() + private TSpec Get(Series series, List episodes) where TSpec : SearchCriteriaBase, new() { var spec = new TSpec(); - spec.SeriesId = series.Id; - spec.SeriesTvRageId = series.TvRageId; + spec.Series = series; spec.SceneTitle = _sceneMapping.GetSceneName(series.TvdbId); + spec.Episodes = episodes; if (string.IsNullOrWhiteSpace(spec.SceneTitle)) { @@ -126,11 +130,12 @@ public List SeasonSearch(int seriesId, int seasonNumber) return spec; } - private List Dispatch(Func> searchAction, SearchCriteriaBase criteriaBase) + private List Dispatch(Func> searchAction, SearchCriteriaBase criteriaBase) { var indexers = _indexerService.GetAvailableIndexers().ToList(); - var reports = new List(); + var reports = new List(); + _logger.ProgressInfo("Searching {0} indexers for {1}", indexers.Count, criteriaBase); var taskList = new List(); var taskFactory = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None); @@ -159,9 +164,9 @@ private List Dispatch(Func> Task.WaitAll(taskList.ToArray()); - _logger.Debug("Total of {0} reports were found for {1} in {2} indexers", reports.Count, criteriaBase, indexers.Count); + _logger.Debug("Total of {0} reports were found for {1} from {2} indexers", reports.Count, criteriaBase, indexers.Count); return _makeDownloadDecision.GetSearchDecision(reports, criteriaBase).ToList(); } } -} \ No newline at end of file +} diff --git a/NzbDrone.Core/IndexerSearch/SeasonSearchCommand.cs b/NzbDrone.Core/IndexerSearch/SeasonSearchCommand.cs index cf9a8ba3e..56be0e0d9 100644 --- a/NzbDrone.Core/IndexerSearch/SeasonSearchCommand.cs +++ b/NzbDrone.Core/IndexerSearch/SeasonSearchCommand.cs @@ -1,10 +1,18 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.IndexerSearch { - public class SeasonSearchCommand : ICommand + public class SeasonSearchCommand : Command { public int SeriesId { get; set; } public int SeasonNumber { get; set; } + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs b/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs index 7572b5b6d..0e4b67eab 100644 --- a/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs +++ b/NzbDrone.Core/IndexerSearch/SeasonSearchService.cs @@ -1,5 +1,8 @@ -using NzbDrone.Common.Messaging; +using NLog; using NzbDrone.Core.Download; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.IndexerSearch { @@ -7,17 +10,23 @@ public class SeasonSearchService : IExecute { private readonly ISearchForNzb _nzbSearchService; private readonly IDownloadApprovedReports _downloadApprovedReports; + private readonly Logger _logger; - public SeasonSearchService(ISearchForNzb nzbSearchService, IDownloadApprovedReports downloadApprovedReports) + public SeasonSearchService(ISearchForNzb nzbSearchService, + IDownloadApprovedReports downloadApprovedReports, + Logger logger) { _nzbSearchService = nzbSearchService; _downloadApprovedReports = downloadApprovedReports; + _logger = logger; } public void Execute(SeasonSearchCommand message) { var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, message.SeasonNumber); - _downloadApprovedReports.DownloadApproved(decisions); + var downloaded = _downloadApprovedReports.DownloadApproved(decisions); + + _logger.ProgressInfo("Season search completed. {0} reports downloaded.", downloaded.Count); } } } diff --git a/NzbDrone.Core/IndexerSearch/SeriesSearchCommand.cs b/NzbDrone.Core/IndexerSearch/SeriesSearchCommand.cs index 4e309fde8..36b75f316 100644 --- a/NzbDrone.Core/IndexerSearch/SeriesSearchCommand.cs +++ b/NzbDrone.Core/IndexerSearch/SeriesSearchCommand.cs @@ -1,9 +1,17 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.IndexerSearch { - public class SeriesSearchCommand : ICommand + public class SeriesSearchCommand : Command { public int SeriesId { get; set; } + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs b/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs index 89dd7c6c2..c87275648 100644 --- a/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs +++ b/NzbDrone.Core/IndexerSearch/SeriesSearchService.cs @@ -1,37 +1,44 @@ using System.Linq; -using NzbDrone.Common.Messaging; +using NLog; using NzbDrone.Core.Download; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Tv; namespace NzbDrone.Core.IndexerSearch { public class SeriesSearchService : IExecute { - private readonly ISeasonService _seasonService; + private readonly ISeriesService _seriesService; private readonly ISearchForNzb _nzbSearchService; private readonly IDownloadApprovedReports _downloadApprovedReports; + private readonly Logger _logger; - public SeriesSearchService(ISeasonService seasonService, + public SeriesSearchService(ISeriesService seriesService, ISearchForNzb nzbSearchService, - IDownloadApprovedReports downloadApprovedReports) + IDownloadApprovedReports downloadApprovedReports, + Logger logger) { - _seasonService = seasonService; + _seriesService = seriesService; _nzbSearchService = nzbSearchService; _downloadApprovedReports = downloadApprovedReports; + _logger = logger; } public void Execute(SeriesSearchCommand message) { - var seasons = _seasonService.GetSeasonsBySeries(message.SeriesId) - .Where(s => s.SeasonNumber > 0) - .OrderBy(s => s.SeasonNumber) - .ToList(); + var series = _seriesService.GetSeries(message.SeriesId); - foreach (var season in seasons) + var downloadedCount = 0; + + foreach (var season in series.Seasons) { var decisions = _nzbSearchService.SeasonSearch(message.SeriesId, season.SeasonNumber); - _downloadApprovedReports.DownloadApproved(decisions); + downloadedCount += _downloadApprovedReports.DownloadApproved(decisions).Count; } + + _logger.ProgressInfo("Series search completed. {0} reports downloaded.", downloadedCount); } } } diff --git a/NzbDrone.Core/Indexers/BasicTorrentRssParser.cs b/NzbDrone.Core/Indexers/BasicTorrentRssParser.cs new file mode 100644 index 000000000..d3c8ab15a --- /dev/null +++ b/NzbDrone.Core/Indexers/BasicTorrentRssParser.cs @@ -0,0 +1,48 @@ +using System; +using System.Xml.Linq; +using NLog; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Indexers +{ + public class BasicTorrentRssParser : RssParserBase + { + protected override ReleaseInfo CreateNewReleaseInfo() + { + return new TorrentInfo(); + } + + protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult) + { + var torrentInfo = (TorrentInfo)currentResult; + + torrentInfo.MagnetUrl = MagnetUrl(item); + torrentInfo.InfoHash = InfoHash(item); + + return torrentInfo; + } + + protected override long GetSize(XElement item) + { + var elementLength = GetTorrentElement(item).Element("contentLength"); + return Convert.ToInt64(elementLength.Value); + } + + protected virtual string MagnetUrl(XElement item) + { + var elementLength = GetTorrentElement(item).Element("magnetURI"); + return elementLength.Value; + } + + protected virtual string InfoHash(XElement item) + { + var elementLength = GetTorrentElement(item).Element("infoHash"); + return elementLength.Value; + } + + private static XElement GetTorrentElement(XElement item) + { + return item.Element("torrent"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/DownloadProtocols.cs b/NzbDrone.Core/Indexers/DownloadProtocols.cs new file mode 100644 index 000000000..4fff5e07d --- /dev/null +++ b/NzbDrone.Core/Indexers/DownloadProtocols.cs @@ -0,0 +1,8 @@ +namespace NzbDrone.Core.Indexers +{ + public enum DownloadProtocols + { + Nzb = 0, + Torrent =1 + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Eztv/Eztv.cs b/NzbDrone.Core/Indexers/Eztv/Eztv.cs new file mode 100644 index 000000000..541f3a078 --- /dev/null +++ b/NzbDrone.Core/Indexers/Eztv/Eztv.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Indexers.Eztv +{ + public class Eztv : IndexerBase + { + public override string Name + { + get { return "Eztv"; } + } + + public override IndexerKind Kind + { + get + { + return IndexerKind.Torrent; + } + } + + public override bool EnableByDefault + { + get { return false; } + } + + public override IParseFeed Parser + { + get + { + return new BasicTorrentRssParser(); + } + } + + public override IEnumerable RecentFeed + { + get + { + return new[] + { + "http://www.ezrss.it/feed/" + }; + } + } + + public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber) + { + yield return string.Format("http://www.ezrss.it/search/index.php?show_name={0}&season={1}&episode={2}&mode=rss", seriesTitle, seasonNumber, episodeNumber); + } + + public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int offset) + { + yield return string.Format("http://www.ezrss.it/search/index.php?show_name={0}&season={1}&mode=rss", seriesTitle, seasonNumber); + + } + + public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date) + { + //EZTV doesn't support searching based on actual epidose airdate. they only support release date. + return new string[0]; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/FetchAndParseRssService.cs b/NzbDrone.Core/Indexers/FetchAndParseRssService.cs index 9ee704910..c9633c8d0 100644 --- a/NzbDrone.Core/Indexers/FetchAndParseRssService.cs +++ b/NzbDrone.Core/Indexers/FetchAndParseRssService.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.Indexers { public interface IFetchAndParseRss { - List Fetch(); + List Fetch(); } public class FetchAndParseRssService : IFetchAndParseRss @@ -25,9 +25,9 @@ public FetchAndParseRssService(IIndexerService indexerService, IFetchFeedFromInd _logger = logger; } - public List Fetch() + public List Fetch() { - var result = new List(); + var result = new List(); var indexers = _indexerService.GetAvailableIndexers().ToList(); diff --git a/NzbDrone.Core/Indexers/IIndexer.cs b/NzbDrone.Core/Indexers/IIndexer.cs index c8e8e950f..a681ae3df 100644 --- a/NzbDrone.Core/Indexers/IIndexer.cs +++ b/NzbDrone.Core/Indexers/IIndexer.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.Indexers public interface IIndexer { string Name { get; } - + bool EnableByDefault { get; } IEnumerable DefaultDefinitions { get; } @@ -16,6 +16,7 @@ public interface IIndexer IEnumerable RecentFeed { get; } IParseFeed Parser { get; } + IndexerKind Kind { get; } IEnumerable GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber); IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date); diff --git a/NzbDrone.Core/Indexers/IParseFeed.cs b/NzbDrone.Core/Indexers/IParseFeed.cs new file mode 100644 index 000000000..a101f0a07 --- /dev/null +++ b/NzbDrone.Core/Indexers/IParseFeed.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.IO; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Indexers +{ + public interface IParseFeed + { + IEnumerable Process(string source, string url); + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/IndexerBase.cs b/NzbDrone.Core/Indexers/IndexerBase.cs index 4b2cd9f99..6f544a2be 100644 --- a/NzbDrone.Core/Indexers/IndexerBase.cs +++ b/NzbDrone.Core/Indexers/IndexerBase.cs @@ -7,6 +7,8 @@ public abstract class IndexerBase : IIndexer { public abstract string Name { get; } + public abstract IndexerKind Kind { get; } + public virtual bool EnableByDefault { get { return true; } } public IndexerDefinition InstanceDefinition { get; set; } @@ -25,17 +27,17 @@ public virtual IEnumerable DefaultDefinitions } } - public virtual IParseFeed Parser - { - get - { - return new BasicRssParser(); - } - } + public virtual IParseFeed Parser { get; private set; } public abstract IEnumerable RecentFeed { get; } public abstract IEnumerable GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber); public abstract IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date); public abstract IEnumerable GetSeasonSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int offset); } + + public enum IndexerKind + { + Usenet, + Torrent + } } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/IndexerFetchService.cs b/NzbDrone.Core/Indexers/IndexerFetchService.cs index 2e585f862..9814adc0d 100644 --- a/NzbDrone.Core/Indexers/IndexerFetchService.cs +++ b/NzbDrone.Core/Indexers/IndexerFetchService.cs @@ -11,11 +11,11 @@ namespace NzbDrone.Core.Indexers { public interface IFetchFeedFromIndexers { - IList FetchRss(IIndexer indexer); + IList FetchRss(IIndexer indexer); - IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria); - IList Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria); - IList Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria); + IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria); + IList Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria); + IList Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria); } public class FetchFeedService : IFetchFeedFromIndexers @@ -31,7 +31,7 @@ public FetchFeedService(IHttpProvider httpProvider, Logger logger) } - public virtual IList FetchRss(IIndexer indexer) + public virtual IList FetchRss(IIndexer indexer) { _logger.Debug("Fetching feeds from " + indexer.Name); @@ -42,23 +42,23 @@ public virtual IList FetchRss(IIndexer indexer) return result; } - public IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria) + public IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria) { _logger.Debug("Searching for {0}", searchCriteria); - var result = Fetch(indexer, searchCriteria, 0).DistinctBy(c => c.NzbUrl).ToList(); + var result = Fetch(indexer, searchCriteria, 0).DistinctBy(c => c.DownloadUrl).ToList(); - _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchCriteria, result.Count); + _logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count); return result; } - private IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria, int offset) + private IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCriteria, int offset) { _logger.Debug("Searching for {0} offset: {1}", searchCriteria, offset); - var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeriesTvRageId, searchCriteria.SeasonNumber, offset); + var searchUrls = indexer.GetSeasonSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.SeasonNumber, offset); var result = Fetch(indexer, searchUrls); @@ -72,34 +72,32 @@ private IList Fetch(IIndexer indexer, SeasonSearchCriteria searchCri return result; } - public IList Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria) + public IList Fetch(IIndexer indexer, SingleEpisodeSearchCriteria searchCriteria) { _logger.Debug("Searching for {0}", searchCriteria); - var searchUrls = indexer.GetEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeriesTvRageId, searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber); + var searchUrls = indexer.GetEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.SeasonNumber, searchCriteria.EpisodeNumber); var result = Fetch(indexer, searchUrls); - _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchCriteria, result.Count); + _logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count); return result; - } - - public IList Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria) + public IList Fetch(IIndexer indexer, DailyEpisodeSearchCriteria searchCriteria) { _logger.Debug("Searching for {0}", searchCriteria); - var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.SeriesTvRageId, searchCriteria.Airtime); + var searchUrls = indexer.GetDailyEpisodeSearchUrls(searchCriteria.QueryTitle, searchCriteria.Series.TvRageId, searchCriteria.Airtime); var result = Fetch(indexer, searchUrls); - _logger.Info("Finished searching {0} on {1}. Found {2}", indexer.Name, searchCriteria, result.Count); + _logger.Info("Finished searching {0} for {1}. Found {2}", indexer.Name, searchCriteria, result.Count); return result; } - private List Fetch(IIndexer indexer, IEnumerable urls) + private List Fetch(IIndexer indexer, IEnumerable urls) { - var result = new List(); + var result = new List(); foreach (var url in urls) { @@ -140,4 +138,4 @@ private List Fetch(IIndexer indexer, IEnumerable urls) return result; } } -} \ No newline at end of file +} diff --git a/NzbDrone.Core/Indexers/IndexerRepository.cs b/NzbDrone.Core/Indexers/IndexerRepository.cs index 678c26fd6..fd66a3910 100644 --- a/NzbDrone.Core/Indexers/IndexerRepository.cs +++ b/NzbDrone.Core/Indexers/IndexerRepository.cs @@ -1,7 +1,9 @@ using System; using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Indexers { @@ -13,8 +15,8 @@ public interface IIndexerRepository : IBasicRepository public class IndexerRepository : BasicRepository, IIndexerRepository { - public IndexerRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public IndexerRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/Indexers/IndexerService.cs b/NzbDrone.Core/Indexers/IndexerService.cs index 168835ffa..d9fc3ba27 100644 --- a/NzbDrone.Core/Indexers/IndexerService.cs +++ b/NzbDrone.Core/Indexers/IndexerService.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Common.Serializer; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using Omu.ValueInjecter; namespace NzbDrone.Core.Indexers @@ -35,15 +37,26 @@ public interface IIndexerService public class IndexerService : IIndexerService, IHandle { private readonly IIndexerRepository _indexerRepository; + private readonly IConfigFileProvider _configFileProvider; private readonly Logger _logger; private readonly List _indexers; - public IndexerService(IIndexerRepository indexerRepository, IEnumerable indexers, Logger logger) + public IndexerService(IIndexerRepository indexerRepository, IEnumerable indexers, IConfigFileProvider configFileProvider, Logger logger) { _indexerRepository = indexerRepository; + _configFileProvider = configFileProvider; _logger = logger; - _indexers = indexers.ToList(); + + + if (!configFileProvider.Torrent) + { + _indexers = indexers.Where(c => c.Kind != IndexerKind.Torrent).ToList(); + } + else + { + _indexers = indexers.ToList(); + } } public List All() @@ -153,10 +166,16 @@ public void Handle(ApplicationStartedEvent message) RemoveMissingImplementations(); - if (!All().Any()) + 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()) { - var definitions = _indexers.SelectMany(indexer => indexer.DefaultDefinitions); - _indexerRepository.InsertMany(definitions.ToList()); + _indexerRepository.InsertMany(newIndexers); } } diff --git a/NzbDrone.Core/Indexers/Newznab/Newznab.cs b/NzbDrone.Core/Indexers/Newznab/Newznab.cs index 51c49e26d..4341c22f7 100644 --- a/NzbDrone.Core/Indexers/Newznab/Newznab.cs +++ b/NzbDrone.Core/Indexers/Newznab/Newznab.cs @@ -123,6 +123,15 @@ public override string Name } } + public override IndexerKind Kind + { + get + { + return IndexerKind.Usenet; + } + } + + private static string NewsnabifyTitle(string title) { return title.Replace("+", "%20"); diff --git a/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs b/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs index 1e1376569..06c45ea1d 100644 --- a/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs +++ b/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs @@ -1,36 +1,38 @@ using System; -using System.Drawing; using System.Linq; using System.Xml.Linq; -using NLog; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Newznab { - public class NewznabParser : BasicRssParser + public class NewznabParser : RssParserBase { - private static readonly XNamespace NewznabNamespace = "http://www.newznab.com/DTD/2010/feeds/attributes/"; - protected override string GetNzbInfoUrl(XElement item) { return item.Comments().Replace("#comments", ""); } - protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) + protected override long GetSize(XElement item) + { + var attributes = item.Elements("attr").ToList(); + var sizeElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("size", StringComparison.CurrentCultureIgnoreCase)); + + if (sizeElement == null) + { + + } + + return Convert.ToInt64(sizeElement.Attribute("value").Value); + } + + protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult) { if (currentResult != null) { - var attributes = item.Elements(NewznabNamespace + "attr").ToList(); - var sizeElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("size", StringComparison.CurrentCultureIgnoreCase)); + var attributes = item.Elements("attr").ToList(); + var rageIdElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("rageid", StringComparison.CurrentCultureIgnoreCase)); - if (sizeElement == null) - { - throw new SizeParsingException("Unable to parse size from: {0} [{1}]", currentResult.Title, currentResult.Indexer); - } - - currentResult.Size = Convert.ToInt64(sizeElement.Attribute("value").Value); - if (rageIdElement != null) { int tvRageId; diff --git a/NzbDrone.Core/Indexers/Newznab/SizeParsingException.cs b/NzbDrone.Core/Indexers/Newznab/SizeParsingException.cs index 84c96535b..549e52f92 100644 --- a/NzbDrone.Core/Indexers/Newznab/SizeParsingException.cs +++ b/NzbDrone.Core/Indexers/Newznab/SizeParsingException.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NzbDrone.Common.Exceptions; +using NzbDrone.Common.Exceptions; namespace NzbDrone.Core.Indexers.Newznab { diff --git a/NzbDrone.Core/Indexers/NzbClub/NzbClub.cs b/NzbDrone.Core/Indexers/NzbClub/NzbClub.cs deleted file mode 100644 index cb2bc8a5f..000000000 --- a/NzbDrone.Core/Indexers/NzbClub/NzbClub.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace NzbDrone.Core.Indexers.NzbClub -{ - public class NzbClub : IndexerBase - { - public override string Name - { - get { return "NzbClub"; } - } - - public override bool EnableByDefault - { - get { return false; } - } - - public override IParseFeed Parser - { - get - { - return new NzbClubParser(); - } - } - - public override IEnumerable RecentFeed - { - get - { - return new[] - { - String.Format("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=102952&st=1&ns=1&q=%23a.b.teevee"), - String.Format("http://www.nzbclub.com/nzbfeed.aspx?ig=2&gid=5542&st=1&ns=1&q=") - }; - } - } - - public override IEnumerable GetEpisodeSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int episodeNumber) - { - var searchUrls = new List(); - - foreach (var url in RecentFeed) - { - searchUrls.Add(String.Format("{0}+{1}+s{2:00}e{3:00}", url, seriesTitle, seasonNumber, episodeNumber)); - } - - return searchUrls; - } - - public override IEnumerable GetSeasonSearchUrls(string seriesTitle, int tvRageId, int seasonNumber, int offset) - { - var searchUrls = new List(); - - foreach (var url in RecentFeed) - { - searchUrls.Add(String.Format("{0}+{1}+s{2:00}", url, seriesTitle, seasonNumber)); - } - - return searchUrls; - } - - public override IEnumerable GetDailyEpisodeSearchUrls(string seriesTitle, int tvRageId, DateTime date) - { - var searchUrls = new List(); - - foreach (var url in RecentFeed) - { - searchUrls.Add(String.Format("{0}+{1}+{2:yyyy MM dd}", url, seriesTitle, date)); - } - - return searchUrls; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs b/NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs deleted file mode 100644 index 9c17b0ffa..000000000 --- a/NzbDrone.Core/Indexers/NzbClub/NzbClubParser.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using System.Xml.Linq; -using NLog; -using NzbDrone.Core.Parser.Model; - -namespace NzbDrone.Core.Indexers.NzbClub -{ - public class NzbClubParser : BasicRssParser - { - - private static readonly Regex SizeRegex = new Regex(@"(?:Size:)\s(?\d+.\d+\s[g|m]i?[b])", RegexOptions.Compiled | RegexOptions.IgnoreCase); - private readonly Logger logger; - - public NzbClubParser() - { - logger = LogManager.GetCurrentClassLogger(); - } - - - protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) - { - if (currentResult != null) - { - var match = SizeRegex.Match(item.Description()); - - if (match.Success && match.Groups["size"].Success) - { - currentResult.Size = GetReportSize(match.Groups["size"].Value); - } - else - { - logger.Warn("Couldn't parse size from {0}", item.Description()); - } - } - - return currentResult; - } - - protected override string GetTitle(XElement item) - { - var title = ParseHeader(item.Title()); - - if (String.IsNullOrWhiteSpace(title)) - return item.Title(); - - return title; - } - - protected override string GetNzbInfoUrl(XElement item) - { - return item.Links().First(); - } - - protected override string GetNzbUrl(XElement item) - { - var enclosure = item.Element("enclosure"); - - return enclosure.Attribute("url").Value; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs b/NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs index 8d8e9b4a9..15f6dbbe5 100644 --- a/NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs +++ b/NzbDrone.Core/Indexers/Omgwtfnzbs/Omgwtfnzbs.cs @@ -10,6 +10,14 @@ public override string Name get { return "omgwtfnzbs"; } } + public override IndexerKind Kind + { + get + { + return IndexerKind.Usenet; + } + } + public override IParseFeed Parser { get diff --git a/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs b/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs index a2369f81a..afaeb3890 100644 --- a/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs +++ b/NzbDrone.Core/Indexers/Omgwtfnzbs/OmgwtfnzbsParser.cs @@ -1,11 +1,10 @@ using System; using System.Text.RegularExpressions; using System.Xml.Linq; -using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Omgwtfnzbs { - public class OmgwtfnzbsParser : BasicRssParser + public class OmgwtfnzbsParser : RssParserBase { protected override string GetNzbInfoUrl(XElement item) { @@ -21,15 +20,10 @@ protected override string GetNzbInfoUrl(XElement item) return String.Empty; } - protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) + protected override long GetSize(XElement item) { - if (currentResult != null) - { - var sizeString = Regex.Match(item.Description(), @"(?:Size:\<\/b\>\s\d+\.)\d{1,2}\s\w{2}(?:\
)", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; - currentResult.Size = GetReportSize(sizeString); - } - - return currentResult; + var sizeString = Regex.Match(item.Description(), @"(?:Size:\<\/b\>\s\d+\.)\d{1,2}\s\w{2}(?:\
)", RegexOptions.IgnoreCase | RegexOptions.Compiled).Value; + return ParseSize(sizeString); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/BasicRssParser.cs b/NzbDrone.Core/Indexers/RssParserBase.cs similarity index 63% rename from NzbDrone.Core/Indexers/BasicRssParser.cs rename to NzbDrone.Core/Indexers/RssParserBase.cs index 84c61f296..485bc0140 100644 --- a/NzbDrone.Core/Indexers/BasicRssParser.cs +++ b/NzbDrone.Core/Indexers/RssParserBase.cs @@ -7,43 +7,45 @@ using System.Xml; using System.Xml.Linq; using NLog; +using NzbDrone.Common.Instrumentation; +using NzbDrone.Core.Indexers.Newznab; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers { - public interface IParseFeed - { - IEnumerable Process(string xml, string url); - } - - public class BasicRssParser : IParseFeed + public abstract class RssParserBase : IParseFeed { private readonly Logger _logger; - public BasicRssParser() + protected virtual ReleaseInfo CreateNewReleaseInfo() { - _logger = LogManager.GetCurrentClassLogger(); + return new ReleaseInfo(); } - public IEnumerable Process(string xml, string url) + protected RssParserBase() + { + _logger = NzbDroneLogger.GetLogger(this); + } + + public IEnumerable Process(string xml, string url) { using (var xmlTextReader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { ProhibitDtd = false, IgnoreComments = true })) { + var document = XDocument.Load(xmlTextReader); var items = document.Descendants("item"); - var result = new List(); + var result = new List(); foreach (var item in items) { try { - var reportInfo = ParseFeedItem(item); + var reportInfo = ParseFeedItem(item.StripNameSpace(), url); if (reportInfo != null) { - reportInfo.NzbUrl = GetNzbUrl(item); - reportInfo.NzbInfoUrl = GetNzbInfoUrl(item); - + reportInfo.DownloadUrl = GetNzbUrl(item); + reportInfo.InfoUrl = GetNzbInfoUrl(item); result.Add(reportInfo); } } @@ -58,6 +60,31 @@ public IEnumerable Process(string xml, string url) } } + private ReleaseInfo ParseFeedItem(XElement item, string url) + { + var title = GetTitle(item); + + var reportInfo = CreateNewReleaseInfo(); + + reportInfo.Title = title; + reportInfo.PublishDate = item.PublishDate(); + reportInfo.ReleaseGroup = ParseReleaseGroup(title); + reportInfo.DownloadUrl = GetNzbUrl(item); + reportInfo.InfoUrl = GetNzbInfoUrl(item); + + try + { + reportInfo.Size = GetSize(item); + } + catch (Exception) + { + throw new SizeParsingException("Unable to parse size from: {0} [{1}]", reportInfo.Title, url); + } + + _logger.Trace("Parsed: {0} from: {1}", reportInfo, item.Title()); + + return PostProcessor(item, reportInfo); + } protected virtual string GetTitle(XElement item) { @@ -74,25 +101,14 @@ protected virtual string GetNzbInfoUrl(XElement item) return String.Empty; } - protected virtual ReportInfo PostProcessor(XElement item, ReportInfo currentResult) + protected abstract long GetSize(XElement item); + + protected virtual ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult) { return currentResult; } - private ReportInfo ParseFeedItem(XElement item) - { - var title = GetTitle(item); - var reportInfo = new ReportInfo(); - - reportInfo.Title = title; - reportInfo.Age = DateTime.Now.Date.Subtract(item.PublishDate().Date).Days; - reportInfo.ReleaseGroup = ParseReleaseGroup(title); - - _logger.Trace("Parsed: {0} from: {1}", reportInfo, item.Title()); - - return PostProcessor(item, reportInfo); - } public static string ParseReleaseGroup(string title) { @@ -110,39 +126,15 @@ public static string ParseReleaseGroup(string title) if (@group.Length == title.Length) return String.Empty; - return @group; - } - - private static readonly Regex[] HeaderRegex = new[] - { - new Regex(@"(?:\[.+\]\-\[.+\]\-\[.+\]\-\[)(?.+)(?:\]\-.+)", - RegexOptions.IgnoreCase), - - new Regex(@"(?:\[.+\]\W+\[.+\]\W+\[.+\]\W+\"")(?.+)(?:\"".+)", - RegexOptions.IgnoreCase), - - new Regex(@"(?:\[)(?.+)(?:\]\-.+)", - RegexOptions.IgnoreCase), - }; - - public static string ParseHeader(string header) - { - foreach (var regex in HeaderRegex) - { - var match = regex.Matches(header); - - if (match.Count != 0) - return match[0].Groups["nzbTitle"].Value.Trim(); - } - - return header; + return @group.Trim('-', ' ', '[', ']'); } private static readonly Regex ReportSizeRegex = new Regex(@"(?\d+\.\d{1,2}|\d+\,\d+\.\d{1,2}|\d+)\W?(?GB|MB|GiB|MiB)", RegexOptions.IgnoreCase | RegexOptions.Compiled); - public static long GetReportSize(string sizeString) + + public static long ParseSize(string sizeString) { var match = ReportSizeRegex.Matches(sizeString); @@ -176,4 +168,4 @@ private static long ConvertToBytes(double value, int power) return Convert.ToInt64(result); } } -} \ No newline at end of file +} diff --git a/NzbDrone.Core/Indexers/RssSyncCommand.cs b/NzbDrone.Core/Indexers/RssSyncCommand.cs index 04a7a123b..56b4532bf 100644 --- a/NzbDrone.Core/Indexers/RssSyncCommand.cs +++ b/NzbDrone.Core/Indexers/RssSyncCommand.cs @@ -1,9 +1,17 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Indexers { - public class RssSyncCommand : ICommand + public class RssSyncCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + } } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/RssSyncService.cs b/NzbDrone.Core/Indexers/RssSyncService.cs index baf414fac..5f9be30a9 100644 --- a/NzbDrone.Core/Indexers/RssSyncService.cs +++ b/NzbDrone.Core/Indexers/RssSyncService.cs @@ -1,8 +1,10 @@ using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Download; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Indexers { @@ -32,13 +34,13 @@ public RssSyncService(IFetchAndParseRss rssFetcherAndParser, public void Sync() { - _logger.Info("Starting RSS Sync"); + _logger.ProgressInfo("Starting RSS Sync"); var reports = _rssFetcherAndParser.Fetch(); var decisions = _downloadDecisionMaker.GetRssDecision(reports); - var qualifiedReports = _downloadApprovedReports.DownloadApproved(decisions); + var downloaded = _downloadApprovedReports.DownloadApproved(decisions); - _logger.Info("RSS Sync Completed. Reports found: {0}, Reports downloaded: {1}", reports.Count, qualifiedReports.Count()); + _logger.ProgressInfo("RSS Sync Completed. Reports found: {0}, Reports downloaded: {1}", reports.Count, downloaded.Count()); } public void Execute(RssSyncCommand message) diff --git a/NzbDrone.Core/Indexers/Wombles/Wombles.cs b/NzbDrone.Core/Indexers/Wombles/Wombles.cs index adff820ec..95cd559de 100644 --- a/NzbDrone.Core/Indexers/Wombles/Wombles.cs +++ b/NzbDrone.Core/Indexers/Wombles/Wombles.cs @@ -10,6 +10,14 @@ public override string Name get { return "WomblesIndex"; } } + public override IndexerKind Kind + { + get + { + return IndexerKind.Usenet; + } + } + public override IParseFeed Parser { get diff --git a/NzbDrone.Core/Indexers/Wombles/WomblesParser.cs b/NzbDrone.Core/Indexers/Wombles/WomblesParser.cs index de2aa0a99..2b67dfe97 100644 --- a/NzbDrone.Core/Indexers/Wombles/WomblesParser.cs +++ b/NzbDrone.Core/Indexers/Wombles/WomblesParser.cs @@ -1,23 +1,17 @@ using System.Xml.Linq; -using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Indexers.Wombles { - public class WomblesParser : BasicRssParser + public class WomblesParser : RssParserBase { protected override string GetNzbInfoUrl(XElement item) { return null; } - protected override ReportInfo PostProcessor(XElement item, ReportInfo currentResult) + protected override long GetSize(XElement item) { - if (currentResult != null) - { - currentResult.Size = 0; - } - - return currentResult; + return 0; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Indexers/XElementExtensions.cs b/NzbDrone.Core/Indexers/XElementExtensions.cs index 8460c34d6..254c9ae6f 100644 --- a/NzbDrone.Core/Indexers/XElementExtensions.cs +++ b/NzbDrone.Core/Indexers/XElementExtensions.cs @@ -5,13 +5,13 @@ using System.Text.RegularExpressions; using System.Xml.Linq; using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Core.Indexers { public static class XElementExtensions { - - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); private static readonly Regex RemoveTimeZoneRegex = new Regex(@"\s[A-Z]{2,4}$", RegexOptions.Compiled); @@ -20,6 +20,21 @@ public static string Title(this XElement item) return item.TryGetValue("title", "Unknown"); } + public static XElement StripNameSpace(this XElement root) + { + var res = new XElement( + root.Name.LocalName, + root.HasElements ? + root.Elements().Select(StripNameSpace) : + (object)root.Value + ); + + res.ReplaceAttributes( + root.Attributes().Where(attr => (!attr.IsNamespaceDeclaration))); + + return res; + } + public static DateTime PublishDate(this XElement item) { string dateString = item.TryGetValue("pubDate"); @@ -32,7 +47,7 @@ public static DateTime PublishDate(this XElement item) dateString = RemoveTimeZoneRegex.Replace(dateString, ""); result = DateTime.Parse(dateString, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal); } - return result.ToUniversalTime(); + return result.ToUniversalTime().Date; } catch (FormatException e) { @@ -58,6 +73,11 @@ public static string Comments(this XElement item) return item.TryGetValue("comments"); } + public static long Length(this XElement item) + { + return long.Parse(item.TryGetValue("length")); + } + private static string TryGetValue(this XElement item, string elementName, string defaultValue = "") { var element = item.Element(elementName); diff --git a/NzbDrone.Core/Instrumentation/Commands/ClearLogCommand.cs b/NzbDrone.Core/Instrumentation/Commands/ClearLogCommand.cs index 19776e76d..986331002 100644 --- a/NzbDrone.Core/Instrumentation/Commands/ClearLogCommand.cs +++ b/NzbDrone.Core/Instrumentation/Commands/ClearLogCommand.cs @@ -1,8 +1,15 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Instrumentation.Commands { - public class ClearLogCommand : ICommand + public class ClearLogCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Instrumentation/Commands/DeleteLogFilesCommand.cs b/NzbDrone.Core/Instrumentation/Commands/DeleteLogFilesCommand.cs index 5d3228afb..1a331247e 100644 --- a/NzbDrone.Core/Instrumentation/Commands/DeleteLogFilesCommand.cs +++ b/NzbDrone.Core/Instrumentation/Commands/DeleteLogFilesCommand.cs @@ -1,8 +1,15 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Instrumentation.Commands { - public class DeleteLogFilesCommand : ICommand + public class DeleteLogFilesCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Instrumentation/Commands/TrimLogCommand.cs b/NzbDrone.Core/Instrumentation/Commands/TrimLogCommand.cs index c00b27020..c85427fc8 100644 --- a/NzbDrone.Core/Instrumentation/Commands/TrimLogCommand.cs +++ b/NzbDrone.Core/Instrumentation/Commands/TrimLogCommand.cs @@ -1,8 +1,8 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Instrumentation.Commands { - public class TrimLogCommand : ICommand + public class TrimLogCommand : Command { } } \ No newline at end of file diff --git a/NzbDrone.Core/Instrumentation/DatabaseTarget.cs b/NzbDrone.Core/Instrumentation/DatabaseTarget.cs index 779643240..673f4f9d6 100644 --- a/NzbDrone.Core/Instrumentation/DatabaseTarget.cs +++ b/NzbDrone.Core/Instrumentation/DatabaseTarget.cs @@ -3,8 +3,9 @@ using NLog; using NLog.Layouts; using NLog.Targets; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Instrumentation { diff --git a/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs b/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs index 5c98646ad..3a12cd057 100644 --- a/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs +++ b/NzbDrone.Core/Instrumentation/DeleteLogFilesService.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; +using System.IO; using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Instrumentation.Commands; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Instrumentation { diff --git a/NzbDrone.Core/Instrumentation/LogRepository.cs b/NzbDrone.Core/Instrumentation/LogRepository.cs index 0a00aa27b..8259b5cf7 100644 --- a/NzbDrone.Core/Instrumentation/LogRepository.cs +++ b/NzbDrone.Core/Instrumentation/LogRepository.cs @@ -1,6 +1,8 @@ using System; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Instrumentation { @@ -11,8 +13,8 @@ public interface ILogRepository : IBasicRepository public class LogRepository : BasicRepository, ILogRepository { - public LogRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public LogRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/Instrumentation/LogService.cs b/NzbDrone.Core/Instrumentation/LogService.cs index 5e5717571..f46dbf5dd 100644 --- a/NzbDrone.Core/Instrumentation/LogService.cs +++ b/NzbDrone.Core/Instrumentation/LogService.cs @@ -1,7 +1,7 @@ -using System; -using NzbDrone.Common.Messaging; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; using NzbDrone.Core.Instrumentation.Commands; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Instrumentation { diff --git a/NzbDrone.Core/Instrumentation/LoggerExtensions.cs b/NzbDrone.Core/Instrumentation/LoggerExtensions.cs new file mode 100644 index 000000000..82ece28d2 --- /dev/null +++ b/NzbDrone.Core/Instrumentation/LoggerExtensions.cs @@ -0,0 +1,34 @@ +using System; +using NLog; + +namespace NzbDrone.Core.Instrumentation +{ + public static class LoggerExtensions + { + public static void ProgressInfo(this Logger logger, string message, params object[] args) + { + var formattedMessage = String.Format(message, args); + LogProgressMessage(logger, LogLevel.Info, formattedMessage); + } + + public static void ProgressDebug(this Logger logger, string message, params object[] args) + { + var formattedMessage = String.Format(message, args); + LogProgressMessage(logger, LogLevel.Debug, formattedMessage); + } + + public static void ProgressTrace(this Logger logger, string message, params object[] args) + { + var formattedMessage = String.Format(message, args); + LogProgressMessage(logger, LogLevel.Trace, formattedMessage); + } + + private static void LogProgressMessage(Logger logger, LogLevel level, string message) + { + var logEvent = new LogEventInfo(level, logger.Name, message); + logEvent.Properties.Add("Status", ""); + + logger.Log(logEvent); + } + } +} diff --git a/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs b/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs index 251ef6f38..61412e8fb 100644 --- a/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs +++ b/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs @@ -2,10 +2,12 @@ using System.Linq; using NLog; using NLog.Config; -using NzbDrone.Common.Messaging; +using NLog.Targets; using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Instrumentation { @@ -28,10 +30,12 @@ public void Reconfigure() var minimumLogLevel = LogLevel.FromString(_configFileProvider.LogLevel); var rules = LogManager.Configuration.LoggingRules; - var rollingFileLogger = rules.Single(s => s.Targets.Any(t => t.Name == "rollingFileLogger")); + var rollingFileLogger = rules.Single(s => s.Targets.Any(t => t is FileTarget)); rollingFileLogger.EnableLoggingForLevel(LogLevel.Trace); SetMinimumLogLevel(rollingFileLogger, minimumLogLevel); + + LogManager.ReconfigExistingLoggers(); } private void SetMinimumLogLevel(LoggingRule rule, LogLevel minimumLogLevel) diff --git a/NzbDrone.Core/Jobs/JobRepository.cs b/NzbDrone.Core/Jobs/JobRepository.cs index 700b8674e..8e2aa5858 100644 --- a/NzbDrone.Core/Jobs/JobRepository.cs +++ b/NzbDrone.Core/Jobs/JobRepository.cs @@ -1,7 +1,8 @@ using System; using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Jobs { @@ -15,8 +16,8 @@ public interface IScheduledTaskRepository : IBasicRepository public class ScheduledTaskRepository : BasicRepository, IScheduledTaskRepository { - public ScheduledTaskRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public ScheduledTaskRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/Jobs/Scheduler.cs b/NzbDrone.Core/Jobs/Scheduler.cs index 20ed73489..2b8b42086 100644 --- a/NzbDrone.Core/Jobs/Scheduler.cs +++ b/NzbDrone.Core/Jobs/Scheduler.cs @@ -2,8 +2,10 @@ using System.Threading; using System.Threading.Tasks; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; using Timer = System.Timers.Timer; using NzbDrone.Common.TPL; @@ -14,15 +16,15 @@ public class Scheduler : IHandle { private readonly ITaskManager _taskManager; - private readonly IMessageAggregator _messageAggregator; + private readonly ICommandExecutor _commandExecutor; private readonly Logger _logger; private static readonly Timer Timer = new Timer(); private static CancellationTokenSource _cancellationTokenSource; - public Scheduler(ITaskManager taskManager, IMessageAggregator messageAggregator, Logger logger) + public Scheduler(ITaskManager taskManager, ICommandExecutor commandExecutor, Logger logger) { _taskManager = taskManager; - _messageAggregator = messageAggregator; + _commandExecutor = commandExecutor; _logger = logger; } @@ -52,7 +54,7 @@ private void ExecuteCommands() try { - _messageAggregator.PublishCommand(task.TypeName); + _commandExecutor.PublishCommand(task.TypeName); } catch (Exception e) { diff --git a/NzbDrone.Core/Jobs/TaskManager.cs b/NzbDrone.Core/Jobs/TaskManager.cs index d77c8b0aa..af16df6f9 100644 --- a/NzbDrone.Core/Jobs/TaskManager.cs +++ b/NzbDrone.Core/Jobs/TaskManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.DataAugmentation.Scene; @@ -10,6 +9,9 @@ using NzbDrone.Core.Instrumentation.Commands; using NzbDrone.Core.Lifecycle; using NzbDrone.Core.MediaFiles.Commands; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands.Tracking; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Providers; using NzbDrone.Core.Tv.Commands; using NzbDrone.Core.Update.Commands; @@ -49,7 +51,8 @@ public void Handle(ApplicationStartedEvent message) new ScheduledTask{ Interval = 1, TypeName = typeof(DownloadedEpisodesScanCommand).FullName}, new ScheduledTask{ Interval = 60, TypeName = typeof(ApplicationUpdateCommand).FullName}, new ScheduledTask{ Interval = 1*60, TypeName = typeof(TrimLogCommand).FullName}, - new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName} + new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName}, + new ScheduledTask{ Interval = 1, TypeName = typeof(TrackedCommandCleanupCommand).FullName} }; var currentTasks = _scheduledTaskRepository.All(); @@ -82,6 +85,7 @@ public void HandleAsync(CommandExecutedEvent message) if (scheduledTask != null) { + _logger.Trace("Updating last run time for: {0}", scheduledTask.TypeName); _scheduledTaskRepository.SetLastExecutionTime(scheduledTask.Id, DateTime.UtcNow); } } @@ -93,4 +97,4 @@ public void HandleAsync(ConfigSavedEvent message) _scheduledTaskRepository.Update(rss); } } -} \ No newline at end of file +} diff --git a/NzbDrone.Core/Lifecycle/ApplicationStartedEvent.cs b/NzbDrone.Core/Lifecycle/ApplicationStartedEvent.cs index 985986b05..f66622dd3 100644 --- a/NzbDrone.Core/Lifecycle/ApplicationStartedEvent.cs +++ b/NzbDrone.Core/Lifecycle/ApplicationStartedEvent.cs @@ -6,5 +6,4 @@ public class ApplicationStartedEvent : IEvent { } - } \ No newline at end of file diff --git a/NzbDrone.Core/MediaCover/MediaCoverService.cs b/NzbDrone.Core/MediaCover/MediaCoverService.cs index e3d247bfa..67b0d0b21 100644 --- a/NzbDrone.Core/MediaCover/MediaCoverService.cs +++ b/NzbDrone.Core/MediaCover/MediaCoverService.cs @@ -5,7 +5,8 @@ using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Events; diff --git a/NzbDrone.Core/MediaFiles/Commands/BackendCommandAttribute.cs b/NzbDrone.Core/MediaFiles/Commands/BackendCommandAttribute.cs new file mode 100644 index 000000000..7e8347897 --- /dev/null +++ b/NzbDrone.Core/MediaFiles/Commands/BackendCommandAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace NzbDrone.Core.MediaFiles.Commands +{ + public class BackendCommandAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/Commands/CleanMediaFileDb.cs b/NzbDrone.Core/MediaFiles/Commands/CleanMediaFileDb.cs index b873dcc5f..a06cb29d7 100644 --- a/NzbDrone.Core/MediaFiles/Commands/CleanMediaFileDb.cs +++ b/NzbDrone.Core/MediaFiles/Commands/CleanMediaFileDb.cs @@ -1,8 +1,8 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles.Commands { - public class CleanMediaFileDb : ICommand + public class CleanMediaFileDb : Command { public int SeriesId { get; private set; } diff --git a/NzbDrone.Core/MediaFiles/Commands/CleanUpRecycleBinCommand.cs b/NzbDrone.Core/MediaFiles/Commands/CleanUpRecycleBinCommand.cs index b2d16f231..cb8d8c3a6 100644 --- a/NzbDrone.Core/MediaFiles/Commands/CleanUpRecycleBinCommand.cs +++ b/NzbDrone.Core/MediaFiles/Commands/CleanUpRecycleBinCommand.cs @@ -1,8 +1,8 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles.Commands { - public class CleanUpRecycleBinCommand : ICommand + public class CleanUpRecycleBinCommand : Command { } } \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/Commands/DownloadedEpisodesScanCommand.cs b/NzbDrone.Core/MediaFiles/Commands/DownloadedEpisodesScanCommand.cs index 0f03f2083..c9abe533a 100644 --- a/NzbDrone.Core/MediaFiles/Commands/DownloadedEpisodesScanCommand.cs +++ b/NzbDrone.Core/MediaFiles/Commands/DownloadedEpisodesScanCommand.cs @@ -1,11 +1,9 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles.Commands { - public class DownloadedEpisodesScanCommand : ICommand + public class DownloadedEpisodesScanCommand : Command { - public DownloadedEpisodesScanCommand() - { - } + } } \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/Commands/RenameSeasonCommand.cs b/NzbDrone.Core/MediaFiles/Commands/RenameSeasonCommand.cs index 723c5d74b..b9a917ca3 100644 --- a/NzbDrone.Core/MediaFiles/Commands/RenameSeasonCommand.cs +++ b/NzbDrone.Core/MediaFiles/Commands/RenameSeasonCommand.cs @@ -1,11 +1,19 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles.Commands { - public class RenameSeasonCommand : ICommand + public class RenameSeasonCommand : Command { - public int SeriesId { get; private set; } - public int SeasonNumber { get; private set; } + public int SeriesId { get; set; } + public int SeasonNumber { get; set; } + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public RenameSeasonCommand(int seriesId, int seasonNumber) { diff --git a/NzbDrone.Core/MediaFiles/Commands/RenameSeriesCommand.cs b/NzbDrone.Core/MediaFiles/Commands/RenameSeriesCommand.cs index 7716c43c0..eb7e578d4 100644 --- a/NzbDrone.Core/MediaFiles/Commands/RenameSeriesCommand.cs +++ b/NzbDrone.Core/MediaFiles/Commands/RenameSeriesCommand.cs @@ -1,10 +1,22 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.MediaFiles.Commands { - public class RenameSeriesCommand : ICommand + public class RenameSeriesCommand : Command { - public int SeriesId { get; private set; } + public int SeriesId { get; set; } + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + + public RenameSeriesCommand() + { + } public RenameSeriesCommand(int seriesId) { diff --git a/NzbDrone.Core/MediaFiles/DiskScanService.cs b/NzbDrone.Core/MediaFiles/DiskScanService.cs index 0fb6901fe..16b64789f 100644 --- a/NzbDrone.Core/MediaFiles/DiskScanService.cs +++ b/NzbDrone.Core/MediaFiles/DiskScanService.cs @@ -1,11 +1,13 @@ -using System.Collections.Generic; -using System.IO; +using System.IO; using System.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Instrumentation; using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.EpisodeImport; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Events; @@ -20,39 +22,28 @@ public class DiskScanService : IDiskScanService, IHandle { - private readonly HashSet _mediaExtensions; - - private const string EXTENSIONS = - //XBMC - ".m4v .3gp .nsv .ts .ty .strm .rm .rmvb .m3u .ifo .mov .qt .divx .xvid .bivx .vob .nrg .img " + - ".iso .pva .wmv .asf .asx .ogm .m2v .avi .bin .dat .dvr-ms .mpg .mpeg .mp4 .mkv .avc .vp3 " + - ".svq3 .nuv .viv .dv .fli .flv .wpl " + - //Other - ".m2ts"; - private readonly IDiskProvider _diskProvider; private readonly IMakeImportDecision _importDecisionMaker; private readonly IImportApprovedEpisodes _importApprovedEpisodes; - private readonly IMessageAggregator _messageAggregator; + private readonly ICommandExecutor _commandExecutor; private readonly Logger _logger; public DiskScanService(IDiskProvider diskProvider, IMakeImportDecision importDecisionMaker, IImportApprovedEpisodes importApprovedEpisodes, - IMessageAggregator messageAggregator, Logger logger) + ICommandExecutor commandExecutor, Logger logger) { _diskProvider = diskProvider; _importDecisionMaker = importDecisionMaker; _importApprovedEpisodes = importApprovedEpisodes; - _messageAggregator = messageAggregator; + _commandExecutor = commandExecutor; _logger = logger; - - _mediaExtensions = new HashSet(EXTENSIONS.Split(' ').Select(c => c.ToLower())); } private void Scan(Series series) { - _messageAggregator.PublishCommand(new CleanMediaFileDb(series.Id)); + _logger.ProgressInfo("Scanning disk for {0}", series.Title); + _commandExecutor.PublishCommand(new CleanMediaFileDb(series.Id)); if (!_diskProvider.FolderExists(series.Path)) { @@ -73,7 +64,7 @@ public string[] GetVideoFiles(string path, bool allDirectories = true) var searchOption = allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; var filesOnDisk = _diskProvider.GetFiles(path, searchOption); - var mediaFileList = filesOnDisk.Where(c => _mediaExtensions.Contains(Path.GetExtension(c).ToLower())).ToList(); + var mediaFileList = filesOnDisk.Where(c => MediaFileExtensions.Extensions.Contains(Path.GetExtension(c).ToLower())).ToList(); _logger.Trace("{0} video files were found in {1}", mediaFileList.Count, path); return mediaFileList.ToArray(); diff --git a/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs b/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs index f5bf03915..55c6f4638 100644 --- a/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs +++ b/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs @@ -4,11 +4,12 @@ using System.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles.Commands; using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Parser; using NzbDrone.Core.Tv; @@ -51,13 +52,13 @@ private void ProcessDownloadedEpisodesFolder() if (String.IsNullOrEmpty(downloadedEpisodesFolder)) { - _logger.Warn("Downloaded Episodes Folder is not configured"); + _logger.Warn("Drone Factory folder is not configured"); return; } if (!_diskProvider.FolderExists(downloadedEpisodesFolder)) { - _logger.Warn("Downloaded Episodes Folder [{0}] doesn't exist.", downloadedEpisodesFolder); + _logger.Warn("Drone Factory folder [{0}] doesn't exist.", downloadedEpisodesFolder); return; } @@ -112,7 +113,7 @@ private List ProcessSubFolder(DirectoryInfo subfolderInfo) var videoFiles = _diskScanService.GetVideoFiles(subfolderInfo.FullName); - return ProcessFiles(videoFiles, series); + return ProcessFiles(series, videoFiles); } private void ProcessVideoFile(string videoFile) @@ -125,16 +126,16 @@ private void ProcessVideoFile(string videoFile) return; } - if (_diskProvider.IsFileLocked(new FileInfo(videoFile))) + if (_diskProvider.IsFileLocked(videoFile)) { _logger.Debug("[{0}] is currently locked by another process, skipping", videoFile); return; } - ProcessFiles(new[] { videoFile }, series); + ProcessFiles(series, videoFile); } - private List ProcessFiles(IEnumerable videoFiles, Series series) + private List ProcessFiles(Series series, params string[] videoFiles) { var decisions = _importDecisionMaker.GetImportDecisions(videoFiles, series, true); return _importApprovedEpisodes.Import(decisions, true); diff --git a/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs b/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs index ef230443b..91157d26a 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeFileMovingService.cs @@ -3,8 +3,8 @@ using System.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Common.Messaging; -using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Organizer; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -21,19 +21,19 @@ public class MoveEpisodeFiles : IMoveEpisodeFiles { private readonly IEpisodeService _episodeService; private readonly IBuildFileNames _buildFileNames; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly IDiskProvider _diskProvider; private readonly Logger _logger; public MoveEpisodeFiles(IEpisodeService episodeService, IBuildFileNames buildFileNames, - IMessageAggregator messageAggregator, + IEventAggregator eventAggregator, IDiskProvider diskProvider, Logger logger) { _episodeService = episodeService; _buildFileNames = buildFileNames; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _diskProvider = diskProvider; _logger = logger; } diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs index 733b8580d..a9a92bb87 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/ImportApprovedEpisodes.cs @@ -4,8 +4,10 @@ using System.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Common.Messaging; using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.MediaFiles.EpisodeImport { @@ -19,19 +21,19 @@ public class ImportApprovedEpisodes : IImportApprovedEpisodes private readonly IUpgradeMediaFiles _episodeFileUpgrader; private readonly IMediaFileService _mediaFileService; private readonly IDiskProvider _diskProvider; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; public ImportApprovedEpisodes(IUpgradeMediaFiles episodeFileUpgrader, IMediaFileService mediaFileService, IDiskProvider diskProvider, - IMessageAggregator messageAggregator, + IEventAggregator eventAggregator, Logger logger) { _episodeFileUpgrader = episodeFileUpgrader; _mediaFileService = mediaFileService; _diskProvider = diskProvider; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _logger = logger; } @@ -69,8 +71,8 @@ public List Import(List decisions, bool newDownl { episodeFile.SceneName = Path.GetFileNameWithoutExtension(localEpisode.Path.CleanFilePath()); episodeFile.Path = _episodeFileUpgrader.UpgradeEpisodeFile(episodeFile, localEpisode); - _messageAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile)); - _messageAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode)); + _eventAggregator.PublishEvent(new EpisodeImportedEvent(localEpisode, episodeFile)); + _eventAggregator.PublishEvent(new EpisodeDownloadedEvent(localEpisode)); } _mediaFileService.Add(episodeFile); diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs index 9c499d8fb..609a962cd 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs @@ -23,22 +23,33 @@ public bool IsSatisfiedBy(LocalEpisode localEpisode) { try { + if (localEpisode.ExistingFile) + { + _logger.Trace("Skipping free space check for existing episode"); + return true; + } + var path = Directory.GetParent(localEpisode.Series.Path); var freeSpace = _diskProvider.GetAvailableSpace(path.FullName); + if (!freeSpace.HasValue) + { + _logger.Trace("Free space check returned an invalid result for: {0}", path); + return true; + } + if (freeSpace < localEpisode.Size + 100.Megabytes()) { _logger.Warn("Not enough free space to import: {0}", localEpisode); return false; } - - return true; } catch (Exception ex) { _logger.ErrorException("Unable to check free disk space while importing: " + localEpisode.Path, ex); - throw; } + + return true; } } } diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotInUseSpecification.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotInUseSpecification.cs index 5fd2f453b..1da911a60 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotInUseSpecification.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotInUseSpecification.cs @@ -1,5 +1,4 @@ -using System.IO; -using NLog; +using NLog; using NzbDrone.Common; using NzbDrone.Core.Parser.Model; @@ -20,13 +19,13 @@ public NotInUseSpecification(IDiskProvider diskProvider, Logger logger) public bool IsSatisfiedBy(LocalEpisode localEpisode) { - if (_diskProvider.IsParent(localEpisode.Series.Path, localEpisode.Path)) + if (localEpisode.ExistingFile) { _logger.Trace("{0} is in series folder, skipping in use check", localEpisode.Path); return true; } - if (_diskProvider.IsFileLocked(new FileInfo(localEpisode.Path))) + if (_diskProvider.IsFileLocked(localEpisode.Path)) { _logger.Trace("{0} is in use"); return false; diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs index 51561b005..3d2901974 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotSampleSpecification.cs @@ -1,6 +1,7 @@ using System; using System.IO; using NLog; +using NzbDrone.Core.MediaFiles.MediaInfo; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Providers; using NzbDrone.Core.Tv; diff --git a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs index 0f1e66d3a..b8212e12c 100644 --- a/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs +++ b/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/NotUnpackingSpecification.cs @@ -24,7 +24,7 @@ public NotUnpackingSpecification(IDiskProvider diskProvider, IConfigService conf public bool IsSatisfiedBy(LocalEpisode localEpisode) { - if (_diskProvider.IsParent(localEpisode.Series.Path, localEpisode.Path)) + if (localEpisode.ExistingFile) { _logger.Trace("{0} is in series folder, unpacking check", localEpisode.Path); return true; diff --git a/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs b/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs new file mode 100644 index 000000000..9416e4d4d --- /dev/null +++ b/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using NzbDrone.Core.Qualities; + + +namespace NzbDrone.Core.MediaFiles +{ + public static class MediaFileExtensions + { + private static Dictionary _fileExtensions; + + static MediaFileExtensions() + { + _fileExtensions = new Dictionary + { + { ".m4v", Quality.SDTV }, + { ".3gp", Quality.SDTV }, + { ".nsv", Quality.SDTV }, + { ".ty", Quality.SDTV }, + { ".strm", Quality.SDTV }, + { ".rm", Quality.SDTV }, + { ".rmvb", Quality.SDTV }, + { ".m3u", Quality.SDTV }, + { ".ifo", Quality.SDTV }, + { ".mov", Quality.SDTV }, + { ".qt", Quality.SDTV }, + { ".divx", Quality.SDTV }, + { ".xvid", Quality.SDTV }, + { ".bivx", Quality.SDTV }, + { ".nrg", Quality.SDTV }, + { ".pva", Quality.SDTV }, + { ".wmv", Quality.SDTV }, + { ".asf", Quality.SDTV }, + { ".asx", Quality.SDTV }, + { ".ogm", Quality.SDTV }, + { ".m2v", Quality.SDTV }, + { ".avi", Quality.SDTV }, + { ".bin", Quality.SDTV }, + { ".dat", Quality.SDTV }, + { ".dvr-ms", Quality.SDTV }, + { ".mpg", Quality.SDTV }, + { ".mpeg", Quality.SDTV }, + { ".mp4", Quality.SDTV }, + { ".avc", Quality.SDTV }, + { ".vp3", Quality.SDTV }, + { ".svq3", Quality.SDTV }, + { ".nuv", Quality.SDTV }, + { ".viv", Quality.SDTV }, + { ".dv", Quality.SDTV }, + { ".fli", Quality.SDTV }, + { ".flv", Quality.SDTV }, + { ".wpl", Quality.SDTV }, + + //DVD + { ".img", Quality.DVD }, + { ".iso", Quality.DVD }, + { ".vob", Quality.DVD }, + + //HD + { ".mkv", Quality.HDTV720p }, + { ".ts", Quality.HDTV720p }, + + //Bluray + { ".m2ts", Quality.Bluray720p } + }; + } + + public static HashSet Extensions + { + get { return new HashSet(_fileExtensions.Keys); } + } + + public static Quality GetQualityForExtension(string extension) + { + if (_fileExtensions.ContainsKey(extension)) + { + return _fileExtensions[extension]; + } + + return Quality.Unknown; + } + } +} diff --git a/NzbDrone.Core/MediaFiles/MediaFileRepository.cs b/NzbDrone.Core/MediaFiles/MediaFileRepository.cs index 406bd9669..7a0839594 100644 --- a/NzbDrone.Core/MediaFiles/MediaFileRepository.cs +++ b/NzbDrone.Core/MediaFiles/MediaFileRepository.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.MediaFiles { @@ -16,8 +18,8 @@ public interface IMediaFileRepository : IBasicRepository public class MediaFileRepository : BasicRepository, IMediaFileRepository { - public MediaFileRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public MediaFileRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/MediaFiles/MediaFileService.cs b/NzbDrone.Core/MediaFiles/MediaFileService.cs index 54501c859..b963b408e 100644 --- a/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -1,9 +1,9 @@ -using System; using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv.Events; using NzbDrone.Common; @@ -24,21 +24,21 @@ public interface IMediaFileService public class MediaFileService : IMediaFileService, IHandleAsync { - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly IMediaFileRepository _mediaFileRepository; private readonly Logger _logger; - public MediaFileService(IMediaFileRepository mediaFileRepository, IMessageAggregator messageAggregator, Logger logger) + public MediaFileService(IMediaFileRepository mediaFileRepository, IEventAggregator eventAggregator, Logger logger) { _mediaFileRepository = mediaFileRepository; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _logger = logger; } public EpisodeFile Add(EpisodeFile episodeFile) { var addedFile = _mediaFileRepository.Insert(episodeFile); - _messageAggregator.PublishEvent(new EpisodeFileAddedEvent(addedFile)); + _eventAggregator.PublishEvent(new EpisodeFileAddedEvent(addedFile)); return addedFile; } @@ -51,7 +51,7 @@ public void Delete(EpisodeFile episodeFile, bool forUpgrade = false) { _mediaFileRepository.Delete(episodeFile); - _messageAggregator.PublishEvent(new EpisodeFileDeletedEvent(episodeFile, forUpgrade)); + _eventAggregator.PublishEvent(new EpisodeFileDeletedEvent(episodeFile, forUpgrade)); } public bool Exists(string path) diff --git a/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs b/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs index b72f03b4f..26c4d1905 100644 --- a/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs +++ b/NzbDrone.Core/MediaFiles/MediaFileTableCleanupService.cs @@ -2,8 +2,9 @@ using System.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Common.Messaging; using NzbDrone.Core.MediaFiles.Commands; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Tv; namespace NzbDrone.Core.MediaFiles diff --git a/NzbDrone.Core/Providers/VideoFileInfoReader.cs b/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs similarity index 93% rename from NzbDrone.Core/Providers/VideoFileInfoReader.cs rename to NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs index 1aa8c121e..a2f1143e2 100644 --- a/NzbDrone.Core/Providers/VideoFileInfoReader.cs +++ b/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs @@ -5,7 +5,7 @@ using NzbDrone.Common; using NzbDrone.Core.Model; -namespace NzbDrone.Core.Providers +namespace NzbDrone.Core.MediaFiles.MediaInfo { public interface IVideoFileInfoReader { @@ -31,7 +31,7 @@ public MediaInfoModel GetMediaInfo(string filename) if (!_diskProvider.FileExists(filename)) throw new FileNotFoundException("Media file does not exist: " + filename); - var mediaInfo = new MediaInfo(); + var mediaInfo = new MediaInfoLib.MediaInfo(); try { @@ -112,10 +112,10 @@ public MediaInfoModel GetMediaInfo(string filename) public TimeSpan GetRunTime(string filename) { - var mediaInfo = new MediaInfo(); - + MediaInfoLib.MediaInfo mediaInfo = null; try { + mediaInfo = new MediaInfoLib.MediaInfo(); _logger.Trace("Getting media info from {0}", filename); mediaInfo.Option("ParseSpeed", "0.2"); @@ -133,7 +133,13 @@ public TimeSpan GetRunTime(string filename) catch (Exception ex) { _logger.ErrorException("Unable to parse media info from file: " + filename, ex); - mediaInfo.Close(); + } + finally + { + if (mediaInfo != null) + { + mediaInfo.Close(); + } } return new TimeSpan(); diff --git a/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs b/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs index ff3866b39..00ff120e2 100644 --- a/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs +++ b/NzbDrone.Core/MediaFiles/RecycleBinProvider.cs @@ -3,9 +3,12 @@ using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Messaging; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles.Commands; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv.Events; namespace NzbDrone.Core.MediaFiles @@ -23,7 +26,7 @@ public class RecycleBinProvider : IHandleAsync, IExecute, IExecute< private readonly ISeriesService _seriesService; private readonly IMediaFileService _mediaFileService; private readonly IMoveEpisodeFiles _episodeFileMover; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; public RenameEpisodeFileService(ISeriesService seriesService, IMediaFileService mediaFileService, IMoveEpisodeFiles episodeFileMover, - IMessageAggregator messageAggregator, + IEventAggregator eventAggregator, Logger logger) { _seriesService = seriesService; _mediaFileService = mediaFileService; _episodeFileMover = episodeFileMover; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _logger = logger; } @@ -58,7 +61,7 @@ private void RenameFiles(List episodeFiles, Series series) if (renamed.Any()) { - _messageAggregator.PublishEvent(new SeriesRenamedEvent(series)); + _eventAggregator.PublishEvent(new SeriesRenamedEvent(series)); } } @@ -67,9 +70,9 @@ public void Execute(RenameSeasonCommand message) var series = _seriesService.GetSeries(message.SeriesId); var episodeFiles = _mediaFileService.GetFilesBySeason(message.SeriesId, message.SeasonNumber); - _logger.Info("Renaming {0} files for {1} season {2}", episodeFiles.Count, series.Title, message.SeasonNumber); + _logger.ProgressInfo("Renaming {0} files for {1} season {2}", episodeFiles.Count, series.Title, message.SeasonNumber); RenameFiles(episodeFiles, series); - _logger.Debug("Episode Fies renamed for {0} season {1}", series.Title, message.SeasonNumber); + _logger.ProgressInfo("Episode Fies renamed for {0} season {1}", series.Title, message.SeasonNumber); } public void Execute(RenameSeriesCommand message) @@ -77,9 +80,9 @@ public void Execute(RenameSeriesCommand message) var series = _seriesService.GetSeries(message.SeriesId); var episodeFiles = _mediaFileService.GetFilesBySeries(message.SeriesId); - _logger.Info("Renaming {0} files for {1}", episodeFiles.Count, series.Title); + _logger.ProgressInfo("Renaming {0} files for {1}", episodeFiles.Count, series.Title); RenameFiles(episodeFiles, series); - _logger.Debug("Episode Fies renamed for {0}", series.Title); + _logger.ProgressInfo("Episode Fies renamed for {0}", series.Title); } } } diff --git a/NzbDrone.Core/Messaging/Commands/BackendCommandAttribute.cs b/NzbDrone.Core/Messaging/Commands/BackendCommandAttribute.cs new file mode 100644 index 000000000..b40f64f9c --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/BackendCommandAttribute.cs @@ -0,0 +1,8 @@ +using System; + +namespace NzbDrone.Core.Messaging.Commands +{ + public class BackendCommandAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Messaging/Commands/Command.cs b/NzbDrone.Core/Messaging/Commands/Command.cs new file mode 100644 index 000000000..516464381 --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/Command.cs @@ -0,0 +1,82 @@ +using System; +using FluentMigrator.Runner; +using NzbDrone.Common.Messaging; +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging.Commands.Tracking; + +namespace NzbDrone.Core.Messaging.Commands +{ + public abstract class Command : ModelBase, IMessage + { + private static readonly object Mutex = new object(); + private static int _idCounter; + private readonly StopWatch _stopWatch; + + public CommandStatus State { get; private set; } + public DateTime StateChangeTime { get; private set; } + + public virtual bool SendUpdatesToClient + { + get + { + return false; + } + } + + public TimeSpan Runtime + { + get + { + return _stopWatch.ElapsedTime(); + } + } + + public Exception Exception { get; private set; } + public string Message { get; private set; } + + public string Name { get; private set; } + + protected Command() + { + Name = GetType().Name.Replace("Command", ""); + StateChangeTime = DateTime.UtcNow; + State = CommandStatus.Pending; + _stopWatch = new StopWatch(); + + lock (Mutex) + { + Id = ++_idCounter; + } + } + + public void Start() + { + _stopWatch.Start(); + StateChangeTime = DateTime.UtcNow; + State = CommandStatus.Running; + SetMessage("Starting"); + } + + public void Failed(Exception exception) + { + _stopWatch.Stop(); + StateChangeTime = DateTime.UtcNow; + State = CommandStatus.Failed; + Exception = exception; + SetMessage("Failed"); + } + + public void Completed() + { + _stopWatch.Stop(); + StateChangeTime = DateTime.UtcNow; + State = CommandStatus.Completed; + SetMessage("Completed"); + } + + public void SetMessage(string message) + { + Message = message; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs b/NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs new file mode 100644 index 000000000..2b24895f1 --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Linq; + +namespace NzbDrone.Core.Messaging.Commands +{ + public class CommandEqualityComparer : IEqualityComparer + { + public static readonly CommandEqualityComparer Instance = new CommandEqualityComparer(); + + private CommandEqualityComparer() + { + + } + + public bool Equals(Command x, Command y) + { + if(x.GetType() != y.GetType()) return false; + + var xProperties = x.GetType().GetProperties(); + var yProperties = y.GetType().GetProperties(); + + foreach (var xProperty in xProperties) + { + if (xProperty.Name == "Id") + { + continue; + } + + var yProperty = yProperties.Single(p => p.Name == xProperty.Name); + + var xValue = xProperty.GetValue(x, null); + var yValue = yProperty.GetValue(y, null); + + if (xValue == null && yValue == null) + { + return true; + } + + if (xValue == null || yValue == null) + { + return false; + } + + if (!xValue.Equals(yValue)) + { + return false; + } + } + + return true; + } + + public int GetHashCode(Command obj) + { + return obj.Id.GetHashCode(); + } + } +} diff --git a/NzbDrone.Core/Messaging/Commands/CommandExecutor.cs b/NzbDrone.Core/Messaging/Commands/CommandExecutor.cs new file mode 100644 index 000000000..dd3175304 --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/CommandExecutor.cs @@ -0,0 +1,142 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using NLog; +using NzbDrone.Common; +using NzbDrone.Common.EnsureThat; +using NzbDrone.Common.Serializer; +using NzbDrone.Common.TPL; +using NzbDrone.Core.Messaging.Commands.Tracking; +using NzbDrone.Core.Messaging.Events; +using NzbDrone.Core.ProgressMessaging; + +namespace NzbDrone.Core.Messaging.Commands +{ + public class CommandExecutor : ICommandExecutor + { + private readonly Logger _logger; + private readonly IServiceFactory _serviceFactory; + private readonly ITrackCommands _trackCommands; + private readonly IEventAggregator _eventAggregator; + private readonly TaskFactory _taskFactory; + + public CommandExecutor(Logger logger, IServiceFactory serviceFactory, ITrackCommands trackCommands, IEventAggregator eventAggregator) + { + var scheduler = new LimitedConcurrencyLevelTaskScheduler(3); + + _logger = logger; + _serviceFactory = serviceFactory; + _trackCommands = trackCommands; + _eventAggregator = eventAggregator; + _taskFactory = new TaskFactory(scheduler); + } + + public void PublishCommand(TCommand command) where TCommand : Command + { + Ensure.That(() => command).IsNotNull(); + + _logger.Trace("Publishing {0}", command.GetType().Name); + + if (_trackCommands.FindExisting(command) != null) + { + _logger.Debug("Command is already in progress: {0}", command.GetType().Name); + return; + } + + _trackCommands.Store(command); + + ExecuteCommand(command); + } + + public void PublishCommand(string commandTypeName) + { + dynamic command = GetCommand(commandTypeName); + PublishCommand(command); + } + + public Command PublishCommandAsync(TCommand command) where TCommand : Command + { + Ensure.That(() => command).IsNotNull(); + + _logger.Trace("Publishing {0}", command.GetType().Name); + + var existingCommand = _trackCommands.FindExisting(command); + + if (existingCommand != null) + { + _logger.Debug("Command is already in progress: {0}", command.GetType().Name); + return existingCommand; + } + + _trackCommands.Store(command); + + _taskFactory.StartNew(() => ExecuteCommand(command) + , TaskCreationOptions.PreferFairness) + .LogExceptions(); + + return command; + } + + public Command PublishCommandAsync(string commandTypeName) + { + dynamic command = GetCommand(commandTypeName); + return PublishCommandAsync(command); + } + + private dynamic GetCommand(string commandTypeName) + { + var commandType = _serviceFactory.GetImplementations(typeof(Command)) + .Single(c => c.FullName.Equals(commandTypeName, StringComparison.InvariantCultureIgnoreCase)); + + return Json.Deserialize("{}", commandType); + } + + private void ExecuteCommand(Command command) where TCommand : Command + { + var handlerContract = typeof(IExecute<>).MakeGenericType(command.GetType()); + var handler = (IExecute)_serviceFactory.Build(handlerContract); + + _logger.Trace("{0} -> {1}", command.GetType().Name, handler.GetType().Name); + + try + { + _trackCommands.Start(command); + BroadcastCommandUpdate(command); + + if (!MappedDiagnosticsContext.Contains("CommandId") && command.SendUpdatesToClient) + { + MappedDiagnosticsContext.Set("CommandId", command.Id.ToString()); + } + + handler.Execute((TCommand)command); + _trackCommands.Completed(command); + } + catch (Exception e) + { + _trackCommands.Failed(command, e); + throw; + } + finally + { + BroadcastCommandUpdate(command); + _eventAggregator.PublishEvent(new CommandExecutedEvent(command)); + + if (MappedDiagnosticsContext.Get("CommandId") == command.Id.ToString()) + { + MappedDiagnosticsContext.Remove("CommandId"); + } + } + + _logger.Trace("{0} <- {1} [{2}]", command.GetType().Name, handler.GetType().Name, command.Runtime.ToString("")); + } + + + private void BroadcastCommandUpdate(Command command) + { + if (command.SendUpdatesToClient) + { + _eventAggregator.PublishEvent(new CommandUpdatedEvent(command)); + } + } + } +} diff --git a/NzbDrone.Core/Messaging/Commands/ICommandExecutor.cs b/NzbDrone.Core/Messaging/Commands/ICommandExecutor.cs new file mode 100644 index 000000000..45d300fcd --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/ICommandExecutor.cs @@ -0,0 +1,10 @@ +namespace NzbDrone.Core.Messaging.Commands +{ + public interface ICommandExecutor + { + void PublishCommand(TCommand command) where TCommand : Command; + void PublishCommand(string commandTypeName); + Command PublishCommandAsync(TCommand command) where TCommand : Command; + Command PublishCommandAsync(string commandTypeName); + } +} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/IExecute.cs b/NzbDrone.Core/Messaging/Commands/IExecute.cs similarity index 62% rename from NzbDrone.Common/Messaging/IExecute.cs rename to NzbDrone.Core/Messaging/Commands/IExecute.cs index a065e0828..4e058e4d0 100644 --- a/NzbDrone.Common/Messaging/IExecute.cs +++ b/NzbDrone.Core/Messaging/Commands/IExecute.cs @@ -1,6 +1,6 @@ -namespace NzbDrone.Common.Messaging +namespace NzbDrone.Core.Messaging.Commands { - public interface IExecute : IProcessMessage where TCommand : ICommand + public interface IExecute : IProcessMessage where TCommand : Command { void Execute(TCommand message); } diff --git a/NzbDrone.Core/Messaging/Commands/TestCommand.cs b/NzbDrone.Core/Messaging/Commands/TestCommand.cs new file mode 100644 index 000000000..616c99ee7 --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/TestCommand.cs @@ -0,0 +1,20 @@ +namespace NzbDrone.Core.Messaging.Commands +{ + public class TestCommand : Command + { + public int Duration { get; set; } + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + + public TestCommand() + { + Duration = 4000; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Messaging/Commands/TestCommandExecutor.cs b/NzbDrone.Core/Messaging/Commands/TestCommandExecutor.cs new file mode 100644 index 000000000..8e0ebfb71 --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/TestCommandExecutor.cs @@ -0,0 +1,23 @@ +using System.Threading; +using NLog; +using NzbDrone.Core.Instrumentation; + +namespace NzbDrone.Core.Messaging.Commands +{ + public class TestCommandExecutor : IExecute + { + private readonly Logger _logger; + + public TestCommandExecutor(Logger logger) + { + _logger = logger; + } + + public void Execute(TestCommand message) + { + _logger.ProgressInfo("Starting Test command. duration {0}", message.Duration); + Thread.Sleep(message.Duration); + _logger.ProgressInfo("Completed Test command"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Messaging/Commands/Tracking/CommandStatus.cs b/NzbDrone.Core/Messaging/Commands/Tracking/CommandStatus.cs new file mode 100644 index 000000000..26208e9dd --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/Tracking/CommandStatus.cs @@ -0,0 +1,10 @@ +namespace NzbDrone.Core.Messaging.Commands.Tracking +{ + public enum CommandStatus + { + Pending, + Running, + Completed, + Failed + } +} diff --git a/NzbDrone.Core/Messaging/Commands/Tracking/CommandTrackingService.cs b/NzbDrone.Core/Messaging/Commands/Tracking/CommandTrackingService.cs new file mode 100644 index 000000000..3e5a6c619 --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/Tracking/CommandTrackingService.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Common.Cache; + +namespace NzbDrone.Core.Messaging.Commands.Tracking +{ + public interface ITrackCommands + { + Command GetById(int id); + Command GetById(string id); + void Completed(Command trackedCommand); + void Failed(Command trackedCommand, Exception e); + IEnumerable RunningCommands(); + Command FindExisting(Command command); + void Store(Command command); + void Start(Command command); + } + + public class CommandTrackingService : ITrackCommands, IExecute + { + private readonly ICached _cache; + + public CommandTrackingService(ICacheManger cacheManger) + { + _cache = cacheManger.GetCache(GetType()); + } + + public Command GetById(int id) + { + return _cache.Find(id.ToString()); + } + + public Command GetById(string id) + { + return _cache.Find(id); + } + + public void Start(Command command) + { + command.Start(); + } + + public void Completed(Command trackedCommand) + { + trackedCommand.Completed(); + } + + public void Failed(Command trackedCommand, Exception e) + { + trackedCommand.Failed(e); + } + + public IEnumerable RunningCommands() + { + return _cache.Values.Where(c => c.State == CommandStatus.Running); + } + + public Command FindExisting(Command command) + { + return RunningCommands().Where(c => c.GetType() == command.GetType()) + .SingleOrDefault(t => CommandEqualityComparer.Instance.Equals(t, command)); + } + + public void Store(Command command) + { + if (command.GetType() == typeof(TrackedCommandCleanupCommand)) + { + return; + } + + _cache.Set(command.Id.ToString(), command); + } + + public void Execute(TrackedCommandCleanupCommand message) + { + var old = _cache.Values.Where(c => c.State != CommandStatus.Running && c.StateChangeTime < DateTime.UtcNow.AddMinutes(-5)); + + foreach (var trackedCommand in old) + { + _cache.Remove(trackedCommand.Id.ToString()); + } + } + } +} diff --git a/NzbDrone.Core/Messaging/Commands/Tracking/ExistingCommand.cs b/NzbDrone.Core/Messaging/Commands/Tracking/ExistingCommand.cs new file mode 100644 index 000000000..4fa174d3c --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/Tracking/ExistingCommand.cs @@ -0,0 +1,16 @@ +using System; + +namespace NzbDrone.Core.Messaging.Commands.Tracking +{ + public class ExistingCommand + { + public Boolean Existing { get; set; } + public Command Command { get; set; } + + public ExistingCommand(Boolean exisitng, Command trackedCommand) + { + Existing = exisitng; + Command = trackedCommand; + } + } +} diff --git a/NzbDrone.Core/Messaging/Commands/Tracking/TrackedCommandCleanupCommand.cs b/NzbDrone.Core/Messaging/Commands/Tracking/TrackedCommandCleanupCommand.cs new file mode 100644 index 000000000..744b8a60f --- /dev/null +++ b/NzbDrone.Core/Messaging/Commands/Tracking/TrackedCommandCleanupCommand.cs @@ -0,0 +1,7 @@ +namespace NzbDrone.Core.Messaging.Commands.Tracking +{ + public class TrackedCommandCleanupCommand : Command + { + + } +} diff --git a/NzbDrone.Core/Messaging/Events/CommandCreatedEvent.cs b/NzbDrone.Core/Messaging/Events/CommandCreatedEvent.cs new file mode 100644 index 000000000..5224e2671 --- /dev/null +++ b/NzbDrone.Core/Messaging/Events/CommandCreatedEvent.cs @@ -0,0 +1,15 @@ +using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Messaging.Events +{ + public class CommandCreatedEvent : IEvent + { + public Command Command { get; private set; } + + public CommandCreatedEvent(Command trackedCommand) + { + Command = trackedCommand; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Messaging/Events/CommandExecutedEvent.cs b/NzbDrone.Core/Messaging/Events/CommandExecutedEvent.cs new file mode 100644 index 000000000..83fb2abdb --- /dev/null +++ b/NzbDrone.Core/Messaging/Events/CommandExecutedEvent.cs @@ -0,0 +1,15 @@ +using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Messaging.Events +{ + public class CommandExecutedEvent : IEvent + { + public Command Command { get; private set; } + + public CommandExecutedEvent(Command trackedCommand) + { + Command = trackedCommand; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/MessageAggregator.cs b/NzbDrone.Core/Messaging/Events/EventAggregator.cs similarity index 50% rename from NzbDrone.Common/Messaging/MessageAggregator.cs rename to NzbDrone.Core/Messaging/Events/EventAggregator.cs index 725f84ccf..7dbe1a331 100644 --- a/NzbDrone.Common/Messaging/MessageAggregator.cs +++ b/NzbDrone.Core/Messaging/Events/EventAggregator.cs @@ -1,25 +1,25 @@ -using System; -using System.Diagnostics; -using System.Linq; +using System; using System.Threading.Tasks; using NLog; +using NzbDrone.Common; using NzbDrone.Common.EnsureThat; -using NzbDrone.Common.Serializer; +using NzbDrone.Common.Messaging; using NzbDrone.Common.TPL; -namespace NzbDrone.Common.Messaging +namespace NzbDrone.Core.Messaging.Events { - public class MessageAggregator : IMessageAggregator + public class EventAggregator : IEventAggregator { private readonly Logger _logger; private readonly IServiceFactory _serviceFactory; private readonly TaskFactory _taskFactory; - public MessageAggregator(Logger logger, IServiceFactory serviceFactory) + public EventAggregator(Logger logger, IServiceFactory serviceFactory) { + var scheduler = new LimitedConcurrencyLevelTaskScheduler(3); + _logger = logger; _serviceFactory = serviceFactory; - var scheduler = new LimitedConcurrencyLevelTaskScheduler(2); _taskFactory = new TaskFactory(scheduler); } @@ -36,9 +36,9 @@ public void PublishEvent(TEvent @event) where TEvent : class ,IEvent { try { - _logger.Debug("{0} -> {1}", eventName, handler.GetType().Name); + _logger.Trace("{0} -> {1}", eventName, handler.GetType().Name); handler.Handle(@event); - _logger.Debug("{0} <- {1}", eventName, handler.GetType().Name); + _logger.Trace("{0} <- {1}", eventName, handler.GetType().Name); } catch (Exception e) { @@ -52,15 +52,14 @@ public void PublishEvent(TEvent @event) where TEvent : class ,IEvent _taskFactory.StartNew(() => { - _logger.Debug("{0} ~> {1}", eventName, handlerLocal.GetType().Name); + _logger.Trace("{0} ~> {1}", eventName, handlerLocal.GetType().Name); handlerLocal.HandleAsync(@event); - _logger.Debug("{0} <~ {1}", eventName, handlerLocal.GetType().Name); + _logger.Trace("{0} <~ {1}", eventName, handlerLocal.GetType().Name); }, TaskCreationOptions.PreferFairness) .LogExceptions(); } } - private static string GetEventName(Type eventType) { if (!eventType.IsGenericType) @@ -70,48 +69,5 @@ private static string GetEventName(Type eventType) return string.Format("{0}<{1}>", eventType.Name.Remove(eventType.Name.IndexOf('`')), eventType.GetGenericArguments()[0].Name); } - - - public void PublishCommand(TCommand command) where TCommand : class, ICommand - { - Ensure.That(() => command).IsNotNull(); - - var handlerContract = typeof(IExecute<>).MakeGenericType(command.GetType()); - - _logger.Trace("Publishing {0}", command.GetType().Name); - - var handler = (IExecute)_serviceFactory.Build(handlerContract); - - _logger.Debug("{0} -> {1}", command.GetType().Name, handler.GetType().Name); - - var sw = Stopwatch.StartNew(); - - try - { - handler.Execute(command); - sw.Stop(); - PublishEvent(new CommandCompletedEvent(command)); - } - catch (Exception e) - { - PublishEvent(new CommandFailedEvent(command, e)); - throw; - } - finally - { - PublishEvent(new CommandExecutedEvent(command)); - } - - _logger.Debug("{0} <- {1} [{2}]", command.GetType().Name, handler.GetType().Name, sw.Elapsed.ToString("")); - } - - public void PublishCommand(string commandTypeName) - { - var commandType = _serviceFactory.GetImplementations(typeof(ICommand)) - .Single(c => c.FullName.Equals(commandTypeName, StringComparison.InvariantCultureIgnoreCase)); - - dynamic command = Json.Deserialize("{}", commandType); - PublishCommand(command); - } } } diff --git a/NzbDrone.Core/Messaging/Events/IEventAggregator.cs b/NzbDrone.Core/Messaging/Events/IEventAggregator.cs new file mode 100644 index 000000000..9df32d66d --- /dev/null +++ b/NzbDrone.Core/Messaging/Events/IEventAggregator.cs @@ -0,0 +1,9 @@ +using NzbDrone.Common.Messaging; + +namespace NzbDrone.Core.Messaging.Events +{ + public interface IEventAggregator + { + void PublishEvent(TEvent @event) where TEvent : class, IEvent; + } +} \ No newline at end of file diff --git a/NzbDrone.Common/Messaging/IHandle.cs b/NzbDrone.Core/Messaging/Events/IHandle.cs similarity index 79% rename from NzbDrone.Common/Messaging/IHandle.cs rename to NzbDrone.Core/Messaging/Events/IHandle.cs index ae40448d4..291e099d2 100644 --- a/NzbDrone.Common/Messaging/IHandle.cs +++ b/NzbDrone.Core/Messaging/Events/IHandle.cs @@ -1,4 +1,6 @@ -namespace NzbDrone.Common.Messaging +using NzbDrone.Common.Messaging; + +namespace NzbDrone.Core.Messaging.Events { public interface IHandle : IProcessMessage where TEvent : IEvent { diff --git a/NzbDrone.Common/Messaging/IProcessMessage.cs b/NzbDrone.Core/Messaging/IProcessMessage.cs similarity index 86% rename from NzbDrone.Common/Messaging/IProcessMessage.cs rename to NzbDrone.Core/Messaging/IProcessMessage.cs index b008d9e9b..2207e803d 100644 --- a/NzbDrone.Common/Messaging/IProcessMessage.cs +++ b/NzbDrone.Core/Messaging/IProcessMessage.cs @@ -1,10 +1,9 @@ -namespace NzbDrone.Common.Messaging +namespace NzbDrone.Core.Messaging { public interface IProcessMessage { } public interface IProcessMessageAsync : IProcessMessage { } - public interface IProcessMessage : IProcessMessage { } public interface IProcessMessageAsync : IProcessMessageAsync { } diff --git a/NzbDrone.Core/MetadataSource/Trakt/TraktCommunicationException.cs b/NzbDrone.Core/MetadataSource/Trakt/TraktCommunicationException.cs deleted file mode 100644 index d09dad66c..000000000 --- a/NzbDrone.Core/MetadataSource/Trakt/TraktCommunicationException.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace NzbDrone.Core.MetadataSource.Trakt -{ - public class TraktCommunicationException : Exception - { - public TraktCommunicationException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/NzbDrone.Core/MetadataSource/Trakt/TraktException.cs b/NzbDrone.Core/MetadataSource/Trakt/TraktException.cs new file mode 100644 index 000000000..999122301 --- /dev/null +++ b/NzbDrone.Core/MetadataSource/Trakt/TraktException.cs @@ -0,0 +1,15 @@ +using System.Net; + +namespace NzbDrone.Core.MetadataSource.Trakt +{ + public class TraktException : NzbDroneClientException + { + public TraktException(string message) : base(HttpStatusCode.ServiceUnavailable, message) + { + } + + public TraktException(string message, params object[] args) : base(HttpStatusCode.ServiceUnavailable, message, args) + { + } + } +} diff --git a/NzbDrone.Core/MetadataSource/TraktProxy.cs b/NzbDrone.Core/MetadataSource/TraktProxy.cs index 2c18b5594..f37cc8d6b 100644 --- a/NzbDrone.Core/MetadataSource/TraktProxy.cs +++ b/NzbDrone.Core/MetadataSource/TraktProxy.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Text.RegularExpressions; +using NLog; using NzbDrone.Common; using NzbDrone.Core.MediaCover; using NzbDrone.Core.MetadataSource.Trakt; @@ -15,26 +17,33 @@ namespace NzbDrone.Core.MetadataSource { public class TraktProxy : ISearchForNewSeries, IProvideSeriesInfo { - public List SearchForNewSeries(string title) - { - var client = BuildClient("search", "shows"); - var restRequest = new RestRequest(GetSearchTerm(title)); - var response = client.ExecuteAndValidate>(restRequest); - - return response.Select(MapSearchSeries).ToList(); - } - - + private readonly Logger _logger; private static readonly Regex InvalidSearchCharRegex = new Regex(@"[^a-zA-Z0-9\s-\.]", RegexOptions.Compiled); - private static string GetSearchTerm(string phrase) + public TraktProxy(Logger logger) { - phrase = phrase.RemoveAccent().ToLower(); - phrase = phrase.Replace("&", "and"); - phrase = InvalidSearchCharRegex.Replace(phrase, string.Empty); - phrase = phrase.CleanSpaces().Replace(" ", "+"); + _logger = logger; + } - return phrase; + public List SearchForNewSeries(string title) + { + try + { + var client = BuildClient("search", "shows"); + var restRequest = new RestRequest(GetSearchTerm(title) +"/30/seasons"); + var response = client.ExecuteAndValidate>(restRequest); + + return response.Select(MapSeries).ToList(); + } + catch (WebException ex) + { + throw new TraktException("Search for '{0}' failed. Unable to communicate with Trakt.", title); + } + catch (Exception ex) + { + _logger.WarnException(ex.Message, ex); + throw new TraktException("Search for '{0}' failed. Invalid response received from Trakt.", title); + } } public Tuple> GetSeriesInfo(int tvDbSeriesId) @@ -43,7 +52,6 @@ public Tuple> GetSeriesInfo(int tvDbSeriesId) var restRequest = new RestRequest(tvDbSeriesId.ToString() + "/extended"); var response = client.ExecuteAndValidate(restRequest); - var episodes = response.seasons.SelectMany(c => c.episodes).Select(MapEpisode).ToList(); var series = MapSeries(response); @@ -63,6 +71,7 @@ private static Series MapSeries(Show show) series.ImdbId = show.imdb_id; series.Title = show.title; series.CleanTitle = Parser.Parser.CleanSeriesTitle(show.title); + series.Year = show.year; series.FirstAired = FromIso(show.first_aired_iso); series.Overview = show.overview; series.Runtime = show.runtime; @@ -71,6 +80,10 @@ private static Series MapSeries(Show show) series.TitleSlug = show.url.ToLower().Replace("http://trakt.tv/show/", ""); series.Status = GetSeriesStatus(show.status); + series.Seasons = show.seasons.Select(s => new Tv.Season + { + SeasonNumber = s.season + }).OrderByDescending(s => s.SeasonNumber).ToList(); series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Banner, Url = show.images.banner }); series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Poster, Url = GetPosterThumbnailUrl(show.images.poster) }); series.Images.Add(new MediaCover.MediaCover { CoverType = MediaCoverTypes.Fanart, Url = show.images.fanart }); @@ -141,6 +154,12 @@ private static SeriesStatusType GetSeriesStatus(string status) { DateTime result; + //Todo: Remove this when DST ends and/or trakt fixes DST airings in EST/EDT + if (iso != null && iso.EndsWith("-05:00")) + { + iso = iso.Replace("-05:00", "-04:00"); + } + if (!DateTime.TryParse(iso, out result)) return null; @@ -158,6 +177,14 @@ private static string FromIsoToString(string iso) return match.Captures[0].Value; } + private static string GetSearchTerm(string phrase) + { + phrase = phrase.RemoveAccent().ToLower(); + phrase = phrase.Replace("&", "and"); + phrase = InvalidSearchCharRegex.Replace(phrase, string.Empty); + phrase = phrase.CleanSpaces().Replace(" ", "+"); + return phrase; + } } -} \ No newline at end of file +} diff --git a/NzbDrone.Core/Notifications/Email/EmailService.cs b/NzbDrone.Core/Notifications/Email/EmailService.cs index 38c37086d..5625a9785 100644 --- a/NzbDrone.Core/Notifications/Email/EmailService.cs +++ b/NzbDrone.Core/Notifications/Email/EmailService.cs @@ -2,7 +2,8 @@ using System.Net; using System.Net.Mail; using NLog; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using Omu.ValueInjecter; namespace NzbDrone.Core.Notifications.Email diff --git a/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs b/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs index 258884788..3d9ec3797 100644 --- a/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs +++ b/NzbDrone.Core/Notifications/Email/TestEmailCommand.cs @@ -1,9 +1,17 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Email { - public class TestEmailCommand : ICommand + public class TestEmailCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string Server { get; set; } public int Port { get; set; } public bool Ssl { get; set; } diff --git a/NzbDrone.Core/Notifications/Growl/GrowlService.cs b/NzbDrone.Core/Notifications/Growl/GrowlService.cs index deed4b369..5ec00df7c 100644 --- a/NzbDrone.Core/Notifications/Growl/GrowlService.cs +++ b/NzbDrone.Core/Notifications/Growl/GrowlService.cs @@ -3,7 +3,9 @@ using System.Linq; using Growl.Connector; using NLog; -using NzbDrone.Common.Messaging; +using NzbDrone.Common.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using GrowlNotification = Growl.Connector.Notification; namespace NzbDrone.Core.Notifications.Growl @@ -15,7 +17,7 @@ public interface IGrowlService public class GrowlService : IGrowlService, IExecute { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); private readonly Application _growlApplication = new Application("NzbDrone"); private GrowlConnector _growlConnector; @@ -24,7 +26,7 @@ public class GrowlService : IGrowlService, IExecute public GrowlService() { _notificationTypes = GetNotificationTypes(); - _growlApplication.Icon = "https://github.com/NzbDrone/NzbDrone/raw/master/NzbDrone.Core/NzbDrone.jpg"; + _growlApplication.Icon = "https://raw.github.com/NzbDrone/NzbDrone/master/Logo/64.png"; } public void SendNotification(string title, string message, string notificationTypeName, string hostname, int port, string password) diff --git a/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs b/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs index 35890fff9..c1d880541 100644 --- a/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs +++ b/NzbDrone.Core/Notifications/Growl/TestGrowlCommand.cs @@ -1,9 +1,16 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Growl { - public class TestGrowlCommand : ICommand + public class TestGrowlCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string Host { get; set; } public int Port { get; set; } public string Password { get; set; } diff --git a/NzbDrone.Core/Notifications/NotificationRepository.cs b/NzbDrone.Core/Notifications/NotificationRepository.cs index f9f9feb0d..79dedcf34 100644 --- a/NzbDrone.Core/Notifications/NotificationRepository.cs +++ b/NzbDrone.Core/Notifications/NotificationRepository.cs @@ -1,7 +1,9 @@ using System; using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Notifications { @@ -13,8 +15,8 @@ public interface INotificationRepository : IBasicRepository, INotificationRepository { - public NotificationRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public NotificationRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/Notifications/NotificationService.cs b/NzbDrone.Core/Notifications/NotificationService.cs index d48398898..366129433 100644 --- a/NzbDrone.Core/Notifications/NotificationService.cs +++ b/NzbDrone.Core/Notifications/NotificationService.cs @@ -3,10 +3,11 @@ using System.Linq; using NLog; using NzbDrone.Common.Composition; -using NzbDrone.Common.Messaging; using NzbDrone.Common.Serializer; using NzbDrone.Core.Download; using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; using Omu.ValueInjecter; diff --git a/NzbDrone.Core/Notifications/Plex/PlexService.cs b/NzbDrone.Core/Notifications/Plex/PlexService.cs index 15e01420c..5a5bc82e3 100644 --- a/NzbDrone.Core/Notifications/Plex/PlexService.cs +++ b/NzbDrone.Core/Notifications/Plex/PlexService.cs @@ -4,7 +4,8 @@ using System.Xml.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Plex { diff --git a/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs b/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs index 6df162ab4..6de213789 100644 --- a/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs +++ b/NzbDrone.Core/Notifications/Plex/TestPlexClientCommand.cs @@ -1,9 +1,16 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Plex { - public class TestPlexClientCommand : ICommand + public class TestPlexClientCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string Host { get; set; } public int Port { get; set; } public string Username { get; set; } diff --git a/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs b/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs index 49089afea..19fac0641 100644 --- a/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs +++ b/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs @@ -1,9 +1,17 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Plex { - public class TestPlexServerCommand : ICommand + public class TestPlexServerCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string Host { get; set; } public int Port { get; set; } } diff --git a/NzbDrone.Core/Notifications/Prowl/ProwlService.cs b/NzbDrone.Core/Notifications/Prowl/ProwlService.cs index bf850215f..e8c080f75 100644 --- a/NzbDrone.Core/Notifications/Prowl/ProwlService.cs +++ b/NzbDrone.Core/Notifications/Prowl/ProwlService.cs @@ -1,6 +1,7 @@ using System; using NLog; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using Prowlin; namespace NzbDrone.Core.Notifications.Prowl diff --git a/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs b/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs index e58bf5a9c..50d02677e 100644 --- a/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs +++ b/NzbDrone.Core/Notifications/Prowl/TestProwlCommand.cs @@ -1,9 +1,16 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Prowl { - public class TestProwlCommand : ICommand + public class TestProwlCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string ApiKey { get; set; } public int Priority { get; set; } } diff --git a/NzbDrone.Core/Notifications/Pushover/PushoverService.cs b/NzbDrone.Core/Notifications/Pushover/PushoverService.cs index cab9639bb..7e5725711 100644 --- a/NzbDrone.Core/Notifications/Pushover/PushoverService.cs +++ b/NzbDrone.Core/Notifications/Pushover/PushoverService.cs @@ -1,4 +1,5 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using RestSharp; using NzbDrone.Core.Rest; diff --git a/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs b/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs index 31bc034f9..13e232401 100644 --- a/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs +++ b/NzbDrone.Core/Notifications/Pushover/TestPushoverCommand.cs @@ -1,9 +1,17 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Pushover { - public class TestPushoverCommand : ICommand + public class TestPushoverCommand : Command { + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } public string UserKey { get; set; } public int Priority { get; set; } } diff --git a/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs b/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs index af5fe2366..1d7cb5377 100644 --- a/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs +++ b/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs @@ -26,7 +26,7 @@ public void Notify(XbmcSettings settings, string title, string message) var parameters = new JObject( new JProperty("title", title), new JProperty("message", message), - new JProperty("image", "https://raw.github.com/NzbDrone/NzbDrone/develop/Logo/64.png"), + new JProperty("image", "https://raw.github.com/NzbDrone/NzbDrone/master/Logo/64.png"), new JProperty("displaytime", settings.DisplayTime * 1000)); var postJson = BuildJsonRequest("GUI.ShowNotification", parameters); diff --git a/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs b/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs index 56eb75ee8..cb8acb361 100644 --- a/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs +++ b/NzbDrone.Core/Notifications/Xbmc/TestXbmcCommand.cs @@ -1,9 +1,17 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Notifications.Xbmc { - public class TestXbmcCommand : ICommand + public class TestXbmcCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string Host { get; set; } public int Port { get; set; } public string Username { get; set; } diff --git a/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs b/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs index cc0f9bf3c..bbc8a237a 100644 --- a/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs +++ b/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs @@ -5,7 +5,9 @@ using Newtonsoft.Json.Linq; using NLog; using NzbDrone.Common; -using NzbDrone.Common.Messaging; +using NzbDrone.Common.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Tv; using NzbDrone.Core.Model.Xbmc; @@ -21,7 +23,7 @@ public interface IXbmcService public class XbmcService : IXbmcService, IExecute { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); private readonly IHttpProvider _httpProvider; private readonly IEnumerable _apiProviders; diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 793db0f0a..dae681ba9 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -123,6 +123,7 @@ + @@ -130,7 +131,6 @@ - @@ -160,6 +160,10 @@ + + + + @@ -169,6 +173,8 @@ + + @@ -177,10 +183,13 @@ - + + + + @@ -207,10 +216,14 @@ + + + + @@ -219,6 +232,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -244,7 +283,6 @@ - @@ -262,7 +300,7 @@ - + @@ -271,8 +309,6 @@ - - @@ -341,7 +377,8 @@ - + + @@ -385,10 +422,8 @@ - - @@ -533,9 +568,6 @@ - - - {F6FC6BE7-0847-4817-A1ED-223DC647C3D7} diff --git a/NzbDrone.Core/NzbDrone.Core.ncrunchproject b/NzbDrone.Core/NzbDrone.Core.ncrunchproject index 73e1ebbb9..b2eed192e 100644 --- a/NzbDrone.Core/NzbDrone.Core.ncrunchproject +++ b/NzbDrone.Core/NzbDrone.Core.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,10 +12,12 @@ false true true - 60000 - - + 5000 + Debug + x86 - AutoDetect + x86 + STA + x86 PostBuildEventDisabled \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.jpg b/NzbDrone.Core/NzbDrone.jpg deleted file mode 100644 index 4e544b00f..000000000 Binary files a/NzbDrone.Core/NzbDrone.jpg and /dev/null differ diff --git a/NzbDrone.Core/NzbDroneClientException.cs b/NzbDrone.Core/NzbDroneClientException.cs new file mode 100644 index 000000000..cc94caffc --- /dev/null +++ b/NzbDrone.Core/NzbDroneClientException.cs @@ -0,0 +1,21 @@ +using System.Net; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core +{ + public class NzbDroneClientException : NzbDroneException + { + public HttpStatusCode StatusCode { get; private set; } + + public NzbDroneClientException(HttpStatusCode statusCode, string message, params object[] args) : base(message, args) + { + StatusCode = statusCode; + } + + public NzbDroneClientException(HttpStatusCode statusCode, string message) + : base(message) + { + StatusCode = statusCode; + } + } +} diff --git a/NzbDrone.Core/Parser/Model/LocalEpisode.cs b/NzbDrone.Core/Parser/Model/LocalEpisode.cs index 3936ca4b7..06f620a96 100644 --- a/NzbDrone.Core/Parser/Model/LocalEpisode.cs +++ b/NzbDrone.Core/Parser/Model/LocalEpisode.cs @@ -7,12 +7,13 @@ namespace NzbDrone.Core.Parser.Model { public class LocalEpisode { - public string Path { get; set; } + public String Path { get; set; } public Int64 Size { get; set; } public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } public Series Series { get; set; } public List Episodes { get; set; } public QualityModel Quality { get; set; } + public Boolean ExistingFile { get; set; } public int SeasonNumber { diff --git a/NzbDrone.Core/Parser/Model/ReleaseInfo.cs b/NzbDrone.Core/Parser/Model/ReleaseInfo.cs new file mode 100644 index 000000000..e5b3d9ed8 --- /dev/null +++ b/NzbDrone.Core/Parser/Model/ReleaseInfo.cs @@ -0,0 +1,27 @@ +using System; + +namespace NzbDrone.Core.Parser.Model +{ + public class ReleaseInfo + { + public string Title { get; set; } + public long Size { get; set; } + public string DownloadUrl { get; set; } + public string InfoUrl { get; set; } + public string CommentUrl { get; set; } + public String Indexer { get; set; } + + public DateTime PublishDate { get; set; } + + public int Age + { + get + { + return DateTime.UtcNow.Subtract(PublishDate).Days; + } + } + + public string ReleaseGroup { get; set; } + public int TvRageId { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Parser/Model/RemoteEpisode.cs b/NzbDrone.Core/Parser/Model/RemoteEpisode.cs index 70b001511..ac394d0d8 100644 --- a/NzbDrone.Core/Parser/Model/RemoteEpisode.cs +++ b/NzbDrone.Core/Parser/Model/RemoteEpisode.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.Parser.Model { public class RemoteEpisode { - public ReportInfo Report { get; set; } + public ReleaseInfo Release { get; set; } public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } @@ -22,7 +22,7 @@ public bool IsRecentEpisode() public override string ToString() { - return Report.Title; + return Release.Title; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Parser/Model/ReportInfo.cs b/NzbDrone.Core/Parser/Model/ReportInfo.cs deleted file mode 100644 index d357d7d66..000000000 --- a/NzbDrone.Core/Parser/Model/ReportInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace NzbDrone.Core.Parser.Model -{ - public class ReportInfo - { - public string Title { get; set; } - public long Size { get; set; } - public string NzbUrl { get; set; } - public string NzbInfoUrl { get; set; } - public String Indexer { get; set; } - public int Age { get; set; } - public string ReleaseGroup { get; set; } - public int TvRageId { get; set; } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Parser/Model/TorrentInfo.cs b/NzbDrone.Core/Parser/Model/TorrentInfo.cs new file mode 100644 index 000000000..434967b2b --- /dev/null +++ b/NzbDrone.Core/Parser/Model/TorrentInfo.cs @@ -0,0 +1,8 @@ +namespace NzbDrone.Core.Parser.Model +{ + public class TorrentInfo : ReleaseInfo + { + public string MagnetUrl { get; set; } + public string InfoHash { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Parser/Parser.cs b/NzbDrone.Core/Parser/Parser.cs index 3178b5084..85ef1be7c 100644 --- a/NzbDrone.Core/Parser/Parser.cs +++ b/NzbDrone.Core/Parser/Parser.cs @@ -4,16 +4,14 @@ using System.Linq; using System.Text.RegularExpressions; using NLog; -using NzbDrone.Common; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; namespace NzbDrone.Core.Parser { public static class Parser { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); private static readonly Regex[] ReportTitleRegex = new[] { diff --git a/NzbDrone.Core/Parser/ParsingService.cs b/NzbDrone.Core/Parser/ParsingService.cs index 44113b8a4..b678c5eeb 100644 --- a/NzbDrone.Core/Parser/ParsingService.cs +++ b/NzbDrone.Core/Parser/ParsingService.cs @@ -1,6 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Common; +using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -8,30 +12,33 @@ namespace NzbDrone.Core.Parser { public interface IParsingService { - LocalEpisode GetEpisodes(string filename, Series series); LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource); Series GetSeries(string title); - RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId); + RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null); + List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null); } public class ParsingService : IParsingService { private readonly IEpisodeService _episodeService; private readonly ISeriesService _seriesService; + private readonly IDiskProvider _diskProvider; + private readonly ISceneMappingService _sceneMappingService; private readonly Logger _logger; - public ParsingService(IEpisodeService episodeService, ISeriesService seriesService, Logger logger) + public ParsingService(IEpisodeService episodeService, + ISeriesService seriesService, + IDiskProvider diskProvider, + ISceneMappingService sceneMappingService, + Logger logger) { _episodeService = episodeService; _seriesService = seriesService; + _diskProvider = diskProvider; + _sceneMappingService = sceneMappingService; _logger = logger; } - public LocalEpisode GetEpisodes(string filename, Series series) - { - return GetEpisodes(filename, series, false); - } - public LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource) { var parsedEpisodeInfo = Parser.ParsePath(filename); @@ -54,14 +61,14 @@ public LocalEpisode GetEpisodes(string filename, Series series, bool sceneSource Quality = parsedEpisodeInfo.Quality, Episodes = episodes, Path = filename, - ParsedEpisodeInfo = parsedEpisodeInfo + ParsedEpisodeInfo = parsedEpisodeInfo, + ExistingFile = _diskProvider.IsParent(series.Path, filename) }; } public Series GetSeries(string title) { var searchTitle = title; - var parsedEpisodeInfo = Parser.ParseTitle(title); if (parsedEpisodeInfo != null) @@ -72,34 +79,28 @@ public Series GetSeries(string title) return _seriesService.FindByTitle(searchTitle); } - public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId) + public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null) { var remoteEpisode = new RemoteEpisode { ParsedEpisodeInfo = parsedEpisodeInfo, }; - var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle); - - if (series == null && tvRageId > 0) - { - series = _seriesService.FindByTvRageId(tvRageId); - //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import - } + var series = searchCriteria == null ? GetSeries(parsedEpisodeInfo, tvRageId) : + GetSeries(parsedEpisodeInfo, tvRageId, searchCriteria); if (series == null) { - _logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle); return remoteEpisode; } remoteEpisode.Series = series; - remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, true); + remoteEpisode.Episodes = GetEpisodes(parsedEpisodeInfo, series, true, searchCriteria); return remoteEpisode; } - private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource) + public List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null) { var result = new List(); @@ -108,10 +109,10 @@ private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series se if (series.SeriesType == SeriesTypes.Standard) { _logger.Warn("Found daily-style episode for non-daily series: {0}.", series); - return new List(); + return null; } - - var episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.AirDate.Value); + + var episodeInfo = GetDailyEpisode(series, parsedEpisodeInfo.AirDate.Value, searchCriteria); if (episodeInfo != null) { @@ -130,7 +131,16 @@ private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series se if (series.UseSceneNumbering && sceneSource) { - episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber, true); + if (searchCriteria != null) + { + episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SceneSeasonNumber == parsedEpisodeInfo.SeasonNumber && + e.SceneEpisodeNumber == episodeNumber); + } + + if (episodeInfo == null) + { + episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber, true); + } if (episodeInfo != null) { @@ -143,15 +153,22 @@ private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series se } } + if (episodeInfo == null && searchCriteria != null) + { + episodeInfo = searchCriteria.Episodes.SingleOrDefault(e => e.SeasonNumber == parsedEpisodeInfo.SeasonNumber && + e.EpisodeNumber == episodeNumber); + } + if (episodeInfo == null) { - episodeInfo = _episodeService.GetEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); + episodeInfo = _episodeService.FindEpisode(series.Id, parsedEpisodeInfo.SeasonNumber, episodeNumber); } if (episodeInfo != null) { result.Add(episodeInfo); } + else { _logger.Debug("Unable to find {0}", parsedEpisodeInfo); @@ -160,5 +177,68 @@ private List GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series se return result; } + + private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria) + { + var tvdbId = _sceneMappingService.GetTvDbId(parsedEpisodeInfo.SeriesTitle); + + if (tvdbId.HasValue) + { + if (searchCriteria.Series.TvdbId == tvdbId) + { + return searchCriteria.Series; + } + } + + if (parsedEpisodeInfo.SeriesTitle.CleanSeriesTitle() == searchCriteria.Series.CleanTitle) + { + return searchCriteria.Series; + } + + if (tvRageId == searchCriteria.Series.TvRageId) + { + //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import + return searchCriteria.Series; + } + + return GetSeries(parsedEpisodeInfo, tvRageId); + } + + private Series GetSeries(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId) + { + var series = _seriesService.FindByTitle(parsedEpisodeInfo.SeriesTitle); + + if (series == null && tvRageId > 0) + { + //TODO: If series is found by TvRageId, we should report it as a scene naming exception, since it will fail to import + series = _seriesService.FindByTvRageId(tvRageId); + } + + if (series == null) + { + _logger.Trace("No matching series {0}", parsedEpisodeInfo.SeriesTitle); + return null; + } + + return series; + } + + private Episode GetDailyEpisode(Series series, DateTime airDate, SearchCriteriaBase searchCriteria) + { + Episode episodeInfo = null; + + if (searchCriteria != null) + { + episodeInfo = searchCriteria.Episodes.SingleOrDefault( + e => e.AirDate == airDate.ToString(Episode.AIR_DATE_FORMAT)); + } + + if (episodeInfo == null) + { + episodeInfo = _episodeService.FindEpisode(series.Id, airDate); + } + + return episodeInfo; + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Parser/QualityParser.cs b/NzbDrone.Core/Parser/QualityParser.cs index 8cd3077e2..bbfd7ed3b 100644 --- a/NzbDrone.Core/Parser/QualityParser.cs +++ b/NzbDrone.Core/Parser/QualityParser.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; using System.Text.RegularExpressions; using NLog; using NzbDrone.Common; +using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Qualities; using NzbDrone.Core.Tv; @@ -152,33 +150,7 @@ public static QualityModel ParseQuality(string name) { try { - switch (Path.GetExtension(name).ToLower()) - { - case ".avi": - case ".xvid": - case ".divx": - case ".wmv": - case ".mp4": - case ".mpg": - case ".mpeg": - case ".mov": - case ".rm": - case ".rmvb": - case ".flv": - case ".dvr-ms": - case ".ogm": - case ".strm": - { - result.Quality = Quality.SDTV; - break; - } - case ".mkv": - case ".ts": - { - result.Quality = Quality.HDTV720p; - break; - } - } + result.Quality = MediaFileExtensions.GetQualityForExtension(Path.GetExtension(name)); } catch (ArgumentException) { diff --git a/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs b/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs new file mode 100644 index 000000000..49c20b0b2 --- /dev/null +++ b/NzbDrone.Core/ProgressMessaging/ProgressMessageTarget.cs @@ -0,0 +1,68 @@ +using NLog.Config; +using NLog; +using NLog.Targets; +using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Commands.Tracking; +using NzbDrone.Core.Messaging.Events; + +namespace NzbDrone.Core.ProgressMessaging +{ + + public class ProgressMessageTarget : Target, IHandle + { + private readonly IEventAggregator _eventAggregator; + private readonly ITrackCommands _trackCommands; + private static LoggingRule _rule; + + public ProgressMessageTarget(IEventAggregator eventAggregator, ITrackCommands trackCommands) + { + _eventAggregator = eventAggregator; + _trackCommands = trackCommands; + } + + protected override void Write(LogEventInfo logEvent) + { + var command = GetCurrentCommand(); + + if (IsClientMessage(logEvent, command)) + { + command.SetMessage(logEvent.FormattedMessage); + _eventAggregator.PublishEvent(new CommandUpdatedEvent(command)); + } + } + + + private Command GetCurrentCommand() + { + var commandId = MappedDiagnosticsContext.Get("CommandId"); + + if (string.IsNullOrWhiteSpace(commandId)) + { + return null; + } + + return _trackCommands.GetById(commandId); + } + + private bool IsClientMessage(LogEventInfo logEvent, Command command) + { + if (command == null || !command.SendUpdatesToClient) + { + return false; + } + + return logEvent.Properties.ContainsKey("Status"); + } + + public void Handle(ApplicationStartedEvent message) + { + _rule = new LoggingRule("*", LogLevel.Trace, this); + + LogManager.Configuration.AddTarget("ProgressMessagingLogger", this); + LogManager.Configuration.LoggingRules.Add(_rule); + LogManager.ReconfigExistingLoggers(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs b/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs index da4b4b53f..a30c0c70d 100644 --- a/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs +++ b/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs @@ -1,10 +1,10 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Providers { - public class UpdateXemMappingsCommand : ICommand + public class UpdateXemMappingsCommand : Command { - public int? SeriesId { get; private set; } + public int? SeriesId { get; set; } public UpdateXemMappingsCommand(int? seriesId) { diff --git a/NzbDrone.Core/Providers/XemCommunicationProvider.cs b/NzbDrone.Core/Providers/XemCommunicationProvider.cs index 803b74e65..5c0e07132 100644 --- a/NzbDrone.Core/Providers/XemCommunicationProvider.cs +++ b/NzbDrone.Core/Providers/XemCommunicationProvider.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NzbDrone.Common; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Model.Xem; namespace NzbDrone.Core.Providers @@ -20,7 +21,7 @@ public class XemCommunicationProvider : IXemCommunicationProvider { private readonly IHttpProvider _httpProvider; - private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger _logger = NzbDroneLogger.GetLogger(); private const string XEM_BASE_URL = "http://thexem.de/map/"; diff --git a/NzbDrone.Core/Providers/XemProvider.cs b/NzbDrone.Core/Providers/XemProvider.cs index 931793dfb..e66000afc 100644 --- a/NzbDrone.Core/Providers/XemProvider.cs +++ b/NzbDrone.Core/Providers/XemProvider.cs @@ -3,8 +3,11 @@ using System.Linq; using NLog; using NzbDrone.Common.Cache; -using NzbDrone.Common.Messaging; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; using NzbDrone.Core.Tv.Events; @@ -25,7 +28,7 @@ public class XemProvider : IXemProvider, IExecute, IHa private readonly ISeriesService _seriesService; private readonly ICached _cache; - private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger _logger = NzbDroneLogger.GetLogger(); public XemProvider(IEpisodeService episodeService, IXemCommunicationProvider xemCommunicationProvider, @@ -131,8 +134,7 @@ public void PerformUpdate(Series series) catch (Exception ex) { - //TODO: We should increase this back to warn when caching is in place - _logger.TraceException("Error updating scene numbering mappings for: " + series, ex); + _logger.ErrorException("Error updating scene numbering mappings for: " + series, ex); } } diff --git a/NzbDrone.Core/Qualities/QualityProfileRepository.cs b/NzbDrone.Core/Qualities/QualityProfileRepository.cs index fcb70f3ff..64e36b70b 100644 --- a/NzbDrone.Core/Qualities/QualityProfileRepository.cs +++ b/NzbDrone.Core/Qualities/QualityProfileRepository.cs @@ -1,5 +1,7 @@ -using NzbDrone.Common.Messaging; -using NzbDrone.Core.Datastore; +using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Qualities { @@ -10,8 +12,8 @@ public interface IQualityProfileRepository : IBasicRepository public class QualityProfileRepository : BasicRepository, IQualityProfileRepository { - public QualityProfileRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public QualityProfileRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } } diff --git a/NzbDrone.Core/Qualities/QualityProfileService.cs b/NzbDrone.Core/Qualities/QualityProfileService.cs index fb96cee99..de79e36e9 100644 --- a/NzbDrone.Core/Qualities/QualityProfileService.cs +++ b/NzbDrone.Core/Qualities/QualityProfileService.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; diff --git a/NzbDrone.Core/Qualities/QualitySizeRepository.cs b/NzbDrone.Core/Qualities/QualitySizeRepository.cs index ef4bb4138..84a21355d 100644 --- a/NzbDrone.Core/Qualities/QualitySizeRepository.cs +++ b/NzbDrone.Core/Qualities/QualitySizeRepository.cs @@ -1,7 +1,9 @@ using System; using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Qualities { @@ -12,8 +14,8 @@ public interface IQualitySizeRepository : IBasicRepository public class QualitySizeRepository : BasicRepository, IQualitySizeRepository { - public QualitySizeRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public QualitySizeRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/Qualities/QualitySizeService.cs b/NzbDrone.Core/Qualities/QualitySizeService.cs index d3dce3199..c7719ef8b 100644 --- a/NzbDrone.Core/Qualities/QualitySizeService.cs +++ b/NzbDrone.Core/Qualities/QualitySizeService.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Lifecycle; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Qualities { diff --git a/NzbDrone.Core/Rest/RestSharpExtensions.cs b/NzbDrone.Core/Rest/RestSharpExtensions.cs index b0c09368c..33b5e681b 100644 --- a/NzbDrone.Core/Rest/RestSharpExtensions.cs +++ b/NzbDrone.Core/Rest/RestSharpExtensions.cs @@ -1,6 +1,7 @@ using System.Net; using NLog; using NzbDrone.Common.EnsureThat; +using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Serializer; using RestSharp; @@ -8,7 +9,7 @@ namespace NzbDrone.Core.Rest { public static class RestSharpExtensions { - private static readonly Logger Logger = LogManager.GetLogger("RestExtensions"); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public static IRestResponse ValidateResponse(this IRestResponse response, IRestClient restClient) { @@ -22,7 +23,7 @@ public static IRestResponse ValidateResponse(this IRestResponse response, IRestC Ensure.That(() => response.Request).IsNotNull(); Ensure.That(() => restClient).IsNotNull(); - Logger.Trace("Validating Responses from [{0}] [{1}] status: [{2}] body:[{3}]", response.Request.Method, restClient.BuildUri(response.Request), response.StatusCode, response.Content); + Logger.Trace("Validating Responses from [{0}] [{1}] status: [{2}]", response.Request.Method, restClient.BuildUri(response.Request), response.StatusCode); if (response.ResponseUri == null) { diff --git a/NzbDrone.Core/RootFolders/RootFolderService.cs b/NzbDrone.Core/RootFolders/RootFolderService.cs index 160cd3c9f..9e441d91d 100644 --- a/NzbDrone.Core/RootFolders/RootFolderService.cs +++ b/NzbDrone.Core/RootFolders/RootFolderService.cs @@ -4,6 +4,7 @@ using System.IO; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.Tv; @@ -23,12 +24,15 @@ public interface IRootFolderService public class RootFolderService : IRootFolderService { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); private readonly IBasicRepository _rootFolderRepository; private readonly IDiskProvider _diskProvider; private readonly ISeriesRepository _seriesRepository; private readonly IConfigService _configService; + private static readonly HashSet SpecialFolders = new HashSet { "$recycle.bin", "system volume information", "recycler" }; + + public RootFolderService(IBasicRepository rootFolderRepository, IDiskProvider diskProvider, ISeriesRepository seriesRepository, @@ -118,7 +122,7 @@ public List GetUnmappedFolders(string path) if (Path.GetPathRoot(path).Equals(path, StringComparison.InvariantCultureIgnoreCase)) { - var setToRemove = _diskProvider.SpecialFolders; + var setToRemove = SpecialFolders; results.RemoveAll(x => setToRemove.Contains(new DirectoryInfo(x.Path.ToLowerInvariant()).Name)); } diff --git a/NzbDrone.Core/SeriesStats/SeriesStatistics.cs b/NzbDrone.Core/SeriesStats/SeriesStatistics.cs index 24893eb26..170985718 100644 --- a/NzbDrone.Core/SeriesStats/SeriesStatistics.cs +++ b/NzbDrone.Core/SeriesStats/SeriesStatistics.cs @@ -6,7 +6,6 @@ namespace NzbDrone.Core.SeriesStats public class SeriesStatistics : ResultSet { public int SeriesId { get; set; } - public int SeasonCount { get; set; } public string NextAiringString { get; set; } public int EpisodeFileCount { get; set; } public int EpisodeCount { get; set; } diff --git a/NzbDrone.Core/SeriesStats/SeriesStatisticsRepository.cs b/NzbDrone.Core/SeriesStats/SeriesStatisticsRepository.cs index eece02c35..09244dc06 100644 --- a/NzbDrone.Core/SeriesStats/SeriesStatisticsRepository.cs +++ b/NzbDrone.Core/SeriesStats/SeriesStatisticsRepository.cs @@ -54,10 +54,9 @@ private string GetSelectClause() { return @"SELECT SeriesId, - SUM(CASE WHEN (Monitored = 1 AND AirdateUtc <= @currentDate) OR Episodes.EpisodeFileId > 0 THEN 1 ELSE 0 END) AS EpisodeCount, - SUM(CASE WHEN Episodes.EpisodeFileId > 0 THEN 1 ELSE 0 END) AS EpisodeFileCount, - MAX(Episodes.SeasonNumber) as SeasonCount, - MIN(CASE WHEN AirDateUtc < @currentDate THEN NULL ELSE AirDateUtc END) AS NextAiringString + SUM(CASE WHEN (Monitored = 1 AND AirdateUtc <= @currentDate) OR EpisodeFileId > 0 THEN 1 ELSE 0 END) AS EpisodeCount, + SUM(CASE WHEN EpisodeFileId > 0 THEN 1 ELSE 0 END) AS EpisodeFileCount, + MIN(CASE WHEN AirDateUtc < @currentDate OR EpisodeFileId > 0 THEN NULL ELSE AirDateUtc END) AS NextAiringString FROM Episodes"; } diff --git a/NzbDrone.Core/Tv/Commands/RefreshSeriesCommand.cs b/NzbDrone.Core/Tv/Commands/RefreshSeriesCommand.cs index f38af963c..8ccc3a2b4 100644 --- a/NzbDrone.Core/Tv/Commands/RefreshSeriesCommand.cs +++ b/NzbDrone.Core/Tv/Commands/RefreshSeriesCommand.cs @@ -1,14 +1,26 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Tv.Commands { - public class RefreshSeriesCommand : ICommand + public class RefreshSeriesCommand : Command { - public int? SeriesId { get; private set; } + public int? SeriesId { get; set; } + + public RefreshSeriesCommand() + { + } public RefreshSeriesCommand(int? seriesId) { SeriesId = seriesId; } + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Tv/EpisodeRepository.cs b/NzbDrone.Core/Tv/EpisodeRepository.cs index 280dec96e..cd12016ed 100644 --- a/NzbDrone.Core/Tv/EpisodeRepository.cs +++ b/NzbDrone.Core/Tv/EpisodeRepository.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using Marr.Data.QGen; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Tv @@ -31,8 +32,8 @@ public class EpisodeRepository : BasicRepository, IEpisodeRepository { private readonly IDatabase _database; - public EpisodeRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public EpisodeRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { _database = database; } diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index dce584fa4..8d3f3ccd2 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; +using NzbDrone.Common.Instrumentation; using NzbDrone.Core.Configuration; using NzbDrone.Core.Datastore; using NzbDrone.Core.MediaFiles.Events; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv.Events; namespace NzbDrone.Core.Tv @@ -40,7 +42,7 @@ public class EpisodeService : IEpisodeService, IHandleAsync { - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger logger = NzbDroneLogger.GetLogger(); private readonly IEpisodeRepository _episodeRepository; private readonly IConfigService _configService; @@ -128,7 +130,7 @@ public void SetEpisodeMonitored(int episodeId, bool monitored) var episode = _episodeRepository.Get(episodeId); _episodeRepository.SetMonitoredFlat(episode, monitored); - logger.Info("Monitored flag for Episode:{0} was set to {1}", episodeId, monitored); + logger.Debug("Monitored flag for Episode:{0} was set to {1}", episodeId, monitored); } public void SetEpisodeMonitoredBySeason(int seriesId, int seasonNumber, bool monitored) diff --git a/NzbDrone.Core/Tv/RefreshEpisodeService.cs b/NzbDrone.Core/Tv/RefreshEpisodeService.cs index c62d8a5d2..5c4beb1b0 100644 --- a/NzbDrone.Core/Tv/RefreshEpisodeService.cs +++ b/NzbDrone.Core/Tv/RefreshEpisodeService.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv.Events; namespace NzbDrone.Core.Tv @@ -15,27 +16,24 @@ public interface IRefreshEpisodeService public class RefreshEpisodeService : IRefreshEpisodeService { private readonly IEpisodeService _episodeService; - private readonly ISeasonService _seasonService; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; - public RefreshEpisodeService(IEpisodeService episodeService, - ISeasonService seasonService, IMessageAggregator messageAggregator, Logger logger) + public RefreshEpisodeService(IEpisodeService episodeService, IEventAggregator eventAggregator, Logger logger) { _episodeService = episodeService; - _seasonService = seasonService; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _logger = logger; } public void RefreshEpisodeInfo(Series series, IEnumerable remoteEpisodes) { - _logger.Info("Starting series info refresh for: {0}", series); + _logger.Info("Starting episode info refresh for: {0}", series); var successCount = 0; var failCount = 0; var existingEpisodes = _episodeService.GetEpisodeBySeries(series.Id); - var seasons = _seasonService.GetSeasonsBySeries(series.Id); + var seasons = series.Seasons; var updateList = new List(); var newList = new List(); @@ -44,7 +42,7 @@ public void RefreshEpisodeInfo(Series series, IEnumerable remoteEpisode { try { - var episodeToUpdate = existingEpisodes.SingleOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber); + var episodeToUpdate = existingEpisodes.FirstOrDefault(e => e.SeasonNumber == episode.SeasonNumber && e.EpisodeNumber == episode.EpisodeNumber); if (episodeToUpdate != null) { @@ -88,17 +86,17 @@ public void RefreshEpisodeInfo(Series series, IEnumerable remoteEpisode if (newList.Any()) { - _messageAggregator.PublishEvent(new EpisodeInfoAddedEvent(newList, series)); + _eventAggregator.PublishEvent(new EpisodeInfoAddedEvent(newList, series)); } if (updateList.Any()) { - _messageAggregator.PublishEvent(new EpisodeInfoUpdatedEvent(updateList)); + _eventAggregator.PublishEvent(new EpisodeInfoUpdatedEvent(updateList)); } if (existingEpisodes.Any()) { - _messageAggregator.PublishEvent(new EpisodeInfoDeletedEvent(updateList)); + _eventAggregator.PublishEvent(new EpisodeInfoDeletedEvent(updateList)); } if (failCount != 0) @@ -114,11 +112,6 @@ public void RefreshEpisodeInfo(Series series, IEnumerable remoteEpisode private static bool GetMonitoredStatus(Episode episode, IEnumerable seasons) { - if (episode.SeasonNumber == 0) - { - return false; - } - if (episode.EpisodeNumber == 0 && episode.SeasonNumber != 1) { return false; @@ -132,7 +125,7 @@ private static void AdjustMultiEpisodeAirTime(Series series, IEnumerable c.AirDateUtc.HasValue) - .GroupBy(e => new { e.SeriesId, e.AirDate }) + .GroupBy(e => new { e.SeasonNumber, e.AirDate }) .Where(g => g.Count() > 1) .ToList(); diff --git a/NzbDrone.Core/Tv/RefreshSeriesService.cs b/NzbDrone.Core/Tv/RefreshSeriesService.cs index bbdb8e472..d6607dda5 100644 --- a/NzbDrone.Core/Tv/RefreshSeriesService.cs +++ b/NzbDrone.Core/Tv/RefreshSeriesService.cs @@ -1,9 +1,13 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using NLog; -using NzbDrone.Common.Messaging; using NzbDrone.Core.DataAugmentation.DailySeries; +using NzbDrone.Core.Instrumentation; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.MetadataSource; using NzbDrone.Core.Tv.Commands; using NzbDrone.Core.Tv.Events; @@ -16,53 +20,23 @@ public class RefreshSeriesService : IExecute, IHandleAsync private readonly IProvideSeriesInfo _seriesInfo; private readonly ISeriesService _seriesService; private readonly IRefreshEpisodeService _refreshEpisodeService; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly IDailySeriesService _dailySeriesService; private readonly Logger _logger; - public RefreshSeriesService(IProvideSeriesInfo seriesInfo, ISeriesService seriesService, IRefreshEpisodeService refreshEpisodeService, IMessageAggregator messageAggregator, IDailySeriesService dailySeriesService, Logger logger) + public RefreshSeriesService(IProvideSeriesInfo seriesInfo, ISeriesService seriesService, IRefreshEpisodeService refreshEpisodeService, IEventAggregator eventAggregator, IDailySeriesService dailySeriesService, Logger logger) { _seriesInfo = seriesInfo; _seriesService = seriesService; _refreshEpisodeService = refreshEpisodeService; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _dailySeriesService = dailySeriesService; _logger = logger; } - - public void Execute(RefreshSeriesCommand message) - { - if (message.SeriesId.HasValue) - { - var series = _seriesService.GetSeries(message.SeriesId.Value); - RefreshSeriesInfo(series); - } - else - { - var allSeries = _seriesService.GetAllSeries().OrderBy(c => c.LastInfoSync).ToList(); - - foreach (var series in allSeries) - { - try - { - RefreshSeriesInfo(series); - } - catch (Exception e) - { - _logger.ErrorException("Couldn't refresh info for {0}".Inject(series), e); - } - } - } - } - - public void HandleAsync(SeriesAddedEvent message) - { - RefreshSeriesInfo(message.Series); - } - private void RefreshSeriesInfo(Series series) { + _logger.ProgressInfo("Updating Info for {0}", series.Title); var tuple = _seriesInfo.GetSeriesInfo(series.TvdbId); var seriesInfo = tuple.Item1; @@ -93,10 +67,65 @@ private void RefreshSeriesInfo(Series series) _logger.WarnException("Couldn't update series path for " + series.Path, e); } + series.Seasons = UpdateSeasons(series, seriesInfo); + _seriesService.UpdateSeries(series); _refreshEpisodeService.RefreshEpisodeInfo(series, tuple.Item2); - _messageAggregator.PublishEvent(new SeriesUpdatedEvent(series)); + _logger.Debug("Finished series refresh for {0}", series.Title); + _eventAggregator.PublishEvent(new SeriesUpdatedEvent(series)); + } + + private List UpdateSeasons(Series series, Series seriesInfo) + { + foreach (var season in seriesInfo.Seasons) + { + var existingSeason = series.Seasons.SingleOrDefault(s => s.SeasonNumber == season.SeasonNumber); + + //Todo: Should this should use the previous season's monitored state? + if (existingSeason == null) + { + _logger.Trace("New season ({0}) for series: [{1}] {2}, setting monitored to true", season.SeasonNumber, series.TvdbId, series.Title); + season.Monitored = true; + } + + else + { + season.Monitored = existingSeason.Monitored; + } + } + + return seriesInfo.Seasons; + } + + public void Execute(RefreshSeriesCommand message) + { + if (message.SeriesId.HasValue) + { + var series = _seriesService.GetSeries(message.SeriesId.Value); + RefreshSeriesInfo(series); + } + else + { + var allSeries = _seriesService.GetAllSeries().OrderBy(c => c.LastInfoSync).ToList(); + + foreach (var series in allSeries) + { + try + { + RefreshSeriesInfo(series); + } + catch (Exception e) + { + _logger.ErrorException("Couldn't refresh info for {0}".Inject(series), e); + } + } + } + } + + public void HandleAsync(SeriesAddedEvent message) + { + RefreshSeriesInfo(message.Series); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Tv/Season.cs b/NzbDrone.Core/Tv/Season.cs index be36fcc86..5073ea2b4 100644 --- a/NzbDrone.Core/Tv/Season.cs +++ b/NzbDrone.Core/Tv/Season.cs @@ -1,15 +1,11 @@ using System; -using System.Collections.Generic; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Tv { - public class Season : ModelBase + public class Season : IEmbeddedDocument { - public int SeriesId { get; set; } public int SeasonNumber { get; set; } public Boolean Monitored { get; set; } - - public List Episodes { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Tv/SeasonRepository.cs b/NzbDrone.Core/Tv/SeasonRepository.cs index f580c8d94..470c2e5ca 100644 --- a/NzbDrone.Core/Tv/SeasonRepository.cs +++ b/NzbDrone.Core/Tv/SeasonRepository.cs @@ -1,49 +1,27 @@ using System.Collections.Generic; using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Tv { - public interface ISeasonRepository : IBasicRepository + public interface ISeasonRepository : IBasicRepository { - IList GetSeasonNumbers(int seriesId); - Season Get(int seriesId, int seasonNumber); - bool IsMonitored(int seriesId, int seasonNumber); List GetSeasonBySeries(int seriesId); } - public class SeasonRepository : BasicRepository, ISeasonRepository + public class SeasonRepository : BasicRepository, ISeasonRepository { - - public SeasonRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public SeasonRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } - public IList GetSeasonNumbers(int seriesId) - { - return Query.Where(c => c.SeriesId == seriesId).Select(c => c.SeasonNumber).ToList(); - } - - public Season Get(int seriesId, int seasonNumber) - { - return Query.Single(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber); - } - - public bool IsMonitored(int seriesId, int seasonNumber) - { - var season = Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SeasonNumber == seasonNumber); - - if (season == null) return true; - - return season.Monitored; - } - public List GetSeasonBySeries(int seriesId) { - return Query.Where(s => s.SeriesId == seriesId); + return Query.Single(s => s.Id == seriesId).Seasons; } } } \ No newline at end of file diff --git a/NzbDrone.Core/Tv/SeasonService.cs b/NzbDrone.Core/Tv/SeasonService.cs deleted file mode 100644 index 3432f389d..000000000 Binary files a/NzbDrone.Core/Tv/SeasonService.cs and /dev/null differ diff --git a/NzbDrone.Core/Tv/Series.cs b/NzbDrone.Core/Tv/Series.cs index 55a6adb73..107162aaf 100644 --- a/NzbDrone.Core/Tv/Series.cs +++ b/NzbDrone.Core/Tv/Series.cs @@ -34,12 +34,15 @@ public Series() public bool UseSceneNumbering { get; set; } public string TitleSlug { get; set; } public string Path { get; set; } + public int Year { get; set; } public string RootFolderPath { get; set; } public DateTime? FirstAired { get; set; } public LazyLoaded QualityProfile { get; set; } + public List Seasons { get; set; } + public override string ToString() { return string.Format("[{0}][{1}]", TvdbId, Title.NullSafe()); diff --git a/NzbDrone.Core/Tv/SeriesRepository.cs b/NzbDrone.Core/Tv/SeriesRepository.cs index 83fac8844..161ebbac2 100644 --- a/NzbDrone.Core/Tv/SeriesRepository.cs +++ b/NzbDrone.Core/Tv/SeriesRepository.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Datastore; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; + namespace NzbDrone.Core.Tv { @@ -20,8 +22,8 @@ public interface ISeriesRepository : IBasicRepository public class SeriesRepository : BasicRepository, ISeriesRepository { - public SeriesRepository(IDatabase database, IMessageAggregator messageAggregator) - : base(database, messageAggregator) + public SeriesRepository(IDatabase database, IEventAggregator eventAggregator) + : base(database, eventAggregator) { } diff --git a/NzbDrone.Core/Tv/SeriesService.cs b/NzbDrone.Core/Tv/SeriesService.cs index 7c6dba091..9e8d7ba99 100644 --- a/NzbDrone.Core/Tv/SeriesService.cs +++ b/NzbDrone.Core/Tv/SeriesService.cs @@ -4,9 +4,10 @@ using System.Linq; using NLog; using NzbDrone.Common.EnsureThat; -using NzbDrone.Common.Messaging; using NzbDrone.Core.Configuration; using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Organizer; using NzbDrone.Core.Tv.Events; @@ -14,10 +15,8 @@ namespace NzbDrone.Core.Tv { public interface ISeriesService { - bool IsMonitored(int id); Series GetSeries(int seriesId); Series AddSeries(Series newSeries); - void UpdateFromSeriesEditor(IList editedSeries); Series FindByTvdbId(int tvdbId); Series FindByTvRageId(int tvRageId); Series FindByTitle(string title); @@ -26,37 +25,32 @@ public interface ISeriesService List GetAllSeries(); Series UpdateSeries(Series series); bool SeriesPathExists(string folder); - List GetSeriesInList(IEnumerable seriesIds); - Series FindBySlug(string slug); - List GetSeriesPaths(); } public class SeriesService : ISeriesService { private readonly ISeriesRepository _seriesRepository; private readonly IConfigService _configService; - private readonly IMessageAggregator _messageAggregator; + private readonly IEventAggregator _eventAggregator; private readonly ISceneMappingService _sceneMappingService; + private readonly IEpisodeService _episodeService; private readonly Logger _logger; public SeriesService(ISeriesRepository seriesRepository, IConfigService configServiceService, - IMessageAggregator messageAggregator, + IEventAggregator eventAggregator, ISceneMappingService sceneMappingService, + IEpisodeService episodeService, Logger logger) { _seriesRepository = seriesRepository; _configService = configServiceService; - _messageAggregator = messageAggregator; + _eventAggregator = eventAggregator; _sceneMappingService = sceneMappingService; + _episodeService = episodeService; _logger = logger; } - public bool IsMonitored(int id) - { - return _seriesRepository.Get(id).Monitored; - } - public Series GetSeries(int seriesId) { return _seriesRepository.Get(seriesId); @@ -80,29 +74,11 @@ public Series AddSeries(Series newSeries) newSeries.SeasonFolder = _configService.UseSeasonFolder; _seriesRepository.Insert(newSeries); - _messageAggregator.PublishEvent(new SeriesAddedEvent(newSeries)); + _eventAggregator.PublishEvent(new SeriesAddedEvent(newSeries)); return newSeries; } - public void UpdateFromSeriesEditor(IList editedSeries) - { - var allSeries = _seriesRepository.All(); - - foreach (var series in allSeries) - { - //Only update parameters that can be changed in MassEdit - var edited = editedSeries.Single(s => s.Id == series.Id); - series.QualityProfileId = edited.QualityProfileId; - series.Monitored = edited.Monitored; - series.SeasonFolder = edited.SeasonFolder; - series.Path = edited.Path; - - _seriesRepository.Update(series); - } - - } - public Series FindByTvdbId(int tvdbId) { return _seriesRepository.FindByTvdbId(tvdbId); @@ -113,17 +89,6 @@ public Series FindByTvRageId(int tvRageId) return _seriesRepository.FindByTvRageId(tvRageId); } - public Series FindBySlug(string slug) - { - var series = _seriesRepository.FindBySlug(slug); - return series; - } - - public List GetSeriesPaths() - { - return _seriesRepository.GetSeriesPaths(); - } - public Series FindByTitle(string title) { var tvdbId = _sceneMappingService.GetTvDbId(title); @@ -145,7 +110,7 @@ public void DeleteSeries(int seriesId, bool deleteFiles) { var series = _seriesRepository.Get(seriesId); _seriesRepository.Delete(seriesId); - _messageAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles)); + _eventAggregator.PublishEvent(new SeriesDeletedEvent(series, deleteFiles)); } public List GetAllSeries() @@ -155,6 +120,18 @@ public List GetAllSeries() public Series UpdateSeries(Series series) { + var storedSeries = GetSeries(series.Id); + + foreach (var season in series.Seasons) + { + var storedSeason = storedSeries.Seasons.SingleOrDefault(s => s.SeasonNumber == season.SeasonNumber); + + if (storedSeason != null && season.Monitored != storedSeason.Monitored) + { + _episodeService.SetEpisodeMonitoredBySeason(series.Id, season.SeasonNumber, season.Monitored); + } + } + return _seriesRepository.Update(series); } @@ -162,10 +139,5 @@ public bool SeriesPathExists(string folder) { return _seriesRepository.SeriesPathExists(folder); } - - public List GetSeriesInList(IEnumerable seriesIds) - { - return _seriesRepository.Get(seriesIds).ToList(); - } } } diff --git a/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs b/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs index bbbf6a1f3..d961c8e80 100644 --- a/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs +++ b/NzbDrone.Core/Update/Commands/ApplicationUpdateCommand.cs @@ -1,8 +1,15 @@ -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; namespace NzbDrone.Core.Update.Commands { - public class ApplicationUpdateCommand : ICommand + public class ApplicationUpdateCommand : Command { + public override bool SendUpdatesToClient + { + get + { + return true; + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Update/InstallUpdateService.cs b/NzbDrone.Core/Update/InstallUpdateService.cs index d204bcab3..fd74f199c 100644 --- a/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/NzbDrone.Core/Update/InstallUpdateService.cs @@ -3,8 +3,10 @@ using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Update.Commands; +using NzbDrone.Core.Instrumentation; namespace NzbDrone.Core.Update { @@ -36,6 +38,7 @@ public InstallUpdateService(ICheckUpdateService checkUpdateService, IAppFolderIn public void Execute(ApplicationUpdateCommand message) { + _logger.ProgressDebug("Checking for updates"); var latestAvailable = _checkUpdateService.AvailableUpdate(); if (latestAvailable != null) @@ -58,11 +61,11 @@ private void InstallUpdate(UpdatePackage updatePackage) _diskProvider.DeleteFolder(updateSandboxFolder, true); } - _logger.Info("Downloading update package from [{0}] to [{1}]", updatePackage.Url, packageDestination); + _logger.ProgressInfo("Downloading Updated {0} [{1}]", updatePackage.Version, updatePackage.Branch); + _logger.Debug("Downloading update package from [{0}] to [{1}]", updatePackage.Url, packageDestination); _httpProvider.DownloadFile(updatePackage.Url, packageDestination); - _logger.Info("Download completed for update package from [{0}]", updatePackage.FileName); - _logger.Info("Extracting Update package"); + _logger.ProgressInfo("Extracting Update package"); _archiveService.Extract(packageDestination, updateSandboxFolder); _logger.Info("Update package extracted successfully"); @@ -72,6 +75,8 @@ private void InstallUpdate(UpdatePackage updatePackage) _logger.Info("Starting update client {0}", _appFolderInfo.GetUpdateClientExePath()); + _logger.ProgressInfo("NzbDrone will restart shortly."); + _processProvider.Start(_appFolderInfo.GetUpdateClientExePath(), _processProvider.GetCurrentProcess().Id.ToString()); } catch (Exception ex) diff --git a/NzbDrone.Core/Update/UpdateCheckService.cs b/NzbDrone.Core/Update/UpdateCheckService.cs index 313f1ef58..32cb64be2 100644 --- a/NzbDrone.Core/Update/UpdateCheckService.cs +++ b/NzbDrone.Core/Update/UpdateCheckService.cs @@ -1,5 +1,7 @@ using NLog; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Instrumentation; namespace NzbDrone.Core.Update { @@ -8,28 +10,30 @@ public interface ICheckUpdateService UpdatePackage AvailableUpdate(); } - public class CheckUpdateService : ICheckUpdateService { private readonly IUpdatePackageProvider _updatePackageProvider; + private readonly IConfigFileProvider _configFileProvider; private readonly Logger _logger; - public CheckUpdateService(IUpdatePackageProvider updatePackageProvider, Logger logger) + public CheckUpdateService(IUpdatePackageProvider updatePackageProvider, IConfigFileProvider configFileProvider, Logger logger) { _updatePackageProvider = updatePackageProvider; + _configFileProvider = configFileProvider; _logger = logger; } public UpdatePackage AvailableUpdate() { - var latestAvailable = _updatePackageProvider.GetLatestUpdate(); + if (OsInfo.IsLinux) return null; + + var latestAvailable = _updatePackageProvider.GetLatestUpdate(_configFileProvider.Branch, BuildInfo.Version); if (latestAvailable == null) { - _logger.Debug("No update available."); - return null; + _logger.ProgressDebug("No update available."); } return latestAvailable; diff --git a/NzbDrone.Core/Update/UpdatePackage.cs b/NzbDrone.Core/Update/UpdatePackage.cs index ce038ac99..70ca40b87 100644 --- a/NzbDrone.Core/Update/UpdatePackage.cs +++ b/NzbDrone.Core/Update/UpdatePackage.cs @@ -1,13 +1,14 @@ using System; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace NzbDrone.Core.Update { public class UpdatePackage { - public String Id { get; set; } + public string Id { get; set; } - [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))] + [JsonConverter(typeof(VersionConverter))] public Version Version { get; set; } public String Branch { get; set; } diff --git a/NzbDrone.Core/Update/UpdatePackageAvailable.cs b/NzbDrone.Core/Update/UpdatePackageAvailable.cs index 1d76e22d7..c6b99b446 100644 --- a/NzbDrone.Core/Update/UpdatePackageAvailable.cs +++ b/NzbDrone.Core/Update/UpdatePackageAvailable.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace NzbDrone.Core.Update { diff --git a/NzbDrone.Core/Update/UpdatePackageProvider.cs b/NzbDrone.Core/Update/UpdatePackageProvider.cs index e045a89fd..c6b4b64c0 100644 --- a/NzbDrone.Core/Update/UpdatePackageProvider.cs +++ b/NzbDrone.Core/Update/UpdatePackageProvider.cs @@ -1,37 +1,27 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Newtonsoft.Json; -using NLog; using NzbDrone.Common; -using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Core.Configuration; +using RestSharp; +using NzbDrone.Core.Rest; namespace NzbDrone.Core.Update { public interface IUpdatePackageProvider { - UpdatePackage GetLatestUpdate(); + UpdatePackage GetLatestUpdate(string branch, Version currentVersion); } public class UpdatePackageProvider : IUpdatePackageProvider { - private readonly IConfigFileProvider _configFileProvider; - private readonly IHttpProvider _httpProvider; - private readonly Logger _logger; - - public UpdatePackageProvider(IConfigFileProvider configFileProvider, IHttpProvider httpProvider, Logger logger) + public UpdatePackage GetLatestUpdate(string branch, Version currentVersion) { - _configFileProvider = configFileProvider; - _httpProvider = httpProvider; - _logger = logger; - } + var restClient = new RestClient(Services.RootUrl); - public UpdatePackage GetLatestUpdate() - { - var url = String.Format("{0}/v1/update/{1}?version={2}", Services.RootUrl, _configFileProvider.Branch, BuildInfo.Version); - var update = JsonConvert.DeserializeObject(_httpProvider.DownloadString(url)); + var request = new RestRequest("/v1/update/{branch}"); + + request.AddParameter("version", currentVersion); + request.AddUrlSegment("branch", branch); + + var update = restClient.ExecuteAndValidate(request); if (!update.Available) return null; diff --git a/NzbDrone.Core/progressmessaging/CommandUpdatedEvent.cs b/NzbDrone.Core/progressmessaging/CommandUpdatedEvent.cs new file mode 100644 index 000000000..2d8c93269 --- /dev/null +++ b/NzbDrone.Core/progressmessaging/CommandUpdatedEvent.cs @@ -0,0 +1,15 @@ +using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.ProgressMessaging +{ + public class CommandUpdatedEvent : IEvent + { + public Command Command { get; set; } + + public CommandUpdatedEvent(Command command) + { + Command = command; + } + } +} diff --git a/NzbDrone.Host/Bootstrap.cs b/NzbDrone.Host/Bootstrap.cs index e4b592fd1..fb1d23eec 100644 --- a/NzbDrone.Host/Bootstrap.cs +++ b/NzbDrone.Host/Bootstrap.cs @@ -1,5 +1,4 @@ using System.Reflection; -using NLog; using NzbDrone.Common.Composition; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; @@ -12,7 +11,7 @@ public static class Bootstrap { public static IContainer Start(StartupArguments args, IUserAlert userAlert) { - var logger = LogManager.GetLogger("AppMain"); + var logger = NzbDroneLogger.GetLogger(); GlobalExceptionHandlers.Register(); IgnoreCertErrorPolicy.Register(); diff --git a/NzbDrone.Host/MainAppContainerBuilder.cs b/NzbDrone.Host/MainAppContainerBuilder.cs index e12fc9153..299de7925 100644 --- a/NzbDrone.Host/MainAppContainerBuilder.cs +++ b/NzbDrone.Host/MainAppContainerBuilder.cs @@ -1,13 +1,11 @@ -using System; -using Nancy.Bootstrapper; +using Nancy.Bootstrapper; using NzbDrone.Api; -using NzbDrone.Api.SignalR; using NzbDrone.Common.Composition; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Datastore; using NzbDrone.Core.Organizer; using NzbDrone.Core.RootFolders; -using NzbDrone.Host.Owin; +using NzbDrone.SignalR; namespace NzbDrone.Host { @@ -19,7 +17,7 @@ public static IContainer BuildContainer(StartupArguments args) } private MainAppContainerBuilder(StartupArguments args) - : base(args, "NzbDrone.Host", "NzbDrone.Common", "NzbDrone.Core", "NzbDrone.Api") + : base(args, "NzbDrone.Host", "NzbDrone.Common", "NzbDrone.Core", "NzbDrone.Api", "NzbDrone.SignalR") { AutoRegisterImplementations(); diff --git a/NzbDrone.Host/NLog.config b/NzbDrone.Host/NLog.config deleted file mode 100644 index 65103a066..000000000 --- a/NzbDrone.Host/NLog.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - Exception - ${exception:format=ToString} - - - - - - - - - - \ No newline at end of file diff --git a/NzbDrone.Host/NzbDrone.Host.csproj b/NzbDrone.Host/NzbDrone.Host.csproj index c625626ea..7ecf58a13 100644 --- a/NzbDrone.Host/NzbDrone.Host.csproj +++ b/NzbDrone.Host/NzbDrone.Host.csproj @@ -125,6 +125,7 @@ + @@ -141,10 +142,6 @@ - - Always - Designer - Designer @@ -188,6 +185,13 @@ {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205} NzbDrone.Core
+ + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36} + NzbDrone.SignalR + + + + diff --git a/NzbDrone.Host/Owin/MiddleWare/SignalRMiddleWare.cs b/NzbDrone.Host/Owin/MiddleWare/SignalRMiddleWare.cs index ba9a436fa..cf4396edf 100644 --- a/NzbDrone.Host/Owin/MiddleWare/SignalRMiddleWare.cs +++ b/NzbDrone.Host/Owin/MiddleWare/SignalRMiddleWare.cs @@ -1,32 +1,25 @@ -using System.Collections.Generic; +using System; using Microsoft.AspNet.SignalR; -using NzbDrone.Api.SignalR; using NzbDrone.Common.Composition; +using NzbDrone.SignalR; using Owin; namespace NzbDrone.Host.Owin.MiddleWare { public class SignalRMiddleWare : IOwinMiddleWare { - private readonly IEnumerable _persistentConnections; - public int Order { get { return 0; } } - public SignalRMiddleWare(IEnumerable persistentConnections, IContainer container) + public SignalRMiddleWare(IContainer container) { - _persistentConnections = persistentConnections; - SignalrDependencyResolver.Register(container); + + GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(300000); } public void Attach(IAppBuilder appBuilder) { - foreach (var nzbDronePersistentConnection in _persistentConnections) - { - var url = string.Format("signalr/{0}", nzbDronePersistentConnection.Resource.Trim('/')); - appBuilder.MapConnection(url, nzbDronePersistentConnection.GetType(), new ConnectionConfiguration { EnableCrossDomain = true }); - } - + appBuilder.MapConnection("signalr", typeof(NzbDronePersistentConnection), new ConnectionConfiguration { EnableCrossDomain = true }); } } } \ No newline at end of file diff --git a/NzbDrone.Host/Owin/NlogTextWriter.cs b/NzbDrone.Host/Owin/NlogTextWriter.cs index d4cd4da7d..d5b4d04aa 100644 --- a/NzbDrone.Host/Owin/NlogTextWriter.cs +++ b/NzbDrone.Host/Owin/NlogTextWriter.cs @@ -1,12 +1,13 @@ using System.IO; using System.Text; using NLog; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Host.Owin { public class NlogTextWriter : TextWriter { - private readonly Logger logger = LogManager.GetCurrentClassLogger(); + private readonly Logger _logger = NzbDroneLogger.GetLogger(); public override Encoding Encoding @@ -17,25 +18,23 @@ public override Encoding Encoding } } - public override void Write(char value) + public override void Write(char[] buffer, int index, int count) { - logger.Trace(value); + Write(buffer); } - public override void Write(char[] buffer) { - logger.Trace(buffer); + Write(new string(buffer)); } public override void Write(string value) { - logger.Trace(value); + _logger.Trace(value); } - public override void Write(char[] buffer, int index, int count) + public override void Write(char value) { - logger.Trace(buffer); + _logger.Trace(value); } - } } \ No newline at end of file diff --git a/NzbDrone.Host/Owin/OwinHostController.cs b/NzbDrone.Host/Owin/OwinHostController.cs index 9968b22bb..b4d8d24ba 100644 --- a/NzbDrone.Host/Owin/OwinHostController.cs +++ b/NzbDrone.Host/Owin/OwinHostController.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; +using System.Reflection; using Microsoft.Owin.Hosting; using NLog; using NzbDrone.Common.EnvironmentInfo; @@ -50,7 +52,26 @@ public void StartServer() _logger.Info("starting server on {0}", _urlAclAdapter.UrlAcl); - _host = WebApp.Start(OwinServiceProviderFactory.Create(), options, BuildApp); + try + { + _host = WebApp.Start(OwinServiceProviderFactory.Create(), options, BuildApp); + } + catch (TargetInvocationException ex) + { + if (ex.InnerException == null) + { + throw; + } + + if (ex.InnerException is HttpListenerException) + { + throw new PortInUseException("Port {0} is already in use, please ensure NzbDrone is not already running.", + ex, + _configFileProvider.Port); + } + + throw ex.InnerException; + } } private void BuildApp(IAppBuilder appBuilder) diff --git a/NzbDrone.Host/Owin/PortInUseException.cs b/NzbDrone.Host/Owin/PortInUseException.cs new file mode 100644 index 000000000..5c6d7a542 --- /dev/null +++ b/NzbDrone.Host/Owin/PortInUseException.cs @@ -0,0 +1,12 @@ +using System; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Host.Owin +{ + public class PortInUseException : NzbDroneException + { + public PortInUseException(string message, Exception innerException, params object[] args) : base(message, innerException, args) + { + } + } +} diff --git a/NzbDrone.Host/PlatformValidation.cs b/NzbDrone.Host/PlatformValidation.cs index 978a579ae..703918aca 100644 --- a/NzbDrone.Host/PlatformValidation.cs +++ b/NzbDrone.Host/PlatformValidation.cs @@ -3,12 +3,13 @@ using System.Reflection; using NLog; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation; namespace NzbDrone.Host { public static class PlatformValidation { - private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public static bool IsValidate(IUserAlert userAlert) { diff --git a/NzbDrone.Integration.Test/Client/ClientBase.cs b/NzbDrone.Integration.Test/Client/ClientBase.cs index 05ca16711..20e35117f 100644 --- a/NzbDrone.Integration.Test/Client/ClientBase.cs +++ b/NzbDrone.Integration.Test/Client/ClientBase.cs @@ -2,6 +2,7 @@ using System.Net; using FluentAssertions; using NLog; +using NzbDrone.Api; using NzbDrone.Api.REST; using NzbDrone.Common.Serializer; using RestSharp; @@ -35,6 +36,17 @@ public List All() return Get>(request); } + public PagingResource GetPaged(int pageNumber, int pageSize, string sortKey, string sortDir) + { + var request = BuildRequest(); + request.AddParameter("page", pageNumber); + request.AddParameter("pageSize", pageSize); + request.AddParameter("sortKey", sortKey); + request.AddParameter("sortDir", sortDir); + return Get>(request); + + } + public TResource Post(TResource body) { var request = BuildRequest(); @@ -74,7 +86,7 @@ public List InvalidPost(TResource body) return Post>(request, HttpStatusCode.BadRequest); } - protected RestRequest BuildRequest(string command = "") + public RestRequest BuildRequest(string command = "") { return new RestRequest(_resource + "/" + command.Trim('/')) { @@ -82,7 +94,7 @@ protected RestRequest BuildRequest(string command = "") }; } - protected T Get(IRestRequest request, HttpStatusCode statusCode = HttpStatusCode.OK) where T : class, new() + public T Get(IRestRequest request, HttpStatusCode statusCode = HttpStatusCode.OK) where T : class, new() { request.Method = Method.GET; return Execute(request, statusCode); diff --git a/NzbDrone.Integration.Test/Client/EpisodeClient.cs b/NzbDrone.Integration.Test/Client/EpisodeClient.cs index 675c38bd8..8cc89af4e 100644 --- a/NzbDrone.Integration.Test/Client/EpisodeClient.cs +++ b/NzbDrone.Integration.Test/Client/EpisodeClient.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -using System.Net; using NzbDrone.Api.Episodes; -using NzbDrone.Api.Series; using RestSharp; namespace NzbDrone.Integration.Test.Client diff --git a/NzbDrone.Integration.Test/Client/SeasonClient.cs b/NzbDrone.Integration.Test/Client/SeasonClient.cs deleted file mode 100644 index af0572a70..000000000 --- a/NzbDrone.Integration.Test/Client/SeasonClient.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using NzbDrone.Api.Episodes; -using NzbDrone.Api.Seasons; -using RestSharp; - -namespace NzbDrone.Integration.Test.Client -{ - public class SeasonClient : ClientBase - { - public SeasonClient(IRestClient restClient) - : base(restClient) - { - } - - public List GetSeasonsInSeries(int seriesId) - { - var request = BuildRequest("?seriesId=" + seriesId.ToString()); - return Get>(request); - } - } -} diff --git a/NzbDrone.Integration.Test/CommandIntegerationTests.cs b/NzbDrone.Integration.Test/CommandIntegerationTests.cs index c1e06bac1..1dbe33b67 100644 --- a/NzbDrone.Integration.Test/CommandIntegerationTests.cs +++ b/NzbDrone.Integration.Test/CommandIntegerationTests.cs @@ -1,15 +1,40 @@ -using NUnit.Framework; +using System.Net; +using FluentAssertions; +using NUnit.Framework; using NzbDrone.Api.Commands; +using NzbDrone.Common.Serializer; +using RestSharp; namespace NzbDrone.Integration.Test { [TestFixture] + [Ignore] public class CommandIntegrationTest : IntegrationTest { [Test] public void should_be_able_to_run_rss_sync() { - Commands.Post(new CommandResource {Command = "rsssync"}); + var request = new RestRequest("command") + { + RequestFormat = DataFormat.Json, + Method = Method.POST + }; + + request.AddBody(new CommandResource { Name = "rsssync" }); + + var restClient = new RestClient("http://localhost:8989/api"); + var response = restClient.Execute(request); + + if (response.ErrorException != null) + { + throw response.ErrorException; + } + + response.ErrorMessage.Should().BeBlank(); + response.StatusCode.Should().Be(HttpStatusCode.Created); + + var trackedCommand = Json.Deserialize(response.Content); + trackedCommand.Id.Should().NotBe(0); } } } \ No newline at end of file diff --git a/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs b/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs index f4d7fe8c1..681e510ef 100644 --- a/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs +++ b/NzbDrone.Integration.Test/EpisodeIntegrationTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; using FluentAssertions; using NUnit.Framework; using NzbDrone.Api.Series; diff --git a/NzbDrone.Integration.Test/HistoryIntegrationTest.cs b/NzbDrone.Integration.Test/HistoryIntegrationTest.cs new file mode 100644 index 000000000..8c5a6c1be --- /dev/null +++ b/NzbDrone.Integration.Test/HistoryIntegrationTest.cs @@ -0,0 +1,21 @@ +using FluentAssertions; +using NUnit.Framework; + +namespace NzbDrone.Integration.Test +{ + [TestFixture] + public class HistoryIntegrationTest : IntegrationTest + { + [Test] + public void history_should_be_empty() + { + var history = History.GetPaged(1, 15, "date", "desc"); + + history.Records.Count.Should().Be(0); + history.Page.Should().Be(1); + history.PageSize.Should().Be(15); + history.Records.Should().BeEmpty(); + } + + } +} \ No newline at end of file diff --git a/NzbDrone.Integration.Test/IntegrationTest.cs b/NzbDrone.Integration.Test/IntegrationTest.cs index b858e4ff3..a0b5b3bcd 100644 --- a/NzbDrone.Integration.Test/IntegrationTest.cs +++ b/NzbDrone.Integration.Test/IntegrationTest.cs @@ -1,10 +1,10 @@ -using System.Runtime.CompilerServices; -using NLog; +using NLog; using NLog.Config; using NLog.Targets; using NUnit.Framework; using NzbDrone.Api.Commands; using NzbDrone.Api.Config; +using NzbDrone.Api.History; using NzbDrone.Api.RootFolders; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Integration.Test.Client; @@ -23,9 +23,9 @@ public abstract class IntegrationTest protected ClientBase RootFolders; protected ClientBase Commands; protected ReleaseClient Releases; + protected ClientBase History; protected IndexerClient Indexers; protected EpisodeClient Episodes; - protected SeasonClient Seasons; protected ClientBase NamingConfig; private NzbDroneRunner _runner; @@ -59,9 +59,9 @@ private void InitRestClients() Releases = new ReleaseClient(RestClient); RootFolders = new ClientBase(RestClient); Commands = new ClientBase(RestClient); + History = new ClientBase(RestClient); Indexers = new IndexerClient(RestClient); Episodes = new EpisodeClient(RestClient); - Seasons = new SeasonClient(RestClient); NamingConfig = new ClientBase(RestClient, "config/naming"); } @@ -72,5 +72,4 @@ public void SmokeTestTearDown() _runner.KillAll(); } } - } diff --git a/NzbDrone.Integration.Test/NamingConfigTests.cs b/NzbDrone.Integration.Test/NamingConfigTests.cs index 573eaf2ed..07592193e 100644 --- a/NzbDrone.Integration.Test/NamingConfigTests.cs +++ b/NzbDrone.Integration.Test/NamingConfigTests.cs @@ -1,6 +1,4 @@ -using System; -using System.IO; -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; namespace NzbDrone.Integration.Test diff --git a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj index 5762f6877..9bba4d240 100644 --- a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj +++ b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj @@ -94,14 +94,13 @@ - + - diff --git a/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs b/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs index 8af6f77e4..b9a86674e 100644 --- a/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs +++ b/NzbDrone.Integration.Test/ReleaseIntegrationTest.cs @@ -22,7 +22,7 @@ private bool BeValidRelease(ReleaseResource releaseResource) { releaseResource.Age.Should().BeGreaterOrEqualTo(-1); releaseResource.Title.Should().NotBeBlank(); - releaseResource.NzbUrl.Should().NotBeBlank(); + releaseResource.DownloadUrl.Should().NotBeBlank(); releaseResource.SeriesTitle.Should().NotBeBlank(); //TODO: uncomment these after moving to restsharp for rss //releaseResource.NzbInfoUrl.Should().NotBeBlank(); diff --git a/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs b/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs index 8a888fffa..0b7647fdf 100644 --- a/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs +++ b/NzbDrone.Integration.Test/RootFolderIntegrationTest.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using FluentAssertions; using Microsoft.AspNet.SignalR.Client; using Microsoft.AspNet.SignalR.Client.Transports; diff --git a/NzbDrone.Integration.Test/SeasonIntegrationTests.cs b/NzbDrone.Integration.Test/SeasonIntegrationTests.cs deleted file mode 100644 index 7a2458b4f..000000000 --- a/NzbDrone.Integration.Test/SeasonIntegrationTests.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Threading; -using FluentAssertions; -using NUnit.Framework; -using NzbDrone.Api.Series; -using System.Linq; -using NzbDrone.Test.Common; - -namespace NzbDrone.Integration.Test -{ - [TestFixture] - public class SeasonIntegrationTests : IntegrationTest - { - private SeriesResource GivenSeriesWithEpisodes() - { - var series = Series.Lookup("archer").First(); - - series.QualityProfileId = 1; - series.Path = @"C:\Test\Archer".AsOsAgnostic(); - - series = Series.Post(series); - - while (true) - { - if (Seasons.GetSeasonsInSeries(series.Id).Count > 0) - { - return series; - } - - Thread.Sleep(1000); - } - } - - [Test] - public void should_be_able_to_get_all_seasons_in_series() - { - var series = GivenSeriesWithEpisodes(); - Seasons.GetSeasonsInSeries(series.Id).Count.Should().BeGreaterThan(0); - } - - [Test] - public void should_be_able_to_get_a_single_season() - { - var series = GivenSeriesWithEpisodes(); - var seasons = Seasons.GetSeasonsInSeries(series.Id); - - Seasons.Get(seasons.First().Id).Should().NotBeNull(); - } - - [Test] - public void should_be_able_to_set_monitor_status_via_api() - { - var series = GivenSeriesWithEpisodes(); - var seasons = Seasons.GetSeasonsInSeries(series.Id); - var updatedSeason = seasons.First(); - updatedSeason.Monitored = false; - - Seasons.Put(updatedSeason).Monitored.Should().BeFalse(); - } - } -} diff --git a/NzbDrone.SignalR/BroadcastSignalRMessage.cs b/NzbDrone.SignalR/BroadcastSignalRMessage.cs new file mode 100644 index 000000000..d989affc3 --- /dev/null +++ b/NzbDrone.SignalR/BroadcastSignalRMessage.cs @@ -0,0 +1,14 @@ +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.SignalR +{ + public class BroadcastSignalRMessage : Command + { + public SignalRMessage Body { get; private set; } + + public BroadcastSignalRMessage(SignalRMessage body) + { + Body = body; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/SignalR/NoOpPerformanceCounterManager.cs b/NzbDrone.SignalR/NoOpPerformanceCounterManager.cs similarity index 99% rename from NzbDrone.Api/SignalR/NoOpPerformanceCounterManager.cs rename to NzbDrone.SignalR/NoOpPerformanceCounterManager.cs index a3e3d7c1f..2eec1b004 100644 --- a/NzbDrone.Api/SignalR/NoOpPerformanceCounterManager.cs +++ b/NzbDrone.SignalR/NoOpPerformanceCounterManager.cs @@ -2,7 +2,7 @@ using System.Threading; using Microsoft.AspNet.SignalR.Infrastructure; -namespace NzbDrone.Api.SignalR +namespace NzbDrone.SignalR { public class NoOpPerformanceCounterManager : IPerformanceCounterManager { diff --git a/NzbDrone.SignalR/NzbDrone.SignalR.csproj b/NzbDrone.SignalR/NzbDrone.SignalR.csproj new file mode 100644 index 000000000..859651305 --- /dev/null +++ b/NzbDrone.SignalR/NzbDrone.SignalR.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36} + Library + Properties + NzbDrone.SignalR + NzbDrone.SignalR + v4.0 + 512 + ..\ + true + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\Microsoft.AspNet.SignalR.Core.1.1.3\lib\net40\Microsoft.AspNet.SignalR.Core.dll + + + ..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll + + + + + + + + Properties\SharedAssemblyInfo.cs + + + + + + + + + + + + + + + {f2be0fdf-6e47-4827-a420-dd4ef82407f8} + NzbDrone.Common + + + {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205} + NzbDrone.Core + + + + + + \ No newline at end of file diff --git a/NzbDrone.SignalR/NzbDronePersistentConnection.cs b/NzbDrone.SignalR/NzbDronePersistentConnection.cs new file mode 100644 index 000000000..b9a6f1eed --- /dev/null +++ b/NzbDrone.SignalR/NzbDronePersistentConnection.cs @@ -0,0 +1,24 @@ +using Microsoft.AspNet.SignalR; +using Microsoft.AspNet.SignalR.Infrastructure; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.SignalR +{ + public sealed class NzbDronePersistentConnection : PersistentConnection, IExecute + { + private IPersistentConnectionContext Context + { + get + { + return ((ConnectionManager)GlobalHost.ConnectionManager).GetConnection(GetType()); + } + } + + + public void Execute(BroadcastSignalRMessage message) + { + Context.Connection.Broadcast(message.Body); + } + } +} \ No newline at end of file diff --git a/NzbDrone.SignalR/Properties/AssemblyInfo.cs b/NzbDrone.SignalR/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ffaa67fdd --- /dev/null +++ b/NzbDrone.SignalR/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NzbDrone.SignalR")] +[assembly: Guid("98bd985a-4f23-4201-8ed3-f6f3d7f2a5fe")] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NzbDrone.Api/SignalR/Serializer.cs b/NzbDrone.SignalR/Serializer.cs similarity index 95% rename from NzbDrone.Api/SignalR/Serializer.cs rename to NzbDrone.SignalR/Serializer.cs index 4ab2bfc01..e631ef146 100644 --- a/NzbDrone.Api/SignalR/Serializer.cs +++ b/NzbDrone.SignalR/Serializer.cs @@ -3,7 +3,7 @@ using Microsoft.AspNet.SignalR.Json; using NzbDrone.Common.Serializer; -namespace NzbDrone.Api.SignalR +namespace NzbDrone.SignalR { public class Serializer : IJsonSerializer { diff --git a/NzbDrone.SignalR/SignalRMessage.cs b/NzbDrone.SignalR/SignalRMessage.cs new file mode 100644 index 000000000..e8993c286 --- /dev/null +++ b/NzbDrone.SignalR/SignalRMessage.cs @@ -0,0 +1,8 @@ +namespace NzbDrone.SignalR +{ + public class SignalRMessage + { + public object Body { get; set; } + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/SignalR/SignalrDependencyResolver.cs b/NzbDrone.SignalR/SignalrDependencyResolver.cs similarity index 96% rename from NzbDrone.Api/SignalR/SignalrDependencyResolver.cs rename to NzbDrone.SignalR/SignalrDependencyResolver.cs index f95f9ae5a..a3633cbae 100644 --- a/NzbDrone.Api/SignalR/SignalrDependencyResolver.cs +++ b/NzbDrone.SignalR/SignalrDependencyResolver.cs @@ -3,7 +3,7 @@ using Microsoft.AspNet.SignalR.Infrastructure; using NzbDrone.Common.Composition; -namespace NzbDrone.Api.SignalR +namespace NzbDrone.SignalR { public class SignalrDependencyResolver : DefaultDependencyResolver { diff --git a/NzbDrone.SignalR/packages.config b/NzbDrone.SignalR/packages.config new file mode 100644 index 000000000..f82b57a29 --- /dev/null +++ b/NzbDrone.SignalR/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/NzbDrone.Test.Common/Categories/DiskAccessTestAttribute.cs b/NzbDrone.Test.Common/Categories/DiskAccessTestAttribute.cs new file mode 100644 index 000000000..faccff30d --- /dev/null +++ b/NzbDrone.Test.Common/Categories/DiskAccessTestAttribute.cs @@ -0,0 +1,13 @@ +using NUnit.Framework; + +namespace NzbDrone.Test.Common.Categories +{ + public class DiskAccessTestAttribute : CategoryAttribute + { + public DiskAccessTestAttribute() + : base("DiskAccessTest") + { + + } + } +} \ No newline at end of file diff --git a/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj b/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj index 4e85ec162..dd6ffc0fe 100644 --- a/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj +++ b/NzbDrone.Test.Common/NzbDrone.Test.Common.csproj @@ -76,6 +76,7 @@ + diff --git a/NzbDrone.Test.Common/NzbDrone.Test.Common.ncrunchproject b/NzbDrone.Test.Common/NzbDrone.Test.Common.ncrunchproject index 8641d3614..1a2228e7f 100644 --- a/NzbDrone.Test.Common/NzbDrone.Test.Common.ncrunchproject +++ b/NzbDrone.Test.Common/NzbDrone.Test.Common.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,8 +12,11 @@ false true true - 60000 - - - AutoDetect + 5000 + Debug + x86 + + x86 + STA + x86 \ No newline at end of file diff --git a/NzbDrone.Test.Common/TestBase.cs b/NzbDrone.Test.Common/TestBase.cs index 94521c8d5..82e73ab4d 100644 --- a/NzbDrone.Test.Common/TestBase.cs +++ b/NzbDrone.Test.Common/TestBase.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Threading; using FluentAssertions; using Moq; using NLog; @@ -8,6 +9,8 @@ using NzbDrone.Common.Cache; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Messaging; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Events; using NzbDrone.Test.Common.AutoMoq; namespace NzbDrone.Test.Common @@ -42,6 +45,9 @@ protected TSubject Subject public abstract class TestBase : LoggingTest { + + private static readonly Random _random = new Random(); + private AutoMoqer _mocker; protected AutoMoqer Mocker { @@ -58,6 +64,15 @@ protected AutoMoqer Mocker protected Mock MockedRestProvider { get; private set; } + protected int RandomNumber + { + get + { + Thread.Sleep(1); + return _random.Next(0, int.MaxValue); + } + } + private string VirtualPath { get @@ -143,6 +158,11 @@ protected string GetTestFilePath(string fileName) return Path.Combine(SandboxFolder, fileName); } + protected string GetTestFilePath() + { + return GetTestFilePath(Path.GetRandomFileName()); + } + protected string SandboxFolder { get @@ -160,12 +180,12 @@ protected void VerifyEventPublished() where TEvent : class, IEvent protected void VerifyEventPublished(Times times) where TEvent : class, IEvent { - Mocker.GetMock().Verify(c => c.PublishEvent(It.IsAny()), times); + Mocker.GetMock().Verify(c => c.PublishEvent(It.IsAny()), times); } protected void VerifyEventNotPublished() where TEvent : class, IEvent { - Mocker.GetMock().Verify(c => c.PublishEvent(It.IsAny()), Times.Never()); + Mocker.GetMock().Verify(c => c.PublishEvent(It.IsAny()), Times.Never()); } } } diff --git a/NzbDrone.Test.Dummy/NzbDrone.Test.Dummy.ncrunchproject b/NzbDrone.Test.Dummy/NzbDrone.Test.Dummy.ncrunchproject index 8641d3614..1a2228e7f 100644 --- a/NzbDrone.Test.Dummy/NzbDrone.Test.Dummy.ncrunchproject +++ b/NzbDrone.Test.Dummy/NzbDrone.Test.Dummy.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,8 +12,11 @@ false true true - 60000 - - - AutoDetect + 5000 + Debug + x86 + + x86 + STA + x86 \ No newline at end of file diff --git a/NzbDrone.Update.Test/NzbDrone.Update.Test.ncrunchproject b/NzbDrone.Update.Test/NzbDrone.Update.Test.ncrunchproject index 514d01a85..1a2228e7f 100644 --- a/NzbDrone.Update.Test/NzbDrone.Update.Test.ncrunchproject +++ b/NzbDrone.Update.Test/NzbDrone.Update.Test.ncrunchproject @@ -2,7 +2,7 @@ false true false - true + false false false false @@ -12,11 +12,11 @@ false true true - 60000 - - + 5000 + Debug + x86 - AutoDetect + x86 STA x86 \ No newline at end of file diff --git a/NzbDrone.Update/NLog.config b/NzbDrone.Update/NLog.config deleted file mode 100644 index 8398980c9..000000000 --- a/NzbDrone.Update/NLog.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/NzbDrone.Update/NzbDrone.Update.csproj b/NzbDrone.Update/NzbDrone.Update.csproj index c2d12c5c4..a1d42c2e0 100644 --- a/NzbDrone.Update/NzbDrone.Update.csproj +++ b/NzbDrone.Update/NzbDrone.Update.csproj @@ -63,10 +63,6 @@ - - Always - Designer - Designer diff --git a/NzbDrone.Update/NzbDrone.Update.ncrunchproject b/NzbDrone.Update/NzbDrone.Update.ncrunchproject index 8641d3614..1a2228e7f 100644 --- a/NzbDrone.Update/NzbDrone.Update.ncrunchproject +++ b/NzbDrone.Update/NzbDrone.Update.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,8 +12,11 @@ false true true - 60000 - - - AutoDetect + 5000 + Debug + x86 + + x86 + STA + x86 \ No newline at end of file diff --git a/NzbDrone.Update/UpdateApp.cs b/NzbDrone.Update/UpdateApp.cs index 167499373..3a882cd74 100644 --- a/NzbDrone.Update/UpdateApp.cs +++ b/NzbDrone.Update/UpdateApp.cs @@ -16,7 +16,7 @@ public class UpdateApp private readonly IProcessProvider _processProvider; private static IContainer _container; - private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger logger = NzbDroneLogger.GetLogger(); public UpdateApp(IInstallUpdateService installUpdateService, IProcessProvider processProvider) { @@ -28,14 +28,16 @@ public static void Main(string[] args) { try { + var startupArgument = new StartupArguments(args); + LogTargets.Register(startupArgument, true, true); + Console.WriteLine("Starting NzbDrone Update Client"); IgnoreCertErrorPolicy.Register(); GlobalExceptionHandlers.Register(); - new LogglyTarget().Register(LogLevel.Trace); - _container = UpdateContainerBuilder.Build(new StartupArguments(args)); + _container = UpdateContainerBuilder.Build(startupArgument); logger.Info("Updating NzbDrone to version {0}", BuildInfo.Version); _container.Resolve().Start(args); diff --git a/NzbDrone.Update/UpdateEngine/BackupAndRestore.cs b/NzbDrone.Update/UpdateEngine/BackupAndRestore.cs index 78c3b8c73..91552105c 100644 --- a/NzbDrone.Update/UpdateEngine/BackupAndRestore.cs +++ b/NzbDrone.Update/UpdateEngine/BackupAndRestore.cs @@ -31,7 +31,6 @@ public void BackUp(string source) public void Restore(string target) { - //TODO:this should ignore single file failures. _logger.Info("Attempting to rollback upgrade"); _diskProvider.CopyFolder(_appFolderInfo.GetUpdateBackUpFolder(), target); } diff --git a/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs b/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs index ffafff691..0323360cc 100644 --- a/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs +++ b/NzbDrone.Update/UpdateEngine/InstallUpdateService.cs @@ -64,6 +64,7 @@ public void Start(string installationFolder) try { + _diskProvider.EmptyFolder(installationFolder); _diskProvider.CopyFolder(_appFolderInfo.GetUpdatePackageFolder(), installationFolder); } catch (Exception e) diff --git a/NzbDrone.Wix/configuration.xml b/NzbDrone.Wix/configuration.xml new file mode 100644 index 000000000..fdf4f44bf --- /dev/null +++ b/NzbDrone.Wix/configuration.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NzbDrone.Wix/dotNetInstaller.exe b/NzbDrone.Wix/dotNetInstaller.exe new file mode 100644 index 000000000..a0dd60f8b Binary files /dev/null and b/NzbDrone.Wix/dotNetInstaller.exe differ diff --git a/NzbDrone.Wix/nzbdrone.wix.build.bat b/NzbDrone.Wix/nzbdrone.wix.build.bat new file mode 100644 index 000000000..f031916c2 --- /dev/null +++ b/NzbDrone.Wix/nzbdrone.wix.build.bat @@ -0,0 +1,8 @@ +rd _raw /s /q +rd _setup /s /q +xcopy ..\SyntikX.Client\bin\release\*.* _raw\ /S /V /I /F /R + +"C:\Program Files (x86)\WiX Toolset v3.6\bin\candle.exe" -nologo "syntik.wix.build.wxs" -out "_setup\SyntikX.Wix.wixobj" -ext WixNetFxExtension -ext WixUIExtension +"C:\Program Files (x86)\WiX Toolset v3.6\bin\light.exe" -nologo "_setup\SyntikX.Wix.wixobj" -out "_setup\SyntikX.msi" -ext WixNetFxExtension -ext WixUIExtension + +pause \ No newline at end of file diff --git a/NzbDrone.Wix/nzbdrone.wix.build.debug.bat b/NzbDrone.Wix/nzbdrone.wix.build.debug.bat new file mode 100644 index 000000000..abf84ccaf --- /dev/null +++ b/NzbDrone.Wix/nzbdrone.wix.build.debug.bat @@ -0,0 +1,10 @@ +rd _raw /s /q +rd _setup /s /q +xcopy ..\SyntikX.Client\bin\debug\*.* _raw\ /S /V /I /F /R + +SET BUILD_NUMBER=1.9.9.9 + +"C:\Program Files (x86)\WiX Toolset v3.6\bin\candle.exe" -nologo "syntik.wix.build.wxs" -out "_setup\SyntikX.Wix.wixobj" -ext WixNetFxExtension -ext WixUIExtension +"C:\Program Files (x86)\WiX Toolset v3.6\bin\light.exe" -nologo "_setup\SyntikX.Wix.wixobj" -out "_setup\SyntikX.Wix.msi" -ext WixNetFxExtension -ext WixUIExtension + +pause \ No newline at end of file diff --git a/NzbDrone.Wix/nzbdrone.wix.build.wxs b/NzbDrone.Wix/nzbdrone.wix.build.wxs new file mode 100644 index 000000000..36284be23 --- /dev/null +++ b/NzbDrone.Wix/nzbdrone.wix.build.wxs @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NETFRAMEWORK40FULL + + + + + + + + + \ No newline at end of file diff --git a/NzbDrone.ncrunchsolution b/NzbDrone.ncrunchsolution index 6cb47a29a..098d74afe 100644 --- a/NzbDrone.ncrunchsolution +++ b/NzbDrone.ncrunchsolution @@ -1,12 +1,13 @@ 1 - False + True true + true UseDynamicAnalysis Disabled Disabled Disabled - 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 + 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= \ No newline at end of file diff --git a/NzbDrone.sln b/NzbDrone.sln index 4fa697bac..f118155ff 100644 --- a/NzbDrone.sln +++ b/NzbDrone.sln @@ -56,92 +56,228 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.Host", "NzbDrone.H EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Host", "Host", "{486ADF86-DD89-4E19-B805-9D94F19800D9}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NzbDrone.SignalR", "NzbDrone.SignalR\NzbDrone.SignalR.csproj", "{7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Debug|Any CPU.ActiveCfg = Debug|x86 + {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Debug|Mixed Platforms.Build.0 = Debug|x86 {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Debug|x86.ActiveCfg = Debug|x86 {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Debug|x86.Build.0 = Debug|x86 + {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Release|Any CPU.ActiveCfg = Release|x86 + {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Release|Mixed Platforms.Build.0 = Release|x86 {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Release|x86.ActiveCfg = Release|x86 {D12F7F2F-8A3C-415F-88FA-6DD061A84869}.Release|x86.Build.0 = Release|x86 + {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Debug|Mixed Platforms.Build.0 = Debug|x86 {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Debug|x86.ActiveCfg = Debug|x86 {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Debug|x86.Build.0 = Debug|x86 + {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Release|Any CPU.ActiveCfg = Release|x86 + {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Release|Mixed Platforms.Build.0 = Release|x86 {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Release|x86.ActiveCfg = Release|x86 {FF5EE3B6-913B-47CE-9CEB-11C51B4E1205}.Release|x86.Build.0 = Release|x86 + {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Debug|Any CPU.ActiveCfg = Debug|x86 + {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Debug|Mixed Platforms.Build.0 = Debug|x86 {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Debug|x86.ActiveCfg = Debug|x86 {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Debug|x86.Build.0 = Debug|x86 + {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|Any CPU.ActiveCfg = Release|x86 + {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|Mixed Platforms.Build.0 = Release|x86 {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|x86.ActiveCfg = Release|x86 {193ADD3B-792B-4173-8E4C-5A3F8F0237F0}.Release|x86.Build.0 = Release|x86 + {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|Any CPU.ActiveCfg = Debug|x86 + {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|Mixed Platforms.Build.0 = Debug|x86 {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|x86.ActiveCfg = Debug|x86 {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Debug|x86.Build.0 = Debug|x86 + {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Any CPU.ActiveCfg = Release|x86 + {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|Mixed Platforms.Build.0 = Release|x86 {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.ActiveCfg = Release|x86 {C0EA1A40-91AD-4EEB-BD16-2DDDEBD20AE5}.Release|x86.Build.0 = Release|x86 + {FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|Mixed Platforms.Build.0 = Debug|x86 {FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x86.ActiveCfg = Debug|x86 {FAFB5948-A222-4CF6-AD14-026BE7564802}.Debug|x86.Build.0 = Debug|x86 + {FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Any CPU.ActiveCfg = Release|x86 + {FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|Mixed Platforms.Build.0 = Release|x86 {FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x86.ActiveCfg = Release|x86 {FAFB5948-A222-4CF6-AD14-026BE7564802}.Release|x86.Build.0 = Release|x86 + {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Debug|Any CPU.ActiveCfg = Debug|x86 + {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Debug|Mixed Platforms.Build.0 = Debug|x86 {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Debug|x86.ActiveCfg = Debug|x86 {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Debug|x86.Build.0 = Debug|x86 + {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Release|Any CPU.ActiveCfg = Release|x86 + {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Release|Mixed Platforms.Build.0 = Release|x86 {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Release|x86.ActiveCfg = Release|x86 {4CCC53CD-8D5E-4CC4-97D2-5C9312AC2BD7}.Release|x86.Build.0 = Release|x86 + {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Debug|Any CPU.ActiveCfg = Debug|x86 + {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Debug|Mixed Platforms.Build.0 = Debug|x86 {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Debug|x86.ActiveCfg = Debug|x86 {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Debug|x86.Build.0 = Debug|x86 + {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Release|Any CPU.ActiveCfg = Release|x86 + {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Release|Mixed Platforms.Build.0 = Release|x86 {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Release|x86.ActiveCfg = Release|x86 {35388E8E-0CDB-4A84-AD16-E4B6EFDA5D97}.Release|x86.Build.0 = Release|x86 + {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Debug|Any CPU.ActiveCfg = Debug|x86 + {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Debug|Mixed Platforms.Build.0 = Debug|x86 {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Debug|x86.ActiveCfg = Debug|x86 {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Debug|x86.Build.0 = Debug|x86 + {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Release|Any CPU.ActiveCfg = Release|x86 + {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Release|Mixed Platforms.Build.0 = Release|x86 {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Release|x86.ActiveCfg = Release|x86 {F2BE0FDF-6E47-4827-A420-DD4EF82407F8}.Release|x86.Build.0 = Release|x86 + {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Debug|Any CPU.ActiveCfg = Debug|x86 + {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Debug|Mixed Platforms.Build.0 = Debug|x86 {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Debug|x86.ActiveCfg = Debug|x86 {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Debug|x86.Build.0 = Debug|x86 + {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Release|Any CPU.ActiveCfg = Release|x86 + {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Release|Mixed Platforms.Build.0 = Release|x86 {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Release|x86.ActiveCfg = Release|x86 {BEC74619-DDBB-4FBA-B517-D3E20AFC9997}.Release|x86.Build.0 = Release|x86 + {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Debug|Any CPU.ActiveCfg = Debug|x86 + {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Debug|Mixed Platforms.Build.0 = Debug|x86 {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Debug|x86.ActiveCfg = Debug|x86 {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Debug|x86.Build.0 = Debug|x86 + {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Release|Any CPU.ActiveCfg = Release|x86 + {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Release|Mixed Platforms.Build.0 = Release|x86 {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Release|x86.ActiveCfg = Release|x86 {CADDFCE0-7509-4430-8364-2074E1EEFCA2}.Release|x86.Build.0 = Release|x86 + {6BCE712F-846D-4846-9D1B-A66B858DA755}.Debug|Any CPU.ActiveCfg = Debug|x86 + {6BCE712F-846D-4846-9D1B-A66B858DA755}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {6BCE712F-846D-4846-9D1B-A66B858DA755}.Debug|Mixed Platforms.Build.0 = Debug|x86 {6BCE712F-846D-4846-9D1B-A66B858DA755}.Debug|x86.ActiveCfg = Debug|x86 {6BCE712F-846D-4846-9D1B-A66B858DA755}.Debug|x86.Build.0 = Debug|x86 + {6BCE712F-846D-4846-9D1B-A66B858DA755}.Release|Any CPU.ActiveCfg = Release|x86 + {6BCE712F-846D-4846-9D1B-A66B858DA755}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {6BCE712F-846D-4846-9D1B-A66B858DA755}.Release|Mixed Platforms.Build.0 = Release|x86 {6BCE712F-846D-4846-9D1B-A66B858DA755}.Release|x86.ActiveCfg = Release|x86 {6BCE712F-846D-4846-9D1B-A66B858DA755}.Release|x86.Build.0 = Release|x86 + {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Debug|Any CPU.ActiveCfg = Debug|x86 + {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Debug|Mixed Platforms.Build.0 = Debug|x86 {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Debug|x86.ActiveCfg = Debug|x86 {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Debug|x86.Build.0 = Debug|x86 + {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Release|Any CPU.ActiveCfg = Release|x86 + {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Release|Mixed Platforms.Build.0 = Release|x86 {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Release|x86.ActiveCfg = Release|x86 {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4}.Release|x86.Build.0 = Release|x86 + {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Debug|Mixed Platforms.Build.0 = Debug|x86 {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Debug|x86.ActiveCfg = Debug|x86 {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Debug|x86.Build.0 = Debug|x86 + {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Release|Any CPU.ActiveCfg = Release|x86 + {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Release|Mixed Platforms.Build.0 = Release|x86 {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Release|x86.ActiveCfg = Release|x86 {FD286DF8-2D3A-4394-8AD5-443FADE55FB2}.Release|x86.Build.0 = Release|x86 + {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Debug|Any CPU.ActiveCfg = Debug|x86 + {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Debug|Mixed Platforms.Build.0 = Debug|x86 {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Debug|x86.ActiveCfg = Debug|x86 {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Debug|x86.Build.0 = Debug|x86 + {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Release|Any CPU.ActiveCfg = Release|x86 + {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Release|Mixed Platforms.Build.0 = Release|x86 {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Release|x86.ActiveCfg = Release|x86 {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976}.Release|x86.Build.0 = Release|x86 + {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Debug|Any CPU.ActiveCfg = Debug|x86 + {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Debug|Mixed Platforms.Build.0 = Debug|x86 {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Debug|x86.ActiveCfg = Debug|x86 {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Debug|x86.Build.0 = Debug|x86 + {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Release|Any CPU.ActiveCfg = Release|x86 + {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Release|Mixed Platforms.Build.0 = Release|x86 {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Release|x86.ActiveCfg = Release|x86 {D18A5DEB-5102-4775-A1AF-B75DAAA8907B}.Release|x86.Build.0 = Release|x86 + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Debug|Any CPU.ActiveCfg = Debug|x86 + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Debug|Mixed Platforms.Build.0 = Debug|x86 {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Debug|x86.ActiveCfg = Debug|x86 {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Debug|x86.Build.0 = Debug|x86 + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Release|Any CPU.ActiveCfg = Release|x86 + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Release|Mixed Platforms.Build.0 = Release|x86 {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Release|x86.ActiveCfg = Release|x86 {F6FC6BE7-0847-4817-A1ED-223DC647C3D7}.Release|x86.Build.0 = Release|x86 + {CBF6B8B0-A015-413A-8C86-01238BB45770}.Debug|Any CPU.ActiveCfg = Debug|x86 + {CBF6B8B0-A015-413A-8C86-01238BB45770}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {CBF6B8B0-A015-413A-8C86-01238BB45770}.Debug|Mixed Platforms.Build.0 = Debug|x86 {CBF6B8B0-A015-413A-8C86-01238BB45770}.Debug|x86.ActiveCfg = Debug|x86 {CBF6B8B0-A015-413A-8C86-01238BB45770}.Debug|x86.Build.0 = Debug|x86 + {CBF6B8B0-A015-413A-8C86-01238BB45770}.Release|Any CPU.ActiveCfg = Release|x86 + {CBF6B8B0-A015-413A-8C86-01238BB45770}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {CBF6B8B0-A015-413A-8C86-01238BB45770}.Release|Mixed Platforms.Build.0 = Release|x86 {CBF6B8B0-A015-413A-8C86-01238BB45770}.Release|x86.ActiveCfg = Release|x86 {CBF6B8B0-A015-413A-8C86-01238BB45770}.Release|x86.Build.0 = Release|x86 + {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Debug|Any CPU.ActiveCfg = Debug|x86 + {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Debug|Mixed Platforms.Build.0 = Debug|x86 {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Debug|x86.ActiveCfg = Debug|x86 {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Debug|x86.Build.0 = Debug|x86 + {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Release|Any CPU.ActiveCfg = Release|x86 + {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Release|Mixed Platforms.Build.0 = Release|x86 {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Release|x86.ActiveCfg = Release|x86 {8CEFECD0-A6C2-498F-98B1-3FBE5820F9AB}.Release|x86.Build.0 = Release|x86 + {B1784698-592E-4132-BDFA-9817409E3A96}.Debug|Any CPU.ActiveCfg = Debug|x86 + {B1784698-592E-4132-BDFA-9817409E3A96}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {B1784698-592E-4132-BDFA-9817409E3A96}.Debug|Mixed Platforms.Build.0 = Debug|x86 {B1784698-592E-4132-BDFA-9817409E3A96}.Debug|x86.ActiveCfg = Debug|x86 {B1784698-592E-4132-BDFA-9817409E3A96}.Debug|x86.Build.0 = Debug|x86 + {B1784698-592E-4132-BDFA-9817409E3A96}.Release|Any CPU.ActiveCfg = Release|x86 + {B1784698-592E-4132-BDFA-9817409E3A96}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {B1784698-592E-4132-BDFA-9817409E3A96}.Release|Mixed Platforms.Build.0 = Release|x86 {B1784698-592E-4132-BDFA-9817409E3A96}.Release|x86.ActiveCfg = Release|x86 {B1784698-592E-4132-BDFA-9817409E3A96}.Release|x86.Build.0 = Release|x86 + {95C11A9E-56ED-456A-8447-2C89C1139266}.Debug|Any CPU.ActiveCfg = Debug|x86 + {95C11A9E-56ED-456A-8447-2C89C1139266}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {95C11A9E-56ED-456A-8447-2C89C1139266}.Debug|Mixed Platforms.Build.0 = Debug|x86 {95C11A9E-56ED-456A-8447-2C89C1139266}.Debug|x86.ActiveCfg = Debug|x86 {95C11A9E-56ED-456A-8447-2C89C1139266}.Debug|x86.Build.0 = Debug|x86 + {95C11A9E-56ED-456A-8447-2C89C1139266}.Release|Any CPU.ActiveCfg = Release|x86 + {95C11A9E-56ED-456A-8447-2C89C1139266}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {95C11A9E-56ED-456A-8447-2C89C1139266}.Release|Mixed Platforms.Build.0 = Release|x86 {95C11A9E-56ED-456A-8447-2C89C1139266}.Release|x86.ActiveCfg = Release|x86 {95C11A9E-56ED-456A-8447-2C89C1139266}.Release|x86.Build.0 = Release|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Debug|Any CPU.ActiveCfg = Debug|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Debug|x86.ActiveCfg = Debug|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Debug|x86.Build.0 = Debug|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Release|Any CPU.ActiveCfg = Release|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Release|Mixed Platforms.Build.0 = Release|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Release|x86.ActiveCfg = Release|x86 + {7C2CC69F-5CA0-4E5C-85CB-983F9F6C3B36}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -159,9 +295,9 @@ Global {CADDFCE0-7509-4430-8364-2074E1EEFCA2} = {47697CDB-27B6-4B05-B4F8-0CBE6F6EDF97} {6BCE712F-846D-4846-9D1B-A66B858DA755} = {F9E67978-5CD6-4A5F-827B-4249711C0B02} {700D0B95-95CD-43F3-B6C9-FAA0FC1358D4} = {F9E67978-5CD6-4A5F-827B-4249711C0B02} - {D12F7F2F-8A3C-415F-88FA-6DD061A84869} = {486ADF86-DD89-4E19-B805-9D94F19800D9} {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976} = {486ADF86-DD89-4E19-B805-9D94F19800D9} {95C11A9E-56ED-456A-8447-2C89C1139266} = {486ADF86-DD89-4E19-B805-9D94F19800D9} + {D12F7F2F-8A3C-415F-88FA-6DD061A84869} = {486ADF86-DD89-4E19-B805-9D94F19800D9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35 diff --git a/NzbDrone.sln.DotSettings b/NzbDrone.sln.DotSettings index 2aded5528..a7de7ce4f 100644 --- a/NzbDrone.sln.DotSettings +++ b/NzbDrone.sln.DotSettings @@ -11,6 +11,8 @@ WARNING WARNING HINT + Fixture + ^(.*)\.Test(\..*)$ <?xml version="1.0" encoding="utf-16"?><Profile name="NzbDrone"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>IMPLICIT_EXCEPT_SIMPLE_TYPES</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSUpdateFileHeader>True</CSUpdateFileHeader><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReorderTypeMembers>True</CSReorderTypeMembers><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags></Profile> NzbDrone True @@ -47,6 +49,8 @@ True True True + 4 + True Backbone model 5 True diff --git a/NzbDrone/Properties/Resources.Designer.cs b/NzbDrone/Properties/Resources.Designer.cs index d47a0a7ea..65584111d 100644 --- a/NzbDrone/Properties/Resources.Designer.cs +++ b/NzbDrone/Properties/Resources.Designer.cs @@ -9,9 +9,6 @@ //------------------------------------------------------------------------------ namespace NzbDrone.Properties { - using System; - - /// /// A strongly-typed resource class, for looking up localized strings, etc. /// diff --git a/NzbDrone/WindowsApp.cs b/NzbDrone/WindowsApp.cs index 4d4dc9d69..5b20de23b 100644 --- a/NzbDrone/WindowsApp.cs +++ b/NzbDrone/WindowsApp.cs @@ -2,6 +2,7 @@ using System.Windows.Forms; using NLog; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Instrumentation; using NzbDrone.Host; using NzbDrone.SysTray; @@ -9,12 +10,17 @@ namespace NzbDrone { public static class WindowsApp { + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public static void Main(string[] args) { try { - var container = Bootstrap.Start(new StartupArguments(args), new MessageBoxUserAlert()); + var startupArgs = new StartupArguments(args); + + LogTargets.Register(startupArgs, false, true); + + var container = Bootstrap.Start(startupArgs, new MessageBoxUserAlert()); container.Register(); container.Resolve().Start(); } @@ -23,9 +29,10 @@ public static void Main(string[] args) } catch (Exception e) { + Logger.FatalException("EPIC FAIL: " + e.Message, e); var message = string.Format("{0}: {1}", e.GetType().Name, e.Message); MessageBox.Show(text: message, buttons: MessageBoxButtons.OK, icon: MessageBoxIcon.Error, caption: "Epic Fail!"); } } } -} \ No newline at end of file +} diff --git a/ServiceHelpers/ServiceInstall/ServiceInstall.ncrunchproject b/ServiceHelpers/ServiceInstall/ServiceInstall.ncrunchproject index 73e1ebbb9..b2eed192e 100644 --- a/ServiceHelpers/ServiceInstall/ServiceInstall.ncrunchproject +++ b/ServiceHelpers/ServiceInstall/ServiceInstall.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,10 +12,12 @@ false true true - 60000 - - + 5000 + Debug + x86 - AutoDetect + x86 + STA + x86 PostBuildEventDisabled \ No newline at end of file diff --git a/ServiceHelpers/ServiceUninstall/ServiceUninstall.ncrunchproject b/ServiceHelpers/ServiceUninstall/ServiceUninstall.ncrunchproject index 73e1ebbb9..b2eed192e 100644 --- a/ServiceHelpers/ServiceUninstall/ServiceUninstall.ncrunchproject +++ b/ServiceHelpers/ServiceUninstall/ServiceUninstall.ncrunchproject @@ -1,8 +1,8 @@ false - false + true false - true + false false false false @@ -12,10 +12,12 @@ false true true - 60000 - - + 5000 + Debug + x86 - AutoDetect + x86 + STA + x86 PostBuildEventDisabled \ No newline at end of file diff --git a/UI/AddSeries/AddSeriesTemplate.html b/UI/AddSeries/AddSeriesTemplate.html index 87715f2b1..696eb92f7 100644 --- a/UI/AddSeries/AddSeriesTemplate.html +++ b/UI/AddSeries/AddSeriesTemplate.html @@ -1,9 +1,9 @@ {{#if folder.path}} -
-
- {{folder.path}} -
+
+
+ {{folder.path}}
+
{{/if}}