From e71a55a33c9e8c8b34fe7530cbccd1a3f567eec3 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 13 Jun 2011 11:25:40 -0700 Subject: [PATCH 1/8] More episode parsing tests. Added new regex to find multi-episode files without series titles. Cleaned up some of the regex. Normalizing regex will remove all numbers except years that are in the 1900's or 2000's --- NzbDrone.Core.Test/ParserTest.cs | 8 ++--- NzbDrone.Core/Parser.cs | 51 +++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/NzbDrone.Core.Test/ParserTest.cs b/NzbDrone.Core.Test/ParserTest.cs index eefe09956..9ee4165a8 100644 --- a/NzbDrone.Core.Test/ParserTest.cs +++ b/NzbDrone.Core.Test/ParserTest.cs @@ -17,10 +17,6 @@ namespace NzbDrone.Core.Test * Unreported.World.Chinas.Lost.Sons.WS.PDTV.XviD-FTP */ - - - - [Test] [TestCase("Sonny.With.a.Chance.S02E15", "Sonny.With.a.Chance", 2, 15)] [TestCase("Two.and.a.Half.Me.103.720p.HDTV.X264-DIMENSION", "Two.and.a.Half.Me", 1, 3)] @@ -137,6 +133,10 @@ namespace NzbDrone.Core.Test [TestCase("White.Collar.2x04.2x05.720p.BluRay-FUTV", "White.Collar", 2, new[] { 4, 5 }, 2)] [TestCase("Desperate.Housewives.S07E22E23.720p.HDTV.X264-DIMENSION", "Desperate.Housewives", 7, new[] { 22,23 }, 2)] //[Row("The.Kennedys.Part.1.and.Part.2.DSR.XviD-SYS", 1, new[] { 1, 2 })] + [TestCase("S07E22 - 7x23 - And Lots of Security.. [HDTV].mkv", "", 7, new[] { 22, 23 }, 2)] + [TestCase("Desparate Housewives - S07E22 - 7x23 - And Lots of Security.. [HDTV].mkv", "Desparate Housewives", 7, new[] { 22, 23 }, 2)] + [TestCase("Desparate Housewives - S07E22 - S07E23 - And Lots of Security.. [HDTV].mkv", "Desparate Housewives", 7, new[] { 22, 23 }, 2)] + [TestCase("S03E01.S03E02.720p.HDTV.X264-DIMENSION", "", 3, new[] { 1, 2 }, 2)] public void episode_multipart_parse(string postTitle, string title, int season, int[] episodes, int count) { var result = Parser.ParseEpisodeInfo(postTitle); diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs index 8f1d15cfa..0b561f017 100644 --- a/NzbDrone.Core/Parser.cs +++ b/NzbDrone.Core/Parser.cs @@ -14,21 +14,36 @@ namespace NzbDrone.Core private static readonly Regex[] ReportTitleRegex = new[] { + //Episodes with airdate new Regex(@"^(?.+?)?\W*(?<airyear>\d{4})\W+(?<airmonth>\d{2})\W+(?<airday>\d{2})\W?(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), - new Regex(@"^(?<title>.+?)(?:\WS?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+)+\W?(?!\\)", + + //Multi-Part episodes without a title (S01E05.S01E06) + new Regex(@"^(?:\W*S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+){2,}\W?(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), - new Regex(@"^(?<title>.*?)?(?:\W?S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+)+\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //Single episodes or multi-episode (S01E05E06, S01E05-06, etc) + new Regex(@"^(?<title>.+?)(?:\W+S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+)+\W?(?!\\)", + RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //No Title - Single episodes or multi-episode (S01E05E06, S01E05-06, etc) + new Regex(@"^(?:\W?S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+\W*)+\W?(?!\\)", + RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //Supports 103/113 naming new Regex(@"^(?<title>.+?)?\W?(?:\W(?<season>\d+)(?<episode>\d{2}))+\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), //Supports 103/113 naming - new Regex(@"^(?<title>.*?)?(?:\W?S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|to)+(?<episode>\d+))+)+\W?(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //Episodes over 99 (3-digits or more) + new Regex(@"^(?<title>.*?)(?:\W?S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|to)+(?<episode>\d+))+)+\W?(?!\\)", + RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //Supports Season only releases new Regex(@"^(?<title>.*?)\W(?:S|Season\W?)?(?<season>\d{1,2}(?!\d+))+\W?(?!\\)", - RegexOptions.IgnoreCase | RegexOptions.Compiled) //Supports Season only releases + RegexOptions.IgnoreCase | RegexOptions.Compiled) }; - private static readonly Regex NormalizeRegex = new Regex(@"((^|\W)(a|an|the|and|or|of)($|\W))|\W|\b(?!(?:19\d{2}|20\d{2}))\d+\b", + private static readonly Regex NormalizeRegex = new Regex(@"((^|\W)(a|an|the|and|or|of)($|\W))|\W|(?:(?<=[^0-9]+)|\b)(?!(?:19\d{2}|20\d{2}))\d+(?=[^0-9ip]+|\b)", RegexOptions.IgnoreCase | RegexOptions.Compiled); private static readonly Regex SimpleTitleRegex = new Regex(@"480[i|p]|720[i|p]|1080[i|p]|[x|h]264|\\|\/|\<|\>|\?|\*|\:|\|", @@ -203,19 +218,19 @@ namespace NzbDrone.Core Logger.Trace("Trying to parse quality for {0}", name); name = name.Trim(); - var normilizedName = NormalizeTitle(name); + var normalizedName = NormalizeTitle(name); var result = new Quality { QualityType = QualityTypes.Unknown }; - result.Proper = normilizedName.Contains("proper"); + result.Proper = normalizedName.Contains("proper"); - if (normilizedName.Contains("dvd") || normilizedName.Contains("bdrip") || normilizedName.Contains("brrip")) + if (normalizedName.Contains("dvd") || normalizedName.Contains("bdrip") || normalizedName.Contains("brrip")) { result.QualityType = QualityTypes.DVD; return result; } - if (normilizedName.Contains("xvid") || normilizedName.Contains("divx")) + if (normalizedName.Contains("xvid") || normalizedName.Contains("divx")) { - if (normilizedName.Contains("bluray")) + if (normalizedName.Contains("bluray")) { result.QualityType = QualityTypes.DVD; return result; @@ -225,15 +240,15 @@ namespace NzbDrone.Core return result; } - if (normilizedName.Contains("bluray")) + if (normalizedName.Contains("bluray")) { - if (normilizedName.Contains("720p")) + if (normalizedName.Contains("720p")) { result.QualityType = QualityTypes.Bluray720p; return result; } - if (normilizedName.Contains("1080p")) + if (normalizedName.Contains("1080p")) { result.QualityType = QualityTypes.Bluray1080p; return result; @@ -242,12 +257,12 @@ namespace NzbDrone.Core result.QualityType = QualityTypes.Bluray720p; return result; } - if (normilizedName.Contains("webdl")) + if (normalizedName.Contains("webdl")) { result.QualityType = QualityTypes.WEBDL; return result; } - if (normilizedName.Contains("x264") || normilizedName.Contains("h264") || normilizedName.Contains("720p")) + if (normalizedName.Contains("x264") || normalizedName.Contains("h264") || normalizedName.Contains("720p")) { result.QualityType = QualityTypes.HDTV; return result; @@ -284,7 +299,7 @@ namespace NzbDrone.Core } } - if (normilizedName.Contains("sdtv") || (result.QualityType == QualityTypes.Unknown && normilizedName.Contains("hdtv"))) + if (normalizedName.Contains("sdtv") || (result.QualityType == QualityTypes.Unknown && normalizedName.Contains("hdtv"))) { result.QualityType = QualityTypes.SDTV; return result; From ab2007cb6f88c52695de746b1548a1ab18504284 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Mon, 13 Jun 2011 19:15:55 -0700 Subject: [PATCH 2/8] SceneNaming is now stored on NzbDrone webserver. Database will update every 12 hours from CSV on server. --- NzbDrone.Core.Test/SceneNameHelperTest.cs | 92 ++++++++++- NzbDrone.Core/CentralDispatch.cs | 1 + NzbDrone.Core/Datastore/Migrations.cs | 1 + NzbDrone.Core/Helpers/SceneNameHelper.cs | 146 ------------------ NzbDrone.Core/NzbDrone.Core.csproj | 4 +- .../Providers/Jobs/EpisodeSearchJob.cs | 9 +- .../Providers/Jobs/UpdateSceneMappingsJob.cs | 42 +++++ .../Providers/SceneNameMappingProvider.cs | 91 +++++++++++ NzbDrone.Core/Providers/SeriesProvider.cs | 7 +- NzbDrone.Core/Repository/SceneNameMapping.cs | 18 +++ SceneNameMappings.csv | 58 +++++++ 11 files changed, 311 insertions(+), 158 deletions(-) delete mode 100644 NzbDrone.Core/Helpers/SceneNameHelper.cs create mode 100644 NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs create mode 100644 NzbDrone.Core/Providers/SceneNameMappingProvider.cs create mode 100644 NzbDrone.Core/Repository/SceneNameMapping.cs create mode 100644 SceneNameMappings.csv diff --git a/NzbDrone.Core.Test/SceneNameHelperTest.cs b/NzbDrone.Core.Test/SceneNameHelperTest.cs index 91feadcb0..56b5c3cd8 100644 --- a/NzbDrone.Core.Test/SceneNameHelperTest.cs +++ b/NzbDrone.Core.Test/SceneNameHelperTest.cs @@ -1,11 +1,19 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Text; +using AutoMoq; +using FizzWare.NBuilder; using FluentAssertions; +using Moq; using NUnit.Framework; using NzbDrone.Core.Helpers; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Repository; using NzbDrone.Core.Test.Framework; +using SubSonic.Repository; +using TvdbLib.Data; namespace NzbDrone.Core.Test { @@ -13,20 +21,92 @@ namespace NzbDrone.Core.Test // ReSharper disable InconsistentNaming public class SceneNameHelperTest : TestBase { + [Test] + public void GetSceneName_exists() + { + //Setup + var fakeMap = Builder<SceneNameMapping>.CreateNew() + .With(f => f.SeriesId = 12345) + .With(f => f.SceneName = "Law and Order") + .Build(); + + var mocker = new AutoMoqer(); + + mocker.GetMock<IRepository>() + .Setup(f => f.Single<SceneNameMapping>(It.IsAny<Expression<Func<SceneNameMapping, bool>>>())) + .Returns(fakeMap); + + //Act + var sceneName = mocker.Resolve<SceneNameMappingProvider>().GetSceneName(fakeMap.SeriesId); + + //Assert + Assert.AreEqual(fakeMap.SceneName, sceneName); + } [Test] - public void GetIdByName_exists() + public void GetSeriesId_exists() { - var id = SceneNameHelper.GetIdByName("CSI New York"); - id.Should().Be(73696); + //Setup + var fakeMap = Builder<SceneNameMapping>.CreateNew() + .With(f => f.SeriesId = 12345) + .With(f => f.SceneName = "Law and Order") + .With(f => f.SceneName = "laworder") + .Build(); + + var mocker = new AutoMoqer(); + + mocker.GetMock<IRepository>() + .Setup(f => f.Single<SceneNameMapping>(It.IsAny<Expression<Func<SceneNameMapping, bool>>>())) + .Returns(fakeMap); + + //Act + var seriesId = mocker.Resolve<SceneNameMappingProvider>().GetSeriesId(fakeMap.SceneCleanName); + + //Assert + Assert.AreEqual(fakeMap.SeriesId, seriesId); } + [Test] + public void GetSceneName_null() + { + //Setup + var fakeMap = Builder<SceneNameMapping>.CreateNew() + .With(f => f.SeriesId = 12345) + .With(f => f.SceneName = "Law and Order") + .Build(); + + var mocker = new AutoMoqer(); + + mocker.GetMock<IRepository>() + .Setup(f => f.Single<SceneNameMapping>(It.IsAny<Expression<Func<SceneNameMapping, bool>>>())); + + //Act + var sceneName = mocker.Resolve<SceneNameMappingProvider>().GetSceneName(fakeMap.SeriesId); + + //Assert + Assert.AreEqual(null, sceneName); + } [Test] - public void GetTitleById_exists() + public void GetSeriesId_null() { - var title = SceneNameHelper.GetTitleById(71256); - title.Should().Be("The Daily Show"); + //Setup + var fakeMap = Builder<SceneNameMapping>.CreateNew() + .With(f => f.SeriesId = 12345) + .With(f => f.SceneName = "Law and Order") + .With(f => f.SceneName = "laworder") + .Build(); + + var mocker = new AutoMoqer(); + + mocker.GetMock<IRepository>() + .Setup(f => f.Single<SceneNameMapping>(It.IsAny<Expression<Func<SceneNameMapping, bool>>>())); + + //Act + var seriesId = mocker.Resolve<SceneNameMappingProvider>().GetSeriesId(fakeMap.SceneCleanName); + + //Assert + Assert.AreEqual(null, seriesId); } } } diff --git a/NzbDrone.Core/CentralDispatch.cs b/NzbDrone.Core/CentralDispatch.cs index c4a3c3dd5..f855d2064 100644 --- a/NzbDrone.Core/CentralDispatch.cs +++ b/NzbDrone.Core/CentralDispatch.cs @@ -122,6 +122,7 @@ namespace NzbDrone.Core _kernel.Bind<IJob>().To<EpisodeSearchJob>().InTransientScope(); _kernel.Bind<IJob>().To<RenameEpisodeJob>().InTransientScope(); _kernel.Bind<IJob>().To<PostDownloadScanJob>().InTransientScope(); + _kernel.Bind<IJob>().To<UpdateSceneMappingsJob>().InTransientScope(); _kernel.Get<JobProvider>().Initialize(); _kernel.Get<WebTimer>().StartTimer(30); diff --git a/NzbDrone.Core/Datastore/Migrations.cs b/NzbDrone.Core/Datastore/Migrations.cs index b87f8589f..e942c7c05 100644 --- a/NzbDrone.Core/Datastore/Migrations.cs +++ b/NzbDrone.Core/Datastore/Migrations.cs @@ -58,6 +58,7 @@ namespace NzbDrone.Core.Datastore repository.Single<QualityProfile>(1); repository.Single<History>(1); repository.Single<IndexerSetting>(1); + repository.Single<SceneNameMapping>(1); } diff --git a/NzbDrone.Core/Helpers/SceneNameHelper.cs b/NzbDrone.Core/Helpers/SceneNameHelper.cs deleted file mode 100644 index 1342bc591..000000000 --- a/NzbDrone.Core/Helpers/SceneNameHelper.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace NzbDrone.Core.Helpers -{ - public static class SceneNameHelper - { - //Todo: Move this to a publically available location (so updates can be applied without releasing a new version of NzbDrone) - //Todo: GoogleDocs? WCF Web Services on NzbDrone.com? - private static readonly Dictionary<String, Int32> SeriesIdLookupList = new Dictionary<string, int>(); - private static readonly Dictionary<Int32, String> SceneNameLookupList = new Dictionary<Int32, String>(); - - - static SceneNameHelper() - { - //These values are used to match report titles parsed out of RSS to a series in the DB - SeriesIdLookupList.Add(Parser.NormalizeTitle("CSI"), 72546); - SeriesIdLookupList.Add(Parser.NormalizeTitle("CSI New York"), 73696); - SeriesIdLookupList.Add(Parser.NormalizeTitle("CSI NY"), 73696); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Archer"), 110381); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Life After People The Series"), 83897); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Life After People"), 83897); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Kitchen Nightmares US"), 80552); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Daily Show"), 71256); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Daily Show with Jon Stewart"), 71256); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Law and Order SVU"), 75692); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Law and Order Special Victims Unit"), 75692); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Law and Order Criminal Intent"), 71489); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Law and Order CI"), 71489); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Dancing With The Stars US"), 79590); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Craig Ferguson"), 73387); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Jimmy Fallon"), 85355); - SeriesIdLookupList.Add(Parser.NormalizeTitle("David Letterman"), 75088); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Big Brother US"), 76706); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Colony"), 105521); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Colony US"), 105521); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Americas Funniest Home Videos"), 76235); - SeriesIdLookupList.Add(Parser.NormalizeTitle("AFHV"), 76235); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Childrens Hospital US"), 139941); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Childrens Hospital"), 139941); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Merlin"), 83123); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Merlin 2008"), 83123); - SeriesIdLookupList.Add(Parser.NormalizeTitle("WWE Monday Night RAW"), 76779); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Shit My Dad Says"), 164951); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Genius with Dave Gorman"), 83714); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Law and Order LA"), 168161); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Star Trek TOS"), 77526); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Star Trek DS9"), 72073); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Ellen Degeneres"), 72194); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Drinking Made Easy"), 195831); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Zane Lampreys Drinking Made Easy"), 195831); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Poirot"), 76133); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Agatha Christies Poirot"), 76133); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Real World Road Rules Challenge"), 70870); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Challenge Cutthroat"), 70870); - SeriesIdLookupList.Add(Parser.NormalizeTitle("This Old House Program"), 77444); - SeriesIdLookupList.Add(Parser.NormalizeTitle("60 Minutes US"), 73290); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Conan"), 194751); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Conan 2010"), 194751); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Carlos 2010"), 164451); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Babalon 5"), 70726); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Babalon5"), 70726); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Genius"), 83714); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Genius With Dave Gormand"), 83714); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Come Fly With Me 2010"), 212571); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Border Security"), 81563); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Border Security Australias Frontline"), 81563); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Silent Library US"), 172381); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Sci-Fi Science"), 131791); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Frontline"), 80646); - SeriesIdLookupList.Add(Parser.NormalizeTitle("Frontline US"), 80646); - SeriesIdLookupList.Add(Parser.NormalizeTitle("RBT AU"), 189931); - SeriesIdLookupList.Add(Parser.NormalizeTitle("House"), 73255); - SeriesIdLookupList.Add(Parser.NormalizeTitle("House MD"), 73255); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Office"), 73244); - SeriesIdLookupList.Add(Parser.NormalizeTitle("The Office US"), 73244); - - //These values are used when doing an indexer search. - SceneNameLookupList.Add(72546, "CSI"); //CSI - SceneNameLookupList.Add(73696, "CSI"); //CSI NY - SceneNameLookupList.Add(110381, "Archer"); - SceneNameLookupList.Add(83897, "Life After People"); - SceneNameLookupList.Add(80552, "Kitchen Nightmares US"); - SceneNameLookupList.Add(71256, "The Daily Show"); //The Daily Show with Jon Stewart - SceneNameLookupList.Add(75692, "Law and Order"); //SVU - SceneNameLookupList.Add(71489, "Law and Order");//CI - SceneNameLookupList.Add(79590, "Dancing With The Stars US"); - SceneNameLookupList.Add(73387, "Craig Ferguson"); - SceneNameLookupList.Add(85355, "Jimmy Fallon"); - SceneNameLookupList.Add(75088, "David Letterman"); - SceneNameLookupList.Add(76706, "Big Brother US"); - SceneNameLookupList.Add(105521, "The Colony"); - SceneNameLookupList.Add(76235, "Americas Funniest Home Videos"); - SceneNameLookupList.Add(139941, "Childrens Hospital"); - SceneNameLookupList.Add(83123, "Merlin"); - SceneNameLookupList.Add(76779, "WWE Monday Night RAW"); - SceneNameLookupList.Add(164951, "Shit My Dad Says"); - SceneNameLookupList.Add(168161, "Law and Order LA"); - SceneNameLookupList.Add(77526, "Star Trek TOS"); - SceneNameLookupList.Add(72073, "Star Trek DS9"); - SceneNameLookupList.Add(72194, "Ellen Degeneres"); - SceneNameLookupList.Add(195831, "Drinking Made Easy");//Zane Lampreys Drinking Made Easy - SceneNameLookupList.Add(76133, "Poirot"); //Agatha Christies Poirot - SceneNameLookupList.Add(70870, "The Real World Road Rules Challenge"); - SceneNameLookupList.Add(77444, "This Old House Program"); - SceneNameLookupList.Add(73290, "60 Minutes US"); - SceneNameLookupList.Add(194751, "Conan"); - SceneNameLookupList.Add(164451, "Carlos 2010"); - SceneNameLookupList.Add(70726, "Babalon"); //5 - SceneNameLookupList.Add(83714, "Genius"); //Genius With Dave Gormand - SceneNameLookupList.Add(212571, "Come Fly With Me 2010"); - SceneNameLookupList.Add(81563, "Border Security"); - SceneNameLookupList.Add(172381, "Silent Library US"); - SceneNameLookupList.Add(131791, "Sci-Fi Science"); - SceneNameLookupList.Add(80646, "Frontline"); - SceneNameLookupList.Add(189931, "RBT AU"); - SceneNameLookupList.Add(73255, "House"); - SceneNameLookupList.Add(73244, "The Office"); - } - - - public static Nullable<Int32> GetIdByName(string cleanSeriesName) - { - int id; - - if (SeriesIdLookupList.TryGetValue(Parser.NormalizeTitle(cleanSeriesName), out id)) - { - return id; - } - - return null; - } - - public static String GetTitleById(int seriesId) - { - string title; - - if (SceneNameLookupList.TryGetValue(seriesId, out title)) - { - return title; - } - - return null; - } - } -} \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index daee93be9..bb5169eb0 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -168,7 +168,6 @@ <Compile Include="Helpers\EpisodeRenameHelper.cs" /> <Compile Include="Helpers\EpisodeSortingHelper.cs" /> <Compile Include="Helpers\FileSizeFormatHelpercs.cs" /> - <Compile Include="Helpers\SceneNameHelper.cs" /> <Compile Include="Instrumentation\LogProvider.cs" /> <Compile Include="Instrumentation\SubsonicTarget.cs" /> <Compile Include="Instrumentation\ExceptioneerTarget.cs" /> @@ -185,6 +184,7 @@ <Compile Include="Providers\Indexer\SyndicationFeedXmlReader.cs" /> <Compile Include="Providers\AutoConfigureProvider.cs" /> <Compile Include="Providers\Indexer\NzbMatrix.cs" /> + <Compile Include="Providers\Jobs\UpdateSceneMappingsJob.cs" /> <Compile Include="Providers\Jobs\PostDownloadScanJob.cs" /> <Compile Include="Providers\Jobs\RenameEpisodeJob.cs" /> <Compile Include="Providers\Jobs\EpisodeSearchJob.cs" /> @@ -197,6 +197,7 @@ <Compile Include="Providers\Jobs\IJob.cs" /> <Compile Include="Providers\Jobs\RssSyncJob.cs" /> <Compile Include="Providers\Jobs\UpdateInfoJob.cs" /> + <Compile Include="Providers\SceneNameMappingProvider.cs" /> <Compile Include="Providers\StatsProvider.cs" /> <Compile Include="Repository\ExternalNotificationSetting.cs" /> <Compile Include="Repository\JobSetting.cs" /> @@ -241,6 +242,7 @@ <Compile Include="Repository\Quality\QualityProfile.cs" /> <Compile Include="Repository\RootDir.cs" /> <Compile Include="Repository\Quality\QualityTypes.cs" /> + <Compile Include="Repository\SceneNameMapping.cs" /> <Compile Include="Repository\Series.cs" /> <Compile Include="CentralDispatch.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> diff --git a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs index bbb151fa5..a2696423f 100644 --- a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs +++ b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs @@ -16,16 +16,19 @@ namespace NzbDrone.Core.Providers.Jobs private readonly DownloadProvider _downloadProvider; private readonly IndexerProvider _indexerProvider; private readonly EpisodeProvider _episodeProvider; - + private readonly SceneNameMappingProvider _sceneNameMappingProvider; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - public EpisodeSearchJob(InventoryProvider inventoryProvider, DownloadProvider downloadProvider, IndexerProvider indexerProvider, EpisodeProvider episodeProvider) + public EpisodeSearchJob(InventoryProvider inventoryProvider, DownloadProvider downloadProvider, + IndexerProvider indexerProvider, EpisodeProvider episodeProvider, + SceneNameMappingProvider sceneNameMappingProvider) { _inventoryProvider = inventoryProvider; _downloadProvider = downloadProvider; _indexerProvider = indexerProvider; _episodeProvider = episodeProvider; + _sceneNameMappingProvider = sceneNameMappingProvider; } public string Name @@ -56,7 +59,7 @@ namespace NzbDrone.Core.Providers.Jobs var indexers = _indexerProvider.GetEnabledIndexers(); var reports = new List<EpisodeParseResult>(); - var title = SceneNameHelper.GetTitleById(series.SeriesId); + var title = _sceneNameMappingProvider.GetSceneName(series.SeriesId); if(string.IsNullOrWhiteSpace(title)) { diff --git a/NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs b/NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs new file mode 100644 index 000000000..ad517287e --- /dev/null +++ b/NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs @@ -0,0 +1,42 @@ +using System.Linq; +using System.Collections.Generic; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Providers.Jobs +{ + public class UpdateSceneMappingsJob : IJob + { + private readonly SceneNameMappingProvider _sceneNameMappingProvider; + + public UpdateSceneMappingsJob(SceneNameMappingProvider sceneNameMappingProvider) + { + _sceneNameMappingProvider = sceneNameMappingProvider; + } + + public UpdateSceneMappingsJob() + { + + } + + public string Name + { + get { return "Update Scene Mappings"; } + } + + public int DefaultInterval + { + get { return 720; } //Every 12 hours + } + + public virtual void Start(ProgressNotification notification, int targetId) + { + notification.CurrentMessage = "Updating Scene Mappings"; + if (_sceneNameMappingProvider.UpdateMappings()) + notification.CurrentMessage = "Scene Mappings Completed"; + + else + notification.CurrentMessage = "Scene Mappings Failed"; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/SceneNameMappingProvider.cs b/NzbDrone.Core/Providers/SceneNameMappingProvider.cs new file mode 100644 index 000000000..f40aa1e9b --- /dev/null +++ b/NzbDrone.Core/Providers/SceneNameMappingProvider.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using NLog; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; +using SubSonic.Repository; + +namespace NzbDrone.Core.Providers +{ + public class SceneNameMappingProvider + { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private readonly IRepository _repository; + private readonly HttpProvider _httpProvider; + + public SceneNameMappingProvider(IRepository repository, HttpProvider httpProvider) + { + _repository = repository; + _httpProvider = httpProvider; + } + + public virtual bool UpdateMappings() + { + try + { + var mapping = _httpProvider.DownloadString("http://vps.nzbdrone.com/SceneNameMappings.csv"); + var newMaps = new List<SceneNameMapping>(); + + using (var reader = new StringReader(mapping)) + { + string line; + while ((line = reader.ReadLine()) != null) + { + var split = line.Split(','); + var seriesId = 0; + Int32.TryParse(split[1], out seriesId); + + var map = new SceneNameMapping(); + map.SceneCleanName = split[0]; + map.SeriesId = seriesId; + map.SceneName = split[2]; + + newMaps.Add(map); + } + } + + Logger.Debug("Deleting all existing Scene Mappings."); + _repository.DeleteMany<SceneNameMapping>(GetAll()); + + Logger.Debug("Adding Scene Mappings"); + _repository.AddMany(newMaps); + } + + catch (Exception ex) + { + Logger.InfoException("Failed to Update Scene Mappings", ex); + return false; + } + return true; + } + + public virtual List<SceneNameMapping> GetAll() + { + return _repository.All<SceneNameMapping>().ToList(); + } + + public virtual string GetSceneName(int seriesId) + { + var item = _repository.Single<SceneNameMapping>(s => s.SeriesId == seriesId); + + if (item == null) + return null; + + return item.SceneName; + } + + public virtual Nullable<Int32> GetSeriesId(string cleanName) + { + var item = _repository.Single<SceneNameMapping>(s => s.SceneCleanName == cleanName); + + if (item == null) + return null; + + return item.SeriesId; + } + } +} diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index c164cb32e..79fe43d26 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -22,12 +22,15 @@ namespace NzbDrone.Core.Providers private readonly IRepository _repository; private readonly ConfigProvider _configProvider; private readonly TvDbProvider _tvDbProvider; + private readonly SceneNameMappingProvider _sceneNameMappingProvider; - public SeriesProvider(ConfigProvider configProviderProvider, IRepository repository, TvDbProvider tvDbProviderProvider) + public SeriesProvider(ConfigProvider configProviderProvider, IRepository repository, + TvDbProvider tvDbProviderProvider, SceneNameMappingProvider sceneNameMappingProvider) { _configProvider = configProviderProvider; _repository = repository; _tvDbProvider = tvDbProviderProvider; + _sceneNameMappingProvider = sceneNameMappingProvider; } public SeriesProvider() @@ -105,7 +108,7 @@ namespace NzbDrone.Core.Providers { var normalizeTitle = Parser.NormalizeTitle(title); - var seriesId = SceneNameHelper.GetIdByName(normalizeTitle); + var seriesId = _sceneNameMappingProvider.GetSeriesId(normalizeTitle); if (seriesId != null) { return GetSeries(seriesId.Value); diff --git a/NzbDrone.Core/Repository/SceneNameMapping.cs b/NzbDrone.Core/Repository/SceneNameMapping.cs new file mode 100644 index 000000000..b6338bfeb --- /dev/null +++ b/NzbDrone.Core/Repository/SceneNameMapping.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using SubSonic.SqlGeneration.Schema; + +namespace NzbDrone.Core.Repository +{ + public class SceneNameMapping + { + [SubSonicPrimaryKey] + public virtual string SceneCleanName { get; set; } + + public virtual int SeriesId { get; set; } + + public virtual string SceneName { get; set; } + } +} diff --git a/SceneNameMappings.csv b/SceneNameMappings.csv new file mode 100644 index 000000000..0da6a367d --- /dev/null +++ b/SceneNameMappings.csv @@ -0,0 +1,58 @@ +csinewyork,73696,CSI +csiny,73696,CSI +archer,110381,Archer +lifeafterpeopleseries,83897,Life After People +lifeafterpeople,83897,Life After People +kitchennightmaresus,80552,Kitchen Nightmares US +dailyshow,71256,The Daily Show +dailyshowwithjonstewart,71256,The Daily Show +lawordersvu,75692,Law and Order SVU +laworderspecialvictimsunit,75692,Law and Order +lawordercriminalintent,71489,Law and Order +laworderci,71489,Law and Order +dancingwithstarsus,79590,Dancing With The Stars +craigferguson,73387,Craig Ferguson +jimmyfallon,85355,Jimmy Fallon +davidletterman,75088,David Letterman +bigbrotherus,76706,Big Brother +colony,105521,The Colony +colonyus,105521,The Colony +americasfunniesthomevideos,76235,Americas Funniest Home Videos +afhv,76235,Americas Funniest Home Videos +childrenshospitalus,139941,Childrens Hospital +childrenshospital,139941,Childrens Hospital +merlin,83123,Merlin +merlin2008,83123,Merlin +wwemondaynightraw,76779,WWE Monday Night RAW +shitmydadsays,164951,Shit My Dad Says +geniuswithdavegorman,83714,Genius with Dave Gorman +laworderla,168161,Law and Order +startrektos,77526,Star Trek TOS +startrekds,72073,Star Trek DS9 +ellendegeneres,72194,Ellen Degeneres +drinkingmadeeasy,195831,Drinking Made Easy +zanelampreysdrinkingmadeeasy,195831,Drinking Made Easy +poirot,76133,Poirot +agathachristiespoirot,76133,Poirot +realworldroadruleschallenge,70870,The Real World Road Rules Challenge +challengecutthroat,70870,The Challenge Cutthroat +thisoldhouseprogram,77444,This Old House Program +minutesus,73290,60 Minutes +conan,194751,Conan +conan2010,194751,Conan +carlos2010,164451,Carlos 2010 +babalon,70726,Babalon +genius,83714,Genius +geniuswithdavegormand,83714,Genius With Dave Gormand +comeflywithme2010,212571,Come Fly With Me 2010 +bordersecurity,81563,Border Security +bordersecurityaustraliasfrontline,81563,Border Security Australias Frontline +silentlibraryus,172381,Silent Library US +scifiscience,131791,Sci Fi Science +frontline,80646,Frontline +frontlineus,80646,Frontline +rbtau,189931,RBT AU +house,73255,House +housemd,73255,House +office,73244,The Office +officeus,73244,The Office \ No newline at end of file From e934e71b3b7c85327c4f6cb8b27a406f6a076d6c Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Mon, 13 Jun 2011 22:52:12 -0700 Subject: [PATCH 3/8] Fixed broken tests after new SceneName method. --- NzbDrone.Core.Test/EpisodeSearchJobTest.cs | 11 ++++++++++- NzbDrone.Core.Test/SeriesProviderTest.cs | Bin 6325 -> 6329 bytes NzbDrone.Core/Parser.cs | 2 +- .../Providers/SceneNameMappingProvider.cs | 5 +++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/NzbDrone.Core.Test/EpisodeSearchJobTest.cs b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs index 1faab1de6..1e7be7186 100644 --- a/NzbDrone.Core.Test/EpisodeSearchJobTest.cs +++ b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs @@ -247,6 +247,9 @@ namespace NzbDrone.Core.Test mocker.GetMock<InventoryProvider>() .Setup(c => c.IsQualityNeeded(It.Is<EpisodeParseResult>(d => d.Series != null && d.Episodes.Count != 0))).Returns(false); + mocker.GetMock<SceneNameMappingProvider>() + .Setup(s => s.GetSceneName(It.IsAny<int>())).Returns(""); + //Act mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId); @@ -295,6 +298,9 @@ namespace NzbDrone.Core.Test mocker.GetMock<InventoryProvider>() .Setup(c => c.IsQualityNeeded(It.Is<EpisodeParseResult>(d => d.Series != null && d.Episodes.Count != 0))).Returns(false); + mocker.GetMock<SceneNameMappingProvider>() + .Setup(s => s.GetSceneName(71256)).Returns("The Daily Show"); + //Act mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId); @@ -347,7 +353,10 @@ namespace NzbDrone.Core.Test .Returns(indexers); mocker.GetMock<InventoryProvider>() - .Setup(c => c.IsQualityNeeded(It.Is<EpisodeParseResult>(d => d.Series != null && d.Episodes.Count != 0))).Returns(false); + .Setup(c => c.IsQualityNeeded(It.Is<EpisodeParseResult>(d => d.Series != null && d.Episodes.Count != 0))).Returns(false);; + + mocker.GetMock<SceneNameMappingProvider>() + .Setup(s => s.GetSceneName(It.IsAny<int>())).Returns(""); //Act mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId); diff --git a/NzbDrone.Core.Test/SeriesProviderTest.cs b/NzbDrone.Core.Test/SeriesProviderTest.cs index bd5086dd611b37e33b25d5f056ac2bb5f63a3dd9..8adc1c23492bd4cb7bd9b957286c8a8d8dda7fe6 100644 GIT binary patch delta 12 TcmdmLxYKY0JI7{bj!gmp9*zVM delta 16 YcmdmKxYck2JICZXf;^kWITi^305i}9U;qFB diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs index 0b561f017..bf259a852 100644 --- a/NzbDrone.Core/Parser.cs +++ b/NzbDrone.Core/Parser.cs @@ -46,7 +46,7 @@ namespace NzbDrone.Core private static readonly Regex NormalizeRegex = new Regex(@"((^|\W)(a|an|the|and|or|of)($|\W))|\W|(?:(?<=[^0-9]+)|\b)(?!(?:19\d{2}|20\d{2}))\d+(?=[^0-9ip]+|\b)", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex SimpleTitleRegex = new Regex(@"480[i|p]|720[i|p]|1080[i|p]|[x|h]264|\\|\/|\<|\>|\?|\*|\:|\|", + private static readonly Regex SimpleTitleRegex = new Regex(@"480[i|p]|720[i|p]|1080[i|p]|[x|h]264|\<|\>|\?|\*|\:|\|", RegexOptions.IgnoreCase | RegexOptions.Compiled); /// <summary> diff --git a/NzbDrone.Core/Providers/SceneNameMappingProvider.cs b/NzbDrone.Core/Providers/SceneNameMappingProvider.cs index f40aa1e9b..fd6869edd 100644 --- a/NzbDrone.Core/Providers/SceneNameMappingProvider.cs +++ b/NzbDrone.Core/Providers/SceneNameMappingProvider.cs @@ -23,6 +23,11 @@ namespace NzbDrone.Core.Providers _httpProvider = httpProvider; } + public SceneNameMappingProvider() + { + + } + public virtual bool UpdateMappings() { try From 3572855c34d24b1fe550ec82b49bf181b25955c2 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Tue, 14 Jun 2011 08:07:48 -0700 Subject: [PATCH 4/8] History view will no longer hit the DB with the same request so many times. --- NzbDrone.Web/Controllers/HistoryController.cs | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/NzbDrone.Web/Controllers/HistoryController.cs b/NzbDrone.Web/Controllers/HistoryController.cs index e824f7245..bfebae68b 100644 --- a/NzbDrone.Web/Controllers/HistoryController.cs +++ b/NzbDrone.Web/Controllers/HistoryController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using NzbDrone.Core.Model; @@ -44,20 +45,30 @@ namespace NzbDrone.Web.Controllers //TODO: possible subsonic bug, IQuarible causes some issues so ToList() is called //https://github.com/subsonic/SubSonic-3.0/issues/263 - var history = _historyProvider.AllItems().ToList().Select(h => new HistoryModel - { - HistoryId = h.HistoryId, - SeasonNumber = h.Episode.SeasonNumber, - EpisodeNumber = h.Episode.EpisodeNumber, - EpisodeTitle = h.Episode.Title, - EpisodeOverview = h.Episode.Overview, - SeriesTitle = h.Episode.Series.Title, - NzbTitle = h.NzbTitle, - Quality = h.Quality.ToString(), - IsProper = h.IsProper, - Date = h.Date, - Indexer = h.Indexer - }); + var historyDb = _historyProvider.AllItems().ToList(); + + var history = new List<HistoryModel>(); + + foreach (var item in historyDb) + { + var episode = item.Episode; + var series = episode.Series; + + history.Add(new HistoryModel + { + HistoryId = item.HistoryId, + SeasonNumber = episode.SeasonNumber, + EpisodeNumber = episode.EpisodeNumber, + EpisodeTitle = episode.Title, + EpisodeOverview = episode.Overview, + SeriesTitle = series.Title, + NzbTitle = item.NzbTitle, + Quality = item.Quality.ToString(), + IsProper = item.IsProper, + Date = item.Date, + Indexer = item.Indexer + }); + } return View(new GridModel(history)); } From c8ae3de2925dca4b88d5b303e7b3a8963080eec7 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Tue, 14 Jun 2011 08:14:33 -0700 Subject: [PATCH 5/8] Upcoming view will no longer hit the DB with the same request so many times. --- .../Controllers/UpcomingController.cs | 116 ++++++++++++------ 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/NzbDrone.Web/Controllers/UpcomingController.cs b/NzbDrone.Web/Controllers/UpcomingController.cs index 17e5438bd..a214e3ebf 100644 --- a/NzbDrone.Web/Controllers/UpcomingController.cs +++ b/NzbDrone.Web/Controllers/UpcomingController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Web.Mvc; using NzbDrone.Core.Providers; @@ -27,17 +28,25 @@ namespace NzbDrone.Web.Controllers [GridAction] public ActionResult _AjaxBindingYesterday() { - var upcoming = _upcomingEpisodesProvider.Yesterday().Select(e => new UpcomingEpisodeModel - { - SeriesId = e.Series.SeriesId, - EpisodeId = e.EpisodeId, - SeriesName = e.Series.Title, - SeasonNumber = e.SeasonNumber, - EpisodeNumber = e.EpisodeNumber, - Title = e.Title, - Overview = e.Overview, - AirDate = e.AirDate.Add(Convert.ToDateTime(e.Series.AirTimes).TimeOfDay) - }); + var upcomingDb = _upcomingEpisodesProvider.Yesterday(); + var upcoming = new List<UpcomingEpisodeModel>(); + + foreach (var item in upcomingDb) + { + var series = item.Series; + + upcoming.Add(new UpcomingEpisodeModel + { + SeriesId = series.SeriesId, + EpisodeId = item.EpisodeId, + SeriesName = series.Title, + SeasonNumber = item.SeasonNumber, + EpisodeNumber = item.EpisodeNumber, + Title = item.Title, + Overview = item.Overview, + AirDate = item.AirDate.Add(Convert.ToDateTime(series.AirTimes).TimeOfDay) + }); + } return View(new GridModel(upcoming)); } @@ -45,16 +54,25 @@ namespace NzbDrone.Web.Controllers [GridAction] public ActionResult _AjaxBindingToday() { - var upcoming = _upcomingEpisodesProvider.Today().Select(e => new UpcomingEpisodeModel - { - SeriesId = e.Series.SeriesId, - SeriesName = e.Series.Title, - SeasonNumber = e.SeasonNumber, - EpisodeNumber = e.EpisodeNumber, - Title = e.Title, - Overview = e.Overview, - AirDate = e.AirDate.Add(Convert.ToDateTime(e.Series.AirTimes).TimeOfDay) - }); + var upcomingDb = _upcomingEpisodesProvider.Today(); + var upcoming = new List<UpcomingEpisodeModel>(); + + foreach (var item in upcomingDb) + { + var series = item.Series; + + upcoming.Add(new UpcomingEpisodeModel + { + SeriesId = series.SeriesId, + EpisodeId = item.EpisodeId, + SeriesName = series.Title, + SeasonNumber = item.SeasonNumber, + EpisodeNumber = item.EpisodeNumber, + Title = item.Title, + Overview = item.Overview, + AirDate = item.AirDate.Add(Convert.ToDateTime(series.AirTimes).TimeOfDay) + }); + } return View(new GridModel(upcoming)); } @@ -62,16 +80,25 @@ namespace NzbDrone.Web.Controllers [GridAction] public ActionResult _AjaxBindingTomorrow() { - var upcoming = _upcomingEpisodesProvider.Tomorrow().Select(e => new UpcomingEpisodeModel + var upcomingDb = _upcomingEpisodesProvider.Tomorrow(); + var upcoming = new List<UpcomingEpisodeModel>(); + + foreach (var item in upcomingDb) { - SeriesId = e.Series.SeriesId, - SeriesName = e.Series.Title, - SeasonNumber = e.SeasonNumber, - EpisodeNumber = e.EpisodeNumber, - Title = e.Title, - Overview = e.Overview, - AirDate = e.AirDate.Add(Convert.ToDateTime(e.Series.AirTimes).TimeOfDay) - }); + var series = item.Series; + + upcoming.Add(new UpcomingEpisodeModel + { + SeriesId = series.SeriesId, + EpisodeId = item.EpisodeId, + SeriesName = series.Title, + SeasonNumber = item.SeasonNumber, + EpisodeNumber = item.EpisodeNumber, + Title = item.Title, + Overview = item.Overview, + AirDate = item.AirDate.Add(Convert.ToDateTime(series.AirTimes).TimeOfDay) + }); + } return View(new GridModel(upcoming)); } @@ -79,16 +106,25 @@ namespace NzbDrone.Web.Controllers [GridAction] public ActionResult _AjaxBindingWeek() { - var upcoming = _upcomingEpisodesProvider.Week().Select(e => new UpcomingEpisodeModel - { - SeriesId = e.Series.SeriesId, - SeriesName = e.Series.Title, - SeasonNumber = e.SeasonNumber, - EpisodeNumber = e.EpisodeNumber, - Title = e.Title, - Overview = e.Overview, - AirDate = e.AirDate.Add(Convert.ToDateTime(e.Series.AirTimes).TimeOfDay) - }); + var upcomingDb = _upcomingEpisodesProvider.Week(); + var upcoming = new List<UpcomingEpisodeModel>(); + + foreach (var item in upcomingDb) + { + var series = item.Series; + + upcoming.Add(new UpcomingEpisodeModel + { + SeriesId = series.SeriesId, + EpisodeId = item.EpisodeId, + SeriesName = series.Title, + SeasonNumber = item.SeasonNumber, + EpisodeNumber = item.EpisodeNumber, + Title = item.Title, + Overview = item.Overview, + AirDate = item.AirDate.Add(Convert.ToDateTime(series.AirTimes).TimeOfDay) + }); + } return View(new GridModel(upcoming)); } From 1fbf9a14165819c45c11690ee9014927ef438c91 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Tue, 14 Jun 2011 16:39:09 -0700 Subject: [PATCH 6/8] Using .ToList() to get around IEnumerable multi-DB calls and possible SubSonic bug with IQueryable. --- NzbDrone.Core/Providers/MediaFileProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index 7a6bbf762..325fdfc2b 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -205,12 +205,12 @@ namespace NzbDrone.Core.Providers public virtual Tuple<int, int> GetEpisodeFilesCount(int seriesId) { - var allEpisodes = _episodeProvider.GetEpisodeBySeries(seriesId); + var allEpisodes = _episodeProvider.GetEpisodeBySeries(seriesId).ToList(); - var episodeTotal = allEpisodes.Where(e => !e.Ignored && e.AirDate <= DateTime.Today && e.AirDate.Year > 1900); - var avilableEpisodes = episodeTotal.Where(e => e.EpisodeFileId > 0); + var episodeTotal = allEpisodes.Where(e => !e.Ignored && e.AirDate <= DateTime.Today && e.AirDate.Year > 1900).ToList(); + var avilableEpisodes = episodeTotal.Where(e => e.EpisodeFileId > 0).ToList(); - return new Tuple<int, int>(avilableEpisodes.Count(), episodeTotal.Count()); + return new Tuple<int, int>(avilableEpisodes.Count, episodeTotal.Count); } private List<string> GetMediaFileList(string path) From c3d4732baa16ee2e17047b2d3aa0695041d2624d Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Wed, 15 Jun 2011 19:32:55 -0700 Subject: [PATCH 7/8] Adding a root dir will not add it to the DB until after it is saved, also will not save if the path is blank. --- .../Controllers/AddSeriesController.cs | 38 ++++++++++++------- NzbDrone.Web/Views/AddSeries/Index.cshtml | 19 ++++++---- NzbDrone.Web/Views/AddSeries/RootDir.cshtml | 10 ++--- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/NzbDrone.Web/Controllers/AddSeriesController.cs b/NzbDrone.Web/Controllers/AddSeriesController.cs index 74d1f96d6..4737ebca7 100644 --- a/NzbDrone.Web/Controllers/AddSeriesController.cs +++ b/NzbDrone.Web/Controllers/AddSeriesController.cs @@ -187,9 +187,16 @@ namespace NzbDrone.Web.Controllers [HttpPost] public JsonResult SaveRootDir(int id, string path) { + if (String.IsNullOrWhiteSpace(path)) + return new JsonResult { Data = "failed" }; + try { - _rootFolderProvider.Update(new RootDir { Id = id, Path = path }); + if (id == 0) + id = _rootFolderProvider.Add(new RootDir { Path = path }); + + else + _rootFolderProvider.Update(new RootDir { Id = id, Path = path }); } catch (Exception ex) { @@ -199,30 +206,33 @@ namespace NzbDrone.Web.Controllers return new JsonResult { Data = "failed" }; } - return new JsonResult { Data = "ok" }; + return new JsonResult { Data = id }; } public PartialViewResult AddRootDir() { - var rootDir = new RootDir { Path = String.Empty }; + var model = new RootDirModel + { + Id = 0, + Path = "", + SelectList = new SelectList(new List<string> { "" }, "") + }; - var id = _rootFolderProvider.Add(rootDir); - rootDir.Id = id; - - var model = new RootDirModel(); - model.Id = rootDir.Id; - model.Path = rootDir.Path; - model.SelectList = new SelectList(new List<string> { rootDir.Path }, rootDir.Path); + ViewData["guid"] = Guid.NewGuid(); return PartialView("RootDir", model); } public ActionResult GetRootDirView(RootDir rootDir) { - var model = new RootDirModel(); - model.Id = rootDir.Id; - model.Path = rootDir.Path; - model.SelectList = new SelectList(new List<string> { rootDir.Path }, rootDir.Path); + var model = new RootDirModel + { + Id = rootDir.Id, + Path = rootDir.Path, + SelectList = new SelectList(new List<string> { rootDir.Path }, rootDir.Path) + }; + + ViewData["guid"] = Guid.NewGuid(); return PartialView("RootDir", model); } diff --git a/NzbDrone.Web/Views/AddSeries/Index.cshtml b/NzbDrone.Web/Views/AddSeries/Index.cshtml index ce1e3e29e..e47a1fe4e 100644 --- a/NzbDrone.Web/Views/AddSeries/Index.cshtml +++ b/NzbDrone.Web/Views/AddSeries/Index.cshtml @@ -132,11 +132,12 @@ var deleteRootDirUrl = '@Url.Action("DeleteRootDir", "AddSeries")'; - function deleteRootDir(id) { - sendDeleteToServer(id); + function deleteRootDir(guid) { + var id = $('#id_' + guid).val(); + sendDeleteToServer(id, guid); } - function sendDeleteToServer(id) { + function sendDeleteToServer(id, guid) { $.ajax({ type: "POST", url: deleteRootDirUrl, @@ -145,15 +146,16 @@ alert("Sorry! We could not delete your Root Directory at this time. " + error); }, success: function () { - $("#rootDir_" + id).remove(); + $("#rootDir_" + guid).remove(); } }); } var saveRootDirUrl = '@Url.Action("SaveRootDir", "AddSeries")'; - function saveRootDir(id) { - var path = $("#path_" + id).data("tComboBox").value(); + function saveRootDir(guid) { + var path = $("#path_" + guid).data("tComboBox").value(); + var id = $("#id_" + guid).val(); $.ajax({ type: "POST", @@ -163,8 +165,11 @@ alert("Sorry! We could not save " + path + " at this time. " + error); }, success: function (data, textStatus, jqXHR) { - if (data != 'ok') + if (data == 'failed') alert("An error occurred while saving Root Directory: " + path); + else { + $("#id_" + guid).val(data); + } } }); } diff --git a/NzbDrone.Web/Views/AddSeries/RootDir.cshtml b/NzbDrone.Web/Views/AddSeries/RootDir.cshtml index 618e70eae..0925819b3 100644 --- a/NzbDrone.Web/Views/AddSeries/RootDir.cshtml +++ b/NzbDrone.Web/Views/AddSeries/RootDir.cshtml @@ -4,10 +4,10 @@ Layout = null; } -<div class="rootDirSection" id="rootDir_@(Model.Id)" style="padding: 7px; padding-left: 3px;"> +<div class="rootDirSection" id="rootDir_@(ViewData["guid"])" style="padding: 7px; padding-left: 3px;"> <fieldset style="padding: 5px; height: 40px;"> @{Html.Telerik().ComboBox() - .Name("path_" + Model.Id) + .Name("path_" + ViewData["guid"]) .BindTo(Model.SelectList) .DataBinding(binding => binding.Ajax().Select("_autoCompletePath", "Directory").Delay(400).Cache(false)) .Filterable(f => f.FilterMode(AutoCompleteFilterMode.StartsWith)) @@ -16,10 +16,10 @@ .Render();} - <a href="#RemoveRootDir" class="deleteRow" onclick="deleteRootDir(@Model.Id); return false;"> + <a href="#RemoveRootDir" class="deleteRow" onclick="deleteRootDir('@ViewData["guid"]'); return false;"> <img src="../../Content/Images/X.png" alt="Delete" width="20px" height="20px" style="vertical-align: middle; margin-top: 7px;"/></a> - <button style="padding: 2px 10px 2px 10px; vertical-align: middle; margin: 0px; margin-top: 7px;" onclick="saveRootDir(@Model.Id)">Save</button> + <button style="padding: 2px 10px 2px 10px; vertical-align: middle; margin: 0px; margin-top: 7px;" onclick="saveRootDir('@ViewData["guid"]')">Save</button> - @Html.HiddenFor(x => x.Id, new { id = "id_" + Model.Id }) + @Html.HiddenFor(x => x.Id, new { id = "id_" + ViewData["guid"] }) </fieldset> </div> \ No newline at end of file From 93340f7d53b351fe46b701c6ac38aece4fbec342 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Wed, 15 Jun 2011 23:18:19 -0700 Subject: [PATCH 8/8] Added AJAX wheel for Refresh Root Dirs, so you can tell its working now. --- NzbDrone.Web/Views/AddSeries/Index.cshtml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Web/Views/AddSeries/Index.cshtml b/NzbDrone.Web/Views/AddSeries/Index.cshtml index e47a1fe4e..8fcff4512 100644 --- a/NzbDrone.Web/Views/AddSeries/Index.cshtml +++ b/NzbDrone.Web/Views/AddSeries/Index.cshtml @@ -59,6 +59,7 @@ } </div> <button onclick="reloadExistingSeries()" style="padding: 2px 10px 2px 10px; margin: 5px; margin-bottom: 10px;">Refresh Unmapped</button> + <span id="reloadAjax" style="display: none"><img src="../../Content/Images/ajax-loader.gif" width="22px" height="22px" style="margin-bottom: -6px;"/></span> </div> </text>); }).Render(); @@ -175,6 +176,10 @@ } function reloadExistingSeries() { - $('#existingSeries').load('@Url.Action("AddExisting", "AddSeries")'); + $('#reloadAjax').show(); + $('#existingSeries').load('@Url.Action("AddExisting", "AddSeries")', + function (responseText, textStatus, XMLHttpRequest) { + $('#reloadAjax').hide(); + }); } </script> \ No newline at end of file