From 198ff059c4287f24af01860e573691b73b690dd1 Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Sun, 30 Mar 2014 13:50:17 +0200 Subject: [PATCH 01/31] Fixed: Season Search now correctly uses scene numbering. --- .../DownloadDecisionMakerFixture.cs | 43 ++++- .../NzbSearchServiceFixture.cs | 170 ++++++++++++++++++ .../NzbDrone.Core.Test.csproj | 1 + .../DecisionEngine/DownloadDecisionMaker.cs | 11 ++ .../IndexerSearch/NzbSearchService.cs | 39 +++- 5 files changed, 260 insertions(+), 4 deletions(-) create mode 100644 src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs index 23b662982..2d501ca2a 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs @@ -10,6 +10,7 @@ using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Tv; using NzbDrone.Test.Common; +using FizzWare.NBuilder; namespace NzbDrone.Core.Test.DecisionEngineTests { @@ -141,7 +142,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests results.Should().BeEmpty(); } - [Test] public void should_not_attempt_to_map_episode_series_title_is_blank() + [Test] + public void should_not_attempt_to_map_episode_series_title_is_blank() { GivenSpecifications(_pass1, _pass2, _pass3); _reports[0].Title = "1937 - Snow White and the Seven Dwarves"; @@ -204,5 +206,44 @@ namespace NzbDrone.Core.Test.DecisionEngineTests result.Should().HaveCount(1); } + + [Test] + public void should_only_include_reports_for_requested_episodes() + { + var series = Builder.CreateNew().Build(); + + var episodes = Builder.CreateListOfSize(2) + .All() + .With(v => v.SeriesId, series.Id) + .With(v => v.Series, series) + .With(v => v.SeasonNumber, 1) + .With(v => v.SceneSeasonNumber, 2) + .BuildList(); + + var criteria = new SeasonSearchCriteria { Episodes = episodes.Take(1).ToList(), SeasonNumber = 1 }; + + var reports = episodes.Select(v => + new ReleaseInfo() + { + Title = string.Format("{0}.S{1:00}E{2:00}.720p.WEB-DL-DRONE", series.Title, v.SceneSeasonNumber, v.SceneEpisodeNumber) + }).ToList(); + + Mocker.GetMock() + .Setup(v => v.Map(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((p,id,c) => + new RemoteEpisode + { + DownloadAllowed = true, + ParsedEpisodeInfo = p, + Series = series, + Episodes = episodes.Where(v => v.SceneEpisodeNumber == p.EpisodeNumbers.First()).ToList() + }); + + Mocker.SetConstant>(new List()); + + var decisions = Subject.GetSearchDecision(reports, criteria); + + Assert.AreEqual(1, decisions.Count); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs b/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs new file mode 100644 index 000000000..2ccfa51ea --- /dev/null +++ b/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs @@ -0,0 +1,170 @@ +using NzbDrone.Core.IndexerSearch; +using NzbDrone.Core.Test.Framework; +using FizzWare.NBuilder; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.IndexerSearch.Definitions; + +namespace NzbDrone.Core.Test.IndexerSearchTests +{ + public class NzbSearchServiceFixture : CoreTest + { + private Series _xemSeries; + private List _xemEpisodes; + + [SetUp] + public void SetUp() + { + var indexer = Mocker.GetMock(); + indexer.SetupGet(s => s.SupportsSearching).Returns(true); + + Mocker.GetMock() + .Setup(s => s.GetAvailableProviders()) + .Returns(new List{indexer.Object}); + + Mocker.GetMock() + .Setup(s => s.GetSearchDecision(It.IsAny>(), It.IsAny())) + .Returns(new List()); + + _xemSeries = Builder.CreateNew() + .With(v => v.UseSceneNumbering = true) + .Build(); + + _xemEpisodes = new List(); + + Mocker.GetMock() + .Setup(v => v.GetSeries(_xemSeries.Id)) + .Returns(_xemSeries); + + Mocker.GetMock() + .Setup(v => v.GetEpisodesBySeason(_xemSeries.Id, It.IsAny())) + .Returns((i, j) => _xemEpisodes.Where(d => d.SeasonNumber == j).ToList()); + } + + private void WithEpisode(int seasonNumber, int episodeNumber, int sceneSeasonNumber, int sceneEpisodeNumber) + { + var episode = Builder.CreateNew() + .With(v => v.SeriesId == _xemSeries.Id) + .With(v => v.Series == _xemSeries) + .With(v => v.SeasonNumber, seasonNumber) + .With(v => v.EpisodeNumber, episodeNumber) + .With(v => v.SceneSeasonNumber, sceneSeasonNumber) + .With(v => v.SceneEpisodeNumber, sceneEpisodeNumber) + .Build(); + + _xemEpisodes.Add(episode); + } + + private void WithEpisodes() + { + // Season 1 maps to Scene Season 2 (one-to-one) + WithEpisode(1, 12, 2, 3); + WithEpisode(1, 13, 2, 4); + + // Season 2 maps to Scene Season 3 & 4 (one-to-one) + WithEpisode(2, 1, 3, 11); + WithEpisode(2, 2, 3, 12); + WithEpisode(2, 3, 4, 11); + WithEpisode(2, 4, 4, 12); + + // Season 3 maps to Scene Season 5 (partial) + // Season 4 maps to Scene Season 5 & 6 (partial) + WithEpisode(3, 1, 5, 11); + WithEpisode(3, 2, 5, 12); + WithEpisode(4, 1, 5, 13); + WithEpisode(4, 2, 5, 14); + WithEpisode(4, 3, 6, 11); + WithEpisode(5, 1, 6, 12); + } + + private List WatchForSearchCriteria() + { + List result = new List(); + + Mocker.GetMock() + .Setup(v => v.Fetch(It.IsAny(), It.IsAny())) + .Callback((i, s) => result.Add(s)) + .Returns(new List()); + + Mocker.GetMock() + .Setup(v => v.Fetch(It.IsAny(), It.IsAny())) + .Callback((i,s) => result.Add(s)) + .Returns(new List()); + + return result; + } + + [Test] + public void scene_episodesearch() + { + WithEpisodes(); + + var allCriteria = WatchForSearchCriteria(); + + Subject.EpisodeSearch(_xemEpisodes.First()); + + var criteria = allCriteria.OfType().ToList(); + + Assert.AreEqual(1, criteria.Count); + Assert.AreEqual(2, criteria[0].SeasonNumber); + Assert.AreEqual(3, criteria[0].EpisodeNumber); + } + + [Test] + public void scene_seasonsearch() + { + WithEpisodes(); + + var allCriteria = WatchForSearchCriteria(); + + Subject.SeasonSearch(_xemSeries.Id, 1); + + var criteria = allCriteria.OfType().ToList(); + + Assert.AreEqual(1, criteria.Count); + Assert.AreEqual(2, criteria[0].SeasonNumber); + } + + [Test] + public void scene_seasonsearch_should_search_multiple_seasons() + { + WithEpisodes(); + + var allCriteria = WatchForSearchCriteria(); + + Subject.SeasonSearch(_xemSeries.Id, 2); + + var criteria = allCriteria.OfType().ToList(); + + Assert.AreEqual(2, criteria.Count); + Assert.AreEqual(3, criteria[0].SeasonNumber); + Assert.AreEqual(4, criteria[1].SeasonNumber); + } + + [Test] + public void scene_seasonsearch_should_search_single_episode_if_possible() + { + WithEpisodes(); + + var allCriteria = WatchForSearchCriteria(); + + Subject.SeasonSearch(_xemSeries.Id, 4); + + var criteria1 = allCriteria.OfType().ToList(); + var criteria2 = allCriteria.OfType().ToList(); + + Assert.AreEqual(1, criteria1.Count); + Assert.AreEqual(5, criteria1[0].SeasonNumber); + + Assert.AreEqual(1, criteria2.Count); + Assert.AreEqual(6, criteria2[0].SeasonNumber); + Assert.AreEqual(11, criteria2[0].EpisodeNumber); + } + } +} diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index f140c68b8..70086788a 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -138,6 +138,7 @@ + diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index cfed22a5c..b5b13d9a7 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -97,6 +97,17 @@ namespace NzbDrone.Core.DecisionEngine if (decision != null) { + if (searchCriteria != null) + { + var criteriaEpisodes = searchCriteria.Episodes.Select(v => v.Id).ToList(); + var remoteEpisodes = decision.RemoteEpisode.Episodes.Select(v => v.Id).ToList(); + if (!criteriaEpisodes.Intersect(remoteEpisodes).Any()) + { + _logger.Debug("Release rejected since the episode wasn't requested: {0}", decision.RemoteEpisode.ParsedEpisodeInfo); + continue; + } + } + if (decision.Rejections.Any()) { _logger.Debug("Release rejected for the following reasons: {0}", String.Join(", ", decision.Rejections)); diff --git a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index b2b3ddb1d..a5b140beb 100644 --- a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -138,10 +138,43 @@ namespace NzbDrone.Core.IndexerSearch return SearchSpecial(series, episodes); } - var searchSpec = Get(series, episodes); - searchSpec.SeasonNumber = seasonNumber; + List downloadDecisions = new List(); - return Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); + if (series.UseSceneNumbering) + { + var sceneSeasonGroups = episodes.GroupBy(v => v.SceneSeasonNumber).Distinct(); + + foreach (var sceneSeasonEpisodes in sceneSeasonGroups) + { + if (sceneSeasonEpisodes.Count() == 1) + { + var searchSpec = Get(series, sceneSeasonEpisodes.ToList()); + searchSpec.SeasonNumber = sceneSeasonEpisodes.Key; + searchSpec.EpisodeNumber = sceneSeasonEpisodes.First().SceneEpisodeNumber; + + var decisions = Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); + downloadDecisions.AddRange(decisions); + } + else + { + var searchSpec = Get(series, sceneSeasonEpisodes.ToList()); + searchSpec.SeasonNumber = sceneSeasonEpisodes.Key; + + var decisions = Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); + downloadDecisions.AddRange(decisions); + } + } + } + else + { + var searchSpec = Get(series, episodes); + searchSpec.SeasonNumber = seasonNumber; + + var decisions = Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); + downloadDecisions.AddRange(decisions); + } + + return downloadDecisions; } private TSpec Get(Series series, List episodes) where TSpec : SearchCriteriaBase, new() From 38b0fae29a676084bad1ff59cd9ec440dadddd9b Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Tue, 1 Apr 2014 21:39:17 +0200 Subject: [PATCH 02/31] Moved Episode Not Requested check to new Specification. Updated tests. --- .../DownloadDecisionMakerFixture.cs | 9 +++- .../NzbSearchServiceFixture.cs | 54 +++++++++++++------ .../DecisionEngine/DownloadDecisionMaker.cs | 11 ---- .../Search/EpisodeRequestedSpecification.cs | 43 +++++++++++++++ .../IndexerSearch/NzbSearchService.cs | 14 ++++- src/NzbDrone.Core/NzbDrone.Core.csproj | 1 + 6 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs index 2d501ca2a..90b251ace 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/DownloadDecisionMakerFixture.cs @@ -239,11 +239,16 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Episodes = episodes.Where(v => v.SceneEpisodeNumber == p.EpisodeNumbers.First()).ToList() }); - Mocker.SetConstant>(new List()); + Mocker.SetConstant>(new List + { + Mocker.Resolve() + }); var decisions = Subject.GetSearchDecision(reports, criteria); - Assert.AreEqual(1, decisions.Count); + var approvedDecisions = decisions.Where(v => v.Approved).ToList(); + + approvedDecisions.Count.Should().Be(1); } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs b/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs index 2ccfa51ea..d6780b56f 100644 --- a/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; +using FluentAssertions; using Moq; using NUnit.Framework; using NzbDrone.Core.Tv; @@ -26,12 +27,12 @@ namespace NzbDrone.Core.Test.IndexerSearchTests Mocker.GetMock() .Setup(s => s.GetAvailableProviders()) - .Returns(new List{indexer.Object}); + .Returns(new List { indexer.Object }); Mocker.GetMock() .Setup(s => s.GetSearchDecision(It.IsAny>(), It.IsAny())) .Returns(new List()); - + _xemSeries = Builder.CreateNew() .With(v => v.UseSceneNumbering = true) .Build(); @@ -81,12 +82,16 @@ namespace NzbDrone.Core.Test.IndexerSearchTests WithEpisode(4, 2, 5, 14); WithEpisode(4, 3, 6, 11); WithEpisode(5, 1, 6, 12); + + // Season 7+ maps normally, so no mapping specified. + WithEpisode(7, 1, 0, 0); + WithEpisode(7, 2, 0, 0); } private List WatchForSearchCriteria() { List result = new List(); - + Mocker.GetMock() .Setup(v => v.Fetch(It.IsAny(), It.IsAny())) .Callback((i, s) => result.Add(s)) @@ -94,7 +99,7 @@ namespace NzbDrone.Core.Test.IndexerSearchTests Mocker.GetMock() .Setup(v => v.Fetch(It.IsAny(), It.IsAny())) - .Callback((i,s) => result.Add(s)) + .Callback((i, s) => result.Add(s)) .Returns(new List()); return result; @@ -111,9 +116,9 @@ namespace NzbDrone.Core.Test.IndexerSearchTests var criteria = allCriteria.OfType().ToList(); - Assert.AreEqual(1, criteria.Count); - Assert.AreEqual(2, criteria[0].SeasonNumber); - Assert.AreEqual(3, criteria[0].EpisodeNumber); + criteria.Count.Should().Be(1); + criteria[0].SeasonNumber.Should().Be(2); + criteria[0].EpisodeNumber.Should().Be(3); } [Test] @@ -127,8 +132,8 @@ namespace NzbDrone.Core.Test.IndexerSearchTests var criteria = allCriteria.OfType().ToList(); - Assert.AreEqual(1, criteria.Count); - Assert.AreEqual(2, criteria[0].SeasonNumber); + criteria.Count.Should().Be(1); + criteria[0].SeasonNumber.Should().Be(2); } [Test] @@ -142,9 +147,9 @@ namespace NzbDrone.Core.Test.IndexerSearchTests var criteria = allCriteria.OfType().ToList(); - Assert.AreEqual(2, criteria.Count); - Assert.AreEqual(3, criteria[0].SeasonNumber); - Assert.AreEqual(4, criteria[1].SeasonNumber); + criteria.Count.Should().Be(2); + criteria[0].SeasonNumber.Should().Be(3); + criteria[1].SeasonNumber.Should().Be(4); } [Test] @@ -159,12 +164,27 @@ namespace NzbDrone.Core.Test.IndexerSearchTests var criteria1 = allCriteria.OfType().ToList(); var criteria2 = allCriteria.OfType().ToList(); - Assert.AreEqual(1, criteria1.Count); - Assert.AreEqual(5, criteria1[0].SeasonNumber); + criteria1.Count.Should().Be(1); + criteria1[0].SeasonNumber.Should().Be(5); - Assert.AreEqual(1, criteria2.Count); - Assert.AreEqual(6, criteria2[0].SeasonNumber); - Assert.AreEqual(11, criteria2[0].EpisodeNumber); + criteria2.Count.Should().Be(1); + criteria2[0].SeasonNumber.Should().Be(6); + criteria2[0].EpisodeNumber.Should().Be(11); + } + + [Test] + public void scene_seasonsearch_should_use_seasonnumber_if_no_scene_number_is_available() + { + WithEpisodes(); + + var allCriteria = WatchForSearchCriteria(); + + Subject.SeasonSearch(_xemSeries.Id, 7); + + var criteria = allCriteria.OfType().ToList(); + + criteria.Count.Should().Be(1); + criteria[0].SeasonNumber.Should().Be(7); } } } diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs index b5b13d9a7..cfed22a5c 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionMaker.cs @@ -97,17 +97,6 @@ namespace NzbDrone.Core.DecisionEngine if (decision != null) { - if (searchCriteria != null) - { - var criteriaEpisodes = searchCriteria.Episodes.Select(v => v.Id).ToList(); - var remoteEpisodes = decision.RemoteEpisode.Episodes.Select(v => v.Id).ToList(); - if (!criteriaEpisodes.Intersect(remoteEpisodes).Any()) - { - _logger.Debug("Release rejected since the episode wasn't requested: {0}", decision.RemoteEpisode.ParsedEpisodeInfo); - continue; - } - } - if (decision.Rejections.Any()) { _logger.Debug("Release rejected for the following reasons: {0}", String.Join(", ", decision.Rejections)); diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs new file mode 100644 index 000000000..6df94a6fc --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/EpisodeRequestedSpecification.cs @@ -0,0 +1,43 @@ +using System.Linq; +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + + +namespace NzbDrone.Core.DecisionEngine.Specifications.Search +{ + public class EpisodeRequestedSpecification : IDecisionEngineSpecification + { + private readonly Logger _logger; + + public EpisodeRequestedSpecification(Logger logger) + { + _logger = logger; + } + + public string RejectionReason + { + get + { + return "Episode wasn't requested"; + } + } + + public bool IsSatisfiedBy(RemoteEpisode remoteEpisode, SearchCriteriaBase searchCriteria) + { + if (searchCriteria == null) + { + return true; + } + var criteriaEpisodes = searchCriteria.Episodes.Select(v => v.Id).ToList(); + var remoteEpisodes = remoteEpisode.Episodes.Select(v => v.Id).ToList(); + if (!criteriaEpisodes.Intersect(remoteEpisodes).Any()) + { + _logger.Debug("Release rejected since the episode wasn't requested: {0}", remoteEpisode.ParsedEpisodeInfo); + return false; + } + + return true; + } + } +} diff --git a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index a5b140beb..1b71323e6 100644 --- a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -142,15 +142,25 @@ namespace NzbDrone.Core.IndexerSearch if (series.UseSceneNumbering) { - var sceneSeasonGroups = episodes.GroupBy(v => v.SceneSeasonNumber).Distinct(); + var sceneSeasonGroups = episodes.GroupBy(v => + { + if (v.SceneSeasonNumber == 0 && v.SceneEpisodeNumber == 0) + return v.SeasonNumber; + else + return v.SceneSeasonNumber; + }).Distinct(); foreach (var sceneSeasonEpisodes in sceneSeasonGroups) { if (sceneSeasonEpisodes.Count() == 1) { + var episode = sceneSeasonEpisodes.First(); var searchSpec = Get(series, sceneSeasonEpisodes.ToList()); searchSpec.SeasonNumber = sceneSeasonEpisodes.Key; - searchSpec.EpisodeNumber = sceneSeasonEpisodes.First().SceneEpisodeNumber; + if (episode.SceneSeasonNumber == 0 && episode.SceneEpisodeNumber == 0) + searchSpec.EpisodeNumber = episode.EpisodeNumber; + else + searchSpec.EpisodeNumber = episode.SceneEpisodeNumber; var decisions = Dispatch(indexer => _feedFetcher.Fetch(indexer, searchSpec), searchSpec); downloadDecisions.AddRange(decisions); diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 0e3b6e8b7..c331efd94 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -217,6 +217,7 @@ + From f92aded4f09713a543d2d95c8018530f52ab07cc Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Thu, 27 Mar 2014 23:13:31 +0100 Subject: [PATCH 03/31] Revised Authentication logic for api and logfiles. --- .../EnableStatelessAuthInNancy.cs | 19 +++---------------- .../Extensions/RequestExtensions.cs | 8 +------- src/NzbDrone.Api/Logs/LogFileModule.cs | 18 ++++++++++++++++++ src/UI/System/Logs/Files/ContentsModel.js | 2 +- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs b/src/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs index 8996690bd..1bcb8685e 100644 --- a/src/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs +++ b/src/NzbDrone.Api/Authentication/EnableStatelessAuthInNancy.cs @@ -12,12 +12,10 @@ namespace NzbDrone.Api.Authentication { public class EnableStatelessAuthInNancy : IRegisterNancyPipeline { - private readonly IAuthenticationService _authenticationService; private static String API_KEY; - public EnableStatelessAuthInNancy(IAuthenticationService authenticationService, IConfigFileProvider configFileProvider) + public EnableStatelessAuthInNancy(IConfigFileProvider configFileProvider) { - _authenticationService = authenticationService; API_KEY = configFileProvider.ApiKey; } @@ -29,17 +27,12 @@ namespace NzbDrone.Api.Authentication public Response ValidateApiKey(NancyContext context) { Response response = null; - - if (!RuntimeInfo.IsProduction && context.Request.IsLocalRequest()) - { - return response; - } var authorizationHeader = context.Request.Headers.Authorization; var apiKeyHeader = context.Request.Headers["X-Api-Key"].FirstOrDefault(); var apiKey = apiKeyHeader.IsNullOrWhiteSpace() ? authorizationHeader : apiKeyHeader; - if (context.Request.IsApiRequest() && !ValidApiKey(apiKey) && !IsAuthenticated(context)) + if (context.Request.IsApiRequest() && !ValidApiKey(apiKey)) { response = new Response { StatusCode = HttpStatusCode.Unauthorized }; } @@ -49,15 +42,9 @@ namespace NzbDrone.Api.Authentication private bool ValidApiKey(string apiKey) { - if (apiKey.IsNullOrWhiteSpace()) return false; - if (!apiKey.Equals(API_KEY)) return false; + if (!API_KEY.Equals(apiKey)) return false; return true; } - - private bool IsAuthenticated(NancyContext context) - { - return _authenticationService.Enabled && _authenticationService.IsAuthenticated(context); - } } } \ No newline at end of file diff --git a/src/NzbDrone.Api/Extensions/RequestExtensions.cs b/src/NzbDrone.Api/Extensions/RequestExtensions.cs index 672cdb7d5..02686deb6 100644 --- a/src/NzbDrone.Api/Extensions/RequestExtensions.cs +++ b/src/NzbDrone.Api/Extensions/RequestExtensions.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Api.Extensions { public static bool IsApiRequest(this Request request) { - return request.Path.StartsWith("/api/", StringComparison.InvariantCultureIgnoreCase) || request.IsLogFileRequest(); + return request.Path.StartsWith("/api/", StringComparison.InvariantCultureIgnoreCase); } public static bool IsSignalRRequest(this Request request) @@ -21,11 +21,5 @@ namespace NzbDrone.Api.Extensions request.UserHostAddress.Equals("127.0.0.1") || request.UserHostAddress.Equals("::1")); } - - private static bool IsLogFileRequest(this Request request) - { - return request.Path.StartsWith("/log/", StringComparison.InvariantCultureIgnoreCase) && - request.Path.EndsWith(".txt", StringComparison.InvariantCultureIgnoreCase); - } } } diff --git a/src/NzbDrone.Api/Logs/LogFileModule.cs b/src/NzbDrone.Api/Logs/LogFileModule.cs index 94153428a..b44e77b6d 100644 --- a/src/NzbDrone.Api/Logs/LogFileModule.cs +++ b/src/NzbDrone.Api/Logs/LogFileModule.cs @@ -4,11 +4,15 @@ using System.Linq; using NzbDrone.Common; using NzbDrone.Common.Disk; using NzbDrone.Common.EnvironmentInfo; +using Nancy; +using Nancy.Responses; namespace NzbDrone.Api.Logs { public class LogFileModule : NzbDroneRestModule { + private const string LOGFILE_ROUTE = @"/(?nzbdrone(?:\.\d+)?\.txt)"; + private readonly IAppFolderInfo _appFolderInfo; private readonly IDiskProvider _diskProvider; @@ -19,6 +23,8 @@ namespace NzbDrone.Api.Logs _appFolderInfo = appFolderInfo; _diskProvider = diskProvider; GetResourceAll = GetLogFiles; + + Get[LOGFILE_ROUTE] = options => GetLogFile(options.filename); } private List GetLogFiles() @@ -41,5 +47,17 @@ namespace NzbDrone.Api.Logs return result.OrderByDescending(l => l.LastWriteTime).ToList(); } + + private Response GetLogFile(string filename) + { + var filePath = Path.Combine(_appFolderInfo.GetLogFolder(), filename); + + if (!_diskProvider.FileExists(filePath)) + return new NotFoundResponse(); + + var data = _diskProvider.ReadAllText(filePath); + + return new TextResponse(data); + } } } \ No newline at end of file diff --git a/src/UI/System/Logs/Files/ContentsModel.js b/src/UI/System/Logs/Files/ContentsModel.js index b8d2bdc73..d109013dc 100644 --- a/src/UI/System/Logs/Files/ContentsModel.js +++ b/src/UI/System/Logs/Files/ContentsModel.js @@ -6,7 +6,7 @@ define( ], function (Backbone, StatusModel) { return Backbone.Model.extend({ url: function () { - return StatusModel.get('urlBase') + '/logfile/' + this.get('filename'); + return StatusModel.get('urlBase') + '/api/log/file/' + this.get('filename'); }, parse: function (contents) { From cfc29f4424742ba4ed755d0c5c8cf03a28dafdbf Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Thu, 27 Mar 2014 23:13:24 +0100 Subject: [PATCH 04/31] Fixed typo in delete episode. --- src/UI/Cells/DeleteEpisodeFileCell.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UI/Cells/DeleteEpisodeFileCell.js b/src/UI/Cells/DeleteEpisodeFileCell.js index ac754c002..8e88f9c6e 100644 --- a/src/UI/Cells/DeleteEpisodeFileCell.js +++ b/src/UI/Cells/DeleteEpisodeFileCell.js @@ -22,7 +22,7 @@ define( _onClick: function () { var self = this; - if (window.confirm('Are you sure you want to delete \'{0}\' form disk?'.format(this.model.get('path')))) { + if (window.confirm('Are you sure you want to delete \'{0}\' from disk?'.format(this.model.get('path')))) { this.model.destroy() .done(function () { vent.trigger(vent.Events.EpisodeFileDeleted, { episodeFile: self.model }); From 118089c272aae01805ebafbf46ff8facf64e8c94 Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Thu, 27 Mar 2014 23:13:37 +0100 Subject: [PATCH 05/31] Process start logged as Debug instead of Info. --- src/NzbDrone.Common/Processes/ProcessProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NzbDrone.Common/Processes/ProcessProvider.cs b/src/NzbDrone.Common/Processes/ProcessProvider.cs index f0ac02148..7581a4b5c 100644 --- a/src/NzbDrone.Common/Processes/ProcessProvider.cs +++ b/src/NzbDrone.Common/Processes/ProcessProvider.cs @@ -116,7 +116,7 @@ namespace NzbDrone.Common.Processes }; - logger.Info("Starting {0} {1}", path, args); + logger.Debug("Starting {0} {1}", path, args); var process = new Process { @@ -163,7 +163,7 @@ namespace NzbDrone.Common.Processes path = "mono"; } - Logger.Info("Starting {0} {1}", path, args); + Logger.Debug("Starting {0} {1}", path, args); var startInfo = new ProcessStartInfo(path, args); var process = new Process From fe02467a7167132ef7b1fe1322e847397f85b766 Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Thu, 27 Mar 2014 23:13:45 +0100 Subject: [PATCH 06/31] Exceptron now uses 'en' culture if none is specified. --- src/Exceptron.Client/ExceptronClient.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Exceptron.Client/ExceptronClient.cs b/src/Exceptron.Client/ExceptronClient.cs index 149a739d7..89ac5170f 100644 --- a/src/Exceptron.Client/ExceptronClient.cs +++ b/src/Exceptron.Client/ExceptronClient.cs @@ -184,6 +184,9 @@ namespace Exceptron.Client { report.cul = Thread.CurrentThread.CurrentCulture.Name; + if (string.IsNullOrEmpty(report.cul)) + report.cul = "en"; + try { report.os = Environment.OSVersion.VersionString; From 73c3cca69c0a643dc50a23da180ea6b343179578 Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Sat, 15 Feb 2014 11:56:28 +0100 Subject: [PATCH 07/31] Revised History Details labels. --- .../Details/HistoryDetailsViewTemplate.html | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/UI/History/Details/HistoryDetailsViewTemplate.html b/src/UI/History/Details/HistoryDetailsViewTemplate.html index 9c313a9ae..3df76c8e5 100644 --- a/src/UI/History/Details/HistoryDetailsViewTemplate.html +++ b/src/UI/History/Details/HistoryDetailsViewTemplate.html @@ -13,22 +13,22 @@ {{#if_eq eventType compare="grabbed"}}
-
Name
+
Name:
{{sourceTitle}}
{{#with data}} {{#if indexer}} -
Indexer
+
Indexer:
{{indexer}}
{{/if}} {{#if releaseGroup}} -
Release Group
+
Release Group:
{{releaseGroup}}
{{/if}} {{#if nzbInfoUrl}} -
Info
+
Info:
{{nzbInfoUrl}}
{{/if}} {{/with}} @@ -36,19 +36,25 @@ {{/if_eq}} {{#if_eq eventType compare="downloadFailed"}}
-
Source Title
+ +
Name:
{{sourceTitle}}
{{#with data}} -
Message
+
Message:
{{message}}
{{/with}}
{{/if_eq}} {{#if_eq eventType compare="downloadFolderImported"}} - {{#if data}} +
+ + {{#if sourceTitle}} +
Name:
+
{{sourceTitle}}
+ {{/if}} + {{#with data}} -
{{#if droppedPath}}
Source:
{{droppedPath}}
@@ -58,11 +64,8 @@
Imported To:
{{importedPath}}
{{/if}} -
{{/with}} - {{else}} - No details available - {{/if}} +
{{/if_eq}} + {{#unless existing}}
{{#unless path}}
Path
@@ -29,6 +30,7 @@
Starting Season
Quality Profile
+ {{/unless}}
{{#if existing}} From 2427b1f2543e0c3c09cec29ac1db7892c95e07f3 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 4 Apr 2014 08:32:06 -0700 Subject: [PATCH 18/31] New: Blacklist release from episode details --- .../Activity/EpisodeActivityActionsCell.js | 41 +++++++++++++++++++ .../Episode/Activity/EpisodeActivityLayout.js | 17 +++++++- src/UI/History/Details/HistoryDetailsView.js | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/UI/Episode/Activity/EpisodeActivityActionsCell.js diff --git a/src/UI/Episode/Activity/EpisodeActivityActionsCell.js b/src/UI/Episode/Activity/EpisodeActivityActionsCell.js new file mode 100644 index 000000000..2c706fddf --- /dev/null +++ b/src/UI/Episode/Activity/EpisodeActivityActionsCell.js @@ -0,0 +1,41 @@ +'use strict'; + +define( + [ + 'jquery', + 'vent', + 'marionette', + 'Cells/NzbDroneCell' + ], function ($, vent, Marionette, NzbDroneCell) { + return NzbDroneCell.extend({ + + className: 'episode-actions-cell', + + events: { + 'click .x-failed' : '_markAsFailed' + }, + + render: function () { + this.$el.empty(); + + if (this.model.get('eventType') === 'grabbed') { + this.$el.html(''); + } + + return this; + }, + + _markAsFailed: function () { + var url = window.NzbDrone.ApiRoot + '/history/failed'; + var data = { + id: this.model.get('id') + }; + + $.ajax({ + url: url, + type: 'POST', + data: data + }); + } + }); + }); diff --git a/src/UI/Episode/Activity/EpisodeActivityLayout.js b/src/UI/Episode/Activity/EpisodeActivityLayout.js index 53de87fdc..b9c9582d6 100644 --- a/src/UI/Episode/Activity/EpisodeActivityLayout.js +++ b/src/UI/Episode/Activity/EpisodeActivityLayout.js @@ -7,9 +7,18 @@ define( 'Cells/EventTypeCell', 'Cells/QualityCell', 'Cells/RelativeDateCell', + 'Episode/Activity/EpisodeActivityActionsCell', 'Episode/Activity/NoActivityView', 'Shared/LoadingView' - ], function (Marionette, Backgrid, HistoryCollection, EventTypeCell, QualityCell, RelativeDateCell, NoActivityView, LoadingView) { + ], function (Marionette, + Backgrid, + HistoryCollection, + EventTypeCell, + QualityCell, + RelativeDateCell, + EpisodeActivityActionsCell, + NoActivityView, + LoadingView) { return Marionette.Layout.extend({ template: 'Episode/Activity/EpisodeActivityLayoutTemplate', @@ -40,6 +49,12 @@ define( name : 'date', label: 'Date', cell : RelativeDateCell + }, + { + name : 'this', + label : '', + cell : EpisodeActivityActionsCell, + sortable: false } ], diff --git a/src/UI/History/Details/HistoryDetailsView.js b/src/UI/History/Details/HistoryDetailsView.js index c1470cb46..ba2177ac5 100644 --- a/src/UI/History/Details/HistoryDetailsView.js +++ b/src/UI/History/Details/HistoryDetailsView.js @@ -24,6 +24,7 @@ define( type: 'POST', data: data }); + vent.trigger(vent.Commands.CloseModalCommand); } }); From 52c5c19b0a7fdee5c17db3c31378452f0c3909c2 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 4 Apr 2014 08:56:29 -0700 Subject: [PATCH 19/31] New: Show release name when downloading (series details & calendar) --- src/UI/Calendar/CalendarView.js | 15 +++++++++++++++ src/UI/Cells/EpisodeStatusCell.js | 2 +- src/UI/Content/Overrides/bootstrap.less | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/UI/Calendar/CalendarView.js b/src/UI/Calendar/CalendarView.js index d0ed7902b..4e3aec332 100644 --- a/src/UI/Calendar/CalendarView.js +++ b/src/UI/Calendar/CalendarView.js @@ -76,6 +76,10 @@ define( size : 14, animate : false }); + + this.$(element).find('.chart').tooltip({ + title: 'Episode is downloading - {0}% {1}'.format(event.progress.toFixed(1), event.releaseTitle) + }); } }, @@ -109,6 +113,7 @@ define( allDay : false, statusLevel : self._getStatusLevel(model, end), progress : self._getDownloadProgress(model), + releaseTitle: self._getReleaseTitle(model), model : model }; @@ -163,6 +168,16 @@ define( } return 100 - (downloading.get('sizeleft') / downloading.get('size') * 100); + }, + + _getReleaseTitle: function (element) { + var downloading = QueueCollection.findEpisode(element.get('id')); + + if (!downloading) { + return ''; + } + + return downloading.get('title'); } }); }); diff --git a/src/UI/Cells/EpisodeStatusCell.js b/src/UI/Cells/EpisodeStatusCell.js index c48fffcf1..1b4d6a68c 100644 --- a/src/UI/Cells/EpisodeStatusCell.js +++ b/src/UI/Cells/EpisodeStatusCell.js @@ -69,7 +69,7 @@ define( if (downloading) { var progress = 100 - (downloading.get('sizeleft') / downloading.get('size') * 100); - this.$el.html('
'.format(progress.toFixed(1)) + + this.$el.html('
'.format(progress.toFixed(1), downloading.get('title')) + '
'.format(progress)); return; } diff --git a/src/UI/Content/Overrides/bootstrap.less b/src/UI/Content/Overrides/bootstrap.less index 9a2477e8f..86f2f92a9 100644 --- a/src/UI/Content/Overrides/bootstrap.less +++ b/src/UI/Content/Overrides/bootstrap.less @@ -41,3 +41,7 @@ height : 1em; line-height : 1em; } + +.tooltip-inner { + word-wrap: break-word; +} From 0f9360bccb81cda7e26a5825e0c4b620a74db046 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 4 Apr 2014 09:25:18 -0700 Subject: [PATCH 20/31] New: Advanced option to skip checking free space when importing files --- .../FreeSpaceSpecificationFixture.cs | 11 ++++++++ .../Configuration/ConfigService.cs | 7 +++++ .../Configuration/IConfigService.cs | 1 + .../Specifications/FreeSpaceSpecification.cs | 12 +++++++-- .../Sorting/SortingViewTemplate.html | 27 +++++++++++++++++++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecificationFixture.cs index 664f56b8b..6037e8681 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecificationFixture.cs @@ -7,6 +7,7 @@ using Moq; using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Common.Disk; +using NzbDrone.Core.Configuration; using NzbDrone.Core.MediaFiles.EpisodeImport.Specifications; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; @@ -143,5 +144,15 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); } + + [Test] + public void should_return_true_when_skip_check_is_enabled() + { + Mocker.GetMock() + .Setup(s => s.SkipFreeSpaceCheckWhenImporting) + .Returns(true); + + Subject.IsSatisfiedBy(_localEpisode).Should().BeTrue(); + } } } diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 9626b969b..883d463ad 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -164,6 +164,13 @@ namespace NzbDrone.Core.Configuration set { SetValue("DownloadedEpisodesScanInterval", value); } } + public Boolean SkipFreeSpaceCheckWhenImporting + { + get { return GetValueBoolean("SkipFreeSpaceCheckWhenImporting", false); } + + set { SetValue("SkipFreeSpaceCheckWhenImporting", value); } + } + public Boolean SetPermissionsLinux { get { return GetValueBoolean("SetPermissionsLinux", false); } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index 9a513e75a..caaaea82a 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -26,6 +26,7 @@ namespace NzbDrone.Core.Configuration Boolean AutoDownloadPropers { get; set; } Boolean CreateEmptySeriesFolders { get; set; } FileDateType FileDate { get; set; } + Boolean SkipFreeSpaceCheckWhenImporting { get; set; } //Permissions (Media Management) Boolean SetPermissionsLinux { get; set; } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs index bad570bf4..1256a5207 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/Specifications/FreeSpaceSpecification.cs @@ -1,8 +1,8 @@ using System; using System.IO; using NLog; -using NzbDrone.Common; using NzbDrone.Common.Disk; +using NzbDrone.Core.Configuration; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications @@ -10,11 +10,13 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications public class FreeSpaceSpecification : IImportDecisionEngineSpecification { private readonly IDiskProvider _diskProvider; + private readonly IConfigService _configService; private readonly Logger _logger; - public FreeSpaceSpecification(IDiskProvider diskProvider, Logger logger) + public FreeSpaceSpecification(IDiskProvider diskProvider, IConfigService configService, Logger logger) { _diskProvider = diskProvider; + _configService = configService; _logger = logger; } @@ -22,6 +24,12 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications public bool IsSatisfiedBy(LocalEpisode localEpisode) { + if (_configService.SkipFreeSpaceCheckWhenImporting) + { + _logger.Debug("Skipping free space check when importing"); + return true; + } + try { if (localEpisode.ExistingFile) diff --git a/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html b/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html index 76f25867a..95e77c199 100644 --- a/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html +++ b/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html @@ -22,3 +22,30 @@
+ +{{#isMono}} +
+ Importing + +
+ + +
+
+
+{{/isMono}} From b65f2e7845b7b1bc01b16f5fb1f3ba13d66264bc Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Fri, 4 Apr 2014 10:58:21 -0700 Subject: [PATCH 21/31] Messenger supports hideOnNavigate now New: After adding a series you will be able to navigate to it via the UI notification --- src/UI/AddSeries/SearchResultView.js | 28 +++++++++++++++++++++++++--- src/UI/Shared/Messenger.js | 5 ++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/UI/AddSeries/SearchResultView.js b/src/UI/AddSeries/SearchResultView.js index c176115ed..4450cee72 100644 --- a/src/UI/AddSeries/SearchResultView.js +++ b/src/UI/AddSeries/SearchResultView.js @@ -1,9 +1,10 @@ 'use strict'; define( [ + 'underscore', 'vent', 'AppLayout', - 'underscore', + 'backbone', 'marionette', 'Quality/QualityProfileCollection', 'AddSeries/RootFolders/RootFolderCollection', @@ -13,7 +14,18 @@ define( 'Shared/Messenger', 'Mixins/AsValidatedView', 'jquery.dotdotdot' - ], function (vent, AppLayout, _, Marionette, QualityProfiles, RootFolders, RootFolderLayout, SeriesCollection, Config, Messenger, AsValidatedView) { + ], function (_, + vent, + AppLayout, + Backbone, + Marionette, + QualityProfiles, + RootFolders, + RootFolderLayout, + SeriesCollection, + Config, + Messenger, + AsValidatedView) { var view = Marionette.ItemView.extend({ @@ -156,7 +168,17 @@ define( icon.removeClass('icon-spin icon-spinner disabled').addClass('icon-search'); Messenger.show({ - message: 'Added: ' + self.model.get('title') + message: 'Added: ' + self.model.get('title'), + actions : { + goToSeries: { + label: 'Go to Series Page', + action: function() { + Backbone.history.navigate('/series/' + self.model.get('titleSlug'), { trigger: true }); + } + } + }, + hideAfter: 8, + hideOnNavigate: true }); vent.trigger(vent.Events.SeriesAdded, { series: self.model }); diff --git a/src/UI/Shared/Messenger.js b/src/UI/Shared/Messenger.js index 04823f984..2a81b3c70 100644 --- a/src/UI/Shared/Messenger.js +++ b/src/UI/Shared/Messenger.js @@ -23,13 +23,16 @@ define(function () { } } + options.hideOnNavigate = options.hideOnNavigate || false; + return window.Messenger().post({ message : options.message, type : options.type, showCloseButton: true, hideAfter : options.hideAfter, id : options.id, - actions : options.actions + actions : options.actions, + hideOnNavigate : options.hideOnNavigate }); }, From 2cc0dc3aab01a8bb082efd0d4b3b62c3b27ee218 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 6 Apr 2014 19:44:04 -0700 Subject: [PATCH 22/31] New: Username and Password for Plex Server (optional) --- src/NzbDrone.Common/HttpProvider.cs | 3 ++- .../Notifications/Plex/PlexServerSettings.cs | 8 +++++++- src/NzbDrone.Core/Notifications/Plex/PlexService.cs | 12 ++++++++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/NzbDrone.Common/HttpProvider.cs b/src/NzbDrone.Common/HttpProvider.cs index c64b081c6..283e8946b 100644 --- a/src/NzbDrone.Common/HttpProvider.cs +++ b/src/NzbDrone.Common/HttpProvider.cs @@ -13,6 +13,7 @@ namespace NzbDrone.Common { string DownloadString(string url); string DownloadString(string url, string username, string password); + string DownloadString(string url, ICredentials credentials); Dictionary GetHeader(string url); Stream DownloadStream(string url, NetworkCredential credential = null); @@ -44,7 +45,7 @@ namespace NzbDrone.Common return DownloadString(url, new NetworkCredential(username, password)); } - private string DownloadString(string url, ICredentials identity) + public string DownloadString(string url, ICredentials identity) { try { diff --git a/src/NzbDrone.Core/Notifications/Plex/PlexServerSettings.cs b/src/NzbDrone.Core/Notifications/Plex/PlexServerSettings.cs index 020f7f65f..272d0efcc 100644 --- a/src/NzbDrone.Core/Notifications/Plex/PlexServerSettings.cs +++ b/src/NzbDrone.Core/Notifications/Plex/PlexServerSettings.cs @@ -30,7 +30,13 @@ namespace NzbDrone.Core.Notifications.Plex [FieldDefinition(1, Label = "Port")] public Int32 Port { get; set; } - [FieldDefinition(2, Label = "Update Library", Type = FieldType.Checkbox)] + [FieldDefinition(2, Label = "Username")] + public String Username { get; set; } + + [FieldDefinition(3, Label = "Password")] + public String Password { get; set; } + + [FieldDefinition(4, Label = "Update Library", Type = FieldType.Checkbox)] public Boolean UpdateLibrary { get; set; } public bool IsValid diff --git a/src/NzbDrone.Core/Notifications/Plex/PlexService.cs b/src/NzbDrone.Core/Notifications/Plex/PlexService.cs index fc02d0cc7..7fddd2a64 100644 --- a/src/NzbDrone.Core/Notifications/Plex/PlexService.cs +++ b/src/NzbDrone.Core/Notifications/Plex/PlexService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Xml.Linq; using NLog; using NzbDrone.Common; @@ -58,7 +59,7 @@ namespace NzbDrone.Core.Notifications.Plex { _logger.Debug("Getting sections from Plex host: {0}", settings.Host); var url = String.Format("http://{0}:{1}/library/sections", settings.Host, settings.Port); - var xmlStream = _httpProvider.DownloadStream(url, null); + var xmlStream = _httpProvider.DownloadStream(url, GetCredentials(settings)); var xDoc = XDocument.Load(xmlStream); var mediaContainer = xDoc.Descendants("MediaContainer").FirstOrDefault(); var directories = mediaContainer.Descendants("Directory").Where(x => x.Attribute("type").Value == "show"); @@ -70,7 +71,7 @@ namespace NzbDrone.Core.Notifications.Plex { _logger.Debug("Updating Plex host: {0}, Section: {1}", settings.Host, key); var url = String.Format("http://{0}:{1}/library/sections/{2}/refresh", settings.Host, settings.Port, key); - _httpProvider.DownloadString(url); + _httpProvider.DownloadString(url, GetCredentials(settings)); } public string SendCommand(string host, int port, string command, string username, string password) @@ -85,6 +86,13 @@ namespace NzbDrone.Core.Notifications.Plex return _httpProvider.DownloadString(url); } + private NetworkCredential GetCredentials(PlexServerSettings settings) + { + if (settings.Username.IsNullOrWhiteSpace()) return null; + + return new NetworkCredential(settings.Username, settings.Password); + } + public void Execute(TestPlexClientCommand message) { _logger.Debug("Sending Test Notifcation to Plex Client: {0}", message.Host); From e58faf8621a25483023f77f0496af17c2ebd8e66 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 6 Apr 2014 19:46:41 -0700 Subject: [PATCH 23/31] Plex server testing will use username and password if configured --- src/NzbDrone.Core/Notifications/Plex/PlexService.cs | 8 +++++++- .../Notifications/Plex/TestPlexServerCommand.cs | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/NzbDrone.Core/Notifications/Plex/PlexService.cs b/src/NzbDrone.Core/Notifications/Plex/PlexService.cs index 7fddd2a64..0d4c6608b 100644 --- a/src/NzbDrone.Core/Notifications/Plex/PlexService.cs +++ b/src/NzbDrone.Core/Notifications/Plex/PlexService.cs @@ -108,7 +108,13 @@ namespace NzbDrone.Core.Notifications.Plex public void Execute(TestPlexServerCommand message) { - if (!GetSectionKeys(new PlexServerSettings {Host = message.Host, Port = message.Port}).Any()) + if (!GetSectionKeys(new PlexServerSettings + { + Host = message.Host, + Port = message.Port, + Username = message.Username, + Password = message.Password + }).Any()) { throw new Exception("Unable to connect to Plex Server"); } diff --git a/src/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs b/src/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs index 19fac0641..de8394e97 100644 --- a/src/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs +++ b/src/NzbDrone.Core/Notifications/Plex/TestPlexServerCommand.cs @@ -14,5 +14,7 @@ namespace NzbDrone.Core.Notifications.Plex public string Host { get; set; } public int Port { get; set; } + public string Username { get; set; } + public string Password { get; set; } } } From 0a17630c5a8c1f71e068f0e5e508b123aa0e5bed Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 6 Apr 2014 20:10:05 -0700 Subject: [PATCH 24/31] Fixed: Do not treat the pressence of Italy as an Italian release --- src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs | 1 + src/NzbDrone.Core/Parser/Parser.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs index b9e36fc09..3b4ed3746 100644 --- a/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/LanguageParserFixture.cs @@ -39,6 +39,7 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("Ray Donovan - S01E01.720p.HDtv.x264-Evolve (NLsub)", Language.Dutch)] [TestCase("Shield,.The.1x13.Tueurs.De.Flics.FR.DVDRip.XviD", Language.French)] [TestCase("True.Detective.S01E01.1080p.WEB-DL.Rus.Eng.TVKlondike", Language.Russian)] + [TestCase("The.Trip.To.Italy.S02E01.720p.HDTV.x264-TLA", Language.English)] public void should_parse_language(string postTitle, Language language) { var result = Parser.Parser.ParseTitle(postTitle); diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index 994f5df91..0df8b6d6d 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -107,7 +107,7 @@ namespace NzbDrone.Core.Parser private static readonly Regex MultiPartCleanupRegex = new Regex(@"\(\d+\)$", RegexOptions.Compiled); - private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?ita|italian)|(?german\b)|(?flemish)|(?greek)|(?(?:\W|_)FR)(?:\W|_)|(?\brus\b)", + private static readonly Regex LanguageRegex = new Regex(@"(?:\W|_)(?\bita\b|italian)|(?german\b)|(?flemish)|(?greek)|(?(?:\W|_)FR)(?:\W|_)|(?\brus\b)", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex YearInTitleRegex = new Regex(@"^(?.+?)(?:\W|_)?(?<year>\d{4})", From 8b9ea9817a4f2325429ea610dac9609b98889ec2 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Sun, 6 Apr 2014 21:07:55 -0700 Subject: [PATCH 25/31] Show download client and download client id in history details --- src/NzbDrone.Core/Download/DownloadApprovedReports.cs | 1 - src/UI/History/Details/HistoryDetailsViewTemplate.html | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/NzbDrone.Core/Download/DownloadApprovedReports.cs b/src/NzbDrone.Core/Download/DownloadApprovedReports.cs index 1added8cc..1c8ef160b 100644 --- a/src/NzbDrone.Core/Download/DownloadApprovedReports.cs +++ b/src/NzbDrone.Core/Download/DownloadApprovedReports.cs @@ -4,7 +4,6 @@ using System.Linq; using NLog; using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; namespace NzbDrone.Core.Download { diff --git a/src/UI/History/Details/HistoryDetailsViewTemplate.html b/src/UI/History/Details/HistoryDetailsViewTemplate.html index 3df76c8e5..a5232f41d 100644 --- a/src/UI/History/Details/HistoryDetailsViewTemplate.html +++ b/src/UI/History/Details/HistoryDetailsViewTemplate.html @@ -31,6 +31,16 @@ <dt>Info:</dt> <dd><a href="{{nzbInfoUrl}}">{{nzbInfoUrl}}</a></dd> {{/if}} + + {{#if downloadClient}} + <dt>Download Client:</dt> + <dd>{{downloadClient}}</dd> + {{/if}} + + {{#if downloadClientId}} + <dt>Download Client ID:</dt> + <dd>{{downloadClientId}}</dd> + {{/if}} {{/with}} </dl> {{/if_eq}} From c41944ed33adf20d10672785717f3cde7bef981a Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Mon, 7 Apr 2014 11:15:18 -0700 Subject: [PATCH 26/31] if_mono to fix free space check on import --- .../Settings/MediaManagement/Sorting/SortingViewTemplate.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html b/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html index 95e77c199..1f1a22f8a 100644 --- a/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html +++ b/src/UI/Settings/MediaManagement/Sorting/SortingViewTemplate.html @@ -23,7 +23,7 @@ </div> </fieldset> -{{#isMono}} +{{#if_mono}} <fieldset class="advanced-setting"> <legend>Importing</legend> @@ -48,4 +48,4 @@ </div> </div> </fieldset> -{{/isMono}} +{{/if_mono}} From 606875cc2ee18df32f428d756c4fe9e842d17c27 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Mon, 7 Apr 2014 11:51:51 -0700 Subject: [PATCH 27/31] Added SkipFreeSpaceCheckWhenImporting to resource so it can be saved --- src/NzbDrone.Api/Config/MediaManagementConfigResource.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs index 8958fe905..23eeef97c 100644 --- a/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs +++ b/src/NzbDrone.Api/Config/MediaManagementConfigResource.cs @@ -17,5 +17,7 @@ namespace NzbDrone.Api.Config public String FolderChmod { get; set; } public String ChownUser { get; set; } public String ChownGroup { get; set; } + + public Boolean SkipFreeSpaceCheckWhenImporting { get; set; } } } From 31deff1ea3b42cf35afcd995305bede4f2bd9e94 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Tue, 8 Apr 2014 07:12:21 -0700 Subject: [PATCH 28/31] Fixed: Daily shows will no longer skip sample detection --- .../EpisodeImport/SampleServiceFixture.cs | 15 ++++----------- .../MediaFiles/DownloadedEpisodesImportService.cs | 2 +- .../MediaFiles/EpisodeImport/SampleService.cs | 6 ------ 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/SampleServiceFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/SampleServiceFixture.cs index 60db510f1..8e9bcc81a 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/SampleServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/SampleServiceFixture.cs @@ -54,21 +54,14 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport } [Test] - public void should_return_true_if_series_is_daily() - { - _series.SeriesType = SeriesTypes.Daily; - ShouldBeFalse(); - } - - [Test] - public void should_return_true_if_season_zero() + public void should_return_false_if_season_zero() { _localEpisode.Episodes[0].SeasonNumber = 0; ShouldBeFalse(); } [Test] - public void should_return_true_for_flv() + public void should_return_false_for_flv() { _localEpisode.Path = @"C:\Test\some.show.s01e01.flv"; @@ -93,7 +86,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport } [Test] - public void should_return_false_if_runtime_is_less_than_minimum() + public void should_return_true_if_runtime_is_less_than_minimum() { GivenRuntime(60); @@ -101,7 +94,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport } [Test] - public void should_return_true_if_runtime_greater_than_than_minimum() + public void should_return_false_if_runtime_greater_than_than_minimum() { GivenRuntime(120); diff --git a/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs b/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs index 62423f203..54d9bb45f 100644 --- a/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/DownloadedEpisodesImportService.cs @@ -185,7 +185,7 @@ namespace NzbDrone.Core.MediaFiles if (!_sampleService.IsSample(series, quality, videoFile, size, episodeParseResult.SeasonNumber)) { - _logger.Warn("Non-sample file has not been imported: [{0}]", videoFile); + _logger.Warn("Non-sample file detected: [{0}]", videoFile); return false; } } diff --git a/src/NzbDrone.Core/MediaFiles/EpisodeImport/SampleService.cs b/src/NzbDrone.Core/MediaFiles/EpisodeImport/SampleService.cs index 2e5e647c5..cf59efdfb 100644 --- a/src/NzbDrone.Core/MediaFiles/EpisodeImport/SampleService.cs +++ b/src/NzbDrone.Core/MediaFiles/EpisodeImport/SampleService.cs @@ -36,12 +36,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport public bool IsSample(Series series, QualityModel quality, string path, long size, int seasonNumber) { - if (series.SeriesType == SeriesTypes.Daily) - { - _logger.Debug("Daily Series, skipping sample check"); - return false; - } - if (seasonNumber == 0) { _logger.Debug("Special, skipping sample check"); From 6a26204d8e90a583ded8e9ffe60581afbcb939b3 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Tue, 8 Apr 2014 10:22:36 -0700 Subject: [PATCH 29/31] Allow episode zero in tests, but fail in season and episode are both zero --- .../MetadataSourceTests/TraktProxyFixture.cs | 4 +++- src/NzbDrone.Core/Notifications/Growl/GrowlService.cs | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs b/src/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs index f2fe140d4..41734daed 100644 --- a/src/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs +++ b/src/NzbDrone.Core.Test/MetadataSourceTests/TraktProxyFixture.cs @@ -96,7 +96,9 @@ namespace NzbDrone.Core.Test.MetadataSourceTests private void ValidateEpisode(Episode episode) { episode.Should().NotBeNull(); - episode.EpisodeNumber.Should().NotBe(0); + + //TODO: Is there a better way to validate that episode number or season number is greater than zero? + (episode.EpisodeNumber + episode.SeasonNumber).Should().NotBe(0); episode.Should().NotBeNull(); diff --git a/src/NzbDrone.Core/Notifications/Growl/GrowlService.cs b/src/NzbDrone.Core/Notifications/Growl/GrowlService.cs index ce0ca87ef..d7d8686f3 100644 --- a/src/NzbDrone.Core/Notifications/Growl/GrowlService.cs +++ b/src/NzbDrone.Core/Notifications/Growl/GrowlService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Growl.Connector; using NLog; using NzbDrone.Common.Instrumentation; @@ -63,6 +64,8 @@ namespace NzbDrone.Core.Notifications.Growl const string title = "Test Notification"; const string body = "This is a test message from NzbDrone"; + Thread.Sleep(5000); + SendNotification(title, body, "TEST", message.Host, message.Port, message.Password); } } From b582127e145d27801796e05b18ac373b323e23e5 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Tue, 8 Apr 2014 12:14:35 -0700 Subject: [PATCH 30/31] Fixed a couple xbmc metadata bugs --- .../MetaData/Consumers/Xbmc/XbmcMetadata.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs b/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs index 0e74e81c4..78a61588b 100644 --- a/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs +++ b/src/NzbDrone.Core/MetaData/Consumers/Xbmc/XbmcMetadata.cs @@ -193,16 +193,22 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc { metadata.Type = MetadataType.SeasonImage; - var seasonNumber = seasonMatch.Groups["season"].Value; + var seasonNumberMatch = seasonMatch.Groups["season"].Value; + int seasonNumber; - if (seasonNumber.Contains("specials")) + if (seasonNumberMatch.Contains("specials")) { metadata.SeasonNumber = 0; } + else if (Int32.TryParse(seasonNumberMatch, out seasonNumber)) + { + metadata.SeasonNumber = seasonNumber; + } + else { - metadata.SeasonNumber = Convert.ToInt32(seasonNumber); + return null; } return metadata; @@ -462,7 +468,7 @@ namespace NzbDrone.Core.Metadata.Consumers.Xbmc var filename = GetEpisodeImageFilename(episodeFile.Path); var relativePath = DiskProviderBase.GetRelativePath(series.Path, filename); - var existingMetadata = existingMetadataFiles.SingleOrDefault(c => c.Type == MetadataType.EpisodeImage && + var existingMetadata = existingMetadataFiles.FirstOrDefault(c => c.Type == MetadataType.EpisodeImage && c.EpisodeFileId == episodeFile.Id); if (existingMetadata != null) From 492ffb571453a4f294c25b219ced6b0c2c5d70d1 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Tue, 8 Apr 2014 15:41:14 -0700 Subject: [PATCH 31/31] Added more logging for series editor --- src/NzbDrone.Core/Tv/SeriesService.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/NzbDrone.Core/Tv/SeriesService.cs b/src/NzbDrone.Core/Tv/SeriesService.cs index b2cbf8ff6..920ad701e 100644 --- a/src/NzbDrone.Core/Tv/SeriesService.cs +++ b/src/NzbDrone.Core/Tv/SeriesService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using NLog; +using NzbDrone.Common; using NzbDrone.Common.EnsureThat; using NzbDrone.Core.DataAugmentation.Scene; using NzbDrone.Core.Messaging.Events; @@ -197,14 +198,22 @@ namespace NzbDrone.Core.Tv { foreach (var s in series) { - if (!String.IsNullOrWhiteSpace(s.RootFolderPath)) + if (!s.RootFolderPath.IsNullOrWhiteSpace()) { var folderName = new DirectoryInfo(s.Path).Name; s.Path = Path.Combine(s.RootFolderPath, folderName); + _logger.Trace("Changing path for {0} to {1}", s.Title, s.Path); + } + + else + { + _logger.Trace("Not changing path for: {0}", s.Title); } } + _logger.Debug("Updating {0} series", series.Count); _seriesRepository.UpdateMany(series); + _logger.Debug("{0} series updated", series.Count); return series; }