Add running of external applications.
This commit is contained in:
parent
d4cb09e1a3
commit
6ca403f7f2
7 changed files with 152 additions and 0 deletions
|
|
@ -137,6 +137,40 @@
|
|||
</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">Run external program on torrent completion</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" [(ngModel)]="settingRunOnTorrentCompleteFileName" />
|
||||
</div>
|
||||
<p class="help">
|
||||
Path to the executable to run when the torrent and all downloads are finished. No arguments should be passed here.
|
||||
<br />
|
||||
<strong>When running in Docker, this command will run on your docker instance!</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">External program arguments</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" [(ngModel)]="settingRunOnTorrentCompleteArguments" />
|
||||
</div>
|
||||
<p class="help">
|
||||
When the executable above is executed, use these parameters.
|
||||
<br />
|
||||
Supports the following parameters:
|
||||
</p>
|
||||
<ul class="help">
|
||||
<li>%N: Torrent name</li>
|
||||
<li>%L: Category</li>
|
||||
<li>%F: Content path (same as root path for multifile torrent)</li>
|
||||
<li>%R: Root path (first torrent subdirectory path)</li>
|
||||
<li>%D: Save path</li>
|
||||
<li>%C: Number of files</li>
|
||||
<li>%Z: Torrent size (bytes)</li>
|
||||
<li>%I: Info hash</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="activeTab === 1">
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ export class SettingsComponent implements OnInit {
|
|||
public settingTorrentRetryAttempts: number;
|
||||
public settingDeleteOnError: number;
|
||||
public settingTorrentLifetime: number;
|
||||
public settingRunOnTorrentCompleteFileName: string;
|
||||
public settingRunOnTorrentCompleteArguments: string;
|
||||
|
||||
constructor(private settingsService: SettingsService) {}
|
||||
|
||||
|
|
@ -84,6 +86,8 @@ export class SettingsComponent implements OnInit {
|
|||
this.settingTorrentRetryAttempts = parseInt(this.getSetting(results, 'TorrentRetryAttempts'), 10);
|
||||
this.settingDeleteOnError = parseInt(this.getSetting(results, 'DeleteOnError'), 10);
|
||||
this.settingTorrentLifetime = parseInt(this.getSetting(results, 'TorrentLifetime'), 10);
|
||||
this.settingRunOnTorrentCompleteFileName = this.getSetting(results, 'RunOnTorrentCompleteFileName');
|
||||
this.settingRunOnTorrentCompleteArguments = this.getSetting(results, 'RunOnTorrentCompleteArguments');
|
||||
},
|
||||
(err) => {
|
||||
this.error = err.error;
|
||||
|
|
@ -188,6 +192,14 @@ export class SettingsComponent implements OnInit {
|
|||
settingId: 'TorrentLifetime',
|
||||
value: (this.settingTorrentLifetime ?? 0).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'RunOnTorrentCompleteFileName',
|
||||
value: this.settingRunOnTorrentCompleteFileName,
|
||||
},
|
||||
{
|
||||
settingId: 'RunOnTorrentCompleteArguments',
|
||||
value: this.settingRunOnTorrentCompleteArguments,
|
||||
},
|
||||
];
|
||||
|
||||
this.settingsService.update(settings).subscribe(
|
||||
|
|
|
|||
|
|
@ -195,6 +195,18 @@ namespace RdtClient.Data.Data
|
|||
SettingId = "TorrentRetryAttempts",
|
||||
Type = "Int32",
|
||||
Value = "1"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "RunOnTorrentCompleteFileName",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "RunOnTorrentCompleteArguments",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ namespace RdtClient.Data.Data
|
|||
TorrentRetryAttempts = GetInt32("TorrentRetryAttempts"),
|
||||
DeleteOnError = GetInt32("DeleteOnError"),
|
||||
TorrentLifetime = GetInt32("TorrentLifetime"),
|
||||
RunOnTorrentCompleteFileName = GetString("RunOnTorrentCompleteFileName"),
|
||||
RunOnTorrentCompleteArguments = GetString("RunOnTorrentCompleteArguments")
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,5 +28,7 @@ namespace RdtClient.Data.Models.Internal
|
|||
public Int32 TorrentRetryAttempts { get; set; }
|
||||
public Int32 DeleteOnError { get; set; }
|
||||
public Int32 TorrentLifetime { get; set; }
|
||||
public String RunOnTorrentCompleteFileName { get; set; }
|
||||
public String RunOnTorrentCompleteArguments { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -573,6 +573,8 @@ namespace RdtClient.Service.Services
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
await _torrents.RunTorrentComplete(torrent.TorrentId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -624,6 +627,91 @@ namespace RdtClient.Service.Services
|
|||
await _torrentData.Update(torrent);
|
||||
}
|
||||
|
||||
public async Task RunTorrentComplete(Guid torrentId)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(Settings.Get.RunOnTorrentCompleteFileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var torrent = await _torrentData.GetById(torrentId);
|
||||
var downloads = await _downloads.GetForTorrent(torrentId);
|
||||
|
||||
var fileName = Settings.Get.RunOnTorrentCompleteFileName;
|
||||
var arguments = Settings.Get.RunOnTorrentCompleteArguments ?? "";
|
||||
|
||||
Log($"Parsing external program {fileName} with arguments {arguments}", torrent);
|
||||
|
||||
var downloadPath = DownloadPath(torrent);
|
||||
var torrentPath = Path.Combine(downloadPath, torrent.RdName);
|
||||
|
||||
arguments = arguments.Replace("%N", $"\"{torrent.RdName}\"");
|
||||
arguments = arguments.Replace("%L", $"\"{torrent.Category}\"");
|
||||
arguments = arguments.Replace("%F", $"\"{torrentPath}\"");
|
||||
arguments = arguments.Replace("%R", $"\"{downloadPath}\"");
|
||||
arguments = arguments.Replace("%D", $"\"{torrentPath}\"");
|
||||
arguments = arguments.Replace("%C", downloads.Count.ToString(CultureInfo.InvariantCulture).Replace(",", "").Replace(".", ""));
|
||||
arguments = arguments.Replace("%Z", torrent.RdSize.ToString(CultureInfo.InvariantCulture).Replace(",", "").Replace(".", ""));
|
||||
arguments = arguments.Replace("%I", torrent.Hash);
|
||||
|
||||
Log($"Executing external program {fileName} with arguments {arguments}", torrent);
|
||||
|
||||
var errorSb = new StringBuilder();
|
||||
var outputSb = new StringBuilder();
|
||||
|
||||
using var process = new Process();
|
||||
|
||||
process.StartInfo.FileName = fileName;
|
||||
process.StartInfo.Arguments = arguments;
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
|
||||
process.OutputDataReceived += (_, data) =>
|
||||
{
|
||||
if (data.Data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
outputSb.AppendLine(data.Data.Trim());
|
||||
};
|
||||
process.ErrorDataReceived += (_, data) =>
|
||||
{
|
||||
if (data.Data == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
errorSb.AppendLine(data.Data.Trim());
|
||||
};
|
||||
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
var exited = process.WaitForExit(60000 * 10);
|
||||
|
||||
var errors = errorSb.ToString();
|
||||
var output = outputSb.ToString();
|
||||
|
||||
if (errors.Length > 0)
|
||||
{
|
||||
Log($"External application exited with errors: {errors}", torrent);
|
||||
}
|
||||
|
||||
if (output.Length > 0)
|
||||
{
|
||||
Log($"External application exited with output: {output}", torrent);
|
||||
}
|
||||
|
||||
if (!exited)
|
||||
{
|
||||
Log("External application after a 60 second timeout", torrent);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateTorrentClientData(Torrent torrent, TorrentClientTorrent torrentClientTorrent = null)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
Loading…
Reference in a new issue