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/),
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.

View file

@ -55,7 +55,7 @@
<a class="navbar-item" routerLink="profile"> Profile </a>
<a class="navbar-item" (click)="logout()"> Logout </a>
<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>

View file

@ -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": {

View file

@ -185,25 +185,7 @@ public class DownloadData(DataContext dataContext)
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)
{
var dbDownload = await dataContext.Downloads
@ -236,22 +218,6 @@ public class DownloadData(DataContext dataContext)
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)
{
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.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}")
};

View file

@ -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<DownloadCompleteEventArgs>? DownloadComplete;
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 const Int32 MaxRetries = 10;
public async Task<String> 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<String> 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<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)
{
_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<String>();
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()

View file

@ -64,12 +64,7 @@ public class Downloads(DownloadData downloadData)
{
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)
{
await downloadData.UpdateRetryCount(downloadId, retryCount);
@ -79,12 +74,7 @@ public class Downloads(DownloadData downloadData)
{
await downloadData.UpdateRemoteId(downloadId, remoteId);
}
public async Task UpdateRemoteIds(Dictionary<Guid, String> downloadIds)
{
await downloadData.UpdateRemoteIds(downloadIds);
}
public async Task DeleteForTorrent(Guid 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, UnpackClient> ActiveUnpackClients = new();
private readonly Dictionary<Guid, String> _aggregatedDownloadResults = [];
private readonly Dictionary<Guid, String> _aggregatedDownloadErrors = [];
private readonly HttpClient _httpClient = new()
{
Timeout = TimeSpan.FromSeconds(10)
@ -334,27 +331,24 @@ public class TorrentRunner(ILogger<TorrentRunner> 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<TorrentRunner> 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<TorrentRunner> 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)

View file

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