mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-21 22:04:31 -04:00
New: Rebuilt Completed/Failed download handling from scratch
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download.TrackedDownloads;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
|
||||
@@ -11,208 +9,59 @@ namespace NzbDrone.Core.Download
|
||||
{
|
||||
public interface IFailedDownloadService
|
||||
{
|
||||
void MarkAsFailed(TrackedDownload trackedDownload, History.History grabbedHistory);
|
||||
void CheckForFailedItem(IDownloadClient downloadClient, TrackedDownload trackedDownload, List<History.History> grabbedHistory, List<History.History> failedHistory);
|
||||
void MarkAsFailed(int historyId);
|
||||
void Process(TrackedDownload trackedDownload);
|
||||
}
|
||||
|
||||
public class FailedDownloadService : IFailedDownloadService
|
||||
{
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public FailedDownloadService(IHistoryService historyService,
|
||||
IEventAggregator eventAggregator,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
IEventAggregator eventAggregator)
|
||||
{
|
||||
_historyService = historyService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void MarkAsFailed(TrackedDownload trackedDownload, History.History history)
|
||||
public void MarkAsFailed(int historyId)
|
||||
{
|
||||
if (trackedDownload != null && trackedDownload.State == TrackedDownloadState.Downloading)
|
||||
{
|
||||
trackedDownload.State = TrackedDownloadState.DownloadFailed;
|
||||
}
|
||||
var history = _historyService.Get(historyId);
|
||||
|
||||
var downloadClientId = history.Data.GetValueOrDefault(DownloadTrackingService.DOWNLOAD_CLIENT_ID);
|
||||
if (downloadClientId.IsNullOrWhiteSpace())
|
||||
var downloadId = history.DownloadId;
|
||||
if (downloadId.IsNullOrWhiteSpace())
|
||||
{
|
||||
PublishDownloadFailedEvent(new List<History.History> { history }, "Manually marked as failed");
|
||||
}
|
||||
else
|
||||
{
|
||||
var grabbedHistory = GetHistoryItems(_historyService.Grabbed(), downloadClientId);
|
||||
|
||||
var grabbedHistory = _historyService.Find(downloadId, HistoryEventType.Grabbed).ToList();
|
||||
PublishDownloadFailedEvent(grabbedHistory, "Manually marked as failed");
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckForFailedItem(IDownloadClient downloadClient, TrackedDownload trackedDownload, List<History.History> grabbedHistory, List<History.History> failedHistory)
|
||||
public void Process(TrackedDownload trackedDownload)
|
||||
{
|
||||
if (!_configService.EnableFailedDownloadHandling)
|
||||
var grabbedItems = _historyService.Find(trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed)
|
||||
.ToList();
|
||||
|
||||
if (grabbedItems.Empty())
|
||||
{
|
||||
trackedDownload.Warn("Download wasn't grabbed by sonarr, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
if (trackedDownload.DownloadItem.IsEncrypted && trackedDownload.State == TrackedDownloadState.Downloading)
|
||||
if (trackedDownload.DownloadItem.IsEncrypted)
|
||||
{
|
||||
var grabbedItems = GetHistoryItems(grabbedHistory, trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
if (!grabbedItems.Any())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download wasn't grabbed by drone, ignoring download");
|
||||
return;
|
||||
}
|
||||
|
||||
trackedDownload.State = TrackedDownloadState.DownloadFailed;
|
||||
|
||||
var failedItems = GetHistoryItems(failedHistory, trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
if (failedItems.Any())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Already added to history as failed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PublishDownloadFailedEvent(grabbedItems, "Encrypted download detected");
|
||||
}
|
||||
trackedDownload.State = TrackedDownloadStage.DownloadFailed;
|
||||
PublishDownloadFailedEvent(grabbedItems, "Encrypted download detected");
|
||||
}
|
||||
|
||||
if (trackedDownload.DownloadItem.Status == DownloadItemStatus.Failed && trackedDownload.State == TrackedDownloadState.Downloading)
|
||||
else if (trackedDownload.DownloadItem.Status == DownloadItemStatus.Failed)
|
||||
{
|
||||
var grabbedItems = GetHistoryItems(grabbedHistory, trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
if (!grabbedItems.Any())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download wasn't grabbed by drone, ignoring download");
|
||||
return;
|
||||
}
|
||||
|
||||
var failedItems = GetHistoryItems(failedHistory, trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
if (failedItems.Any())
|
||||
{
|
||||
trackedDownload.State = TrackedDownloadState.DownloadFailed;
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Already added to history as failed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FailedDownloadForRecentRelease(downloadClient, trackedDownload, grabbedItems))
|
||||
{
|
||||
_logger.Debug("[{0}] Recent release Failed, do not blacklist.", trackedDownload.DownloadItem.Title);
|
||||
return;
|
||||
}
|
||||
|
||||
trackedDownload.State = TrackedDownloadState.DownloadFailed;
|
||||
|
||||
PublishDownloadFailedEvent(grabbedItems, trackedDownload.DownloadItem.Message);
|
||||
}
|
||||
trackedDownload.State = TrackedDownloadStage.DownloadFailed;
|
||||
PublishDownloadFailedEvent(grabbedItems, trackedDownload.DownloadItem.Message);
|
||||
}
|
||||
|
||||
if (trackedDownload.DownloadItem.Status != DownloadItemStatus.Failed && trackedDownload.State == TrackedDownloadState.Downloading)
|
||||
{
|
||||
var grabbedItems = GetHistoryItems(grabbedHistory, trackedDownload.DownloadItem.DownloadClientId);
|
||||
var failedItems = GetHistoryItems(failedHistory, trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
if (grabbedItems.Any() && failedItems.Any())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Already added to history as failed, updating tracked state.");
|
||||
trackedDownload.State = TrackedDownloadState.DownloadFailed;
|
||||
}
|
||||
}
|
||||
|
||||
if (_configService.RemoveFailedDownloads && trackedDownload.State == TrackedDownloadState.DownloadFailed)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.Debug("[{0}] Removing failed download from client.", trackedDownload.DownloadItem.Title);
|
||||
downloadClient.RemoveItem(trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
trackedDownload.State = TrackedDownloadState.Removed;
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Removing item not supported by your download client.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool FailedDownloadForRecentRelease(IDownloadClient downloadClient, TrackedDownload trackedDownload, List<History.History> matchingHistoryItems)
|
||||
{
|
||||
double ageHours;
|
||||
|
||||
if (!Double.TryParse(matchingHistoryItems.First().Data.GetValueOrDefault("ageHours"), out ageHours))
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Info, "Unable to determine age of failed download.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ageHours > _configService.BlacklistGracePeriod)
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Info, "Download Failed, Failed download is older than the grace period.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trackedDownload.RetryCount >= _configService.BlacklistRetryLimit)
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Info, "Download Failed, Retry limit reached.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (trackedDownload.LastRetry == new DateTime())
|
||||
{
|
||||
trackedDownload.LastRetry = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (trackedDownload.LastRetry.AddMinutes(_configService.BlacklistRetryInterval) < DateTime.UtcNow)
|
||||
{
|
||||
trackedDownload.LastRetry = DateTime.UtcNow;
|
||||
trackedDownload.RetryCount++;
|
||||
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Info, "Download Failed, initiating retry attempt {0}/{1}.", trackedDownload.RetryCount, _configService.BlacklistRetryLimit);
|
||||
|
||||
try
|
||||
{
|
||||
var newDownloadClientId = downloadClient.RetryDownload(trackedDownload.DownloadItem.DownloadClientId);
|
||||
|
||||
if (newDownloadClientId != trackedDownload.DownloadItem.DownloadClientId)
|
||||
{
|
||||
var oldTrackingId = trackedDownload.TrackingId;
|
||||
var newTrackingId = String.Format("{0}-{1}", downloadClient.Definition.Id, newDownloadClientId);
|
||||
|
||||
trackedDownload.TrackingId = newTrackingId;
|
||||
trackedDownload.DownloadItem.DownloadClientId = newDownloadClientId;
|
||||
|
||||
_logger.Debug("[{0}] Changed id from {1} to {2}.", trackedDownload.DownloadItem.Title, oldTrackingId, newTrackingId);
|
||||
var newHistoryData = new Dictionary<String, String>(matchingHistoryItems.First().Data);
|
||||
newHistoryData[DownloadTrackingService.DOWNLOAD_CLIENT_ID] = newDownloadClientId;
|
||||
_historyService.UpdateHistoryData(matchingHistoryItems.First().Id, newHistoryData);
|
||||
}
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Debug, "Retrying failed downloads is not supported by your download client.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Warn, "Download Failed, waiting for retry interval to expire.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<History.History> GetHistoryItems(List<History.History> grabbedHistory, string downloadClientId)
|
||||
{
|
||||
return grabbedHistory.Where(h => downloadClientId.Equals(h.Data.GetValueOrDefault(DownloadTrackingService.DOWNLOAD_CLIENT_ID)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private void PublishDownloadFailedEvent(List<History.History> historyItems, string message)
|
||||
@@ -225,35 +74,15 @@ namespace NzbDrone.Core.Download
|
||||
EpisodeIds = historyItems.Select(h => h.EpisodeId).ToList(),
|
||||
Quality = historyItem.Quality,
|
||||
SourceTitle = historyItem.SourceTitle,
|
||||
DownloadClient = historyItem.Data.GetValueOrDefault(DownloadTrackingService.DOWNLOAD_CLIENT),
|
||||
DownloadClientId = historyItem.Data.GetValueOrDefault(DownloadTrackingService.DOWNLOAD_CLIENT_ID),
|
||||
Message = message
|
||||
DownloadClient = historyItem.Data.GetValueOrDefault(History.History.DOWNLOAD_CLIENT),
|
||||
DownloadId = historyItem.DownloadId,
|
||||
Message = message,
|
||||
Data = historyItem.Data
|
||||
};
|
||||
|
||||
downloadFailedEvent.Data = downloadFailedEvent.Data.Merge(historyItem.Data);
|
||||
|
||||
_eventAggregator.PublishEvent(downloadFailedEvent);
|
||||
}
|
||||
|
||||
private void UpdateStatusMessage(TrackedDownload trackedDownload, LogLevel logLevel, String message, params object[] args)
|
||||
{
|
||||
var statusMessage = String.Format(message, args);
|
||||
var logMessage = String.Format("[{0}] {1}", trackedDownload.DownloadItem.Title, statusMessage);
|
||||
|
||||
if (trackedDownload.StatusMessage != statusMessage)
|
||||
{
|
||||
trackedDownload.SetStatusLevel(logLevel);
|
||||
trackedDownload.StatusMessage = statusMessage;
|
||||
trackedDownload.StatusMessages = new List<TrackedDownloadStatusMessage>
|
||||
{
|
||||
new TrackedDownloadStatusMessage(trackedDownload.DownloadItem.Title, statusMessage)
|
||||
};
|
||||
_logger.Log(logLevel, logMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Debug(logMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user