diff --git a/NzbDrone.Core.Test/DiskScanJobTest.cs b/NzbDrone.Core.Test/DiskScanJobTest.cs index 4c6636827..463b8a7ee 100644 --- a/NzbDrone.Core.Test/DiskScanJobTest.cs +++ b/NzbDrone.Core.Test/DiskScanJobTest.cs @@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test //Act - mocker.Resolve().Start(new ProgressNotification("Test"), series.SeriesId); + mocker.Resolve().Start(new ProgressNotification("Test"), series.SeriesId, 0); //Assert mocker.VerifyAllMocks(); @@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test .Setup(s => s.Scan(series[1])) .Returns(new List()); - mocker.Resolve().Start(new ProgressNotification("Test"), 0); + mocker.Resolve().Start(new ProgressNotification("Test"), 0, 0); mocker.VerifyAllMocks(); @@ -94,7 +94,7 @@ namespace NzbDrone.Core.Test .Setup(s => s.Scan(series[1])) .Throws(new InvalidOperationException("Bad Job")); - mocker.Resolve().Start(new ProgressNotification("Test"), 0); + mocker.Resolve().Start(new ProgressNotification("Test"), 0, 0); mocker.VerifyAllMocks(); @@ -123,7 +123,7 @@ namespace NzbDrone.Core.Test .Setup(s => s.Scan(series[1])) .Returns(new List()); - mocker.Resolve().Start(new ProgressNotification("Test"), 0); + mocker.Resolve().Start(new ProgressNotification("Test"), 0, 0); diff --git a/NzbDrone.Core.Test/EpisodeSearchJobTest.cs b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs index 0e34f0a64..676231688 100644 --- a/NzbDrone.Core.Test/EpisodeSearchJobTest.cs +++ b/NzbDrone.Core.Test/EpisodeSearchJobTest.cs @@ -207,7 +207,7 @@ namespace NzbDrone.Core.Test public void start_target_id_less_than_0_throws_exception(int target) { var mocker = new AutoMoqer(MockBehavior.Strict); - mocker.Resolve().Start(new ProgressNotification("Test"), target); + mocker.Resolve().Start(new ProgressNotification("Test"), target, 0); } @@ -251,7 +251,7 @@ namespace NzbDrone.Core.Test .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); //Act - mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId); + mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId, 0); //Assert @@ -302,7 +302,7 @@ namespace NzbDrone.Core.Test .Setup(s => s.GetSceneName(71256)).Returns("The Daily Show"); //Act - mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId); + mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId, 0); //Assert @@ -359,7 +359,7 @@ namespace NzbDrone.Core.Test .Setup(s => s.GetSceneName(It.IsAny())).Returns(""); //Act - mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId); + mocker.Resolve().Start(new ProgressNotification("Test"), episode.EpisodeId, 0); //Assert @@ -385,7 +385,7 @@ namespace NzbDrone.Core.Test .Returns(null); //Act - mocker.Resolve().Start(new ProgressNotification("Test"), 12); + mocker.Resolve().Start(new ProgressNotification("Test"), 12, 0); //Assert diff --git a/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs b/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs index 0a0fe7597..c1432c486 100644 --- a/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs +++ b/NzbDrone.Core.Test/ImportNewSeriesJobTest.cs @@ -36,21 +36,21 @@ namespace NzbDrone.Core.Test mocker.GetMock() - .Setup(j => j.Start(notification, series[0].SeriesId)) + .Setup(j => j.Start(notification, series[0].SeriesId, 0)) .Callback(() => series[0].LastDiskSync = DateTime.Now); mocker.GetMock() - .Setup(j => j.Start(notification, series[1].SeriesId)) + .Setup(j => j.Start(notification, series[1].SeriesId, 0)) .Callback(() => series[1].LastDiskSync = DateTime.Now); mocker.GetMock() - .Setup(j => j.Start(notification, series[0].SeriesId)) + .Setup(j => j.Start(notification, series[0].SeriesId, 0)) .Callback(() => series[0].LastInfoSync = DateTime.Now); mocker.GetMock() - .Setup(j => j.Start(notification, series[1].SeriesId)) + .Setup(j => j.Start(notification, series[1].SeriesId, 0)) .Callback(() => series[1].LastInfoSync = DateTime.Now); mocker.GetMock() @@ -63,16 +63,16 @@ namespace NzbDrone.Core.Test .Setup(s => s.GetSeriesFiles(It.IsAny())).Returns(new List()); //Act - mocker.Resolve().Start(notification, 0); + mocker.Resolve().Start(notification, 0, 0); //Assert mocker.VerifyAllMocks(); - mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once()); - mocker.GetMock().Verify(j => j.Start(notification, series[1].SeriesId), Times.Once()); + mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once()); + mocker.GetMock().Verify(j => j.Start(notification, series[1].SeriesId, 0), Times.Once()); - mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once()); - mocker.GetMock().Verify(j => j.Start(notification, series[1].SeriesId), Times.Once()); + mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once()); + mocker.GetMock().Verify(j => j.Start(notification, series[1].SeriesId, 0), Times.Once()); } @@ -98,15 +98,15 @@ namespace NzbDrone.Core.Test .Returns(series); mocker.GetMock() - .Setup(j => j.Start(notification, series[0].SeriesId)) + .Setup(j => j.Start(notification, series[0].SeriesId, 0)) .Callback(() => series[0].LastInfoSync = DateTime.Now); mocker.GetMock() - .Setup(j => j.Start(notification, series[1].SeriesId)) + .Setup(j => j.Start(notification, series[1].SeriesId, 0)) .Throws(new InvalidOperationException()); mocker.GetMock() - .Setup(j => j.Start(notification, series[0].SeriesId)) + .Setup(j => j.Start(notification, series[0].SeriesId, 0)) .Callback(() => series[0].LastDiskSync = DateTime.Now); @@ -117,15 +117,15 @@ namespace NzbDrone.Core.Test .Setup(s => s.GetSeriesFiles(It.IsAny())).Returns(new List()); //Act - mocker.Resolve().Start(notification, 0); + mocker.Resolve().Start(notification, 0, 0); //Assert mocker.VerifyAllMocks(); - mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once()); - mocker.GetMock().Verify(j => j.Start(notification, series[1].SeriesId), Times.Once()); + mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once()); + mocker.GetMock().Verify(j => j.Start(notification, series[1].SeriesId, 0), Times.Once()); - mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once()); + mocker.GetMock().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once()); ExceptionVerification.ExcpectedErrors(1); diff --git a/NzbDrone.Core.Test/JobProviderTest.cs b/NzbDrone.Core.Test/JobProviderTest.cs index 81464dfe3..e09a6dae3 100644 --- a/NzbDrone.Core.Test/JobProviderTest.cs +++ b/NzbDrone.Core.Test/JobProviderTest.cs @@ -7,6 +7,7 @@ using AutoMoq; using FluentAssertions; using Moq; using NUnit.Framework; +using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers.Jobs; using NzbDrone.Core.Test.Framework; @@ -84,7 +85,7 @@ namespace NzbDrone.Core.Test timerProvider.QueueScheduled(); Thread.Sleep(500); - fakeJob.ExexutionCount.Should().Be(1); + fakeJob.ExecutionCount.Should().Be(1); } [Test] @@ -106,7 +107,7 @@ namespace NzbDrone.Core.Test timerProvider.QueueJob(typeof(FakeJob)); Thread.Sleep(1000); JobProvider.Queue.Should().BeEmpty(); - fakeJob.ExexutionCount.Should().Be(2); + fakeJob.ExecutionCount.Should().Be(2); } [Test] @@ -154,7 +155,7 @@ namespace NzbDrone.Core.Test Thread.Sleep(2000); JobProvider.Queue.Should().BeEmpty(); - brokenJob.ExexutionCount.Should().Be(2); + brokenJob.ExecutionCount.Should().Be(2); ExceptionVerification.ExcpectedErrors(2); } @@ -184,7 +185,7 @@ namespace NzbDrone.Core.Test thread2.Join(); - slowJob.ExexutionCount = 2; + slowJob.ExecutionCount = 2; } @@ -216,7 +217,7 @@ namespace NzbDrone.Core.Test Thread.Sleep(5000); - Assert.AreEqual(1, slowJob.ExexutionCount); + Assert.AreEqual(1, slowJob.ExecutionCount); JobProvider.Queue.Should().BeEmpty(); } @@ -352,7 +353,7 @@ namespace NzbDrone.Core.Test //Assert - Assert.AreEqual(0, disabledJob.ExexutionCount); + Assert.AreEqual(0, disabledJob.ExecutionCount); } [Test] @@ -411,7 +412,13 @@ namespace NzbDrone.Core.Test mocker.SetConstant(MockLib.GetEmptyDatabase()); mocker.SetConstant(fakeJobs); - var fakeQueueItem = new Tuple(fakeJob.GetType(), 12); + var fakeQueueItem = new JobQueueItem + { + JobType = fakeJob.GetType(), + TargetId = 12, + SecondaryTargetId = 0 + }; + //Act var jobProvider = mocker.Resolve(); jobProvider.Initialize(); @@ -420,7 +427,7 @@ namespace NzbDrone.Core.Test Thread.Sleep(1000); //Assert - fakeJob.ExexutionCount.Should().Be(1); + fakeJob.ExecutionCount.Should().Be(1); } @@ -449,8 +456,8 @@ namespace NzbDrone.Core.Test //Assert JobProvider.Queue.Should().BeEmpty(); - slowJob.ExexutionCount.Should().Be(1); - disabledJob.ExexutionCount.Should().Be(1); + slowJob.ExecutionCount.Should().Be(1); + disabledJob.ExecutionCount.Should().Be(1); } } @@ -466,11 +473,11 @@ namespace NzbDrone.Core.Test get { return 15; } } - public int ExexutionCount { get; set; } + public int ExecutionCount { get; set; } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { - ExexutionCount++; + ExecutionCount++; } } @@ -486,11 +493,11 @@ namespace NzbDrone.Core.Test get { return 0; } } - public int ExexutionCount { get; set; } + public int ExecutionCount { get; set; } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { - ExexutionCount++; + ExecutionCount++; } } @@ -506,11 +513,11 @@ namespace NzbDrone.Core.Test get { return 15; } } - public int ExexutionCount { get; set; } + public int ExecutionCount { get; set; } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { - ExexutionCount++; + ExecutionCount++; throw new ApplicationException("Broken job is broken"); } } @@ -527,13 +534,13 @@ namespace NzbDrone.Core.Test get { return 15; } } - public int ExexutionCount { get; set; } + public int ExecutionCount { get; set; } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { Console.WriteLine("Starting Job"); Thread.Sleep(2000); - ExexutionCount++; + ExecutionCount++; Console.WriteLine("Finishing Job"); } } diff --git a/NzbDrone.Core.Test/MediaFileProviderTests.cs b/NzbDrone.Core.Test/MediaFileProviderTests.cs index 979e423c0..71170266e 100644 --- a/NzbDrone.Core.Test/MediaFileProviderTests.cs +++ b/NzbDrone.Core.Test/MediaFileProviderTests.cs @@ -50,6 +50,35 @@ namespace NzbDrone.Core.Test result.Should().HaveSameCount(firstSeriesFiles); } + [Test] + public void get_season_files() + { + var firstSeriesFiles = Builder.CreateListOfSize(10) + .WhereAll() + .Have(s => s.SeriesId = 12) + .Have(s => s.SeasonNumber = 1) + .Build(); + + var secondSeriesFiles = Builder.CreateListOfSize(10) + .WhereAll() + .Have(s => s.SeriesId = 12) + .Have(s => s.SeasonNumber = 2) + .Build(); + + var mocker = new AutoMoqer(); + + var database = MockLib.GetEmptyDatabase(true); + + database.InsertMany(firstSeriesFiles); + database.InsertMany(secondSeriesFiles); + + mocker.SetConstant(database); + + var result = mocker.Resolve().GetSeasonFiles(12, 1); + + result.Should().HaveSameCount(firstSeriesFiles); + } + [Test] public void Scan_series_should_skip_series_with_no_episodes() { diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 97485f93a..bd1d1b24c 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -88,6 +88,7 @@ + diff --git a/NzbDrone.Core.Test/SeasonSearchJobTest.cs b/NzbDrone.Core.Test/SeasonSearchJobTest.cs new file mode 100644 index 000000000..499b645b6 --- /dev/null +++ b/NzbDrone.Core.Test/SeasonSearchJobTest.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using AutoMoq; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.Model; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers; +using NzbDrone.Core.Providers.Indexer; +using NzbDrone.Core.Providers.Jobs; +using NzbDrone.Core.Repository; +using NzbDrone.Core.Repository.Quality; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test +{ + [TestFixture] + // ReSharper disable InconsistentNaming + public class SeasonSearchJobTest : TestBase + { + [Test] + public void SeasonSearch_success() + { + var episodes = Builder.CreateListOfSize(5) + .WhereAll() + .Have(e => e.SeriesId = 1) + .Have(e => e.SeasonNumber = 1) + .Build(); + + var mocker = new AutoMoqer(MockBehavior.Strict); + + var notification = new ProgressNotification("Season Search"); + + mocker.GetMock() + .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes); + + mocker.GetMock() + .Setup(c => c.Start(notification, It.IsAny(), 0)).Verifiable(); + + //Act + mocker.Resolve().Start(notification, 1, 1); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.Start(notification, It.IsAny(), 0), + Times.Exactly(episodes.Count)); + } + + [Test] + public void SeasonSearch_no_episodes() + { + var mocker = new AutoMoqer(MockBehavior.Strict); + var notification = new ProgressNotification("Season Search"); + List nullList = null; + + mocker.GetMock() + .Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(nullList); + + //Act + mocker.Resolve().Start(notification, 1, 1); + + //Assert + mocker.VerifyAllMocks(); + mocker.GetMock().Verify(c => c.Start(notification, It.IsAny(), 0), + Times.Never()); + ExceptionVerification.ExcpectedWarns(1); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/CentralDispatch.cs b/NzbDrone.Core/CentralDispatch.cs index 993d96d13..873a25e5f 100644 --- a/NzbDrone.Core/CentralDispatch.cs +++ b/NzbDrone.Core/CentralDispatch.cs @@ -104,6 +104,8 @@ namespace NzbDrone.Core _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To().InSingletonScope(); _kernel.Bind().To().InSingletonScope(); + _kernel.Bind().To().InSingletonScope(); + _kernel.Bind().To().InSingletonScope(); _kernel.Get().Initialize(); _kernel.Get().StartTimer(30); diff --git a/NzbDrone.Core/Model/JobQueueItem.cs b/NzbDrone.Core/Model/JobQueueItem.cs new file mode 100644 index 000000000..71322190f --- /dev/null +++ b/NzbDrone.Core/Model/JobQueueItem.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.Model +{ + public class JobQueueItem : IEquatable + { + public Type JobType { get; set; } + public int TargetId { get; set; } + public int SecondaryTargetId { get; set; } + + public bool Equals(JobQueueItem other) + { + if (JobType == other.JobType && TargetId == other.TargetId + && SecondaryTargetId == other.SecondaryTargetId) + { + return true; + } + + return false; + } + } +} diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 20f03346f..3f1f8aec0 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -188,6 +188,7 @@ + @@ -196,6 +197,8 @@ + + diff --git a/NzbDrone.Core/Providers/Jobs/DeleteSeriesJob.cs b/NzbDrone.Core/Providers/Jobs/DeleteSeriesJob.cs index 98c322578..462e3a246 100644 --- a/NzbDrone.Core/Providers/Jobs/DeleteSeriesJob.cs +++ b/NzbDrone.Core/Providers/Jobs/DeleteSeriesJob.cs @@ -27,7 +27,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 0; } } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { DeleteSeries(notification, targetId); } diff --git a/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs b/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs index cc8b1a76d..237567d59 100644 --- a/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs +++ b/NzbDrone.Core/Providers/Jobs/DiskScanJob.cs @@ -35,7 +35,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 60; } } - public virtual void Start(ProgressNotification notification, int targetId) + public virtual void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { IList seriesToScan; if (targetId == 0) diff --git a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs index d011acc28..dac5a6d9a 100644 --- a/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs +++ b/NzbDrone.Core/Providers/Jobs/EpisodeSearchJob.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using NLog; +using Ninject; using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Repository; @@ -18,6 +19,7 @@ namespace NzbDrone.Core.Providers.Jobs private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + [Inject] public EpisodeSearchJob(InventoryProvider inventoryProvider, DownloadProvider downloadProvider, IndexerProvider indexerProvider, EpisodeProvider episodeProvider, SceneMappingProvider sceneNameMappingProvider) @@ -29,6 +31,11 @@ namespace NzbDrone.Core.Providers.Jobs _sceneNameMappingProvider = sceneNameMappingProvider; } + public EpisodeSearchJob() + { + + } + public string Name { get { return "Episode Search"; } @@ -39,7 +46,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 0; } } - public void Start(ProgressNotification notification, int targetId) + public virtual void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { if (targetId <= 0) throw new ArgumentOutOfRangeException("targetId"); diff --git a/NzbDrone.Core/Providers/Jobs/IJob.cs b/NzbDrone.Core/Providers/Jobs/IJob.cs index ac2f85e76..36bfa4763 100644 --- a/NzbDrone.Core/Providers/Jobs/IJob.cs +++ b/NzbDrone.Core/Providers/Jobs/IJob.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.Providers.Jobs /// /// Name of the timer. /// This is the name that will be visible in all UI elements - /// + /// \\\ string Name { get; } @@ -25,6 +25,7 @@ namespace NzbDrone.Core.Providers.Jobs /// Notification object that is passed in by JobProvider. /// this object should be used to update the progress on the UI /// The that should be used to limit the target of this job - void Start(ProgressNotification notification, int targetId); + /// /// The that should be used to limit the target of this job + void Start(ProgressNotification notification, int targetId, int secondaryTargetId); } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs b/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs index 6da507c41..e9a0f333a 100644 --- a/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs +++ b/NzbDrone.Core/Providers/Jobs/ImportNewSeriesJob.cs @@ -46,7 +46,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 1; } } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId ) { _attemptedSeries = new List(); ScanSeries(notification); @@ -67,8 +67,8 @@ namespace NzbDrone.Core.Providers.Jobs _attemptedSeries.Add(currentSeries.SeriesId); notification.CurrentMessage = String.Format("Searching for '{0}'", new DirectoryInfo(currentSeries.Path).Name); - _updateInfoJob.Start(notification, currentSeries.SeriesId); - _diskScanJob.Start(notification, currentSeries.SeriesId); + _updateInfoJob.Start(notification, currentSeries.SeriesId, 0); + _diskScanJob.Start(notification, currentSeries.SeriesId, 0); var updatedSeries = _seriesProvider.GetSeries(currentSeries.SeriesId); AutoIgnoreSeasons(updatedSeries.SeriesId); diff --git a/NzbDrone.Core/Providers/Jobs/JobProvider.cs b/NzbDrone.Core/Providers/Jobs/JobProvider.cs index dc9573285..fd0988fa0 100644 --- a/NzbDrone.Core/Providers/Jobs/JobProvider.cs +++ b/NzbDrone.Core/Providers/Jobs/JobProvider.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using Ninject; using NLog; +using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Repository; using PetaPoco; @@ -28,7 +29,7 @@ namespace NzbDrone.Core.Providers.Jobs private Thread _jobThread; private static bool _isRunning; - private static readonly List> _queue = new List>(); + private static readonly List _queue = new List(); private ProgressNotification _notification; @@ -51,7 +52,7 @@ namespace NzbDrone.Core.Providers.Jobs /// /// Gets the active queue. /// - public static List> Queue + public static List Queue { get { @@ -122,8 +123,10 @@ namespace NzbDrone.Core.Providers.Jobs /// Type of the job that should be queued. /// The targetId could be any Id parameter eg. SeriesId. it will be passed to the job implementation /// to allow it to filter it's target of execution. + /// /// The secondaryTargetId could be any Id parameter eg. SeasonNumber. it will be passed to + /// the timer implementation to further allow it to filter it's target of execution /// Job is only added to the queue if same job with the same targetId doesn't already exist in the queue. - public virtual void QueueJob(Type jobType, int targetId = 0) + public virtual void QueueJob(Type jobType, int targetId = 0, int secondaryTargetId = 0) { Logger.Debug("Adding [{0}:{1}] to the queue", jobType.Name, targetId); @@ -131,11 +134,16 @@ namespace NzbDrone.Core.Providers.Jobs { lock (Queue) { - var queueTuple = new Tuple(jobType, targetId); - - if (!Queue.Contains(queueTuple)) + var queueItem = new JobQueueItem + { + JobType = jobType, + TargetId = targetId, + SecondaryTargetId = secondaryTargetId + }; + + if (!Queue.Contains(queueItem)) { - Queue.Add(queueTuple); + Queue.Add(queueItem); Logger.Trace("Job [{0}:{1}] added to the queue", jobType.Name, targetId); } @@ -195,7 +203,7 @@ namespace NzbDrone.Core.Providers.Jobs { try { - Tuple job = null; + JobQueueItem job = null; lock (Queue) { @@ -208,7 +216,7 @@ namespace NzbDrone.Core.Providers.Jobs if (job != null) { - Execute(job.Item1, job.Item2); + Execute(job.JobType, job.TargetId, job.SecondaryTargetId); } } @@ -231,7 +239,9 @@ namespace NzbDrone.Core.Providers.Jobs /// Type of the job that should be executed /// The targetId could be any Id parameter eg. SeriesId. it will be passed to the timer implementation /// to allow it to filter it's target of execution - private void Execute(Type jobType, int targetId = 0) + /// /// The secondaryTargetId could be any Id parameter eg. SeasonNumber. it will be passed to + /// the timer implementation to further allow it to filter it's target of execution + private void Execute(Type jobType, int targetId = 0, int secondaryTargetId = 0) { var jobImplementation = _jobs.Where(t => t.GetType() == jobType).Single(); if (jobImplementation == null) @@ -251,7 +261,7 @@ namespace NzbDrone.Core.Providers.Jobs var sw = Stopwatch.StartNew(); _notificationProvider.Register(_notification); - jobImplementation.Start(_notification, targetId); + jobImplementation.Start(_notification, targetId, secondaryTargetId); _notification.Status = ProgressNotificationStatus.Completed; settings.LastExecution = DateTime.Now; diff --git a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs index 3af0c7557..f57e7d2b7 100644 --- a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs +++ b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 1; } } - public virtual void Start(ProgressNotification notification, int targetId) + public virtual void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { var dropFolder = _configProvider.SabDropDirectory; diff --git a/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs b/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs index 9e7920767..4cc3ca156 100644 --- a/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs +++ b/NzbDrone.Core/Providers/Jobs/RenameEpisodeJob.cs @@ -1,4 +1,5 @@ -using Ninject; +using System; +using Ninject; using NLog; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers.Core; @@ -30,10 +31,14 @@ namespace NzbDrone.Core.Providers.Jobs get { return 0; } } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { + if (targetId <= 0) + throw new ArgumentOutOfRangeException("targetId"); + var episode = _mediaFileProvider.GetEpisodeFile(targetId); _diskScanProvider.MoveEpisodeFile(episode); + notification.CurrentMessage = String.Format("Episode rename completed for: {0} ", targetId); } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Jobs/RenameSeasonJob.cs b/NzbDrone.Core/Providers/Jobs/RenameSeasonJob.cs new file mode 100644 index 000000000..ba6dfebe0 --- /dev/null +++ b/NzbDrone.Core/Providers/Jobs/RenameSeasonJob.cs @@ -0,0 +1,59 @@ +using System; +using Ninject; +using NLog; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Providers.Core; + +namespace NzbDrone.Core.Providers.Jobs +{ + public class RenameSeasonJob : IJob + { + private readonly MediaFileProvider _mediaFileProvider; + private readonly DiskScanProvider _diskScanProvider; + + + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + [Inject] + public RenameSeasonJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider) + { + _mediaFileProvider = mediaFileProvider; + _diskScanProvider = diskScanProvider; + } + + public string Name + { + get { return "Rename Season"; } + } + + public int DefaultInterval + { + get { return 0; } + } + + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) + { + if (targetId <= 0) + throw new ArgumentOutOfRangeException("targetId"); + + if (secondaryTargetId <= 0) + throw new ArgumentOutOfRangeException("secondaryTargetId"); + + Logger.Debug("Getting episodes from database for series: {0} and season: {1}", targetId, secondaryTargetId); + var episodeFiles = _mediaFileProvider.GetSeasonFiles(targetId, secondaryTargetId); + + if (episodeFiles == null || episodeFiles.Count == 0) + { + Logger.Warn("No episodes in database found for series: {0} and season: {1}. No", targetId, secondaryTargetId); + return; + } + + foreach (var episodeFile in episodeFiles) + { + _diskScanProvider.MoveEpisodeFile(episodeFile); + } + + notification.CurrentMessage = String.Format("Season rename completed for Series: {0} Season: {1}", targetId, secondaryTargetId); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Jobs/RssSyncJob.cs b/NzbDrone.Core/Providers/Jobs/RssSyncJob.cs index 9fa4bf7f3..c8ea15522 100644 --- a/NzbDrone.Core/Providers/Jobs/RssSyncJob.cs +++ b/NzbDrone.Core/Providers/Jobs/RssSyncJob.cs @@ -38,7 +38,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 15; } } - public void Start(ProgressNotification notification, int targetId) + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { var reports = new List(); diff --git a/NzbDrone.Core/Providers/Jobs/SeasonSearchJob.cs b/NzbDrone.Core/Providers/Jobs/SeasonSearchJob.cs new file mode 100644 index 000000000..94c77c942 --- /dev/null +++ b/NzbDrone.Core/Providers/Jobs/SeasonSearchJob.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using NzbDrone.Core.Model; +using NzbDrone.Core.Model.Notification; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Providers.Jobs +{ + public class SeasonSearchJob : IJob + { + private readonly EpisodeProvider _episodeProvider; + private readonly EpisodeSearchJob _episodeSearchJob; + + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + public SeasonSearchJob(EpisodeProvider episodeProvider, EpisodeSearchJob episodeSearchJob) + { + _episodeProvider = episodeProvider; + _episodeSearchJob = episodeSearchJob; + } + + public string Name + { + get { return "Season Search"; } + } + + public int DefaultInterval + { + get { return 0; } + } + + public void Start(ProgressNotification notification, int targetId, int secondaryTargetId) + { + if (targetId <= 0) + throw new ArgumentOutOfRangeException("targetId"); + + if (secondaryTargetId <= 0) + throw new ArgumentOutOfRangeException("secondaryTargetId"); + + Logger.Debug("Getting episodes from database for series: {0} and season: {1}", targetId, secondaryTargetId); + var episodes = _episodeProvider.GetEpisodesBySeason(targetId, secondaryTargetId); + + if (episodes == null) + { + Logger.Warn("No episodes in database found for series: {0} and season: {1}. No", targetId, secondaryTargetId); + return; + } + + //Todo: Search for a full season NZB before individual episodes + + foreach (var episode in episodes) + { + _episodeSearchJob.Start(notification, episode.EpisodeId, 0); + } + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Jobs/UpdateInfoJob.cs b/NzbDrone.Core/Providers/Jobs/UpdateInfoJob.cs index ac4bd2fb3..59a322a62 100644 --- a/NzbDrone.Core/Providers/Jobs/UpdateInfoJob.cs +++ b/NzbDrone.Core/Providers/Jobs/UpdateInfoJob.cs @@ -33,7 +33,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 1440; } //Daily } - public virtual void Start(ProgressNotification notification, int targetId) + public virtual void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { IList seriesToUpdate; if (targetId == 0) diff --git a/NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs b/NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs index 189a81458..b98ab2f2f 100644 --- a/NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs +++ b/NzbDrone.Core/Providers/Jobs/UpdateSceneMappingsJob.cs @@ -26,7 +26,7 @@ namespace NzbDrone.Core.Providers.Jobs get { return 720; } //Every 12 hours } - public virtual void Start(ProgressNotification notification, int targetId) + public virtual void Start(ProgressNotification notification, int targetId, int secondaryTargetId) { _sceneNameMappingProvider.UpdateMappings(); } diff --git a/NzbDrone.Core/Providers/MediaFileProvider.cs b/NzbDrone.Core/Providers/MediaFileProvider.cs index 6d464b29b..2df284100 100644 --- a/NzbDrone.Core/Providers/MediaFileProvider.cs +++ b/NzbDrone.Core/Providers/MediaFileProvider.cs @@ -31,8 +31,6 @@ namespace NzbDrone.Core.Providers { } - - public virtual int Add(EpisodeFile episodeFile) { return Convert.ToInt32(_database.Insert(episodeFile)); @@ -65,7 +63,12 @@ namespace NzbDrone.Core.Providers public virtual IList GetSeriesFiles(int seriesId) { - return _database.Fetch("WHERE seriesId= @0", seriesId); + return _database.Fetch("WHERE SeriesId= @0", seriesId); + } + + public virtual IList GetSeasonFiles(int seriesId, int seasonNumber) + { + return _database.Fetch("WHERE SeriesId= @0 AND SeasonNumber = @1", seriesId, seasonNumber); } public virtual Tuple GetEpisodeFilesCount(int seriesId) @@ -132,7 +135,6 @@ namespace NzbDrone.Core.Providers return updated; } - public virtual string GetNewFilename(IList episodes, string seriesTitle, QualityTypes quality) { var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SortingSeparatorStyle); diff --git a/NzbDrone.Web/Controllers/EpisodeController.cs b/NzbDrone.Web/Controllers/EpisodeController.cs index 5e1c4972a..899e270db 100644 --- a/NzbDrone.Web/Controllers/EpisodeController.cs +++ b/NzbDrone.Web/Controllers/EpisodeController.cs @@ -11,27 +11,40 @@ namespace NzbDrone.Web.Controllers { public class EpisodeController : Controller { - private readonly JobProvider _jobProvider; - public EpisodeController(JobProvider jobProvider) { _jobProvider = jobProvider; } + [HttpPost] public JsonResult Search(int episodeId) { _jobProvider.QueueJob(typeof(EpisodeSearchJob), episodeId); return new JsonResult { Data = "ok" }; } + [HttpPost] + public JsonResult SearchSeason(int seriesId, int seasonNumber) + { + _jobProvider.QueueJob(typeof(SeasonSearchJob), seriesId, seasonNumber); + return new JsonResult { Data = "ok" }; + } + public JsonResult Rename(int episodeFileId) { _jobProvider.QueueJob(typeof(RenameEpisodeJob), episodeFileId); return new JsonResult { Data = "ok" }; } + + public JsonResult RenameSeason(int seriesId, int seasonNumber) + { + _jobProvider.QueueJob(typeof(RenameSeasonJob), seriesId, seasonNumber); + + return new JsonResult { Data = "ok" }; + } } } \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/SystemController.cs b/NzbDrone.Web/Controllers/SystemController.cs index ec1a15d2e..5fc6723ca 100644 --- a/NzbDrone.Web/Controllers/SystemController.cs +++ b/NzbDrone.Web/Controllers/SystemController.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Web.Mvc; using NzbDrone.Core.Helpers; +using NzbDrone.Core.Model; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Providers.Jobs; @@ -29,7 +30,11 @@ namespace NzbDrone.Web.Controllers public ActionResult Jobs() { - ViewData["Queue"] = JobProvider.Queue.Select(c => new Tuple(c.Item1.Name, c.Item2)); + ViewData["Queue"] = JobProvider.Queue.Select(c => new JobQueueItemModel { + Name = c.JobType.Name, + TargetId = c.TargetId, + SecondaryTargetId = c.SecondaryTargetId + }); return View(_jobProvider.All()); } diff --git a/NzbDrone.Web/Models/JobQueueItemModel.cs b/NzbDrone.Web/Models/JobQueueItemModel.cs new file mode 100644 index 000000000..92f196714 --- /dev/null +++ b/NzbDrone.Web/Models/JobQueueItemModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace NzbDrone.Web.Models +{ + public class JobQueueItemModel + { + public string Name { get; set; } + public int TargetId { get; set; } + public int SecondaryTargetId { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index fe130accf..ca5f77003 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -165,6 +165,7 @@ + diff --git a/NzbDrone.Web/Scripts/episodeSearch.js b/NzbDrone.Web/Scripts/episodeSearch.js index 6f091df37..213c43249 100644 --- a/NzbDrone.Web/Scripts/episodeSearch.js +++ b/NzbDrone.Web/Scripts/episodeSearch.js @@ -9,5 +9,4 @@ function searchForEpisode(id) { alert("Sorry! We could search for " + id + " at this time. " + error); } }); -} - \ No newline at end of file +} \ No newline at end of file diff --git a/NzbDrone.Web/Scripts/seriesDetails.js b/NzbDrone.Web/Scripts/seriesDetails.js index 28a13be6c..edd0c1e86 100644 --- a/NzbDrone.Web/Scripts/seriesDetails.js +++ b/NzbDrone.Web/Scripts/seriesDetails.js @@ -3,7 +3,11 @@ var ignoredImage = '../../Content/Images/ignored.png'; var seriesId = 0; var saveSeasonIgnoreUrl = '../Series/SaveSeasonIgnore'; var saveEpisodeIgnoreUrl = '../Series/SaveEpisodeIgnore'; +var renameEpisodeUrl = '../Episode/Rename'; +var renameSeasonUrl = '../Episode/RenameSeason'; +var searchSeasonUrl = '../Episode/SearchSeason'; +//Episode Ignore Functions $(".ignoreEpisode").live("click", function () { var toggle = $(this); var ignored = toggle.hasClass('ignored'); @@ -123,6 +127,7 @@ function grid_dataBound(e) { toggleMaster(seasonNumber); } +//Episode Ignore Saving function saveSeasonIgnore(seasonNumber, ignored) { $.ajax({ type: "POST", @@ -143,4 +148,39 @@ function saveEpisodeIgnore(episodeId, ignored) { alert("Sorry! We could save the ignore settings for Episode: " + episodeId + " at this time. " + error); } }); +} + +//Episode Renaming +function renameEpisode(id) { + $.ajax({ + type: "POST", + url: renameEpisodeUrl, + data: jQuery.param({ episodeFileId: id }), + error: function (req, status, error) { + alert("Sorry! We could rename " + id + " at this time. " + error); + } + }); +} + +function renameSeason(seriesId, seasonNumber) { + $.ajax({ + type: "POST", + url: renameSeasonUrl, + data: jQuery.param({ seriesId: seriesId, seasonNumber: seasonNumber }), + error: function (req, status, error) { + alert("Sorry! We could rename series: " + seriesId + " season: " + seasonNumber + " at this time. " + error); + } + }); +} + +//Season Search +function searchSeason(seriesId, seasonNumber) { + $.ajax({ + type: "POST", + url: searchSeasonUrl, + data: jQuery.param({ seriesId: seriesId, seasonNumber: seasonNumber }), + error: function (req, status, error) { + alert("Sorry! We could search for series: " + seriesId + " season: " + seasonNumber + " at this time. " + error); + } + }); } \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/Details.cshtml b/NzbDrone.Web/Views/Series/Details.cshtml index 81dfa4e0c..1369ccd8a 100644 --- a/NzbDrone.Web/Views/Series/Details.cshtml +++ b/NzbDrone.Web/Views/Series/Details.cshtml @@ -84,8 +84,11 @@ } - @foreach (var season in Model.Seasons.Where(s => s > 0).Reverse()) + @foreach (var s in Model.Seasons.Where(s => s > 0).Reverse()) { + var seriesId = @Model.SeriesId; + var season = s; +

Season @season

@@ -107,9 +110,10 @@ columns.Bound(c => c.Quality).Width(0); columns.Bound(c => c.Status).Width(0); columns.Bound(o => o.EpisodeId).Title("") - .ClientTemplate("'); return false;\">Search" - + " | " + - "'); return false;\">Rename"); + .ClientTemplate("\" onclick=\"searchForEpisode('<#= EpisodeId #>'); return false;\">Search" + + " | " + + "\" onclick=\"renameEpisode('<#= EpisodeFileId #>'); return false;\">Rename" + ); }) .DetailView(detailView => detailView.ClientTemplate("
<#= Overview #>
<#= Path #>
")) .Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(false)) @@ -118,10 +122,13 @@ d => d.Ajax().Select("_AjaxSeasonGrid", "Series", new RouteValueDictionary { { "seriesId", Model.SeriesId }, { "seasonNumber", season } })) - .ToolBar( - c => - c.Custom().Text("Rename Season").Action("RenameSeason", "Series", new { seasonId = season }) - .ButtonType(GridButtonType.Text)) + .ToolBar(toolbar => toolbar.Template(@ + + )) .ClientEvents(clientEvents => { clientEvents.OnRowDataBound("grid_rowBound"); @@ -171,24 +178,6 @@ } @section Scripts{ } diff --git a/NzbDrone.Web/Views/System/Jobs.cshtml b/NzbDrone.Web/Views/System/Jobs.cshtml index 25d266f33..61194f6bd 100644 --- a/NzbDrone.Web/Views/System/Jobs.cshtml +++ b/NzbDrone.Web/Views/System/Jobs.cshtml @@ -1,4 +1,5 @@ @using System.Collections +@using NzbDrone.Web.Models @model IEnumerable @section TitleContent{ Jobs @@ -9,7 +10,9 @@ Jobs Items currently in queue - @{Html.Telerik().Grid((IEnumerable>)ViewData["Queue"]).Name("QueueGrid") - .Columns(c => c.Bound(g => g.Item1).Title("Type").Width(100)).Columns(c => c.Bound(g => g.Item2).Title("Target")) + @{Html.Telerik().Grid((IEnumerable)ViewData["Queue"]).Name("QueueGrid") + .Columns(c => c.Bound(g => g.Name).Title("Type").Width(100)) + .Columns(c => c.Bound(g => g.TargetId).Title("Target")) + .Columns(c => c.Bound(g => g.SecondaryTargetId).Title("Secondary Target")) .Render();} }