diff --git a/NzbDrone.Core.Test/ProviderTests/DownloadProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/DownloadProviderTest.cs index 1549f9447..09983e269 100644 --- a/NzbDrone.Core.Test/ProviderTests/DownloadProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/DownloadProviderTest.cs @@ -38,7 +38,7 @@ namespace NzbDrone.Core.Test.ProviderTests .Returns(false); Mocker.GetMock() - .Setup(s => s.AddByUrl(parseResult.NzbUrl, sabTitle)) + .Setup(s => s.AddByUrl(parseResult, sabTitle)) .Returns(true); Mocker.GetMock() diff --git a/NzbDrone.Core.Test/ProviderTests/HistoryProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/HistoryProviderTest.cs index 8e3b6921c..7c6a9782d 100644 --- a/NzbDrone.Core.Test/ProviderTests/HistoryProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/HistoryProviderTest.cs @@ -191,5 +191,185 @@ namespace NzbDrone.Core.Test.ProviderTests history.Quality.Should().Be(storedHistory.First().Quality); history.IsProper.Should().Be(storedHistory.First().IsProper); } + + [Test] + public void IsBlacklisted_should_return_false_if_nzbTitle_doesnt_exist() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = false) + .Build(); + + Db.Insert(history); + + //Act + var result = Mocker.Resolve().IsBlacklisted("Not a Real NZB Title"); + + //Assert + result.Should().BeFalse(); + } + + [Test] + public void IsBlacklisted_should_return_false_if_nzbTitle_is_not_blacklisted() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = false) + .Build(); + + Db.Insert(history); + + //Act + var result = Mocker.Resolve().IsBlacklisted(history.NzbTitle); + + //Assert + result.Should().BeFalse(); + } + + [Test] + public void IsBlacklisted_should_return_true_if_nzbTitle_is_blacklisted() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = true) + .Build(); + + Db.Insert(history); + + //Act + var result = Mocker.Resolve().IsBlacklisted(history.NzbTitle); + + //Assert + result.Should().BeTrue(); + } + + [Test] + public void IsBlacklisted_should_return_true_if_nzbTitle_is_blacklisted_ignoring_case() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = true) + .Build(); + + Db.Insert(history); + + //Act + var result = Mocker.Resolve().IsBlacklisted(history.NzbTitle.ToLower()); + + //Assert + result.Should().BeTrue(); + } + + [Test] + public void IsBlacklisted_should_return_false_if_newzbinId_doesnt_exist() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = false) + .Build(); + + Db.Insert(history); + + //Act + var result = Mocker.Resolve().IsBlacklisted(555); + + //Assert + result.Should().BeFalse(); + } + + [Test] + public void IsBlacklisted_should_return_false_if_newzbinId_is_not_blacklisted() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = false) + .Build(); + + Db.Insert(history); + + //Act + var result = Mocker.Resolve().IsBlacklisted(history.NewzbinId); + + //Assert + result.Should().BeFalse(); + } + + [Test] + public void IsBlacklisted_should_return_true_if_newzbinId_is_blacklisted() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = true) + .Build(); + + Db.Insert(history); + + //Act + var result = Mocker.Resolve().IsBlacklisted(history.NewzbinId); + + //Assert + result.Should().BeTrue(); + } + + [Test] + public void IsBlacklisted_should_throw_if_newzbinId_is_less_than_1() + { + Assert.Throws(() => + Mocker.Resolve().IsBlacklisted(0) + ); + } + + [Test] + public void SetBlacklist_should_set_to_true_when_true_is_passed_in() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = false) + .Build(); + + Db.Insert(history); + + //Act + Mocker.Resolve().SetBlacklist(history.HistoryId, true); + + //Assert + var result = Db.Single(history.HistoryId); + result.Blacklisted.Should().BeTrue(); + } + + [Test] + public void SetBlacklist_should_set_to_false_when_false_is_passed_in() + { + WithRealDb(); + + var history = Builder.CreateNew() + .With(h => h.Blacklisted = true) + .Build(); + + Db.Insert(history); + + //Act + Mocker.Resolve().SetBlacklist(history.HistoryId, false); + + //Assert + var result = Db.Single(history.HistoryId); + result.Blacklisted.Should().BeFalse(); + } + + [Test] + public void SetBlacklist_should_throw_if_newzbinId_is_less_than_1() + { + Assert.Throws(() => + Mocker.Resolve().SetBlacklist(0, true) + ); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/InventoryProviderTests/QualityNeededFixture.cs b/NzbDrone.Core.Test/ProviderTests/InventoryProviderTests/QualityNeededFixture.cs index 74d94f7d1..7c1526732 100644 --- a/NzbDrone.Core.Test/ProviderTests/InventoryProviderTests/QualityNeededFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/InventoryProviderTests/QualityNeededFixture.cs @@ -29,6 +29,13 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests private QualityProfile sdProfile; private Series series; + private void WithNoBlacklist() + { + Mocker.GetMock() + .Setup(s => s.IsBlacklisted(It.IsAny())) + .Returns(false); + } + [SetUp] public void Setup() { @@ -186,6 +193,7 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests public void IsQualityNeeded_lesser_file_in_history_should_be_downloaded() { WithStrictMocker(); + WithNoBlacklist(); parseResultSingle.Series.QualityProfile = sdProfile; parseResultSingle.Quality.QualityType = QualityTypes.DVD; @@ -240,6 +248,8 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests .Setup(s => s.Get(It.IsAny())) .Returns(new QualityType { MaxSize = 100, MinSize = 0 }); + WithNoBlacklist(); + episode.EpisodeFile.Quality = QualityTypes.SDTV; //Act bool result = Mocker.Resolve().IsQualityNeeded(parseResultSingle); @@ -299,6 +309,8 @@ namespace NzbDrone.Core.Test.ProviderTests.InventoryProviderTests .Setup(s => s.Get(It.IsAny())) .Returns(new QualityType { MaxSize = 100, MinSize = 0 }); + WithNoBlacklist(); + episode.EpisodeFile.Quality = QualityTypes.SDTV; //Act bool result = Mocker.Resolve().IsQualityNeeded(parseResultSingle, true); diff --git a/NzbDrone.Core.Test/ProviderTests/SabProviderTests/SabProviderTest.cs b/NzbDrone.Core.Test/ProviderTests/SabProviderTests/SabProviderTest.cs index 576ec8439..8b82f81fe 100644 --- a/NzbDrone.Core.Test/ProviderTests/SabProviderTests/SabProviderTest.cs +++ b/NzbDrone.Core.Test/ProviderTests/SabProviderTests/SabProviderTest.cs @@ -21,6 +21,9 @@ namespace NzbDrone.Core.Test.ProviderTests.SabProviderTests // ReSharper disable InconsistentNaming public class SabProviderTest : CoreTest { + private EpisodeParseResult newzbinResult; + private EpisodeParseResult nonNewzbinResult; + [SetUp] public void Setup() { @@ -39,8 +42,17 @@ namespace NzbDrone.Core.Test.ProviderTests.SabProviderTests fakeConfig.SetupGet(c => c.SabUsername).Returns(username); fakeConfig.SetupGet(c => c.SabPassword).Returns(password); fakeConfig.SetupGet(c => c.SabTvCategory).Returns(cat); - } + newzbinResult = Builder.CreateNew() + .With(r => r.NewzbinId = 6107863) + .With(r => r.Indexer = "Newzbin") + .Build(); + + nonNewzbinResult = Builder.CreateNew() + .With(r => r.NzbUrl = "http://www.nzbclub.com/nzb_download.aspx?mid=1950232") + .With(r => r.Indexer = "Not Newzbin") + .Build(); + } private void WithFailResponse() { @@ -59,9 +71,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SabProviderTests .Returns("ok"); //Act - bool result = Mocker.Resolve().AddByUrl( - "http://www.nzbclub.com/nzb_download.aspx?mid=1950232", - "This is an Nzb"); + bool result = Mocker.Resolve().AddByUrl(nonNewzbinResult, "This is an Nzb"); //Assert result.Should().BeTrue(); @@ -79,9 +89,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SabProviderTests .Returns("ok"); //Act - bool result = Mocker.Resolve().AddByUrl( - "http://www.newzbin.com/browse/post/6107863/nzb", - "This is an Nzb"); + bool result = Mocker.Resolve().AddByUrl(newzbinResult, "This is an Nzb"); //Assert result.Should().BeTrue(); @@ -94,7 +102,7 @@ namespace NzbDrone.Core.Test.ProviderTests.SabProviderTests //Act var sabProvider = Mocker.Resolve(); - var result = sabProvider.AddByUrl("http://www.nzbclub.com/nzb_download.aspx?mid=1950232", "This is an nzb"); + var result = sabProvider.AddByUrl(nonNewzbinResult, "This is an nzb"); //Assert Assert.IsFalse(result); diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20120127.cs b/NzbDrone.Core/Datastore/Migrations/Migration20120127.cs new file mode 100644 index 000000000..fa393c4e0 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20120127.cs @@ -0,0 +1,16 @@ +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + [Migration(20120127)] + public class Migration20120127 : NzbDroneMigration + { + protected override void MainDbUpgrade() + { + Database.AddColumn("History", "NewzbinId", DbType.Int32, ColumnProperty.Null); + Database.AddColumn("History", "Blacklisted", DbType.Boolean, ColumnProperty.Null); + Database.ExecuteNonQuery("UPDATE History SET Blacklisted = 0"); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Model/EpisodeParseResult.cs b/NzbDrone.Core/Model/EpisodeParseResult.cs index 820f5bb98..69205971e 100644 --- a/NzbDrone.Core/Model/EpisodeParseResult.cs +++ b/NzbDrone.Core/Model/EpisodeParseResult.cs @@ -39,6 +39,8 @@ namespace NzbDrone.Core.Model public long Size { get; set; } + public int NewzbinId { get; set; } + public override string ToString() { if (AirDate != null && EpisodeNumbers == null) diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index db80ed76c..5048a3fc0 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -212,6 +212,7 @@ + diff --git a/NzbDrone.Core/Providers/DownloadProvider.cs b/NzbDrone.Core/Providers/DownloadProvider.cs index 3a1b580e6..f2cfc971c 100644 --- a/NzbDrone.Core/Providers/DownloadProvider.cs +++ b/NzbDrone.Core/Providers/DownloadProvider.cs @@ -38,7 +38,7 @@ namespace NzbDrone.Core.Providers } var sabTitle = _sabProvider.GetSabTitle(parseResult); - var addSuccess = _sabProvider.AddByUrl(parseResult.NzbUrl, sabTitle); + var addSuccess = _sabProvider.AddByUrl(parseResult, sabTitle); if (addSuccess) { @@ -54,6 +54,7 @@ namespace NzbDrone.Core.Providers history.NzbTitle = parseResult.OriginalString; history.EpisodeId = episode.EpisodeId; history.SeriesId = episode.SeriesId; + history.NewzbinId = parseResult.NewzbinId; _historyProvider.Add(history); _episodeProvider.MarkEpisodeAsFetched(episode.EpisodeId); diff --git a/NzbDrone.Core/Providers/HistoryProvider.cs b/NzbDrone.Core/Providers/HistoryProvider.cs index 14e17231e..9768072cd 100644 --- a/NzbDrone.Core/Providers/HistoryProvider.cs +++ b/NzbDrone.Core/Providers/HistoryProvider.cs @@ -68,5 +68,26 @@ namespace NzbDrone.Core.Providers { _database.Delete(historyId); } + + public virtual bool IsBlacklisted(string nzbTitle) + { + return _database.Exists("WHERE Blacklisted = 1 AND NzbTitle = @0", nzbTitle); + } + + public virtual bool IsBlacklisted(int newzbinId) + { + if (newzbinId <= 0) + throw new ArgumentException("Newzbin ID must be greater than 0"); + + return _database.Exists("WHERE Blacklisted = 1 AND NewzbinId = @0", newzbinId); + } + + public virtual void SetBlacklist(int historyId, bool toggle) + { + if (historyId <= 0) + throw new ArgumentException("HistoryId must be greater than 0"); + + _database.Execute("UPDATE History SET Blacklisted = @0 WHERE HistoryId = @1", toggle, historyId); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Indexer/Newzbin.cs b/NzbDrone.Core/Providers/Indexer/Newzbin.cs index f925fc4e9..1e11e2e1d 100644 --- a/NzbDrone.Core/Providers/Indexer/Newzbin.cs +++ b/NzbDrone.Core/Providers/Indexer/Newzbin.cs @@ -85,7 +85,7 @@ namespace NzbDrone.Core.Providers.Indexer }; } - + //Don't change the name or things that rely on it being "Newzbin" will fail... ugly... public override string Name { get { return "Newzbin"; } @@ -101,19 +101,18 @@ namespace NzbDrone.Core.Providers.Indexer if (currentResult != null) { var quality = Parser.ParseQuality(item.Summary.Text); - currentResult.Quality = quality; var languageString = Regex.Match(item.Summary.Text, @"Language - \w*", RegexOptions.IgnoreCase).Value; - currentResult.Language = Parser.ParseLanguage(languageString); var sizeString = Regex.Match(item.Summary.Text, @"\(Size: \d*\,?\d+\.\d{1,2}\w{2}\)", RegexOptions.IgnoreCase).Value; - currentResult.Size = Parser.GetReportSize(sizeString); + + var id = Regex.Match(NzbDownloadUrl(item), @"\d{5,10}").Value; + currentResult.NewzbinId = Int32.Parse(id); } return currentResult; } - } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/InventoryProvider.cs b/NzbDrone.Core/Providers/InventoryProvider.cs index e374e5e77..d196b222c 100644 --- a/NzbDrone.Core/Providers/InventoryProvider.cs +++ b/NzbDrone.Core/Providers/InventoryProvider.cs @@ -116,6 +116,26 @@ namespace NzbDrone.Core.Providers } } + if (parsedReport.Indexer == "Newzbin") + { + //Check for Blacklisting by NewzbinId + Logger.Trace("Checking if Newzbin ID has been black listed: ", parsedReport.NewzbinId); + if (_historyProvider.IsBlacklisted(parsedReport.NewzbinId)) + { + Logger.Info("Newzbin ID has been blacklisted: [{0}] Skipping", parsedReport.NewzbinId); + return false; + } + } + + else + { + Logger.Trace("Checking if Nzb has been black listed: ", parsedReport.OriginalString); + if(_historyProvider.IsBlacklisted(parsedReport.OriginalString)) + { + Logger.Info("Nzb has been blacklisted: [{0}] Skipping", parsedReport.OriginalString); + return false; + } + } } Logger.Debug("Episode {0} is needed", parsedReport); diff --git a/NzbDrone.Core/Providers/SabProvider.cs b/NzbDrone.Core/Providers/SabProvider.cs index 0abc378fb..e1e43a6f4 100644 --- a/NzbDrone.Core/Providers/SabProvider.cs +++ b/NzbDrone.Core/Providers/SabProvider.cs @@ -31,21 +31,23 @@ namespace NzbDrone.Core.Providers _httpProvider = httpProvider; } - public virtual bool AddByUrl(string url, string title) + public virtual bool AddByUrl(EpisodeParseResult parseResult, string title) { string cat = _configProvider.SabTvCategory; int priority = (int)_configProvider.SabTvPriority; - string name = GetNzbName(url); + string name = GetNzbName(parseResult.NzbUrl); string nzbName = HttpUtility.UrlEncode(title); + string mode = "addurl"; - string action = string.Format("mode=addurl&name={0}&priority={1}&pp=3&cat={2}&nzbname={3}", - name, priority, cat, nzbName); - - if (url.ToLower().Contains("newzbin")) + if (parseResult.Indexer == "Newzbin") { - action = action.Replace("mode=addurl", "mode=addid"); + mode = "addid"; + name = parseResult.NewzbinId.ToString(); } + string action = string.Format("mode={0}&name={1}&priority={2}&pp=3&cat={3}&nzbname={4}", mode, + name, priority, cat, nzbName); + string request = GetSabRequest(action); Logger.Info("Adding report [{0}] to the queue.", title); @@ -63,13 +65,6 @@ namespace NzbDrone.Core.Providers private static string GetNzbName(string urlString) { - var url = new Uri(urlString); - if (url.Host.ToLower().Contains("newzbin")) - { - var postId = Regex.Match(urlString, @"\d{5,10}").Value; - return postId; - } - return urlString.Replace("&", "%26"); } diff --git a/NzbDrone.Core/Repository/History.cs b/NzbDrone.Core/Repository/History.cs index d34d6c2b2..9295c4955 100644 --- a/NzbDrone.Core/Repository/History.cs +++ b/NzbDrone.Core/Repository/History.cs @@ -16,6 +16,8 @@ namespace NzbDrone.Core.Repository public DateTime Date { get; set; } public bool IsProper { get; set; } public string Indexer { get; set; } + public int NewzbinId { get; set; } + public bool Blacklisted { get; set; } [ResultColumn] public Episode Episode { get; set; } diff --git a/NzbDrone.Web/Content/Images/blacklist_false.png b/NzbDrone.Web/Content/Images/blacklist_false.png new file mode 100644 index 000000000..39a1bfece Binary files /dev/null and b/NzbDrone.Web/Content/Images/blacklist_false.png differ diff --git a/NzbDrone.Web/Content/Images/blacklist_true.png b/NzbDrone.Web/Content/Images/blacklist_true.png new file mode 100644 index 000000000..5ef7a3307 Binary files /dev/null and b/NzbDrone.Web/Content/Images/blacklist_true.png differ diff --git a/NzbDrone.Web/Controllers/HistoryController.cs b/NzbDrone.Web/Controllers/HistoryController.cs index f742f4804..ee0ea089a 100644 --- a/NzbDrone.Web/Controllers/HistoryController.cs +++ b/NzbDrone.Web/Controllers/HistoryController.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Web.Mvc; using NzbDrone.Core.Jobs; using NzbDrone.Core.Providers; @@ -73,10 +74,18 @@ namespace NzbDrone.Web.Controllers IsProper = h.IsProper, Date = h.Date, Indexer = h.Indexer, - EpisodeId = h.EpisodeId + EpisodeId = h.EpisodeId, + Blacklisted = h.Blacklisted }); return View(new GridModel(history)); } + + [HttpPost] + public JsonResult ToggleBlacklist(int historyId, bool toggle) + { + _historyProvider.SetBlacklist(historyId, toggle); + return new JsonResult { Data = "Success" }; + } } } \ No newline at end of file diff --git a/NzbDrone.Web/Models/HistoryModel.cs b/NzbDrone.Web/Models/HistoryModel.cs index 1bb6cbd25..e95452388 100644 --- a/NzbDrone.Web/Models/HistoryModel.cs +++ b/NzbDrone.Web/Models/HistoryModel.cs @@ -18,5 +18,6 @@ namespace NzbDrone.Web.Models public bool IsProper { get; set; } public string Indexer { get; set; } public int EpisodeId { get; set; } + public bool Blacklisted { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index b6a2e430f..19de351d9 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -141,6 +141,8 @@ + + diff --git a/NzbDrone.Web/Views/History/Index.cshtml b/NzbDrone.Web/Views/History/Index.cshtml index d82974c37..99b46d38e 100644 --- a/NzbDrone.Web/Views/History/Index.cshtml +++ b/NzbDrone.Web/Views/History/Index.cshtml @@ -12,6 +12,20 @@ @section HeaderContent { @Html.IncludeCss("Grid.css") + + }
@{Html.Telerik().Grid().Name("history") @@ -34,7 +48,8 @@ columns.Bound(c => c.Date).Title("Grabbed on"); columns.Bound(c => c.HistoryId) .Title("Actions") - .ClientTemplate(Ajax.ImageActionLink("../../Content/Images/X.png", new { Alt = "Delete", Title = "Delete from history", @class = "searchImage" }, "Delete", "History", new { HistoryId = "<#= HistoryId #>" }, new AjaxOptions { OnSuccess = "reloadHistoryGrid" }, null).ToString() + + .ClientTemplate("" + + Ajax.ImageActionLink("../../Content/Images/X.png", new { Alt = "Delete", Title = "Delete from history", @class = "searchImage" }, "Delete", "History", new { HistoryId = "<#= HistoryId #>" }, new AjaxOptions { OnSuccess = "reloadHistoryGrid" }, null).ToString() + Ajax.ImageActionLink("../../Content/Images/Downloading.png", new { Alt = "Redownload", Title = "Redownlod Episode", @class = "searchImage" }, "Redownload", "History", new { HistoryId = "<#= HistoryId #>", EpisodeId = "<#= EpisodeId #>" }, new AjaxOptions { OnSuccess = "reloadHistoryGrid" }, null).ToString()) .Width("40"); }) @@ -60,9 +75,41 @@