mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +01:00
Merge pull request #116 from NzbDrone/cdh-improvements
CDH Improvements
This commit is contained in:
commit
73963011ee
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Api.REST;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Api.Series;
|
||||
using NzbDrone.Api.Episodes;
|
||||
@ -17,6 +19,7 @@ public class QueueResource : RestResource
|
||||
public TimeSpan? Timeleft { get; set; }
|
||||
public DateTime? EstimatedCompletionTime { get; set; }
|
||||
public String Status { get; set; }
|
||||
public String ErrorMessage { get; set; }
|
||||
public String TrackedDownloadStatus { get; set; }
|
||||
public List<TrackedDownloadStatusMessage> StatusMessages { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -69,10 +69,7 @@ private void GivenQueue(IEnumerable<RemoteEpisode> remoteEpisodes, TrackedDownlo
|
||||
queue.Add(new TrackedDownload
|
||||
{
|
||||
State = state,
|
||||
DownloadItem = new DownloadClientItem
|
||||
{
|
||||
RemoteEpisode = remoteEpisode
|
||||
}
|
||||
RemoteEpisode = remoteEpisode
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9,11 +9,13 @@
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download
|
||||
@ -27,15 +29,18 @@ public class CompletedDownloadServiceFixture : CoreTest<DownloadTrackingService>
|
||||
public void Setup()
|
||||
{
|
||||
_completed = Builder<DownloadClientItem>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Completed)
|
||||
.With(h => h.OutputPath = @"C:\DropFolder\MyDownload".AsOsAgnostic())
|
||||
.With(h => h.RemoteEpisode = new RemoteEpisode
|
||||
{
|
||||
Episodes = new List<Episode> { new Episode { Id = 1 } }
|
||||
})
|
||||
.Build()
|
||||
.ToList();
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Completed)
|
||||
.With(h => h.OutputPath = @"C:\DropFolder\MyDownload".AsOsAgnostic())
|
||||
.With(h => h.Title = "Drone.S01E01.HDTV")
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
var remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
Series = new Series(),
|
||||
Episodes = new List<Episode> {new Episode {Id = 1}}
|
||||
};
|
||||
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(c => c.GetDownloadClients())
|
||||
@ -43,7 +48,7 @@ public void Setup()
|
||||
|
||||
Mocker.GetMock<IDownloadClient>()
|
||||
.SetupGet(c => c.Definition)
|
||||
.Returns(new Core.Download.DownloadClientDefinition { Id = 1, Name = "testClient" });
|
||||
.Returns(new DownloadClientDefinition { Id = 1, Name = "testClient" });
|
||||
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.EnableCompletedDownloadHandling)
|
||||
@ -56,6 +61,14 @@ public void Setup()
|
||||
Mocker.GetMock<IHistoryService>()
|
||||
.Setup(s => s.Failed())
|
||||
.Returns(new List<History.History>());
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Int32>(), It.IsAny<IEnumerable<Int32>>()))
|
||||
.Returns(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Int32>(), (SearchCriteriaBase)null))
|
||||
.Returns(remoteEpisode);
|
||||
|
||||
Mocker.SetConstant<ICompletedDownloadService>(Mocker.Resolve<CompletedDownloadService>());
|
||||
}
|
||||
@ -311,10 +324,7 @@ public void should_process_as_already_imported_if_drone_factory_import_history_e
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Completed)
|
||||
.With(h => h.OutputPath = @"C:\DropFolder\MyDownload".AsOsAgnostic())
|
||||
.With(h => h.RemoteEpisode = new RemoteEpisode
|
||||
{
|
||||
Episodes = new List<Episode> { new Episode { Id = 1 } }
|
||||
})
|
||||
.With(h => h.Title = "Drone.S01E01.HDTV")
|
||||
.Build());
|
||||
|
||||
var grabbedHistory = Builder<History.History>.CreateListOfSize(2)
|
||||
|
@ -5,6 +5,7 @@
|
||||
using NUnit.Framework;
|
||||
using FluentAssertions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Parser;
|
||||
@ -30,7 +31,7 @@ public void SetupBase()
|
||||
.Returns(30);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), null))
|
||||
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), (SearchCriteriaBase)null))
|
||||
.Returns(() => CreateRemoteEpisode());
|
||||
|
||||
Mocker.GetMock<IHttpClient>()
|
||||
@ -64,11 +65,7 @@ protected void VerifyIdentifiable(DownloadClientItem downloadClientItem)
|
||||
{
|
||||
downloadClientItem.DownloadClient.Should().Be(Subject.Definition.Name);
|
||||
downloadClientItem.DownloadClientId.Should().NotBeNullOrEmpty();
|
||||
|
||||
downloadClientItem.Title.Should().NotBeNullOrEmpty();
|
||||
|
||||
downloadClientItem.RemoteEpisode.Should().NotBeNull();
|
||||
|
||||
}
|
||||
|
||||
protected void VerifyQueued(DownloadClientItem downloadClientItem)
|
||||
|
@ -7,8 +7,12 @@
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.Download
|
||||
@ -23,16 +27,24 @@ public class FailedDownloadServiceFixture : CoreTest<DownloadTrackingService>
|
||||
public void Setup()
|
||||
{
|
||||
_completed = Builder<DownloadClientItem>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Completed)
|
||||
.Build()
|
||||
.ToList();
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Completed)
|
||||
.With(h => h.Title = "Drone.S01E01.HDTV")
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_failed = Builder<DownloadClientItem>.CreateListOfSize(1)
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Failed)
|
||||
.Build()
|
||||
.ToList();
|
||||
.All()
|
||||
.With(h => h.Status = DownloadItemStatus.Failed)
|
||||
.With(h => h.Title = "Drone.S01E01.HDTV")
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
var remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
Series = new Series(),
|
||||
Episodes = new List<Episode> { new Episode { Id = 1 } }
|
||||
};
|
||||
|
||||
Mocker.GetMock<IProvideDownloadClient>()
|
||||
.Setup(c => c.GetDownloadClients())
|
||||
@ -40,7 +52,7 @@ public void Setup()
|
||||
|
||||
Mocker.GetMock<IDownloadClient>()
|
||||
.SetupGet(c => c.Definition)
|
||||
.Returns(new Core.Download.DownloadClientDefinition { Id = 1, Name = "testClient" });
|
||||
.Returns(new DownloadClientDefinition { Id = 1, Name = "testClient" });
|
||||
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.EnableFailedDownloadHandling)
|
||||
@ -50,6 +62,14 @@ public void Setup()
|
||||
.Setup(s => s.Imported())
|
||||
.Returns(new List<History.History>());
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Int32>(), It.IsAny<IEnumerable<Int32>>()))
|
||||
.Returns(remoteEpisode);
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<Int32>(), (SearchCriteriaBase)null))
|
||||
.Returns(remoteEpisode);
|
||||
|
||||
Mocker.SetConstant<IFailedDownloadService>(Mocker.Resolve<FailedDownloadService>());
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ public bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriter
|
||||
{
|
||||
var queue = _downloadTrackingService.GetQueuedDownloads()
|
||||
.Where(v => v.State == TrackedDownloadState.Downloading)
|
||||
.Select(q => q.DownloadItem.RemoteEpisode).ToList();
|
||||
.Select(q => q.RemoteEpisode).ToList();
|
||||
|
||||
if (IsInQueue(subject, queue))
|
||||
{
|
||||
|
@ -201,9 +201,6 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
if (downloadClientItem.Category == Settings.TvCategory)
|
||||
{
|
||||
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
|
||||
if (downloadClientItem.RemoteEpisode == null) continue;
|
||||
|
||||
yield return downloadClientItem;
|
||||
}
|
||||
}
|
||||
|
@ -103,9 +103,6 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
historyItem.Status = DownloadItemStatus.Completed;
|
||||
}
|
||||
|
||||
historyItem.RemoteEpisode = GetRemoteEpisode(historyItem.Title);
|
||||
if (historyItem.RemoteEpisode == null) continue;
|
||||
|
||||
yield return historyItem;
|
||||
}
|
||||
}
|
||||
|
@ -188,9 +188,6 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
{
|
||||
if (downloadClientItem.Category == Settings.TvCategory)
|
||||
{
|
||||
downloadClientItem.RemoteEpisode = GetRemoteEpisode(downloadClientItem.Title);
|
||||
if (downloadClientItem.RemoteEpisode == null) continue;
|
||||
|
||||
yield return downloadClientItem;
|
||||
}
|
||||
}
|
||||
|
@ -90,9 +90,6 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
historyItem.RemainingTime = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
historyItem.RemoteEpisode = GetRemoteEpisode(historyItem.Title);
|
||||
if (historyItem.RemoteEpisode == null) continue;
|
||||
|
||||
yield return historyItem;
|
||||
}
|
||||
|
||||
@ -121,9 +118,6 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
historyItem.RemainingTime = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
historyItem.RemoteEpisode = GetRemoteEpisode(historyItem.Title);
|
||||
if (historyItem.RemoteEpisode == null) continue;
|
||||
|
||||
yield return historyItem;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using System.IO;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
@ -20,21 +19,18 @@ public interface ICompletedDownloadService
|
||||
|
||||
public class CompletedDownloadService : ICompletedDownloadService
|
||||
{
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CompletedDownloadService(IEventAggregator eventAggregator,
|
||||
IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
IDownloadedEpisodesImportService downloadedEpisodesImportService,
|
||||
IHistoryService historyService,
|
||||
Logger logger)
|
||||
public CompletedDownloadService(IConfigService configService,
|
||||
IDiskProvider diskProvider,
|
||||
IDownloadedEpisodesImportService downloadedEpisodesImportService,
|
||||
IHistoryService historyService,
|
||||
Logger logger)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_configService = configService;
|
||||
_diskProvider = diskProvider;
|
||||
_downloadedEpisodesImportService = downloadedEpisodesImportService;
|
||||
@ -61,7 +57,7 @@ public void CheckForCompletedItem(IDownloadClient downloadClient, TrackedDownloa
|
||||
|
||||
if (!grabbedItems.Any() && trackedDownload.DownloadItem.Category.IsNullOrWhiteSpace())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Download wasn't grabbed by drone or not in a category, ignoring download.");
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download wasn't grabbed by drone or not in a category, ignoring download.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -73,10 +69,16 @@ public void CheckForCompletedItem(IDownloadClient downloadClient, TrackedDownloa
|
||||
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Already added to history as imported.");
|
||||
}
|
||||
else if (trackedDownload.Status != TrackedDownloadStatus.Ok)
|
||||
{
|
||||
_logger.Debug("Tracked download status is: {0}, skipping import.", trackedDownload.Status);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
string downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder;
|
||||
string downloadItemOutputPath = trackedDownload.DownloadItem.OutputPath;
|
||||
var downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder;
|
||||
var downloadItemOutputPath = trackedDownload.DownloadItem.OutputPath;
|
||||
|
||||
if (downloadItemOutputPath.IsNullOrWhiteSpace())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download doesn't contain intermediate path, ignoring download.");
|
||||
@ -105,7 +107,7 @@ public void CheckForCompletedItem(IDownloadClient downloadClient, TrackedDownloa
|
||||
{
|
||||
if (grabbedItems.Any())
|
||||
{
|
||||
var episodeIds = trackedDownload.DownloadItem.RemoteEpisode.Episodes.Select(v => v.Id).ToList();
|
||||
var episodeIds = trackedDownload.RemoteEpisode.Episodes.Select(v => v.Id).ToList();
|
||||
|
||||
// Check if we can associate it with a previous drone factory import.
|
||||
importedItems = importedHistory.Where(v => v.Data.GetValueOrDefault(DownloadTrackingService.DOWNLOAD_CLIENT_ID) == null &&
|
||||
@ -171,7 +173,7 @@ private void UpdateStatusMessage(TrackedDownload trackedDownload, LogLevel logLe
|
||||
|
||||
if (trackedDownload.StatusMessage != statusMessage)
|
||||
{
|
||||
trackedDownload.HasError = logLevel >= LogLevel.Warn;
|
||||
trackedDownload.SetStatusLevel(logLevel);
|
||||
trackedDownload.StatusMessage = statusMessage;
|
||||
_logger.Log(logLevel, logMessage);
|
||||
}
|
||||
@ -200,6 +202,9 @@ private void ProcessImportResults(TrackedDownload trackedDownload, List<ImportRe
|
||||
.Select(v => v.Errors.Aggregate(Path.GetFileName(v.ImportDecision.LocalEpisode.Path), (a, r) => a + "\r\n- " + r))
|
||||
.Aggregate("Failed to import:", (a, r) => a + "\r\n" + r);
|
||||
|
||||
trackedDownload.StatusMessages = importResults.Where(v => v.Result == ImportResultType.Skipped || v.Result == ImportResultType.Rejected)
|
||||
.Select(v => new TrackedDownloadStatusMessage(Path.GetFileName(v.ImportDecision.LocalEpisode.Path), v.Errors)).ToList();
|
||||
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Error, errors);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
@ -21,7 +20,6 @@ public abstract class DownloadClientBase<TSettings> : IDownloadClient
|
||||
{
|
||||
protected readonly IConfigService _configService;
|
||||
protected readonly IDiskProvider _diskProvider;
|
||||
protected readonly IParsingService _parsingService;
|
||||
protected readonly IRemotePathMappingService _remotePathMappingService;
|
||||
protected readonly Logger _logger;
|
||||
|
||||
@ -55,7 +53,6 @@ protected DownloadClientBase(IConfigService configService, IDiskProvider diskPro
|
||||
{
|
||||
_configService = configService;
|
||||
_diskProvider = diskProvider;
|
||||
_parsingService = parsingService;
|
||||
_remotePathMappingService = remotePathMappingService;
|
||||
_logger = logger;
|
||||
}
|
||||
@ -76,17 +73,6 @@ public abstract DownloadProtocol Protocol
|
||||
public abstract String RetryDownload(string id);
|
||||
public abstract DownloadClientStatus GetStatus();
|
||||
|
||||
protected RemoteEpisode GetRemoteEpisode(String title)
|
||||
{
|
||||
var parsedEpisodeInfo = Parser.Parser.ParseTitle(title);
|
||||
if (parsedEpisodeInfo == null) return null;
|
||||
|
||||
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0);
|
||||
if (remoteEpisode.Series == null) return null;
|
||||
|
||||
return remoteEpisode;
|
||||
}
|
||||
|
||||
public ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -1,5 +1,4 @@
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
@ -21,7 +20,5 @@ public class DownloadClientItem
|
||||
public DownloadItemStatus Status { get; set; }
|
||||
public Boolean IsEncrypted { get; set; }
|
||||
public Boolean IsReadOnly { get; set; }
|
||||
|
||||
public RemoteEpisode RemoteEpisode { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public enum DownloadItemStatus
|
||||
{
|
||||
|
@ -9,6 +9,7 @@
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Lifecycle;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Queue;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
@ -22,7 +23,10 @@ public interface IDownloadTrackingService
|
||||
void MarkAsFailed(Int32 historyId);
|
||||
}
|
||||
|
||||
public class DownloadTrackingService : IDownloadTrackingService, IExecute<CheckForFinishedDownloadCommand>, IHandleAsync<ApplicationStartedEvent>, IHandle<EpisodeGrabbedEvent>
|
||||
public class DownloadTrackingService : IDownloadTrackingService,
|
||||
IExecute<CheckForFinishedDownloadCommand>,
|
||||
IHandleAsync<ApplicationStartedEvent>,
|
||||
IHandle<EpisodeGrabbedEvent>
|
||||
{
|
||||
private readonly IProvideDownloadClient _downloadClientProvider;
|
||||
private readonly IHistoryService _historyService;
|
||||
@ -30,6 +34,7 @@ public class DownloadTrackingService : IDownloadTrackingService, IExecute<CheckF
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IFailedDownloadService _failedDownloadService;
|
||||
private readonly ICompletedDownloadService _completedDownloadService;
|
||||
private readonly IParsingService _parsingService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
private readonly ICached<TrackedDownload[]> _trackedDownloadCache;
|
||||
@ -44,6 +49,7 @@ public DownloadTrackingService(IProvideDownloadClient downloadClientProvider,
|
||||
ICacheManager cacheManager,
|
||||
IFailedDownloadService failedDownloadService,
|
||||
ICompletedDownloadService completedDownloadService,
|
||||
IParsingService parsingService,
|
||||
Logger logger)
|
||||
{
|
||||
_downloadClientProvider = downloadClientProvider;
|
||||
@ -52,6 +58,7 @@ public DownloadTrackingService(IProvideDownloadClient downloadClientProvider,
|
||||
_configService = configService;
|
||||
_failedDownloadService = failedDownloadService;
|
||||
_completedDownloadService = completedDownloadService;
|
||||
_parsingService = parsingService;
|
||||
_logger = logger;
|
||||
|
||||
_trackedDownloadCache = cacheManager.GetCache<TrackedDownload[]>(GetType());
|
||||
@ -73,7 +80,7 @@ public TrackedDownload[] GetQueuedDownloads()
|
||||
{
|
||||
return _trackedDownloadCache.Get("queued", () =>
|
||||
{
|
||||
UpdateTrackedDownloads();
|
||||
UpdateTrackedDownloads(_historyService.Grabbed());
|
||||
|
||||
return FilterQueuedDownloads(GetTrackedDownloads());
|
||||
|
||||
@ -119,7 +126,7 @@ private TrackedDownload[] FilterQueuedDownloads(IEnumerable<TrackedDownload> tra
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private Boolean UpdateTrackedDownloads()
|
||||
private Boolean UpdateTrackedDownloads(List<History.History> grabbedHistory)
|
||||
{
|
||||
var downloadClients = _downloadClientProvider.GetDownloadClients();
|
||||
|
||||
@ -140,13 +147,9 @@ private Boolean UpdateTrackedDownloads()
|
||||
|
||||
if (!oldTrackedDownloads.TryGetValue(trackingId, out trackedDownload))
|
||||
{
|
||||
trackedDownload = new TrackedDownload
|
||||
{
|
||||
TrackingId = trackingId,
|
||||
DownloadClient = downloadClient.Definition.Id,
|
||||
StartedTracking = DateTime.UtcNow,
|
||||
State = TrackedDownloadState.Unknown
|
||||
};
|
||||
trackedDownload = GetTrackedDownload(trackingId, downloadClient.Definition.Id, downloadItem, grabbedHistory);
|
||||
|
||||
if (trackedDownload == null) continue;
|
||||
|
||||
_logger.Debug("[{0}] Started tracking download with id {1}.", downloadItem.Title, trackingId);
|
||||
stateChanged = true;
|
||||
@ -182,9 +185,9 @@ private void ProcessTrackedDownloads()
|
||||
var failedHistory = _historyService.Failed();
|
||||
var importedHistory = _historyService.Imported();
|
||||
|
||||
var stateChanged = UpdateTrackedDownloads();
|
||||
var stateChanged = UpdateTrackedDownloads(grabbedHistory);
|
||||
|
||||
var downloadClients = _downloadClientProvider.GetDownloadClients();
|
||||
var downloadClients = _downloadClientProvider.GetDownloadClients().ToList();
|
||||
var trackedDownloads = GetTrackedDownloads();
|
||||
|
||||
foreach (var trackedDownload in trackedDownloads)
|
||||
@ -215,6 +218,50 @@ private void ProcessTrackedDownloads()
|
||||
}
|
||||
}
|
||||
|
||||
private TrackedDownload GetTrackedDownload(String trackingId, Int32 downloadClient, DownloadClientItem downloadItem, List<History.History> grabbedHistory)
|
||||
{
|
||||
var trackedDownload = new TrackedDownload
|
||||
{
|
||||
TrackingId = trackingId,
|
||||
DownloadClient = downloadClient,
|
||||
DownloadItem = downloadItem,
|
||||
StartedTracking = DateTime.UtcNow,
|
||||
State = TrackedDownloadState.Unknown,
|
||||
Status = TrackedDownloadStatus.Ok,
|
||||
};
|
||||
|
||||
var historyItems = grabbedHistory.Where(h =>
|
||||
{
|
||||
var downloadClientId = h.Data.GetValueOrDefault(DOWNLOAD_CLIENT_ID);
|
||||
|
||||
if (downloadClientId == null) return false;
|
||||
|
||||
return downloadClientId.Equals(trackedDownload.DownloadItem.DownloadClientId);
|
||||
}).ToList();
|
||||
|
||||
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
|
||||
if (parsedEpisodeInfo == null) return null;
|
||||
|
||||
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0);
|
||||
|
||||
if (remoteEpisode.Series == null)
|
||||
{
|
||||
if (historyItems.Empty()) return null;
|
||||
|
||||
trackedDownload.Status = TrackedDownloadStatus.Warning;
|
||||
trackedDownload.StatusMessages.Add(new TrackedDownloadStatusMessage(
|
||||
trackedDownload.DownloadItem.Title,
|
||||
"Series title mismatch, automatic import is not possible")
|
||||
);
|
||||
|
||||
remoteEpisode = _parsingService.Map(parsedEpisodeInfo, historyItems.First().SeriesId, historyItems.Select(h => h.EpisodeId));
|
||||
}
|
||||
|
||||
trackedDownload.RemoteEpisode = remoteEpisode;
|
||||
|
||||
return trackedDownload;
|
||||
}
|
||||
|
||||
public void Execute(CheckForFinishedDownloadCommand message)
|
||||
{
|
||||
ProcessTrackedDownloads();
|
||||
|
@ -3,10 +3,8 @@
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
@ -68,7 +66,7 @@ public void CheckForFailedItem(IDownloadClient downloadClient, TrackedDownload t
|
||||
|
||||
if (!grabbedItems.Any())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Download was not grabbed by drone, ignoring download");
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download was not grabbed by drone, ignoring download");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -92,7 +90,7 @@ public void CheckForFailedItem(IDownloadClient downloadClient, TrackedDownload t
|
||||
|
||||
if (!grabbedItems.Any())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Download wasn't grabbed by drone or not in a category, ignoring download.");
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download wasn't grabbed by drone or not in a category, ignoring download.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -244,8 +242,12 @@ private void UpdateStatusMessage(TrackedDownload trackedDownload, LogLevel logLe
|
||||
|
||||
if (trackedDownload.StatusMessage != statusMessage)
|
||||
{
|
||||
trackedDownload.HasError = logLevel >= LogLevel.Warn;
|
||||
trackedDownload.SetStatusLevel(logLevel);
|
||||
trackedDownload.StatusMessage = statusMessage;
|
||||
trackedDownload.StatusMessages = new List<TrackedDownloadStatusMessage>
|
||||
{
|
||||
new TrackedDownloadStatusMessage(trackedDownload.DownloadItem.Title, statusMessage)
|
||||
};
|
||||
_logger.Log(logLevel, logMessage);
|
||||
}
|
||||
else
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
@ -9,12 +11,33 @@ public class TrackedDownload
|
||||
public Int32 DownloadClient { get; set; }
|
||||
public DownloadClientItem DownloadItem { get; set; }
|
||||
public TrackedDownloadState State { get; set; }
|
||||
public TrackedDownloadStatus Status { get; set; }
|
||||
public DateTime StartedTracking { get; set; }
|
||||
public DateTime LastRetry { get; set; }
|
||||
public Int32 RetryCount { get; set; }
|
||||
public Boolean HasError { get; set; }
|
||||
public String StatusMessage { get; set; }
|
||||
public List<String> StatusMessages { get; set; }
|
||||
public RemoteEpisode RemoteEpisode { get; set; }
|
||||
public List<TrackedDownloadStatusMessage> StatusMessages { get; set; }
|
||||
|
||||
public TrackedDownload()
|
||||
{
|
||||
StatusMessages = new List<TrackedDownloadStatusMessage>();
|
||||
}
|
||||
|
||||
public void SetStatusLevel(LogLevel logLevel)
|
||||
{
|
||||
if (logLevel == LogLevel.Warn)
|
||||
{
|
||||
Status = TrackedDownloadStatus.Warning;
|
||||
}
|
||||
|
||||
if (logLevel >= LogLevel.Error)
|
||||
{
|
||||
Status = TrackedDownloadStatus.Error;
|
||||
}
|
||||
|
||||
else Status = TrackedDownloadStatus.Ok;
|
||||
}
|
||||
}
|
||||
|
||||
public enum TrackedDownloadState
|
||||
@ -25,4 +48,11 @@ public enum TrackedDownloadState
|
||||
DownloadFailed,
|
||||
Removed
|
||||
}
|
||||
|
||||
public enum TrackedDownloadStatus
|
||||
{
|
||||
Ok,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
}
|
||||
|
23
src/NzbDrone.Core/Download/TrackedDownloadStatusMessage.cs
Normal file
23
src/NzbDrone.Core/Download/TrackedDownloadStatusMessage.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Download
|
||||
{
|
||||
public class TrackedDownloadStatusMessage
|
||||
{
|
||||
public String Title { get; set; }
|
||||
public List<String> Messages { get; set; }
|
||||
|
||||
public TrackedDownloadStatusMessage(String title, List<String> messages)
|
||||
{
|
||||
Title = title;
|
||||
Messages = messages;
|
||||
}
|
||||
|
||||
public TrackedDownloadStatusMessage(String title, String message)
|
||||
{
|
||||
Title = title;
|
||||
Messages = new List<String>{ message };
|
||||
}
|
||||
}
|
||||
}
|
@ -317,6 +317,7 @@
|
||||
<Compile Include="Download\Clients\UsenetBlackhole\UsenetBlackhole.cs" />
|
||||
<Compile Include="Download\Clients\UsenetBlackhole\UsenetBlackholeSettings.cs" />
|
||||
<Compile Include="Download\CompletedDownloadService.cs" />
|
||||
<Compile Include="Download\TrackedDownloadStatusMessage.cs" />
|
||||
<Compile Include="Download\UsenetClientBase.cs" />
|
||||
<Compile Include="Download\DownloadClientBase.cs" />
|
||||
<Compile Include="Download\DownloadClientDefinition.cs" />
|
||||
|
@ -16,7 +16,8 @@ public interface IParsingService
|
||||
{
|
||||
LocalEpisode GetLocalEpisode(string filename, Series series, bool sceneSource);
|
||||
Series GetSeries(string title);
|
||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 tvRageId, SearchCriteriaBase searchCriteria = null);
|
||||
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 seriesId, IEnumerable<Int32> episodeIds);
|
||||
List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
|
||||
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvRageId, SearchCriteriaBase searchCriteria = null);
|
||||
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, Series series);
|
||||
@ -99,7 +100,7 @@ public Series GetSeries(string title)
|
||||
return series;
|
||||
}
|
||||
|
||||
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, SearchCriteriaBase searchCriteria = null)
|
||||
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 tvRageId, SearchCriteriaBase searchCriteria = null)
|
||||
{
|
||||
var remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
@ -120,6 +121,18 @@ public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int tvRageId, Sear
|
||||
return remoteEpisode;
|
||||
}
|
||||
|
||||
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 seriesId, IEnumerable<Int32> episodeIds)
|
||||
{
|
||||
var remoteEpisode = new RemoteEpisode
|
||||
{
|
||||
ParsedEpisodeInfo = parsedEpisodeInfo,
|
||||
Series = _seriesService.GetSeries(seriesId),
|
||||
Episodes = _episodeService.GetEpisodes(episodeIds)
|
||||
};
|
||||
|
||||
return remoteEpisode;
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null)
|
||||
{
|
||||
var result = new List<Episode>();
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
@ -17,7 +19,8 @@ public class Queue : ModelBase
|
||||
public TimeSpan? Timeleft { get; set; }
|
||||
public DateTime? EstimatedCompletionTime { get; set; }
|
||||
public String Status { get; set; }
|
||||
public String ErrorMessage { get; set; }
|
||||
public String TrackedDownloadStatus { get; set; }
|
||||
public List<TrackedDownloadStatusMessage> StatusMessages { get; set; }
|
||||
public RemoteEpisode RemoteEpisode { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -28,33 +28,30 @@ public List<Queue> GetQueue()
|
||||
return MapQueue(queueItems);
|
||||
}
|
||||
|
||||
private List<Queue> MapQueue(IEnumerable<TrackedDownload> queueItems)
|
||||
private List<Queue> MapQueue(IEnumerable<TrackedDownload> trackedDownloads)
|
||||
{
|
||||
var queued = new List<Queue>();
|
||||
|
||||
foreach (var queueItem in queueItems)
|
||||
foreach (var trackedDownload in trackedDownloads)
|
||||
{
|
||||
foreach (var episode in queueItem.DownloadItem.RemoteEpisode.Episodes)
|
||||
foreach (var episode in trackedDownload.RemoteEpisode.Episodes)
|
||||
{
|
||||
var queue = new Queue
|
||||
{
|
||||
Id = episode.Id ^ (queueItem.DownloadItem.DownloadClientId.GetHashCode().GetHashCode() << 16),
|
||||
Series = queueItem.DownloadItem.RemoteEpisode.Series,
|
||||
Id = episode.Id ^ (trackedDownload.DownloadItem.DownloadClientId.GetHashCode().GetHashCode() << 16),
|
||||
Series = trackedDownload.RemoteEpisode.Series,
|
||||
Episode = episode,
|
||||
Quality = queueItem.DownloadItem.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||
Title = queueItem.DownloadItem.Title,
|
||||
Size = queueItem.DownloadItem.TotalSize,
|
||||
Sizeleft = queueItem.DownloadItem.RemainingSize,
|
||||
Timeleft = queueItem.DownloadItem.RemainingTime,
|
||||
Status = queueItem.DownloadItem.Status.ToString(),
|
||||
RemoteEpisode = queueItem.DownloadItem.RemoteEpisode
|
||||
Quality = trackedDownload.RemoteEpisode.ParsedEpisodeInfo.Quality,
|
||||
Title = trackedDownload.DownloadItem.Title,
|
||||
Size = trackedDownload.DownloadItem.TotalSize,
|
||||
Sizeleft = trackedDownload.DownloadItem.RemainingSize,
|
||||
Timeleft = trackedDownload.DownloadItem.RemainingTime,
|
||||
Status = trackedDownload.DownloadItem.Status.ToString(),
|
||||
RemoteEpisode = trackedDownload.RemoteEpisode,
|
||||
TrackedDownloadStatus = trackedDownload.Status.ToString(),
|
||||
StatusMessages = trackedDownload.StatusMessages
|
||||
};
|
||||
|
||||
if (queueItem.HasError)
|
||||
{
|
||||
queue.ErrorMessage = queueItem.StatusMessage;
|
||||
}
|
||||
|
||||
if (queue.Timeleft.HasValue)
|
||||
{
|
||||
queue.EstimatedCompletionTime = DateTime.UtcNow.Add(queue.Timeleft.Value);
|
||||
|
@ -14,6 +14,7 @@ namespace NzbDrone.Core.Tv
|
||||
public interface IEpisodeService
|
||||
{
|
||||
Episode GetEpisode(int id);
|
||||
List<Episode> GetEpisodes(IEnumerable<Int32> ids);
|
||||
Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber);
|
||||
Episode FindEpisode(int seriesId, int absoluteEpisodeNumber);
|
||||
Episode FindEpisodeByName(int seriesId, int seasonNumber, string episodeTitle);
|
||||
@ -37,9 +38,9 @@ public interface IEpisodeService
|
||||
}
|
||||
|
||||
public class EpisodeService : IEpisodeService,
|
||||
IHandle<EpisodeFileDeletedEvent>,
|
||||
IHandle<EpisodeFileAddedEvent>,
|
||||
IHandleAsync<SeriesDeletedEvent>
|
||||
IHandle<EpisodeFileDeletedEvent>,
|
||||
IHandle<EpisodeFileAddedEvent>,
|
||||
IHandleAsync<SeriesDeletedEvent>
|
||||
{
|
||||
private readonly IEpisodeRepository _episodeRepository;
|
||||
private readonly IConfigService _configService;
|
||||
@ -57,6 +58,11 @@ public Episode GetEpisode(int id)
|
||||
return _episodeRepository.Get(id);
|
||||
}
|
||||
|
||||
public List<Episode> GetEpisodes(IEnumerable<int> ids)
|
||||
{
|
||||
return _episodeRepository.Get(ids).ToList();
|
||||
}
|
||||
|
||||
public Episode FindEpisode(int seriesId, int seasonNumber, int episodeNumber)
|
||||
{
|
||||
return _episodeRepository.Find(seriesId, seasonNumber, episodeNumber);
|
||||
|
@ -204,3 +204,7 @@
|
||||
.icon-nd-deleted:before {
|
||||
.icon(@trash);
|
||||
}
|
||||
|
||||
.icon-nd-warning:before {
|
||||
color: @brand-warning;
|
||||
}
|
||||
|
@ -2,20 +2,26 @@
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Cells/NzbDroneCell'
|
||||
], function (NzbDroneCell) {
|
||||
], function (Marionette, NzbDroneCell) {
|
||||
return NzbDroneCell.extend({
|
||||
|
||||
className: 'queue-status-cell',
|
||||
className : 'queue-status-cell',
|
||||
template : 'History/Queue/QueueStatusCellTemplate',
|
||||
|
||||
render: function () {
|
||||
this.$el.empty();
|
||||
|
||||
if (this.cellValue) {
|
||||
var status = this.cellValue.get('status').toLowerCase();
|
||||
var errorMessage = (this.cellValue.get('errorMessage') || '');
|
||||
var trackedDownloadStatus = this.cellValue.get('trackedDownloadStatus').toLowerCase();
|
||||
var hasError = this.cellValue.get('hasError') || false;
|
||||
var hasWarning = this.cellValue.get('hasWarning') || false;
|
||||
var icon = 'icon-nd-downloading';
|
||||
var title = 'Downloading';
|
||||
var itemTitle = this.cellValue.get('title');
|
||||
var content = itemTitle;
|
||||
|
||||
if (status === 'paused') {
|
||||
icon = 'icon-pause';
|
||||
@ -39,7 +45,7 @@ define(
|
||||
|
||||
if (status === 'failed') {
|
||||
icon = 'icon-nd-download-failed';
|
||||
title = 'Download failed: check download client for more details';
|
||||
title = 'Download failed';
|
||||
}
|
||||
|
||||
if (status === 'warning') {
|
||||
@ -47,29 +53,37 @@ define(
|
||||
title = 'Download warning: check download client for more details';
|
||||
}
|
||||
|
||||
if (errorMessage !== '') {
|
||||
if (trackedDownloadStatus === 'warning') {
|
||||
icon += ' icon-nd-warning';
|
||||
// title = 'Download failed';
|
||||
|
||||
this.templateFunction = Marionette.TemplateCache.get(this.template);
|
||||
content = this.templateFunction(this.cellValue.toJSON());
|
||||
}
|
||||
|
||||
if (trackedDownloadStatus === 'error') {
|
||||
if (status === 'completed') {
|
||||
icon = 'icon-nd-import-failed';
|
||||
title = 'Import failed';
|
||||
title = 'Import failed: ' + itemTitle;
|
||||
}
|
||||
else {
|
||||
icon = 'icon-nd-download-failed';
|
||||
title = 'Download failed';
|
||||
}
|
||||
this.$el.html('<i class="{0}"></i>'.format(icon));
|
||||
|
||||
this.$el.popover({
|
||||
content : errorMessage.replace(new RegExp('\r\n', 'g'), '<br/>'),
|
||||
html : true,
|
||||
trigger : 'hover',
|
||||
title : title,
|
||||
placement: 'right',
|
||||
container: this.$el
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.$el.html('<i class="{0}" title="{1}"></i>'.format(icon, title));
|
||||
|
||||
this.templateFunction = Marionette.TemplateCache.get(this.template);
|
||||
content = this.templateFunction(this.cellValue.toJSON());
|
||||
}
|
||||
|
||||
this.$el.html('<i class="{0}"></i>'.format(icon));
|
||||
this.$el.popover({
|
||||
content : content,
|
||||
html : true,
|
||||
trigger : 'hover',
|
||||
title : title,
|
||||
placement: 'right',
|
||||
container: this.$el
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
|
8
src/UI/History/Queue/QueueStatusCellTemplate.hbs
Normal file
8
src/UI/History/Queue/QueueStatusCellTemplate.hbs
Normal file
@ -0,0 +1,8 @@
|
||||
{{#each statusMessages}}
|
||||
{{title}}
|
||||
<ul>
|
||||
{{#each messages}}
|
||||
<li>{{this}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/each}}
|
Loading…
Reference in New Issue
Block a user