Add watch folder monitor.
This commit is contained in:
parent
cd6002b5d6
commit
3fe9680e35
14 changed files with 253 additions and 64 deletions
|
|
@ -12,6 +12,9 @@
|
|||
<li [ngClass]="{ 'is-active': activeTab === 2 }" (click)="activeTab = 2">
|
||||
<a>Radarr/Sonarr</a>
|
||||
</li>
|
||||
<li [ngClass]="{ 'is-active': activeTab === 5 }" (click)="activeTab = 5">
|
||||
<a>Watch</a>
|
||||
</li>
|
||||
<li [ngClass]="{ 'is-active': activeTab === 3 }" (click)="activeTab = 3">
|
||||
<a>Tests</a>
|
||||
</li>
|
||||
|
|
@ -70,7 +73,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Automatically import category.</label>
|
||||
<label class="label">Automatically import category</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="100" [(ngModel)]="settingProviderAutoImportCategory" />
|
||||
</div>
|
||||
|
|
@ -87,6 +90,27 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Connection Timeout</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" min="1" max="9999" [(ngModel)]="settingProviderTimeout" />
|
||||
</div>
|
||||
<p class="help">
|
||||
The timeout to use to connect to the provider in seconds, with a minimum of 5 seconds. Increase if you experience
|
||||
timeout errors in the log. This setting is only used when there is a user connected to the web interface or if
|
||||
AutoImport/AutoDelete has been enabled.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Check Interval</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" min="10" max="9999" [(ngModel)]="settingProviderCheckInterval" />
|
||||
</div>
|
||||
<p class="help">
|
||||
This interval is used to check Real-Debrid or AllDebrid for updates. This setting is only used when there are
|
||||
active downloads. If there are no active downloads it will use this setting * 3, with a minimum of 30 seconds.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="activeTab === 0">
|
||||
|
|
@ -417,6 +441,50 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="activeTab === 5">
|
||||
<div class="field">
|
||||
<label class="label">Watch Path</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="100" [(ngModel)]="settingWatchPath" />
|
||||
</div>
|
||||
<p class="help">
|
||||
Watch this path for .torrent or .magnet files. When a file is found it will be automatically imported.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" [(ngModel)]="settingProviderAutoDelete" />
|
||||
Automatically delete downloads removed from provider.
|
||||
</label>
|
||||
<div class="help">
|
||||
When selected, cancel and delete downloads that have been removed from Real-Debrid or AllDebrid.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Connection Timeout</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" min="1" max="9999" [(ngModel)]="settingProviderTimeout" />
|
||||
</div>
|
||||
<p class="help">
|
||||
The timeout to use to connect to the provider in seconds, with a minimum of 5 seconds. Increase if you experience
|
||||
timeout errors in the log. This setting is only used when there is a user connected to the web interface or if
|
||||
AutoImport/AutoDelete has been enabled.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Check Interval</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" min="10" max="9999" [(ngModel)]="settingProviderCheckInterval" />
|
||||
</div>
|
||||
<p class="help">
|
||||
This interval is used to check Real-Debrid or AllDebrid for updates. This setting is only used when there are
|
||||
active downloads. If there are no active downloads it will use this setting * 3, with a minimum of 30 seconds.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<div class="notification is-danger is-light" *ngIf="error?.length > 0">Error saving settings: {{ error }}</div>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ export class SettingsComponent implements OnInit {
|
|||
public settingProviderAutoImport: boolean;
|
||||
public settingProviderAutoImportCategory: string;
|
||||
public settingProviderAutoDelete: boolean;
|
||||
public settingProviderTimeout: number;
|
||||
public settingProviderCheckInterval: number;
|
||||
public settingRealDebridApiKey: string;
|
||||
public settingDownloadPath: string;
|
||||
public settingMappedPath: string;
|
||||
|
|
@ -67,6 +69,8 @@ export class SettingsComponent implements OnInit {
|
|||
this.settingProviderAutoImport = this.getSetting(results, 'ProviderAutoImport') === '1';
|
||||
this.settingProviderAutoImportCategory = this.getSetting(results, 'ProviderAutoImportCategory');
|
||||
this.settingProviderAutoDelete = this.getSetting(results, 'ProviderAutoDelete') === '1';
|
||||
this.settingProviderTimeout = parseInt(this.getSetting(results, 'ProviderTimeout'), 10);
|
||||
this.settingProviderCheckInterval = parseInt(this.getSetting(results, 'ProviderCheckInterval'), 10);
|
||||
this.settingRealDebridApiKey = this.getSetting(results, 'RealDebridApiKey');
|
||||
this.settingLogLevel = this.getSetting(results, 'LogLevel');
|
||||
this.settingDownloadPath = this.getSetting(results, 'DownloadPath');
|
||||
|
|
@ -116,6 +120,14 @@ export class SettingsComponent implements OnInit {
|
|||
settingId: 'ProviderAutoDelete',
|
||||
value: this.settingProviderAutoDelete ? '1' : '0',
|
||||
},
|
||||
{
|
||||
settingId: 'ProviderTimeout',
|
||||
value: (this.settingProviderTimeout ?? 10).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'ProviderCheckInterval',
|
||||
value: (this.settingProviderCheckInterval ?? 10).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'RealDebridApiKey',
|
||||
value: this.settingRealDebridApiKey,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,18 @@ public class DataContext : IdentityDbContext
|
|||
Value = "0"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProviderTimeout",
|
||||
Type = "Int32",
|
||||
Value = "10"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProviderCheckInterval",
|
||||
Type = "Int32",
|
||||
Value = "10"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DeleteOnError",
|
||||
Type = "Int32",
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ public class SettingData
|
|||
ProviderAutoImport = GetInt32("ProviderAutoImport"),
|
||||
ProviderAutoImportCategory = GetString("ProviderAutoImportCategory"),
|
||||
ProviderAutoDelete = GetInt32("ProviderAutoDelete"),
|
||||
ProviderTimeout = GetInt32("ProviderTimeout"),
|
||||
ProviderCheckInterval = GetInt32("ProviderCheckInterval"),
|
||||
RealDebridApiKey = GetString("RealDebridApiKey"),
|
||||
DownloadPath = GetString("DownloadPath"),
|
||||
DownloadClient = GetString("DownloadClient"),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ public class DbSettings
|
|||
public Int32 ProviderAutoImport { get; set; }
|
||||
public String ProviderAutoImportCategory { get; set; }
|
||||
public Int32 ProviderAutoDelete { get; set; }
|
||||
public Int32 ProviderTimeout { get; set; }
|
||||
public Int32 ProviderCheckInterval { get; set; }
|
||||
public String RealDebridApiKey { get; set; }
|
||||
public String DownloadPath { get; set; }
|
||||
public String DownloadClient { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RdtClient.Service.Services;
|
||||
|
||||
namespace RdtClient.Service.BackgroundServices;
|
||||
|
||||
public class ProviderUpdater : BackgroundService
|
||||
{
|
||||
private readonly ILogger<TaskRunner> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
private static DateTime _nextUpdate = DateTime.UtcNow;
|
||||
|
||||
public ProviderUpdater(ILogger<TaskRunner> logger, IServiceProvider serviceProvider)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var torrentService = scope.ServiceProvider.GetRequiredService<Torrents>();
|
||||
|
||||
_logger.LogInformation("ProviderUpdater started.");
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var torrents = await torrentService.Get();
|
||||
|
||||
if (_nextUpdate < DateTime.UtcNow && ((torrents.Count > 0 && Settings.Get.ProviderAutoImport == 0) || Settings.Get.ProviderAutoImport == 1))
|
||||
{
|
||||
_logger.LogDebug($"Updating torrent info from Real-Debrid");
|
||||
|
||||
var updateTime = Settings.Get.ProviderCheckInterval * 3;
|
||||
|
||||
if (updateTime < 30)
|
||||
{
|
||||
updateTime = 30;
|
||||
}
|
||||
|
||||
if (RdtHub.HasConnections)
|
||||
{
|
||||
updateTime = Settings.Get.ProviderCheckInterval;
|
||||
|
||||
if (updateTime < 5)
|
||||
{
|
||||
updateTime = 5;
|
||||
}
|
||||
}
|
||||
|
||||
_nextUpdate = DateTime.UtcNow.AddSeconds(updateTime);
|
||||
|
||||
await torrentService.UpdateRdData();
|
||||
|
||||
_logger.LogDebug($"Finished updating torrent info from Real-Debrid, next update in {updateTime} seconds");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Unexpected error occurred in ProviderUpdater: {ex.Message}");
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
|
||||
}
|
||||
|
||||
_logger.LogInformation("ProviderUpdater stopped.");
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,11 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Service.Services;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace RdtClient.Service.Services;
|
||||
namespace RdtClient.Service.BackgroundServices;
|
||||
|
||||
public class Startup : IHostedService
|
||||
{
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RdtClient.Service.Services;
|
||||
|
||||
namespace RdtClient.Service.Services;
|
||||
namespace RdtClient.Service.BackgroundServices;
|
||||
|
||||
public class TaskRunner : BackgroundService
|
||||
{
|
||||
|
|
@ -34,7 +35,7 @@ public class TaskRunner : BackgroundService
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error occurred in TorrentDownloadManager.Tick");
|
||||
_logger.LogError(ex, $"Unexpected error occurred in TorrentDownloadManager.Tick: {ex.Message}");
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);
|
||||
|
|
@ -4,16 +4,16 @@ using Microsoft.Extensions.Hosting;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace RdtClient.Service.Services;
|
||||
namespace RdtClient.Service.BackgroundServices;
|
||||
|
||||
public class UpdateChecker : BackgroundService
|
||||
{
|
||||
public static String CurrentVersion { get; private set; }
|
||||
public static String LatestVersion { get; private set; }
|
||||
|
||||
private readonly ILogger<TaskRunner> _logger;
|
||||
private readonly ILogger<UpdateChecker> _logger;
|
||||
|
||||
public UpdateChecker(ILogger<TaskRunner> logger)
|
||||
public UpdateChecker(ILogger<UpdateChecker> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using RdtClient.Service.BackgroundServices;
|
||||
using RdtClient.Service.Services;
|
||||
using RdtClient.Service.Services.TorrentClients;
|
||||
|
||||
|
|
@ -18,6 +19,7 @@ public static class DiConfig
|
|||
services.AddScoped<Torrents>();
|
||||
services.AddScoped<TorrentRunner>();
|
||||
|
||||
services.AddHostedService<ProviderUpdater>();
|
||||
services.AddHostedService<Startup>();
|
||||
services.AddHostedService<TaskRunner>();
|
||||
services.AddHostedService<UpdateChecker>();
|
||||
|
|
|
|||
|
|
@ -24,19 +24,34 @@ public class AllDebridTorrentClient : ITorrentClient
|
|||
|
||||
private AllDebridNETClient GetClient()
|
||||
{
|
||||
var apiKey = Settings.Get.RealDebridApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
try
|
||||
{
|
||||
throw new Exception("All-Debrid API Key not set in the settings");
|
||||
var apiKey = Settings.Get.RealDebridApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new Exception("All-Debrid API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
var allDebridNetClient = new AllDebridNETClient("RealDebridClient", apiKey);
|
||||
|
||||
return allDebridNetClient;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
_logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
_logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
var allDebridNetClient = new AllDebridNETClient("RealDebridClient", apiKey);
|
||||
|
||||
return allDebridNetClient;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static TorrentClientTorrent Map(Magnet torrent)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ public class RealDebridTorrentClient : ITorrentClient
|
|||
{
|
||||
private readonly ILogger<RealDebridTorrentClient> _logger;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private TimeSpan _offset;
|
||||
private TimeSpan? _offset = null;
|
||||
|
||||
public RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
|
|
@ -21,24 +21,42 @@ public class RealDebridTorrentClient : ITorrentClient
|
|||
|
||||
private RdNetClient GetClient()
|
||||
{
|
||||
var apiKey = Settings.Get.RealDebridApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
try
|
||||
{
|
||||
throw new Exception("Real-Debrid API Key not set in the settings");
|
||||
var apiKey = Settings.Get.RealDebridApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
throw new Exception("Real-Debrid API Key not set in the settings");
|
||||
}
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.ProviderTimeout);
|
||||
|
||||
var rdtNetClient = new RdNetClient(null, httpClient, 5);
|
||||
rdtNetClient.UseApiAuthentication(apiKey);
|
||||
|
||||
// Get the server time to fix up the timezones on results
|
||||
if (_offset == null)
|
||||
{
|
||||
var serverTime = rdtNetClient.Api.GetIsoTimeAsync().Result;
|
||||
_offset = serverTime.Offset;
|
||||
}
|
||||
|
||||
return rdtNetClient;
|
||||
}
|
||||
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
|
||||
{
|
||||
_logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(10);
|
||||
throw;
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
_logger.LogError(ex, $"The connection to RealDebrid has timed out: {ex.Message}");
|
||||
|
||||
var rdtNetClient = new RdNetClient(null, httpClient, 5);
|
||||
rdtNetClient.UseApiAuthentication(apiKey);
|
||||
|
||||
// Get the server time to fix up the timezones on results
|
||||
var serverTime = rdtNetClient.Api.GetIsoTimeAsync().Result;
|
||||
_offset = serverTime.Offset;
|
||||
|
||||
return rdtNetClient;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private TorrentClientTorrent Map(Torrent torrent)
|
||||
|
|
@ -334,7 +352,12 @@ public class RealDebridTorrentClient : ITorrentClient
|
|||
|
||||
private DateTimeOffset? ChangeTimeZone(DateTimeOffset? dateTimeOffset)
|
||||
{
|
||||
return dateTimeOffset?.Subtract(_offset).ToOffset(_offset);
|
||||
if (_offset == null)
|
||||
{
|
||||
return dateTimeOffset;
|
||||
}
|
||||
|
||||
return dateTimeOffset?.Subtract(_offset.Value).ToOffset(_offset.Value);
|
||||
}
|
||||
|
||||
private void Log(String message, Data.Models.Data.Torrent torrent = null)
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ namespace RdtClient.Service.Services;
|
|||
|
||||
public class TorrentRunner
|
||||
{
|
||||
private static DateTime _nextUpdate = DateTime.UtcNow;
|
||||
|
||||
public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new();
|
||||
public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new();
|
||||
|
||||
|
|
@ -297,35 +295,6 @@ public class TorrentRunner
|
|||
|
||||
torrents = torrents.Where(m => m.Completed == null).ToList();
|
||||
|
||||
// Only poll Real-Debrid every second when a hub is connected, otherwise every 30 seconds
|
||||
if (_nextUpdate < DateTime.UtcNow && ((torrents.Count > 0 && Settings.Get.ProviderAutoImport == 0) || Settings.Get.ProviderAutoImport == 1))
|
||||
{
|
||||
Log($"Updating torrent info from Real-Debrid");
|
||||
|
||||
#if DEBUG
|
||||
var updateTime = 0;
|
||||
|
||||
_nextUpdate = DateTime.UtcNow;
|
||||
#else
|
||||
var updateTime = 30;
|
||||
|
||||
if (RdtHub.HasConnections)
|
||||
{
|
||||
updateTime = 10;
|
||||
}
|
||||
|
||||
_nextUpdate = DateTime.UtcNow.AddSeconds(updateTime);
|
||||
#endif
|
||||
|
||||
await _torrents.UpdateRdData();
|
||||
|
||||
// Re-get torrents to account for updated info
|
||||
torrents = await _torrents.Get();
|
||||
torrents = torrents.Where(m => m.Completed == null).ToList();
|
||||
|
||||
Log($"Finished updating torrent info from Real-Debrid, next update in {updateTime} seconds");
|
||||
}
|
||||
|
||||
if (torrents.Count > 0)
|
||||
{
|
||||
Log($"Processing {torrents.Count} torrents");
|
||||
|
|
@ -569,7 +538,14 @@ public class TorrentRunner
|
|||
break;
|
||||
}
|
||||
|
||||
await _torrents.RunTorrentComplete(torrent.TorrentId);
|
||||
try
|
||||
{
|
||||
await _torrents.RunTorrentComplete(torrent.TorrentId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.Message, "Unable to run post process: {Message}", ex.Message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using RdtClient.Data.Data;
|
|||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Data.Models.TorrentClient;
|
||||
using RdtClient.Service.BackgroundServices;
|
||||
using RdtClient.Service.Helpers;
|
||||
using RdtClient.Service.Services.TorrentClients;
|
||||
using Torrent = RdtClient.Data.Models.Data.Torrent;
|
||||
|
|
|
|||
Loading…
Reference in a new issue