From eb7dda0629b3b719cea817f46e31cb9a1e425d8d Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 16 Sep 2013 11:14:09 -0700 Subject: [PATCH 01/49] Added NMA support, also added generic HttpStatusCode handling --- .../ErrorManagement/NzbDroneErrorPipeline.cs | 1 + .../Exceptions/BadRequestException.cs | 20 ++++++ .../Exceptions/DownstreamException.cs | 21 ++++++ .../NzbDroneClientException.cs | 2 +- .../Exceptions/StatusCodeToExceptions.cs | 34 +++++++++ .../MetadataSource/Trakt/TraktException.cs | 1 + .../NotifyMyAndroid/NotifyMyAndroid.cs | 47 +++++++++++++ .../NotifyMyAndroidPriority.cs | 11 +++ .../NotifyMyAndroid/NotifyMyAndroidProxy.cs | 69 +++++++++++++++++++ .../NotifyMyAndroidSettings.cs | 22 ++++++ .../TestNotifyMyAndroidCommand.cs | 18 +++++ NzbDrone.Core/NzbDrone.Core.csproj | 10 ++- 12 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 NzbDrone.Core/Exceptions/BadRequestException.cs create mode 100644 NzbDrone.Core/Exceptions/DownstreamException.cs rename NzbDrone.Core/{ => Exceptions}/NzbDroneClientException.cs (93%) create mode 100644 NzbDrone.Core/Exceptions/StatusCodeToExceptions.cs create mode 100644 NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs create mode 100644 NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidPriority.cs create mode 100644 NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidProxy.cs create mode 100644 NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidSettings.cs create mode 100644 NzbDrone.Core/Notifications/NotifyMyAndroid/TestNotifyMyAndroidCommand.cs diff --git a/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs b/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs index 47d79c849..ebc49505f 100644 --- a/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs +++ b/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs @@ -4,6 +4,7 @@ using NLog; using Nancy; using NzbDrone.Api.Extensions; using NzbDrone.Core; +using NzbDrone.Core.Exceptions; using HttpStatusCode = Nancy.HttpStatusCode; namespace NzbDrone.Api.ErrorManagement diff --git a/NzbDrone.Core/Exceptions/BadRequestException.cs b/NzbDrone.Core/Exceptions/BadRequestException.cs new file mode 100644 index 000000000..07d23d263 --- /dev/null +++ b/NzbDrone.Core/Exceptions/BadRequestException.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Exceptions +{ + public class BadRequestException : DownstreamException + { + public BadRequestException(HttpStatusCode statusCode, string message) : base(statusCode, message) + { + } + + public BadRequestException(HttpStatusCode statusCode, string message, params object[] args) : base(statusCode, message, args) + { + } + } +} diff --git a/NzbDrone.Core/Exceptions/DownstreamException.cs b/NzbDrone.Core/Exceptions/DownstreamException.cs new file mode 100644 index 000000000..0ce8f1a94 --- /dev/null +++ b/NzbDrone.Core/Exceptions/DownstreamException.cs @@ -0,0 +1,21 @@ +using System.Net; +using NzbDrone.Common.Exceptions; + +namespace NzbDrone.Core.Exceptions +{ + public class DownstreamException : NzbDroneException + { + public HttpStatusCode StatusCode { get; private set; } + + public DownstreamException(HttpStatusCode statusCode, string message, params object[] args) : base(message, args) + { + StatusCode = statusCode; + } + + public DownstreamException(HttpStatusCode statusCode, string message) + : base(message) + { + StatusCode = statusCode; + } + } +} diff --git a/NzbDrone.Core/NzbDroneClientException.cs b/NzbDrone.Core/Exceptions/NzbDroneClientException.cs similarity index 93% rename from NzbDrone.Core/NzbDroneClientException.cs rename to NzbDrone.Core/Exceptions/NzbDroneClientException.cs index cc94caffc..d9225eb2c 100644 --- a/NzbDrone.Core/NzbDroneClientException.cs +++ b/NzbDrone.Core/Exceptions/NzbDroneClientException.cs @@ -1,7 +1,7 @@ using System.Net; using NzbDrone.Common.Exceptions; -namespace NzbDrone.Core +namespace NzbDrone.Core.Exceptions { public class NzbDroneClientException : NzbDroneException { diff --git a/NzbDrone.Core/Exceptions/StatusCodeToExceptions.cs b/NzbDrone.Core/Exceptions/StatusCodeToExceptions.cs new file mode 100644 index 000000000..ab394a9a6 --- /dev/null +++ b/NzbDrone.Core/Exceptions/StatusCodeToExceptions.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; + +namespace NzbDrone.Core.Exceptions +{ + public static class StatusCodeToExceptions + { + public static void VerifyStatusCode(this HttpStatusCode statusCode, string message = null) + { + if (String.IsNullOrEmpty(message)) + { + message = statusCode.ToString(); + } + + switch (statusCode) + { + case HttpStatusCode.BadRequest: + throw new BadRequestException(statusCode, message); + + case HttpStatusCode.Unauthorized: + throw new UnauthorizedAccessException(message); + + case HttpStatusCode.PaymentRequired: + throw new DownstreamException(statusCode, message); + + case HttpStatusCode.InternalServerError: + throw new DownstreamException(statusCode, message); + } + } + } +} diff --git a/NzbDrone.Core/MetadataSource/Trakt/TraktException.cs b/NzbDrone.Core/MetadataSource/Trakt/TraktException.cs index 999122301..3d51fe8ab 100644 --- a/NzbDrone.Core/MetadataSource/Trakt/TraktException.cs +++ b/NzbDrone.Core/MetadataSource/Trakt/TraktException.cs @@ -1,4 +1,5 @@ using System.Net; +using NzbDrone.Core.Exceptions; namespace NzbDrone.Core.MetadataSource.Trakt { diff --git a/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs new file mode 100644 index 000000000..22f4656f7 --- /dev/null +++ b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroid.cs @@ -0,0 +1,47 @@ +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Notifications.NotifyMyAndroid +{ + public class NotifyMyAndroid : NotificationBase + { + private readonly INotifyMyAndroidProxy _notifyMyAndroidProxy; + + public NotifyMyAndroid(INotifyMyAndroidProxy notifyMyAndroidProxy) + { + _notifyMyAndroidProxy = notifyMyAndroidProxy; + } + + public override string Name + { + get { return "NotifyMyAndroid"; } + } + + public override string ImplementationName + { + get { return "NotifyMyAndroid"; } + } + + public override string Link + { + get { return "http://www.notifymyandroid.com/"; } + } + + public override void OnGrab(string message) + { + const string title = "Episode Grabbed"; + + _notifyMyAndroidProxy.SendNotification(title, message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority); + } + + public override void OnDownload(string message, Series series) + { + const string title = "Episode Downloaded"; + + _notifyMyAndroidProxy.SendNotification(title, message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority); + } + + public override void AfterRename(Series series) + { + } + } +} diff --git a/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidPriority.cs b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidPriority.cs new file mode 100644 index 000000000..fd91e91d5 --- /dev/null +++ b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidPriority.cs @@ -0,0 +1,11 @@ +namespace NzbDrone.Core.Notifications.NotifyMyAndroid +{ + public enum NotifyMyAndroidPriority + { + VeryLow = -2, + Moderate = -1, + Normal = 0, + High = 1, + Emergency = 2 + } +} diff --git a/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidProxy.cs b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidProxy.cs new file mode 100644 index 000000000..170bf8522 --- /dev/null +++ b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidProxy.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using System.Net; +using System.Xml.Linq; +using NLog.LayoutRenderers; +using NzbDrone.Core.Exceptions; +using NzbDrone.Core.Messaging; +using NzbDrone.Core.Messaging.Commands; +using RestSharp; +using NzbDrone.Core.Rest; + +namespace NzbDrone.Core.Notifications.NotifyMyAndroid +{ + public interface INotifyMyAndroidProxy + { + void SendNotification(string title, string message, string apiKye, NotifyMyAndroidPriority priority); + } + + public class NotifyMyAndroidProxy : INotifyMyAndroidProxy, IExecute + { + private const string URL = "https://www.notifymyandroid.com/publicapi"; + + public void SendNotification(string title, string message, string apiKey, NotifyMyAndroidPriority priority) + { + var client = new RestClient(URL); + var request = new RestRequest("notify", Method.POST); + request.RequestFormat = DataFormat.Xml; + request.AddParameter("apikey", apiKey); + request.AddParameter("application", "NzbDrone"); + request.AddParameter("event", title); + request.AddParameter("description", message); + request.AddParameter("priority", (int)priority); + + var response = client.ExecuteAndValidate(request); + ValidateResponse(response); + } + + private void Verify(string apiKey) + { + var client = new RestClient(URL); + var request = new RestRequest("verify", Method.GET); + request.RequestFormat = DataFormat.Xml; + request.AddParameter("apikey", apiKey, ParameterType.GetOrPost); + + var response = client.ExecuteAndValidate(request); + ValidateResponse(response); + } + + private void ValidateResponse(IRestResponse response) + { + var xDoc = XDocument.Parse(response.Content); + var nma = xDoc.Descendants("nma").Single(); + var error = nma.Descendants("error").SingleOrDefault(); + + if (error != null) + { + ((HttpStatusCode)Convert.ToInt32(error.Attribute("code").Value)).VerifyStatusCode(error.Value); + } + } + + public void Execute(TestNotifyMyAndroidCommand message) + { + const string title = "Test Notification"; + const string body = "This is a test message from NzbDrone"; + Verify(message.ApiKey); + SendNotification(title, body, message.ApiKey, (NotifyMyAndroidPriority)message.Priority); + } + } +} diff --git a/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidSettings.cs b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidSettings.cs new file mode 100644 index 000000000..c8c941239 --- /dev/null +++ b/NzbDrone.Core/Notifications/NotifyMyAndroid/NotifyMyAndroidSettings.cs @@ -0,0 +1,22 @@ +using System; +using NzbDrone.Core.Annotations; + +namespace NzbDrone.Core.Notifications.NotifyMyAndroid +{ + public class NotifyMyAndroidSettings : INotifcationSettings + { + [FieldDefinition(0, Label = "API Key", HelpLink = "http://www.notifymyandroid.com/")] + public String ApiKey { get; set; } + + [FieldDefinition(1, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(NotifyMyAndroidPriority))] + public Int32 Priority { get; set; } + + public bool IsValid + { + get + { + return !String.IsNullOrWhiteSpace(ApiKey) && Priority != null & Priority >= -1 && Priority <= 2; + } + } + } +} diff --git a/NzbDrone.Core/Notifications/NotifyMyAndroid/TestNotifyMyAndroidCommand.cs b/NzbDrone.Core/Notifications/NotifyMyAndroid/TestNotifyMyAndroidCommand.cs new file mode 100644 index 000000000..bb93b8fdd --- /dev/null +++ b/NzbDrone.Core/Notifications/NotifyMyAndroid/TestNotifyMyAndroidCommand.cs @@ -0,0 +1,18 @@ +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Notifications.NotifyMyAndroid +{ + public class TestNotifyMyAndroidCommand : Command + { + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string ApiKey { get; set; } + public int Priority { get; set; } + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index dae681ba9..acb50ac6d 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -210,6 +210,10 @@ + + + + @@ -245,7 +249,11 @@ - + + + + + From 48c06de098a8be5d39b320ac9064adab1f1931b0 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Mon, 16 Sep 2013 21:52:02 -0700 Subject: [PATCH 02/49] updated CommandExecutedEvent to be handled sync. --- .../NzbDrone.Common.Test.csproj | 1 - .../Messaging/Events/EventAggregatorFixture.cs | 17 ++++++++--------- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 + .../Instrumentation/SetLoggingLevel.cs | 1 - NzbDrone.Core/Jobs/Scheduler.cs | 1 - NzbDrone.Core/Jobs/TaskManager.cs | 7 +++---- 6 files changed, 12 insertions(+), 16 deletions(-) rename NzbDrone.Common.Test/MessagingTests/MessageAggregatorEventTests.cs => NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs (91%) diff --git a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj index 2347a37bf..1ec40855c 100644 --- a/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj +++ b/NzbDrone.Common.Test/NzbDrone.Common.Test.csproj @@ -67,7 +67,6 @@ - diff --git a/NzbDrone.Common.Test/MessagingTests/MessageAggregatorEventTests.cs b/NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs similarity index 91% rename from NzbDrone.Common.Test/MessagingTests/MessageAggregatorEventTests.cs rename to NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs index 30a177c81..f1fc6bee1 100644 --- a/NzbDrone.Common.Test/MessagingTests/MessageAggregatorEventTests.cs +++ b/NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs @@ -4,15 +4,15 @@ using System.Threading; using FluentAssertions; using Moq; using NUnit.Framework; +using NzbDrone.Common; using NzbDrone.Common.Messaging; -using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Events; using NzbDrone.Test.Common; -namespace NzbDrone.Common.Test.MessagingTests +namespace NzbDrone.Core.Test.Messaging.Events { [TestFixture] - public class MessageAggregatorEventTests : TestBase + public class EventAggregatorFixture : TestBase { private Mock> HandlerA1; private Mock> HandlerA2; @@ -119,11 +119,11 @@ namespace NzbDrone.Common.Test.MessagingTests AsyncHandlerA1.Setup(c => c.HandleAsync(It.IsAny())) .Callback(c => - { - var id = counter.Start(); - Thread.Sleep(1000); - counter.Stop(id); - }); + { + var id = counter.Start(); + Thread.Sleep(1000); + counter.Stop(id); + }); Subject.PublishEvent(eventA); @@ -143,5 +143,4 @@ namespace NzbDrone.Common.Test.MessagingTests { } - } \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 5035ea37e..c855d7ad9 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -150,6 +150,7 @@ + diff --git a/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs b/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs index 61412e8fb..a436c0fc1 100644 --- a/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs +++ b/NzbDrone.Core/Instrumentation/SetLoggingLevel.cs @@ -6,7 +6,6 @@ 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 diff --git a/NzbDrone.Core/Jobs/Scheduler.cs b/NzbDrone.Core/Jobs/Scheduler.cs index 2b8b42086..e0d6e8194 100644 --- a/NzbDrone.Core/Jobs/Scheduler.cs +++ b/NzbDrone.Core/Jobs/Scheduler.cs @@ -3,7 +3,6 @@ using System.Threading; using System.Threading.Tasks; using NLog; using NzbDrone.Core.Lifecycle; -using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Events; using Timer = System.Timers.Timer; diff --git a/NzbDrone.Core/Jobs/TaskManager.cs b/NzbDrone.Core/Jobs/TaskManager.cs index 9d1a316c5..a04931ac3 100644 --- a/NzbDrone.Core/Jobs/TaskManager.cs +++ b/NzbDrone.Core/Jobs/TaskManager.cs @@ -9,7 +9,6 @@ using NzbDrone.Core.Indexers; 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; @@ -23,7 +22,7 @@ namespace NzbDrone.Core.Jobs IList GetPending(); } - public class TaskManager : ITaskManager, IHandle, IHandleAsync, IHandleAsync + public class TaskManager : ITaskManager, IHandle, IHandle, IHandleAsync { private readonly IScheduledTaskRepository _scheduledTaskRepository; private readonly IConfigService _configService; @@ -79,7 +78,7 @@ namespace NzbDrone.Core.Jobs } } - public void HandleAsync(CommandExecutedEvent message) + public void Handle(CommandExecutedEvent message) { var scheduledTask = _scheduledTaskRepository.All().SingleOrDefault(c => c.TypeName == message.Command.GetType().FullName); @@ -97,4 +96,4 @@ namespace NzbDrone.Core.Jobs _scheduledTaskRepository.Update(rss); } } -} \ No newline at end of file +} From 3d71c129eb44f1a41991f7b367731098940a97f7 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Mon, 16 Sep 2013 21:52:33 -0700 Subject: [PATCH 03/49] event aggregator uses threadpool to publish events --- .../Events/EventAggregatorFixture.cs | 4 ++-- .../Messaging/Events/EventAggregator.cs | 22 ++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs b/NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs index f1fc6bee1..e5bdd313a 100644 --- a/NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs +++ b/NzbDrone.Core.Test/Messaging/Events/EventAggregatorFixture.cs @@ -88,7 +88,7 @@ namespace NzbDrone.Core.Test.Messaging.Events } - [Test] + /* [Test] public void should_queue_multiple_async_events() { var eventA = new EventA(); @@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.Messaging.Events counter.WaitForAllItems(); counter.MaxThreads.Should().Be(3); - } + }*/ } diff --git a/NzbDrone.Core/Messaging/Events/EventAggregator.cs b/NzbDrone.Core/Messaging/Events/EventAggregator.cs index 7dbe1a331..891d1c8b8 100644 --- a/NzbDrone.Core/Messaging/Events/EventAggregator.cs +++ b/NzbDrone.Core/Messaging/Events/EventAggregator.cs @@ -16,11 +16,9 @@ namespace NzbDrone.Core.Messaging.Events public EventAggregator(Logger logger, IServiceFactory serviceFactory) { - var scheduler = new LimitedConcurrencyLevelTaskScheduler(3); - _logger = logger; _serviceFactory = serviceFactory; - _taskFactory = new TaskFactory(scheduler); + _taskFactory = new TaskFactory(); } public void PublishEvent(TEvent @event) where TEvent : class ,IEvent @@ -29,8 +27,26 @@ namespace NzbDrone.Core.Messaging.Events var eventName = GetEventName(@event.GetType()); +/* + int workerThreads; + int completionPortThreads; + ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); + + int maxCompletionPortThreads; + int maxWorkerThreads; + ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads); + + + int minCompletionPortThreads; + int minWorkerThreads; + ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads); + + _logger.Warn("Thread pool state WT:{0} PT:{1} MAXWT:{2} MAXPT:{3} MINWT:{4} MINPT:{5}", workerThreads, completionPortThreads, maxWorkerThreads, maxCompletionPortThreads, minWorkerThreads, minCompletionPortThreads); +*/ + _logger.Trace("Publishing {0}", eventName); + //call synchronous handlers first. foreach (var handler in _serviceFactory.BuildAll>()) { From 0794affae0f05a4fb945adae3f67a07f451a06cc Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Mon, 16 Sep 2013 22:16:16 -0700 Subject: [PATCH 04/49] removed dead code. --- .../Qualities/QualityProfileModule.cs | 4 +-- NzbDrone.Api/Qualities/QualitySizeModule.cs | 4 +-- .../DownloadedEpisodesImportService.cs | 1 - .../MediaFiles/MediaFileRepository.cs | 11 -------- NzbDrone.Core/MediaFiles/MediaFileService.cs | 12 --------- NzbDrone.Core/NzbDrone.Core.csproj | 1 - .../Qualities/QualityProfileRepository.cs | 1 - .../Qualities/QualityProfileService.cs | 1 - NzbDrone.Core/Qualities/QualitySizeService.cs | 6 ----- NzbDrone.Core/Tv/EpisodeRepository.cs | 18 ------------- NzbDrone.Core/Tv/EpisodeService.cs | 27 +------------------ NzbDrone.Core/Tv/SeasonRepository.cs | 27 ------------------- NzbDrone.Core/Tv/SeriesRepository.cs | 18 ------------- 13 files changed, 5 insertions(+), 126 deletions(-) delete mode 100644 NzbDrone.Core/Tv/SeasonRepository.cs diff --git a/NzbDrone.Api/Qualities/QualityProfileModule.cs b/NzbDrone.Api/Qualities/QualityProfileModule.cs index 1ff4379a6..a08fdb12c 100644 --- a/NzbDrone.Api/Qualities/QualityProfileModule.cs +++ b/NzbDrone.Api/Qualities/QualityProfileModule.cs @@ -8,9 +8,9 @@ namespace NzbDrone.Api.Qualities { public class QualityProfileModule : NzbDroneRestModule { - private readonly QualityProfileService _qualityProfileService; + private readonly IQualityProfileService _qualityProfileService; - public QualityProfileModule(QualityProfileService qualityProfileService) + public QualityProfileModule(IQualityProfileService qualityProfileService) : base("/qualityprofiles") { _qualityProfileService = qualityProfileService; diff --git a/NzbDrone.Api/Qualities/QualitySizeModule.cs b/NzbDrone.Api/Qualities/QualitySizeModule.cs index 27d3b329e..206bc9c51 100644 --- a/NzbDrone.Api/Qualities/QualitySizeModule.cs +++ b/NzbDrone.Api/Qualities/QualitySizeModule.cs @@ -6,9 +6,9 @@ namespace NzbDrone.Api.Qualities { public class QualitySizeModule : NzbDroneRestModule { - private readonly QualitySizeService _qualityTypeProvider; + private readonly IQualitySizeService _qualityTypeProvider; - public QualitySizeModule(QualitySizeService qualityTypeProvider) + public QualitySizeModule(IQualitySizeService qualityTypeProvider) { _qualityTypeProvider = qualityTypeProvider; diff --git a/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs b/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs index 55c6f4638..5f36b5910 100644 --- a/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs +++ b/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs @@ -8,7 +8,6 @@ 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; diff --git a/NzbDrone.Core/MediaFiles/MediaFileRepository.cs b/NzbDrone.Core/MediaFiles/MediaFileRepository.cs index 7a0839594..48c507c98 100644 --- a/NzbDrone.Core/MediaFiles/MediaFileRepository.cs +++ b/NzbDrone.Core/MediaFiles/MediaFileRepository.cs @@ -9,10 +9,8 @@ namespace NzbDrone.Core.MediaFiles { public interface IMediaFileRepository : IBasicRepository { - EpisodeFile GetFileByPath(string path); List GetFilesBySeries(int seriesId); List GetFilesBySeason(int seriesId, int seasonNumber); - bool Exists(string path); } @@ -23,11 +21,6 @@ namespace NzbDrone.Core.MediaFiles { } - public EpisodeFile GetFileByPath(string path) - { - return Query.SingleOrDefault(c => c.Path == path); - } - public List GetFilesBySeries(int seriesId) { return Query.Where(c => c.SeriesId == seriesId).ToList(); @@ -40,9 +33,5 @@ namespace NzbDrone.Core.MediaFiles .ToList(); } - public bool Exists(string path) - { - return Query.Any(c => c.Path == path); - } } } \ No newline at end of file diff --git a/NzbDrone.Core/MediaFiles/MediaFileService.cs b/NzbDrone.Core/MediaFiles/MediaFileService.cs index b963b408e..cb2e44ea8 100644 --- a/NzbDrone.Core/MediaFiles/MediaFileService.cs +++ b/NzbDrone.Core/MediaFiles/MediaFileService.cs @@ -14,8 +14,6 @@ namespace NzbDrone.Core.MediaFiles EpisodeFile Add(EpisodeFile episodeFile); void Update(EpisodeFile episodeFile); void Delete(EpisodeFile episodeFile, bool forUpgrade = false); - bool Exists(string path); - EpisodeFile GetFileByPath(string path); List GetFilesBySeries(int seriesId); List GetFilesBySeason(int seriesId, int seasonNumber); List FilterExistingFiles(List files, int seriesId); @@ -54,16 +52,6 @@ namespace NzbDrone.Core.MediaFiles _eventAggregator.PublishEvent(new EpisodeFileDeletedEvent(episodeFile, forUpgrade)); } - public bool Exists(string path) - { - return _mediaFileRepository.Exists(path); - } - - public EpisodeFile GetFileByPath(string path) - { - return _mediaFileRepository.GetFileByPath(path.Normalize()); - } - public List GetFilesBySeries(int seriesId) { return _mediaFileRepository.GetFilesBySeries(seriesId); diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index acb50ac6d..8d8c65364 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -407,7 +407,6 @@ - diff --git a/NzbDrone.Core/Qualities/QualityProfileRepository.cs b/NzbDrone.Core/Qualities/QualityProfileRepository.cs index 64e36b70b..408cc009e 100644 --- a/NzbDrone.Core/Qualities/QualityProfileRepository.cs +++ b/NzbDrone.Core/Qualities/QualityProfileRepository.cs @@ -1,5 +1,4 @@ using NzbDrone.Core.Datastore; -using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Events; diff --git a/NzbDrone.Core/Qualities/QualityProfileService.cs b/NzbDrone.Core/Qualities/QualityProfileService.cs index de79e36e9..82baaaa39 100644 --- a/NzbDrone.Core/Qualities/QualityProfileService.cs +++ b/NzbDrone.Core/Qualities/QualityProfileService.cs @@ -2,7 +2,6 @@ using System.Linq; using NLog; using NzbDrone.Core.Lifecycle; -using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; diff --git a/NzbDrone.Core/Qualities/QualitySizeService.cs b/NzbDrone.Core/Qualities/QualitySizeService.cs index c7719ef8b..0671a6c10 100644 --- a/NzbDrone.Core/Qualities/QualitySizeService.cs +++ b/NzbDrone.Core/Qualities/QualitySizeService.cs @@ -2,7 +2,6 @@ using System.Linq; using NLog; using NzbDrone.Core.Lifecycle; -using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.Qualities @@ -10,7 +9,6 @@ namespace NzbDrone.Core.Qualities public interface IQualitySizeService { void Update(QualitySize qualitySize); - void UpdateAll(List qualitySizes); List All(); QualitySize Get(int qualityId); } @@ -31,10 +29,6 @@ namespace NzbDrone.Core.Qualities _qualitySizeRepository.Update(qualitySize); } - public virtual void UpdateAll(List qualitySizes) - { - _qualitySizeRepository.UpdateMany(qualitySizes); - } public virtual List All() { diff --git a/NzbDrone.Core/Tv/EpisodeRepository.cs b/NzbDrone.Core/Tv/EpisodeRepository.cs index cd12016ed..3a9ade8d9 100644 --- a/NzbDrone.Core/Tv/EpisodeRepository.cs +++ b/NzbDrone.Core/Tv/EpisodeRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using Marr.Data.QGen; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Events; @@ -11,7 +10,6 @@ namespace NzbDrone.Core.Tv { public interface IEpisodeRepository : IBasicRepository { - Episode Get(int seriesId, int season, int episodeNumber); Episode Find(int seriesId, int season, int episodeNumber); Episode Get(int seriesId, DateTime date); Episode Find(int seriesId, DateTime date); @@ -19,9 +17,7 @@ namespace NzbDrone.Core.Tv List GetEpisodes(int seriesId, int seasonNumber); List GetEpisodeByFileId(int fileId); PagingSpec EpisodesWithoutFiles(PagingSpec pagingSpec, bool includeSpecials); - Episode GetEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber); Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber); - List EpisodesWithFiles(); List EpisodesBetweenDates(DateTime startDate, DateTime endDate); void SetMonitoredFlat(Episode episode, bool monitored); void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored); @@ -38,11 +34,6 @@ namespace NzbDrone.Core.Tv _database = database; } - public Episode Get(int seriesId, int season, int episodeNumber) - { - return Query.Single(s => s.SeriesId == seriesId && s.SeasonNumber == season && s.EpisodeNumber == episodeNumber); - } - public Episode Find(int seriesId, int season, int episodeNumber) { return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SeasonNumber == season && s.EpisodeNumber == episodeNumber); @@ -89,20 +80,11 @@ namespace NzbDrone.Core.Tv return pagingSpec; } - public Episode GetEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) - { - return Query.Single(s => s.SeriesId == seriesId && s.SceneSeasonNumber == seasonNumber && s.SceneEpisodeNumber == episodeNumber); - } - public Episode FindEpisodeBySceneNumbering(int seriesId, int seasonNumber, int episodeNumber) { return Query.SingleOrDefault(s => s.SeriesId == seriesId && s.SceneSeasonNumber == seasonNumber && s.SceneEpisodeNumber == episodeNumber); } - public List EpisodesWithFiles() - { - return Query.Where(s => s.EpisodeFileId != 0).ToList(); - } public List EpisodesBetweenDates(DateTime startDate, DateTime endDate) { diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index 8d3f3ccd2..2e83cc659 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -6,7 +6,6 @@ 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; @@ -15,7 +14,6 @@ namespace NzbDrone.Core.Tv public interface IEpisodeService { Episode GetEpisode(int id); - Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useScene = false); Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useScene = false); Episode GetEpisode(int seriesId, DateTime date); Episode FindEpisode(int seriesId, DateTime date); @@ -23,9 +21,7 @@ namespace NzbDrone.Core.Tv List GetEpisodesBySeason(int seriesId, int seasonNumber); PagingSpec EpisodesWithoutFiles(PagingSpec pagingSpec); List GetEpisodesByFileId(int episodeFileId); - List EpisodesWithFiles(); void UpdateEpisode(Episode episode); - List GetEpisodeNumbersBySeason(int seriesId, int seasonNumber); void SetEpisodeMonitored(int episodeId, bool monitored); bool IsFirstOrLastEpisodeOfSeason(int episodeId); void UpdateEpisodes(List episodes); @@ -42,8 +38,6 @@ namespace NzbDrone.Core.Tv IHandleAsync { - private static readonly Logger logger = NzbDroneLogger.GetLogger(); - private readonly IEpisodeRepository _episodeRepository; private readonly IConfigService _configService; private readonly Logger _logger; @@ -60,15 +54,6 @@ namespace NzbDrone.Core.Tv return _episodeRepository.Get(id); } - public Episode GetEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useSceneNumbering = false) - { - if (useSceneNumbering) - { - return _episodeRepository.GetEpisodeBySceneNumbering(seriesId, seasonNumber, episodeNumber); - } - return _episodeRepository.Find(seriesId, seasonNumber, episodeNumber); - } - public Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber, bool useSceneNumbering = false) { if (useSceneNumbering) @@ -110,27 +95,17 @@ namespace NzbDrone.Core.Tv return _episodeRepository.GetEpisodeByFileId(episodeFileId); } - public List EpisodesWithFiles() - { - return _episodeRepository.EpisodesWithFiles(); - } - public void UpdateEpisode(Episode episode) { _episodeRepository.Update(episode); } - public List GetEpisodeNumbersBySeason(int seriesId, int seasonNumber) - { - return GetEpisodesBySeason(seriesId, seasonNumber).Select(c => c.Id).ToList(); - } - public void SetEpisodeMonitored(int episodeId, bool monitored) { var episode = _episodeRepository.Get(episodeId); _episodeRepository.SetMonitoredFlat(episode, monitored); - logger.Debug("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/SeasonRepository.cs b/NzbDrone.Core/Tv/SeasonRepository.cs deleted file mode 100644 index 470c2e5ca..000000000 --- a/NzbDrone.Core/Tv/SeasonRepository.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using NzbDrone.Core.Datastore; -using NzbDrone.Core.Messaging; -using NzbDrone.Core.Messaging.Events; - - -namespace NzbDrone.Core.Tv -{ - public interface ISeasonRepository : IBasicRepository - { - List GetSeasonBySeries(int seriesId); - } - - public class SeasonRepository : BasicRepository, ISeasonRepository - { - public SeasonRepository(IDatabase database, IEventAggregator eventAggregator) - : base(database, eventAggregator) - { - } - - public List GetSeasonBySeries(int seriesId) - { - return Query.Single(s => s.Id == seriesId).Seasons; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/Tv/SeriesRepository.cs b/NzbDrone.Core/Tv/SeriesRepository.cs index 161ebbac2..86f74ece3 100644 --- a/NzbDrone.Core/Tv/SeriesRepository.cs +++ b/NzbDrone.Core/Tv/SeriesRepository.cs @@ -11,13 +11,10 @@ namespace NzbDrone.Core.Tv public interface ISeriesRepository : IBasicRepository { bool SeriesPathExists(string path); - List Search(string title); Series FindByTitle(string cleanTitle); Series FindByTvdbId(int tvdbId); Series FindByTvRageId(int tvRageId); void SetSeriesType(int seriesId, SeriesTypes seriesTypes); - Series FindBySlug(string slug); - List GetSeriesPaths(); } public class SeriesRepository : BasicRepository, ISeriesRepository @@ -32,11 +29,6 @@ namespace NzbDrone.Core.Tv return Query.Any(c => c.Path == path); } - public List Search(string title) - { - return Query.Where(s => s.Title.Contains(title)); - } - public Series FindByTitle(string cleanTitle) { return Query.SingleOrDefault(s => s.CleanTitle.Equals(cleanTitle, StringComparison.InvariantCultureIgnoreCase)); @@ -56,15 +48,5 @@ namespace NzbDrone.Core.Tv { SetFields(new Series { Id = seriesId, SeriesType = seriesType }, s => s.SeriesType); } - - public Series FindBySlug(string slug) - { - return Query.SingleOrDefault(c => c.TitleSlug == slug.ToLower()); - } - - public List GetSeriesPaths() - { - return Query.Select(s => s.Path).ToList(); - } } } \ No newline at end of file From b8827096b9725fce7f42845e0270da44d839058a Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Mon, 16 Sep 2013 22:16:49 -0700 Subject: [PATCH 05/49] left over test. --- .../MediaFiles/MediaFileRepositoryFixture.cs | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs b/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs index 2b54fff52..53d26122d 100644 --- a/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs +++ b/NzbDrone.Core.Test/MediaFiles/MediaFileRepositoryFixture.cs @@ -33,37 +33,5 @@ namespace NzbDrone.Core.Test.MediaFiles seriesFiles.Should().OnlyContain(c => c.SeriesId == 12); } - - - [Test] - public void GetFileByPath_should_return_null_if_file_does_not_exist_in_database() - { - Subject.GetFileByPath(@"C:\Test\EpisodeFile.avi").Should().BeNull(); - } - - [Test] - public void exists_should_return_false_if_file_doesnt_exist() - { - Subject.Exists(@"C:\Test\EpisodeFile.avi").Should().BeFalse(); - } - - [Test] - public void GetFileByPath_should_return_EpisodeFile_if_file_exists_in_database() - { - var path = @"C:\Test\EpisodeFile.avi".AsOsAgnostic(); - - var episodeFile = Builder.CreateNew() - .With(f => f.Id = 0) - .With(f => f.Path = path.CleanFilePath()) - .Build(); - - Subject.Insert(episodeFile); - - var file = Subject.GetFileByPath(path); - - //Resolve - file.Should().NotBeNull(); - file.Path.Should().Be(path.CleanFilePath()); - } } } \ No newline at end of file From 430f76e6c7df810a53761a39ac741a7646cae76c Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Mon, 16 Sep 2013 23:14:47 -0700 Subject: [PATCH 06/49] Xem cleanup. --- .../DiskProviderTests/FreeSpaceFixture.cs | 33 ++++--- .../DiskProviderTests/IsParentFixture.cs | 33 +++---- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 2 +- .../GetSceneTvdbMappingsFixture.cs | 17 ++-- .../GetXemSeriesIdsFixture.cs | 55 ----------- .../Providers/XemProxyFixture.cs | 59 ++++++++++++ NzbDrone.Core/NzbDrone.Core.csproj | 4 +- .../Providers/UpdateXemMappingsCommand.cs | 7 +- .../Providers/XemCommunicationProvider.cs | 68 -------------- NzbDrone.Core/Providers/XemProxy.cs | 76 +++++++++++++++ .../{XemProvider.cs => XemService.cs} | 93 +++++++------------ 11 files changed, 214 insertions(+), 233 deletions(-) delete mode 100644 NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetXemSeriesIdsFixture.cs create mode 100644 NzbDrone.Core.Test/Providers/XemProxyFixture.cs delete mode 100644 NzbDrone.Core/Providers/XemCommunicationProvider.cs create mode 100644 NzbDrone.Core/Providers/XemProxy.cs rename NzbDrone.Core/Providers/{XemProvider.cs => XemService.cs} (69%) diff --git a/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs b/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs index f3ede03eb..b29f60180 100644 --- a/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs +++ b/NzbDrone.Common.Test/DiskProviderTests/FreeSpaceFixture.cs @@ -1,36 +1,43 @@ -using FluentAssertions; +using System.IO; +using FluentAssertions; using NUnit.Framework; using NzbDrone.Test.Common; namespace NzbDrone.Common.Test.DiskProviderTests { [TestFixture] - public class IsParentFixture : TestBase + public class FreeSpaceFixture : TestBase { - private string _parent = @"C:\Test".AsOsAgnostic(); - [Test] - public void should_return_false_when_not_a_child() + public void should_get_free_space_for_folder() { - var path = @"C:\Another Folder".AsOsAgnostic(); + var path = @"C:\".AsOsAgnostic(); - Subject.IsParent(_parent, path).Should().BeFalse(); + Subject.GetAvailableSpace(path).Should().NotBe(0); } [Test] - public void should_return_true_when_folder_is_parent_of_another_folder() + public void should_get_free_space_for_folder_that_doesnt_exist() { - var path = @"C:\Test\TV".AsOsAgnostic(); + var path = @"C:\".AsOsAgnostic(); - Subject.IsParent(_parent, path).Should().BeTrue(); + Subject.GetAvailableSpace(Path.Combine(path, "invalidFolder")).Should().NotBe(0); } + [Test] - public void should_return_true_when_folder_is_parent_of_a_file() + public void should_get_free_space_for_drive_that_doesnt_exist() { - var path = @"C:\Test\30.Rock.S01E01.Pilot.avi".AsOsAgnostic(); + WindowsOnly(); + + Assert.Throws(() => Subject.GetAvailableSpace("J:\\").Should().NotBe(0)); + } - Subject.IsParent(_parent, path).Should().BeTrue(); + [Test] + public void should_be_able_to_check_space_on_ramdrive() + { + LinuxOnly(); + Subject.GetAvailableSpace("/run/").Should().NotBe(0); } } } diff --git a/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs b/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs index b29f60180..f3ede03eb 100644 --- a/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs +++ b/NzbDrone.Common.Test/DiskProviderTests/IsParentFixture.cs @@ -1,43 +1,36 @@ -using System.IO; -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; using NzbDrone.Test.Common; namespace NzbDrone.Common.Test.DiskProviderTests { [TestFixture] - public class FreeSpaceFixture : TestBase + public class IsParentFixture : TestBase { - [Test] - public void should_get_free_space_for_folder() - { - var path = @"C:\".AsOsAgnostic(); - - Subject.GetAvailableSpace(path).Should().NotBe(0); - } + private string _parent = @"C:\Test".AsOsAgnostic(); [Test] - public void should_get_free_space_for_folder_that_doesnt_exist() + public void should_return_false_when_not_a_child() { - var path = @"C:\".AsOsAgnostic(); + var path = @"C:\Another Folder".AsOsAgnostic(); - Subject.GetAvailableSpace(Path.Combine(path, "invalidFolder")).Should().NotBe(0); + Subject.IsParent(_parent, path).Should().BeFalse(); } - [Test] - public void should_get_free_space_for_drive_that_doesnt_exist() + public void should_return_true_when_folder_is_parent_of_another_folder() { - WindowsOnly(); + var path = @"C:\Test\TV".AsOsAgnostic(); - Assert.Throws(() => Subject.GetAvailableSpace("J:\\").Should().NotBe(0)); + Subject.IsParent(_parent, path).Should().BeTrue(); } [Test] - public void should_be_able_to_check_space_on_ramdrive() + public void should_return_true_when_folder_is_parent_of_a_file() { - LinuxOnly(); - Subject.GetAvailableSpace("/run/").Should().NotBe(0); + var path = @"C:\Test\30.Rock.S01E01.Pilot.avi".AsOsAgnostic(); + + Subject.IsParent(_parent, path).Should().BeTrue(); } } } diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index c855d7ad9..44efa3221 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -165,6 +165,7 @@ + @@ -198,7 +199,6 @@ - diff --git a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs index c8066ace5..65da5567d 100644 --- a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs @@ -7,57 +7,58 @@ using NzbDrone.Common; using NzbDrone.Core.Providers; using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.Categories; namespace NzbDrone.Core.Test.ProviderTests.XemCommunicationProviderTests { [TestFixture] - + [IntegrationTest] public class GetSceneTvdbMappingsFixture : CoreTest { private void WithFailureJson() { Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files","Xem","Failure.txt")); + .Returns(ReadAllText("Files", "Xem", "Failure.txt")); } private void WithIdsJson() { Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files","Xem","Ids.txt")); + .Returns(ReadAllText("Files", "Xem", "Ids.txt")); } private void WithMappingsJson() { Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files","Xem","Mappings.txt")); + .Returns(ReadAllText("Files", "Xem", "Mappings.txt")); } [Test] public void should_throw_when_failure_is_found() { WithFailureJson(); - Assert.Throws(() => Mocker.Resolve().GetSceneTvdbMappings(12345)); + Assert.Throws(() => Mocker.Resolve().GetSceneTvdbMappings(12345)); } [Test] public void should_get_list_of_mappings() { WithMappingsJson(); - Mocker.Resolve().GetSceneTvdbMappings(12345).Should().NotBeEmpty(); + Mocker.Resolve().GetSceneTvdbMappings(12345).Should().NotBeEmpty(); } [Test] public void should_have_two_mappings() { WithMappingsJson(); - Mocker.Resolve().GetSceneTvdbMappings(12345).Should().HaveCount(2); + Mocker.Resolve().GetSceneTvdbMappings(12345).Should().HaveCount(2); } [Test] public void should_have_expected_results() { WithMappingsJson(); - var results = Mocker.Resolve().GetSceneTvdbMappings(12345); + var results = Mocker.Resolve().GetSceneTvdbMappings(12345); var first = results.First(); first.Scene.Absolute.Should().Be(1); first.Scene.Season.Should().Be(1); diff --git a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetXemSeriesIdsFixture.cs b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetXemSeriesIdsFixture.cs deleted file mode 100644 index 82035d5c8..000000000 --- a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetXemSeriesIdsFixture.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Providers; - -using NzbDrone.Core.Test.Framework; - -namespace NzbDrone.Core.Test.ProviderTests.XemCommunicationProviderTests -{ - [TestFixture] - - public class GetXemSeriesIdsFixture : CoreTest - { - private void WithFailureJson() - { - Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files", "Xem", "Failure.txt")); - } - - private void WithIdsJson() - { - Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files", "Xem", "Ids.txt")); - } - - private void WithMappingsJson() - { - Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files", "Xem", "Mappings.txt")); - } - - [Test] - public void should_throw_when_failure_is_found() - { - WithFailureJson(); - Assert.Throws(() => Mocker.Resolve().GetXemSeriesIds()); - } - - [Test] - public void should_get_list_of_int() - { - WithIdsJson(); - Mocker.Resolve().GetXemSeriesIds().Should().NotBeEmpty(); - } - - [Test] - public void should_have_two_ids() - { - WithIdsJson(); - Mocker.Resolve().GetXemSeriesIds().Should().HaveCount(2); - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core.Test/Providers/XemProxyFixture.cs b/NzbDrone.Core.Test/Providers/XemProxyFixture.cs new file mode 100644 index 000000000..3bc1f73c2 --- /dev/null +++ b/NzbDrone.Core.Test/Providers/XemProxyFixture.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Test.Common.Categories; + +namespace NzbDrone.Core.Test.Providers +{ + [TestFixture] + [IntegrationTest] + public class XemProxyFixture : CoreTest + { + [SetUp] + public void Setup() + { + UseRealHttp(); + } + + [Test] + public void get_series_ids() + { + Subject.GetXemSeriesIds().Should().NotBeEmpty(); + } + + + [Test] + [Ignore("XEM's data is not clean")] + public void get_mapping_for_all_series() + { + var ids = Subject.GetXemSeriesIds(); + + var randomIds = ids.OrderBy(x => Guid.NewGuid()).Take(5); + + foreach (var randomId in randomIds) + { + Subject.GetSceneTvdbMappings(randomId).Should().NotBeEmpty(); + } + } + + [Test] + public void should_throw_when_failure_is_found() + { + Assert.Throws(() => Subject.GetSceneTvdbMappings(12345)); + } + + + [Test] + public void should_get_mapping() + { + var result = Subject.GetSceneTvdbMappings(82807); + + result.Should().NotBeEmpty(); + result.Should().OnlyContain(c => c.Scene != null); + result.Should().OnlyContain(c => c.Tvdb != null); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 8d8c65364..26e301627 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -437,8 +437,8 @@ - - + + diff --git a/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs b/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs index a30c0c70d..5f35eaf1d 100644 --- a/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs +++ b/NzbDrone.Core/Providers/UpdateXemMappingsCommand.cs @@ -4,11 +4,6 @@ namespace NzbDrone.Core.Providers { public class UpdateXemMappingsCommand : Command { - public int? SeriesId { get; set; } - - public UpdateXemMappingsCommand(int? seriesId) - { - SeriesId = seriesId; - } + } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/XemCommunicationProvider.cs b/NzbDrone.Core/Providers/XemCommunicationProvider.cs deleted file mode 100644 index 5c0e07132..000000000 --- a/NzbDrone.Core/Providers/XemCommunicationProvider.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using NLog; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NzbDrone.Common; -using NzbDrone.Common.Instrumentation; -using NzbDrone.Core.Model.Xem; - -namespace NzbDrone.Core.Providers -{ - public interface IXemCommunicationProvider - { - List GetXemSeriesIds(string origin = "tvdb"); - List GetSceneTvdbMappings(int id); - void CheckForFailureResult(string response); - } - - public class XemCommunicationProvider : IXemCommunicationProvider - { - private readonly IHttpProvider _httpProvider; - - private static readonly Logger _logger = NzbDroneLogger.GetLogger(); - - private const string XEM_BASE_URL = "http://thexem.de/map/"; - - public XemCommunicationProvider(IHttpProvider httpProvider) - { - _httpProvider = httpProvider; - } - - public List GetXemSeriesIds(string origin = "tvdb") - { - _logger.Trace("Fetching Series IDs from: {0}", origin); - - var url = String.Format("{0}havemap?origin={1}", XEM_BASE_URL, origin); - var response =_httpProvider.DownloadString(url); - - CheckForFailureResult(response); - - var result = JsonConvert.DeserializeObject>>(response); - - return result.Data.ToList(); - } - - public List GetSceneTvdbMappings(int id) - { - _logger.Trace("Fetching Mappings for: {0}", id); - var url = String.Format("{0}all?id={1}&origin=tvdb", XEM_BASE_URL, id); - var response = _httpProvider.DownloadString(url); - - CheckForFailureResult(response); - - var result = JsonConvert.DeserializeObject>(JObject.Parse(response).SelectToken("data").ToString()); - - return result; - } - - public void CheckForFailureResult(string response) - { - var result = JsonConvert.DeserializeObject>(response); - - if (result != null && result.Result.Equals("failure", StringComparison.InvariantCultureIgnoreCase)) - throw new Exception("Error response received from Xem: " + result.Message); - } - } -} diff --git a/NzbDrone.Core/Providers/XemProxy.cs b/NzbDrone.Core/Providers/XemProxy.cs new file mode 100644 index 000000000..0d500cdee --- /dev/null +++ b/NzbDrone.Core/Providers/XemProxy.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using NzbDrone.Core.Model.Xem; +using NzbDrone.Core.Rest; +using RestSharp; + +namespace NzbDrone.Core.Providers +{ + public interface IXemProxy + { + List GetXemSeriesIds(); + List GetSceneTvdbMappings(int id); + } + + public class XemProxy : IXemProxy + { + private readonly Logger _logger; + + private const string XEM_BASE_URL = "http://thexem.de/map/"; + + public XemProxy(Logger logger) + { + _logger = logger; + } + + + private static RestRequest BuildRequest(string resource) + { + var req = new RestRequest(resource, Method.GET); + req.AddParameter("origin", "tvdb"); + return req; + } + + public List GetXemSeriesIds() + { + _logger.Trace("Fetching Series IDs from"); + + var restClient = new RestClient(XEM_BASE_URL); + + var request = BuildRequest("havemap"); + + var response = restClient.ExecuteAndValidate>>(request); + CheckForFailureResult(response); + + return response.Data.ToList(); + } + + public List GetSceneTvdbMappings(int id) + { + _logger.Trace("Fetching Mappings for: {0}", id); + var url = String.Format("{0}all?id={1}&origin=tvdb", XEM_BASE_URL, id); + + + var restClient = new RestClient(XEM_BASE_URL); + + var request = BuildRequest("all"); + request.AddParameter("id", id); + + var response = restClient.ExecuteAndValidate>>(request); + CheckForFailureResult(response); + + return response.Data; + } + + private static void CheckForFailureResult(XemResult response) + { + if (response.Result.Equals("failure", StringComparison.InvariantCultureIgnoreCase) && + !response.Message.Contains("no show with the tvdb_id")) + { + throw new Exception("Error response received from Xem: " + response.Message); + } + } + } +} diff --git a/NzbDrone.Core/Providers/XemProvider.cs b/NzbDrone.Core/Providers/XemService.cs similarity index 69% rename from NzbDrone.Core/Providers/XemProvider.cs rename to NzbDrone.Core/Providers/XemService.cs index e66000afc..ac36a0bb7 100644 --- a/NzbDrone.Core/Providers/XemProvider.cs +++ b/NzbDrone.Core/Providers/XemService.cs @@ -3,9 +3,7 @@ using System.Collections.Generic; using System.Linq; using NLog; using NzbDrone.Common.Cache; -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; @@ -13,35 +11,44 @@ using NzbDrone.Core.Tv.Events; namespace NzbDrone.Core.Providers { - public interface IXemProvider - { - void UpdateMappings(); - void UpdateMappings(int seriesId); - void UpdateMappings(Series series); - void PerformUpdate(Series series); - } - - public class XemProvider : IXemProvider, IExecute, IHandle, IHandleAsync + public class XemService : IExecute, IHandle, IHandleAsync { private readonly IEpisodeService _episodeService; - private readonly IXemCommunicationProvider _xemCommunicationProvider; + private readonly IXemProxy _xemProxy; private readonly ISeriesService _seriesService; + private readonly Logger _logger; private readonly ICached _cache; - private static readonly Logger _logger = NzbDroneLogger.GetLogger(); - - public XemProvider(IEpisodeService episodeService, - IXemCommunicationProvider xemCommunicationProvider, - ISeriesService seriesService, ICacheManger cacheManger) + public XemService(IEpisodeService episodeService, + IXemProxy xemProxy, + ISeriesService seriesService, ICacheManger cacheManger, Logger logger) { if (seriesService == null) throw new ArgumentNullException("seriesService"); _episodeService = episodeService; - _xemCommunicationProvider = xemCommunicationProvider; + _xemProxy = xemProxy; _seriesService = seriesService; + _logger = logger; + _logger = logger; _cache = cacheManger.GetCache(GetType()); } - public void UpdateMappings() + + public void Execute(UpdateXemMappingsCommand message) + { + UpdateMappings(); + } + + public void Handle(SeriesUpdatedEvent message) + { + UpdateMappings(message.Series); + } + + public void HandleAsync(ApplicationStartedEvent message) + { + GetXemSeriesIds(); + } + + private void UpdateMappings() { _logger.Trace("Starting scene numbering update"); @@ -66,20 +73,7 @@ namespace NzbDrone.Core.Providers } } - public void UpdateMappings(int seriesId) - { - var series = _seriesService.GetSeries(seriesId); - - if (series == null) - { - _logger.Trace("Series could not be found: {0}", seriesId); - return; - } - - UpdateMappings(series); - } - - public void UpdateMappings(Series series) + private void UpdateMappings(Series series) { if (!_cache.Find(series.TvdbId.ToString())) { @@ -90,17 +84,18 @@ namespace NzbDrone.Core.Providers PerformUpdate(series); } - public void PerformUpdate(Series series) + private void PerformUpdate(Series series) { _logger.Trace("Updating scene numbering mapping for: {0}", series); try { var episodesToUpdate = new List(); - var mappings = _xemCommunicationProvider.GetSceneTvdbMappings(series.TvdbId); + var mappings = _xemProxy.GetSceneTvdbMappings(series.TvdbId); - if (mappings == null) + if (!mappings.Any()) { - _logger.Trace("Mappings for: {0} are null, skipping", series); + _logger.Trace("Mappings for: {0} are empty, skipping", series); + _cache.Remove(series.TvdbId.ToString()); return; } @@ -142,7 +137,7 @@ namespace NzbDrone.Core.Providers { _cache.Clear(); - var ids = _xemCommunicationProvider.GetXemSeriesIds(); + var ids = _xemProxy.GetXemSeriesIds(); foreach (var id in ids) { @@ -151,27 +146,5 @@ namespace NzbDrone.Core.Providers return ids; } - - public void Execute(UpdateXemMappingsCommand message) - { - if (message.SeriesId.HasValue) - { - UpdateMappings(message.SeriesId.Value); - } - else - { - UpdateMappings(); - } - } - - public void Handle(SeriesUpdatedEvent message) - { - UpdateMappings(message.Series); - } - - public void HandleAsync(ApplicationStartedEvent message) - { - GetXemSeriesIds(); - } } } From 1f00b05259968c27efb16632682f353115a0766e Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Mon, 16 Sep 2013 23:15:54 -0700 Subject: [PATCH 07/49] fixed broken test --- NzbDrone.Core.Test/Providers/XemProxyFixture.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NzbDrone.Core.Test/Providers/XemProxyFixture.cs b/NzbDrone.Core.Test/Providers/XemProxyFixture.cs index 3bc1f73c2..37c625b55 100644 --- a/NzbDrone.Core.Test/Providers/XemProxyFixture.cs +++ b/NzbDrone.Core.Test/Providers/XemProxyFixture.cs @@ -40,9 +40,9 @@ namespace NzbDrone.Core.Test.Providers } [Test] - public void should_throw_when_failure_is_found() + public void should_return_empty_when_series_is_not_found() { - Assert.Throws(() => Subject.GetSceneTvdbMappings(12345)); + Subject.GetSceneTvdbMappings(12345).Should().BeEmpty(); } From bb72308afa468a648b7d03dbf79b33c35515cdd6 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 17 Sep 2013 07:07:56 -0700 Subject: [PATCH 08/49] Trakt fixed DST issue, removing workaround --- NzbDrone.Core/MetadataSource/TraktProxy.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/NzbDrone.Core/MetadataSource/TraktProxy.cs b/NzbDrone.Core/MetadataSource/TraktProxy.cs index eb8bb263e..69a526140 100644 --- a/NzbDrone.Core/MetadataSource/TraktProxy.cs +++ b/NzbDrone.Core/MetadataSource/TraktProxy.cs @@ -133,12 +133,6 @@ namespace NzbDrone.Core.MetadataSource { 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; From eba9fab51833ea3ce64d1fdf7976ec18dfbbd616 Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Tue, 17 Sep 2013 10:34:39 -0700 Subject: [PATCH 09/49] moved console polyfills to polyfills.js --- UI/Instrumentation/ErrorHandler.js | 9 --------- UI/app.js | 3 +-- UI/index.html | 1 + UI/polyfills.js | 11 ++++++++++- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/UI/Instrumentation/ErrorHandler.js b/UI/Instrumentation/ErrorHandler.js index 7909dc5fb..475edc68f 100644 --- a/UI/Instrumentation/ErrorHandler.js +++ b/UI/Instrumentation/ErrorHandler.js @@ -1,14 +1,5 @@ 'use strict'; (function () { - - window.console = window.console || {}; - window.console.log = window.console.log || function(){}; - window.console.group = window.console.group || function(){}; - window.console.groupEnd = window.console.groupEnd || function(){}; - window.console.debug = window.console.debug || function(){}; - window.console.warn = window.console.warn || function(){}; - window.console.assert = window.console.assert || function(){}; - window.alert = function (message) { window.Messenger().post(message); }; diff --git a/UI/app.js b/UI/app.js index 280a16d39..73d8be5e5 100644 --- a/UI/app.js +++ b/UI/app.js @@ -36,8 +36,7 @@ require.config({ init: function () { require( [ - 'jQuery/ToTheTop', - 'Instrumentation/ErrorHandler' + 'jQuery/ToTheTop' ]); } diff --git a/UI/index.html b/UI/index.html index 5ffff693e..178c5beb2 100644 --- a/UI/index.html +++ b/UI/index.html @@ -63,6 +63,7 @@ + diff --git a/UI/polyfills.js b/UI/polyfills.js index 3e6797bf8..5bce992df 100644 --- a/UI/polyfills.js +++ b/UI/polyfills.js @@ -1,3 +1,12 @@ +window.console = window.console || {}; +window.console.log = window.console.log || function(){}; +window.console.group = window.console.group || function(){}; +window.console.groupEnd = window.console.groupEnd || function(){}; +window.console.debug = window.console.debug || function(){}; +window.console.warn = window.console.warn || function(){}; +window.console.assert = window.console.assert || function(){}; + + if (!String.prototype.startsWith) { Object.defineProperty(String.prototype, 'startsWith', { enumerable: false, @@ -29,4 +38,4 @@ if(!('contains' in String.prototype)) String.prototype.contains = function(str, startIndex) { return -1 !== String.prototype.indexOf.call(this, str, startIndex); }; -} \ No newline at end of file +} From fa993109fc1ca3d1e18c96d2c206ad2a595b2b20 Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Tue, 17 Sep 2013 10:36:57 -0700 Subject: [PATCH 10/49] removed old tests. --- NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 - .../GetSceneTvdbMappingsFixture.cs | 71 ------------------- 2 files changed, 72 deletions(-) delete mode 100644 NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 44efa3221..119e1d333 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -198,7 +198,6 @@ - diff --git a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs b/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs deleted file mode 100644 index 65da5567d..000000000 --- a/NzbDrone.Core.Test/ProviderTests/XemCommunicationProviderTests/GetSceneTvdbMappingsFixture.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Linq; -using FluentAssertions; -using Moq; -using NUnit.Framework; -using NzbDrone.Common; -using NzbDrone.Core.Providers; - -using NzbDrone.Core.Test.Framework; -using NzbDrone.Test.Common.Categories; - -namespace NzbDrone.Core.Test.ProviderTests.XemCommunicationProviderTests -{ - [TestFixture] - [IntegrationTest] - public class GetSceneTvdbMappingsFixture : CoreTest - { - private void WithFailureJson() - { - Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files", "Xem", "Failure.txt")); - } - - private void WithIdsJson() - { - Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files", "Xem", "Ids.txt")); - } - - private void WithMappingsJson() - { - Mocker.GetMock().Setup(s => s.DownloadString(It.IsAny())) - .Returns(ReadAllText("Files", "Xem", "Mappings.txt")); - } - - [Test] - public void should_throw_when_failure_is_found() - { - WithFailureJson(); - Assert.Throws(() => Mocker.Resolve().GetSceneTvdbMappings(12345)); - } - - [Test] - public void should_get_list_of_mappings() - { - WithMappingsJson(); - Mocker.Resolve().GetSceneTvdbMappings(12345).Should().NotBeEmpty(); - } - - [Test] - public void should_have_two_mappings() - { - WithMappingsJson(); - Mocker.Resolve().GetSceneTvdbMappings(12345).Should().HaveCount(2); - } - - [Test] - public void should_have_expected_results() - { - WithMappingsJson(); - var results = Mocker.Resolve().GetSceneTvdbMappings(12345); - var first = results.First(); - first.Scene.Absolute.Should().Be(1); - first.Scene.Season.Should().Be(1); - first.Scene.Episode.Should().Be(1); - first.Tvdb.Absolute.Should().Be(1); - first.Tvdb.Season.Should().Be(1); - first.Tvdb.Episode.Should().Be(1); - } - } -} \ No newline at end of file From cdb195a44f4e7d3f0b3d93bbd18abbe57f9d1738 Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Tue, 17 Sep 2013 15:03:16 -0700 Subject: [PATCH 11/49] added IE=edge header to IndexHtml --- NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs | 9 +++++++++ .../Frontend/Mappers/StaticResourceMapperBase.cs | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs b/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs index fb11b68aa..b15167619 100644 --- a/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs +++ b/NzbDrone.Api/Frontend/Mappers/IndexHtmlMapper.cs @@ -1,4 +1,5 @@ using System.IO; +using Nancy; using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; @@ -27,6 +28,14 @@ namespace NzbDrone.Api.Frontend.Mappers return !resourceUrl.Contains("."); } + public override Response GetResponse(string resourceUrl) + { + var response = base.GetResponse(resourceUrl); + response.Headers["X-UA-Compatible"] = "IE=edge"; + + return response; + } + protected override Stream GetContentStream(string filePath) { return StringToStream(GetIndexText()); diff --git a/NzbDrone.Api/Frontend/Mappers/StaticResourceMapperBase.cs b/NzbDrone.Api/Frontend/Mappers/StaticResourceMapperBase.cs index e789c01a7..eae69b9de 100644 --- a/NzbDrone.Api/Frontend/Mappers/StaticResourceMapperBase.cs +++ b/NzbDrone.Api/Frontend/Mappers/StaticResourceMapperBase.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Api.Frontend.Mappers public abstract bool CanHandle(string resourceUrl); - public Response GetResponse(string resourceUrl) + public virtual Response GetResponse(string resourceUrl) { var filePath = Map(resourceUrl); From ac18d6c7045db0121832524f89174d2a9731948b Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Tue, 17 Sep 2013 17:54:57 -0700 Subject: [PATCH 12/49] added not found placeholder in add series added path validation to add series --- UI/AddSeries/AddSeriesView.js | 34 ++++++++++++------- UI/AddSeries/NotFoundTemplate.html | 8 +++++ UI/AddSeries/NotFoundView.js | 20 +++++++++++ .../RootFolderSelectionPartial.html | 22 ++++++------ UI/AddSeries/SearchResultView.js | 10 ++++-- UI/AddSeries/SearchResultViewTemplate.html | 20 ++++++----- UI/Content/theme.less | 22 +++++++++--- UI/app.js | 3 +- UI/jQuery/jquery.validation.js | 8 ++++- 9 files changed, 108 insertions(+), 39 deletions(-) create mode 100644 UI/AddSeries/NotFoundTemplate.html create mode 100644 UI/AddSeries/NotFoundView.js diff --git a/UI/AddSeries/AddSeriesView.js b/UI/AddSeries/AddSeriesView.js index a28433c53..324ac0f90 100644 --- a/UI/AddSeries/AddSeriesView.js +++ b/UI/AddSeries/AddSeriesView.js @@ -5,8 +5,9 @@ define( 'marionette', 'AddSeries/Collection', 'AddSeries/SearchResultCollectionView', - 'Shared/LoadingView' - ], function (App, Marionette, AddSeriesCollection, SearchResultCollectionView, LoadingView) { + 'AddSeries/NotFoundView', + 'Shared/LoadingView', + ], function (App, Marionette, AddSeriesCollection, SearchResultCollectionView, NotFoundView, LoadingView) { return Marionette.Layout.extend({ template: 'AddSeries/AddSeriesTemplate', @@ -44,6 +45,8 @@ define( else { this.className = 'new-series'; } + + this.listenTo(this.collection, 'sync', this._showResults); }, _onSeriesAdded: function (options) { @@ -78,8 +81,6 @@ define( search: function (options) { - var self = this; - this.abortExistingSearch(); this.collection.reset(); @@ -89,20 +90,29 @@ define( } else { this.searchResult.show(new LoadingView()); + this.collection.term = options.term; this.currentSearchPromise = this.collection.fetch({ data: { term: options.term } - }).done(function () { - if (!self.isClosed) { - self.searchResult.show(self.resultCollectionView); - if (!self.showingAll && self.isExisting) { - self.ui.loadMore.show(); - } - } - }); + }); } return this.currentSearchPromise; }, + _showResults: function () { + if (!this.isClosed) { + + if (this.collection.length === 0) { + this.searchResult.show(new NotFoundView({term: this.collection.term})); + } + else { + this.searchResult.show(this.resultCollectionView); + if (!this.showingAll && this.isExisting) { + this.ui.loadMore.show(); + } + } + } + }, + abortExistingSearch: function () { if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) { console.log('aborting previous pending search request.'); diff --git a/UI/AddSeries/NotFoundTemplate.html b/UI/AddSeries/NotFoundTemplate.html new file mode 100644 index 000000000..fa6c8f5b2 --- /dev/null +++ b/UI/AddSeries/NotFoundTemplate.html @@ -0,0 +1,8 @@ +
+

+ Sorry. We couldn't find any series matching '{{term}}' +

+ Why can't I find my show? + +
+{{debug}} diff --git a/UI/AddSeries/NotFoundView.js b/UI/AddSeries/NotFoundView.js new file mode 100644 index 000000000..e59c3e4f5 --- /dev/null +++ b/UI/AddSeries/NotFoundView.js @@ -0,0 +1,20 @@ +'use strict'; + +define( + [ + 'marionette' + ], function (Marionette) { + + return Marionette.CompositeView.extend({ + template: 'AddSeries/NotFoundTemplate', + + initialize: function (options) { + this.options = options; + }, + + templateHelpers: function () { + return this.options; + } + + }); + }); diff --git a/UI/AddSeries/RootFolders/RootFolderSelectionPartial.html b/UI/AddSeries/RootFolders/RootFolderSelectionPartial.html index 56d547da5..64e1e9358 100644 --- a/UI/AddSeries/RootFolders/RootFolderSelectionPartial.html +++ b/UI/AddSeries/RootFolders/RootFolderSelectionPartial.html @@ -1,10 +1,12 @@ - +
+ +
diff --git a/UI/AddSeries/SearchResultView.js b/UI/AddSeries/SearchResultView.js index 28ba1a774..e19bfecaf 100644 --- a/UI/AddSeries/SearchResultView.js +++ b/UI/AddSeries/SearchResultView.js @@ -9,10 +9,11 @@ define( 'Series/SeriesCollection', 'Config', 'Shared/Messenger', + 'Mixins/AsValidatedView', 'jquery.dotdotdot' - ], function (App, Marionette, QualityProfiles, RootFolders, RootFolderLayout, SeriesCollection, Config, Messenger) { + ], function (App, Marionette, QualityProfiles, RootFolders, RootFolderLayout, SeriesCollection, Config, Messenger, AsValidatedView) { - return Marionette.ItemView.extend({ + var view = Marionette.ItemView.extend({ template: 'AddSeries/SearchResultViewTemplate', @@ -146,4 +147,9 @@ define( }); } }); + + + AsValidatedView.apply(view); + + return view; }); diff --git a/UI/AddSeries/SearchResultViewTemplate.html b/UI/AddSeries/SearchResultViewTemplate.html index 574422b17..091c8bb35 100644 --- a/UI/AddSeries/SearchResultViewTemplate.html +++ b/UI/AddSeries/SearchResultViewTemplate.html @@ -2,7 +2,8 @@
@@ -14,22 +15,23 @@ {{overview}}
- {{#if existing}} +
+ {{#if existing}}
Already Exists
- {{else}} + {{else}} {{#unless path}} - {{> RootFolderSelectionPartial rootFolders}} + {{> RootFolderSelectionPartial rootFolders}} {{/unless}} {{> StartingSeasonSelectionPartial seasons}} {{> QualityProfileSelectionPartial qualityProfiles}} - -
- Add -
- {{/if}} + Add + + + {{/if}} +
diff --git a/UI/Content/theme.less b/UI/Content/theme.less index a11ceb566..debaf887c 100644 --- a/UI/Content/theme.less +++ b/UI/Content/theme.less @@ -46,6 +46,10 @@ margin-bottom : 30px; } +.page-container { + min-height : 500px; +} + #scroll-up { .clickable; @@ -151,13 +155,23 @@ footer { } .status-primary { - color: @linkColor; + color : @linkColor; } .status-success { - color: @successText; + color : @successText; } .status-danger { - color: @errorText; -} \ No newline at end of file + color : @errorText; +} + +.form-inline { + div { + display : inline-block; + } +} + +.error { + .formFieldState(@errorText, @errorText, @errorBackground); +} diff --git a/UI/app.js b/UI/app.js index 73d8be5e5..280a16d39 100644 --- a/UI/app.js +++ b/UI/app.js @@ -36,7 +36,8 @@ require.config({ init: function () { require( [ - 'jQuery/ToTheTop' + 'jQuery/ToTheTop', + 'Instrumentation/ErrorHandler' ]); } diff --git a/UI/jQuery/jquery.validation.js b/UI/jQuery/jquery.validation.js index ca03d0aa7..6f94a50db 100644 --- a/UI/jQuery/jquery.validation.js +++ b/UI/jQuery/jquery.validation.js @@ -31,7 +31,13 @@ define( } var controlGroup = input.parents('.control-group'); - controlGroup.find('.controls').append('' + error.errorMessage + ''); + + if(controlGroup.length ===0){ + controlGroup = input.parent(); + } + else{ + controlGroup.find('.controls').append('' + error.errorMessage + ''); + } controlGroup.addClass('error'); From 174689f533a4a3d0d01853c091cefce8b446f358 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 17 Sep 2013 17:59:40 -0700 Subject: [PATCH 13/49] Fixed: Issue parsing size from newznab mocking indexers --- NzbDrone.Core/Indexers/Newznab/NewznabParser.cs | 6 +++--- NzbDrone.Core/Indexers/RssParserBase.cs | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs b/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs index 06c45ea1d..fc9e54d93 100644 --- a/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs +++ b/NzbDrone.Core/Indexers/Newznab/NewznabParser.cs @@ -17,12 +17,12 @@ namespace NzbDrone.Core.Indexers.Newznab var attributes = item.Elements("attr").ToList(); var sizeElement = attributes.SingleOrDefault(e => e.Attribute("name").Value.Equals("size", StringComparison.CurrentCultureIgnoreCase)); - if (sizeElement == null) + if (sizeElement != null) { - + return Convert.ToInt64(sizeElement.Attribute("value").Value); } - return Convert.ToInt64(sizeElement.Attribute("value").Value); + return ParseSize(item.Description()); } protected override ReleaseInfo PostProcessor(XElement item, ReleaseInfo currentResult) diff --git a/NzbDrone.Core/Indexers/RssParserBase.cs b/NzbDrone.Core/Indexers/RssParserBase.cs index 485bc0140..ca4bf91f8 100644 --- a/NzbDrone.Core/Indexers/RssParserBase.cs +++ b/NzbDrone.Core/Indexers/RssParserBase.cs @@ -108,8 +108,6 @@ namespace NzbDrone.Core.Indexers return currentResult; } - - public static string ParseReleaseGroup(string title) { title = title.Trim(); @@ -132,8 +130,6 @@ namespace NzbDrone.Core.Indexers 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 ParseSize(string sizeString) { var match = ReportSizeRegex.Matches(sizeString); From 07b68b17ee12e0114a8b6e84292f2b4922705bb4 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 17 Sep 2013 22:28:05 -0700 Subject: [PATCH 14/49] Added housekeeping to cleanup orphaned episodes and history items --- .../HistoryTests/HistoryRepositoryFixture.cs | 67 +++++++++++++++++++ NzbDrone.Core.Test/NzbDrone.Core.Test.csproj | 1 + .../CleanupOrphanedEpisodesFixture.cs | 44 ++++++++++++ NzbDrone.Core/History/HistoryRepository.cs | 30 ++++++++- NzbDrone.Core/History/HistoryService.cs | 7 ++ .../Housekeepers/CleanupOrphanedEpisodes.cs | 23 +++++++ .../CleanupOrphanedHistoryItems.cs | 23 +++++++ .../Housekeeping/HousekeepingCommand.cs | 12 ++++ .../Housekeeping/HousekeepingService.cs | 43 ++++++++++++ .../Housekeeping/IHousekeepingTask.cs | 10 +++ NzbDrone.Core/Jobs/TaskManager.cs | 4 +- NzbDrone.Core/NzbDrone.Core.csproj | 5 ++ NzbDrone.Core/Tv/EpisodeRepository.cs | 13 ++++ NzbDrone.Core/Tv/EpisodeService.cs | 6 ++ 14 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/CleanupOrphanedEpisodesFixture.cs create mode 100644 NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedEpisodes.cs create mode 100644 NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs create mode 100644 NzbDrone.Core/Housekeeping/HousekeepingCommand.cs create mode 100644 NzbDrone.Core/Housekeeping/HousekeepingService.cs create mode 100644 NzbDrone.Core/Housekeeping/IHousekeepingTask.cs diff --git a/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs b/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs index 4ccc2364b..3a15958bc 100644 --- a/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs +++ b/NzbDrone.Core.Test/HistoryTests/HistoryRepositoryFixture.cs @@ -4,6 +4,7 @@ using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.History; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.Test.HistoryTests { @@ -41,5 +42,71 @@ namespace NzbDrone.Core.Test.HistoryTests StoredModel.Data.Should().HaveCount(2); } + + [Test] + public void should_delete_orphaned_items_by_series() + { + var history = Builder.CreateNew().BuildNew(); + Subject.Insert(history); + + Subject.CleanupOrphanedBySeries(); + Subject.All().Should().BeEmpty(); + } + + [Test] + public void should_delete_orphaned_items_by_episode() + { + var history = Builder.CreateNew().BuildNew(); + Subject.Insert(history); + + Subject.CleanupOrphanedByEpisode(); + Subject.All().Should().BeEmpty(); + } + + [Test] + public void should_not_delete_unorphaned_data_by_series() + { + var series = Builder.CreateNew() + .BuildNew(); + + Db.Insert(series); + + var history = Builder.CreateListOfSize(2) + .All() + .With(h => h.Id = 0) + .TheFirst(1) + .With(h => h.SeriesId = series.Id) + .Build(); + + + Subject.InsertMany(history); + + Subject.CleanupOrphanedBySeries(); + Subject.All().Should().HaveCount(1); + Subject.All().Should().Contain(h => h.SeriesId == series.Id); + } + + [Test] + public void should_not_delete_unorphaned_data_by_episode() + { + var episode = Builder.CreateNew() + .BuildNew(); + + Db.Insert(episode); + + var history = Builder.CreateListOfSize(2) + .All() + .With(h => h.Id = 0) + .TheFirst(1) + .With(h => h.EpisodeId = episode.Id) + .Build(); + + + Subject.InsertMany(history); + + Subject.CleanupOrphanedByEpisode(); + Subject.All().Should().HaveCount(1); + Subject.All().Should().Contain(h => h.EpisodeId == episode.Id); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 119e1d333..8344a179d 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -179,6 +179,7 @@ + diff --git a/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/CleanupOrphanedEpisodesFixture.cs b/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/CleanupOrphanedEpisodesFixture.cs new file mode 100644 index 000000000..d21ef817d --- /dev/null +++ b/NzbDrone.Core.Test/TvTests/EpisodeRepositoryTests/CleanupOrphanedEpisodesFixture.cs @@ -0,0 +1,44 @@ +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests +{ + [TestFixture] + public class CleanupOrphanedEpisodesFixture : DbTest + { + [Test] + public void should_delete_orphaned_episodes() + { + var episode = Builder.CreateNew() + .BuildNew(); + + Subject.Insert(episode); + Subject.CleanupOrphanedEpisodes(); + Subject.All().Should().BeEmpty(); + } + + [Test] + public void should_not_delete_unorphaned_episodes() + { + var series = Builder.CreateNew() + .BuildNew(); + + Db.Insert(series); + + var episodes = Builder.CreateListOfSize(2) + .All() + .With(e => e.Id = 0) + .TheFirst(1) + .With(e => e.SeriesId = series.Id) + .Build(); + + Subject.InsertMany(episodes); + Subject.CleanupOrphanedEpisodes(); + Subject.All().Should().HaveCount(1); + Subject.All().Should().Contain(e => e.SeriesId == series.Id); + } + } +} diff --git a/NzbDrone.Core/History/HistoryRepository.cs b/NzbDrone.Core/History/HistoryRepository.cs index 096397248..50ad32994 100644 --- a/NzbDrone.Core/History/HistoryRepository.cs +++ b/NzbDrone.Core/History/HistoryRepository.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using Marr.Data.QGen; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Messaging; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Tv; @@ -13,13 +12,18 @@ namespace NzbDrone.Core.History { void Trim(); List GetEpisodeHistory(int episodeId); + void CleanupOrphanedBySeries(); + void CleanupOrphanedByEpisode(); } public class HistoryRepository : BasicRepository, IHistoryRepository { + private readonly IDatabase _database; + public HistoryRepository(IDatabase database, IEventAggregator eventAggregator) : base(database, eventAggregator) { + _database = database; } public void Trim() @@ -35,6 +39,30 @@ namespace NzbDrone.Core.History return history.Select(h => h.Quality).ToList(); } + public void CleanupOrphanedBySeries() + { + var mapper = _database.GetDataMapper(); + + mapper.ExecuteNonQuery(@"DELETE FROM History + WHERE Id IN ( + SELECT History.Id FROM History + LEFT OUTER JOIN Series + ON History.SeriesId = Series.Id + WHERE Series.Id IS NULL)"); + } + + public void CleanupOrphanedByEpisode() + { + var mapper = _database.GetDataMapper(); + + mapper.ExecuteNonQuery(@"DELETE FROM History + WHERE Id IN ( + SELECT History.Id FROM History + LEFT OUTER JOIN Episodes + ON History.EpisodeId = Episodes.Id + WHERE Episodes.Id IS NULL)"); + } + public override PagingSpec GetPaged(PagingSpec pagingSpec) { var pagingQuery = Query.Join(JoinType.Inner, h => h.Series, (h, s) => h.SeriesId == s.Id) diff --git a/NzbDrone.Core/History/HistoryService.cs b/NzbDrone.Core/History/HistoryService.cs index 6053541d0..4fc398c72 100644 --- a/NzbDrone.Core/History/HistoryService.cs +++ b/NzbDrone.Core/History/HistoryService.cs @@ -18,6 +18,7 @@ namespace NzbDrone.Core.History void Trim(); QualityModel GetBestQualityInHistory(int episodeId); PagingSpec Paged(PagingSpec pagingSpec); + void CleanupOrphaned(); } public class HistoryService : IHistoryService, IHandle, IHandle @@ -41,6 +42,12 @@ namespace NzbDrone.Core.History return _historyRepository.GetPaged(pagingSpec); } + public void CleanupOrphaned() + { + _historyRepository.CleanupOrphanedBySeries(); + _historyRepository.CleanupOrphanedByEpisode(); + } + public void Purge() { _historyRepository.Purge(); diff --git a/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedEpisodes.cs b/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedEpisodes.cs new file mode 100644 index 000000000..69cb713ca --- /dev/null +++ b/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedEpisodes.cs @@ -0,0 +1,23 @@ +using NLog; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Housekeeping.Housekeepers +{ + public class CleanupOrphanedEpisodes : IHousekeepingTask + { + private readonly IEpisodeService _episodeService; + private readonly Logger _logger; + + public CleanupOrphanedEpisodes(IEpisodeService episodeService, Logger logger) + { + _episodeService = episodeService; + _logger = logger; + } + + public void Clean() + { + _logger.Trace("Running orphaned episodes cleanup"); + _episodeService.CleanupOrphanedEpisodes(); + } + } +} diff --git a/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs b/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs new file mode 100644 index 000000000..0df2e5c1f --- /dev/null +++ b/NzbDrone.Core/Housekeeping/Housekeepers/CleanupOrphanedHistoryItems.cs @@ -0,0 +1,23 @@ +using NLog; +using NzbDrone.Core.History; + +namespace NzbDrone.Core.Housekeeping.Housekeepers +{ + public class CleanupOrphanedHistoryItems : IHousekeepingTask + { + private readonly IHistoryService _historyService; + private readonly Logger _logger; + + public CleanupOrphanedHistoryItems(IHistoryService historyService, Logger logger) + { + _historyService = historyService; + _logger = logger; + } + + public void Clean() + { + _logger.Trace("Running orphaned history cleanup"); + _historyService.CleanupOrphaned(); + } + } +} diff --git a/NzbDrone.Core/Housekeeping/HousekeepingCommand.cs b/NzbDrone.Core/Housekeeping/HousekeepingCommand.cs new file mode 100644 index 000000000..ffdc21fc8 --- /dev/null +++ b/NzbDrone.Core/Housekeeping/HousekeepingCommand.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Housekeeping +{ + public class HousekeepingCommand : Command + { + } +} diff --git a/NzbDrone.Core/Housekeeping/HousekeepingService.cs b/NzbDrone.Core/Housekeeping/HousekeepingService.cs new file mode 100644 index 000000000..00933844b --- /dev/null +++ b/NzbDrone.Core/Housekeeping/HousekeepingService.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NLog; +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Housekeeping +{ + public interface IHousekeepingService + { + + } + + public class HousekeepingService : IHousekeepingService, IExecute + { + private readonly IEnumerable _housekeepers; + private readonly Logger _logger; + + public HousekeepingService(IEnumerable housekeepers, Logger logger) + { + _housekeepers = housekeepers; + _logger = logger; + } + + public void Execute(HousekeepingCommand message) + { + _logger.Info("Running housecleaning tasks"); + + foreach (var housekeeper in _housekeepers) + { + try + { + housekeeper.Clean(); + } + catch (Exception ex) + { + _logger.ErrorException("Error running housekeeping task: " + housekeeper.GetType().FullName, ex); + } + } + } + } +} diff --git a/NzbDrone.Core/Housekeeping/IHousekeepingTask.cs b/NzbDrone.Core/Housekeeping/IHousekeepingTask.cs new file mode 100644 index 000000000..f6c536c7d --- /dev/null +++ b/NzbDrone.Core/Housekeeping/IHousekeepingTask.cs @@ -0,0 +1,10 @@ +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.Housekeeping +{ + public interface IHousekeepingTask + { + void Clean(); + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Jobs/TaskManager.cs b/NzbDrone.Core/Jobs/TaskManager.cs index a04931ac3..7851cbbf8 100644 --- a/NzbDrone.Core/Jobs/TaskManager.cs +++ b/NzbDrone.Core/Jobs/TaskManager.cs @@ -5,6 +5,7 @@ using NLog; using NzbDrone.Core.Configuration; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.Housekeeping; using NzbDrone.Core.Indexers; using NzbDrone.Core.Instrumentation.Commands; using NzbDrone.Core.Lifecycle; @@ -51,7 +52,8 @@ namespace NzbDrone.Core.Jobs 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 = 1, TypeName = typeof(TrackedCommandCleanupCommand).FullName} + new ScheduledTask{ Interval = 1, TypeName = typeof(TrackedCommandCleanupCommand).FullName}, + new ScheduledTask{ Interval = 24*60, TypeName = typeof(HousekeepingCommand).FullName} }; var currentTasks = _scheduledTaskRepository.All(); diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 26e301627..e53438f4a 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -214,6 +214,11 @@ + + + + + diff --git a/NzbDrone.Core/Tv/EpisodeRepository.cs b/NzbDrone.Core/Tv/EpisodeRepository.cs index 3a9ade8d9..db9984a0b 100644 --- a/NzbDrone.Core/Tv/EpisodeRepository.cs +++ b/NzbDrone.Core/Tv/EpisodeRepository.cs @@ -22,6 +22,7 @@ namespace NzbDrone.Core.Tv void SetMonitoredFlat(Episode episode, bool monitored); void SetMonitoredBySeason(int seriesId, int seasonNumber, bool monitored); void SetFileId(int episodeId, int fileId); + void CleanupOrphanedEpisodes(); } public class EpisodeRepository : BasicRepository, IEpisodeRepository @@ -123,6 +124,18 @@ namespace NzbDrone.Core.Tv SetFields(new Episode { Id = episodeId, EpisodeFileId = fileId }, episode => episode.EpisodeFileId); } + public void CleanupOrphanedEpisodes() + { + var mapper = _database.GetDataMapper(); + + mapper.ExecuteNonQuery(@"DELETE FROM Episodes + WHERE Id IN ( + SELECT Episodes.Id FROM Episodes + LEFT OUTER JOIN Series + ON Episodes.SeriesId = Series.Id + WHERE Series.Id IS NULL)"); + } + private SortBuilder GetEpisodesWithoutFilesQuery(PagingSpec pagingSpec, DateTime currentTime, int startingSeasonNumber) { return Query.Join(JoinType.Inner, e => e.Series, (e, s) => e.SeriesId == s.Id) diff --git a/NzbDrone.Core/Tv/EpisodeService.cs b/NzbDrone.Core/Tv/EpisodeService.cs index 2e83cc659..b58825e6a 100644 --- a/NzbDrone.Core/Tv/EpisodeService.cs +++ b/NzbDrone.Core/Tv/EpisodeService.cs @@ -30,6 +30,7 @@ namespace NzbDrone.Core.Tv void UpdateMany(List episodes); void DeleteMany(List episodes); void SetEpisodeMonitoredBySeason(int seriesId, int seasonNumber, bool monitored); + void CleanupOrphanedEpisodes(); } public class EpisodeService : IEpisodeService, @@ -113,6 +114,11 @@ namespace NzbDrone.Core.Tv _episodeRepository.SetMonitoredBySeason(seriesId, seasonNumber, monitored); } + public void CleanupOrphanedEpisodes() + { + _episodeRepository.CleanupOrphanedEpisodes(); + } + public bool IsFirstOrLastEpisodeOfSeason(int episodeId) { var episode = GetEpisode(episodeId); From 927dbba945cc2d06832c1141646d053731c6fa92 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Tue, 17 Sep 2013 22:24:27 -0700 Subject: [PATCH 15/49] add series cleanup --- UI/AddSeries/AddSeriesView.js | 78 +++++++++++++++++++---------------- UI/AddSeries/Collection.js | 5 --- 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/UI/AddSeries/AddSeriesView.js b/UI/AddSeries/AddSeriesView.js index 324ac0f90..6e3b832f9 100644 --- a/UI/AddSeries/AddSeriesView.js +++ b/UI/AddSeries/AddSeriesView.js @@ -7,7 +7,8 @@ define( 'AddSeries/SearchResultCollectionView', 'AddSeries/NotFoundView', 'Shared/LoadingView', - ], function (App, Marionette, AddSeriesCollection, SearchResultCollectionView, NotFoundView, LoadingView) { + 'underscore' + ], function (App, Marionette, AddSeriesCollection, SearchResultCollectionView, NotFoundView, LoadingView, _) { return Marionette.Layout.extend({ template: 'AddSeries/AddSeriesTemplate', @@ -25,18 +26,13 @@ define( 'click .x-load-more': '_onLoadMore' }, - _onLoadMore: function () { - var showingAll = this.resultCollectionView.showMore(); - - if (showingAll) { - this.ui.loadMore.hide(); - this.ui.searchBar.show(); - } - }, - initialize: function (options) { - this.collection = new AddSeriesCollection({unmappedFolderModel: this.model}); this.isExisting = options.isExisting; + this.collection = new AddSeriesCollection(); + + if (this.isExisting) { + this.collection.unmappedFolderModel = this.model; + } if (this.isExisting) { this.className = 'existing-series'; @@ -47,55 +43,65 @@ define( } this.listenTo(this.collection, 'sync', this._showResults); + + this.resultCollectionView = new SearchResultCollectionView({ + collection: this.collection, + isExisting: this.isExisting + }); + + this.throttledSearch = _.throttle(this.search, 1000, {trailing: true}).bind(this); }, _onSeriesAdded: function (options) { - if (options.series.get('path') === this.model.get('folder').path) { + if (this.isExisting && options.series.get('path') === this.model.get('folder').path) { this.close(); } }, + _onLoadMore: function () { + var showingAll = this.resultCollectionView.showMore(); + + if (showingAll) { + this.ui.loadMore.hide(); + this.ui.searchBar.show(); + } + }, + onRender: function () { var self = this; this.$el.addClass(this.className); - this.ui.seriesSearch.data('timeout', null).keyup(function () { - window.clearTimeout(self.$el.data('timeout')); - self.$el.data('timeout', window.setTimeout(function () { - self.search.call(self, { - term: self.ui.seriesSearch.val() - }); - }, 500)); + this.ui.seriesSearch.keyup(function () { + self.searchResult.close(); + self._abortExistingSearch(); + self.throttledSearch({ + term: self.ui.seriesSearch.val() + }) }); if (this.isExisting) { this.ui.searchBar.hide(); } + }, - this.resultCollectionView = new SearchResultCollectionView({ - collection: this.collection, - isExisting: this.isExisting - }); + onShow: function () { + this.searchResult.show(this.resultCollectionView); }, search: function (options) { - this.abortExistingSearch(); - this.collection.reset(); - if (!options || options.term === '') { - this.searchResult.close(); + if (!options.term || options.term === this.collection.term) { + return; } - else { - this.searchResult.show(new LoadingView()); - this.collection.term = options.term; - this.currentSearchPromise = this.collection.fetch({ - data: { term: options.term } - }); - } - return this.currentSearchPromise; + + this.searchResult.show(new LoadingView()); + this.collection.term = options.term; + this.currentSearchPromise = this.collection.fetch({ + data: { term: options.term } + }); }, _showResults: function () { @@ -113,7 +119,7 @@ define( } }, - abortExistingSearch: function () { + _abortExistingSearch: function () { if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) { console.log('aborting previous pending search request.'); this.currentSearchPromise.abort(); diff --git a/UI/AddSeries/Collection.js b/UI/AddSeries/Collection.js index 0dce7bc1e..c44acf9a2 100644 --- a/UI/AddSeries/Collection.js +++ b/UI/AddSeries/Collection.js @@ -8,11 +8,6 @@ define( url : window.NzbDrone.ApiRoot + '/series/lookup', model: SeriesModel, - initialize: function (options) { - this.unmappedFolderModel = options.unmappedFolderModel; - }, - - parse: function (response) { var self = this; From 344844234a33bb2a137089cb06daaf5f829d8de4 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Tue, 17 Sep 2013 22:50:13 -0700 Subject: [PATCH 16/49] fixed results not showing on render --- UI/Mixins/AsValidatedView.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/UI/Mixins/AsValidatedView.js b/UI/Mixins/AsValidatedView.js index d074b054e..cf4ef661e 100644 --- a/UI/Mixins/AsValidatedView.js +++ b/UI/Mixins/AsValidatedView.js @@ -9,7 +9,6 @@ define( return function () { var originalOnRender = this.prototype.onRender; - var originalOnClose = this.prototype.onClose; var originalBeforeClose = this.prototype.onBeforeClose; var errorHandler = function (response) { @@ -65,18 +64,6 @@ define( } }; - this.prototype.onClose = function () { - - if (this.model && this.model.isNew()) { - this.model.destroy(); - } - - if (originalOnClose) { - originalBeforeClose.call(this); - } - }; - - return this; }; }); From 4b5d03ac9456eea19c21519c2221131b9e285c83 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Tue, 17 Sep 2013 23:03:09 -0700 Subject: [PATCH 17/49] use _.debounce instead of _.throttle for search --- UI/AddSeries/AddSeriesView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/AddSeries/AddSeriesView.js b/UI/AddSeries/AddSeriesView.js index 6e3b832f9..e3815a3a5 100644 --- a/UI/AddSeries/AddSeriesView.js +++ b/UI/AddSeries/AddSeriesView.js @@ -49,7 +49,7 @@ define( isExisting: this.isExisting }); - this.throttledSearch = _.throttle(this.search, 1000, {trailing: true}).bind(this); + this.throttledSearch = _.debounce(this.search, 1000, {trailing: true}).bind(this); }, _onSeriesAdded: function (options) { From 742a21252cc2c35ffd07d8cd758c5a73fecb2d3f Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Tue, 17 Sep 2013 23:19:36 -0700 Subject: [PATCH 18/49] Command Equality ignores properties defined in Command --- NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs | 5 +++++ .../Messaging/Commands/Tracking/CommandTrackingService.cs | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs b/NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs index 2b24895f1..38017fe4e 100644 --- a/NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs +++ b/NzbDrone.Core/Messaging/Commands/CommandEqualityComparer.cs @@ -26,6 +26,11 @@ namespace NzbDrone.Core.Messaging.Commands continue; } + if (xProperty.DeclaringType == typeof (Command)) + { + continue; + } + var yProperty = yProperties.Single(p => p.Name == xProperty.Name); var xValue = xProperty.GetValue(x, null); diff --git a/NzbDrone.Core/Messaging/Commands/Tracking/CommandTrackingService.cs b/NzbDrone.Core/Messaging/Commands/Tracking/CommandTrackingService.cs index 3e5a6c619..f4ea7ddd0 100644 --- a/NzbDrone.Core/Messaging/Commands/Tracking/CommandTrackingService.cs +++ b/NzbDrone.Core/Messaging/Commands/Tracking/CommandTrackingService.cs @@ -58,8 +58,7 @@ namespace NzbDrone.Core.Messaging.Commands.Tracking public Command FindExisting(Command command) { - return RunningCommands().Where(c => c.GetType() == command.GetType()) - .SingleOrDefault(t => CommandEqualityComparer.Instance.Equals(t, command)); + return RunningCommands().SingleOrDefault(t => CommandEqualityComparer.Instance.Equals(t, command)); } public void Store(Command command) From 42efef0bb2ba0c8618f86dffb651fe3390d36e68 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Wed, 18 Sep 2013 00:33:31 -0700 Subject: [PATCH 19/49] Added PushBullet notifications --- .../Notifications/PushBullet/PushBullet.cs | 47 +++++++++++++++++++ .../PushBullet/PushBulletProxy.cs | 38 +++++++++++++++ .../PushBullet/PushBulletSettings.cs | 22 +++++++++ .../PushBullet/TestPushBulletCommand.cs | 18 +++++++ NzbDrone.Core/NzbDrone.Core.csproj | 4 ++ 5 files changed, 129 insertions(+) create mode 100644 NzbDrone.Core/Notifications/PushBullet/PushBullet.cs create mode 100644 NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs create mode 100644 NzbDrone.Core/Notifications/PushBullet/PushBulletSettings.cs create mode 100644 NzbDrone.Core/Notifications/PushBullet/TestPushBulletCommand.cs diff --git a/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs b/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs new file mode 100644 index 000000000..82c058729 --- /dev/null +++ b/NzbDrone.Core/Notifications/PushBullet/PushBullet.cs @@ -0,0 +1,47 @@ +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.Notifications.PushBullet +{ + public class PushBullet : NotificationBase + { + private readonly IPushBulletProxy _pushBulletProxy; + + public PushBullet(IPushBulletProxy pushBulletProxy) + { + _pushBulletProxy = pushBulletProxy; + } + + public override string Name + { + get { return "PushBullet"; } + } + + public override string ImplementationName + { + get { return "PushBullet"; } + } + + public override string Link + { + get { return "https://www.pushbullet.com/"; } + } + + public override void OnGrab(string message) + { + const string title = "Episode Grabbed"; + + _pushBulletProxy.SendNotification(title, message, Settings.ApiKey, Settings.DeviceId); + } + + public override void OnDownload(string message, Series series) + { + const string title = "Episode Downloaded"; + + _pushBulletProxy.SendNotification(title, message, Settings.ApiKey, Settings.DeviceId); + } + + public override void AfterRename(Series series) + { + } + } +} diff --git a/NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs b/NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs new file mode 100644 index 000000000..edeed98c4 --- /dev/null +++ b/NzbDrone.Core/Notifications/PushBullet/PushBulletProxy.cs @@ -0,0 +1,38 @@ +using System; +using NzbDrone.Core.Messaging.Commands; +using RestSharp; +using NzbDrone.Core.Rest; + +namespace NzbDrone.Core.Notifications.PushBullet +{ + public interface IPushBulletProxy + { + void SendNotification(string title, string message, string apiKey, int deviceId); + } + + public class PushBulletProxy : IPushBulletProxy, IExecute + { + private const string URL = "https://www.pushbullet.com/api/pushes"; + + public void SendNotification(string title, string message, string apiKey, int deviceId) + { + var client = new RestClient(URL); + var request = new RestRequest(Method.POST); + request.AddParameter("device_id", deviceId); + request.AddParameter("type", "note"); + request.AddParameter("title", title); + request.AddParameter("body", message); + + client.Authenticator = new HttpBasicAuthenticator(apiKey, String.Empty); + client.ExecuteAndValidate(request); + } + + public void Execute(TestPushBulletCommand message) + { + const string title = "Test Notification"; + const string body = "This is a test message from NzbDrone"; + + SendNotification(title, body, message.ApiKey, message.DeviceId); + } + } +} diff --git a/NzbDrone.Core/Notifications/PushBullet/PushBulletSettings.cs b/NzbDrone.Core/Notifications/PushBullet/PushBulletSettings.cs new file mode 100644 index 000000000..f7a492622 --- /dev/null +++ b/NzbDrone.Core/Notifications/PushBullet/PushBulletSettings.cs @@ -0,0 +1,22 @@ +using System; +using NzbDrone.Core.Annotations; + +namespace NzbDrone.Core.Notifications.PushBullet +{ + public class PushBulletSettings : INotifcationSettings + { + [FieldDefinition(0, Label = "API Key", HelpLink = "https://www.pushbullet.com/")] + public String ApiKey { get; set; } + + [FieldDefinition(1, Label = "Device ID")] + public Int32 DeviceId { get; set; } + + public bool IsValid + { + get + { + return !String.IsNullOrWhiteSpace(ApiKey) && DeviceId > 0; + } + } + } +} diff --git a/NzbDrone.Core/Notifications/PushBullet/TestPushBulletCommand.cs b/NzbDrone.Core/Notifications/PushBullet/TestPushBulletCommand.cs new file mode 100644 index 000000000..715be4661 --- /dev/null +++ b/NzbDrone.Core/Notifications/PushBullet/TestPushBulletCommand.cs @@ -0,0 +1,18 @@ +using NzbDrone.Core.Messaging.Commands; + +namespace NzbDrone.Core.Notifications.PushBullet +{ + public class TestPushBulletCommand : Command + { + + public override bool SendUpdatesToClient + { + get + { + return true; + } + } + public string ApiKey { get; set; } + public int DeviceId { get; set; } + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index e53438f4a..591ccb069 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -254,6 +254,10 @@ + + + + From 904061c2f0d3eabb243765978bbdaa61b221fb75 Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Wed, 18 Sep 2013 12:43:37 -0700 Subject: [PATCH 20/49] fix add existing series. --- UI/AddSeries/AddSeriesTemplate.html | 11 +++++++---- UI/AddSeries/AddSeriesView.js | 6 ++++-- UI/AddSeries/Existing/CollectionView.js | 11 ++++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/UI/AddSeries/AddSeriesTemplate.html b/UI/AddSeries/AddSeriesTemplate.html index 696eb92f7..518a5ed15 100644 --- a/UI/AddSeries/AddSeriesTemplate.html +++ b/UI/AddSeries/AddSeriesTemplate.html @@ -1,14 +1,17 @@ {{#if folder.path}} -
+
{{folder.path}}
-
-{{/if}} +
{{/if}}
@@ -17,4 +20,4 @@ +
{{debug}} diff --git a/UI/AddSeries/AddSeriesView.js b/UI/AddSeries/AddSeriesView.js index e3815a3a5..e66b1e891 100644 --- a/UI/AddSeries/AddSeriesView.js +++ b/UI/AddSeries/AddSeriesView.js @@ -60,10 +60,10 @@ define( _onLoadMore: function () { var showingAll = this.resultCollectionView.showMore(); + this.ui.searchBar.show(); if (showingAll) { this.ui.loadMore.hide(); - this.ui.searchBar.show(); } }, @@ -94,7 +94,7 @@ define( this.collection.reset(); if (!options.term || options.term === this.collection.term) { - return; + return $.Deferred().resolve(); } this.searchResult.show(new LoadingView()); @@ -102,6 +102,8 @@ define( this.currentSearchPromise = this.collection.fetch({ data: { term: options.term } }); + + return this.currentSearchPromise; }, _showResults: function () { diff --git a/UI/AddSeries/Existing/CollectionView.js b/UI/AddSeries/Existing/CollectionView.js index e5added59..74108d33d 100644 --- a/UI/AddSeries/Existing/CollectionView.js +++ b/UI/AddSeries/Existing/CollectionView.js @@ -20,17 +20,18 @@ define( }, _showAndSearch: function (index) { - + var self = this; var model = this.collection.at(index); if (model) { - var self = this; var currentIndex = index; var folderName = model.get('folder').name; this.addItemView(model, this.getItemView(), index); - $.when(this.children.findByModel(model).search({term: folderName})).then(function () { - self._showAndSearch(currentIndex + 1); - }); + this.children.findByModel(model) + .search({term: folderName}) + .always((function () { + self._showAndSearch(currentIndex + 1); + })); } }, From 41c5619d1bfbb48f34dfa988bf131a93fce0792b Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Wed, 18 Sep 2013 17:24:50 -0700 Subject: [PATCH 21/49] added custom IntConverter to get around the mono bug http://json.codeplex.com/workitem/24176 --- NzbDrone.Common/NzbDrone.Common.csproj | 1 + NzbDrone.Common/RestProvider.cs | 4 +- NzbDrone.Common/Serializer/IntConverter.cs | 39 +++++++++++++++++++ NzbDrone.Common/Serializer/Json.cs | 39 +++++++++++-------- .../NzbgetProviderTests/DownloadNzbFixture.cs | 10 ++++- .../Download/Clients/Nzbget/NzbgetClient.cs | 16 ++++---- .../Download/Clients/Sabnzbd/SabnzbdClient.cs | 12 +++--- .../Indexers/IndexerSettingProvider.cs | 4 +- .../NotificationSettingsProvider.cs | 4 +- .../Notifications/Xbmc/JsonApiProvider.cs | 10 ++--- .../Notifications/Xbmc/XbmcService.cs | 7 ++-- .../JsonTests/JsonFixture.cs | 12 ++++-- .../NzbDrone.Libraries.Test.csproj | 12 ++++-- NzbDrone.Libraries.Test/packages.config | 2 + NzbDrone.Test.Common/ObjectExtentions.cs | 8 ++-- 15 files changed, 123 insertions(+), 57 deletions(-) create mode 100644 NzbDrone.Common/Serializer/IntConverter.cs diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index d55670a61..f1899c2b5 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -98,6 +98,7 @@ + diff --git a/NzbDrone.Common/RestProvider.cs b/NzbDrone.Common/RestProvider.cs index 9b1823257..6964d4b87 100644 --- a/NzbDrone.Common/RestProvider.cs +++ b/NzbDrone.Common/RestProvider.cs @@ -2,9 +2,9 @@ using System.IO; using System.Net; using System.Text; -using Newtonsoft.Json; using NzbDrone.Common.Contract; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Serializer; namespace NzbDrone.Common { @@ -27,7 +27,7 @@ namespace NzbDrone.Common { try { - var json = JsonConvert.SerializeObject(message); + var json = message.ToJson(); var request = (HttpWebRequest)WebRequest.Create(url); request.Timeout = TIMEOUT; diff --git a/NzbDrone.Common/Serializer/IntConverter.cs b/NzbDrone.Common/Serializer/IntConverter.cs new file mode 100644 index 000000000..d30db4c74 --- /dev/null +++ b/NzbDrone.Common/Serializer/IntConverter.cs @@ -0,0 +1,39 @@ +using System; +using Newtonsoft.Json; + +namespace NzbDrone.Common.Serializer +{ + public class IntConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else + { + writer.WriteValue(value); + } + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (!CanConvert(objectType)) + { + throw new JsonSerializationException("Can't convert type " + existingValue.GetType().FullName + " to number"); + } + if (objectType == typeof(Int64)) + { + return Convert.ToInt64(reader.Value); + } + + return Convert.ToInt32(reader.Value); + } + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Int32) || objectType == typeof(Int64) || objectType == typeof(int); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Common/Serializer/Json.cs b/NzbDrone.Common/Serializer/Json.cs index 866776ec9..c6c4ded7e 100644 --- a/NzbDrone.Common/Serializer/Json.cs +++ b/NzbDrone.Common/Serializer/Json.cs @@ -8,42 +8,47 @@ namespace NzbDrone.Common.Serializer { public static class Json { - private static readonly JsonSerializer JsonNetSerializer; - + private static readonly JsonSerializer Serializer; + private static readonly JsonSerializerSettings SerializerSetting; static Json() { - JsonNetSerializer = new JsonSerializer() - { - DateTimeZoneHandling = DateTimeZoneHandling.Utc, - NullValueHandling = NullValueHandling.Ignore, - Formatting = Formatting.Indented, - DefaultValueHandling = DefaultValueHandling.Include, - ContractResolver = new CamelCasePropertyNamesContractResolver() - }; - - JsonNetSerializer.Converters.Add(new StringEnumConverter { CamelCaseText = true }); + SerializerSetting = new JsonSerializerSettings + { + DateTimeZoneHandling = DateTimeZoneHandling.Utc, + NullValueHandling = NullValueHandling.Ignore, + Formatting = Formatting.Indented, + DefaultValueHandling = DefaultValueHandling.Include, + ContractResolver = new CamelCasePropertyNamesContractResolver() + }; + + + SerializerSetting.Converters.Add(new StringEnumConverter { CamelCaseText = true }); + SerializerSetting.Converters.Add(new IntConverter()); + + Serializer = JsonSerializer.Create(SerializerSetting); + } - public static T Deserialize(string json) where T : class, new() + public static T Deserialize(string json) where T : new() { - return JsonConvert.DeserializeObject(json); + return JsonConvert.DeserializeObject(json, SerializerSetting); } public static object Deserialize(string json, Type type) { - return JsonConvert.DeserializeObject(json, type); + return JsonConvert.DeserializeObject(json, type, SerializerSetting); } public static string ToJson(this object obj) { - return JsonConvert.SerializeObject(obj); + return JsonConvert.SerializeObject(obj, SerializerSetting); } public static void Serialize(TModel model, TextWriter outputStream) { var jsonTextWriter = new JsonTextWriter(outputStream); - JsonNetSerializer.Serialize(jsonTextWriter, model); + Serializer.Serialize(jsonTextWriter, model); jsonTextWriter.Flush(); } diff --git a/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs b/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs index c897df251..2e042dca1 100644 --- a/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs +++ b/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/DownloadNzbFixture.cs @@ -4,6 +4,7 @@ using FizzWare.NBuilder; using Moq; using NUnit.Framework; using NzbDrone.Common; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.Nzbget; using NzbDrone.Core.Parser.Model; @@ -51,9 +52,16 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetProviderTests [Test] public void should_add_item_to_queue() { + + var command = new JsonRequest + { + Method = "appendurl", + Params = new object[] { "30.Rock.S01E01.Pilot.720p.hdtv.nzb", "TV", 50, false, "http://www.nzbdrone.com" } + }; + Mocker.GetMock() .Setup(s => s.PostCommand("192.168.5.55:6789", "nzbget", "pass", - It.Is(c => c.Equals("{\"method\":\"appendurl\",\"params\":[\"30.Rock.S01E01.Pilot.720p.hdtv.nzb\",\"TV\",50,false,\"http://www.nzbdrone.com\"]}")))) + It.Is(c => c.Equals(command.ToJson())))) .Returns("{\"version\": \"1.1\",\"result\": true}"); Mocker.Resolve().DownloadNzb(_remoteEpisode); diff --git a/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs b/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs index 144805113..ee94a2198 100644 --- a/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs +++ b/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; -using Newtonsoft.Json; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; using NzbDrone.Core.Parser.Model; @@ -36,11 +36,11 @@ namespace NzbDrone.Core.Download.Clients.Nzbget }; _logger.Info("Adding report [{0}] to the queue.", title); - var response = PostCommand(JsonConvert.SerializeObject(command)); + var response = PostCommand(command.ToJson()); CheckForError(response); - var success = JsonConvert.DeserializeObject(response).Result; + var success = Json.Deserialize(response).Result; _logger.Debug("Queue Response: [{0}]", success); } @@ -61,11 +61,11 @@ namespace NzbDrone.Core.Download.Clients.Nzbget Params = null }; - var response = PostCommand(JsonConvert.SerializeObject(command)); + var response = PostCommand(command.ToJson()); CheckForError(response); - var itmes = JsonConvert.DeserializeObject(response).QueueItems; + var itmes = Json.Deserialize(response).QueueItems; foreach (var nzbGetQueueItem in itmes) { @@ -101,11 +101,11 @@ namespace NzbDrone.Core.Download.Clients.Nzbget }; var address = String.Format(@"{0}:{1}", host, port); - var response = _httpProvider.PostCommand(address, username, password, JsonConvert.SerializeObject(command)); + var response = _httpProvider.PostCommand(address, username, password, command.ToJson()); CheckForError(response); - return JsonConvert.DeserializeObject(response); + return Json.Deserialize(response); } public virtual string Test(string host, int port, string username, string password) @@ -134,7 +134,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget private void CheckForError(string response) { - var result = JsonConvert.DeserializeObject(response); + var result = Json.Deserialize(response); if (result.Error != null) throw new ApplicationException(result.Error.ToString()); diff --git a/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs b/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs index 40a241a1c..7a6eac544 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.Web; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using NzbDrone.Common; using NzbDrone.Common.Cache; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; using NzbDrone.Core.Parser.Model; using RestSharp; @@ -107,7 +107,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd CheckForError(response); - var sabQueue = JsonConvert.DeserializeObject(JObject.Parse(response).SelectToken("queue").ToString()).Items; + var sabQueue = Json.Deserialize(JObject.Parse(response).SelectToken("queue").ToString()).Items; var queueItems = new List(); @@ -134,7 +134,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd CheckForError(response); - var items = JsonConvert.DeserializeObject(JObject.Parse(response).SelectToken("history").ToString()).Items; + var items = Json.Deserialize(JObject.Parse(response).SelectToken("history").ToString()).Items; return items ?? new List(); } @@ -166,7 +166,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd if (String.IsNullOrWhiteSpace(response)) return new SabCategoryModel { categories = new List() }; - var categories = JsonConvert.DeserializeObject(response); + var categories = Json.Deserialize(response); return categories; } @@ -199,7 +199,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd if (String.IsNullOrWhiteSpace(response)) return null; - var version = JsonConvert.DeserializeObject(response); + var version = Json.Deserialize(response); return version; } @@ -232,7 +232,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd private void CheckForError(string response) { - var result = JsonConvert.DeserializeObject(response); + var result = Json.Deserialize(response); if (result.Status != null && result.Status.Equals("false", StringComparison.InvariantCultureIgnoreCase)) throw new ApplicationException(result.Error); diff --git a/NzbDrone.Core/Indexers/IndexerSettingProvider.cs b/NzbDrone.Core/Indexers/IndexerSettingProvider.cs index a0a0650c7..ab7da9e58 100644 --- a/NzbDrone.Core/Indexers/IndexerSettingProvider.cs +++ b/NzbDrone.Core/Indexers/IndexerSettingProvider.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using NzbDrone.Common.Serializer; namespace NzbDrone.Core.Indexers { @@ -25,7 +25,7 @@ namespace NzbDrone.Core.Indexers return new TSetting(); } - return JsonConvert.DeserializeObject(indexerDef.Settings); + return Json.Deserialize(indexerDef.Settings); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Notifications/NotificationSettingsProvider.cs b/NzbDrone.Core/Notifications/NotificationSettingsProvider.cs index 2ee5d7443..53f4d8a58 100644 --- a/NzbDrone.Core/Notifications/NotificationSettingsProvider.cs +++ b/NzbDrone.Core/Notifications/NotificationSettingsProvider.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using NzbDrone.Common.Serializer; namespace NzbDrone.Core.Notifications { @@ -25,7 +25,7 @@ namespace NzbDrone.Core.Notifications return new TSetting(); } - return JsonConvert.DeserializeObject(indexerDef.Settings); + return Json.Deserialize(indexerDef.Settings); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs b/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs index 1d7cb5377..aa06901d1 100644 --- a/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs +++ b/NzbDrone.Core/Notifications/Xbmc/JsonApiProvider.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Linq; using NLog; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NzbDrone.Common; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Model.Xbmc; using NzbDrone.Core.Tv; @@ -72,7 +72,7 @@ namespace NzbDrone.Core.Notifications.Xbmc if (CheckForError(response)) return new List(); - var result = JsonConvert.DeserializeObject(response); + var result = Json.Deserialize(response); return result.Result; } @@ -97,7 +97,7 @@ namespace NzbDrone.Core.Notifications.Xbmc if (response.StartsWith("{\"error\"")) { - var error = JsonConvert.DeserializeObject(response); + var error = Json.Deserialize(response); var code = error.Error["code"]; var message = error.Error["message"]; @@ -159,7 +159,7 @@ namespace NzbDrone.Core.Notifications.Xbmc if (CheckForError(response)) return; _logger.Trace(" from response"); - var result = JsonConvert.DeserializeObject>(response); + var result = Json.Deserialize>(response); if (!result.Result.Equals("OK", StringComparison.InvariantCultureIgnoreCase)) { @@ -185,7 +185,7 @@ namespace NzbDrone.Core.Notifications.Xbmc if (CheckForError(response)) return new List(); - var result = JsonConvert.DeserializeObject(response); + var result = Json.Deserialize(response); var shows = result.Result.TvShows; return shows; diff --git a/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs b/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs index bbc8a237a..9444b79f0 100644 --- a/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs +++ b/NzbDrone.Core/Notifications/Xbmc/XbmcService.cs @@ -1,12 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using NzbDrone.Common; using NzbDrone.Common.Instrumentation; -using NzbDrone.Core.Messaging; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Tv; using NzbDrone.Core.Model.Xbmc; @@ -63,7 +62,7 @@ namespace NzbDrone.Core.Notifications.Xbmc var response = _httpProvider.PostCommand(settings.Address, settings.Username, settings.Password, postJson.ToString()); Logger.Trace("Getting version from response"); - var result = JsonConvert.DeserializeObject>(response); + var result = Json.Deserialize>(response); var versionObject = result.Result.Property("version"); @@ -71,7 +70,7 @@ namespace NzbDrone.Core.Notifications.Xbmc return new XbmcVersion((int)versionObject.Value); if (versionObject.Value.Type == JTokenType.Object) - return JsonConvert.DeserializeObject(versionObject.Value.ToString()); + return Json.Deserialize(versionObject.Value.ToString()); throw new InvalidCastException("Unknown Version structure!: " + versionObject); } diff --git a/NzbDrone.Libraries.Test/JsonTests/JsonFixture.cs b/NzbDrone.Libraries.Test/JsonTests/JsonFixture.cs index 19a663db4..951eaa4a1 100644 --- a/NzbDrone.Libraries.Test/JsonTests/JsonFixture.cs +++ b/NzbDrone.Libraries.Test/JsonTests/JsonFixture.cs @@ -1,3 +1,5 @@ +using System; +using FluentAssertions; using NUnit.Framework; using NzbDrone.Common.Serializer; using NzbDrone.Test.Common; @@ -9,15 +11,19 @@ namespace NzbDrone.Libraries.Test.JsonTests { public class TypeWithNumbers { - public int Id { get; set; } + public int Int32 { get; set; } + public Int64 Int64 { get; set; } + public int? nullableIntIsNull { get; set; } + public int? nullableWithValue { get; set; } } [Test] public void should_be_able_to_deserialize_numbers() { - var quality = new TypeWithNumbers { Id = 12 }; + var quality = new TypeWithNumbers { Int32 = Int32.MaxValue, Int64 = Int64.MaxValue, nullableWithValue = 12 }; + var result = Json.Deserialize(quality.ToJson()); - Json.Deserialize(quality.ToJson()); + result.ShouldHave().AllProperties().EqualTo(quality); } } } diff --git a/NzbDrone.Libraries.Test/NzbDrone.Libraries.Test.csproj b/NzbDrone.Libraries.Test/NzbDrone.Libraries.Test.csproj index 11d3bbdc2..14c492823 100644 --- a/NzbDrone.Libraries.Test/NzbDrone.Libraries.Test.csproj +++ b/NzbDrone.Libraries.Test/NzbDrone.Libraries.Test.csproj @@ -33,6 +33,14 @@ MinimumRecommendedRules.ruleset + + False + ..\packages\FluentAssertions.2.1.0.0\lib\net40\FluentAssertions.dll + + + False + ..\packages\Newtonsoft.Json.5.0.6\lib\net40\Newtonsoft.Json.dll + ..\packages\NUnit.2.6.2\lib\nunit.framework.dll @@ -65,9 +73,7 @@ NzbDrone.Test.Common - - - + - + \ No newline at end of file diff --git a/NzbDrone.Api/packages.config b/NzbDrone.Api/packages.config index dff31468c..722ec5a8b 100644 --- a/NzbDrone.Api/packages.config +++ b/NzbDrone.Api/packages.config @@ -2,8 +2,8 @@ - - + + diff --git a/NzbDrone.Host/NzbDrone.Host.csproj b/NzbDrone.Host/NzbDrone.Host.csproj index 7ecf58a13..71a3643d6 100644 --- a/NzbDrone.Host/NzbDrone.Host.csproj +++ b/NzbDrone.Host/NzbDrone.Host.csproj @@ -89,12 +89,13 @@ False ..\packages\Microsoft.Owin.Hosting.1.1.0-beta2\lib\net40\Microsoft.Owin.Hosting.dll - - ..\packages\Nancy.0.18.0\lib\net40\Nancy.dll + + False + ..\packages\Nancy.0.20.0\lib\net40\Nancy.dll - + False - ..\packages\Nancy.Owin.0.18.0\lib\net40\Nancy.Owin.dll + ..\packages\Nancy.Owin.0.20.0\lib\net40\Nancy.Owin.dll False diff --git a/NzbDrone.Host/Owin/MiddleWare/NancyMiddleWare.cs b/NzbDrone.Host/Owin/MiddleWare/NancyMiddleWare.cs index 54fc516d8..9e74c1e71 100644 --- a/NzbDrone.Host/Owin/MiddleWare/NancyMiddleWare.cs +++ b/NzbDrone.Host/Owin/MiddleWare/NancyMiddleWare.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Nancy.Bootstrapper; +using Nancy.Bootstrapper; using Nancy.Owin; using Owin; @@ -20,8 +17,13 @@ namespace NzbDrone.Host.Owin.MiddleWare public void Attach(IAppBuilder appBuilder) { - var nancyOwinHost = new NancyOwinHost(null, _nancyBootstrapper, new HostConfiguration()); - appBuilder.Use((Func, Task>, Func, Task>>)(next => (Func, Task>)nancyOwinHost.Invoke), new object[0]); + var options = new NancyOptions + { + Bootstrapper = _nancyBootstrapper, + PerformPassThrough = context => context.Request.Path.StartsWith("/signalr") + }; + + appBuilder.UseNancy(options); } } } \ No newline at end of file diff --git a/NzbDrone.Host/packages.config b/NzbDrone.Host/packages.config index 36d7e82de..f134e38ed 100644 --- a/NzbDrone.Host/packages.config +++ b/NzbDrone.Host/packages.config @@ -5,8 +5,8 @@ - - + + diff --git a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj index 9bba4d240..10d08e97d 100644 --- a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj +++ b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj @@ -59,13 +59,13 @@ False ..\packages\Moq.4.0.10827\lib\NET40\Moq.dll - + False - ..\packages\Nancy.0.18.0\lib\net40\Nancy.dll + ..\packages\Nancy.0.20.0\lib\net40\Nancy.dll - + False - ..\packages\Nancy.Owin.0.18.0\lib\net40\Nancy.Owin.dll + ..\packages\Nancy.Owin.0.20.0\lib\net40\Nancy.Owin.dll False diff --git a/NzbDrone.Integration.Test/packages.config b/NzbDrone.Integration.Test/packages.config index 268fde156..7a828fde8 100644 --- a/NzbDrone.Integration.Test/packages.config +++ b/NzbDrone.Integration.Test/packages.config @@ -7,8 +7,8 @@ - - + + From 5f8c6da9d95d925c8ecd5826502799629d31deb1 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Wed, 18 Sep 2013 23:55:28 -0700 Subject: [PATCH 35/49] updated restsharp --- NzbDrone.Core/NzbDrone.Core.csproj | 5 +++-- NzbDrone.Core/packages.config | 2 +- NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj | 5 +++-- NzbDrone.Integration.Test/packages.config | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index c9fe66259..08803f3fa 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -96,8 +96,9 @@ False ..\packages\Prowlin.0.9.4456.26422\lib\net40\Prowlin.dll - - ..\packages\RestSharp.104.1\lib\net4\RestSharp.dll + + False + ..\packages\RestSharp.104.2.0\lib\net4\RestSharp.dll diff --git a/NzbDrone.Core/packages.config b/NzbDrone.Core/packages.config index 6674b3182..b0bc75532 100644 --- a/NzbDrone.Core/packages.config +++ b/NzbDrone.Core/packages.config @@ -7,6 +7,6 @@ - + \ No newline at end of file diff --git a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj index 10d08e97d..75bea0f21 100644 --- a/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj +++ b/NzbDrone.Integration.Test/NzbDrone.Integration.Test.csproj @@ -81,8 +81,9 @@ False ..\packages\Owin.1.0\lib\net40\Owin.dll - - ..\packages\RestSharp.104.1\lib\net4\RestSharp.dll + + False + ..\packages\RestSharp.104.2.0\lib\net4\RestSharp.dll diff --git a/NzbDrone.Integration.Test/packages.config b/NzbDrone.Integration.Test/packages.config index 7a828fde8..101c14bc1 100644 --- a/NzbDrone.Integration.Test/packages.config +++ b/NzbDrone.Integration.Test/packages.config @@ -13,5 +13,5 @@ - + \ No newline at end of file From e0fb77a52125447405c789300412131e4d3a9a99 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 19 Sep 2013 00:16:59 -0700 Subject: [PATCH 36/49] NzbDroneErrorPipeline uses the new aggregate exceptions. --- .../ErrorManagement/NzbDroneErrorPipeline.cs | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs b/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs index bc94aee29..04e9263e2 100644 --- a/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs +++ b/NzbDrone.Api/ErrorManagement/NzbDroneErrorPipeline.cs @@ -17,9 +17,11 @@ namespace NzbDrone.Api.ErrorManagement _logger = logger; } - public Response HandleException(NancyContext context, Exception exception) + public Response HandleException(NancyContext context, Exception aggregateException) { - var apiException = exception as ApiException; + var innerException = (aggregateException.InnerException).InnerException; + + var apiException = innerException as ApiException; if (apiException != null) { @@ -27,7 +29,7 @@ namespace NzbDrone.Api.ErrorManagement return apiException.ToErrorResponse(); } - var validationException = exception as ValidationException; + var validationException = innerException as ValidationException; if (validationException != null) { @@ -36,23 +38,23 @@ namespace NzbDrone.Api.ErrorManagement return validationException.Errors.AsResponse(HttpStatusCode.BadRequest); } - var clientException = exception as NzbDroneClientException; + var clientException = innerException as NzbDroneClientException; if (clientException != null) { return new ErrorModel { - Message = exception.Message, - Description = exception.ToString() + Message = innerException.Message, + Description = innerException.ToString() }.AsResponse((HttpStatusCode)clientException.StatusCode); } - _logger.FatalException("Request Failed", exception); + _logger.FatalException("Request Failed", innerException); return new ErrorModel { - Message = exception.Message, - Description = exception.ToString() + Message = innerException.Message, + Description = innerException.ToString() }.AsResponse(HttpStatusCode.InternalServerError); } } From 57540f9ecb78dc8b9589dc4244f23c53700d3a52 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 19 Sep 2013 00:20:40 -0700 Subject: [PATCH 37/49] adde json.net to update package. --- build.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build.ps1 b/build.ps1 index ba9018dde..f8bc5ad62 100644 --- a/build.ps1 +++ b/build.ps1 @@ -86,6 +86,7 @@ Function AddJsonNet() { get-childitem $outputFolder -File -Filter Newtonsoft.Json.* -Recurse | foreach ($_) {remove-item $_.fullname} Copy-Item .\packages\Newtonsoft.Json.5.*\lib\net35\*.dll -Destination $outputFolder + Copy-Item .\packages\Newtonsoft.Json.5.*\lib\net35\*.dll -Destination $outputFolder\NzbDrone.Update } Function PackageTests() From 2735e41098d4c04f9b9e13dac0d11586e66befc6 Mon Sep 17 00:00:00 2001 From: "kay.one" Date: Thu, 19 Sep 2013 00:33:11 -0700 Subject: [PATCH 38/49] updated history/missing layout to use collection sync events. --- UI/History/HistoryLayout.js | 15 ++++++++------- UI/Missing/MissingLayout.js | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/UI/History/HistoryLayout.js b/UI/History/HistoryLayout.js index 146361aef..160ef9227 100644 --- a/UI/History/HistoryLayout.js +++ b/UI/History/HistoryLayout.js @@ -77,6 +77,13 @@ define( } ], + + initialize: function () { + this.collection = new HistoryCollection(); + this.listenTo(this.collection, 'sync', this._showTable); + }, + + _showTable: function (collection) { this.history.show(new Backgrid.Grid({ @@ -92,14 +99,8 @@ define( }, onShow: function () { - var self = this; - this.history.show(new LoadingView()); - - var collection = new HistoryCollection(); - collection.fetch().done(function () { - self._showTable(collection); - }); + this.collection.fetch(); } }); diff --git a/UI/Missing/MissingLayout.js b/UI/Missing/MissingLayout.js index 805998380..3b4ae5195 100644 --- a/UI/Missing/MissingLayout.js +++ b/UI/Missing/MissingLayout.js @@ -74,16 +74,17 @@ define( })); }, - onShow: function () { - var self = this; - - this.missing.show(new LoadingView()); + initialize: function () { this.missingCollection = new MissingCollection(); - this.missingCollection.fetch().done(function () { - self._showTable(); - }); + this.listenTo(this.missingCollection, 'sync', this._showTable); + }, + + + onShow: function () { + this.missing.show(new LoadingView()); + this.missingCollection.fetch(); this._showToolbar(); }, From 689f27bee66f772e1a703cc6583cc82cb333a43d Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 19 Sep 2013 12:20:39 -0700 Subject: [PATCH 39/49] episode actions cell width so button does shift whole table --- UI/Cells/cells.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/Cells/cells.less b/UI/Cells/cells.less index 1c699302e..1e4ec5618 100644 --- a/UI/Cells/cells.less +++ b/UI/Cells/cells.less @@ -64,7 +64,7 @@ td.episode-status-cell, td.quality-cell { } .episode-actions-cell { - width: 20px; + width: 50px; li { .clickable(); From a40ad82fa72844208d5944c46bed4f98f2662460 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 20 Sep 2013 00:49:36 -0700 Subject: [PATCH 40/49] DownloadAllowed logic moved, using proper validation --- NzbDrone.Api/Indexers/ReleaseModule.cs | 10 +++------- NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs | 6 +++--- NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs | 1 + NzbDrone.Core/Download/DownloadService.cs | 5 +++++ NzbDrone.Core/Parser/Model/RemoteEpisode.cs | 4 +--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/NzbDrone.Api/Indexers/ReleaseModule.cs b/NzbDrone.Api/Indexers/ReleaseModule.cs index f9f6e88cd..96f2aebc6 100644 --- a/NzbDrone.Api/Indexers/ReleaseModule.cs +++ b/NzbDrone.Api/Indexers/ReleaseModule.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using FluentValidation; using Nancy; using NzbDrone.Api.Mapping; using NzbDrone.Api.REST; @@ -38,18 +39,14 @@ namespace NzbDrone.Api.Indexers _parsingService = parsingService; GetResourceAll = GetReleases; Post["/"] = x=> DownloadRelease(this.Bind()); + + PostValidator.RuleFor(s => s.DownloadAllowed); } private Response DownloadRelease(ReleaseResource release) { var remoteEpisode = _parsingService.Map(release.InjectTo(), 0); remoteEpisode.Release = release.InjectTo(); - - if (remoteEpisode.Series == null || remoteEpisode.Episodes == null || !remoteEpisode.Episodes.Any()) - { - throw new BadRequestException(release); - } - _downloadService.DownloadReport(remoteEpisode); return release.AsResponse(); @@ -92,7 +89,6 @@ namespace NzbDrone.Api.Indexers release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo); release.InjectFrom(downloadDecision); release.Rejections = downloadDecision.Rejections.ToList(); - release.DownloadAllowed = downloadDecision.RemoteEpisode.Series != null; result.Add(release); } diff --git a/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs index b3b840491..411a31f41 100644 --- a/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs +++ b/NzbDrone.Common/EnsureThat/EnsureStringExtensions.cs @@ -1,4 +1,7 @@ +using System.Collections; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Text.RegularExpressions; using NzbDrone.Common.EnsureThat.Resources; using NzbDrone.Common.EnvironmentInfo; @@ -73,7 +76,6 @@ namespace NzbDrone.Common.EnsureThat return param; } - [DebuggerStepThrough] public static Param IsRelativePath(this Param param) { @@ -94,8 +96,6 @@ namespace NzbDrone.Common.EnsureThat return param; } - - [DebuggerStepThrough] public static Param IsValidPath(this Param param) { diff --git a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index f2b1404cf..c03e22d27 100644 --- a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -74,6 +74,7 @@ namespace NzbDrone.Core.DecisionEngine } else { + remoteEpisode.DownloadAllowed = false; decision = new DownloadDecision(remoteEpisode, "Unknown Series"); } } diff --git a/NzbDrone.Core/Download/DownloadService.cs b/NzbDrone.Core/Download/DownloadService.cs index 963f279e6..e57cad094 100644 --- a/NzbDrone.Core/Download/DownloadService.cs +++ b/NzbDrone.Core/Download/DownloadService.cs @@ -1,4 +1,5 @@ using NLog; +using NzbDrone.Common.EnsureThat; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Parser.Model; @@ -27,6 +28,10 @@ namespace NzbDrone.Core.Download public void DownloadReport(RemoteEpisode remoteEpisode) { + Ensure.That(() => remoteEpisode.Series).IsNotNull(); + Ensure.That(() => remoteEpisode.Episodes).IsNotNull(); + Ensure.That(() => remoteEpisode.Episodes).HasItems(); + var downloadTitle = remoteEpisode.Release.Title; var downloadClient = _downloadClientProvider.GetDownloadClient(); diff --git a/NzbDrone.Core/Parser/Model/RemoteEpisode.cs b/NzbDrone.Core/Parser/Model/RemoteEpisode.cs index ac394d0d8..f87f71c8e 100644 --- a/NzbDrone.Core/Parser/Model/RemoteEpisode.cs +++ b/NzbDrone.Core/Parser/Model/RemoteEpisode.cs @@ -8,12 +8,10 @@ namespace NzbDrone.Core.Parser.Model public class RemoteEpisode { public ReleaseInfo Release { get; set; } - public ParsedEpisodeInfo ParsedEpisodeInfo { get; set; } - public Series Series { get; set; } - public List Episodes { get; set; } + public Boolean DownloadAllowed { get; set; } public bool IsRecentEpisode() { From 5343bde0700fcbe09232849161afdfff517c4213 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 20 Sep 2013 14:09:13 -0700 Subject: [PATCH 41/49] Cleaned up some validation --- NzbDrone.Api/Indexers/ReleaseModule.cs | 2 +- NzbDrone.Core/Download/DownloadService.cs | 1 - NzbDrone.Core/Rest/RestSharpExtensions.cs | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/NzbDrone.Api/Indexers/ReleaseModule.cs b/NzbDrone.Api/Indexers/ReleaseModule.cs index 96f2aebc6..adba147de 100644 --- a/NzbDrone.Api/Indexers/ReleaseModule.cs +++ b/NzbDrone.Api/Indexers/ReleaseModule.cs @@ -40,7 +40,7 @@ namespace NzbDrone.Api.Indexers GetResourceAll = GetReleases; Post["/"] = x=> DownloadRelease(this.Bind()); - PostValidator.RuleFor(s => s.DownloadAllowed); + PostValidator.RuleFor(s => s.DownloadAllowed).Equal(true); } private Response DownloadRelease(ReleaseResource release) diff --git a/NzbDrone.Core/Download/DownloadService.cs b/NzbDrone.Core/Download/DownloadService.cs index e57cad094..f41acdafa 100644 --- a/NzbDrone.Core/Download/DownloadService.cs +++ b/NzbDrone.Core/Download/DownloadService.cs @@ -29,7 +29,6 @@ namespace NzbDrone.Core.Download public void DownloadReport(RemoteEpisode remoteEpisode) { Ensure.That(() => remoteEpisode.Series).IsNotNull(); - Ensure.That(() => remoteEpisode.Episodes).IsNotNull(); Ensure.That(() => remoteEpisode.Episodes).HasItems(); var downloadTitle = remoteEpisode.Release.Title; diff --git a/NzbDrone.Core/Rest/RestSharpExtensions.cs b/NzbDrone.Core/Rest/RestSharpExtensions.cs index 33b5e681b..2a25a1222 100644 --- a/NzbDrone.Core/Rest/RestSharpExtensions.cs +++ b/NzbDrone.Core/Rest/RestSharpExtensions.cs @@ -62,7 +62,6 @@ namespace NzbDrone.Core.Rest return client.Execute(request).ValidateResponse(client); } - public static void AddQueryString(this IRestRequest request, string name, object value) { request.AddParameter(name, value.ToString(), ParameterType.GetOrPost); From f826890d2b537679cf08f936c99c64a836b384ff Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 20 Sep 2013 16:56:17 -0700 Subject: [PATCH 42/49] Check if URL is registered when running in non-admin and run accordingly --- NzbDrone.App.Test/MonitoringProviderTest.cs | 1 + NzbDrone.Common.Test/ProcessProviderTests.cs | 1 + NzbDrone.Common/NzbDrone.Common.csproj | 3 +- NzbDrone.Common/Processes/ProcessOutput.cs | 17 +++++++++ .../{ => Processes}/ProcessProvider.cs | 18 ++++++---- .../UpdateTests/UpdateServiceFixture.cs | 1 + NzbDrone.Core/Update/InstallUpdateService.cs | 1 + NzbDrone.Host/AccessControl/UrlAclAdapter.cs | 35 ++++++++++++++++--- NzbDrone.Host/ApplicationServer.cs | 1 + NzbDrone.Host/Owin/OwinHostController.cs | 32 ++++++++++++----- NzbDrone.Host/PriorityMonitor.cs | 1 + NzbDrone.Integration.Test/NzbDroneRunner.cs | 1 + NzbDrone.Update.Test/ProgramFixture.cs | 1 + NzbDrone.Update.Test/StartNzbDroneService.cs | 1 + NzbDrone.Update/UpdateApp.cs | 1 + .../UpdateEngine/DetectApplicationType.cs | 1 + NzbDrone.Update/UpdateEngine/StartNzbDrone.cs | 1 + .../UpdateEngine/TerminateNzbDrone.cs | 1 + NzbDrone/SysTray/SysTrayApp.cs | 1 + 19 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 NzbDrone.Common/Processes/ProcessOutput.cs rename NzbDrone.Common/{ => Processes}/ProcessProvider.cs (94%) diff --git a/NzbDrone.App.Test/MonitoringProviderTest.cs b/NzbDrone.App.Test/MonitoringProviderTest.cs index 2d504b072..f1e4b0a9b 100644 --- a/NzbDrone.App.Test/MonitoringProviderTest.cs +++ b/NzbDrone.App.Test/MonitoringProviderTest.cs @@ -4,6 +4,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Common.Model; +using NzbDrone.Common.Processes; using NzbDrone.Host; using NzbDrone.Test.Common; diff --git a/NzbDrone.Common.Test/ProcessProviderTests.cs b/NzbDrone.Common.Test/ProcessProviderTests.cs index 4155ab4d0..099defbe9 100644 --- a/NzbDrone.Common.Test/ProcessProviderTests.cs +++ b/NzbDrone.Common.Test/ProcessProviderTests.cs @@ -6,6 +6,7 @@ using System.Linq; using FluentAssertions; using NUnit.Framework; using NzbDrone.Common.Model; +using NzbDrone.Common.Processes; using NzbDrone.Test.Common; using NzbDrone.Test.Dummy; diff --git a/NzbDrone.Common/NzbDrone.Common.csproj b/NzbDrone.Common/NzbDrone.Common.csproj index 8fe4ab3af..0d76e91a2 100644 --- a/NzbDrone.Common/NzbDrone.Common.csproj +++ b/NzbDrone.Common/NzbDrone.Common.csproj @@ -98,6 +98,7 @@ + @@ -122,7 +123,7 @@ - + diff --git a/NzbDrone.Common/Processes/ProcessOutput.cs b/NzbDrone.Common/Processes/ProcessOutput.cs new file mode 100644 index 000000000..231b6097e --- /dev/null +++ b/NzbDrone.Common/Processes/ProcessOutput.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Common.Processes +{ + public class ProcessOutput + { + public List Standard { get; set; } + public List Error { get; set; } + + public ProcessOutput() + { + Standard = new List(); + Error = new List(); + } + } +} diff --git a/NzbDrone.Common/ProcessProvider.cs b/NzbDrone.Common/Processes/ProcessProvider.cs similarity index 94% rename from NzbDrone.Common/ProcessProvider.cs rename to NzbDrone.Common/Processes/ProcessProvider.cs index e93edd781..75bde50f8 100644 --- a/NzbDrone.Common/ProcessProvider.cs +++ b/NzbDrone.Common/Processes/ProcessProvider.cs @@ -9,7 +9,7 @@ using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Model; -namespace NzbDrone.Common +namespace NzbDrone.Common.Processes { public interface IProcessProvider { @@ -23,6 +23,7 @@ namespace NzbDrone.Common ProcessPriorityClass GetCurrentProcessPriority(); Process Start(string path, string args = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null); Process SpawnNewProcess(string path, string args = null); + ProcessOutput StartAndCapture(string path, string args = null); } public class ProcessProvider : IProcessProvider @@ -88,11 +89,8 @@ namespace NzbDrone.Common process.Start(); } - - public Process Start(string path, string args = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null) { - if (OsInfo.IsMono && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase)) { args = path + " " + args; @@ -147,7 +145,6 @@ namespace NzbDrone.Common process.BeginErrorReadLine(); process.BeginOutputReadLine(); - return process; } @@ -172,6 +169,16 @@ namespace NzbDrone.Common return process; } + public ProcessOutput StartAndCapture(string path, string args = null) + { + var output = new ProcessOutput(); + var process = Start(path, args, s => output.Standard.Add(s), error => output.Error.Add(error)); + + WaitForExit(process); + + return output; + } + public void WaitForExit(Process process) { Logger.Trace("Waiting for process {0} to exit.", process.ProcessName); @@ -225,7 +232,6 @@ namespace NzbDrone.Common return null; } - private static string GetExeFileName(Process process) { if (process.MainModule.FileName != "mono.exe") diff --git a/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs b/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs index 31ebee3e5..32b0c49ea 100644 --- a/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs +++ b/NzbDrone.Core.Test/UpdateTests/UpdateServiceFixture.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Model; +using NzbDrone.Common.Processes; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Update; using NzbDrone.Core.Update.Commands; diff --git a/NzbDrone.Core/Update/InstallUpdateService.cs b/NzbDrone.Core/Update/InstallUpdateService.cs index 262806e05..b0088ee87 100644 --- a/NzbDrone.Core/Update/InstallUpdateService.cs +++ b/NzbDrone.Core/Update/InstallUpdateService.cs @@ -3,6 +3,7 @@ using System.IO; using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Update.Commands; using NzbDrone.Core.Instrumentation; diff --git a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs index e536fffbd..dff7d1f19 100644 --- a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs +++ b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs @@ -1,7 +1,9 @@ using System; +using System.Linq; using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using NzbDrone.Core.Configuration; namespace NzbDrone.Host.AccessControl @@ -9,11 +11,15 @@ namespace NzbDrone.Host.AccessControl public interface IUrlAclAdapter { void RefreshRegistration(); + bool IsRegistered(); string UrlAcl { get; } + string LocalUrlAcl { get; } } public class UrlAclAdapter : IUrlAclAdapter { + private const string URL_ACL = "http://{0}:{1}/"; + private readonly IProcessProvider _processProvider; private readonly IConfigFileProvider _configFileProvider; private readonly Logger _logger; @@ -25,11 +31,29 @@ namespace NzbDrone.Host.AccessControl _logger = logger; } + public bool IsRegistered() + { + var arguments = String.Format("http show urlacl {0}", UrlAcl); + var output = RunNetsh(arguments); + + if (output == null || !output.Standard.Any()) return false; + + return output.Standard.Any(line => line.Contains(UrlAcl)); + } + public string UrlAcl { get { - return "http://*:" + _configFileProvider.Port + "/"; + return String.Format(URL_ACL, "*", _configFileProvider.Port); + } + } + + public string LocalUrlAcl + { + get + { + return String.Format(URL_ACL, "localhost", _configFileProvider.Port); } } @@ -47,17 +71,20 @@ namespace NzbDrone.Host.AccessControl RunNetsh(arguments); } - private void RunNetsh(string arguments) + private ProcessOutput RunNetsh(string arguments) { try { - var process = _processProvider.Start("netsh.exe", arguments); - process.WaitForExit(5000); + var output = _processProvider.StartAndCapture("netsh.exe", arguments); + + return output; } catch (Exception ex) { _logger.WarnException("Error executing netsh with arguments: " + arguments, ex); } + + return null; } } } \ No newline at end of file diff --git a/NzbDrone.Host/ApplicationServer.cs b/NzbDrone.Host/ApplicationServer.cs index d6e1e3625..ab0ee6f6e 100644 --- a/NzbDrone.Host/ApplicationServer.cs +++ b/NzbDrone.Host/ApplicationServer.cs @@ -3,6 +3,7 @@ using System.ServiceProcess; using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using NzbDrone.Core.Configuration; using NzbDrone.Host.Owin; diff --git a/NzbDrone.Host/Owin/OwinHostController.cs b/NzbDrone.Host/Owin/OwinHostController.cs index b4d8d24ba..14d5d331c 100644 --- a/NzbDrone.Host/Owin/OwinHostController.cs +++ b/NzbDrone.Host/Owin/OwinHostController.cs @@ -38,19 +38,14 @@ namespace NzbDrone.Host.Owin public void StartServer() { IgnoreCertErrorPolicy.Register(); + var urlAcl = DetermineUrlAcl(); - if (OsInfo.IsWindows && _runtimeInfo.IsAdmin) - { - _urlAclAdapter.RefreshRegistration(); - _firewallAdapter.MakeAccessible(); - } - - var options = new StartOptions(_urlAclAdapter.UrlAcl) + var options = new StartOptions(urlAcl) { ServerFactory = "Microsoft.Owin.Host.HttpListener" }; - _logger.Info("starting server on {0}", _urlAclAdapter.UrlAcl); + _logger.Info("starting server on {0}", urlAcl); try { @@ -100,5 +95,26 @@ namespace NzbDrone.Host.Owin _logger.Info("Host has stopped"); } + private string DetermineUrlAcl() + { + if (OsInfo.IsWindows && _runtimeInfo.IsAdmin) + { + if (_runtimeInfo.IsAdmin) + { + _urlAclAdapter.RefreshRegistration(); + _firewallAdapter.MakeAccessible(); + } + + else + { + if (!_urlAclAdapter.IsRegistered()) + { + return _urlAclAdapter.LocalUrlAcl; + } + } + } + + return _urlAclAdapter.UrlAcl; + } } } \ No newline at end of file diff --git a/NzbDrone.Host/PriorityMonitor.cs b/NzbDrone.Host/PriorityMonitor.cs index 28caf28ac..9d2533791 100644 --- a/NzbDrone.Host/PriorityMonitor.cs +++ b/NzbDrone.Host/PriorityMonitor.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Threading; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Processes; namespace NzbDrone.Host { diff --git a/NzbDrone.Integration.Test/NzbDroneRunner.cs b/NzbDrone.Integration.Test/NzbDroneRunner.cs index 5955166ff..b4bb8fa85 100644 --- a/NzbDrone.Integration.Test/NzbDroneRunner.cs +++ b/NzbDrone.Integration.Test/NzbDroneRunner.cs @@ -5,6 +5,7 @@ using System.Threading; using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using RestSharp; namespace NzbDrone.Integration.Test diff --git a/NzbDrone.Update.Test/ProgramFixture.cs b/NzbDrone.Update.Test/ProgramFixture.cs index 2a955e7f6..bace6d3ae 100644 --- a/NzbDrone.Update.Test/ProgramFixture.cs +++ b/NzbDrone.Update.Test/ProgramFixture.cs @@ -3,6 +3,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Common.Model; +using NzbDrone.Common.Processes; using NzbDrone.Test.Common; using NzbDrone.Update.UpdateEngine; diff --git a/NzbDrone.Update.Test/StartNzbDroneService.cs b/NzbDrone.Update.Test/StartNzbDroneService.cs index 4b1c0c4bb..3051e529f 100644 --- a/NzbDrone.Update.Test/StartNzbDroneService.cs +++ b/NzbDrone.Update.Test/StartNzbDroneService.cs @@ -3,6 +3,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using NzbDrone.Test.Common; using NzbDrone.Update.UpdateEngine; using IServiceProvider = NzbDrone.Common.IServiceProvider; diff --git a/NzbDrone.Update/UpdateApp.cs b/NzbDrone.Update/UpdateApp.cs index 3a882cd74..3e1c89442 100644 --- a/NzbDrone.Update/UpdateApp.cs +++ b/NzbDrone.Update/UpdateApp.cs @@ -5,6 +5,7 @@ using NzbDrone.Common; using NzbDrone.Common.Composition; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; +using NzbDrone.Common.Processes; using NzbDrone.Common.Security; using NzbDrone.Update.UpdateEngine; diff --git a/NzbDrone.Update/UpdateEngine/DetectApplicationType.cs b/NzbDrone.Update/UpdateEngine/DetectApplicationType.cs index 68a60b004..ec9a80a96 100644 --- a/NzbDrone.Update/UpdateEngine/DetectApplicationType.cs +++ b/NzbDrone.Update/UpdateEngine/DetectApplicationType.cs @@ -1,4 +1,5 @@ using NzbDrone.Common; +using NzbDrone.Common.Processes; namespace NzbDrone.Update.UpdateEngine { diff --git a/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs b/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs index e7ad7753c..211961a83 100644 --- a/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs +++ b/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs @@ -3,6 +3,7 @@ using System.IO; using NLog; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using IServiceProvider = NzbDrone.Common.IServiceProvider; namespace NzbDrone.Update.UpdateEngine diff --git a/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs b/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs index 80ae0e91b..329c9555e 100644 --- a/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs +++ b/NzbDrone.Update/UpdateEngine/TerminateNzbDrone.cs @@ -1,6 +1,7 @@ using System; using NLog; using NzbDrone.Common; +using NzbDrone.Common.Processes; using IServiceProvider = NzbDrone.Common.IServiceProvider; namespace NzbDrone.Update.UpdateEngine diff --git a/NzbDrone/SysTray/SysTrayApp.cs b/NzbDrone/SysTray/SysTrayApp.cs index 595a53f16..74270f09b 100644 --- a/NzbDrone/SysTray/SysTrayApp.cs +++ b/NzbDrone/SysTray/SysTrayApp.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Windows.Forms; using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; using NzbDrone.Host.Owin; namespace NzbDrone.SysTray From 32b287387d849b53582625d942464b56ad7f9a87 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 20 Sep 2013 19:50:57 -0700 Subject: [PATCH 43/49] Moved UrlAcl registration to adapter --- NzbDrone.Host/AccessControl/UrlAclAdapter.cs | 54 ++++++++++++-------- NzbDrone.Host/Owin/OwinHostController.cs | 37 +++++--------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs index dff7d1f19..0fbbd3611 100644 --- a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs +++ b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs @@ -10,10 +10,8 @@ namespace NzbDrone.Host.AccessControl { public interface IUrlAclAdapter { - void RefreshRegistration(); - bool IsRegistered(); + void ConfigureUrl(); string UrlAcl { get; } - string LocalUrlAcl { get; } } public class UrlAclAdapter : IUrlAclAdapter @@ -22,42 +20,43 @@ namespace NzbDrone.Host.AccessControl private readonly IProcessProvider _processProvider; private readonly IConfigFileProvider _configFileProvider; + private readonly IRuntimeInfo _runtimeInfo; private readonly Logger _logger; - public UrlAclAdapter(IProcessProvider processProvider, IConfigFileProvider configFileProvider, Logger logger) + public string UrlAcl { get; private set; } + private string _localUrl; + private string _wildcardUrl; + + public UrlAclAdapter(IProcessProvider processProvider, + IConfigFileProvider configFileProvider, + IRuntimeInfo runtimeInfo, + Logger logger) { _processProvider = processProvider; _configFileProvider = configFileProvider; + _runtimeInfo = runtimeInfo; _logger = logger; - } - - public bool IsRegistered() - { - var arguments = String.Format("http show urlacl {0}", UrlAcl); - var output = RunNetsh(arguments); - if (output == null || !output.Standard.Any()) return false; + _localUrl = String.Format(URL_ACL, "localhost", _configFileProvider.Port); + _wildcardUrl = String.Format(URL_ACL, "*", _configFileProvider.Port); - return output.Standard.Any(line => line.Contains(UrlAcl)); + UrlAcl = _wildcardUrl; } - public string UrlAcl + public void ConfigureUrl() { - get + if (!_runtimeInfo.IsAdmin && !IsRegistered) { - return String.Format(URL_ACL, "*", _configFileProvider.Port); + UrlAcl = _localUrl; } - } - public string LocalUrlAcl - { - get + if (_runtimeInfo.IsAdmin) { - return String.Format(URL_ACL, "localhost", _configFileProvider.Port); + RefreshRegistration(); } } - public void RefreshRegistration() + private void RefreshRegistration() { if (OsInfo.Version.Major < 6) return; @@ -65,6 +64,19 @@ namespace NzbDrone.Host.AccessControl RegisterUrl(); } + private bool IsRegistered + { + get + { + var arguments = String.Format("http show urlacl {0}", _wildcardUrl); + var output = RunNetsh(arguments); + + if (output == null || !output.Standard.Any()) return false; + + return output.Standard.Any(line => line.Contains(_wildcardUrl)); + } + } + private void RegisterUrl() { var arguments = String.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", UrlAcl); diff --git a/NzbDrone.Host/Owin/OwinHostController.cs b/NzbDrone.Host/Owin/OwinHostController.cs index 14d5d331c..52d2878e5 100644 --- a/NzbDrone.Host/Owin/OwinHostController.cs +++ b/NzbDrone.Host/Owin/OwinHostController.cs @@ -38,14 +38,23 @@ namespace NzbDrone.Host.Owin public void StartServer() { IgnoreCertErrorPolicy.Register(); - var urlAcl = DetermineUrlAcl(); - var options = new StartOptions(urlAcl) + if (OsInfo.IsWindows) + { + if (_runtimeInfo.IsAdmin) + { + _firewallAdapter.MakeAccessible(); + } + + _urlAclAdapter.ConfigureUrl(); + } + + var options = new StartOptions(_urlAclAdapter.UrlAcl) { ServerFactory = "Microsoft.Owin.Host.HttpListener" }; - _logger.Info("starting server on {0}", urlAcl); + _logger.Info("starting server on {0}", _urlAclAdapter.UrlAcl); try { @@ -94,27 +103,5 @@ namespace NzbDrone.Host.Owin _host = null; _logger.Info("Host has stopped"); } - - private string DetermineUrlAcl() - { - if (OsInfo.IsWindows && _runtimeInfo.IsAdmin) - { - if (_runtimeInfo.IsAdmin) - { - _urlAclAdapter.RefreshRegistration(); - _firewallAdapter.MakeAccessible(); - } - - else - { - if (!_urlAclAdapter.IsRegistered()) - { - return _urlAclAdapter.LocalUrlAcl; - } - } - } - - return _urlAclAdapter.UrlAcl; - } } } \ No newline at end of file From c5908c52a56d8b79cb8ea2961680f134c2c2503d Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 20 Sep 2013 22:49:54 -0700 Subject: [PATCH 44/49] Allow manual downloads --- NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index c03e22d27..32e7087c6 100644 --- a/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -70,11 +70,11 @@ namespace NzbDrone.Core.DecisionEngine if (remoteEpisode.Series != null) { + remoteEpisode.DownloadAllowed = true; decision = GetDecisionForReport(remoteEpisode, searchCriteria); } else { - remoteEpisode.DownloadAllowed = false; decision = new DownloadDecision(remoteEpisode, "Unknown Series"); } } From a5528f00cb449968ff888a7c8239688316708d91 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 21 Sep 2013 00:45:45 -0700 Subject: [PATCH 45/49] Fixed UI for downloadAllowed --- NzbDrone.Api/Indexers/ReleaseModule.cs | 1 + UI/Cells/cells.less | 4 ++++ UI/Release/DownloadReportCell.js | 9 +++++++++ 3 files changed, 14 insertions(+) diff --git a/NzbDrone.Api/Indexers/ReleaseModule.cs b/NzbDrone.Api/Indexers/ReleaseModule.cs index adba147de..2fd32e2ea 100644 --- a/NzbDrone.Api/Indexers/ReleaseModule.cs +++ b/NzbDrone.Api/Indexers/ReleaseModule.cs @@ -89,6 +89,7 @@ namespace NzbDrone.Api.Indexers release.InjectFrom(downloadDecision.RemoteEpisode.ParsedEpisodeInfo); release.InjectFrom(downloadDecision); release.Rejections = downloadDecision.Rejections.ToList(); + release.DownloadAllowed = downloadDecision.RemoteEpisode.DownloadAllowed; result.Add(release); } diff --git a/UI/Cells/cells.less b/UI/Cells/cells.less index 1e4ec5618..91e89f2b6 100644 --- a/UI/Cells/cells.less +++ b/UI/Cells/cells.less @@ -23,6 +23,10 @@ .download-report-cell { .clickable(); + + i { + .clickable(); + } } .toggle-cell{ diff --git a/UI/Release/DownloadReportCell.js b/UI/Release/DownloadReportCell.js index 49a666f8b..a9a1c7a3e 100644 --- a/UI/Release/DownloadReportCell.js +++ b/UI/Release/DownloadReportCell.js @@ -14,6 +14,11 @@ define( _onClick: function () { + if (!this.model.get('downloadAllowed')) + { + return; + } + var self = this; this.$el.html(''); @@ -36,6 +41,10 @@ define( this.$el.html(''); } + else { + this.className = 'no-download-report-cell'; + } + return this; } }); From 8ff34aac4d060855ccff73681699f0e861284701 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 21 Sep 2013 12:16:48 -0700 Subject: [PATCH 46/49] Fixed series actions wrapping to two lines --- UI/Cells/SeriesActionsCell.js | 37 +++++++++++++++++++ .../Index/Table => Cells}/SeriesStatusCell.js | 0 UI/Cells/cells.less | 4 ++ UI/Series/Index/SeriesIndexLayout.js | 10 ++--- .../Index/Table/ControlsColumnTemplate.html | 2 - UI/Series/Index/Table/Row.js | 22 ----------- .../Index/Table/SeriesTitleTemplate.html | 1 - 7 files changed, 45 insertions(+), 31 deletions(-) create mode 100644 UI/Cells/SeriesActionsCell.js rename UI/{Series/Index/Table => Cells}/SeriesStatusCell.js (100%) delete mode 100644 UI/Series/Index/Table/ControlsColumnTemplate.html delete mode 100644 UI/Series/Index/Table/Row.js delete mode 100644 UI/Series/Index/Table/SeriesTitleTemplate.html diff --git a/UI/Cells/SeriesActionsCell.js b/UI/Cells/SeriesActionsCell.js new file mode 100644 index 000000000..84e90c688 --- /dev/null +++ b/UI/Cells/SeriesActionsCell.js @@ -0,0 +1,37 @@ +'use strict'; + +define( + [ + 'app', + 'Cells/NzbDroneCell' + ], function (App, NzbDroneCell) { + return NzbDroneCell.extend({ + + className: 'series-actions-cell', + + events: { + 'click .x-edit-series' : '_editSeries', + 'click .x-remove-series': '_removeSeries' + }, + + render: function () { + this.$el.empty(); + + this.$el.html( + ' ' + + '' + ); + + this.delegateEvents(); + return this; + }, + + _editSeries: function () { + App.vent.trigger(App.Commands.EditSeriesCommand, {series:this.model}); + }, + + _removeSeries: function () { + App.vent.trigger(App.Commands.DeleteSeriesCommand, {series:this.model}); + } + }); + }); diff --git a/UI/Series/Index/Table/SeriesStatusCell.js b/UI/Cells/SeriesStatusCell.js similarity index 100% rename from UI/Series/Index/Table/SeriesStatusCell.js rename to UI/Cells/SeriesStatusCell.js diff --git a/UI/Cells/cells.less b/UI/Cells/cells.less index 91e89f2b6..38789980c 100644 --- a/UI/Cells/cells.less +++ b/UI/Cells/cells.less @@ -87,4 +87,8 @@ td.episode-status-cell, td.quality-cell { color: rgb(255, 255, 255); background-color: rgb(0, 129, 194); } +} + +.series-actions-cell { + width: 40px; } \ No newline at end of file diff --git a/UI/Series/Index/SeriesIndexLayout.js b/UI/Series/Index/SeriesIndexLayout.js index 8b7a48c12..704e7f725 100644 --- a/UI/Series/Index/SeriesIndexLayout.js +++ b/UI/Series/Index/SeriesIndexLayout.js @@ -11,9 +11,9 @@ define( 'Cells/TemplatedCell', 'Cells/QualityProfileCell', 'Cells/EpisodeProgressCell', + 'Cells/SeriesActionsCell', 'Shared/Grid/DateHeaderCell', - 'Series/Index/Table/SeriesStatusCell', - 'Series/Index/Table/Row', + 'Cells/SeriesStatusCell', 'Series/Index/FooterView', 'Series/Index/FooterModel', 'Shared/Toolbar/ToolbarLayout' @@ -27,9 +27,9 @@ define( TemplatedCell, QualityProfileCell, EpisodeProgressCell, + SeriesActionsCell, DateHeaderCell, SeriesStatusCell, - SeriesIndexRow, FooterView, FooterModel, ToolbarLayout) { @@ -86,8 +86,7 @@ define( name : 'this', label : '', sortable: false, - template: 'Series/Index/Table/ControlsColumnTemplate', - cell : TemplatedCell + cell : SeriesActionsCell } ], @@ -124,7 +123,6 @@ define( _showTable: function () { this.currentView = new Backgrid.Grid({ - row : SeriesIndexRow, collection: SeriesCollection, columns : this.columns, className : 'table table-hover' diff --git a/UI/Series/Index/Table/ControlsColumnTemplate.html b/UI/Series/Index/Table/ControlsColumnTemplate.html deleted file mode 100644 index 042f6897e..000000000 --- a/UI/Series/Index/Table/ControlsColumnTemplate.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/UI/Series/Index/Table/Row.js b/UI/Series/Index/Table/Row.js deleted file mode 100644 index 4e17c21c1..000000000 --- a/UI/Series/Index/Table/Row.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; -define( - [ - 'app', - 'backgrid' - ], function (App, Backgrid) { - return Backgrid.Row.extend({ - events: { - 'click .x-edit' : 'editSeries', - 'click .x-remove': 'removeSeries' - }, - - editSeries: function () { - App.vent.trigger(App.Commands.EditSeriesCommand, {series:this.model}); - }, - - removeSeries: function () { - App.vent.trigger(App.Commands.DeleteSeriesCommand, {series:this.model}); - }, - }); - }); - diff --git a/UI/Series/Index/Table/SeriesTitleTemplate.html b/UI/Series/Index/Table/SeriesTitleTemplate.html deleted file mode 100644 index b4786d5b1..000000000 --- a/UI/Series/Index/Table/SeriesTitleTemplate.html +++ /dev/null @@ -1 +0,0 @@ -{{title}} From 48dbc127a7dfeacc1e2aee8ed58cbb8f972bc09f Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 22 Sep 2013 00:55:44 -0700 Subject: [PATCH 47/49] Added search bar under nav bar to jump to series by searching --- UI/Content/menu.less | 72 +++++++++++++++--------- UI/Mixins/AutoComplete.js | 1 - UI/Navbar/NavbarTemplate.html | 11 ++++ UI/Navbar/NavbarView.js | 13 ++++- UI/Navbar/Search.js | 29 ++++++++++ UI/Series/Details/SeriesDetailsLayout.js | 5 +- 6 files changed, 98 insertions(+), 33 deletions(-) create mode 100644 UI/Navbar/Search.js diff --git a/UI/Content/menu.less b/UI/Content/menu.less index 91f2d9a3b..ce423051a 100644 --- a/UI/Content/menu.less +++ b/UI/Content/menu.less @@ -1,6 +1,9 @@ @import "prefixer"; #main-menu-region { + text-align : center; + margin-bottom : 10px; + i:before { font-size : 35px; } @@ -8,27 +11,6 @@ i { width : 40px; } -} - -.backdrop #nav-region { - background-color : #000000; - .opacity(0.85); -} - -#nav-region li a:hover, #in-sub-nav li a.active { - background-color : #555555; - text-decoration : none; -} - -#nav-region { - - margin-bottom : 80px; - height : 120px; - - .span12 { - margin-left : 0px; - } - .logo { margin-top : 25px; @@ -37,10 +19,6 @@ width : 70px; } - ul { - text-align : center; - margin-bottom : 10px; - } li { list-style-type : none; display : inline-block; @@ -55,7 +33,7 @@ padding : 15px 10px 5px; min-height : 56px; min-width : 64px; - margin : 20px 10px 10px; + margin : 20px 10px 5px; color : #b9b9b9; font-weight : 100; } @@ -66,3 +44,45 @@ } } } + +.backdrop #nav-region { + background-color : #000000; + .opacity(0.85); +} + +#nav-region li a:hover, #in-sub-nav li a.active { + background-color : #555555; + text-decoration : none; +} + +#nav-region { + margin-bottom : 80px; + height : 150px; + + .span12 { + margin-left : 0px; + } +} + +.search { + text-align: center; + + input, .add-on { + background-color: #333333; + border-color: #333333; + color: #cccccc; + } + + ul { + text-align: left; + } + + .dropdown-menu { + background-color: #333333; + color: #cccccc; + + > li > a { + color: #cccccc; + } + } +} \ No newline at end of file diff --git a/UI/Mixins/AutoComplete.js b/UI/Mixins/AutoComplete.js index 90856cb20..0bc512be2 100644 --- a/UI/Mixins/AutoComplete.js +++ b/UI/Mixins/AutoComplete.js @@ -19,5 +19,4 @@ define(function () { items : 20 }); }; - }); diff --git a/UI/Navbar/NavbarTemplate.html b/UI/Navbar/NavbarTemplate.html index c2ee2fab4..d9b1c83e6 100644 --- a/UI/Navbar/NavbarTemplate.html +++ b/UI/Navbar/NavbarTemplate.html @@ -59,4 +59,15 @@ + +
+
+ +
+
diff --git a/UI/Navbar/NavbarView.js b/UI/Navbar/NavbarView.js index b78f8f678..85ca28cb6 100644 --- a/UI/Navbar/NavbarView.js +++ b/UI/Navbar/NavbarView.js @@ -1,14 +1,23 @@ 'use strict'; define( [ - 'marionette' + 'marionette', + 'Navbar/Search' ], function (Marionette) { return Marionette.ItemView.extend({ + template : 'Navbar/NavbarTemplate', + + ui: { + search: '.x-series-search' + }, + events: { 'click a': 'onClick' }, - template : 'Navbar/NavbarTemplate', + onRender: function (){ + this.ui.search.bindSearch(); + }, onClick: function (event) { diff --git a/UI/Navbar/Search.js b/UI/Navbar/Search.js new file mode 100644 index 000000000..35e0d733b --- /dev/null +++ b/UI/Navbar/Search.js @@ -0,0 +1,29 @@ +'use strict'; +define( + [ + 'app', + 'Series/SeriesCollection' + ], function (App, SeriesCollection) { + $.fn.bindSearch = function () { + $(this).typeahead({ + source : function () { + return SeriesCollection.map(function (model) { + return model.get('title'); + }); + }, + + sorter: function (items) { + return items.sort(); + }, + + updater: function (item) { + var series = SeriesCollection.find(function (model) { + return model.get('title') === item; + }); + + this.$element.blur(); + App.Router.navigate('/series/{0}'.format(series.get('titleSlug')), { trigger: true }); + } + }); + }; + }); diff --git a/UI/Series/Details/SeriesDetailsLayout.js b/UI/Series/Details/SeriesDetailsLayout.js index ec6fc38c9..238ffb00c 100644 --- a/UI/Series/Details/SeriesDetailsLayout.js +++ b/UI/Series/Details/SeriesDetailsLayout.js @@ -41,14 +41,13 @@ define( }, initialize: function () { - $('body').addClass('backdrop'); - this.listenTo(this.model, 'change:monitored', this._setMonitoredState); this.listenTo(App.vent, App.Events.SeriesDeleted, this._onSeriesDeleted); this.listenTo(App.vent, App.Events.SeasonRenamed, this._onSeasonRenamed); }, onShow: function () { + $('body').addClass('backdrop'); var fanArt = this._getFanArt(); if (fanArt) { @@ -61,8 +60,6 @@ define( this._showSeasons(); this._setMonitoredState(); this._showInfo(); - - }, onRender: function () { From 2b85b7f4668b2f45b35411de0b5d0d462ee20a04 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 22 Sep 2013 12:57:03 -0700 Subject: [PATCH 48/49] Added ssl support (config file only) --- .../Configuration/ConfigFileProvider.cs | 18 +++++ NzbDrone.Host/AccessControl/NetshProvider.cs | 39 ++++++++++ NzbDrone.Host/AccessControl/SslAdapter.cs | 55 ++++++++++++++ NzbDrone.Host/AccessControl/UrlAclAdapter.cs | 73 ++++++++----------- NzbDrone.Host/NzbDrone.Host.csproj | 2 + NzbDrone.Host/Owin/OwinHostController.cs | 22 +++++- 6 files changed, 162 insertions(+), 47 deletions(-) create mode 100644 NzbDrone.Host/AccessControl/NetshProvider.cs create mode 100644 NzbDrone.Host/AccessControl/SslAdapter.cs diff --git a/NzbDrone.Core/Configuration/ConfigFileProvider.cs b/NzbDrone.Core/Configuration/ConfigFileProvider.cs index a25e85b7c..4a1e93518 100644 --- a/NzbDrone.Core/Configuration/ConfigFileProvider.cs +++ b/NzbDrone.Core/Configuration/ConfigFileProvider.cs @@ -20,6 +20,8 @@ namespace NzbDrone.Core.Configuration void SaveConfigDictionary(Dictionary configValues); int Port { get; } + int SslPort { get; } + bool EnableSsl { get; } bool LaunchBrowser { get; } bool AuthenticationEnabled { get; } string Username { get; } @@ -27,6 +29,7 @@ namespace NzbDrone.Core.Configuration string LogLevel { get; } string Branch { get; } bool Torrent { get; } + string SslCertHash { get; } } public class ConfigFileProvider : IConfigFileProvider @@ -90,6 +93,16 @@ namespace NzbDrone.Core.Configuration get { return GetValueInt("Port", 8989); } } + public int SslPort + { + get { return GetValueInt("SslPort", 9898); } + } + + public bool EnableSsl + { + get { return GetValueBoolean("EnableSsl", false); } + } + public bool LaunchBrowser { get { return GetValueBoolean("LaunchBrowser", true); } @@ -125,6 +138,11 @@ namespace NzbDrone.Core.Configuration get { return GetValue("LogLevel", "Info"); } } + public string SslCertHash + { + get { return GetValue("SslCertHash", ""); } + } + public int GetValueInt(string key, int defaultValue) { return Convert.ToInt32(GetValue(key, defaultValue)); diff --git a/NzbDrone.Host/AccessControl/NetshProvider.cs b/NzbDrone.Host/AccessControl/NetshProvider.cs new file mode 100644 index 000000000..dc9e8b754 --- /dev/null +++ b/NzbDrone.Host/AccessControl/NetshProvider.cs @@ -0,0 +1,39 @@ +using System; +using NLog; +using NzbDrone.Common.Processes; + +namespace NzbDrone.Host.AccessControl +{ + public interface INetshProvider + { + ProcessOutput Run(string arguments); + } + + public class NetshProvider : INetshProvider + { + private readonly IProcessProvider _processProvider; + private readonly Logger _logger; + + public NetshProvider(IProcessProvider processProvider, Logger logger) + { + _processProvider = processProvider; + _logger = logger; + } + + public ProcessOutput Run(string arguments) + { + try + { + var output = _processProvider.StartAndCapture("netsh.exe", arguments); + + return output; + } + catch (Exception ex) + { + _logger.WarnException("Error executing netsh with arguments: " + arguments, ex); + } + + return null; + } + } +} diff --git a/NzbDrone.Host/AccessControl/SslAdapter.cs b/NzbDrone.Host/AccessControl/SslAdapter.cs new file mode 100644 index 000000000..c94e307a3 --- /dev/null +++ b/NzbDrone.Host/AccessControl/SslAdapter.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using NLog; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Host.AccessControl +{ + public interface ISslAdapter + { + void Register(); + } + + public class SslAdapter : ISslAdapter + { + private const string APP_ID = "C2172AF4-F9A6-4D91-BAEE-C2E4EE680613"; + + private readonly INetshProvider _netshProvider; + private readonly IConfigFileProvider _configFileProvider; + private readonly Logger _logger; + + public SslAdapter(INetshProvider netshProvider, IConfigFileProvider configFileProvider, Logger logger) + { + _netshProvider = netshProvider; + _configFileProvider = configFileProvider; + _logger = logger; + } + + public void Register() + { + if (!_configFileProvider.EnableSsl) return; + if (IsRegistered()) return; + + if (String.IsNullOrWhiteSpace(_configFileProvider.SslCertHash)) + { + _logger.Warn("Unable to enable SSL, SSL Cert Hash is required"); + return; + } + + var arguments = String.Format("netsh http add sslcert ipport=0.0.0.0:{0} certhash={1} appid={{{2}}", _configFileProvider.SslPort, _configFileProvider.SslCertHash, APP_ID); + _netshProvider.Run(arguments); + } + + private bool IsRegistered() + { + var ipPort = "0.0.0.0:" + _configFileProvider.SslPort; + var arguments = String.Format("http show sslcert ipport={0}", ipPort); + + var output = _netshProvider.Run(arguments); + + if (output == null || !output.Standard.Any()) return false; + + return output.Standard.Any(line => line.Contains(ipPort)); + } + } +} diff --git a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs index 0fbbd3611..cca29eb2b 100644 --- a/NzbDrone.Host/AccessControl/UrlAclAdapter.cs +++ b/NzbDrone.Host/AccessControl/UrlAclAdapter.cs @@ -1,9 +1,7 @@ using System; using System.Linq; using NLog; -using NzbDrone.Common; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Processes; using NzbDrone.Core.Configuration; namespace NzbDrone.Host.AccessControl @@ -11,43 +9,50 @@ namespace NzbDrone.Host.AccessControl public interface IUrlAclAdapter { void ConfigureUrl(); - string UrlAcl { get; } + string Url { get; } + string HttpsUrl { get; } } public class UrlAclAdapter : IUrlAclAdapter { - private const string URL_ACL = "http://{0}:{1}/"; - - private readonly IProcessProvider _processProvider; + private readonly INetshProvider _netshProvider; private readonly IConfigFileProvider _configFileProvider; private readonly IRuntimeInfo _runtimeInfo; private readonly Logger _logger; - public string UrlAcl { get; private set; } + public string Url { get; private set; } + public string HttpsUrl { get; private set; } + private string _localUrl; private string _wildcardUrl; + private string _localHttpsUrl; + private string _wildcardHttpsUrl; - public UrlAclAdapter(IProcessProvider processProvider, + public UrlAclAdapter(INetshProvider netshProvider, IConfigFileProvider configFileProvider, IRuntimeInfo runtimeInfo, Logger logger) { - _processProvider = processProvider; + _netshProvider = netshProvider; _configFileProvider = configFileProvider; _runtimeInfo = runtimeInfo; _logger = logger; - _localUrl = String.Format(URL_ACL, "localhost", _configFileProvider.Port); - _wildcardUrl = String.Format(URL_ACL, "*", _configFileProvider.Port); + _localUrl = String.Format("http://localhost:{0}/", _configFileProvider.Port); + _wildcardUrl = String.Format("http://*:{0}/", _configFileProvider.Port); + _localHttpsUrl = String.Format("https://localhost:{0}/", _configFileProvider.SslPort); + _wildcardHttpsUrl = String.Format("https://*:{0}/", _configFileProvider.SslPort); - UrlAcl = _wildcardUrl; + Url = _wildcardUrl; + HttpsUrl = _wildcardHttpsUrl; } public void ConfigureUrl() { - if (!_runtimeInfo.IsAdmin && !IsRegistered) + if (!_runtimeInfo.IsAdmin) { - UrlAcl = _localUrl; + if (!IsRegistered(_wildcardUrl)) Url = _localUrl; + if (!IsRegistered(_wildcardHttpsUrl)) HttpsUrl = _localHttpsUrl; } if (_runtimeInfo.IsAdmin) @@ -61,42 +66,24 @@ namespace NzbDrone.Host.AccessControl if (OsInfo.Version.Major < 6) return; - RegisterUrl(); + RegisterUrl(Url); + RegisterUrl(HttpsUrl); } - - private bool IsRegistered + + private bool IsRegistered(string urlAcl) { - get - { - var arguments = String.Format("http show urlacl {0}", _wildcardUrl); - var output = RunNetsh(arguments); + var arguments = String.Format("http show urlacl {0}", urlAcl); + var output = _netshProvider.Run(arguments); - if (output == null || !output.Standard.Any()) return false; - - return output.Standard.Any(line => line.Contains(_wildcardUrl)); - } - } + if (output == null || !output.Standard.Any()) return false; - private void RegisterUrl() - { - var arguments = String.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", UrlAcl); - RunNetsh(arguments); + return output.Standard.Any(line => line.Contains(urlAcl)); } - private ProcessOutput RunNetsh(string arguments) + private void RegisterUrl(string urlAcl) { - try - { - var output = _processProvider.StartAndCapture("netsh.exe", arguments); - - return output; - } - catch (Exception ex) - { - _logger.WarnException("Error executing netsh with arguments: " + arguments, ex); - } - - return null; + var arguments = String.Format("http add urlacl {0} sddl=D:(A;;GX;;;S-1-1-0)", urlAcl); + _netshProvider.Run(arguments); } } } \ No newline at end of file diff --git a/NzbDrone.Host/NzbDrone.Host.csproj b/NzbDrone.Host/NzbDrone.Host.csproj index 71a3643d6..ff8922c0c 100644 --- a/NzbDrone.Host/NzbDrone.Host.csproj +++ b/NzbDrone.Host/NzbDrone.Host.csproj @@ -117,6 +117,8 @@ Properties\SharedAssemblyInfo.cs + + Component diff --git a/NzbDrone.Host/Owin/OwinHostController.cs b/NzbDrone.Host/Owin/OwinHostController.cs index 52d2878e5..5af5a96c5 100644 --- a/NzbDrone.Host/Owin/OwinHostController.cs +++ b/NzbDrone.Host/Owin/OwinHostController.cs @@ -21,17 +21,24 @@ namespace NzbDrone.Host.Owin private readonly IRuntimeInfo _runtimeInfo; private readonly IUrlAclAdapter _urlAclAdapter; private readonly IFirewallAdapter _firewallAdapter; + private readonly ISslAdapter _sslAdapter; private readonly Logger _logger; private IDisposable _host; - public OwinHostController(IConfigFileProvider configFileProvider, IEnumerable owinMiddleWares, - IRuntimeInfo runtimeInfo, IUrlAclAdapter urlAclAdapter, IFirewallAdapter firewallAdapter, Logger logger) + public OwinHostController(IConfigFileProvider configFileProvider, + IEnumerable owinMiddleWares, + IRuntimeInfo runtimeInfo, + IUrlAclAdapter urlAclAdapter, + IFirewallAdapter firewallAdapter, + ISslAdapter sslAdapter, + Logger logger) { _configFileProvider = configFileProvider; _owinMiddleWares = owinMiddleWares; _runtimeInfo = runtimeInfo; _urlAclAdapter = urlAclAdapter; _firewallAdapter = firewallAdapter; + _sslAdapter = sslAdapter; _logger = logger; } @@ -44,17 +51,24 @@ namespace NzbDrone.Host.Owin if (_runtimeInfo.IsAdmin) { _firewallAdapter.MakeAccessible(); + _sslAdapter.Register(); } _urlAclAdapter.ConfigureUrl(); } - var options = new StartOptions(_urlAclAdapter.UrlAcl) + var options = new StartOptions(_urlAclAdapter.Url) { ServerFactory = "Microsoft.Owin.Host.HttpListener" }; - _logger.Info("starting server on {0}", _urlAclAdapter.UrlAcl); + if (_configFileProvider.EnableSsl) + { + _logger.Trace("SSL enabled, listening on: {0}", _urlAclAdapter.HttpsUrl); + options.Urls.Add(_urlAclAdapter.HttpsUrl); + } + + _logger.Info("starting server on {0}", _urlAclAdapter.Url); try { From 503be832c4f5f98bab768f904747dcd5c758881c Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Fri, 20 Sep 2013 15:03:16 -0700 Subject: [PATCH 49/49] updated jshint options --- UI/.idea/inspectionProfiles/Project_Default.xml | 1 + UI/.idea/jsLinters/jshint.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/UI/.idea/inspectionProfiles/Project_Default.xml b/UI/.idea/inspectionProfiles/Project_Default.xml index 1e9bbe387..195e8ea0b 100644 --- a/UI/.idea/inspectionProfiles/Project_Default.xml +++ b/UI/.idea/inspectionProfiles/Project_Default.xml @@ -68,6 +68,7 @@