Rename folder to path everywhere.
Added a remote path setting for path mapping for Sonarr/Radarr.
This commit is contained in:
parent
2300b51ede
commit
7f9d6e6cba
11 changed files with 160 additions and 101 deletions
|
|
@ -18,11 +18,21 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Download folder</label>
|
||||
<label class="label">Download path</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingDownloadFolder" />
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingDownloadPath" />
|
||||
</div>
|
||||
<p class="help">Path on the host where RealDebrid Client is hosted.</p>
|
||||
<p class="help">Path in the docker container to download files to (i.e. /data/downloads).</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Mapped path</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingMappedPath" />
|
||||
</div>
|
||||
<p class="help">
|
||||
Path where files are downloaded to on your host (i.e. D:\Downloads). This path is used for Radarr and Sonarr
|
||||
to find your downloads.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Maximum parallel downloads</label>
|
||||
|
|
@ -38,16 +48,30 @@
|
|||
</div>
|
||||
<p class="help">Maximum amount of downloads that get unpacked on your host at the same time.</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Minimum file size to download</label>
|
||||
<div class="control">
|
||||
<div class="field has-addons" style="margin-bottom: 0">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="number" max="100" min="1" [(ngModel)]="settingMinFileSize" />
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-static">MB</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="help">When selecting files in the torrent, skip files smaller than this setting.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="notification is-danger is-light" *ngIf="error?.length > 0">Error saving settings: {{ error }}</div>
|
||||
|
||||
<div class="notification is-danger is-light" *ngIf="testFolderError?.length > 0">
|
||||
Could not test your download folder<br />
|
||||
{{ testFolderError }}
|
||||
<div class="notification is-danger is-light" *ngIf="testPathError?.length > 0">
|
||||
Could not test your download path<br />
|
||||
{{ testPathError }}
|
||||
</div>
|
||||
|
||||
<div class="notification is-success is-light" *ngIf="testFolderSuccess">
|
||||
Successfully tested your download folder
|
||||
</div>
|
||||
<div class="notification is-success is-light" *ngIf="testPathSuccess">Successfully tested your download path</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
|
||||
|
|
|
|||
|
|
@ -25,13 +25,15 @@ export class SettingsComponent implements OnInit {
|
|||
|
||||
public saving = false;
|
||||
public error: string;
|
||||
public testFolderError: string;
|
||||
public testFolderSuccess: boolean;
|
||||
public testPathError: string;
|
||||
public testPathSuccess: boolean;
|
||||
|
||||
public settingRealDebridApiKey: string;
|
||||
public settingDownloadFolder: string;
|
||||
public settingDownloadPath: string;
|
||||
public settingMappedPath: string;
|
||||
public settingDownloadLimit: number;
|
||||
public settingUnpackLimit: number;
|
||||
public settingMinFileSize: number;
|
||||
|
||||
constructor(private settingsService: SettingsService) {}
|
||||
|
||||
|
|
@ -44,9 +46,11 @@ export class SettingsComponent implements OnInit {
|
|||
this.settingsService.get().subscribe(
|
||||
(results) => {
|
||||
this.settingRealDebridApiKey = this.getSetting(results, 'RealDebridApiKey');
|
||||
this.settingDownloadFolder = this.getSetting(results, 'DownloadFolder');
|
||||
this.settingDownloadPath = this.getSetting(results, 'DownloadPath');
|
||||
this.settingMappedPath = this.getSetting(results, 'MappedPath');
|
||||
this.settingDownloadLimit = parseInt(this.getSetting(results, 'DownloadLimit'), 10);
|
||||
this.settingUnpackLimit = parseInt(this.getSetting(results, 'UnpackLimit'), 10);
|
||||
this.settingMinFileSize = parseInt(this.getSetting(results, 'MinFileSize'), 10);
|
||||
},
|
||||
(err) => {
|
||||
this.error = err.error;
|
||||
|
|
@ -64,8 +68,12 @@ export class SettingsComponent implements OnInit {
|
|||
value: this.settingRealDebridApiKey,
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadFolder',
|
||||
value: this.settingDownloadFolder,
|
||||
settingId: 'DownloadPath',
|
||||
value: this.settingDownloadPath,
|
||||
},
|
||||
{
|
||||
settingId: 'MappedPath',
|
||||
value: this.settingMappedPath,
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadLimit',
|
||||
|
|
@ -75,6 +83,10 @@ export class SettingsComponent implements OnInit {
|
|||
settingId: 'UnpackLimit',
|
||||
value: (this.settingUnpackLimit ?? 1).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'MinFileSize',
|
||||
value: (this.settingMinFileSize ?? 0).toString(),
|
||||
},
|
||||
];
|
||||
|
||||
this.settingsService.update(settings).subscribe(
|
||||
|
|
@ -90,17 +102,17 @@ export class SettingsComponent implements OnInit {
|
|||
|
||||
public test(): void {
|
||||
this.saving = true;
|
||||
this.testFolderError = null;
|
||||
this.testFolderSuccess = false;
|
||||
this.testPathError = null;
|
||||
this.testPathSuccess = false;
|
||||
|
||||
this.settingsService.testFolder(this.settingDownloadFolder).subscribe(
|
||||
this.settingsService.testPath(this.settingDownloadPath).subscribe(
|
||||
() => {
|
||||
this.saving = false;
|
||||
this.testFolderSuccess = true;
|
||||
this.testPathSuccess = true;
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
this.testFolderError = err.error;
|
||||
this.testPathError = err.error;
|
||||
this.saving = false;
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export class SettingsService {
|
|||
return this.http.get<Profile>(`/Api/Settings/Profile`);
|
||||
}
|
||||
|
||||
public testFolder(folder: string): Observable<void> {
|
||||
return this.http.post<void>(`/Api/Settings/TestFolder`, { folder });
|
||||
public testPath(path: string): Observable<void> {
|
||||
return this.http.post<void>(`/Api/Settings/TestPath`, { path });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,69 +13,71 @@ export class TorrentStatusPipe implements PipeTransform {
|
|||
return 'Finished';
|
||||
}
|
||||
|
||||
const allFinished = torrent.downloads.all((m) => m.completed != null);
|
||||
if (torrent.downloads.length > 0) {
|
||||
const allFinished = torrent.downloads.all((m) => m.completed != null);
|
||||
|
||||
if (allFinished) {
|
||||
return 'Finished';
|
||||
}
|
||||
|
||||
const errors = torrent.downloads.where((m) => m.error != null);
|
||||
|
||||
if (errors.length > 0) {
|
||||
return 'Error';
|
||||
}
|
||||
|
||||
const downloading = torrent.downloads.where((m) => m.downloadStarted && !m.downloadFinished);
|
||||
|
||||
if (downloading.length > 0) {
|
||||
const bytesDone = downloading.sum((m) => m.bytesDone);
|
||||
const bytesTotal = downloading.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = downloading.sum((m) => m.speed) / downloading.length;
|
||||
|
||||
let speed: string | string[] = '0';
|
||||
if (allSpeeds > 0) {
|
||||
speed = this.pipe.transform(allSpeeds, 'filesize');
|
||||
|
||||
return `Downloading (${progress.toFixed(2)}% - ${speed}/s)`;
|
||||
if (allFinished) {
|
||||
return 'Finished';
|
||||
}
|
||||
}
|
||||
|
||||
const unpacking = torrent.downloads.where((m) => m.unpackingStarted && !m.unpackingFinished);
|
||||
const errors = torrent.downloads.where((m) => m.error != null);
|
||||
|
||||
if (unpacking.length > 0) {
|
||||
const bytesDone = unpacking.sum((m) => m.bytesDone);
|
||||
const bytesTotal = unpacking.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = unpacking.sum((m) => m.speed) / unpacking.length;
|
||||
|
||||
if (allSpeeds > 0) {
|
||||
return `Extracting (${progress.toFixed(2)}%)`;
|
||||
if (errors.length > 0) {
|
||||
return 'Error';
|
||||
}
|
||||
}
|
||||
|
||||
const queuedForUnpacking = torrent.downloads.where((m) => m.unpackingQueued && !m.unpackingStarted);
|
||||
const downloading = torrent.downloads.where((m) => m.downloadStarted && !m.downloadFinished);
|
||||
|
||||
if (queuedForUnpacking.length > 0) {
|
||||
return `Queued for unpacking`;
|
||||
}
|
||||
if (downloading.length > 0) {
|
||||
const bytesDone = downloading.sum((m) => m.bytesDone);
|
||||
const bytesTotal = downloading.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = downloading.sum((m) => m.speed) / downloading.length;
|
||||
|
||||
const queuedForDownload = torrent.downloads.where((m) => m.downloadQueued && !m.downloadStarted);
|
||||
let speed: string | string[] = '0';
|
||||
if (allSpeeds > 0) {
|
||||
speed = this.pipe.transform(allSpeeds, 'filesize');
|
||||
|
||||
if (queuedForDownload.length > 0) {
|
||||
return `Queued for downloading`;
|
||||
}
|
||||
return `Downloading (${progress.toFixed(2)}% - ${speed}/s)`;
|
||||
}
|
||||
}
|
||||
|
||||
const unpacked = torrent.downloads.where((m) => m.unpackingFinished != null);
|
||||
const unpacking = torrent.downloads.where((m) => m.unpackingStarted && !m.unpackingFinished);
|
||||
|
||||
if (unpacked.length > 0) {
|
||||
return `Files unpacked`;
|
||||
}
|
||||
if (unpacking.length > 0) {
|
||||
const bytesDone = unpacking.sum((m) => m.bytesDone);
|
||||
const bytesTotal = unpacking.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = unpacking.sum((m) => m.speed) / unpacking.length;
|
||||
|
||||
const downloaded = torrent.downloads.where((m) => m.downloadFinished != null);
|
||||
if (allSpeeds > 0) {
|
||||
return `Extracting (${progress.toFixed(2)}%)`;
|
||||
}
|
||||
}
|
||||
|
||||
if (downloaded.length > 0) {
|
||||
return `Files downloaded to host`;
|
||||
const queuedForUnpacking = torrent.downloads.where((m) => m.unpackingQueued && !m.unpackingStarted);
|
||||
|
||||
if (queuedForUnpacking.length > 0) {
|
||||
return `Queued for unpacking`;
|
||||
}
|
||||
|
||||
const queuedForDownload = torrent.downloads.where((m) => m.downloadQueued && !m.downloadStarted);
|
||||
|
||||
if (queuedForDownload.length > 0) {
|
||||
return `Queued for downloading`;
|
||||
}
|
||||
|
||||
const unpacked = torrent.downloads.where((m) => m.unpackingFinished != null);
|
||||
|
||||
if (unpacked.length > 0) {
|
||||
return `Files unpacked`;
|
||||
}
|
||||
|
||||
const downloaded = torrent.downloads.where((m) => m.downloadFinished != null);
|
||||
|
||||
if (downloaded.length > 0) {
|
||||
return `Files downloaded to host`;
|
||||
}
|
||||
}
|
||||
|
||||
switch (torrent.rdStatus) {
|
||||
|
|
@ -89,7 +91,7 @@ export class TorrentStatusPipe implements PipeTransform {
|
|||
case RealDebridStatus.Error:
|
||||
return `Torrent error: ${torrent.rdStatusRaw}`;
|
||||
case RealDebridStatus.Finished:
|
||||
return `Torrent finished`;
|
||||
return `Torrent finished, waiting to download`;
|
||||
default:
|
||||
return 'Unknown status';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace RdtClient.Data.Data
|
|||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadFolder",
|
||||
SettingId = "DownloadPath",
|
||||
Type = "String",
|
||||
#if DEBUG
|
||||
Value = @"C:\Temp\rdtclient"
|
||||
|
|
@ -57,6 +57,16 @@ namespace RdtClient.Data.Data
|
|||
#endif
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "MappedPath",
|
||||
Type = "String",
|
||||
#if DEBUG
|
||||
Value = @"C:\Temp\rdtclient"
|
||||
#else
|
||||
Value = "D:\Downloads"
|
||||
#endif
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadLimit",
|
||||
Type = "Int32",
|
||||
|
|
@ -67,6 +77,12 @@ namespace RdtClient.Data.Data
|
|||
SettingId = "UnpackLimit",
|
||||
Type = "Int32",
|
||||
Value = "1"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "MinFileSize",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
public async Task<String> AppDefaultSavePath()
|
||||
{
|
||||
var downloadPath = await _settings.GetString("DownloadFolder");
|
||||
var downloadPath = await _settings.GetString("MappedPath");
|
||||
downloadPath = downloadPath.TrimEnd('\\')
|
||||
.TrimEnd('/');
|
||||
downloadPath += Path.DirectorySeparatorChar;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace RdtClient.Service.Services
|
|||
Task Update(IList<Setting> settings);
|
||||
Task<String> GetString(String key);
|
||||
Task<Int32> GetNumber(String key);
|
||||
Task TestFolder(String folder);
|
||||
Task TestPath(String path);
|
||||
}
|
||||
|
||||
public class Settings : ISettings
|
||||
|
|
@ -59,21 +59,21 @@ namespace RdtClient.Service.Services
|
|||
return Int32.Parse(setting.Value);
|
||||
}
|
||||
|
||||
public async Task TestFolder(String folder)
|
||||
public async Task TestPath(String path)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(folder))
|
||||
if (String.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
throw new Exception("Folder path is not set");
|
||||
throw new Exception("Path is not set");
|
||||
}
|
||||
|
||||
folder = folder.TrimEnd('/').TrimEnd('\\');
|
||||
path = path.TrimEnd('/').TrimEnd('\\');
|
||||
|
||||
if (!Directory.Exists(folder))
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
throw new Exception($"Folder {folder} does not exist");
|
||||
throw new Exception($"Path {path} does not exist");
|
||||
}
|
||||
|
||||
var testFile = $"{folder}/test.txt";
|
||||
var testFile = $"{path}/test.txt";
|
||||
|
||||
await File.WriteAllTextAsync(testFile, "RealDebridClient Test File, you can remove this file.");
|
||||
File.Delete(testFile);
|
||||
|
|
|
|||
|
|
@ -20,12 +20,8 @@ namespace RdtClient.Service.Services
|
|||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
#if DEBUG
|
||||
await Task.Delay(TimeSpan.FromSeconds(3), stoppingToken);
|
||||
#else
|
||||
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
|
||||
#endif
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var torrentRunner = scope.ServiceProvider.GetRequiredService<ITorrentRunner>();
|
||||
|
||||
|
|
@ -44,7 +40,7 @@ namespace RdtClient.Service.Services
|
|||
_logger.LogError(ex, "Unexpected error occurred in TorrentDownloadManager.Tick");
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);
|
||||
}
|
||||
|
||||
_logger.LogInformation("TaskRunner stopped.");
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ namespace RdtClient.Service.Services
|
|||
public async Task Tick()
|
||||
{
|
||||
var settingApiKey = await _settings.GetString("RealDebridApiKey");
|
||||
var minFileSizeSetting = await _settings.GetNumber("MinFileSize");
|
||||
|
||||
if (String.IsNullOrWhiteSpace(settingApiKey))
|
||||
{
|
||||
|
|
@ -164,6 +165,14 @@ namespace RdtClient.Service.Services
|
|||
.Select(m => m.Id.ToString())
|
||||
.ToArray();
|
||||
|
||||
if (minFileSizeSetting > 0)
|
||||
{
|
||||
fileIds = torrent.Files
|
||||
.Where(m => m.Bytes * 1024 * 1024 > minFileSizeSetting)
|
||||
.Select(m => m.Id.ToString())
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
await _torrents.SelectFiles(torrent.RdId, fileIds);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -165,9 +165,9 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
if (deleteLocalFiles)
|
||||
{
|
||||
var settingDownloadFolder = await _settings.GetString("DownloadFolder");
|
||||
var settingDownloadPath = await _settings.GetString("DownloadPath");
|
||||
|
||||
var torrentPath = Path.Combine(settingDownloadFolder, torrent.RdName);
|
||||
var torrentPath = Path.Combine(settingDownloadPath, torrent.RdName);
|
||||
|
||||
if (Directory.Exists(torrentPath))
|
||||
{
|
||||
|
|
@ -210,7 +210,7 @@ namespace RdtClient.Service.Services
|
|||
public async Task Download(Guid downloadId)
|
||||
{
|
||||
var settingDownloadLimit = await _settings.GetNumber("DownloadLimit");
|
||||
var settingDownloadFolder = await _settings.GetString("DownloadFolder");
|
||||
var settingDownloadPath = await _settings.GetString("DownloadPath");
|
||||
|
||||
var download = await _downloads.GetById(downloadId);
|
||||
|
||||
|
|
@ -224,7 +224,7 @@ namespace RdtClient.Service.Services
|
|||
await _downloads.UpdateDownloadStarted(download.DownloadId, download.DownloadStarted);
|
||||
|
||||
// Start the download process
|
||||
var downloadClient = new DownloadClient(download, settingDownloadFolder);
|
||||
var downloadClient = new DownloadClient(download, settingDownloadPath);
|
||||
|
||||
if (TorrentRunner.ActiveDownloadClients.TryAdd(downloadId, downloadClient))
|
||||
{
|
||||
|
|
@ -235,7 +235,7 @@ namespace RdtClient.Service.Services
|
|||
public async Task Unpack(Guid downloadId)
|
||||
{
|
||||
var settingUnpackLimit = await _settings.GetNumber("UnpackLimit");
|
||||
var settingDownloadFolder = await _settings.GetString("DownloadFolder");
|
||||
var settingDownloadPath = await _settings.GetString("DownloadPath");
|
||||
|
||||
var download = await _downloads.GetById(downloadId);
|
||||
|
||||
|
|
@ -270,7 +270,7 @@ namespace RdtClient.Service.Services
|
|||
await _downloads.UpdateUnpackingStarted(download.DownloadId, download.UnpackingStarted);
|
||||
|
||||
// Start the unpacking process
|
||||
var unpackClient = new UnpackClient(download, settingDownloadFolder);
|
||||
var unpackClient = new UnpackClient(download, settingDownloadPath);
|
||||
|
||||
if (TorrentRunner.ActiveUnpackClients.TryAdd(downloadId, unpackClient))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ namespace RdtClient.Web.Controllers
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("TestFolder")]
|
||||
public async Task<ActionResult<Profile>> TestFolder([FromBody] SettingsControllerTestFolderRequest request)
|
||||
[Route("TestPath")]
|
||||
public async Task<ActionResult<Profile>> TestPath([FromBody] SettingsControllerTestPathRequest request)
|
||||
{
|
||||
await _settings.TestFolder(request.Folder);
|
||||
await _settings.TestPath(request.Path);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
|
@ -63,8 +63,8 @@ namespace RdtClient.Web.Controllers
|
|||
public IList<Setting> Settings { get; set; }
|
||||
}
|
||||
|
||||
public class SettingsControllerTestFolderRequest
|
||||
public class SettingsControllerTestPathRequest
|
||||
{
|
||||
public String Folder { get; set; }
|
||||
public String Path { get; set; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue