mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-16 11:37:58 +02:00
commit
452649ed1d
@ -101,6 +101,7 @@
|
||||
<Compile Include="Profiles\Delay\DelayProfileModule.cs" />
|
||||
<Compile Include="Profiles\Delay\DelayProfileResource.cs" />
|
||||
<Compile Include="Profiles\Delay\DelayProfileValidator.cs" />
|
||||
<Compile Include="Queue\QueueActionModule.cs" />
|
||||
<Compile Include="RemotePathMappings\RemotePathMappingModule.cs" />
|
||||
<Compile Include="RemotePathMappings\RemotePathMappingResource.cs" />
|
||||
<Compile Include="Config\UiConfigModule.cs" />
|
||||
@ -202,6 +203,7 @@
|
||||
<Compile Include="ResourceChangeMessage.cs" />
|
||||
<Compile Include="Restrictions\RestrictionModule.cs" />
|
||||
<Compile Include="Restrictions\RestrictionResource.cs" />
|
||||
<Compile Include="REST\NotFoundException.cs" />
|
||||
<Compile Include="REST\BadRequestException.cs" />
|
||||
<Compile Include="REST\MethodNotAllowedException.cs" />
|
||||
<Compile Include="REST\ResourceValidator.cs" />
|
||||
|
113
src/NzbDrone.Api/Queue/QueueActionModule.cs
Normal file
113
src/NzbDrone.Api/Queue/QueueActionModule.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using System.Linq;
|
||||
using Nancy;
|
||||
using Nancy.Responses;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Queue;
|
||||
|
||||
namespace NzbDrone.Api.Queue
|
||||
{
|
||||
public class QueueActionModule : NzbDroneRestModule<QueueResource>
|
||||
{
|
||||
private readonly IQueueService _queueService;
|
||||
private readonly IDownloadTrackingService _downloadTrackingService;
|
||||
private readonly ICompletedDownloadService _completedDownloadService;
|
||||
private readonly IProvideDownloadClient _downloadClientProvider;
|
||||
private readonly IPendingReleaseService _pendingReleaseService;
|
||||
private readonly IDownloadService _downloadService;
|
||||
|
||||
public QueueActionModule(IQueueService queueService,
|
||||
IDownloadTrackingService downloadTrackingService,
|
||||
ICompletedDownloadService completedDownloadService,
|
||||
IProvideDownloadClient downloadClientProvider,
|
||||
IPendingReleaseService pendingReleaseService,
|
||||
IDownloadService downloadService)
|
||||
{
|
||||
_queueService = queueService;
|
||||
_downloadTrackingService = downloadTrackingService;
|
||||
_completedDownloadService = completedDownloadService;
|
||||
_downloadClientProvider = downloadClientProvider;
|
||||
_pendingReleaseService = pendingReleaseService;
|
||||
_downloadService = downloadService;
|
||||
|
||||
Delete[@"/(?<id>[\d]{1,10})"] = x => Remove((int)x.Id);
|
||||
Post["/import"] = x => Import();
|
||||
Post["/grab"] = x => Grab();
|
||||
}
|
||||
|
||||
private Response Remove(int id)
|
||||
{
|
||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id);
|
||||
|
||||
if (pendingRelease != null)
|
||||
{
|
||||
_pendingReleaseService.RemovePendingQueueItem(id);
|
||||
}
|
||||
|
||||
var trackedDownload = GetTrackedDownload(id);
|
||||
|
||||
if (trackedDownload == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var downloadClient = _downloadClientProvider.Get(trackedDownload.DownloadClient);
|
||||
|
||||
if (downloadClient == null)
|
||||
{
|
||||
throw new BadRequestException();
|
||||
}
|
||||
|
||||
downloadClient.RemoveItem(trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
return new object().AsResponse();
|
||||
}
|
||||
|
||||
private JsonResponse<QueueResource> Import()
|
||||
{
|
||||
var resource = Request.Body.FromJson<QueueResource>();
|
||||
var trackedDownload = GetTrackedDownload(resource.Id);
|
||||
|
||||
_completedDownloadService.Import(trackedDownload);
|
||||
|
||||
return resource.AsResponse();
|
||||
}
|
||||
|
||||
private JsonResponse<QueueResource> Grab()
|
||||
{
|
||||
var resource = Request.Body.FromJson<QueueResource>();
|
||||
|
||||
var pendingRelease = _pendingReleaseService.FindPendingQueueItem(resource.Id);
|
||||
|
||||
if (pendingRelease == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
_downloadService.DownloadReport(pendingRelease.RemoteEpisode);
|
||||
|
||||
return resource.AsResponse();
|
||||
}
|
||||
|
||||
private TrackedDownload GetTrackedDownload(int queueId)
|
||||
{
|
||||
var queueItem = _queueService.Find(queueId);
|
||||
|
||||
if (queueItem == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var trackedDownload = _downloadTrackingService.Find(queueItem.TrackingId);
|
||||
|
||||
if (trackedDownload == null)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
return trackedDownload;
|
||||
}
|
||||
}
|
||||
}
|
13
src/NzbDrone.Api/REST/NotFoundException.cs
Normal file
13
src/NzbDrone.Api/REST/NotFoundException.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using Nancy;
|
||||
using NzbDrone.Api.ErrorManagement;
|
||||
|
||||
namespace NzbDrone.Api.REST
|
||||
{
|
||||
public class NotFoundException : ApiException
|
||||
{
|
||||
public NotFoundException(object content = null)
|
||||
: base(HttpStatusCode.NotFound, content)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -115,7 +115,7 @@ private void GivenCompletedDownloadClientHistory(bool hasStorage = true)
|
||||
private void GivenCompletedImport()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalEpisode() { Path = @"C:\TestPath\Droned.S01E01.mkv" }))
|
||||
@ -125,7 +125,7 @@ private void GivenCompletedImport()
|
||||
private void GivenFailedImport()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>()
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalEpisode() { Path = @"C:\TestPath\Droned.S01E01.mkv" }, "Test Failure"))
|
||||
@ -135,13 +135,13 @@ private void GivenFailedImport()
|
||||
private void VerifyNoImports()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Verify(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()), Times.Never());
|
||||
.Verify(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()), Times.Never());
|
||||
}
|
||||
|
||||
private void VerifyImports()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Verify(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()), Times.Once());
|
||||
.Verify(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -473,7 +473,7 @@ public void should_not_mark_as_imported_if_all_files_were_rejected()
|
||||
GivenNoImportedHistory();
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(
|
||||
@ -505,7 +505,7 @@ public void should_not_mark_as_imported_if_all_files_were_skipped()
|
||||
GivenNoImportedHistory();
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(
|
||||
@ -537,7 +537,7 @@ public void should_not_mark_as_imported_if_some_files_were_skipped()
|
||||
GivenNoImportedHistory();
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalEpisode() {Path = @"C:\TestPath\Droned.S01E01.mkv"})),
|
||||
|
@ -5,7 +5,9 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
@ -102,7 +104,7 @@ public void should_delete_if_the_grabbed_quality_is_the_same()
|
||||
{
|
||||
GivenHeldRelease(_parsedEpisodeInfo.Quality);
|
||||
|
||||
Subject.RemoveGrabbed(new List<DownloadDecision> { _temporarilyRejected });
|
||||
Subject.Handle(new EpisodeGrabbedEvent(_remoteEpisode));
|
||||
|
||||
VerifyDelete();
|
||||
}
|
||||
@ -112,7 +114,7 @@ public void should_delete_if_the_grabbed_quality_is_the_higher()
|
||||
{
|
||||
GivenHeldRelease(new QualityModel(Quality.SDTV));
|
||||
|
||||
Subject.RemoveGrabbed(new List<DownloadDecision> { _temporarilyRejected });
|
||||
Subject.Handle(new EpisodeGrabbedEvent(_remoteEpisode));
|
||||
|
||||
VerifyDelete();
|
||||
}
|
||||
@ -122,7 +124,7 @@ public void should_not_delete_if_the_grabbed_quality_is_the_lower()
|
||||
{
|
||||
GivenHeldRelease(new QualityModel(Quality.Bluray720p));
|
||||
|
||||
Subject.RemoveGrabbed(new List<DownloadDecision> { _temporarilyRejected });
|
||||
Subject.Handle(new EpisodeGrabbedEvent(_remoteEpisode));
|
||||
|
||||
VerifyNoDelete();
|
||||
}
|
||||
|
@ -5,7 +5,9 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Download.Pending;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Profiles;
|
||||
@ -104,7 +106,9 @@ public void should_remove_if_it_is_the_same_release_from_the_same_indexer()
|
||||
{
|
||||
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate);
|
||||
|
||||
Subject.RemoveRejected(new List<DownloadDecision> { _temporarilyRejected });
|
||||
Subject.Handle(new RssSyncCompleteEvent(new ProcessedDecisions(new List<DownloadDecision>(),
|
||||
new List<DownloadDecision>(),
|
||||
new List<DownloadDecision> { _temporarilyRejected })));
|
||||
|
||||
VerifyDelete();
|
||||
}
|
||||
@ -114,7 +118,9 @@ public void should_not_remove_if_title_is_different()
|
||||
{
|
||||
GivenHeldRelease(_release.Title + "-RP", _release.Indexer, _release.PublishDate);
|
||||
|
||||
Subject.RemoveRejected(new List<DownloadDecision> { _temporarilyRejected });
|
||||
Subject.Handle(new RssSyncCompleteEvent(new ProcessedDecisions(new List<DownloadDecision>(),
|
||||
new List<DownloadDecision>(),
|
||||
new List<DownloadDecision> { _temporarilyRejected })));
|
||||
|
||||
VerifyNoDelete();
|
||||
}
|
||||
@ -124,7 +130,9 @@ public void should_not_remove_if_indexer_is_different()
|
||||
{
|
||||
GivenHeldRelease(_release.Title, "AnotherIndexer", _release.PublishDate);
|
||||
|
||||
Subject.RemoveRejected(new List<DownloadDecision> { _temporarilyRejected });
|
||||
Subject.Handle(new RssSyncCompleteEvent(new ProcessedDecisions(new List<DownloadDecision>(),
|
||||
new List<DownloadDecision>(),
|
||||
new List<DownloadDecision> { _temporarilyRejected })));
|
||||
|
||||
VerifyNoDelete();
|
||||
}
|
||||
@ -134,7 +142,9 @@ public void should_not_remove_if_publish_date_is_different()
|
||||
{
|
||||
GivenHeldRelease(_release.Title, _release.Indexer, _release.PublishDate.AddHours(1));
|
||||
|
||||
Subject.RemoveRejected(new List<DownloadDecision> { _temporarilyRejected });
|
||||
Subject.Handle(new RssSyncCompleteEvent(new ProcessedDecisions(new List<DownloadDecision>(),
|
||||
new List<DownloadDecision>(),
|
||||
new List<DownloadDecision> { _temporarilyRejected })));
|
||||
|
||||
VerifyNoDelete();
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ public void should_search_for_a_newly_added_episode()
|
||||
|
||||
Mocker.GetMock<IProcessDownloadDecisions>()
|
||||
.Setup(s => s.ProcessDecisions(It.IsAny<List<DownloadDecision>>()))
|
||||
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>()));
|
||||
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>()));
|
||||
|
||||
Subject.Handle(new EpisodeInfoRefreshedEvent(_series, _added, _updated));
|
||||
|
||||
|
@ -37,7 +37,7 @@ public void Setup()
|
||||
|
||||
Mocker.GetMock<IProcessDownloadDecisions>()
|
||||
.Setup(s => s.ProcessDecisions(It.IsAny<List<DownloadDecision>>()))
|
||||
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>()));
|
||||
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>(), new List<DownloadDecision>()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -87,6 +87,8 @@ public void should_skip_if_no_series_found()
|
||||
[Test]
|
||||
public void should_not_import_if_folder_is_a_series_path()
|
||||
{
|
||||
GivenValidSeries();
|
||||
|
||||
Mocker.GetMock<ISeriesService>()
|
||||
.Setup(s => s.SeriesPathExists(It.IsAny<String>()))
|
||||
.Returns(true);
|
||||
@ -97,8 +99,8 @@ public void should_not_import_if_folder_is_a_series_path()
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetSeries(It.IsAny<String>()), Times.Never());
|
||||
Mocker.GetMock<IDiskScanService>()
|
||||
.Verify(v => v.GetVideoFiles(It.IsAny<String>(), true), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
@ -118,13 +118,14 @@ public List<ImportResult> Import(TrackedDownload trackedDownload, String overrid
|
||||
|
||||
if (_diskProvider.FolderExists(outputPath))
|
||||
{
|
||||
importResults = _downloadedEpisodesImportService.ProcessFolder(new DirectoryInfo(outputPath), trackedDownload.DownloadItem);
|
||||
importResults = _downloadedEpisodesImportService.ProcessFolder(new DirectoryInfo(outputPath), trackedDownload.RemoteEpisode.Series, trackedDownload.DownloadItem);
|
||||
|
||||
ProcessImportResults(trackedDownload, outputPath, importResults);
|
||||
}
|
||||
|
||||
else if (_diskProvider.FileExists(outputPath))
|
||||
{
|
||||
importResults = _downloadedEpisodesImportService.ProcessFile(new FileInfo(outputPath), trackedDownload.DownloadItem);
|
||||
importResults = _downloadedEpisodesImportService.ProcessFile(new FileInfo(outputPath), trackedDownload.RemoteEpisode.Series, trackedDownload.DownloadItem);
|
||||
|
||||
ProcessImportResults(trackedDownload, outputPath, importResults);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ public interface IProvideDownloadClient
|
||||
{
|
||||
IDownloadClient GetDownloadClient(DownloadProtocol downloadProtocol);
|
||||
IEnumerable<IDownloadClient> GetDownloadClients();
|
||||
IDownloadClient Get(int id);
|
||||
}
|
||||
|
||||
public class DownloadClientProvider : IProvideDownloadClient
|
||||
@ -28,5 +29,10 @@ public IEnumerable<IDownloadClient> GetDownloadClients()
|
||||
{
|
||||
return _downloadClientFactory.GetAvailableProviders();
|
||||
}
|
||||
|
||||
public IDownloadClient Get(int id)
|
||||
{
|
||||
return _downloadClientFactory.GetAvailableProviders().Single(d => d.Definition.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ public interface IDownloadTrackingService
|
||||
{
|
||||
TrackedDownload[] GetCompletedDownloads();
|
||||
TrackedDownload[] GetQueuedDownloads();
|
||||
|
||||
TrackedDownload Find(string trackingId);
|
||||
void MarkAsFailed(Int32 historyId);
|
||||
}
|
||||
|
||||
@ -88,6 +88,11 @@ public TrackedDownload[] GetQueuedDownloads()
|
||||
}, TimeSpan.FromSeconds(5.0));
|
||||
}
|
||||
|
||||
public TrackedDownload Find(string trackingId)
|
||||
{
|
||||
return GetQueuedDownloads().SingleOrDefault(t => t.TrackingId == trackingId);
|
||||
}
|
||||
|
||||
public void MarkAsFailed(Int32 historyId)
|
||||
{
|
||||
var item = _historyService.Get(historyId);
|
||||
|
@ -4,6 +4,7 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.DecisionEngine;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@ -17,15 +18,19 @@ namespace NzbDrone.Core.Download.Pending
|
||||
public interface IPendingReleaseService
|
||||
{
|
||||
void Add(DownloadDecision decision);
|
||||
void RemoveGrabbed(List<DownloadDecision> grabbed);
|
||||
void RemoveRejected(List<DownloadDecision> rejected);
|
||||
|
||||
List<ReleaseInfo> GetPending();
|
||||
List<RemoteEpisode> GetPendingRemoteEpisodes(int seriesId);
|
||||
List<Queue.Queue> GetPendingQueue();
|
||||
Queue.Queue FindPendingQueueItem(int queueId);
|
||||
void RemovePendingQueueItem(int queueId);
|
||||
RemoteEpisode OldestPendingRelease(int seriesId, IEnumerable<int> episodeIds);
|
||||
}
|
||||
|
||||
public class PendingReleaseService : IPendingReleaseService, IHandle<SeriesDeletedEvent>
|
||||
public class PendingReleaseService : IPendingReleaseService,
|
||||
IHandle<SeriesDeletedEvent>,
|
||||
IHandle<EpisodeGrabbedEvent>,
|
||||
IHandle<RssSyncCompleteEvent>
|
||||
{
|
||||
private readonly IPendingReleaseRepository _repository;
|
||||
private readonly ISeriesService _seriesService;
|
||||
@ -49,6 +54,7 @@ public PendingReleaseService(IPendingReleaseRepository repository,
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
public void Add(DownloadDecision decision)
|
||||
{
|
||||
var alreadyPending = GetPendingReleases();
|
||||
@ -69,61 +75,6 @@ public void Add(DownloadDecision decision)
|
||||
Insert(decision);
|
||||
}
|
||||
|
||||
public void RemoveGrabbed(List<DownloadDecision> grabbed)
|
||||
{
|
||||
_logger.Debug("Removing grabbed releases from pending");
|
||||
var alreadyPending = GetPendingReleases();
|
||||
|
||||
foreach (var decision in grabbed)
|
||||
{
|
||||
var decisionLocal = decision;
|
||||
var episodeIds = decisionLocal.RemoteEpisode.Episodes.Select(e => e.Id);
|
||||
|
||||
var existingReports = alreadyPending.Where(r => r.RemoteEpisode.Episodes.Select(e => e.Id)
|
||||
.Intersect(episodeIds)
|
||||
.Any())
|
||||
.ToList();
|
||||
|
||||
if (existingReports.Empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var profile = decisionLocal.RemoteEpisode.Series.Profile.Value;
|
||||
|
||||
foreach (var existingReport in existingReports)
|
||||
{
|
||||
var compare = new QualityModelComparer(profile).Compare(decision.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||
existingReport.RemoteEpisode.ParsedEpisodeInfo.Quality);
|
||||
|
||||
//Only remove lower/equal quality pending releases
|
||||
//It is safer to retry these releases on the next round than remove it and try to re-add it (if its still in the feed)
|
||||
if (compare >= 0)
|
||||
{
|
||||
_logger.Debug("Removing previously pending release, as it was grabbed.");
|
||||
Delete(existingReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveRejected(List<DownloadDecision> rejected)
|
||||
{
|
||||
_logger.Debug("Removing failed releases from pending");
|
||||
var pending = GetPendingReleases();
|
||||
|
||||
foreach (var rejectedRelease in rejected)
|
||||
{
|
||||
var matching = pending.SingleOrDefault(MatchingReleasePredicate(rejectedRelease));
|
||||
|
||||
if (matching != null)
|
||||
{
|
||||
_logger.Debug("Removing previously pending release, as it has now been rejected.");
|
||||
Delete(matching);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<ReleaseInfo> GetPending()
|
||||
{
|
||||
return _repository.All().Select(p => p.Release).ToList();
|
||||
@ -165,6 +116,18 @@ public List<RemoteEpisode> GetPendingRemoteEpisodes(int seriesId)
|
||||
return queued;
|
||||
}
|
||||
|
||||
public Queue.Queue FindPendingQueueItem(int queueId)
|
||||
{
|
||||
return GetPendingQueue().SingleOrDefault(p => p.Id == queueId);
|
||||
}
|
||||
|
||||
public void RemovePendingQueueItem(int queueId)
|
||||
{
|
||||
var id = FindPendingReleaseId(queueId);
|
||||
|
||||
_repository.Delete(id);
|
||||
}
|
||||
|
||||
public RemoteEpisode OldestPendingRelease(int seriesId, IEnumerable<int> episodeIds)
|
||||
{
|
||||
return GetPendingRemoteEpisodes(seriesId)
|
||||
@ -243,9 +206,73 @@ private int GetDelay(RemoteEpisode remoteEpisode)
|
||||
return delayProfile.GetProtocolDelay(remoteEpisode.Release.DownloadProtocol);
|
||||
}
|
||||
|
||||
private void RemoveGrabbed(RemoteEpisode remoteEpisode)
|
||||
{
|
||||
var pendingReleases = GetPendingReleases();
|
||||
var episodeIds = remoteEpisode.Episodes.Select(e => e.Id);
|
||||
|
||||
var existingReports = pendingReleases.Where(r => r.RemoteEpisode.Episodes.Select(e => e.Id)
|
||||
.Intersect(episodeIds)
|
||||
.Any())
|
||||
.ToList();
|
||||
|
||||
if (existingReports.Empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var profile = remoteEpisode.Series.Profile.Value;
|
||||
|
||||
foreach (var existingReport in existingReports)
|
||||
{
|
||||
var compare = new QualityModelComparer(profile).Compare(remoteEpisode.ParsedEpisodeInfo.Quality,
|
||||
existingReport.RemoteEpisode.ParsedEpisodeInfo.Quality);
|
||||
|
||||
//Only remove lower/equal quality pending releases
|
||||
//It is safer to retry these releases on the next round than remove it and try to re-add it (if its still in the feed)
|
||||
if (compare >= 0)
|
||||
{
|
||||
_logger.Debug("Removing previously pending release, as it was grabbed.");
|
||||
Delete(existingReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveRejected(List<DownloadDecision> rejected)
|
||||
{
|
||||
_logger.Debug("Removing failed releases from pending");
|
||||
var pending = GetPendingReleases();
|
||||
|
||||
foreach (var rejectedRelease in rejected)
|
||||
{
|
||||
var matching = pending.SingleOrDefault(MatchingReleasePredicate(rejectedRelease));
|
||||
|
||||
if (matching != null)
|
||||
{
|
||||
_logger.Debug("Removing previously pending release, as it has now been rejected.");
|
||||
Delete(matching);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int FindPendingReleaseId(int queueId)
|
||||
{
|
||||
return GetPendingReleases().First(p => p.RemoteEpisode.Episodes.Any(e => queueId == (e.Id ^ (p.Id << 16)))).Id;
|
||||
}
|
||||
|
||||
public void Handle(SeriesDeletedEvent message)
|
||||
{
|
||||
_repository.DeleteBySeriesId(message.Series.Id);
|
||||
}
|
||||
|
||||
public void Handle(EpisodeGrabbedEvent message)
|
||||
{
|
||||
RemoveGrabbed(message.Episode);
|
||||
}
|
||||
|
||||
public void Handle(RssSyncCompleteEvent message)
|
||||
{
|
||||
RemoveRejected(message.ProcessedDecisions.Rejected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ public ProcessedDecisions ProcessDecisions(List<DownloadDecision> decisions)
|
||||
}
|
||||
}
|
||||
|
||||
return new ProcessedDecisions(grabbed, pending);
|
||||
return new ProcessedDecisions(grabbed, pending, decisions.Where(d => d.Rejected).ToList());
|
||||
}
|
||||
|
||||
internal List<DownloadDecision> GetQualifiedReports(IEnumerable<DownloadDecision> decisions)
|
||||
|
@ -7,11 +7,13 @@ public class ProcessedDecisions
|
||||
{
|
||||
public List<DownloadDecision> Grabbed { get; set; }
|
||||
public List<DownloadDecision> Pending { get; set; }
|
||||
public List<DownloadDecision> Rejected { get; set; }
|
||||
|
||||
public ProcessedDecisions(List<DownloadDecision> grabbed, List<DownloadDecision> pending)
|
||||
public ProcessedDecisions(List<DownloadDecision> grabbed, List<DownloadDecision> pending, List<DownloadDecision> rejected)
|
||||
{
|
||||
Grabbed = grabbed;
|
||||
Pending = pending;
|
||||
Rejected = rejected;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,10 @@ public class TrackedDownloadStatusMessage
|
||||
public String Title { get; set; }
|
||||
public List<String> Messages { get; set; }
|
||||
|
||||
private TrackedDownloadStatusMessage()
|
||||
{
|
||||
}
|
||||
|
||||
public TrackedDownloadStatusMessage(String title, List<String> messages)
|
||||
{
|
||||
Title = title;
|
||||
|
@ -1,8 +1,15 @@
|
||||
using NzbDrone.Common.Messaging;
|
||||
using NzbDrone.Core.Download;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
public class RssSyncCompleteEvent : IEvent
|
||||
{
|
||||
public ProcessedDecisions ProcessedDecisions { get; private set; }
|
||||
|
||||
public RssSyncCompleteEvent(ProcessedDecisions processedDecisions)
|
||||
{
|
||||
ProcessedDecisions = processedDecisions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,15 +40,13 @@ public RssSyncService(IFetchAndParseRss rssFetcherAndParser,
|
||||
}
|
||||
|
||||
|
||||
private List<DownloadDecision> Sync()
|
||||
private ProcessedDecisions Sync()
|
||||
{
|
||||
_logger.ProgressInfo("Starting RSS Sync");
|
||||
|
||||
var reports = _rssFetcherAndParser.Fetch().Concat(_pendingReleaseService.GetPending()).ToList();
|
||||
var decisions = _downloadDecisionMaker.GetRssDecision(reports);
|
||||
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
|
||||
_pendingReleaseService.RemoveGrabbed(processed.Grabbed);
|
||||
_pendingReleaseService.RemoveRejected(decisions.Where(d => d.Rejected).ToList());
|
||||
|
||||
var message = String.Format("RSS Sync Completed. Reports found: {0}, Reports grabbed: {1}", reports.Count, processed.Grabbed.Count);
|
||||
|
||||
@ -59,20 +57,21 @@ private List<DownloadDecision> Sync()
|
||||
|
||||
_logger.ProgressInfo(message);
|
||||
|
||||
return processed.Grabbed.Concat(processed.Pending).ToList();
|
||||
return processed;
|
||||
}
|
||||
|
||||
public void Execute(RssSyncCommand message)
|
||||
{
|
||||
var processed = Sync();
|
||||
var grabbedOrPending = processed.Grabbed.Concat(processed.Pending).ToList();
|
||||
|
||||
if (message.LastExecutionTime.HasValue && DateTime.UtcNow.Subtract(message.LastExecutionTime.Value).TotalHours > 3)
|
||||
{
|
||||
_logger.Info("RSS Sync hasn't run since: {0}. Searching for any missing episodes since then.", message.LastExecutionTime.Value);
|
||||
_episodeSearchService.MissingEpisodesAiredAfter(message.LastExecutionTime.Value.AddDays(-1), processed.SelectMany(d => d.RemoteEpisode.Episodes).Select(e => e.Id));
|
||||
_episodeSearchService.MissingEpisodesAiredAfter(message.LastExecutionTime.Value.AddDays(-1), grabbedOrPending.SelectMany(d => d.RemoteEpisode.Episodes).Select(e => e.Id));
|
||||
}
|
||||
|
||||
_eventAggregator.PublishEvent(new RssSyncCompleteEvent());
|
||||
_eventAggregator.PublishEvent(new RssSyncCompleteEvent(processed));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv;
|
||||
@ -17,7 +16,9 @@ public interface IDownloadedEpisodesImportService
|
||||
{
|
||||
List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo);
|
||||
List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem = null);
|
||||
List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, Series series, DownloadClientItem downloadClientItem = null);
|
||||
List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem downloadClientItem = null);
|
||||
List<ImportResult> ProcessFile(FileInfo fileInfo, Series series, DownloadClientItem downloadClientItem = null);
|
||||
}
|
||||
|
||||
public class DownloadedEpisodesImportService : IDownloadedEpisodesImportService
|
||||
@ -26,7 +27,6 @@ public class DownloadedEpisodesImportService : IDownloadedEpisodesImportService
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
private readonly ISeriesService _seriesService;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IMakeImportDecision _importDecisionMaker;
|
||||
private readonly IImportApprovedEpisodes _importApprovedEpisodes;
|
||||
private readonly ISampleService _sampleService;
|
||||
@ -36,7 +36,6 @@ public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
IDiskScanService diskScanService,
|
||||
ISeriesService seriesService,
|
||||
IParsingService parsingService,
|
||||
IConfigService configService,
|
||||
IMakeImportDecision importDecisionMaker,
|
||||
IImportApprovedEpisodes importApprovedEpisodes,
|
||||
ISampleService sampleService,
|
||||
@ -46,7 +45,6 @@ public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
_diskScanService = diskScanService;
|
||||
_seriesService = seriesService;
|
||||
_parsingService = parsingService;
|
||||
_configService = configService;
|
||||
_importDecisionMaker = importDecisionMaker;
|
||||
_importApprovedEpisodes = importApprovedEpisodes;
|
||||
_sampleService = sampleService;
|
||||
@ -73,6 +71,25 @@ public List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo)
|
||||
}
|
||||
|
||||
public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem = null)
|
||||
{
|
||||
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
|
||||
var series = _parsingService.GetSeries(cleanedUpName);
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unknown Series {0}", cleanedUpName);
|
||||
|
||||
return new List<ImportResult>
|
||||
{
|
||||
UnknownSeriesResult("Unknown Series")
|
||||
};
|
||||
}
|
||||
|
||||
return ProcessFolder(directoryInfo, series, downloadClientItem);
|
||||
}
|
||||
|
||||
public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, Series series,
|
||||
DownloadClientItem downloadClientItem = null)
|
||||
{
|
||||
if (_seriesService.SeriesPathExists(directoryInfo.FullName))
|
||||
{
|
||||
@ -81,18 +98,9 @@ public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadCli
|
||||
}
|
||||
|
||||
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
|
||||
var series = _parsingService.GetSeries(cleanedUpName);
|
||||
var quality = QualityParser.ParseQuality(cleanedUpName);
|
||||
_logger.Debug("{0} folder quality: {1}", cleanedUpName, quality);
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unknown Series {0}", cleanedUpName);
|
||||
return new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(null, "Unknown Series"), "Unknown Series")
|
||||
};
|
||||
}
|
||||
_logger.Debug("{0} folder quality: {1}", cleanedUpName, quality);
|
||||
|
||||
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
|
||||
|
||||
@ -102,20 +110,18 @@ public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadCli
|
||||
{
|
||||
if (_diskProvider.IsFileLocked(videoFile))
|
||||
{
|
||||
_logger.Debug("[{0}] is currently locked by another process, skipping", videoFile);
|
||||
return new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, "Locked file, try again later"), "Locked file, try again later")
|
||||
FileIsLockedResult(videoFile)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), series, true, quality);
|
||||
|
||||
var importResults = _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
|
||||
|
||||
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && importResults.Any() && ShouldDeleteFolder(directoryInfo))
|
||||
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && importResults.Any() && ShouldDeleteFolder(directoryInfo, series))
|
||||
{
|
||||
_logger.Debug("Deleting folder after importing valid files");
|
||||
_diskProvider.DeleteFolder(directoryInfo.FullName, true);
|
||||
@ -131,15 +137,26 @@ public List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem down
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unknown Series for file: {0}", fileInfo.Name);
|
||||
return new List<ImportResult>() { new ImportResult(new ImportDecision(new LocalEpisode { Path = fileInfo.FullName }, "Unknown Series"), String.Format("Unknown Series for file: {0}", fileInfo.Name)) };
|
||||
|
||||
return new List<ImportResult>
|
||||
{
|
||||
UnknownSeriesResult(String.Format("Unknown Series for file: {0}", fileInfo.Name), fileInfo.FullName)
|
||||
};
|
||||
}
|
||||
|
||||
return ProcessFile(fileInfo, series, downloadClientItem);
|
||||
}
|
||||
|
||||
public List<ImportResult> ProcessFile(FileInfo fileInfo, Series series, DownloadClientItem downloadClientItem = null)
|
||||
{
|
||||
if (downloadClientItem == null)
|
||||
{
|
||||
if (_diskProvider.IsFileLocked(fileInfo.FullName))
|
||||
{
|
||||
_logger.Debug("[{0}] is currently locked by another process, skipping", fileInfo.FullName);
|
||||
return new List<ImportResult>();
|
||||
return new List<ImportResult>
|
||||
{
|
||||
FileIsLockedResult(fileInfo.FullName)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,11 +172,9 @@ private string GetCleanedUpFolderName(string folder)
|
||||
return folder;
|
||||
}
|
||||
|
||||
private bool ShouldDeleteFolder(DirectoryInfo directoryInfo)
|
||||
private bool ShouldDeleteFolder(DirectoryInfo directoryInfo, Series series)
|
||||
{
|
||||
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
|
||||
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
|
||||
var series = _parsingService.GetSeries(cleanedUpName);
|
||||
|
||||
foreach (var videoFile in videoFiles)
|
||||
{
|
||||
@ -184,5 +199,18 @@ private bool ShouldDeleteFolder(DirectoryInfo directoryInfo)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ImportResult FileIsLockedResult(string videoFile)
|
||||
{
|
||||
_logger.Debug("[{0}] is currently locked by another process, skipping", videoFile);
|
||||
return new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, "Locked file, try again later"), "Locked file, try again later");
|
||||
}
|
||||
|
||||
private ImportResult UnknownSeriesResult(string message, string videoFile = null)
|
||||
{
|
||||
var localEpisode = videoFile == null ? null : new LocalEpisode { Path = videoFile };
|
||||
|
||||
return new ImportResult(new ImportDecision(localEpisode, "Unknown Series"), message);
|
||||
}
|
||||
}
|
||||
}
|
@ -59,40 +59,40 @@ private IEnumerable<ImportDecision> GetDecisions(IEnumerable<String> videoFiles,
|
||||
|
||||
try
|
||||
{
|
||||
var parsedEpisode = _parsingService.GetLocalEpisode(file, series, sceneSource);
|
||||
var localEpisode = _parsingService.GetLocalEpisode(file, series, sceneSource);
|
||||
|
||||
if (parsedEpisode != null)
|
||||
if (localEpisode != null)
|
||||
{
|
||||
if (quality != null &&
|
||||
new QualityModelComparer(parsedEpisode.Series.Profile).Compare(quality,
|
||||
parsedEpisode.Quality) > 0)
|
||||
new QualityModelComparer(localEpisode.Series.Profile).Compare(quality,
|
||||
localEpisode.Quality) > 0)
|
||||
{
|
||||
_logger.Debug("Using quality from folder: {0}", quality);
|
||||
parsedEpisode.Quality = quality;
|
||||
localEpisode.Quality = quality;
|
||||
}
|
||||
|
||||
parsedEpisode.Size = _diskProvider.GetFileSize(file);
|
||||
_logger.Debug("Size: {0}", parsedEpisode.Size);
|
||||
localEpisode.Size = _diskProvider.GetFileSize(file);
|
||||
_logger.Debug("Size: {0}", localEpisode.Size);
|
||||
|
||||
parsedEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
|
||||
localEpisode.MediaInfo = _videoFileInfoReader.GetMediaInfo(file);
|
||||
|
||||
decision = GetDecision(parsedEpisode);
|
||||
decision = GetDecision(localEpisode);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
parsedEpisode = new LocalEpisode();
|
||||
parsedEpisode.Path = file;
|
||||
localEpisode = new LocalEpisode();
|
||||
localEpisode.Path = file;
|
||||
|
||||
decision = new ImportDecision(parsedEpisode, "Unable to parse file");
|
||||
decision = new ImportDecision(localEpisode, "Unable to parse file");
|
||||
}
|
||||
}
|
||||
catch (EpisodeNotFoundException e)
|
||||
{
|
||||
var parsedEpisode = new LocalEpisode();
|
||||
parsedEpisode.Path = file;
|
||||
var localEpisode = new LocalEpisode();
|
||||
localEpisode.Path = file;
|
||||
|
||||
decision = new ImportDecision(parsedEpisode, e.Message);
|
||||
decision = new ImportDecision(localEpisode, e.Message);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles.EpisodeImport
|
||||
{
|
||||
public class ManualImportService
|
||||
{
|
||||
public ManualImportService(Logger logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -543,6 +543,7 @@
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecision.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMaker.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportResultType.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ManualImportService.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\SampleService.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FreeSpaceSpecification.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\Specifications\FullSeasonSpecification.cs" />
|
||||
|
@ -8,6 +8,7 @@ namespace NzbDrone.Core.Queue
|
||||
public interface IQueueService
|
||||
{
|
||||
List<Queue> GetQueue();
|
||||
Queue Find(int id);
|
||||
}
|
||||
|
||||
public class QueueService : IQueueService
|
||||
@ -28,6 +29,11 @@ public List<Queue> GetQueue()
|
||||
return MapQueue(queueItems);
|
||||
}
|
||||
|
||||
public Queue Find(int id)
|
||||
{
|
||||
return GetQueue().SingleOrDefault(q => q.Id == id);
|
||||
}
|
||||
|
||||
private List<Queue> MapQueue(IEnumerable<TrackedDownload> trackedDownloads)
|
||||
{
|
||||
var queued = new List<Queue>();
|
||||
|
93
src/UI/Activity/Queue/QueueActionsCell.js
Normal file
93
src/UI/Activity/Queue/QueueActionsCell.js
Normal file
@ -0,0 +1,93 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'jquery',
|
||||
'marionette',
|
||||
'Cells/NzbDroneCell'
|
||||
], function ($, Marionette, NzbDroneCell) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className : 'queue-actions-cell',
|
||||
|
||||
events: {
|
||||
'click .x-remove' : '_remove',
|
||||
'click .x-import' : '_import',
|
||||
'click .x-grab' : '_grab'
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
|
||||
if (this.cellValue) {
|
||||
var status = this.cellValue.get('status').toLowerCase();
|
||||
var trackedDownloadStatus = this.cellValue.has('trackedDownloadStatus') ? this.cellValue.get('trackedDownloadStatus').toLowerCase() : 'ok';
|
||||
var icon = '';
|
||||
var title = '';
|
||||
|
||||
if (status === 'completed' && trackedDownloadStatus === 'warning') {
|
||||
icon = 'icon-inbox x-import';
|
||||
title = 'Force import';
|
||||
}
|
||||
|
||||
if (status === 'pending') {
|
||||
icon = 'icon-download-alt x-grab';
|
||||
title = 'Add to download queue (Override Delay Profile)';
|
||||
}
|
||||
|
||||
//TODO: Show manual import if its completed or option to blacklist
|
||||
//if (trackedDownloadStatus === 'error') {
|
||||
// if (status === 'completed') {
|
||||
// icon = 'icon-nd-import-failed';
|
||||
// title = 'Import failed: ' + itemTitle;
|
||||
// }
|
||||
//TODO: What do we show when waiting for retry to take place?
|
||||
|
||||
// else {
|
||||
// icon = 'icon-nd-download-failed';
|
||||
// title = 'Download failed';
|
||||
// }
|
||||
//}
|
||||
|
||||
this.$el.html('<i class="{0}" title="{1}"></i>'.format(icon, title) +
|
||||
'<i class="icon-nd-delete x-remove" title="Remove from Download Client"></i>');
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_remove : function () {
|
||||
this.model.destroy();
|
||||
},
|
||||
|
||||
_import : function () {
|
||||
var self = this;
|
||||
|
||||
var promise = $.ajax({
|
||||
url: window.NzbDrone.ApiRoot + '/queue/import',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(this.model.toJSON())
|
||||
});
|
||||
|
||||
promise.success(function () {
|
||||
//find models that have the same series id and episode ids and remove them
|
||||
self.model.trigger('destroy', self.model);
|
||||
});
|
||||
},
|
||||
|
||||
_grab : function () {
|
||||
var self = this;
|
||||
|
||||
var promise = $.ajax({
|
||||
url: window.NzbDrone.ApiRoot + '/queue/grab',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(this.model.toJSON())
|
||||
});
|
||||
|
||||
promise.success(function () {
|
||||
//find models that have the same series id and episode ids and remove them
|
||||
self.model.trigger('destroy', self.model);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
@ -9,6 +9,7 @@ define(
|
||||
'Cells/EpisodeTitleCell',
|
||||
'Cells/QualityCell',
|
||||
'Activity/Queue/QueueStatusCell',
|
||||
'Activity/Queue/QueueActionsCell',
|
||||
'Activity/Queue/TimeleftCell',
|
||||
'Activity/Queue/ProgressCell',
|
||||
'Shared/Grid/Pager'
|
||||
@ -20,6 +21,7 @@ define(
|
||||
EpisodeTitleCell,
|
||||
QualityCell,
|
||||
QueueStatusCell,
|
||||
QueueActionsCell,
|
||||
TimeleftCell,
|
||||
ProgressCell,
|
||||
GridPager) {
|
||||
@ -74,6 +76,12 @@ define(
|
||||
label : 'Progress',
|
||||
cell : ProgressCell,
|
||||
cellValue : 'this'
|
||||
},
|
||||
{
|
||||
name : 'status',
|
||||
label : '',
|
||||
cell : QueueActionsCell,
|
||||
cellValue : 'this'
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -147,24 +147,34 @@ td.episode-status-cell, td.quality-cell, td.history-quality-cell, td.progress-ce
|
||||
.timeleft-cell {
|
||||
cursor : default;
|
||||
width : 80px;
|
||||
text-align: center;
|
||||
text-align : center;
|
||||
}
|
||||
|
||||
.queue-status-cell {
|
||||
width: 20px;
|
||||
text-align: center !important;
|
||||
width : 20px;
|
||||
text-align : center !important;
|
||||
}
|
||||
|
||||
.queue-actions-cell {
|
||||
width : 55px;
|
||||
text-align : right !important;
|
||||
|
||||
i {
|
||||
margin-left : 3px;
|
||||
margin-right : 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.download-log-cell {
|
||||
width: 80px;
|
||||
width : 80px;
|
||||
}
|
||||
|
||||
td.delete-episode-file-cell {
|
||||
.clickable();
|
||||
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
font-size: 20px;
|
||||
text-align : center;
|
||||
width : 20px;
|
||||
font-size : 20px;
|
||||
|
||||
i {
|
||||
.clickable();
|
||||
|
@ -24,7 +24,8 @@ define(
|
||||
'slide .x-slider': '_updateSize'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
initialize: function (options) {
|
||||
this.profileCollection = options.profiles;
|
||||
this.filesize = fileSize;
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user