From 39b57cfe6e6ee382a26ea3b931dec43b62ef9faf Mon Sep 17 00:00:00 2001 From: ta264 Date: Sat, 14 Nov 2020 05:27:07 +0000 Subject: [PATCH] Fixed: Get actual file names from QBittorrent API (#5226) * Fixed: Get actual file names from QBittorrent API Previously we were assuming that the output filename was the category output dir + the torrent name. This isn't true if the torrent has been renamed or sanitized. --- .../ImportFixture.cs | 1 + .../ProcessFixture.cs | 4 + .../QBittorrentTests/QBittorrentFixture.cs | 125 ++++++++++++++++++ .../Clients/QBittorrent/QBittorrent.cs | 58 +++++++- .../QBittorrent/QBittorrentProxySelector.cs | 1 + .../Clients/QBittorrent/QBittorrentProxyV1.cs | 8 ++ .../Clients/QBittorrent/QBittorrentProxyV2.cs | 9 ++ .../Clients/QBittorrent/QBittorrentTorrent.cs | 11 ++ .../Download/CompletedDownloadService.cs | 10 +- .../Download/DownloadClientBase.cs | 6 + .../Download/DownloadClientItem.cs | 15 +++ src/NzbDrone.Core/Download/IDownloadClient.cs | 3 +- .../Download/PrepareImportService.cs | 24 ++++ .../TrackedDownloads/TrackedDownload.cs | 1 + .../TrackImport/Manual/ManualImportService.cs | 10 +- 15 files changed, 272 insertions(+), 14 deletions(-) create mode 100644 src/NzbDrone.Core/Download/PrepareImportService.cs diff --git a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs index 125d6ada5..fd2f56c13 100644 --- a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs +++ b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ImportFixture.cs @@ -37,6 +37,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests _trackedDownload = Builder.CreateNew() .With(c => c.State = TrackedDownloadState.Downloading) + .With(c => c.ImportItem = completed) .With(c => c.DownloadItem = completed) .With(c => c.RemoteAlbum = remoteAlbum) .Build(); diff --git a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ProcessFixture.cs b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ProcessFixture.cs index 2884fcd43..9b7ebebc2 100644 --- a/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ProcessFixture.cs +++ b/src/NzbDrone.Core.Test/Download/CompletedDownloadServiceTests/ProcessFixture.cs @@ -45,6 +45,10 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests .Setup(c => c.Get(It.IsAny())) .Returns(Mocker.GetMock().Object); + Mocker.GetMock() + .Setup(c => c.ProvideImportItem(It.IsAny(), It.IsAny())) + .Returns((DownloadClientItem item, DownloadClientItem previous) => item); + Mocker.GetMock() .Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId)) .Returns(new History.History()); diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs index 49e3ea8ab..d6341fc35 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/QBittorrentTests/QBittorrentFixture.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using FluentAssertions; using Moq; using NUnit.Framework; +using NzbDrone.Common.Disk; using NzbDrone.Common.Http; using NzbDrone.Core.Download; using NzbDrone.Core.Download.Clients.QBittorrent; @@ -124,6 +126,24 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests Mocker.GetMock() .Setup(s => s.GetTorrents(It.IsAny())) .Returns(torrents); + + foreach (var torrent in torrents) + { + Mocker.GetMock() + .Setup(s => s.GetTorrentProperties(torrent.Hash.ToLower(), It.IsAny())) + .Returns(new QBittorrentTorrentProperties { SavePath = torrent.SavePath }); + + Mocker.GetMock() + .Setup(s => s.GetTorrentFiles(torrent.Hash.ToLower(), It.IsAny())) + .Returns(new List { new QBittorrentTorrentFile { Name = torrent.Name } }); + } + } + + private void GivenTorrentFiles(string hash, List files) + { + Mocker.GetMock() + .Setup(s => s.GetTorrentFiles(hash.ToLower(), It.IsAny())) + .Returns(files); } [Test] @@ -259,6 +279,78 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests } [Test] + public void single_file_torrent_outputpath_should_have_sanitised_name() + { + var torrent = new QBittorrentTorrent + { + Hash = "HASH", + Name = @"Droned.S01E01.Test\'s.1080p.WEB-DL-DRONE.mkv", + Size = 1000, + Progress = 0.7, + Eta = 8640000, + State = "stalledDL", + Label = "", + SavePath = @"C:\Torrents".AsOsAgnostic() + }; + + var file = new QBittorrentTorrentFile + { + Name = "Droned.S01E01.Tests.1080p.WEB-DL-DRONE.mkv" + }; + + GivenTorrents(new List { torrent }); + GivenTorrentFiles(torrent.Hash, new List { file }); + + var item = new DownloadClientItem + { + DownloadId = torrent.Hash + }; + + var result = Subject.GetImportItem(item, null); + + result.OutputPath.FullPath.Should().Be(Path.Combine(torrent.SavePath, file.Name)); + } + + [Test] + public void multi_file_torrent_outputpath_should_have_sanitised_name() + { + var torrent = new QBittorrentTorrent + { + Hash = "HASH", + Name = @"Droned.S01.\1/2", + Size = 1000, + Progress = 0.7, + Eta = 8640000, + State = "stalledDL", + Label = "", + SavePath = @"C:\Torrents".AsOsAgnostic() + }; + + var files = new List + { + new QBittorrentTorrentFile + { + Name = @"Droned.S01.12\E01.mkv".AsOsAgnostic() + }, + new QBittorrentTorrentFile + { + Name = @"Droned.S01.12\E02.mkv".AsOsAgnostic() + } + }; + + GivenTorrents(new List { torrent }); + GivenTorrentFiles(torrent.Hash, files); + + var item = new DownloadClientItem + { + DownloadId = torrent.Hash + }; + + var result = Subject.GetImportItem(item, null); + + result.OutputPath.FullPath.Should().Be(Path.Combine(torrent.SavePath, "Droned.S01.12")); + } + public void missingFiles_item_should_have_required_properties() { var torrent = new QBittorrentTorrent @@ -279,6 +371,39 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.QBittorrentTests item.RemainingTime.Should().NotHaveValue(); } + [Test] + public void api_261_should_use_content_path() + { + var torrent = new QBittorrentTorrent + { + Hash = "HASH", + Name = @"Droned.S01.\1/2", + Size = 1000, + Progress = 0.7, + Eta = 8640000, + State = "stalledDL", + Label = "", + SavePath = @"C:\Torrents".AsOsAgnostic(), + ContentPath = @"C:\Torrents\Droned.S01.12".AsOsAgnostic() + }; + + GivenTorrents(new List { torrent }); + + Mocker.GetMock() + .Setup(v => v.GetApiVersion(It.IsAny())) + .Returns(new Version(2, 6, 1)); + + var item = new DownloadClientItem + { + DownloadId = torrent.Hash, + OutputPath = new OsPath(torrent.ContentPath) + }; + + var result = Subject.GetImportItem(item, null); + + result.OutputPath.FullPath.Should().Be(torrent.ContentPath); + } + [Test] public void Download_should_return_unique_id() { diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs index fd46c664c..850f724d0 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using FluentValidation.Results; using NLog; @@ -122,6 +123,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent public override IEnumerable GetItems() { + var version = Proxy.GetApiVersion(Settings); var config = Proxy.GetConfig(Settings); var torrents = Proxy.GetTorrents(Settings); @@ -138,19 +140,18 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent DownloadClient = Definition.Name, RemainingSize = (long)(torrent.Size * (1.0 - torrent.Progress)), RemainingTime = GetRemainingTime(torrent), - SeedRatio = torrent.Ratio, - OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.SavePath)), + SeedRatio = torrent.Ratio }; + if (version >= new Version("2.6.1")) + { + item.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, new OsPath(torrent.ContentPath)); + } + // Avoid removing torrents that haven't reached the global max ratio. // Removal also requires the torrent to be paused, in case a higher max ratio was set on the torrent itself (which is not exposed by the api). item.CanMoveFiles = item.CanBeRemoved = torrent.State == "pausedUP" && HasReachedSeedLimit(torrent, config); - if (!item.OutputPath.IsEmpty && item.OutputPath.FileName != torrent.Name) - { - item.OutputPath += torrent.Name; - } - switch (torrent.State) { case "error": // some error occurred, applies to paused torrents @@ -224,6 +225,49 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent Proxy.RemoveTorrent(hash.ToLower(), deleteData, Settings); } + public override DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt) + { + // On API version >= 2.6.1 this is already set correctly + if (!item.OutputPath.IsEmpty) + { + return item; + } + + var files = Proxy.GetTorrentFiles(item.DownloadId.ToLower(), Settings); + if (!files.Any()) + { + _logger.Debug($"No files found for torrent {item.Title} in qBittorrent"); + return item; + } + + var properties = Proxy.GetTorrentProperties(item.DownloadId.ToLower(), Settings); + var savePath = new OsPath(properties.SavePath); + + var result = item.Clone(); + + OsPath outputPath; + if (files.Count == 1) + { + outputPath = savePath + files[0].Name; + } + else + { + // we have multiple files in the torrent so just get + // the first subdirectory + var relativePath = new OsPath(files[0].Name); + while (!relativePath.Directory.IsEmpty) + { + relativePath = relativePath.Directory; + } + + outputPath = savePath + relativePath.FileName; + } + + result.OutputPath = _remotePathMappingService.RemapRemoteToLocal(Settings.Host, outputPath); + + return result; + } + public override DownloadClientInfo GetStatus() { var config = Proxy.GetConfig(Settings); diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs index bbd308eea..248d96bb2 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxySelector.cs @@ -16,6 +16,7 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent QBittorrentPreferences GetConfig(QBittorrentSettings settings); List GetTorrents(QBittorrentSettings settings); QBittorrentTorrentProperties GetTorrentProperties(string hash, QBittorrentSettings settings); + List GetTorrentFiles(string hash, QBittorrentSettings settings); void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings); void AddTorrentFromFile(string fileName, byte[] fileContent, QBittorrentSettings settings); diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs index f095f32ee..4fb1fbb69 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV1.cs @@ -107,6 +107,14 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent return response; } + public List GetTorrentFiles(string hash, QBittorrentSettings settings) + { + var request = BuildRequest(settings).Resource($"/query/propertiesFiles/{hash}"); + var response = ProcessRequest>(request, settings); + + return response; + } + public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource("/command/download") diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs index d20d15126..878d2be82 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentProxyV2.cs @@ -105,6 +105,15 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent return response; } + public List GetTorrentFiles(string hash, QBittorrentSettings settings) + { + var request = BuildRequest(settings).Resource("/api/v2/torrents/files") + .AddQueryParam("hash", hash); + var response = ProcessRequest>(request, settings); + + return response; + } + public void AddTorrentFromUrl(string torrentUrl, QBittorrentSettings settings) { var request = BuildRequest(settings).Resource("/api/v2/torrents/add") diff --git a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs index 4a5540e78..3007241bb 100644 --- a/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs +++ b/src/NzbDrone.Core/Download/Clients/QBittorrent/QBittorrentTorrent.cs @@ -24,6 +24,9 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent [JsonProperty(PropertyName = "save_path")] public string SavePath { get; set; } // Torrent save path + [JsonProperty(PropertyName = "content_path")] + public string ContentPath { get; set; } // Torrent save path + public float Ratio { get; set; } // Torrent share ratio [JsonProperty(PropertyName = "ratio_limit")] // Per torrent seeding ratio limit (-2 = use global, -1 = unlimited) @@ -40,7 +43,15 @@ namespace NzbDrone.Core.Download.Clients.QBittorrent { public string Hash { get; set; } // Torrent hash + [JsonProperty(PropertyName = "save_path")] + public string SavePath { get; set; } + [JsonProperty(PropertyName = "seeding_time")] public long SeedingTime { get; set; } // Torrent seeding time } + + public class QBittorrentTorrentFile + { + public string Name { get; set; } + } } diff --git a/src/NzbDrone.Core/Download/CompletedDownloadService.cs b/src/NzbDrone.Core/Download/CompletedDownloadService.cs index 1fc346c8e..df0ef5a1b 100644 --- a/src/NzbDrone.Core/Download/CompletedDownloadService.cs +++ b/src/NzbDrone.Core/Download/CompletedDownloadService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using NzbDrone.Common.EnvironmentInfo; @@ -25,16 +26,19 @@ namespace NzbDrone.Core.Download private readonly IHistoryService _historyService; private readonly IDownloadedTracksImportService _downloadedTracksImportService; private readonly IArtistService _artistService; + private readonly IProvideImportItemService _importItemService; private readonly ITrackedDownloadAlreadyImported _trackedDownloadAlreadyImported; public CompletedDownloadService(IEventAggregator eventAggregator, IHistoryService historyService, + IProvideImportItemService importItemService, IDownloadedTracksImportService downloadedTracksImportService, IArtistService artistService, ITrackedDownloadAlreadyImported trackedDownloadAlreadyImported) { _eventAggregator = eventAggregator; _historyService = historyService; + _importItemService = importItemService; _downloadedTracksImportService = downloadedTracksImportService; _artistService = artistService; _trackedDownloadAlreadyImported = trackedDownloadAlreadyImported; @@ -48,6 +52,8 @@ namespace NzbDrone.Core.Download return; } + trackedDownload.ImportItem = _importItemService.ProvideImportItem(trackedDownload.DownloadItem, trackedDownload.ImportItem); + // Only process tracked downloads that are still downloading if (trackedDownload.State != TrackedDownloadState.Downloading) { @@ -62,7 +68,7 @@ namespace NzbDrone.Core.Download return; } - var downloadItemOutputPath = trackedDownload.DownloadItem.OutputPath; + var downloadItemOutputPath = trackedDownload.ImportItem.OutputPath; if (downloadItemOutputPath.IsEmpty) { @@ -100,7 +106,7 @@ namespace NzbDrone.Core.Download { trackedDownload.State = TrackedDownloadState.Importing; - var outputPath = trackedDownload.DownloadItem.OutputPath.FullPath; + var outputPath = trackedDownload.ImportItem.OutputPath.FullPath; var importResults = _downloadedTracksImportService.ProcessPath(outputPath, ImportMode.Auto, trackedDownload.RemoteAlbum.Artist, trackedDownload.DownloadItem); if (importResults.Empty()) diff --git a/src/NzbDrone.Core/Download/DownloadClientBase.cs b/src/NzbDrone.Core/Download/DownloadClientBase.cs index e5895ffe1..edcf02910 100644 --- a/src/NzbDrone.Core/Download/DownloadClientBase.cs +++ b/src/NzbDrone.Core/Download/DownloadClientBase.cs @@ -62,6 +62,12 @@ namespace NzbDrone.Core.Download public abstract string Download(RemoteAlbum remoteAlbum); public abstract IEnumerable GetItems(); + + public virtual DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt) + { + return item; + } + public abstract void RemoveItem(string downloadId, bool deleteData); public abstract DownloadClientInfo GetStatus(); diff --git a/src/NzbDrone.Core/Download/DownloadClientItem.cs b/src/NzbDrone.Core/Download/DownloadClientItem.cs index 3348be4a9..ef890244c 100644 --- a/src/NzbDrone.Core/Download/DownloadClientItem.cs +++ b/src/NzbDrone.Core/Download/DownloadClientItem.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using NzbDrone.Common.Disk; +using NzbDrone.Core.Indexers; namespace NzbDrone.Core.Download { @@ -25,6 +26,20 @@ namespace NzbDrone.Core.Download public bool CanMoveFiles { get; set; } public bool CanBeRemoved { get; set; } + public bool Removed { get; set; } + + public DownloadClientItem Clone() + { + return MemberwiseClone() as DownloadClientItem; + } + } + + public class DownloadClientItemClientInfo + { + public DownloadProtocol Protocol { get; set; } + public string Type { get; set; } + public int Id { get; set; } + public string Name { get; set; } public bool Removed { get; set; } } diff --git a/src/NzbDrone.Core/Download/IDownloadClient.cs b/src/NzbDrone.Core/Download/IDownloadClient.cs index 99b9c0faa..362e51236 100644 --- a/src/NzbDrone.Core/Download/IDownloadClient.cs +++ b/src/NzbDrone.Core/Download/IDownloadClient.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NzbDrone.Core.Indexers; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.ThingiProvider; @@ -11,6 +11,7 @@ namespace NzbDrone.Core.Download string Download(RemoteAlbum remoteAlbum); IEnumerable GetItems(); + DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt); void RemoveItem(string downloadId, bool deleteData); DownloadClientInfo GetStatus(); void MarkItemAsImported(DownloadClientItem downloadClientItem); diff --git a/src/NzbDrone.Core/Download/PrepareImportService.cs b/src/NzbDrone.Core/Download/PrepareImportService.cs new file mode 100644 index 000000000..a816d2341 --- /dev/null +++ b/src/NzbDrone.Core/Download/PrepareImportService.cs @@ -0,0 +1,24 @@ +namespace NzbDrone.Core.Download +{ + public interface IProvideImportItemService + { + DownloadClientItem ProvideImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt); + } + + public class ProvideImportItemService : IProvideImportItemService + { + private readonly IProvideDownloadClient _downloadClientProvider; + + public ProvideImportItemService(IProvideDownloadClient downloadClientProvider) + { + _downloadClientProvider = downloadClientProvider; + } + + public DownloadClientItem ProvideImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt) + { + var client = _downloadClientProvider.Get(item.DownloadClientInfo.Id); + + return client.GetImportItem(item, previousImportAttempt); + } + } +} diff --git a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs index 46c46d529..02d313751 100644 --- a/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs +++ b/src/NzbDrone.Core/Download/TrackedDownloads/TrackedDownload.cs @@ -7,6 +7,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads { public int DownloadClient { get; set; } public DownloadClientItem DownloadItem { get; set; } + public DownloadClientItem ImportItem { get; set; } public TrackedDownloadState State { get; set; } public TrackedDownloadStatus Status { get; private set; } public RemoteAlbum RemoteAlbum { get; set; } diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs index d002973a7..9b7729098 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs @@ -89,7 +89,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual return new List(); } - path = trackedDownload.DownloadItem.OutputPath.FullPath; + path = trackedDownload.ImportItem.OutputPath.FullPath; } if (!_diskProvider.FolderExists(path)) @@ -363,13 +363,15 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual { var trackedDownload = groupedTrackedDownload.First().TrackedDownload; - if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath.FullPath)) + var outputPath = trackedDownload.ImportItem.OutputPath.FullPath; + + if (_diskProvider.FolderExists(outputPath)) { if (_downloadedTracksImportService.ShouldDeleteFolder( - _diskProvider.GetDirectoryInfo(trackedDownload.DownloadItem.OutputPath.FullPath), + _diskProvider.GetDirectoryInfo(outputPath), trackedDownload.RemoteAlbum.Artist) && trackedDownload.DownloadItem.CanMoveFiles) { - _diskProvider.DeleteFolder(trackedDownload.DownloadItem.OutputPath.FullPath, true); + _diskProvider.DeleteFolder(outputPath, true); } }