Updated JobProvider to allow jobs with two targets.

JobQueueItem class created instead of using Tuples.
Added Search for Season and Rename Season jobs , plus links for them on Series/Details.
Add GetSeasonFiles added to MediaFileProvider.
This commit is contained in:
Mark McDowall 2011-08-21 17:48:37 -07:00
parent 56da830296
commit 350e0388de
32 changed files with 454 additions and 108 deletions

View File

@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test
//Act
mocker.Resolve<DiskScanJob>().Start(new ProgressNotification("Test"), series.SeriesId);
mocker.Resolve<DiskScanJob>().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<EpisodeFile>());
mocker.Resolve<DiskScanJob>().Start(new ProgressNotification("Test"), 0);
mocker.Resolve<DiskScanJob>().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<DiskScanJob>().Start(new ProgressNotification("Test"), 0);
mocker.Resolve<DiskScanJob>().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<EpisodeFile>());
mocker.Resolve<DiskScanJob>().Start(new ProgressNotification("Test"), 0);
mocker.Resolve<DiskScanJob>().Start(new ProgressNotification("Test"), 0, 0);

View File

@ -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<EpisodeSearchJob>().Start(new ProgressNotification("Test"), target);
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), target, 0);
}
@ -251,7 +251,7 @@ namespace NzbDrone.Core.Test
.Setup(s => s.GetSceneName(It.IsAny<int>())).Returns("");
//Act
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId);
mocker.Resolve<EpisodeSearchJob>().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<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId);
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId, 0);
//Assert
@ -359,7 +359,7 @@ namespace NzbDrone.Core.Test
.Setup(s => s.GetSceneName(It.IsAny<int>())).Returns("");
//Act
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId);
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId, 0);
//Assert
@ -385,7 +385,7 @@ namespace NzbDrone.Core.Test
.Returns<Episode>(null);
//Act
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), 12);
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), 12, 0);
//Assert

View File

@ -36,21 +36,21 @@ namespace NzbDrone.Core.Test
mocker.GetMock<DiskScanJob>()
.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<DiskScanJob>()
.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<UpdateInfoJob>()
.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<UpdateInfoJob>()
.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<SeriesProvider>()
@ -63,16 +63,16 @@ namespace NzbDrone.Core.Test
.Setup(s => s.GetSeriesFiles(It.IsAny<int>())).Returns(new List<EpisodeFile>());
//Act
mocker.Resolve<ImportNewSeriesJob>().Start(notification, 0);
mocker.Resolve<ImportNewSeriesJob>().Start(notification, 0, 0);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<DiskScanJob>().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once());
mocker.GetMock<DiskScanJob>().Verify(j => j.Start(notification, series[1].SeriesId), Times.Once());
mocker.GetMock<DiskScanJob>().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once());
mocker.GetMock<DiskScanJob>().Verify(j => j.Start(notification, series[1].SeriesId, 0), Times.Once());
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once());
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[1].SeriesId), Times.Once());
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once());
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[1].SeriesId, 0), Times.Once());
}
@ -98,15 +98,15 @@ namespace NzbDrone.Core.Test
.Returns(series);
mocker.GetMock<UpdateInfoJob>()
.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<UpdateInfoJob>()
.Setup(j => j.Start(notification, series[1].SeriesId))
.Setup(j => j.Start(notification, series[1].SeriesId, 0))
.Throws(new InvalidOperationException());
mocker.GetMock<DiskScanJob>()
.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<int>())).Returns(new List<EpisodeFile>());
//Act
mocker.Resolve<ImportNewSeriesJob>().Start(notification, 0);
mocker.Resolve<ImportNewSeriesJob>().Start(notification, 0, 0);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once());
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[1].SeriesId), Times.Once());
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once());
mocker.GetMock<UpdateInfoJob>().Verify(j => j.Start(notification, series[1].SeriesId, 0), Times.Once());
mocker.GetMock<DiskScanJob>().Verify(j => j.Start(notification, series[0].SeriesId), Times.Once());
mocker.GetMock<DiskScanJob>().Verify(j => j.Start(notification, series[0].SeriesId, 0), Times.Once());
ExceptionVerification.ExcpectedErrors(1);

View File

@ -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<Type, int>(fakeJob.GetType(), 12);
var fakeQueueItem = new JobQueueItem
{
JobType = fakeJob.GetType(),
TargetId = 12,
SecondaryTargetId = 0
};
//Act
var jobProvider = mocker.Resolve<JobProvider>();
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");
}
}

View File

@ -50,6 +50,35 @@ namespace NzbDrone.Core.Test
result.Should().HaveSameCount(firstSeriesFiles);
}
[Test]
public void get_season_files()
{
var firstSeriesFiles = Builder<EpisodeFile>.CreateListOfSize(10)
.WhereAll()
.Have(s => s.SeriesId = 12)
.Have(s => s.SeasonNumber = 1)
.Build();
var secondSeriesFiles = Builder<EpisodeFile>.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<MediaFileProvider>().GetSeasonFiles(12, 1);
result.Should().HaveSameCount(firstSeriesFiles);
}
[Test]
public void Scan_series_should_skip_series_with_no_episodes()
{

View File

@ -88,6 +88,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="SeasonSearchJobTest.cs" />
<Compile Include="EventClientProviderTest.cs" />
<Compile Include="CentralDispatchTest.cs" />
<Compile Include="XbmcProviderTest.cs" />

View File

@ -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<Episode>.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<EpisodeProvider>()
.Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes);
mocker.GetMock<EpisodeSearchJob>()
.Setup(c => c.Start(notification, It.IsAny<int>(), 0)).Verifiable();
//Act
mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
Times.Exactly(episodes.Count));
}
[Test]
public void SeasonSearch_no_episodes()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
var notification = new ProgressNotification("Season Search");
List<Episode> nullList = null;
mocker.GetMock<EpisodeProvider>()
.Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(nullList);
//Act
mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
//Assert
mocker.VerifyAllMocks();
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
Times.Never());
ExceptionVerification.ExcpectedWarns(1);
}
}
}

View File

@ -104,6 +104,8 @@ namespace NzbDrone.Core
_kernel.Bind<IJob>().To<RenameEpisodeJob>().InSingletonScope();
_kernel.Bind<IJob>().To<PostDownloadScanJob>().InSingletonScope();
_kernel.Bind<IJob>().To<UpdateSceneMappingsJob>().InSingletonScope();
_kernel.Bind<IJob>().To<SeasonSearchJob>().InSingletonScope();
_kernel.Bind<IJob>().To<RenameSeasonJob>().InSingletonScope();
_kernel.Get<JobProvider>().Initialize();
_kernel.Get<WebTimer>().StartTimer(30);

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NzbDrone.Core.Model
{
public class JobQueueItem : IEquatable<JobQueueItem>
{
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;
}
}
}

View File

@ -188,6 +188,7 @@
<Compile Include="Datastore\PetaPoco\PetaPoco.cs" />
<Compile Include="Model\ConnectionInfoModel.cs" />
<Compile Include="Model\ExternalNotificationType.cs" />
<Compile Include="Model\JobQueueItem.cs" />
<Compile Include="Model\LanguageType.cs" />
<Compile Include="Model\Quality.cs" />
<Compile Include="Model\SabnzbdInfoModel.cs" />
@ -196,6 +197,8 @@
<Compile Include="Model\Xbmc\ErrorResult.cs" />
<Compile Include="Model\Xbmc\IconType.cs" />
<Compile Include="Providers\Core\UdpProvider.cs" />
<Compile Include="Providers\Jobs\RenameSeasonJob.cs" />
<Compile Include="Providers\Jobs\SeasonSearchJob.cs" />
<Compile Include="Providers\Xbmc\ResourceManager.cs" />
<Compile Include="Model\Xbmc\TvShowResult.cs" />
<Compile Include="Model\Xbmc\Params.cs" />

View File

@ -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);
}

View File

@ -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<Series> seriesToScan;
if (targetId == 0)

View File

@ -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");

View File

@ -7,7 +7,7 @@ namespace NzbDrone.Core.Providers.Jobs
/// <summary>
/// Name of the timer.
/// This is the name that will be visible in all UI elements
/// </summary>
/// </summary>\\\
string Name { get; }
@ -25,6 +25,7 @@ namespace NzbDrone.Core.Providers.Jobs
/// <param name="notification">Notification object that is passed in by JobProvider.
/// this object should be used to update the progress on the UI</param>
/// <param name="targetId">The that should be used to limit the target of this job</param>
void Start(ProgressNotification notification, int targetId);
/// /// <param name="secondaryTargetId">The that should be used to limit the target of this job</param>
void Start(ProgressNotification notification, int targetId, int secondaryTargetId);
}
}

View File

@ -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<int>();
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);

View File

@ -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<Tuple<Type, Int32>> _queue = new List<Tuple<Type, int>>();
private static readonly List<JobQueueItem> _queue = new List<JobQueueItem>();
private ProgressNotification _notification;
@ -51,7 +52,7 @@ namespace NzbDrone.Core.Providers.Jobs
/// <summary>
/// Gets the active queue.
/// </summary>
public static List<Tuple<Type, Int32>> Queue
public static List<JobQueueItem> Queue
{
get
{
@ -122,8 +123,10 @@ namespace NzbDrone.Core.Providers.Jobs
/// <param name="jobType">Type of the job that should be queued.</param>
/// <param name="targetId">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.</param>
/// /// <param name="secondaryTargetId">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</param>
/// <remarks>Job is only added to the queue if same job with the same targetId doesn't already exist in the queue.</remarks>
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<Type, int>(jobType, targetId);
var queueItem = new JobQueueItem
{
JobType = jobType,
TargetId = targetId,
SecondaryTargetId = secondaryTargetId
};
if (!Queue.Contains(queueTuple))
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<Type, int> 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
/// <param name="jobType">Type of the job that should be executed</param>
/// <param name="targetId">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</param>
private void Execute(Type jobType, int targetId = 0)
/// /// <param name="secondaryTargetId">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</param>
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;

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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<EpisodeParseResult>();

View File

@ -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);
}
}
}
}

View File

@ -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<Series> seriesToUpdate;
if (targetId == 0)

View File

@ -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();
}

View File

@ -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<EpisodeFile> GetSeriesFiles(int seriesId)
{
return _database.Fetch<EpisodeFile>("WHERE seriesId= @0", seriesId);
return _database.Fetch<EpisodeFile>("WHERE SeriesId= @0", seriesId);
}
public virtual IList<EpisodeFile> GetSeasonFiles(int seriesId, int seasonNumber)
{
return _database.Fetch<EpisodeFile>("WHERE SeriesId= @0 AND SeasonNumber = @1", seriesId, seasonNumber);
}
public virtual Tuple<int, int> GetEpisodeFilesCount(int seriesId)
@ -132,7 +135,6 @@ namespace NzbDrone.Core.Providers
return updated;
}
public virtual string GetNewFilename(IList<Episode> episodes, string seriesTitle, QualityTypes quality)
{
var separatorStyle = EpisodeSortingHelper.GetSeparatorStyle(_configProvider.SortingSeparatorStyle);

View File

@ -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" };
}
}
}

View File

@ -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<String, int>(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());
}

View File

@ -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; }
}
}

View File

@ -165,6 +165,7 @@
<Compile Include="Helpers\IsCurrentActionHelper.cs" />
<Compile Include="Models\ExistingSeriesModel.cs" />
<Compile Include="Models\AddNewSeriesModel.cs" />
<Compile Include="Models\JobQueueItemModel.cs" />
<Compile Include="Models\NotificationResult.cs" />
<Compile Include="Models\PendingProcessingModel.cs" />
<Compile Include="Models\QualityTypeModel.cs" />

View File

@ -9,5 +9,4 @@ function searchForEpisode(id) {
alert("Sorry! We could search for " + id + " at this time. " + error);
}
});
}
}

View File

@ -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);
}
});
}

View File

@ -84,8 +84,11 @@
}
</div>
@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;
<h3>
Season @season</h3>
<div class="grid-container">
@ -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("<a href='#Search' onClick=\"searchForEpisode('<#= EpisodeId #>'); return false;\">Search</a>"
+ " | " +
"<a href='#Rename' onClick=\"renameEpisode('<#= EpisodeFileId #>'); return false;\">Rename</a>");
.ClientTemplate("<a href=\"../Episode/Season?episodeId=<#= EpisodeId #>\" onclick=\"searchForEpisode('<#= EpisodeId #>'); return false;\">Search</a>"
+ " | " +
"<a href=\"../Episode/Rename?episodeFileId=<#= EpisodeId #>\" onclick=\"renameEpisode('<#= EpisodeFileId #>'); return false;\">Rename</a>"
);
})
.DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #> </br><#= Path #> </div>"))
.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(@<text>
<div>
<a href="../Episode/SearchSeason?seriesId=@seriesId&seasonNumber=@season" onclick="searchSeason('@seriesId', @season); return false;">Search for Season</a>
|
<a href="../Episode/RenameSeason?seriesId=@seriesId&seasonNumber=@season" onclick="renameSeason('@seriesId', @season); return false;">Rename Season</a>
</div>
</text>))
.ClientEvents(clientEvents =>
{
clientEvents.OnRowDataBound("grid_rowBound");
@ -171,24 +178,6 @@
}
@section Scripts{
<script type="text/javascript">
function episodeDetailExpanded(e) {
$console.log("OnDetailViewExpand :: " + e.masterRow.cells[1].innerHTML);
}
var renameEpisodeUrl = '@Url.Action("Rename", "Episode")';
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);
}
});
}
seriesId = @Model.SeriesId;
</script>
}

View File

@ -1,4 +1,5 @@
@using System.Collections
@using NzbDrone.Web.Models
@model IEnumerable<NzbDrone.Core.Repository.JobDefinition>
@section TitleContent{
Jobs
@ -9,7 +10,9 @@ Jobs
Items currently in queue
@{Html.Telerik().Grid((IEnumerable<Tuple<String, int>>)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<JobQueueItemModel>)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();}
}