From 367e195ed6d0ea5b51b99cac8e869028203b7daf Mon Sep 17 00:00:00 2001 From: Roger Far Date: Mon, 8 Apr 2024 20:36:42 -0600 Subject: [PATCH] Symlink fixes. --- CHANGELOG.md | 4 + client/src/app/navbar/navbar.component.html | 2 +- package.json | 2 +- server/RdtClient.Data/Data/DownloadData.cs | 36 +--- .../Services/DownloadClient.cs | 2 +- .../Services/Downloaders/SymlinkDownloader.cs | 157 +++++++++++------- .../RdtClient.Service/Services/Downloads.cs | 14 +- .../Services/TorrentRunner.cs | 30 +--- server/RdtClient.Web/RdtClient.Web.csproj | 2 +- 9 files changed, 112 insertions(+), 137 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afc3d2f..e23ba91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.66] - 2024-04-08 +### Changed +- Symlink fixes and improvements. + ## [2.0.65] - 2024-04-07 ### Added - Added option to configure the buffersize for the internal downloader. diff --git a/client/src/app/navbar/navbar.component.html b/client/src/app/navbar/navbar.component.html index e8b5e36..88ce849 100644 --- a/client/src/app/navbar/navbar.component.html +++ b/client/src/app/navbar/navbar.component.html @@ -55,7 +55,7 @@ Profile Logout - Version 2.0.65 + Version 2.0.66 diff --git a/package.json b/package.json index d93ab81..e3e163e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rdt-client", - "version": "2.0.65", + "version": "2.0.66", "description": "This is a web interface to manage your torrents on Real-Debrid.", "main": "index.js", "dependencies": { diff --git a/server/RdtClient.Data/Data/DownloadData.cs b/server/RdtClient.Data/Data/DownloadData.cs index a59357a..4488b9d 100644 --- a/server/RdtClient.Data/Data/DownloadData.cs +++ b/server/RdtClient.Data/Data/DownloadData.cs @@ -185,25 +185,7 @@ public class DownloadData(DataContext dataContext) await TorrentData.VoidCache(); } - - public async Task UpdateErrors(Dictionary downloadIds) - { - foreach (var entry in downloadIds) - { - var dbDownload = await dataContext.Downloads - .FirstOrDefaultAsync(m => m.DownloadId == entry.Key); - - if (dbDownload == null) - { - continue; - } - - dbDownload.Error = entry.Value; - } - - await dataContext.SaveChangesAsync(); - } - + public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount) { var dbDownload = await dataContext.Downloads @@ -236,22 +218,6 @@ public class DownloadData(DataContext dataContext) await dataContext.SaveChangesAsync(); } - public async Task UpdateRemoteIds(Dictionary remoteIds) - { - foreach (var entry in remoteIds) - { - var dbDownload = await dataContext.Downloads.FirstOrDefaultAsync(m => m.DownloadId == entry.Key); - if (dbDownload == null) - { - continue; - } - - dbDownload.RemoteId = entry.Value; - } - - await dataContext.SaveChangesAsync(); - } - public async Task DeleteForTorrent(Guid torrentId) { var downloads = await dataContext.Downloads diff --git a/server/RdtClient.Service/Services/DownloadClient.cs b/server/RdtClient.Service/Services/DownloadClient.cs index edb6815..6290601 100644 --- a/server/RdtClient.Service/Services/DownloadClient.cs +++ b/server/RdtClient.Service/Services/DownloadClient.cs @@ -48,7 +48,7 @@ public class DownloadClient(Download download, Torrent torrent, String destinati Data.Enums.DownloadClient.Internal => new InternalDownloader(download.Link, filePath), Data.Enums.DownloadClient.Bezzad => new BezzadDownloader(download.Link, filePath), Data.Enums.DownloadClient.Aria2c => new Aria2cDownloader(download.RemoteId, download.Link, filePath, downloadPath), - Data.Enums.DownloadClient.Symlink => new SymlinkDownloader(download.Link, filePath), + Data.Enums.DownloadClient.Symlink => new SymlinkDownloader(download.Link, filePath, downloadPath), _ => throw new($"Unknown download client {Settings.Get.DownloadClient}") }; diff --git a/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs b/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs index 03a6036..28bd4e9 100644 --- a/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs +++ b/server/RdtClient.Service/Services/Downloaders/SymlinkDownloader.cs @@ -2,7 +2,7 @@ namespace RdtClient.Service.Services.Downloaders; -public class SymlinkDownloader(String uri, String path) : IDownloader +public class SymlinkDownloader(String uri, String destinationPath, String path) : IDownloader { public event EventHandler? DownloadComplete; public event EventHandler? DownloadProgress; @@ -11,83 +11,118 @@ public class SymlinkDownloader(String uri, String path) : IDownloader private readonly ILogger _logger = Log.ForContext(); + private const Int32 MaxRetries = 10; + public async Task Download() { _logger.Debug($"Starting symlink resolving of {uri}, writing to path: {path}"); - var filePath = new DirectoryInfo(path); - - var fileName = filePath.Name; - var fileExtension = filePath.Extension; - var directoryName = Path.GetDirectoryName(filePath.FullName) ?? throw new($"Cannot get directory name for file {filePath.FullName}"); - var fileDirectory = Path.GetFileName(directoryName) ?? throw new($"Cannot get directory name for file {directoryName}"); - var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName) ?? throw new($"Cannot get directory name for file {fileName}"); - var fileDirectoryWithoutExtension = Path.GetFileNameWithoutExtension(fileDirectory) ?? throw new($"Cannot get directory name for file {fileDirectory}"); - - String[] folders = - [ - fileNameWithoutExtension, - fileDirectoryWithoutExtension, - fileName, - fileDirectory - ]; - - List unWantedExtensions = - [ - "zip", - "rar", - "tar" - ]; - - if (unWantedExtensions.Any(m => fileExtension == m)) + try { - throw new($"Cant handle compressed files with symlink downloader"); - } + var filePath = new FileInfo(path); - DownloadProgress?.Invoke(this, new() - { - BytesDone = 0, - BytesTotal = 0, - Speed = 0 - }); + var rcloneMountPath = Settings.Get.DownloadClient.RcloneMountPath.TrimEnd(['\\', '/']); + var fileName = filePath.Name; + var fileExtension = filePath.Extension; + var pathWithoutFileName = path.Replace(fileName, "").TrimEnd(['\\', '/']); + var searchPath = Path.Combine(rcloneMountPath, pathWithoutFileName); - FileInfo? file = null; + List unWantedExtensions = + [ + "zip", + "rar", + "tar" + ]; - var tries = 1; + if (unWantedExtensions.Any(m => fileExtension == m)) + { + throw new($"Cant handle compressed files with symlink downloader"); + } - while (file == null && tries <= 10) - { - _logger.Debug($"Searching {Settings.Get.DownloadClient.RcloneMountPath} for {fileName} (attempt #{tries})..."); + DownloadProgress?.Invoke(this, + new() + { + BytesDone = 0, + BytesTotal = 0, + Speed = 0 + }); - var dirInfo = new DirectoryInfo(Settings.Get.DownloadClient.RcloneMountPath); - file = dirInfo.EnumerateDirectories().FirstOrDefault(dir => folders.Contains(dir.Name))?.EnumerateFiles().FirstOrDefault(x => x.Name == fileName); + var potentialFilePaths = new List(); + + var directoryInfo = new DirectoryInfo(searchPath); + while (directoryInfo.Parent != null) + { + potentialFilePaths.Add(directoryInfo.FullName + @"\"); + directoryInfo = directoryInfo.Parent; + + if (directoryInfo.FullName == rcloneMountPath) + { + break; + } + } + + FileInfo? file = null; + + for (var retryCount = 0; retryCount < MaxRetries; retryCount++) + { + DownloadProgress?.Invoke(this, + new() + { + BytesDone = retryCount, + BytesTotal = 10, + Speed = 1 + }); + + _logger.Debug($"Searching {Settings.Get.DownloadClient.RcloneMountPath} for {fileName} (attempt #{retryCount})..."); + + foreach (var potentialFilePath in potentialFilePaths) + { + var potentialFilePathWithFileName = Path.Combine(potentialFilePath, fileName); + + if (File.Exists(potentialFilePathWithFileName)) + { + file = new(potentialFilePathWithFileName); + break; + } + } + + if (file == null) + { + await Task.Delay(1000 * retryCount); + } + else + { + break; + } + } if (file == null) { - await Task.Delay(1000 * tries); - - tries++; + throw new("Could not find file from rclone mount!"); } - } - if (file == null) + _logger.Debug($"Found {file.FullName} at {file.FullName}"); + + var result = TryCreateSymbolicLink(file.FullName, destinationPath); + + if (!result) + { + throw new("Could not find file from rclone mount!"); + } + + DownloadComplete?.Invoke(this, new()); + + return file.FullName; + } + catch (Exception ex) { - throw new("Could not find file from rclone mount!"); + DownloadComplete?.Invoke(this, new() + { + Error = ex.Message + }); + + throw; } - - _logger.Debug($"Found {file.FullName} after #{tries} attempts"); - - var result = TryCreateSymbolicLink(file.FullName, filePath.FullName); - - if (!result) - { - throw new("Could not find file from rclone mount!"); - } - - DownloadComplete?.Invoke(this, new()); - - return file.FullName; - } public Task Cancel() diff --git a/server/RdtClient.Service/Services/Downloads.cs b/server/RdtClient.Service/Services/Downloads.cs index 2db0282..e66954f 100644 --- a/server/RdtClient.Service/Services/Downloads.cs +++ b/server/RdtClient.Service/Services/Downloads.cs @@ -64,12 +64,7 @@ public class Downloads(DownloadData downloadData) { await downloadData.UpdateError(downloadId, error); } - - public async Task UpdateErrors(Dictionary downloadIds) - { - await downloadData.UpdateErrors(downloadIds); - } - + public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount) { await downloadData.UpdateRetryCount(downloadId, retryCount); @@ -79,12 +74,7 @@ public class Downloads(DownloadData downloadData) { await downloadData.UpdateRemoteId(downloadId, remoteId); } - - public async Task UpdateRemoteIds(Dictionary downloadIds) - { - await downloadData.UpdateRemoteIds(downloadIds); - } - + public async Task DeleteForTorrent(Guid torrentId) { await downloadData.DeleteForTorrent(torrentId); diff --git a/server/RdtClient.Service/Services/TorrentRunner.cs b/server/RdtClient.Service/Services/TorrentRunner.cs index ef3ce20..11f648d 100644 --- a/server/RdtClient.Service/Services/TorrentRunner.cs +++ b/server/RdtClient.Service/Services/TorrentRunner.cs @@ -17,9 +17,6 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow public static readonly ConcurrentDictionary ActiveDownloadClients = new(); public static readonly ConcurrentDictionary ActiveUnpackClients = new(); - private readonly Dictionary _aggregatedDownloadResults = []; - private readonly Dictionary _aggregatedDownloadErrors = []; - private readonly HttpClient _httpClient = new() { Timeout = TimeSpan.FromSeconds(10) @@ -334,27 +331,24 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow .OrderBy(m => m.DownloadQueued) .ToList(); - _aggregatedDownloadResults.Clear(); - _aggregatedDownloadErrors.Clear(); - Log($"Currently {queuedDownloads.Count} queued downloads and {ActiveDownloadClients.Count} total active downloads", torrent); foreach (var download in queuedDownloads) { Log($"Processing to download", download, torrent); - if (ActiveDownloadClients.Count >= settingDownloadLimit) + if (ActiveDownloadClients.Count >= settingDownloadLimit && torrent.DownloadClient != Data.Enums.DownloadClient.Symlink) { Log($"Not starting download because there are already the max number of downloads active", download, torrent); - continue; + return; } if (ActiveDownloadClients.ContainsKey(download.DownloadId)) { Log($"Not starting download because this download is already active", download, torrent); - continue; + return; } try @@ -376,7 +370,7 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow download.Error = ex.Message; download.Completed = DateTimeOffset.UtcNow; - continue; + return; } Log($"Marking download as started", download, torrent); @@ -413,32 +407,18 @@ public class TorrentRunner(ILogger logger, Torrents torrents, Dow if (download.RemoteId != remoteId) { - _aggregatedDownloadResults.Add(download.DownloadId, remoteId); + await downloads.UpdateRemoteId(download.DownloadId, remoteId); } } catch (Exception ex) { LogError($"Unable to start download: {ex.Message}", download, torrent); - _aggregatedDownloadErrors.Add(download.DownloadId, ex.Message); } Log($"Started download", download, torrent); } } - if (_aggregatedDownloadResults.Count > 0) - { - await downloads.UpdateRemoteIds(_aggregatedDownloadResults); - } - - if (_aggregatedDownloadErrors.Count > 0) - { - await downloads.UpdateErrors(_aggregatedDownloadErrors); - } - - _aggregatedDownloadResults.Clear(); - _aggregatedDownloadErrors.Clear(); - // Check if there are any unpacks that are queued and can be started. var queuedUnpacks = torrent.Downloads .Where(m => m.Completed == null && m.UnpackingQueued != null && m.UnpackingStarted == null && m.Error == null) diff --git a/server/RdtClient.Web/RdtClient.Web.csproj b/server/RdtClient.Web/RdtClient.Web.csproj index 9b113df..b55d46e 100644 --- a/server/RdtClient.Web/RdtClient.Web.csproj +++ b/server/RdtClient.Web/RdtClient.Web.csproj @@ -4,7 +4,7 @@ net8.0 Exe 94c24cba-f03f-4453-a671-3640b517c573 - 2.0.65 + 2.0.66 enable enable latest