Add Symlink downloader.
This commit is contained in:
parent
199296dc57
commit
9488eae8d1
4 changed files with 147 additions and 1 deletions
|
|
@ -8,5 +8,8 @@ public enum DownloadClient
|
|||
Internal,
|
||||
|
||||
[Description("Aria2c")]
|
||||
Aria2c
|
||||
Aria2c,
|
||||
|
||||
[Description("Symlink Downloader")]
|
||||
Symlink,
|
||||
}
|
||||
|
|
@ -111,6 +111,10 @@ http://127.0.0.1:6800/jsonrpc.")]
|
|||
[DisplayName("Aria2c Secret (only used for the Aria2c Downloader)")]
|
||||
[Description("The secret of your Aria2c instance. Optional.")]
|
||||
public String Aria2cSecret { get; set; } = "mysecret123";
|
||||
|
||||
[DisplayName("Rclone mount path (only used for the Symlink Downloader)")]
|
||||
[Description("Path where Rclone is mounted. Required for Symlink Downloader.")]
|
||||
public String RcloneMountPath { get; set; } = "/mnt/rd/";
|
||||
}
|
||||
|
||||
public class DbSettingsProvider
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ public class DownloadClient
|
|||
{
|
||||
Data.Enums.DownloadClient.Internal => new InternalDownloader(_download.Link, filePath),
|
||||
Data.Enums.DownloadClient.Aria2c => new Aria2cDownloader(_download.RemoteId, _download.Link, filePath),
|
||||
Data.Enums.DownloadClient.Symlink => new SymlinkDownloader(_download.Link, filePath),
|
||||
_ => throw new Exception($"Unknown download client {Settings.Get.DownloadClient}")
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
using System.Diagnostics;
|
||||
using Serilog;
|
||||
|
||||
namespace RdtClient.Service.Services.Downloaders;
|
||||
|
||||
public class SymlinkDownloader : IDownloader
|
||||
{
|
||||
public event EventHandler<DownloadCompleteEventArgs>? DownloadComplete;
|
||||
public event EventHandler<DownloadProgressEventArgs>? DownloadProgress;
|
||||
|
||||
private const Int32 RetryCount = 5;
|
||||
|
||||
private readonly String _filePath;
|
||||
private readonly String _uri;
|
||||
|
||||
private readonly CancellationTokenSource _cancellationToken = new();
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SymlinkDownloader(String uri, String filePath)
|
||||
{
|
||||
_logger = Log.ForContext<SymlinkDownloader>();
|
||||
|
||||
_uri = uri;
|
||||
_filePath = filePath;
|
||||
}
|
||||
|
||||
public async Task<String?> Download()
|
||||
{
|
||||
_logger.Debug($"Starting download of {_uri}, writing to path: {_filePath}");
|
||||
|
||||
var fileName = Path.GetFileName(_filePath);
|
||||
|
||||
DownloadProgress?.Invoke(this, new DownloadProgressEventArgs
|
||||
{
|
||||
BytesDone = 0,
|
||||
BytesTotal = 0,
|
||||
Speed = 0
|
||||
});
|
||||
|
||||
var retryCount = 1;
|
||||
|
||||
while (retryCount < RetryCount)
|
||||
{
|
||||
_logger.Debug($"Searching {Settings.Get.DownloadClient.RcloneMountPath} for {fileName} ({retryCount}/{RetryCount}) ");
|
||||
|
||||
// Recursively search for the fileName in the rclone mount location.
|
||||
var foundFiles = Directory.GetFiles(Settings.Get.DownloadClient.RcloneMountPath, fileName, SearchOption.AllDirectories).ToList();
|
||||
|
||||
if (foundFiles.Any())
|
||||
{
|
||||
if (foundFiles.Count > 1)
|
||||
{
|
||||
_logger.Warning($"Found {foundFiles.Count} files named {fileName}");
|
||||
}
|
||||
|
||||
// Assume first matching filename is the one we want.
|
||||
var actualFilePath = foundFiles.First();
|
||||
|
||||
var result = TryCreateSymbolicLink(actualFilePath, _filePath);
|
||||
|
||||
if (result)
|
||||
{
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs());
|
||||
|
||||
return actualFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(30), _cancellationToken.Token);
|
||||
|
||||
retryCount++;
|
||||
}
|
||||
|
||||
_logger.Error($"File '{fileName}' not found after {RetryCount} attempts.");
|
||||
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
|
||||
{
|
||||
Error = $"File '{fileName}' not found after {RetryCount} attempts."
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task Cancel()
|
||||
{
|
||||
_logger.Debug($"Cancelling download {_uri}");
|
||||
|
||||
_cancellationToken.Cancel(false);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Pause()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task Resume()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Boolean TryCreateSymbolicLink(String sourcePath, String symlinkPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var process = new Process();
|
||||
process.StartInfo.FileName = "ln";
|
||||
process.StartInfo.Arguments = @$"-s ""{sourcePath}"" ""{symlinkPath}""";
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
|
||||
process.Start();
|
||||
|
||||
var errors = process.StandardError.ReadToEnd();
|
||||
|
||||
process.WaitForExit();
|
||||
|
||||
if (process.ExitCode == 0)
|
||||
{
|
||||
_logger.Information($"Created symbolic link from {sourcePath} to {symlinkPath}");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.Error($"Failed to create symbolic link: {process.ExitCode} - {errors}");
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"Error creating symbolic link from {sourcePath} to {symlinkPath}: {ex.Message}");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue