mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-09 20:42:37 +01:00
Added SeriesSearch and RenameSeries jobs.
Add UI controls for new jobs. Skip ignored episodes when doing series/season searches.
This commit is contained in:
parent
1d1bbd3a23
commit
f6c9fa4f95
@ -88,6 +88,7 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="SeriesSearchJobTest.cs" />
|
||||||
<Compile Include="SeasonSearchJobTest.cs" />
|
<Compile Include="SeasonSearchJobTest.cs" />
|
||||||
<Compile Include="EventClientProviderTest.cs" />
|
<Compile Include="EventClientProviderTest.cs" />
|
||||||
<Compile Include="CentralDispatchTest.cs" />
|
<Compile Include="CentralDispatchTest.cs" />
|
||||||
|
@ -28,6 +28,7 @@ public void SeasonSearch_success()
|
|||||||
.WhereAll()
|
.WhereAll()
|
||||||
.Have(e => e.SeriesId = 1)
|
.Have(e => e.SeriesId = 1)
|
||||||
.Have(e => e.SeasonNumber = 1)
|
.Have(e => e.SeasonNumber = 1)
|
||||||
|
.Have(e => e.Ignored = false)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var mocker = new AutoMoqer(MockBehavior.Strict);
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
@ -68,5 +69,37 @@ public void SeasonSearch_no_episodes()
|
|||||||
Times.Never());
|
Times.Never());
|
||||||
ExceptionVerification.ExcpectedWarns(1);
|
ExceptionVerification.ExcpectedWarns(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SeasonSearch_skip_ignored()
|
||||||
|
{
|
||||||
|
var episodes = Builder<Episode>.CreateListOfSize(10)
|
||||||
|
.WhereAll()
|
||||||
|
.Have(e => e.SeriesId = 1)
|
||||||
|
.Have(e => e.SeasonNumber = 1)
|
||||||
|
.WhereTheFirst(5)
|
||||||
|
.Have(e => e.Ignored = false)
|
||||||
|
.AndTheRemaining()
|
||||||
|
.Have(e => e.Ignored = true)
|
||||||
|
.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(5));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
103
NzbDrone.Core.Test/SeriesSearchJobTest.cs
Normal file
103
NzbDrone.Core.Test/SeriesSearchJobTest.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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 SeriesSearchJobTest : TestBase
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void SeriesSearch_success()
|
||||||
|
{
|
||||||
|
var episodes = Builder<Episode>.CreateListOfSize(5)
|
||||||
|
.WhereAll()
|
||||||
|
.Have(e => e.SeriesId = 1)
|
||||||
|
.Have(e => e.Ignored = false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
|
||||||
|
var notification = new ProgressNotification("Series Search");
|
||||||
|
|
||||||
|
mocker.GetMock<EpisodeProvider>()
|
||||||
|
.Setup(c => c.GetEpisodeBySeries(1)).Returns(episodes);
|
||||||
|
|
||||||
|
mocker.GetMock<EpisodeSearchJob>()
|
||||||
|
.Setup(c => c.Start(notification, It.IsAny<int>(), 0)).Verifiable();
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mocker.Resolve<SeriesSearchJob>().Start(notification, 1, 0);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
mocker.VerifyAllMocks();
|
||||||
|
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
||||||
|
Times.Exactly(episodes.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SeriesSearch_no_episodes()
|
||||||
|
{
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
var notification = new ProgressNotification("Series Search");
|
||||||
|
List<Episode> nullList = null;
|
||||||
|
|
||||||
|
mocker.GetMock<EpisodeProvider>()
|
||||||
|
.Setup(c => c.GetEpisodeBySeries(1)).Returns(nullList);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mocker.Resolve<SeriesSearchJob>().Start(notification, 1, 0);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
mocker.VerifyAllMocks();
|
||||||
|
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
||||||
|
Times.Never());
|
||||||
|
ExceptionVerification.ExcpectedWarns(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SeriesSearch_skip_ignored()
|
||||||
|
{
|
||||||
|
var episodes = Builder<Episode>.CreateListOfSize(10)
|
||||||
|
.WhereAll()
|
||||||
|
.Have(e => e.SeriesId = 1)
|
||||||
|
.WhereTheFirst(5)
|
||||||
|
.Have(e => e.Ignored = false)
|
||||||
|
.AndTheRemaining()
|
||||||
|
.Have(e => e.Ignored = true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
|
||||||
|
var notification = new ProgressNotification("Series Search");
|
||||||
|
|
||||||
|
mocker.GetMock<EpisodeProvider>()
|
||||||
|
.Setup(c => c.GetEpisodeBySeries(1)).Returns(episodes);
|
||||||
|
|
||||||
|
mocker.GetMock<EpisodeSearchJob>()
|
||||||
|
.Setup(c => c.Start(notification, It.IsAny<int>(), 0)).Verifiable();
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mocker.Resolve<SeriesSearchJob>().Start(notification, 1, 0);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
mocker.VerifyAllMocks();
|
||||||
|
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
||||||
|
Times.Exactly(5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -106,6 +106,8 @@ private static void BindJobs()
|
|||||||
_kernel.Bind<IJob>().To<UpdateSceneMappingsJob>().InSingletonScope();
|
_kernel.Bind<IJob>().To<UpdateSceneMappingsJob>().InSingletonScope();
|
||||||
_kernel.Bind<IJob>().To<SeasonSearchJob>().InSingletonScope();
|
_kernel.Bind<IJob>().To<SeasonSearchJob>().InSingletonScope();
|
||||||
_kernel.Bind<IJob>().To<RenameSeasonJob>().InSingletonScope();
|
_kernel.Bind<IJob>().To<RenameSeasonJob>().InSingletonScope();
|
||||||
|
_kernel.Bind<IJob>().To<SeriesSearchJob>().InSingletonScope();
|
||||||
|
_kernel.Bind<IJob>().To<RenameSeriesJob>().InSingletonScope();
|
||||||
|
|
||||||
_kernel.Get<JobProvider>().Initialize();
|
_kernel.Get<JobProvider>().Initialize();
|
||||||
_kernel.Get<WebTimer>().StartTimer(30);
|
_kernel.Get<WebTimer>().StartTimer(30);
|
||||||
|
@ -197,6 +197,8 @@
|
|||||||
<Compile Include="Model\Xbmc\ErrorResult.cs" />
|
<Compile Include="Model\Xbmc\ErrorResult.cs" />
|
||||||
<Compile Include="Model\Xbmc\IconType.cs" />
|
<Compile Include="Model\Xbmc\IconType.cs" />
|
||||||
<Compile Include="Providers\Core\UdpProvider.cs" />
|
<Compile Include="Providers\Core\UdpProvider.cs" />
|
||||||
|
<Compile Include="Providers\Jobs\RenameSeriesJob.cs" />
|
||||||
|
<Compile Include="Providers\Jobs\SeriesSearchJob.cs" />
|
||||||
<Compile Include="Providers\Jobs\RenameSeasonJob.cs" />
|
<Compile Include="Providers\Jobs\RenameSeasonJob.cs" />
|
||||||
<Compile Include="Providers\Jobs\SeasonSearchJob.cs" />
|
<Compile Include="Providers\Jobs\SeasonSearchJob.cs" />
|
||||||
<Compile Include="Providers\Xbmc\ResourceManager.cs" />
|
<Compile Include="Providers\Xbmc\ResourceManager.cs" />
|
||||||
|
@ -44,7 +44,7 @@ public void Start(ProgressNotification notification, int targetId, int secondary
|
|||||||
|
|
||||||
if (episodeFiles == null || episodeFiles.Count == 0)
|
if (episodeFiles == null || episodeFiles.Count == 0)
|
||||||
{
|
{
|
||||||
Logger.Warn("No episodes in database found for series: {0} and season: {1}. No", targetId, secondaryTargetId);
|
Logger.Warn("No episodes in database found for series: {0} and season: {1}.", targetId, secondaryTargetId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
NzbDrone.Core/Providers/Jobs/RenameSeriesJob.cs
Normal file
56
NzbDrone.Core/Providers/Jobs/RenameSeriesJob.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using Ninject;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Core.Model.Notification;
|
||||||
|
using NzbDrone.Core.Providers.Core;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Providers.Jobs
|
||||||
|
{
|
||||||
|
public class RenameSeriesJob : IJob
|
||||||
|
{
|
||||||
|
private readonly MediaFileProvider _mediaFileProvider;
|
||||||
|
private readonly DiskScanProvider _diskScanProvider;
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
public RenameSeriesJob(MediaFileProvider mediaFileProvider, DiskScanProvider diskScanProvider)
|
||||||
|
{
|
||||||
|
_mediaFileProvider = mediaFileProvider;
|
||||||
|
_diskScanProvider = diskScanProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return "Rename Series"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int DefaultInterval
|
||||||
|
{
|
||||||
|
get { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(ProgressNotification notification, int targetId, int secondaryTargetId)
|
||||||
|
{
|
||||||
|
if (targetId <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException("targetId");
|
||||||
|
|
||||||
|
Logger.Debug("Getting episodes from database for series: {0}", targetId);
|
||||||
|
var episodeFiles = _mediaFileProvider.GetSeriesFiles(targetId);
|
||||||
|
|
||||||
|
if (episodeFiles == null || episodeFiles.Count == 0)
|
||||||
|
{
|
||||||
|
Logger.Warn("No episodes in database found for series: {0}", targetId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var episodeFile in episodeFiles)
|
||||||
|
{
|
||||||
|
_diskScanProvider.MoveEpisodeFile(episodeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.CurrentMessage = String.Format("Series rename completed for Series: {0}", targetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -44,13 +44,13 @@ public void Start(ProgressNotification notification, int targetId, int secondary
|
|||||||
|
|
||||||
if (episodes == null)
|
if (episodes == null)
|
||||||
{
|
{
|
||||||
Logger.Warn("No episodes in database found for series: {0} and season: {1}. No", targetId, secondaryTargetId);
|
Logger.Warn("No episodes in database found for series: {0} and season: {1}.", targetId, secondaryTargetId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Todo: Search for a full season NZB before individual episodes
|
//Todo: Search for a full season NZB before individual episodes
|
||||||
|
|
||||||
foreach (var episode in episodes)
|
foreach (var episode in episodes.Where(e => !e.Ignored))
|
||||||
{
|
{
|
||||||
_episodeSearchJob.Start(notification, episode.EpisodeId, 0);
|
_episodeSearchJob.Start(notification, episode.EpisodeId, 0);
|
||||||
}
|
}
|
||||||
|
56
NzbDrone.Core/Providers/Jobs/SeriesSearchJob.cs
Normal file
56
NzbDrone.Core/Providers/Jobs/SeriesSearchJob.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
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 SeriesSearchJob : IJob
|
||||||
|
{
|
||||||
|
private readonly EpisodeProvider _episodeProvider;
|
||||||
|
private readonly EpisodeSearchJob _episodeSearchJob;
|
||||||
|
|
||||||
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
public SeriesSearchJob(EpisodeProvider episodeProvider, EpisodeSearchJob episodeSearchJob)
|
||||||
|
{
|
||||||
|
_episodeProvider = episodeProvider;
|
||||||
|
_episodeSearchJob = episodeSearchJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { return "Series Search"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int DefaultInterval
|
||||||
|
{
|
||||||
|
get { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start(ProgressNotification notification, int targetId, int secondaryTargetId)
|
||||||
|
{
|
||||||
|
if (targetId <= 0)
|
||||||
|
throw new ArgumentOutOfRangeException("targetId");
|
||||||
|
|
||||||
|
Logger.Debug("Getting episodes from database for series: {0}.", targetId);
|
||||||
|
var episodes = _episodeProvider.GetEpisodeBySeries(targetId);
|
||||||
|
|
||||||
|
if (episodes == null)
|
||||||
|
{
|
||||||
|
Logger.Warn("No episodes in database found for series: {0}.", targetId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Todo: Search for a full season NZB before individual episodes
|
||||||
|
|
||||||
|
foreach (var episode in episodes.Where(e => !e.Ignored))
|
||||||
|
{
|
||||||
|
_episodeSearchJob.Start(notification, episode.EpisodeId, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,13 +37,5 @@ public JsonResult UpdateInfo(int seriesId)
|
|||||||
|
|
||||||
return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
|
return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
|
||||||
}
|
}
|
||||||
|
|
||||||
public JsonResult RenameSeries(int seriesId)
|
|
||||||
{
|
|
||||||
//Syncs the episodes on disk for the specified series
|
|
||||||
//_jobProvider.QueueJob(typeof(UpdateInfoJob), seriesId);
|
|
||||||
|
|
||||||
return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,14 @@ public JsonResult SearchSeason(int seriesId, int seasonNumber)
|
|||||||
return new JsonResult { Data = "ok" };
|
return new JsonResult { Data = "ok" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JsonResult SearchSeries(int seriesId)
|
||||||
|
{
|
||||||
|
//Syncs the episodes on disk for the specified series
|
||||||
|
_jobProvider.QueueJob(typeof(SeriesSearchJob), seriesId);
|
||||||
|
|
||||||
|
return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
|
||||||
|
}
|
||||||
|
|
||||||
public JsonResult Rename(int episodeFileId)
|
public JsonResult Rename(int episodeFileId)
|
||||||
{
|
{
|
||||||
_jobProvider.QueueJob(typeof(RenameEpisodeJob), episodeFileId);
|
_jobProvider.QueueJob(typeof(RenameEpisodeJob), episodeFileId);
|
||||||
@ -46,5 +54,13 @@ public JsonResult RenameSeason(int seriesId, int seasonNumber)
|
|||||||
|
|
||||||
return new JsonResult { Data = "ok" };
|
return new JsonResult { Data = "ok" };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JsonResult RenameSeries(int seriesId)
|
||||||
|
{
|
||||||
|
//Syncs the episodes on disk for the specified series
|
||||||
|
_jobProvider.QueueJob(typeof(RenameSeriesJob), seriesId);
|
||||||
|
|
||||||
|
return new JsonResult { Data = "ok", JsonRequestBehavior = JsonRequestBehavior.AllowGet };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -86,7 +86,8 @@
|
|||||||
<li>@Html.ActionLink("Back to Series List", "Index", "Series")</li>
|
<li>@Html.ActionLink("Back to Series List", "Index", "Series")</li>
|
||||||
<li>@Ajax.ActionLink("Scan For Episodes on Disk", "SyncEpisodesOnDisk", "Command", new { seriesId = Model.SeriesId }, null)</li>
|
<li>@Ajax.ActionLink("Scan For Episodes on Disk", "SyncEpisodesOnDisk", "Command", new { seriesId = Model.SeriesId }, null)</li>
|
||||||
<li>@Ajax.ActionLink("Update Info", "UpdateInfo", "Command", new { seriesId = Model.SeriesId }, null)</li>
|
<li>@Ajax.ActionLink("Update Info", "UpdateInfo", "Command", new { seriesId = Model.SeriesId }, null)</li>
|
||||||
<li>@Ajax.ActionLink("Rename Series", "RenameSeries", "Command", new { seriesId = Model.SeriesId }, null)</li>
|
<li>@Ajax.ActionLink("Search for Series", "SearchSeries", "Episode", new { seriesId = Model.SeriesId }, null)</li>
|
||||||
|
<li>@Ajax.ActionLink("Rename Series", "RenameSeries", "Episode", new { seriesId = Model.SeriesId }, null)</li>
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
@section MainContent{
|
@section MainContent{
|
||||||
|
Loading…
Reference in New Issue
Block a user