Symlink fixes.
Some checks failed
Docker Image CI / build (push) Has been cancelled

This commit is contained in:
Roger Far 2024-04-08 20:36:42 -06:00
parent 949bd7cfd2
commit 367e195ed6
9 changed files with 112 additions and 137 deletions

View file

@ -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/), 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). 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 ## [2.0.65] - 2024-04-07
### Added ### Added
- Added option to configure the buffersize for the internal downloader. - Added option to configure the buffersize for the internal downloader.

View file

@ -55,7 +55,7 @@
<a class="navbar-item" routerLink="profile"> Profile </a> <a class="navbar-item" routerLink="profile"> Profile </a>
<a class="navbar-item" (click)="logout()"> Logout </a> <a class="navbar-item" (click)="logout()"> Logout </a>
<hr class="navbar-divider" /> <hr class="navbar-divider" />
<a href="https://github.com/rogerfar/rdt-client" target="_blank" class="navbar-item">Version 2.0.65</a> <a href="https://github.com/rogerfar/rdt-client" target="_blank" class="navbar-item">Version 2.0.66</a>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,6 +1,6 @@
{ {
"name": "rdt-client", "name": "rdt-client",
"version": "2.0.65", "version": "2.0.66",
"description": "This is a web interface to manage your torrents on Real-Debrid.", "description": "This is a web interface to manage your torrents on Real-Debrid.",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {

View file

@ -186,24 +186,6 @@ public class DownloadData(DataContext dataContext)
await TorrentData.VoidCache(); await TorrentData.VoidCache();
} }
public async Task UpdateErrors(Dictionary<Guid, String> 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) public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount)
{ {
var dbDownload = await dataContext.Downloads var dbDownload = await dataContext.Downloads
@ -236,22 +218,6 @@ public class DownloadData(DataContext dataContext)
await dataContext.SaveChangesAsync(); await dataContext.SaveChangesAsync();
} }
public async Task UpdateRemoteIds(Dictionary<Guid, String> 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) public async Task DeleteForTorrent(Guid torrentId)
{ {
var downloads = await dataContext.Downloads var downloads = await dataContext.Downloads

View file

@ -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.Internal => new InternalDownloader(download.Link, filePath),
Data.Enums.DownloadClient.Bezzad => new BezzadDownloader(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.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}") _ => throw new($"Unknown download client {Settings.Get.DownloadClient}")
}; };

View file

@ -2,7 +2,7 @@
namespace RdtClient.Service.Services.Downloaders; 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<DownloadCompleteEventArgs>? DownloadComplete; public event EventHandler<DownloadCompleteEventArgs>? DownloadComplete;
public event EventHandler<DownloadProgressEventArgs>? DownloadProgress; public event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
@ -11,83 +11,118 @@ public class SymlinkDownloader(String uri, String path) : IDownloader
private readonly ILogger _logger = Log.ForContext<SymlinkDownloader>(); private readonly ILogger _logger = Log.ForContext<SymlinkDownloader>();
private const Int32 MaxRetries = 10;
public async Task<String> Download() public async Task<String> Download()
{ {
_logger.Debug($"Starting symlink resolving of {uri}, writing to path: {path}"); _logger.Debug($"Starting symlink resolving of {uri}, writing to path: {path}");
var filePath = new DirectoryInfo(path); try
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<String> unWantedExtensions =
[
"zip",
"rar",
"tar"
];
if (unWantedExtensions.Any(m => fileExtension == m))
{ {
throw new($"Cant handle compressed files with symlink downloader"); var filePath = new FileInfo(path);
}
DownloadProgress?.Invoke(this, new() var rcloneMountPath = Settings.Get.DownloadClient.RcloneMountPath.TrimEnd(['\\', '/']);
{ var fileName = filePath.Name;
BytesDone = 0, var fileExtension = filePath.Extension;
BytesTotal = 0, var pathWithoutFileName = path.Replace(fileName, "").TrimEnd(['\\', '/']);
Speed = 0 var searchPath = Path.Combine(rcloneMountPath, pathWithoutFileName);
});
FileInfo? file = null; List<String> 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) DownloadProgress?.Invoke(this,
{ new()
_logger.Debug($"Searching {Settings.Get.DownloadClient.RcloneMountPath} for {fileName} (attempt #{tries})..."); {
BytesDone = 0,
BytesTotal = 0,
Speed = 0
});
var dirInfo = new DirectoryInfo(Settings.Get.DownloadClient.RcloneMountPath); var potentialFilePaths = new List<String>();
file = dirInfo.EnumerateDirectories().FirstOrDefault(dir => folders.Contains(dir.Name))?.EnumerateFiles().FirstOrDefault(x => x.Name == fileName);
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) if (file == null)
{ {
await Task.Delay(1000 * tries); throw new("Could not find file from rclone mount!");
tries++;
} }
}
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() public Task Cancel()

View file

@ -65,11 +65,6 @@ public class Downloads(DownloadData downloadData)
await downloadData.UpdateError(downloadId, error); await downloadData.UpdateError(downloadId, error);
} }
public async Task UpdateErrors(Dictionary<Guid, String> downloadIds)
{
await downloadData.UpdateErrors(downloadIds);
}
public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount) public async Task UpdateRetryCount(Guid downloadId, Int32 retryCount)
{ {
await downloadData.UpdateRetryCount(downloadId, retryCount); await downloadData.UpdateRetryCount(downloadId, retryCount);
@ -80,11 +75,6 @@ public class Downloads(DownloadData downloadData)
await downloadData.UpdateRemoteId(downloadId, remoteId); await downloadData.UpdateRemoteId(downloadId, remoteId);
} }
public async Task UpdateRemoteIds(Dictionary<Guid, String> downloadIds)
{
await downloadData.UpdateRemoteIds(downloadIds);
}
public async Task DeleteForTorrent(Guid torrentId) public async Task DeleteForTorrent(Guid torrentId)
{ {
await downloadData.DeleteForTorrent(torrentId); await downloadData.DeleteForTorrent(torrentId);

View file

@ -17,9 +17,6 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new(); public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new();
public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new(); public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new();
private readonly Dictionary<Guid, String> _aggregatedDownloadResults = [];
private readonly Dictionary<Guid, String> _aggregatedDownloadErrors = [];
private readonly HttpClient _httpClient = new() private readonly HttpClient _httpClient = new()
{ {
Timeout = TimeSpan.FromSeconds(10) Timeout = TimeSpan.FromSeconds(10)
@ -334,27 +331,24 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
.OrderBy(m => m.DownloadQueued) .OrderBy(m => m.DownloadQueued)
.ToList(); .ToList();
_aggregatedDownloadResults.Clear();
_aggregatedDownloadErrors.Clear();
Log($"Currently {queuedDownloads.Count} queued downloads and {ActiveDownloadClients.Count} total active downloads", torrent); Log($"Currently {queuedDownloads.Count} queued downloads and {ActiveDownloadClients.Count} total active downloads", torrent);
foreach (var download in queuedDownloads) foreach (var download in queuedDownloads)
{ {
Log($"Processing to download", download, torrent); 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); Log($"Not starting download because there are already the max number of downloads active", download, torrent);
continue; return;
} }
if (ActiveDownloadClients.ContainsKey(download.DownloadId)) if (ActiveDownloadClients.ContainsKey(download.DownloadId))
{ {
Log($"Not starting download because this download is already active", download, torrent); Log($"Not starting download because this download is already active", download, torrent);
continue; return;
} }
try try
@ -376,7 +370,7 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
download.Error = ex.Message; download.Error = ex.Message;
download.Completed = DateTimeOffset.UtcNow; download.Completed = DateTimeOffset.UtcNow;
continue; return;
} }
Log($"Marking download as started", download, torrent); Log($"Marking download as started", download, torrent);
@ -413,32 +407,18 @@ public class TorrentRunner(ILogger<TorrentRunner> logger, Torrents torrents, Dow
if (download.RemoteId != remoteId) if (download.RemoteId != remoteId)
{ {
_aggregatedDownloadResults.Add(download.DownloadId, remoteId); await downloads.UpdateRemoteId(download.DownloadId, remoteId);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
LogError($"Unable to start download: {ex.Message}", download, torrent); LogError($"Unable to start download: {ex.Message}", download, torrent);
_aggregatedDownloadErrors.Add(download.DownloadId, ex.Message);
} }
Log($"Started download", download, torrent); 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. // Check if there are any unpacks that are queued and can be started.
var queuedUnpacks = torrent.Downloads var queuedUnpacks = torrent.Downloads
.Where(m => m.Completed == null && m.UnpackingQueued != null && m.UnpackingStarted == null && m.Error == null) .Where(m => m.Completed == null && m.UnpackingQueued != null && m.UnpackingStarted == null && m.Error == null)

View file

@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<UserSecretsId>94c24cba-f03f-4453-a671-3640b517c573</UserSecretsId> <UserSecretsId>94c24cba-f03f-4453-a671-3640b517c573</UserSecretsId>
<Version>2.0.65</Version> <Version>2.0.66</Version>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>