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