Rewrote the setting store, retrieve and displaying on the settings page to make maintenance a lot easier.
Added settings to set defaults for the provider import, sonarr, gui and watch folders.
This commit is contained in:
parent
3fe9680e35
commit
fc15569e1b
43 changed files with 1901 additions and 1117 deletions
|
|
@ -17,17 +17,14 @@ export class AddNewTorrentComponent implements OnInit {
|
|||
public provider: string;
|
||||
|
||||
public category: string;
|
||||
public priority: number;
|
||||
|
||||
public downloadAction: number = 0;
|
||||
public finishedAction: number = 0;
|
||||
|
||||
public downloadMinSize: number = 0;
|
||||
|
||||
public downloadRetryAttempts: number = 3;
|
||||
public torrentRetryAttempts: number = 1;
|
||||
public downloadRetryAttempts: number = 3;
|
||||
public torrentDeleteOnError: number = 0;
|
||||
public torrentLifetime: number = 0;
|
||||
public priority: number;
|
||||
|
||||
public availableFiles: TorrentFileAvailability[];
|
||||
public downloadFiles: { [key: string]: boolean } = {};
|
||||
|
|
@ -44,7 +41,20 @@ export class AddNewTorrentComponent implements OnInit {
|
|||
private settingsService: SettingsService
|
||||
) {
|
||||
this.settingsService.get().subscribe((settings) => {
|
||||
this.provider = settings.firstOrDefault((m) => m.settingId === 'Provider')?.value;
|
||||
const providerSetting = settings.first((m) => m.key === 'Provider:Provider');
|
||||
this.provider = providerSetting.enumValues[providerSetting.value as number];
|
||||
|
||||
this.category = settings.first((m) => m.key === 'Gui:Default:Category')?.value as string;
|
||||
this.downloadAction =
|
||||
settings.first((m) => m.key === 'Gui:Default:OnlyDownloadAvailableFiles')?.value === true ? 1 : 0;
|
||||
this.finishedAction = settings.first((m) => m.key === 'Gui:Default:FinishedAction')?.value as number;
|
||||
this.downloadMinSize = settings.first((m) => m.key === 'Gui:Default:MinFileSize')?.value as number;
|
||||
this.torrentRetryAttempts = settings.first((m) => m.key === 'Gui:Default:TorrentRetryAttempts')?.value as number;
|
||||
this.downloadRetryAttempts = settings.first((m) => m.key === 'Gui:Default:DownloadRetryAttempts')
|
||||
?.value as number;
|
||||
this.torrentDeleteOnError = settings.first((m) => m.key === 'Gui:Default:DeleteOnError')?.value as number;
|
||||
this.torrentLifetime = settings.first((m) => m.key === 'Gui:Default:TorrentLifetime')?.value as number;
|
||||
this.priority = settings.first((m) => m.key === 'Gui:Default:Priority')?.value as number;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,17 +10,18 @@ import { AddNewTorrentComponent } from './add-new-torrent/add-new-torrent.compon
|
|||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AuthInterceptor } from './auth.interceptor';
|
||||
import { DecodeURIPipe } from './decode-uri.pipe';
|
||||
import { DownloadStatusPipe } from './download-status.pipe';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { MainLayoutComponent } from './main-layout/main-layout.component';
|
||||
import { NavbarComponent } from './navbar/navbar.component';
|
||||
import { Nl2BrPipe } from './nl2br.pipe';
|
||||
import { ProfileComponent } from './profile/profile.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import { SetupComponent } from './setup/setup.component';
|
||||
import { TorrentStatusPipe } from './torrent-status.pipe';
|
||||
import { TorrentTableComponent } from './torrent-table/torrent-table.component';
|
||||
import { TorrentComponent } from './torrent/torrent.component';
|
||||
import { DecodeURIPipe } from './decode-uri.pipe';
|
||||
import { ProfileComponent } from './profile/profile.component';
|
||||
|
||||
curray();
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ curray();
|
|||
TorrentComponent,
|
||||
DecodeURIPipe,
|
||||
ProfileComponent,
|
||||
Nl2BrPipe,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ export class AuthService {
|
|||
});
|
||||
}
|
||||
|
||||
public setupProvider(provider: string, token: string): Observable<void> {
|
||||
return this.http.post<void>(`/Api/Authentication/SetupProvider`, {
|
||||
provider,
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
public login(userName: string, password: string): Observable<void> {
|
||||
return this.http.post<void>(`/Api/Authentication/Login`, {
|
||||
userName,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
export class Setting {
|
||||
public settingId: string;
|
||||
public value: string;
|
||||
key: string;
|
||||
value: boolean | number | null | string;
|
||||
displayName: null | string;
|
||||
description: null | string;
|
||||
type: string;
|
||||
settings: Setting[];
|
||||
enumValues: { [key: string]: string };
|
||||
}
|
||||
|
|
|
|||
27
client/src/app/nl2br.pipe.ts
Normal file
27
client/src/app/nl2br.pipe.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { Pipe, PipeTransform, SecurityContext, VERSION } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Pipe({
|
||||
name: 'nl2br',
|
||||
})
|
||||
export class Nl2BrPipe implements PipeTransform {
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
|
||||
transform(value: string, sanitizeBeforehand?: boolean): string {
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
let result: any;
|
||||
const textParsed = value.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
||||
|
||||
if (!VERSION || VERSION.major === '2') {
|
||||
result = this.sanitizer.bypassSecurityTrustHtml(textParsed);
|
||||
} else if (sanitizeBeforehand) {
|
||||
result = this.sanitizer.sanitize(SecurityContext.HTML, textParsed);
|
||||
} else {
|
||||
result = textParsed;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ export class SettingsService {
|
|||
}
|
||||
|
||||
public update(settings: Setting[]): Observable<void> {
|
||||
return this.http.put<void>(`/Api/Settings`, { settings });
|
||||
return this.http.put<void>(`/Api/Settings`, settings);
|
||||
}
|
||||
|
||||
public getProfile(): Observable<Profile> {
|
||||
|
|
|
|||
|
|
@ -1,373 +1,67 @@
|
|||
<div class="tabs">
|
||||
<ul>
|
||||
<li [ngClass]="{ 'is-active': activeTab === 0 }" (click)="activeTab = 0">
|
||||
<a>General</a>
|
||||
<li [ngClass]="{ 'is-active': activeTab === i }" (click)="activeTab = i" *ngFor="let tab of tabs; let i = index">
|
||||
<a>{{ tab.displayName }}</a>
|
||||
</li>
|
||||
<li [ngClass]="{ 'is-active': activeTab === 4 }" (click)="activeTab = 4">
|
||||
<a>Provider</a>
|
||||
</li>
|
||||
<li [ngClass]="{ 'is-active': activeTab === 1 }" (click)="activeTab = 1">
|
||||
<a>Download Client</a>
|
||||
</li>
|
||||
<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 [ngClass]="{ 'is-active': activeTab === 99 }" (click)="activeTab = 99">
|
||||
<a>Speed Tests</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div *ngIf="activeTab === 4">
|
||||
<div class="field">
|
||||
<label class="label">Provider</label>
|
||||
<div class="control select is-fullwidth">
|
||||
<select [(ngModel)]="settingProvider">
|
||||
<option value="RealDebrid">Real-Debrid</option>
|
||||
<option value="AllDebrid">AllDebrid</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help">
|
||||
The following 2 providers are supported:
|
||||
<br />
|
||||
<a href="https://real-debrid.com/?id=1348683" target="_blank" rel="noopener">https://real-debrid.com</a>
|
||||
<br />
|
||||
<a href="https://alldebrid.com/?uid=2v91l&lang=en" target="_blank" rel="noopener">https://alldebrid.com</a>
|
||||
<br />
|
||||
At this point only 1 provider can be used at the time.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field" *ngIf="settingProvider === 'RealDebrid'">
|
||||
<label class="label">Real-Debrid API Key</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="100" [(ngModel)]="settingRealDebridApiKey" />
|
||||
</div>
|
||||
<p class="help">
|
||||
You can find your API key here:
|
||||
<a href="https://real-debrid.com/apitoken" target="_blank" rel="noopener">https://real-debrid.com/apitoken</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field" *ngIf="settingProvider === 'AllDebrid'">
|
||||
<label class="label">AllDebrid API Key</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="100" [(ngModel)]="settingRealDebridApiKey" />
|
||||
</div>
|
||||
<p class="help">
|
||||
You can find your API key here:
|
||||
<a href="https://alldebrid.com/apikeys/" target="_blank" rel="noopener">https://alldebrid.com/apikeys/</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" [(ngModel)]="settingProviderAutoImport" />
|
||||
Automatically import and process torrents added to provider.
|
||||
</label>
|
||||
<div class="help">
|
||||
When selected, import downloads that are not added through RealDebridClient but have been directly added to
|
||||
Real-Debrid or AllDebrid.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Automatically import category</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="100" [(ngModel)]="settingProviderAutoImportCategory" />
|
||||
</div>
|
||||
<p class="help">When a torrent is imported assign it this category.</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 *ngIf="activeTab === 0">
|
||||
<div class="field">
|
||||
<label class="label">Log level</label>
|
||||
<div class="control select is-fullwidth">
|
||||
<select [(ngModel)]="settingLogLevel">
|
||||
<option value="Verbose">Verbose</option>
|
||||
<option value="Debug">Debug</option>
|
||||
<option value="Information">Information</option>
|
||||
<option value="Warning">Warning</option>
|
||||
<option value="Error">Error</option>
|
||||
<option value="Fatal">Fatal</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help">Recommended level is Warning, set to Debug to get the most info.</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Download path</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingDownloadPath" />
|
||||
</div>
|
||||
<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 unpack processes</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="100" min="1" step="1" [(ngModel)]="settingUnpackLimit" />
|
||||
</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">
|
||||
<div class="field">
|
||||
<label class="label">Download client</label>
|
||||
<div class="control select is-fullwidth">
|
||||
<select [(ngModel)]="settingDownloadClient">
|
||||
<option value="Simple">Simple Downloader</option>
|
||||
<option value="MultiPart">Multi Part Downloader</option>
|
||||
<option value="Aria2c">Aria2c</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help">
|
||||
Select which download client to use, see the
|
||||
<a href="https://github.com/rogerfar/rdt-client/" target="_blank">README</a> for the various options.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field" *ngIf="settingDownloadClient === 'MultiPart'">
|
||||
<label class="label">Temp Download path</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingTempPath" />
|
||||
</div>
|
||||
<p class="help">
|
||||
Path in the docker container to temporarily download to (i.e. /data/temp). Make sure the docker container has
|
||||
enough disk space if using a path inside the container.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Maximum parallel downloads</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="100" min="1" step="1" [(ngModel)]="settingDownloadLimit" />
|
||||
</div>
|
||||
<p class="help">Maximum amount of torrents that get downloaded to your host at the same time.</p>
|
||||
</div>
|
||||
|
||||
<div class="field" *ngIf="settingDownloadClient === 'MultiPart'">
|
||||
<label class="label">Parallel connections per download</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="100" min="0" step="1" [(ngModel)]="settingDownloadChunkCount" />
|
||||
</div>
|
||||
<p class="help">
|
||||
Maximum amount of parallel threads that are used to download a single torrent to your host. If set to 1 no
|
||||
parallel downloading will be done.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field" *ngIf="settingDownloadClient === 'MultiPart' || settingDownloadClient === 'Simple'">
|
||||
<label class="label">Download speed (in MB/s)</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="1000" min="0" step="1" [(ngModel)]="settingDownloadMaxSpeed" />
|
||||
</div>
|
||||
<p class="help">Maximum download speed in Megabytes per second. When set to 0 unlimited speed is used.</p>
|
||||
</div>
|
||||
|
||||
<div class="field" *ngIf="settingDownloadClient === 'MultiPart'">
|
||||
<label class="label">Proxy Server</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingProxyServer" />
|
||||
</div>
|
||||
<p class="help">Address of a proxy server.</p>
|
||||
</div>
|
||||
|
||||
<div class="field" *ngIf="settingDownloadClient === 'Aria2c'">
|
||||
<label class="label">Aria2c URL</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingAria2cUrl" />
|
||||
</div>
|
||||
<p class="help">
|
||||
This is the URL to your Aria2c instance. It must end in /jsonrpc. A common URL is
|
||||
http://192.168.10.2:6800/jsonrpc.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field" *ngIf="settingDownloadClient === 'Aria2c'">
|
||||
<label class="label">Aria2c Secret</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingAria2cSecret" />
|
||||
</div>
|
||||
<p class="help">The secret of your Aria2c instance. Optional.</p>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="button is-warning"
|
||||
(click)="testAria2cConnection()"
|
||||
[disabled]="saving"
|
||||
[ngClass]="{ 'is-loading': saving }"
|
||||
*ngIf="settingDownloadClient === 'Aria2c'"
|
||||
>
|
||||
Test aria2 connection
|
||||
</button>
|
||||
|
||||
<div class="notification is-danger is-light" *ngIf="testAria2cConnectionError">
|
||||
Could connect to Aria2 client<br />
|
||||
{{ testAria2cConnectionError }}
|
||||
</div>
|
||||
|
||||
<div class="notification is-success is-light" *ngIf="testAria2cConnectionSuccess">
|
||||
Found Aria2 client version {{ testAria2cConnectionSuccess }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="activeTab === 2">
|
||||
<h3 class="subtitle">
|
||||
The following settings only apply when a torrent gets added through the qbittorrent API, usually Radarr or Sonarr.
|
||||
</h3>
|
||||
<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="1000" min="0" step="1" [(ngModel)]="settingMinFileSize" />
|
||||
<div *ngFor="let tab of tabs; let i = index" [hidden]="activeTab !== i">
|
||||
<h3>{{ tab.description }}</h3>
|
||||
<ng-container *ngFor="let setting of tab.settings">
|
||||
<h3 *ngIf="setting.type === 'Object'" class="title is-3" style="margin-top: 1.2rem">{{ setting.displayName }}</h3>
|
||||
<div class="field">
|
||||
<label class="label" *ngIf="setting.type !== 'Boolean' && setting.type !== 'Object'">{{
|
||||
setting.displayName
|
||||
}}</label>
|
||||
<ng-container [ngSwitch]="setting.type">
|
||||
<div class="control" *ngSwitchCase="'String'">
|
||||
<input class="input" type="text" maxlength="100" [(ngModel)]="setting.value" />
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-static">MB</a>
|
||||
<div class="control" *ngSwitchCase="'Int32'">
|
||||
<input class="input" type="number" [(ngModel)]="setting.value" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="help">
|
||||
Files that are smaller than this setting are skipped and not downloaded. When set to 0 all files are downloaded.
|
||||
When downloading from Radarr or Sonarr it's recommended to keep this setting at atleast a few MB to avoid
|
||||
Real-Debrid having to re-download the torrent.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="checkbox" *ngSwitchCase="'Boolean'">
|
||||
<input type="checkbox" [(ngModel)]="setting.value" />
|
||||
{{ setting.displayName }}
|
||||
</label>
|
||||
<div class="control select is-fullwidth" *ngSwitchCase="'Enum'">
|
||||
<select [(ngModel)]="setting.value">
|
||||
<option [value]="kvp.key" *ngFor="let kvp of setting.enumValues | keyvalue">{{ kvp.value }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<ng-container *ngSwitchCase="'Object'"></ng-container>
|
||||
<div class="control" *ngSwitchDefault>Invalid setting type {{ setting.type }}</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" [(ngModel)]="settingOnlyDownloadAvailableFiles" />
|
||||
Only download available files on Real-Debrid
|
||||
</label>
|
||||
<div class="help">
|
||||
When selected, it will only download files in the torrent that have been download by Real-Debrid. You can use
|
||||
this in combination with the Min File size setting above.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help" [innerHtml]="setting.description | nl2br"></p>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Automatic retry downloads</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="1000" min="0" step="1" [(ngModel)]="settingDownloadRetryAttempts" />
|
||||
</div>
|
||||
<p class="help">When a single download fails it will retry it this many times before marking it as failed.</p>
|
||||
</div>
|
||||
<ng-container *ngIf="setting.key === 'DownloadClient:Aria2cSecret'">
|
||||
<button
|
||||
class="button is-warning"
|
||||
(click)="testAria2cConnection()"
|
||||
[disabled]="saving"
|
||||
[ngClass]="{ 'is-loading': saving }"
|
||||
>
|
||||
Test aria2 connection
|
||||
</button>
|
||||
<div class="notification is-danger is-light" style="margin-top: 1rem" *ngIf="testAria2cConnectionError">
|
||||
Could connect to Aria2 client<br />
|
||||
{{ testAria2cConnectionError }}
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Automatic retry torrent</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="1000" min="0" step="1" [(ngModel)]="settingTorrentRetryAttempts" />
|
||||
<div class="notification is-success is-light" style="margin-top: 1rem" *ngIf="testAria2cConnectionSuccess">
|
||||
Found Aria2 client version {{ testAria2cConnectionSuccess }}
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<p class="help">
|
||||
When a single download has failed multiple times (see setting above) or when the torrent itself received an error
|
||||
it will retry the full torrent this many times before marking it failed.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Delete download when in error</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="100000" min="0" step="1" [(ngModel)]="settingDeleteOnError" />
|
||||
</div>
|
||||
<p class="help">
|
||||
When a download has been in error for this many minutes, delete it from the provider and the client. 0 to disable.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Torrent maximum lifetime</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="100000" min="0" step="1" [(ngModel)]="settingTorrentLifetime" />
|
||||
</div>
|
||||
<p class="help">
|
||||
The maximum lifetime of a torrent in minutes. When this time has passed, mark the torrent as error. If the torrent
|
||||
is completed and has downloads, the lifetime setting will not apply. 0 to disable.
|
||||
</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<div *ngIf="activeTab === 3">
|
||||
<div *ngIf="activeTab === 99">
|
||||
<div class="field">
|
||||
<label class="label">Test download path permissions</label>
|
||||
<div class="control">
|
||||
|
|
@ -441,57 +135,13 @@
|
|||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" *ngIf="activeTab != 3">
|
||||
<div class="field" *ngIf="activeTab < 99">
|
||||
<div class="control">
|
||||
<button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
|
||||
Save Settings
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Setting } from 'src/app/models/setting.model';
|
||||
import { SettingsService } from 'src/app/settings.service';
|
||||
import { Setting } from '../models/setting.model';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
|
|
@ -10,6 +10,8 @@ import { SettingsService } from 'src/app/settings.service';
|
|||
export class SettingsComponent implements OnInit {
|
||||
public activeTab = 0;
|
||||
|
||||
public tabs: Setting[] = [];
|
||||
|
||||
public saving = false;
|
||||
public error: string;
|
||||
|
||||
|
|
@ -25,34 +27,6 @@ export class SettingsComponent implements OnInit {
|
|||
public testAria2cConnectionError: string = null;
|
||||
public testAria2cConnectionSuccess: string = null;
|
||||
|
||||
public settingLogLevel: string;
|
||||
public settingProvider: string;
|
||||
public settingProviderAutoImport: boolean;
|
||||
public settingProviderAutoImportCategory: string;
|
||||
public settingProviderAutoDelete: boolean;
|
||||
public settingProviderTimeout: number;
|
||||
public settingProviderCheckInterval: number;
|
||||
public settingRealDebridApiKey: string;
|
||||
public settingDownloadPath: string;
|
||||
public settingMappedPath: string;
|
||||
public settingTempPath: string;
|
||||
public settingDownloadClient: string;
|
||||
public settingDownloadLimit: number;
|
||||
public settingDownloadChunkCount: number;
|
||||
public settingDownloadMaxSpeed: number;
|
||||
public settingUnpackLimit: number;
|
||||
public settingMinFileSize: number;
|
||||
public settingOnlyDownloadAvailableFiles: boolean;
|
||||
public settingProxyServer: string;
|
||||
public settingAria2cUrl: string;
|
||||
public settingAria2cSecret: string;
|
||||
public settingDownloadRetryAttempts: number;
|
||||
public settingTorrentRetryAttempts: number;
|
||||
public settingDeleteOnError: number;
|
||||
public settingTorrentLifetime: number;
|
||||
public settingRunOnTorrentCompleteFileName: string;
|
||||
public settingRunOnTorrentCompleteArguments: string;
|
||||
|
||||
constructor(private settingsService: SettingsService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
|
@ -60,161 +34,21 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
|
||||
public reset(): void {
|
||||
this.saving = false;
|
||||
this.error = null;
|
||||
this.settingsService.get().subscribe((settings) => {
|
||||
this.tabs = settings.where((m) => m.key.indexOf(':') === -1);
|
||||
|
||||
this.settingsService.get().subscribe(
|
||||
(results) => {
|
||||
this.settingProvider = this.getSetting(results, 'Provider');
|
||||
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');
|
||||
this.settingMappedPath = this.getSetting(results, 'MappedPath');
|
||||
this.settingTempPath = this.getSetting(results, 'TempPath');
|
||||
this.settingDownloadClient = this.getSetting(results, 'DownloadClient');
|
||||
this.settingDownloadLimit = parseInt(this.getSetting(results, 'DownloadLimit'), 10);
|
||||
this.settingDownloadChunkCount = parseInt(this.getSetting(results, 'DownloadChunkCount'), 10);
|
||||
this.settingDownloadMaxSpeed = parseInt(this.getSetting(results, 'DownloadMaxSpeed'), 10);
|
||||
this.settingUnpackLimit = parseInt(this.getSetting(results, 'UnpackLimit'), 10);
|
||||
this.settingMinFileSize = parseInt(this.getSetting(results, 'MinFileSize'), 10);
|
||||
this.settingOnlyDownloadAvailableFiles = this.getSetting(results, 'OnlyDownloadAvailableFiles') === '1';
|
||||
this.settingProxyServer = this.getSetting(results, 'ProxyServer');
|
||||
this.settingAria2cUrl = this.getSetting(results, 'Aria2cUrl');
|
||||
this.settingAria2cSecret = this.getSetting(results, 'Aria2cSecret');
|
||||
this.settingDownloadRetryAttempts = parseInt(this.getSetting(results, 'DownloadRetryAttempts'), 10);
|
||||
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;
|
||||
this.saving = true;
|
||||
for (let tab of this.tabs) {
|
||||
tab.settings = settings.where((m) => m.key.indexOf(`${tab.key}:`) > -1);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public ok(): void {
|
||||
this.saving = true;
|
||||
|
||||
const settings: Setting[] = [
|
||||
{
|
||||
settingId: 'Provider',
|
||||
value: this.settingProvider,
|
||||
},
|
||||
{
|
||||
settingId: 'ProviderAutoImport',
|
||||
value: this.settingProviderAutoImport ? '1' : '0',
|
||||
},
|
||||
{
|
||||
settingId: 'ProviderAutoImportCategory',
|
||||
value: this.settingProviderAutoImportCategory,
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
settingId: 'LogLevel',
|
||||
value: this.settingLogLevel,
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadPath',
|
||||
value: this.settingDownloadPath,
|
||||
},
|
||||
{
|
||||
settingId: 'MappedPath',
|
||||
value: this.settingMappedPath,
|
||||
},
|
||||
{
|
||||
settingId: 'TempPath',
|
||||
value: this.settingTempPath,
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadClient',
|
||||
value: this.settingDownloadClient,
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadLimit',
|
||||
value: (this.settingDownloadLimit ?? 10).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadChunkCount',
|
||||
value: (this.settingDownloadChunkCount ?? 8).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadMaxSpeed',
|
||||
value: (this.settingDownloadMaxSpeed ?? 0).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'UnpackLimit',
|
||||
value: (this.settingUnpackLimit ?? 1).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'MinFileSize',
|
||||
value: (this.settingMinFileSize ?? 0).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'OnlyDownloadAvailableFiles',
|
||||
value: (this.settingOnlyDownloadAvailableFiles ? '1' : '0').toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'ProxyServer',
|
||||
value: this.settingProxyServer,
|
||||
},
|
||||
{
|
||||
settingId: 'Aria2cUrl',
|
||||
value: this.settingAria2cUrl,
|
||||
},
|
||||
{
|
||||
settingId: 'Aria2cSecret',
|
||||
value: this.settingAria2cSecret,
|
||||
},
|
||||
{
|
||||
settingId: 'DownloadRetryAttempts',
|
||||
value: (this.settingDownloadRetryAttempts ?? 0).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'TorrentRetryAttempts',
|
||||
value: (this.settingTorrentRetryAttempts ?? 0).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'DeleteOnError',
|
||||
value: (this.settingDeleteOnError ?? 0).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'TorrentLifetime',
|
||||
value: (this.settingTorrentLifetime ?? 0).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'RunOnTorrentCompleteFileName',
|
||||
value: this.settingRunOnTorrentCompleteFileName,
|
||||
},
|
||||
{
|
||||
settingId: 'RunOnTorrentCompleteArguments',
|
||||
value: this.settingRunOnTorrentCompleteArguments,
|
||||
},
|
||||
];
|
||||
const settingsToSave = this.tabs.selectMany((m) => m.settings).where((m) => m.type !== 'Object');
|
||||
|
||||
this.settingsService.update(settings).subscribe(
|
||||
this.settingsService.update(settingsToSave).subscribe(
|
||||
() => {
|
||||
setTimeout(() => {
|
||||
this.saving = false;
|
||||
|
|
@ -228,11 +62,15 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
|
||||
public testDownloadPath(): void {
|
||||
const settingDownloadPath = this.tabs
|
||||
.first((m) => m.key === 'DownloadClient')
|
||||
.settings.first((m) => m.key === 'DownloadClient:DownloadPath').value as string;
|
||||
|
||||
this.saving = true;
|
||||
this.testPathError = null;
|
||||
this.testPathSuccess = false;
|
||||
|
||||
this.settingsService.testPath(this.settingDownloadPath).subscribe(
|
||||
this.settingsService.testPath(settingDownloadPath).subscribe(
|
||||
() => {
|
||||
this.saving = false;
|
||||
this.testPathSuccess = true;
|
||||
|
|
@ -278,11 +116,18 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
|
||||
public testAria2cConnection(): void {
|
||||
const settingAria2cUrl = this.tabs
|
||||
.first((m) => m.key === 'DownloadClient')
|
||||
.settings.first((m) => m.key === 'DownloadClient:Aria2cUrl').value as string;
|
||||
const settingAria2cSecret = this.tabs
|
||||
.first((m) => m.key === 'DownloadClient')
|
||||
.settings.first((m) => m.key === 'DownloadClient:Aria2cSecret').value as string;
|
||||
|
||||
this.saving = true;
|
||||
this.testAria2cConnectionError = null;
|
||||
this.testAria2cConnectionSuccess = null;
|
||||
|
||||
this.settingsService.testAria2cConnection(this.settingAria2cUrl, this.settingAria2cSecret).subscribe(
|
||||
this.settingsService.testAria2cConnection(settingAria2cUrl, settingAria2cSecret).subscribe(
|
||||
(result) => {
|
||||
this.saving = false;
|
||||
this.testAria2cConnectionSuccess = result.version;
|
||||
|
|
@ -293,14 +138,4 @@ export class SettingsComponent implements OnInit {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
private getSetting(settings: Setting[], key: string): string {
|
||||
const setting = settings.filter((m) => m.settingId === key);
|
||||
|
||||
if (setting.length !== 1) {
|
||||
throw new Error(`Unable to find setting with key ${key}`);
|
||||
}
|
||||
|
||||
return setting[0].value;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,23 +43,23 @@
|
|||
To be able to use the Real-Debrid Client you need a premium subscription to download torrents.
|
||||
<br /><br />
|
||||
Not premium yet? You have the choice of 2 providers:
|
||||
<br/>
|
||||
<br />
|
||||
<a href="https://real-debrid.com/?id=1348683" target="_blank" rel="noopener"
|
||||
>Use this link to sign up to Real-Debrid.</a
|
||||
>
|
||||
<br/>
|
||||
<br />
|
||||
<a href="https://alldebrid.com/?uid=2v91l&lang=en" target="_blank" rel="noopener"
|
||||
>Use this link to sign up to AllDebrid.</a
|
||||
>
|
||||
<br/>
|
||||
<br />
|
||||
<small>(Referal links)</small>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Provider</label>
|
||||
<div class="control select is-fullwidth">
|
||||
<select [(ngModel)]="provider">
|
||||
<option value="RealDebrid">Real-Debrid</option>
|
||||
<option value="AllDebrid">AllDebrid</option>
|
||||
<option [value]="0">Real-Debrid</option>
|
||||
<option [value]="1">AllDebrid</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AuthService } from '../auth.service';
|
||||
import { Setting } from '../models/setting.model';
|
||||
import { SettingsService } from '../settings.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-setup',
|
||||
|
|
@ -20,7 +18,7 @@ export class SetupComponent implements OnInit {
|
|||
|
||||
public step: number = 1;
|
||||
|
||||
constructor(private authService: AuthService, private settingsService: SettingsService, private router: Router) {}
|
||||
constructor(private authService: AuthService, private router: Router) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
|
|
@ -41,20 +39,12 @@ export class SetupComponent implements OnInit {
|
|||
}
|
||||
|
||||
public setToken(): void {
|
||||
const settingToken = new Setting();
|
||||
settingToken.settingId = 'RealDebridApiKey';
|
||||
settingToken.value = this.token;
|
||||
|
||||
const settingProvider = new Setting();
|
||||
settingProvider.settingId = 'Provider';
|
||||
settingProvider.value = this.provider;
|
||||
|
||||
this.settingsService.update([settingToken, settingProvider]).subscribe(
|
||||
this.authService.setupProvider(this.provider, this.token).subscribe(
|
||||
() => {
|
||||
this.step = 3;
|
||||
this.working = false;
|
||||
},
|
||||
(err) => {
|
||||
(err: any) => {
|
||||
this.working = false;
|
||||
this.error = err.error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,6 @@ public class DataContext : IdentityDbContext
|
|||
public DbSet<Setting> Settings { get; set; }
|
||||
public DbSet<Torrent> Torrents { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
base.OnModelCreating(builder);
|
||||
|
|
@ -32,203 +27,4 @@ public class DataContext : IdentityDbContext
|
|||
fk.DeleteBehavior = DeleteBehavior.Restrict;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Seed()
|
||||
{
|
||||
var seedSettings = new List<Setting>
|
||||
{
|
||||
new Setting
|
||||
{
|
||||
SettingId = "Provider",
|
||||
Type = "String",
|
||||
Value = "RealDebrid"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProviderAutoImport",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProviderAutoImportCategory",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProviderAutoDelete",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProviderTimeout",
|
||||
Type = "Int32",
|
||||
Value = "10"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProviderCheckInterval",
|
||||
Type = "Int32",
|
||||
Value = "10"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DeleteOnError",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "TorrentLifetime",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "RealDebridApiKey",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadPath",
|
||||
Type = "String",
|
||||
#if DEBUG
|
||||
Value = @"C:\Temp\rdtclient"
|
||||
#else
|
||||
Value = "/data/downloads"
|
||||
#endif
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadClient",
|
||||
Type = "String",
|
||||
Value = @"Simple"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "TempPath",
|
||||
Type = "String",
|
||||
#if DEBUG
|
||||
Value = @"C:\Temp\rdtclient"
|
||||
#else
|
||||
Value = "/data/downloads"
|
||||
#endif
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "MappedPath",
|
||||
Type = "String",
|
||||
#if DEBUG
|
||||
Value = @"C:\Temp\rdtclient"
|
||||
#else
|
||||
Value = @"C:\Downloads"
|
||||
#endif
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadLimit",
|
||||
Type = "Int32",
|
||||
Value = "2"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "UnpackLimit",
|
||||
Type = "Int32",
|
||||
Value = "1"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "MinFileSize",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "OnlyDownloadAvailableFiles",
|
||||
Type = "Int32",
|
||||
Value = "1"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadChunkCount",
|
||||
Type = "Int32",
|
||||
Value = "8"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadMaxSpeed",
|
||||
Type = "Int32",
|
||||
Value = "0"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "ProxyServer",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "LogLevel",
|
||||
Type = "String",
|
||||
Value = "Warning"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "Categories",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "Aria2cUrl",
|
||||
Type = "String",
|
||||
Value = "http://127.0.0.1:6800/jsonrpc"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "Aria2cSecret",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "DownloadRetryAttempts",
|
||||
Type = "Int32",
|
||||
Value = "3"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "TorrentRetryAttempts",
|
||||
Type = "Int32",
|
||||
Value = "1"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "RunOnTorrentCompleteFileName",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "RunOnTorrentCompleteArguments",
|
||||
Type = "String",
|
||||
Value = ""
|
||||
}
|
||||
};
|
||||
|
||||
var dbSettings = await Settings.ToListAsync();
|
||||
foreach (var seedSetting in seedSettings)
|
||||
{
|
||||
var dbSetting = dbSettings.FirstOrDefault(m => m.SettingId == seedSetting.SettingId);
|
||||
|
||||
if (dbSetting == null)
|
||||
{
|
||||
await Settings.AddAsync(seedSetting);
|
||||
await SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using Serilog;
|
||||
|
|
@ -7,128 +9,200 @@ namespace RdtClient.Data.Data;
|
|||
|
||||
public class SettingData
|
||||
{
|
||||
private static readonly SemaphoreSlim _settingCacheLock = new(1);
|
||||
|
||||
private readonly DataContext _dataContext;
|
||||
|
||||
public static DbSettings Get { get; } = new DbSettings();
|
||||
|
||||
public SettingData(DataContext dataContext)
|
||||
{
|
||||
_dataContext = dataContext;
|
||||
}
|
||||
|
||||
public static DbSettings Get { get; private set; }
|
||||
public IList<SettingProperty> GetAll()
|
||||
{
|
||||
return GetSettings(Get, null).ToList();
|
||||
}
|
||||
|
||||
public async Task Update(IList<SettingProperty> settings)
|
||||
{
|
||||
var dbSettings = await _dataContext.Settings.ToListAsync();
|
||||
|
||||
foreach (var dbSetting in dbSettings)
|
||||
{
|
||||
var setting = settings.FirstOrDefault(m => m.Key == dbSetting.SettingId);
|
||||
|
||||
if (setting != null)
|
||||
{
|
||||
dbSetting.Value = setting.Value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
await ResetCache();
|
||||
}
|
||||
|
||||
public async Task Update(String settingId, Object value)
|
||||
{
|
||||
var dbSetting = await _dataContext.Settings.FirstOrDefaultAsync(m => m.SettingId == settingId);
|
||||
|
||||
if (dbSetting == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dbSetting.Value = value.ToString();
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
await ResetCache();
|
||||
}
|
||||
|
||||
public async Task ResetCache()
|
||||
{
|
||||
var allSettings = await _dataContext.Settings.AsNoTracking().ToListAsync();
|
||||
var settings = await _dataContext.Settings.AsNoTracking().ToListAsync();
|
||||
|
||||
String GetString(String name)
|
||||
if (settings.Count == 0)
|
||||
{
|
||||
return allSettings.FirstOrDefault(m => m.SettingId == name)?.Value;
|
||||
throw new Exception("No settings found, please restart");
|
||||
}
|
||||
|
||||
Int32 GetInt32(String name)
|
||||
{
|
||||
var strVal = GetString(name);
|
||||
SetSettings(settings, Get, null);
|
||||
}
|
||||
|
||||
if (!Int32.TryParse(strVal, out var intVal))
|
||||
public async Task Seed()
|
||||
{
|
||||
var dbSettings = await _dataContext.Settings.AsNoTracking().ToListAsync();
|
||||
|
||||
var expectedSettings = GetSettings(Get, null).Where(m => m.Type != "Object").Select(m => new Setting
|
||||
{
|
||||
SettingId = m.Key,
|
||||
Value = m.Value?.ToString()
|
||||
}).ToList();
|
||||
|
||||
var newSettings = expectedSettings.Where(m => dbSettings.All(p => p.SettingId != m.SettingId)).ToList();
|
||||
|
||||
if (newSettings.Any())
|
||||
{
|
||||
await _dataContext.Settings.AddRangeAsync(newSettings);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var oldSettings = dbSettings.Where(m => expectedSettings.All(p => p.SettingId != m.SettingId)).ToList();
|
||||
|
||||
if (oldSettings.Any())
|
||||
{
|
||||
_dataContext.Settings.RemoveRange(oldSettings);
|
||||
await _dataContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<SettingProperty> GetSettings(Object defaultSetting, String parent)
|
||||
{
|
||||
var result = new List<SettingProperty>();
|
||||
|
||||
var properties = defaultSetting.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var displayName = (DisplayNameAttribute) Attribute.GetCustomAttribute(property, typeof(DisplayNameAttribute));
|
||||
var description = (DescriptionAttribute) Attribute.GetCustomAttribute(property, typeof(DescriptionAttribute));
|
||||
var propertyName = property.Name;
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
Log.Error("Unable to parse setting {name} to Int32", name);
|
||||
return 0;
|
||||
propertyName = $"{parent}:{propertyName}";
|
||||
}
|
||||
|
||||
return intVal;
|
||||
var settingProperty = new SettingProperty
|
||||
{
|
||||
Key = propertyName,
|
||||
DisplayName = displayName?.DisplayName,
|
||||
Description = description?.Description,
|
||||
Type = property.PropertyType.Name
|
||||
};
|
||||
|
||||
if (property.PropertyType.IsEnum ||
|
||||
property.PropertyType.IsValueType ||
|
||||
property.PropertyType == typeof(String))
|
||||
{
|
||||
settingProperty.Value = property.GetValue(defaultSetting);
|
||||
|
||||
if (property.PropertyType.IsEnum)
|
||||
{
|
||||
settingProperty.Type = "Enum";
|
||||
settingProperty.EnumValues = new Dictionary<Int32, String>();
|
||||
|
||||
foreach (var e in Enum.GetValues(property.PropertyType).Cast<Enum>())
|
||||
{
|
||||
var enumMember = property.PropertyType.GetMember(e.ToString()).First();
|
||||
var enumDescriptionAttribute = enumMember.GetCustomAttribute<DescriptionAttribute>();
|
||||
var enumName = enumDescriptionAttribute?.Description ?? Enum.GetName(property.PropertyType, e);
|
||||
settingProperty.EnumValues.Add((Int32)(Object)e, enumName);
|
||||
}
|
||||
}
|
||||
|
||||
result.Add(settingProperty);
|
||||
}
|
||||
else
|
||||
{
|
||||
settingProperty.Type = "Object";
|
||||
result.Add(settingProperty);
|
||||
|
||||
var childResults = GetSettings(property.GetValue(defaultSetting), propertyName);
|
||||
result.AddRange(childResults);
|
||||
}
|
||||
}
|
||||
|
||||
Get = new DbSettings
|
||||
{
|
||||
Provider = GetString("Provider"),
|
||||
ProviderAutoImport = GetInt32("ProviderAutoImport"),
|
||||
ProviderAutoImportCategory = GetString("ProviderAutoImportCategory"),
|
||||
ProviderAutoDelete = GetInt32("ProviderAutoDelete"),
|
||||
ProviderTimeout = GetInt32("ProviderTimeout"),
|
||||
ProviderCheckInterval = GetInt32("ProviderCheckInterval"),
|
||||
RealDebridApiKey = GetString("RealDebridApiKey"),
|
||||
DownloadPath = GetString("DownloadPath"),
|
||||
DownloadClient = GetString("DownloadClient"),
|
||||
TempPath = GetString("TempPath"),
|
||||
MappedPath = GetString("MappedPath"),
|
||||
DownloadLimit = GetInt32("DownloadLimit"),
|
||||
UnpackLimit = GetInt32("UnpackLimit"),
|
||||
MinFileSize = GetInt32("MinFileSize"),
|
||||
OnlyDownloadAvailableFiles = GetInt32("OnlyDownloadAvailableFiles"),
|
||||
DownloadChunkCount = GetInt32("DownloadChunkCount"),
|
||||
DownloadMaxSpeed = GetInt32("DownloadMaxSpeed"),
|
||||
ProxyServer = GetString("ProxyServer"),
|
||||
LogLevel = GetString("LogLevel"),
|
||||
Categories = GetString("Categories"),
|
||||
Aria2cUrl = GetString("Aria2cUrl"),
|
||||
Aria2cSecret = GetString("Aria2cSecret"),
|
||||
DownloadRetryAttempts = GetInt32("DownloadRetryAttempts"),
|
||||
TorrentRetryAttempts = GetInt32("TorrentRetryAttempts"),
|
||||
DeleteOnError = GetInt32("DeleteOnError"),
|
||||
TorrentLifetime = GetInt32("TorrentLifetime"),
|
||||
RunOnTorrentCompleteFileName = GetString("RunOnTorrentCompleteFileName"),
|
||||
RunOnTorrentCompleteArguments = GetString("RunOnTorrentCompleteArguments")
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<IList<Setting>> GetAll()
|
||||
private static void SetSettings(IList<Setting> settings, Object defaultSetting, String parent)
|
||||
{
|
||||
return await _dataContext.Settings.AsNoTracking().ToListAsync();
|
||||
}
|
||||
var properties = defaultSetting.GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
public async Task Update(IList<Setting> settings)
|
||||
{
|
||||
await _settingCacheLock.WaitAsync();
|
||||
|
||||
try
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var dbSettings = await _dataContext.Settings.ToListAsync();
|
||||
var propertyName = property.Name;
|
||||
|
||||
foreach (var dbSetting in dbSettings)
|
||||
if (parent != null)
|
||||
{
|
||||
var setting = settings.FirstOrDefault(m => m.SettingId == dbSetting.SettingId);
|
||||
propertyName = $"{parent}:{propertyName}";
|
||||
}
|
||||
|
||||
if (property.PropertyType.IsEnum ||
|
||||
property.PropertyType.IsValueType ||
|
||||
property.PropertyType == typeof(String))
|
||||
{
|
||||
var setting = settings.FirstOrDefault(m => m.SettingId == propertyName);
|
||||
|
||||
if (setting != null)
|
||||
{
|
||||
dbSetting.Value = setting.Value;
|
||||
if (property.PropertyType.IsEnum)
|
||||
{
|
||||
var newValue = Enum.Parse(property.PropertyType, setting.Value);
|
||||
property.SetValue(defaultSetting, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var converter = TypeDescriptor.GetConverter(property.PropertyType);
|
||||
|
||||
if (converter.IsValid(setting.Value))
|
||||
{
|
||||
var newValue = converter.ConvertFrom(setting.Value);
|
||||
property.SetValue(defaultSetting, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"Invalid value for setting {propertyName}: {setting.Value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
await ResetCache();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_settingCacheLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateString(String key, String value)
|
||||
{
|
||||
await _settingCacheLock.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
var dbSetting = await _dataContext.Settings.FirstOrDefaultAsync(m => m.SettingId == key);
|
||||
|
||||
if (dbSetting == null)
|
||||
else
|
||||
{
|
||||
throw new Exception($"Cannot find setting with key {key}");
|
||||
SetSettings(settings, property.GetValue(defaultSetting), propertyName);
|
||||
}
|
||||
|
||||
dbSetting.Value = value;
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
||||
await ResetCache();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_settingCacheLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
8
server/RdtClient.Data/Enums/DownloadClient.cs
Normal file
8
server/RdtClient.Data/Enums/DownloadClient.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace RdtClient.Data.Enums;
|
||||
|
||||
public enum DownloadClient
|
||||
{
|
||||
Simple,
|
||||
MultiPart,
|
||||
Aria2c
|
||||
}
|
||||
18
server/RdtClient.Data/Enums/LogLevel.cs
Normal file
18
server/RdtClient.Data/Enums/LogLevel.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace RdtClient.Data.Enums;
|
||||
|
||||
public enum LogLevel
|
||||
{
|
||||
[Description("Debug")]
|
||||
Debug,
|
||||
|
||||
[Description("Information")]
|
||||
Information,
|
||||
|
||||
[Description("Warning")]
|
||||
Warning,
|
||||
|
||||
[Description("Error")]
|
||||
Error
|
||||
}
|
||||
12
server/RdtClient.Data/Enums/Provider.cs
Normal file
12
server/RdtClient.Data/Enums/Provider.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace RdtClient.Data.Enums;
|
||||
|
||||
public enum Provider
|
||||
{
|
||||
[Description("RealDebrid")]
|
||||
RealDebrid,
|
||||
|
||||
[Description("AllDebrid")]
|
||||
AllDebrid
|
||||
}
|
||||
|
|
@ -1,8 +1,15 @@
|
|||
namespace RdtClient.Data.Enums;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace RdtClient.Data.Enums;
|
||||
|
||||
public enum TorrentDownloadAction
|
||||
{
|
||||
[Description("Download All Files")]
|
||||
DownloadAll = 0,
|
||||
|
||||
[Description("Download All Available Files")]
|
||||
DownloadAvailableFiles = 1,
|
||||
|
||||
[Description("Manually Select Files")]
|
||||
DownloadManual = 2
|
||||
}
|
||||
|
|
@ -1,8 +1,15 @@
|
|||
namespace RdtClient.Data.Enums;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace RdtClient.Data.Enums;
|
||||
|
||||
public enum TorrentFinishedAction
|
||||
{
|
||||
[Description("No Action")]
|
||||
None = 0,
|
||||
|
||||
[Description("Remove Torrent From Client And Provider")]
|
||||
RemoveAllTorrents = 1,
|
||||
|
||||
[Description("Remove Torrent From Provider")]
|
||||
RemoveRealDebrid = 2
|
||||
}
|
||||
458
server/RdtClient.Data/Migrations/20220513164723_Settings_Remove_Type.Designer.cs
generated
Normal file
458
server/RdtClient.Data/Migrations/20220513164723_Settings_Remove_Type.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using RdtClient.Data.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20220513164723_Settings_Remove_Type")]
|
||||
partial class Settings_Remove_Type
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.Property<Guid>("DownloadId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Added")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Completed")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadFinished")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadQueued")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadStarted")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Error")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RemoteId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RetryCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("TorrentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingFinished")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingQueued")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingStarted")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("DownloadId");
|
||||
|
||||
b.HasIndex("TorrentId");
|
||||
|
||||
b.ToTable("Downloads");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b =>
|
||||
{
|
||||
b.Property<string>("SettingId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SettingId");
|
||||
|
||||
b.ToTable("Settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Property<Guid>("TorrentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Added")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Completed")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DeleteOnError")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DownloadAction")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DownloadManualFiles")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DownloadMinSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DownloadRetryAttempts")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Error")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileOrMagnet")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("FilesSelected")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("FinishedAction")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Hash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Lifetime")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("Priority")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RdAdded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("RdEnded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdFiles")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdHost")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("RdProgress")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("RdSeeders")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("RdSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("RdSpeed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("RdSplit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RdStatus")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RdStatusRaw")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Retry")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RetryCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TorrentRetryAttempts")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("TorrentId");
|
||||
|
||||
b.ToTable("Torrents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithMany("Downloads")
|
||||
.HasForeignKey("TorrentId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Torrent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Navigation("Downloads");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
public partial class Settings_Remove_Type : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Type",
|
||||
table: "Settings");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Type",
|
||||
table: "Settings",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
458
server/RdtClient.Data/Migrations/20220513175547_Settings_Migrate.Designer.cs
generated
Normal file
458
server/RdtClient.Data/Migrations/20220513175547_Settings_Migrate.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using RdtClient.Data.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20220513175547_Settings_Migrate")]
|
||||
partial class Settings_Migrate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.Property<Guid>("DownloadId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Added")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Completed")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadFinished")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadQueued")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("DownloadStarted")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Error")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Link")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RemoteId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RetryCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<Guid>("TorrentId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingFinished")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingQueued")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("UnpackingStarted")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("DownloadId");
|
||||
|
||||
b.HasIndex("TorrentId");
|
||||
|
||||
b.ToTable("Downloads");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Setting", b =>
|
||||
{
|
||||
b.Property<string>("SettingId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SettingId");
|
||||
|
||||
b.ToTable("Settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Property<Guid>("TorrentId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset>("Added")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Category")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Completed")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DeleteOnError")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DownloadAction")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("DownloadManualFiles")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("DownloadMinSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DownloadRetryAttempts")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Error")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileOrMagnet")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("FilesSelected")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("FinishedAction")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Hash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsFile")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Lifetime")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("Priority")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("RdAdded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("RdEnded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdFiles")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdHost")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RdName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("RdProgress")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("RdSeeders")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("RdSize")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("RdSpeed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("RdSplit")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RdStatus")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("RdStatusRaw")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("Retry")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RetryCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TorrentRetryAttempts")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("TorrentId");
|
||||
|
||||
b.ToTable("Torrents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Download", b =>
|
||||
{
|
||||
b.HasOne("RdtClient.Data.Models.Data.Torrent", "Torrent")
|
||||
.WithMany("Downloads")
|
||||
.HasForeignKey("TorrentId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Torrent");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("RdtClient.Data.Models.Data.Torrent", b =>
|
||||
{
|
||||
b.Navigation("Downloads");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace RdtClient.Data.Migrations
|
||||
{
|
||||
public partial class Settings_Migrate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'General:LogLevel' WHERE SettingId = 'LogLevel'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'General:DownloadLimit' WHERE SettingId = 'DownloadLimit'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'General:UnpackLimit' WHERE SettingId = 'UnpackLimit'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'General:Categories' WHERE SettingId = 'Categories'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'General:RunOnTorrentCompleteFileName' WHERE SettingId = 'RunOnTorrentCompleteFileName'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'General:RunOnTorrentCompleteArguments' WHERE SettingId = 'RunOnTorrentCompleteArguments'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:Client' WHERE SettingId = 'DownloadClient'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:DownloadPath' WHERE SettingId = 'DownloadPath'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:MappedPath' WHERE SettingId = 'MappedPath'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:ChunkCount' WHERE SettingId = 'DownloadChunkCount'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:MaxSpeed' WHERE SettingId = 'DownloadMaxSpeed'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:Aria2cUrl' WHERE SettingId = 'Aria2cUrl'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:Aria2cSecret' WHERE SettingId = 'Aria2cSecret'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:TempPath' WHERE SettingId = 'TempPath'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'DownloadClient:ProxyServer' WHERE SettingId = 'ProxyServer'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Provider' WHERE SettingId = 'Provider'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:ApiKey' WHERE SettingId = 'RealDebridApiKey'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:AutoImport' WHERE SettingId = 'ProviderAutoImport'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:AutoDelete' WHERE SettingId = 'ProviderAutoDelete'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Timeout' WHERE SettingId = 'ProviderTimeout'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:CheckInterval' WHERE SettingId = 'ProviderCheckInterval'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Default:Category' WHERE SettingId = 'ProviderAutoImportCategory'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Default:OnlyDownloadAvailableFiles' WHERE SettingId = 'OnlyDownloadAvailableFiles'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Default:MinFileSize' WHERE SettingId = 'MinFileSize'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Default:TorrentRetryAttempts' WHERE SettingId = 'TorrentRetryAttempts'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Default:DownloadRetryAttempts' WHERE SettingId = 'DownloadRetryAttempts'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Default:DeleteOnError' WHERE SettingId = 'ProviderDeleteOnError'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET SettingId = 'Provider:Default:TorrentLifetime' WHERE SettingId = 'TorrentLifetime'");
|
||||
|
||||
migrationBuilder.Sql("UPDATE Settings SET Value = 'True' WHERE SettingId = 'Provider:AutoDelete' AND Value = '1'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET Value = 'False' WHERE SettingId = 'Provider:AutoDelete' AND Value = '0'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET Value = 'True' WHERE SettingId = 'Provider:AutoImport' AND Value = '1'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET Value = 'False' WHERE SettingId = 'Provider:AutoImport' AND Value = '0'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET Value = 'True' WHERE SettingId = 'Provider:Default:OnlyDownloadAvailableFiles' AND Value = '1'");
|
||||
migrationBuilder.Sql("UPDATE Settings SET Value = 'False' WHERE SettingId = 'Provider:Default:OnlyDownloadAvailableFiles' AND Value = '0'");
|
||||
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ namespace RdtClient.Data.Migrations
|
|||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.1");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.4");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
|
|
@ -269,9 +269,6 @@ namespace RdtClient.Data.Migrations
|
|||
b.Property<string>("SettingId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,4 @@ public class Setting
|
|||
public String SettingId { get; set; }
|
||||
|
||||
public String Value { get; set; }
|
||||
|
||||
public String Type { get; set; }
|
||||
}
|
||||
|
|
@ -1,33 +1,209 @@
|
|||
namespace RdtClient.Data.Models.Internal;
|
||||
using System.ComponentModel;
|
||||
using RdtClient.Data.Enums;
|
||||
|
||||
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
|
||||
|
||||
namespace RdtClient.Data.Models.Internal;
|
||||
|
||||
public class DbSettings
|
||||
{
|
||||
public String Provider { get; set; }
|
||||
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; }
|
||||
public String TempPath { get; set; }
|
||||
public String MappedPath { get; set; }
|
||||
public Int32 DownloadLimit { get; set; }
|
||||
public Int32 UnpackLimit { get; set; }
|
||||
public Int32 MinFileSize { get; set; }
|
||||
public Int32 OnlyDownloadAvailableFiles { get; set; }
|
||||
public Int32 DownloadChunkCount { get; set; }
|
||||
public Int32 DownloadMaxSpeed { get; set; }
|
||||
public String ProxyServer { get; set; }
|
||||
public String LogLevel { get; set; }
|
||||
public String Categories { get; set; }
|
||||
public String Aria2cUrl { get; set; }
|
||||
public String Aria2cSecret { get; set; }
|
||||
public Int32 DownloadRetryAttempts { get; set; }
|
||||
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; }
|
||||
[DisplayName("General")]
|
||||
[Description("")]
|
||||
public DbSettingsGeneral General { get; set; } = new DbSettingsGeneral();
|
||||
|
||||
[DisplayName("Download Client")]
|
||||
[Description("")]
|
||||
public DbSettingsDownloadClient DownloadClient { get; set; } = new DbSettingsDownloadClient();
|
||||
|
||||
[DisplayName("Provider")]
|
||||
[Description("")]
|
||||
public DbSettingsProvider Provider { get; set; } = new DbSettingsProvider();
|
||||
|
||||
[DisplayName("qBittorrent")]
|
||||
[Description("The following settings only apply when a torrent gets added through the qbittorrent API, usually Radarr or Sonarr.")]
|
||||
public DbSettingsIntegrations Integrations { get; set; } = new DbSettingsIntegrations();
|
||||
|
||||
[DisplayName("GUI Defaults")]
|
||||
[Description("Settings used when adding a torrent through the web interface.")]
|
||||
public DbSettingsGui Gui { get; set; } = new DbSettingsGui();
|
||||
|
||||
[DisplayName("Watch")]
|
||||
[Description("The following settings only apply when a torrent gets through the watch folder.")]
|
||||
public DbSettingsWatch Watch { get; set; } = new DbSettingsWatch();
|
||||
}
|
||||
|
||||
public class DbSettingsGeneral
|
||||
{
|
||||
[DisplayName("Log level")]
|
||||
[Description("Recommended level is Warning, set to Debug to get the most info.")]
|
||||
public LogLevel LogLevel { get; set; } = LogLevel.Warning;
|
||||
|
||||
[DisplayName("Maximum parallel downloads")]
|
||||
[Description("Maximum amount of torrents that get downloaded to your host at the same time.")]
|
||||
public Int32 DownloadLimit { get; set; } = 2;
|
||||
|
||||
[DisplayName("Maximum unpack processes")]
|
||||
[Description("Maximum amount of downloads that get unpacked on your host at the same time.")]
|
||||
public Int32 UnpackLimit { get; set; } = 1;
|
||||
|
||||
[DisplayName("Categories")]
|
||||
[Description("Expose these categories through the QBittorrent API.")]
|
||||
public String? Categories { get; set; } = null;
|
||||
|
||||
[DisplayName("Run external program on torrent completion")]
|
||||
[Description("Path to the executable to run when the torrent and all downloads are finished. No arguments should be passed here.When running in Docker, this command will run on your docker instance!")]
|
||||
public String? RunOnTorrentCompleteFileName { get; set; } = null;
|
||||
|
||||
[DisplayName("External program arguments")]
|
||||
[Description(@"When the executable above is executed, use these parameters.
|
||||
Supports the following parameters:
|
||||
%N: Torrent name
|
||||
%L: Category
|
||||
%F: Content path (same as root path for multifile torrent)
|
||||
%R: Root path (first torrent subdirectory path)
|
||||
%D: Save path
|
||||
%C: Number of files
|
||||
%Z: Torrent size (bytes)
|
||||
%I: Info hash")]
|
||||
public String? RunOnTorrentCompleteArguments { get; set; } = null;
|
||||
}
|
||||
|
||||
public class DbSettingsDownloadClient
|
||||
{
|
||||
[DisplayName("Download client")]
|
||||
[Description(@"Select which download client to use, see the
|
||||
<a href=""https://github.com/rogerfar/rdt-client/"" target=""_blank"">README</a> for the various options.")]
|
||||
public DownloadClient Client { get; set; } = DownloadClient.Simple;
|
||||
|
||||
[DisplayName("Download path")]
|
||||
[Description("Path in the docker container to download files to (i.e. /data/downloads), or a local path when using as a service.")]
|
||||
public String DownloadPath { get; set; } = "/data/downloads";
|
||||
|
||||
[DisplayName("Mapped path")]
|
||||
[Description("Path where files are downloaded to on your host (i.e. D:\\Downloads). This path is used for *arr to find your downloads.")]
|
||||
public String MappedPath { get; set; } = @"C:\Downloads";
|
||||
|
||||
[DisplayName("Download speed (in MB/s) (only used for the Simple and Multi Downloader)")]
|
||||
[Description("Maximum download speed in Megabytes per second. When set to 0 unlimited speed is used.")]
|
||||
public Int32 MaxSpeed { get; set; } = 0;
|
||||
|
||||
[DisplayName("Aria2c URL (only used for the Aria2c Downloader)")]
|
||||
[Description(@"This is the URL to your Aria2c instance. It must end in /jsonrpc. A common URL is
|
||||
http://127.0.0.1:6800/jsonrpc.")]
|
||||
public String Aria2cUrl { get; set; } = "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("Temp Download path (only used for the Multi Downloader)")]
|
||||
[Description("Path in the docker container to temporarily download to (i.e. /data/temp). Make sure the docker container has enough disk space if using a path inside the container.")]
|
||||
public String TempPath { get; set; } = "/data/downloads";
|
||||
|
||||
[DisplayName("Parallel connections per download (only used for the Multi Downloader)")]
|
||||
[Description("Maximum amount of parallel threads that are used to download a single torrent to your host. If set to 1 no parallel downloading will be done.")]
|
||||
public Int32 ChunkCount { get; set; } = 8;
|
||||
|
||||
[DisplayName("Proxy Server")]
|
||||
[Description("Address of a proxy server to download through (only used for the Multi Downloader).")]
|
||||
public String? ProxyServer { get; set; } = null;
|
||||
}
|
||||
|
||||
public class DbSettingsProvider
|
||||
{
|
||||
[DisplayName("Provider")]
|
||||
[Description(@"The following 2 providers are supported:
|
||||
<a href=""https://real-debrid.com/?id=1348683"" target=""_blank"" rel=""noopener"">https://real-debrid.com</a>
|
||||
<a href=""https://alldebrid.com/?uid=2v91l&lang=en"" target=""_blank"" rel=""noopener"">https://alldebrid.com</a>
|
||||
At this point only 1 provider can be used at the time.")]
|
||||
public Provider Provider { get; set; } = Provider.RealDebrid;
|
||||
|
||||
[DisplayName("API Key")]
|
||||
[Description(@"You can find your API key here:
|
||||
<a href=""https://real-debrid.com/apitoken"" target=""_blank"" rel=""noopener"">https://real-debrid.com/apitoken</a>
|
||||
or
|
||||
<a href=""https://alldebrid.com/apikeys/"" target=""_blank"" rel=""noopener"">https://alldebrid.com/apikeys/</a>.")]
|
||||
public String ApiKey { get; set; } = "";
|
||||
|
||||
[DisplayName("Automatically import and process torrents added to provider")]
|
||||
[Description("When selected, import downloads that are not added through RealDebridClient but have been directly added to Real-Debrid or AllDebrid.")]
|
||||
public Boolean AutoImport { get; set; } = false;
|
||||
|
||||
[DisplayName("Automatically delete downloads removed from provider")]
|
||||
[Description("When selected, cancel and delete downloads that have been removed from Real-Debrid or AllDebrid.")]
|
||||
public Boolean AutoDelete { get; set; } = false;
|
||||
|
||||
[DisplayName("Connection Timeout")]
|
||||
[Description("Timeout in seconds to make a connection to the provider. Increase if you experience timeouts in the logs.")]
|
||||
public Int32 Timeout { get; set; } = 10;
|
||||
|
||||
[DisplayName("Check Interval")]
|
||||
[Description("The interval to check the torrents info on the providers API. Minumum is 3 seconds. When there are no active downloads this limit is increased * 3.")]
|
||||
public Int32 CheckInterval { get; set; } = 10;
|
||||
|
||||
[DisplayName("Auto Import Defaults")]
|
||||
public DbSettingsDefaults Default { get; set; } = new DbSettingsDefaults();
|
||||
}
|
||||
|
||||
public class DbSettingsIntegrations
|
||||
{
|
||||
public DbSettingsDefaults Default { get; set; } = new DbSettingsDefaults();
|
||||
}
|
||||
|
||||
public class DbSettingsGui
|
||||
{
|
||||
public DbSettingsDefaults Default { get; set; } = new DbSettingsDefaults();
|
||||
}
|
||||
|
||||
public class DbSettingsWatch
|
||||
{
|
||||
[DisplayName("Watch Path")]
|
||||
[Description("Watch this path for .torrent or .magnet files. When a file is found it will be automatically imported.")]
|
||||
public String? Path { get; set; } = null;
|
||||
|
||||
[DisplayName("Check Interval")]
|
||||
[Description("Time in seconds to check the folder for new files.")]
|
||||
public Int32 Interval { get; set; } = 60;
|
||||
|
||||
[DisplayName("Import Defaults")]
|
||||
public DbSettingsDefaults Default { get; set; } = new DbSettingsDefaults();
|
||||
}
|
||||
|
||||
public class DbSettingsDefaults
|
||||
{
|
||||
[DisplayName("Category")]
|
||||
[Description("When a torrent is imported assign it this category.")]
|
||||
public String? Category { get; set; } = null;
|
||||
|
||||
[DisplayName("Only download available files on debrid provider")]
|
||||
[Description("When selected, it will only download files in the torrent that have been download by Real-Debrid. You can use this in combination with the Min File size setting above.")]
|
||||
public Boolean OnlyDownloadAvailableFiles { get; set; } = true;
|
||||
|
||||
[DisplayName("Finished Action")]
|
||||
[Description("When a torrent is finished, perform this action.")]
|
||||
public TorrentFinishedAction FinishedAction { get; set; } = TorrentFinishedAction.RemoveAllTorrents;
|
||||
|
||||
[DisplayName("Minimum file size to download")]
|
||||
[Description("Files that are smaller than this setting are skipped and not downloaded. When set to 0 all files are downloaded. When downloading from Radarr or Sonarr it's recommended to keep this setting at atleast a few MB to avoid the debrid provider having to re-download the torrent.")]
|
||||
public Int32 MinFileSize { get; set; } = 0;
|
||||
|
||||
[DisplayName("Automatic retry torrent")]
|
||||
[Description("When a single download has failed multiple times (see setting above) or when the torrent itself received an error it will retry the full torrent this many times before marking it failed.")]
|
||||
public Int32 TorrentRetryAttempts { get; set; } = 1;
|
||||
|
||||
[DisplayName("Automatic retry downloads")]
|
||||
[Description("When a single download fails it will retry it this many times before marking it as failed.")]
|
||||
public Int32 DownloadRetryAttempts { get; set; } = 3;
|
||||
|
||||
[DisplayName("Delete download when in error")]
|
||||
[Description("When a download has been in error for this many minutes, delete it from the provider and the client. 0 to disable.")]
|
||||
public Int32 DeleteOnError { get; set; } = 0;
|
||||
|
||||
[DisplayName("Torrent maximum lifetime")]
|
||||
[Description("The maximum lifetime of a torrent in minutes. When this time has passed, mark the torrent as error. If the torrent is completed and has downloads, the lifetime setting will not apply. 0 to disable.")]
|
||||
public Int32 TorrentLifetime { get; set; } = 0;
|
||||
|
||||
[DisplayName("Priority")]
|
||||
[Description("Set the priority of a torrent, 1 = highest, 0 = disabled.")]
|
||||
public Int32 Priority { get; set; } = 0;
|
||||
}
|
||||
11
server/RdtClient.Data/Models/Internal/SettingProperty.cs
Normal file
11
server/RdtClient.Data/Models/Internal/SettingProperty.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace RdtClient.Data.Models.Internal;
|
||||
|
||||
public class SettingProperty
|
||||
{
|
||||
public String Key { get; set; }
|
||||
public Object Value { get; set; }
|
||||
public String DisplayName { get; set; }
|
||||
public String Description { get; set; }
|
||||
public String Type { get; set; }
|
||||
public Dictionary<Int32, String> EnumValues { get; set; }
|
||||
}
|
||||
|
|
@ -19,4 +19,8 @@
|
|||
<PackageReference Include="Serilog" Version="2.11.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Attributes\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ public class ProviderUpdater : BackgroundService
|
|||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||
while (!Startup.Ready)
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var torrentService = scope.ServiceProvider.GetRequiredService<Torrents>();
|
||||
|
|
@ -33,11 +36,11 @@ public class ProviderUpdater : BackgroundService
|
|||
{
|
||||
var torrents = await torrentService.Get();
|
||||
|
||||
if (_nextUpdate < DateTime.UtcNow && ((torrents.Count > 0 && Settings.Get.ProviderAutoImport == 0) || Settings.Get.ProviderAutoImport == 1))
|
||||
if (_nextUpdate < DateTime.UtcNow && ((torrents.Count > 0 && Settings.Get.Provider.AutoImport) || Settings.Get.Provider.AutoImport))
|
||||
{
|
||||
_logger.LogDebug($"Updating torrent info from Real-Debrid");
|
||||
|
||||
var updateTime = Settings.Get.ProviderCheckInterval * 3;
|
||||
var updateTime = Settings.Get.Provider.CheckInterval * 3;
|
||||
|
||||
if (updateTime < 30)
|
||||
{
|
||||
|
|
@ -46,7 +49,7 @@ public class ProviderUpdater : BackgroundService
|
|||
|
||||
if (RdtHub.HasConnections)
|
||||
{
|
||||
updateTime = Settings.Get.ProviderCheckInterval;
|
||||
updateTime = Settings.Get.Provider.CheckInterval;
|
||||
|
||||
if (updateTime < 5)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ using Microsoft.Extensions.Hosting;
|
|||
using RdtClient.Data.Data;
|
||||
using RdtClient.Service.Services;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace RdtClient.Service.BackgroundServices;
|
||||
|
||||
public class Startup : IHostedService
|
||||
{
|
||||
public static Boolean Ready { get; private set; }
|
||||
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public Startup(IServiceProvider serviceProvider)
|
||||
|
|
@ -20,36 +21,20 @@ public class Startup : IHostedService
|
|||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var version = Assembly.GetEntryAssembly()?.GetName().Version;
|
||||
Log.Warning($"Starting host on version {version}");
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<DataContext>();
|
||||
await dbContext.Database.MigrateAsync(cancellationToken);
|
||||
await dbContext.Seed();
|
||||
|
||||
var logLevelSettingDb = await dbContext.Settings.FirstOrDefaultAsync(m => m.SettingId == "LogLevel", cancellationToken);
|
||||
|
||||
var logLevelSetting = "Warning";
|
||||
|
||||
if (logLevelSettingDb != null)
|
||||
{
|
||||
logLevelSetting = logLevelSettingDb.Value;
|
||||
}
|
||||
|
||||
if (!Enum.TryParse<LogEventLevel>(logLevelSetting, out var logLevel))
|
||||
{
|
||||
logLevel = LogEventLevel.Warning;
|
||||
}
|
||||
|
||||
Settings.LoggingLevelSwitch.MinimumLevel = logLevel;
|
||||
|
||||
var version = Assembly.GetEntryAssembly()?.GetName().Version;
|
||||
Log.Warning($"Starting host on version {version}");
|
||||
|
||||
var settings = scope.ServiceProvider.GetRequiredService<Settings>();
|
||||
|
||||
await settings.Seed();
|
||||
await settings.ResetCache();
|
||||
|
||||
await settings.Clean();
|
||||
|
||||
Ready = true;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ public class TaskRunner : BackgroundService
|
|||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
|
||||
while (!Startup.Ready)
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var torrentRunner = scope.ServiceProvider.GetRequiredService<TorrentRunner>();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@ public class UpdateChecker : BackgroundService
|
|||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!Startup.Ready)
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
|
||||
var version = Assembly.GetEntryAssembly()?.GetName().Version?.ToString();
|
||||
|
||||
if (String.IsNullOrWhiteSpace(version))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Service.Services;
|
||||
|
||||
namespace RdtClient.Service.BackgroundServices;
|
||||
|
||||
public class WatchFolderChecker : BackgroundService
|
||||
{
|
||||
private readonly ILogger<WatchFolderChecker> _logger;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
private DateTime _prevCheck = DateTime.MinValue;
|
||||
|
||||
public WatchFolderChecker(ILogger<WatchFolderChecker> logger, IServiceProvider serviceProvider)
|
||||
{
|
||||
_logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!Startup.Ready)
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
}
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var torrentService = scope.ServiceProvider.GetRequiredService<Torrents>();
|
||||
|
||||
_logger.LogInformation("ProviderUpdater started.");
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(1000, stoppingToken);
|
||||
|
||||
if (String.IsNullOrWhiteSpace(Settings.Get.Watch.Path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var nextCheck = _prevCheck.AddSeconds(Settings.Get.Watch.Interval);
|
||||
|
||||
if (DateTime.UtcNow < nextCheck)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_prevCheck = DateTime.UtcNow;
|
||||
|
||||
var torrentFiles = Directory.GetFiles(Settings.Get.Watch.Path, "*.torrent,*.magnet", SearchOption.TopDirectoryOnly);
|
||||
|
||||
foreach (var torrentFile in torrentFiles)
|
||||
{
|
||||
var fileInfo = new FileInfo(torrentFile);
|
||||
|
||||
if (IsFileLocked(fileInfo))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var torrent = new Torrent
|
||||
{
|
||||
Category = Settings.Get.Watch.Default.Category,
|
||||
DownloadAction = Settings.Get.Watch.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||
FinishedAction = Settings.Get.Watch.Default.FinishedAction,
|
||||
DownloadMinSize = Settings.Get.Watch.Default.MinFileSize,
|
||||
TorrentRetryAttempts = Settings.Get.Watch.Default.TorrentRetryAttempts,
|
||||
DownloadRetryAttempts = Settings.Get.Watch.Default.DownloadRetryAttempts,
|
||||
DeleteOnError = Settings.Get.Watch.Default.DeleteOnError,
|
||||
Lifetime = Settings.Get.Watch.Default.TorrentLifetime,
|
||||
Priority = Settings.Get.Watch.Default.Priority > 0 ? Settings.Get.Watch.Default.Priority : null
|
||||
};
|
||||
|
||||
if (fileInfo.Extension == ".torrent")
|
||||
{
|
||||
var torrentFileContents = await File.ReadAllBytesAsync(torrentFile, stoppingToken);
|
||||
await torrentService.UploadFile(torrentFileContents, torrent);
|
||||
}
|
||||
else if (fileInfo.Extension == ".magnet")
|
||||
{
|
||||
var magnetLink = await File.ReadAllTextAsync(torrentFile, stoppingToken);
|
||||
await torrentService.UploadMagnet(magnetLink, torrent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Unexpected error occurred in ProviderUpdater: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Boolean IsFileLocked(FileInfo file)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
|
||||
stream.Close();
|
||||
}
|
||||
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ public class DownloadClient
|
|||
_destinationPath = destinationPath;
|
||||
}
|
||||
|
||||
public String Type { get; set; }
|
||||
public Data.Enums.DownloadClient Type { get; set; }
|
||||
|
||||
public Boolean Finished { get; private set; }
|
||||
|
||||
|
|
@ -48,13 +48,13 @@ public class DownloadClient
|
|||
|
||||
await FileHelper.Delete(filePath);
|
||||
|
||||
Type = settings.DownloadClient;
|
||||
Type = settings.DownloadClient.Client;
|
||||
|
||||
Downloader = settings.DownloadClient switch
|
||||
Downloader = settings.DownloadClient.Client switch
|
||||
{
|
||||
"Simple" => new SimpleDownloader(_download.Link, filePath),
|
||||
"MultiPart" => new MultiDownloader(_download.Link, filePath, settings),
|
||||
"Aria2c" => new Aria2cDownloader(_download.RemoteId, _download.Link, filePath, settings),
|
||||
Data.Enums.DownloadClient.Simple => new SimpleDownloader(_download.Link, filePath),
|
||||
Data.Enums.DownloadClient.MultiPart => new MultiDownloader(_download.Link, filePath, settings),
|
||||
Data.Enums.DownloadClient.Aria2c => new Aria2cDownloader(_download.RemoteId, _download.Link, filePath, settings),
|
||||
_ => throw new Exception($"Unknown download client {settings.DownloadClient}")
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class Aria2cDownloader : IDownloader
|
|||
Timeout = TimeSpan.FromSeconds(10)
|
||||
};
|
||||
|
||||
_aria2NetClient = new Aria2NetClient(settings.Aria2cUrl, settings.Aria2cSecret, httpClient, 10);
|
||||
_aria2NetClient = new Aria2NetClient(settings.DownloadClient.Aria2cUrl, settings.DownloadClient.Aria2cSecret, httpClient, 10);
|
||||
}
|
||||
|
||||
public async Task<String> Download()
|
||||
|
|
@ -162,7 +162,7 @@ public class Aria2cDownloader : IDownloader
|
|||
|
||||
if (download == null)
|
||||
{
|
||||
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
|
||||
{
|
||||
Error = $"Download was not found in Aria2"
|
||||
});
|
||||
|
|
@ -172,7 +172,7 @@ public class Aria2cDownloader : IDownloader
|
|||
if (!String.IsNullOrWhiteSpace(download.ErrorMessage) || download.Status == "error")
|
||||
{
|
||||
await Remove();
|
||||
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
|
||||
{
|
||||
Error = $"{download.ErrorCode}: {download.ErrorMessage}"
|
||||
});
|
||||
|
|
@ -190,7 +190,7 @@ public class Aria2cDownloader : IDownloader
|
|||
{
|
||||
if (retryCount >= 10)
|
||||
{
|
||||
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
|
||||
{
|
||||
Error = $"File not found at {_filePath}"
|
||||
});
|
||||
|
|
@ -199,7 +199,7 @@ public class Aria2cDownloader : IDownloader
|
|||
|
||||
if (File.Exists(_filePath))
|
||||
{
|
||||
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs());
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs());
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ public class Aria2cDownloader : IDownloader
|
|||
return;
|
||||
}
|
||||
|
||||
DownloadProgress.Invoke(this, new DownloadProgressEventArgs
|
||||
DownloadProgress?.Invoke(this, new DownloadProgressEventArgs
|
||||
{
|
||||
BytesDone = download.CompletedLength,
|
||||
BytesTotal = download.TotalLength,
|
||||
|
|
|
|||
|
|
@ -23,21 +23,21 @@ public class MultiDownloader : IDownloader
|
|||
_uri = uri;
|
||||
_filePath = filePath;
|
||||
|
||||
var settingTempPath = settings.TempPath;
|
||||
var settingTempPath = settings.DownloadClient.TempPath;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(settingTempPath))
|
||||
{
|
||||
settingTempPath = Path.GetTempPath();
|
||||
}
|
||||
|
||||
var settingDownloadChunkCount = settings.DownloadChunkCount;
|
||||
var settingDownloadChunkCount = settings.DownloadClient.ChunkCount;
|
||||
|
||||
if (settingDownloadChunkCount <= 0)
|
||||
{
|
||||
settingDownloadChunkCount = 1;
|
||||
}
|
||||
|
||||
var settingDownloadMaxSpeed = settings.DownloadMaxSpeed;
|
||||
var settingDownloadMaxSpeed = settings.DownloadClient.MaxSpeed;
|
||||
|
||||
if (settingDownloadMaxSpeed <= 0)
|
||||
{
|
||||
|
|
@ -46,7 +46,7 @@ public class MultiDownloader : IDownloader
|
|||
|
||||
settingDownloadMaxSpeed = settingDownloadMaxSpeed * 1024 * 1024;
|
||||
|
||||
var settingProxyServer = settings.ProxyServer;
|
||||
var settingProxyServer = settings.DownloadClient.ProxyServer;
|
||||
|
||||
var downloadOpt = new DownloadConfiguration
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public class SimpleDownloader : IDownloader
|
|||
throw new IOException("No stream");
|
||||
}
|
||||
|
||||
var speedLimit = Settings.Get.DownloadMaxSpeed;
|
||||
var speedLimit = Settings.Get.DownloadClient.MaxSpeed;
|
||||
|
||||
await using var destinationStream = new ThrottledStream(responseStream, speedLimit * 1000L * 1000L);
|
||||
|
||||
|
|
@ -108,9 +108,9 @@ public class SimpleDownloader : IDownloader
|
|||
BytesTotal = _bytesTotal
|
||||
});
|
||||
|
||||
if (Settings.Get.DownloadMaxSpeed != speedLimit)
|
||||
if (Settings.Get.DownloadClient.MaxSpeed != speedLimit)
|
||||
{
|
||||
speedLimit = Settings.Get.DownloadMaxSpeed;
|
||||
speedLimit = Settings.Get.DownloadClient.MaxSpeed;
|
||||
destinationStream.BandwidthLimit = speedLimit * 1000L * 1000L;
|
||||
}
|
||||
}
|
||||
|
|
@ -139,11 +139,11 @@ public class SimpleDownloader : IDownloader
|
|||
throw new Exception($"Download timed out");
|
||||
}
|
||||
|
||||
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs());
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DownloadComplete.Invoke(this, new DownloadCompleteEventArgs
|
||||
DownloadComplete?.Invoke(this, new DownloadCompleteEventArgs
|
||||
{
|
||||
Error = ex.Message
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Data.Models.QBittorrent;
|
||||
|
||||
namespace RdtClient.Service.Services;
|
||||
|
|
@ -194,7 +195,7 @@ public class QBittorrent
|
|||
|
||||
public String AppDefaultSavePath()
|
||||
{
|
||||
var downloadPath = Settings.Get.MappedPath;
|
||||
var downloadPath = Settings.Get.DownloadClient.MappedPath;
|
||||
|
||||
downloadPath = downloadPath.TrimEnd('\\')
|
||||
.TrimEnd('/');
|
||||
|
|
@ -431,15 +432,15 @@ public class QBittorrent
|
|||
{
|
||||
var torrent = new Torrent
|
||||
{
|
||||
Category = category,
|
||||
DownloadAction = Settings.Get.OnlyDownloadAvailableFiles == 1 ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||
FinishedAction = TorrentFinishedAction.None,
|
||||
DownloadMinSize = Settings.Get.MinFileSize,
|
||||
TorrentRetryAttempts = Settings.Get.TorrentRetryAttempts,
|
||||
DownloadRetryAttempts = Settings.Get.DownloadRetryAttempts,
|
||||
DeleteOnError = Settings.Get.DeleteOnError,
|
||||
Lifetime = Settings.Get.TorrentLifetime,
|
||||
Priority = priority
|
||||
Category = category ?? Settings.Get.Integrations.Default.Category,
|
||||
DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||
FinishedAction = Settings.Get.Integrations.Default.FinishedAction,
|
||||
DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize,
|
||||
TorrentRetryAttempts = Settings.Get.Integrations.Default.TorrentRetryAttempts,
|
||||
DownloadRetryAttempts = Settings.Get.Integrations.Default.DownloadRetryAttempts,
|
||||
DeleteOnError = Settings.Get.Integrations.Default.DeleteOnError,
|
||||
Lifetime = Settings.Get.Integrations.Default.TorrentLifetime,
|
||||
Priority = priority ?? (Settings.Get.Integrations.Default.Priority > 0 ? Settings.Get.Integrations.Default.Priority : null)
|
||||
};
|
||||
|
||||
await _torrents.UploadMagnet(magnetLink, torrent);
|
||||
|
|
@ -449,15 +450,15 @@ public class QBittorrent
|
|||
{
|
||||
var torrent = new Torrent
|
||||
{
|
||||
Category = category,
|
||||
DownloadAction = Settings.Get.OnlyDownloadAvailableFiles == 1 ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||
FinishedAction = TorrentFinishedAction.None,
|
||||
DownloadMinSize = Settings.Get.MinFileSize,
|
||||
TorrentRetryAttempts = Settings.Get.TorrentRetryAttempts,
|
||||
DownloadRetryAttempts = Settings.Get.DownloadRetryAttempts,
|
||||
DeleteOnError = Settings.Get.DeleteOnError,
|
||||
Lifetime = Settings.Get.TorrentLifetime,
|
||||
Priority = priority
|
||||
Category = category ?? Settings.Get.Integrations.Default.Category,
|
||||
DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||
FinishedAction = Settings.Get.Integrations.Default.FinishedAction,
|
||||
DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize,
|
||||
TorrentRetryAttempts = Settings.Get.Integrations.Default.TorrentRetryAttempts,
|
||||
DownloadRetryAttempts = Settings.Get.Integrations.Default.DownloadRetryAttempts,
|
||||
DeleteOnError = Settings.Get.Integrations.Default.DeleteOnError,
|
||||
Lifetime = Settings.Get.Integrations.Default.TorrentLifetime,
|
||||
Priority = priority ?? (Settings.Get.Integrations.Default.Priority > 0 ? Settings.Get.Integrations.Default.Priority : null)
|
||||
};
|
||||
|
||||
await _torrents.UploadFile(fileBytes, torrent);
|
||||
|
|
@ -476,11 +477,13 @@ public class QBittorrent
|
|||
.Select(m => m.Category.ToLower())
|
||||
.ToList();
|
||||
|
||||
var categories = Settings.Get
|
||||
.Categories
|
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
var categoryList = (Settings.Get.General.Categories ?? "")
|
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Distinct(StringComparer.CurrentCultureIgnoreCase)
|
||||
.Select(m => m.Trim())
|
||||
.ToList();
|
||||
|
||||
torrentsToGroup.AddRange(categories);
|
||||
torrentsToGroup.AddRange(categoryList);
|
||||
|
||||
var results = new Dictionary<String, TorrentCategory>();
|
||||
|
||||
|
|
@ -500,48 +503,53 @@ public class QBittorrent
|
|||
|
||||
public async Task CategoryCreate(String category)
|
||||
{
|
||||
var categoriesSetting = Settings.Get.Categories;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(categoriesSetting))
|
||||
{
|
||||
categoriesSetting = category;
|
||||
}
|
||||
else
|
||||
{
|
||||
var categoryList = categoriesSetting
|
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Distinct(StringComparer.CurrentCultureIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (!categoryList.Contains(category))
|
||||
{
|
||||
categoryList.Add(category);
|
||||
}
|
||||
|
||||
categoriesSetting = String.Join(",", categoryList);
|
||||
}
|
||||
|
||||
await _settings.UpdateString("Categories", categoriesSetting);
|
||||
}
|
||||
|
||||
public async Task CategoryRemove(String category)
|
||||
{
|
||||
var categoriesSetting = Settings.Get.Categories;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(categoriesSetting))
|
||||
if (category == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var categoryList = categoriesSetting.Split(",", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Distinct(StringComparer.CurrentCultureIgnoreCase)
|
||||
.ToList();
|
||||
category = category.Trim();
|
||||
|
||||
var categoriesSetting = Settings.Get.General.Categories;
|
||||
|
||||
var categoryList = (categoriesSetting ?? "")
|
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Distinct(StringComparer.CurrentCultureIgnoreCase)
|
||||
.Select(m => m.Trim())
|
||||
.ToList();
|
||||
|
||||
if (!categoryList.Contains(category))
|
||||
{
|
||||
categoryList.Add(category);
|
||||
}
|
||||
|
||||
categoriesSetting = String.Join(",", categoryList);
|
||||
|
||||
await _settings.Update("General:Categories", categoriesSetting);
|
||||
}
|
||||
|
||||
public async Task CategoryRemove(String category)
|
||||
{
|
||||
if (category == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
category = category.Trim();
|
||||
|
||||
var categoriesSetting = Settings.Get.General.Categories;
|
||||
|
||||
var categoryList = (categoriesSetting ?? "")
|
||||
.Split(",", StringSplitOptions.RemoveEmptyEntries)
|
||||
.Distinct(StringComparer.CurrentCultureIgnoreCase)
|
||||
.Select(m => m.Trim())
|
||||
.ToList();
|
||||
|
||||
categoryList = categoryList.Where(m => m != category).ToList();
|
||||
|
||||
categoriesSetting = String.Join(",", categoryList);
|
||||
|
||||
await _settings.UpdateString("Categories", categoriesSetting);
|
||||
await _settings.Update("General:Categories", categoriesSetting);
|
||||
}
|
||||
|
||||
public async Task TorrentsTopPrio(String hash)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
using System.Diagnostics;
|
||||
using Aria2NET;
|
||||
using RdtClient.Data.Data;
|
||||
using RdtClient.Data.Enums;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service.Helpers;
|
||||
using RdtClient.Service.Services.Downloaders;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
|
|
@ -22,19 +24,19 @@ public class Settings
|
|||
|
||||
public static DbSettings Get => SettingData.Get;
|
||||
|
||||
public async Task<IList<Setting>> GetAll()
|
||||
public IList<SettingProperty> GetAll()
|
||||
{
|
||||
return await _settingData.GetAll();
|
||||
return _settingData.GetAll();
|
||||
}
|
||||
|
||||
public async Task Update(IList<Setting> settings)
|
||||
public async Task Update(IList<SettingProperty> settings)
|
||||
{
|
||||
await _settingData.Update(settings);
|
||||
}
|
||||
|
||||
public async Task UpdateString(String key, String value)
|
||||
public async Task Update(String settingId, Object value)
|
||||
{
|
||||
await _settingData.UpdateString(key, value);
|
||||
await _settingData.Update(settingId, value);
|
||||
}
|
||||
|
||||
public async Task TestPath(String path)
|
||||
|
|
@ -60,7 +62,7 @@ public class Settings
|
|||
|
||||
public async Task<Double> TestDownloadSpeed(CancellationToken cancellationToken)
|
||||
{
|
||||
var downloadPath = Get.DownloadPath;
|
||||
var downloadPath = Get.DownloadClient.DownloadPath;
|
||||
|
||||
var testFilePath = Path.Combine(downloadPath, "testDefault.rar");
|
||||
|
||||
|
|
@ -79,18 +81,29 @@ public class Settings
|
|||
|
||||
await downloadClient.Start(Get);
|
||||
|
||||
var httpClient = new HttpClient
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(10)
|
||||
};
|
||||
|
||||
while (!downloadClient.Finished)
|
||||
{
|
||||
#pragma warning disable CA2016 // Forward the 'CancellationToken' parameter to methods that take one
|
||||
// ReSharper disable once MethodSupportsCancellation
|
||||
await Task.Delay(10);
|
||||
#pragma warning restore CA2016 // Forward the 'CancellationToken' parameter to methods that take one
|
||||
|
||||
await Task.Delay(1000, CancellationToken.None);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
await downloadClient.Cancel();
|
||||
}
|
||||
|
||||
if (downloadClient.Downloader is Aria2cDownloader aria2Downloader)
|
||||
{
|
||||
var aria2NetClient = new Aria2NetClient(Settings.Get.DownloadClient.Aria2cUrl, Settings.Get.DownloadClient.Aria2cSecret, httpClient, 1);
|
||||
|
||||
var allDownloads = await aria2NetClient.TellAll(cancellationToken);
|
||||
|
||||
await aria2Downloader.Update(allDownloads);
|
||||
}
|
||||
|
||||
if (downloadClient.BytesDone > 1024 * 1024 * 50)
|
||||
{
|
||||
await downloadClient.Cancel();
|
||||
|
|
@ -108,7 +121,7 @@ public class Settings
|
|||
|
||||
public async Task<Double> TestWriteSpeed()
|
||||
{
|
||||
var downloadPath = Get.DownloadPath;
|
||||
var downloadPath = Get.DownloadClient.DownloadPath;
|
||||
|
||||
var testFilePath = Path.Combine(downloadPath, "test.tmp");
|
||||
|
||||
|
|
@ -155,7 +168,7 @@ public class Settings
|
|||
{
|
||||
try
|
||||
{
|
||||
var tempPath = Get.TempPath;
|
||||
var tempPath = Get.DownloadClient.TempPath;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(tempPath))
|
||||
{
|
||||
|
|
@ -173,8 +186,22 @@ public class Settings
|
|||
}
|
||||
}
|
||||
|
||||
public async Task Seed()
|
||||
{
|
||||
await _settingData.Seed();
|
||||
}
|
||||
|
||||
public async Task ResetCache()
|
||||
{
|
||||
await _settingData.ResetCache();
|
||||
|
||||
LoggingLevelSwitch.MinimumLevel = Settings.Get.General.LogLevel switch
|
||||
{
|
||||
LogLevel.Debug => LogEventLevel.Debug,
|
||||
LogLevel.Information => LogEventLevel.Information,
|
||||
LogLevel.Warning => LogEventLevel.Warning,
|
||||
LogLevel.Error => LogEventLevel.Error,
|
||||
_ => LogEventLevel.Warning
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ public class AllDebridTorrentClient : ITorrentClient
|
|||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.RealDebridApiKey;
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ public class RealDebridTorrentClient : ITorrentClient
|
|||
{
|
||||
private readonly ILogger<RealDebridTorrentClient> _logger;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private TimeSpan? _offset = null;
|
||||
private TimeSpan? _offset;
|
||||
|
||||
public RealDebridTorrentClient(ILogger<RealDebridTorrentClient> logger, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
|
|
@ -23,7 +23,7 @@ public class RealDebridTorrentClient : ITorrentClient
|
|||
{
|
||||
try
|
||||
{
|
||||
var apiKey = Settings.Get.RealDebridApiKey;
|
||||
var apiKey = Settings.Get.Provider.ApiKey;
|
||||
|
||||
if (String.IsNullOrWhiteSpace(apiKey))
|
||||
{
|
||||
|
|
@ -31,7 +31,7 @@ public class RealDebridTorrentClient : ITorrentClient
|
|||
}
|
||||
|
||||
var httpClient = _httpClientFactory.CreateClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.ProviderTimeout);
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(Settings.Get.Provider.Timeout);
|
||||
|
||||
var rdtNetClient = new RdNetClient(null, httpClient, 5);
|
||||
rdtNetClient.UseApiAuthentication(apiKey);
|
||||
|
|
@ -73,7 +73,7 @@ public class RealDebridTorrentClient : ITorrentClient
|
|||
Split = torrent.Split,
|
||||
Progress = torrent.Progress,
|
||||
Status = torrent.Status,
|
||||
Added = ChangeTimeZone(torrent.Added).Value,
|
||||
Added = ChangeTimeZone(torrent.Added)!.Value,
|
||||
Files = (torrent.Files ?? new List<TorrentFile>()).Select(m => new TorrentClientFile
|
||||
{
|
||||
Path = m.Path,
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ public class TorrentRunner
|
|||
|
||||
if (settingsCopy != null)
|
||||
{
|
||||
settingsCopy.RealDebridApiKey = "*****";
|
||||
settingsCopy.Aria2cSecret = "*****";
|
||||
settingsCopy.Provider.ApiKey = "*****";
|
||||
settingsCopy.DownloadClient.Aria2cSecret = "*****";
|
||||
|
||||
Log(JsonSerializer.Serialize(settingsCopy));
|
||||
}
|
||||
|
|
@ -82,25 +82,25 @@ public class TorrentRunner
|
|||
|
||||
public async Task Tick()
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(Settings.Get.RealDebridApiKey))
|
||||
if (String.IsNullOrWhiteSpace(Settings.Get.Provider.ApiKey))
|
||||
{
|
||||
Log($"No RealDebridApiKey set in settings");
|
||||
return;
|
||||
}
|
||||
|
||||
var settingDownloadLimit = Settings.Get.DownloadLimit;
|
||||
var settingDownloadLimit = Settings.Get.General.DownloadLimit;
|
||||
if (settingDownloadLimit < 1)
|
||||
{
|
||||
settingDownloadLimit = 1;
|
||||
}
|
||||
|
||||
var settingUnpackLimit = Settings.Get.UnpackLimit;
|
||||
var settingUnpackLimit = Settings.Get.General.UnpackLimit;
|
||||
if (settingUnpackLimit < 1)
|
||||
{
|
||||
settingUnpackLimit = 1;
|
||||
}
|
||||
|
||||
var settingDownloadPath = Settings.Get.DownloadPath;
|
||||
var settingDownloadPath = Settings.Get.DownloadClient.DownloadPath;
|
||||
if (String.IsNullOrWhiteSpace(settingDownloadPath))
|
||||
{
|
||||
_logger.LogError("No DownloadPath set in settings");
|
||||
|
|
@ -115,11 +115,11 @@ public class TorrentRunner
|
|||
Log($"TorrentRunner Tick Start, {ActiveDownloadClients.Count} active downloads, {ActiveUnpackClients.Count} active unpacks");
|
||||
}
|
||||
|
||||
if (ActiveDownloadClients.Any(m => m.Value.Type == "Aria2c"))
|
||||
if (ActiveDownloadClients.Any(m => m.Value.Type == Data.Enums.DownloadClient.Aria2c))
|
||||
{
|
||||
Log("Updating Aria2 status");
|
||||
|
||||
var aria2NetClient = new Aria2NetClient(Settings.Get.Aria2cUrl, Settings.Get.Aria2cSecret, _httpClient, 1);
|
||||
var aria2NetClient = new Aria2NetClient(Settings.Get.DownloadClient.Aria2cUrl, Settings.Get.DownloadClient.Aria2cSecret, _httpClient, 1);
|
||||
|
||||
var allDownloads = await aria2NetClient.TellAll();
|
||||
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ public class Torrents
|
|||
_torrentData = torrentData;
|
||||
_downloads = downloads;
|
||||
|
||||
_torrentClient = Settings.Get.Provider switch
|
||||
_torrentClient = Settings.Get.Provider.Provider switch
|
||||
{
|
||||
"RealDebrid" => realDebridTorrentClient,
|
||||
"AllDebrid" => allDebridTorrentClient,
|
||||
Provider.RealDebrid => realDebridTorrentClient,
|
||||
Provider.AllDebrid => allDebridTorrentClient,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
|
@ -329,7 +329,7 @@ public class Torrents
|
|||
|
||||
var profile = new Profile
|
||||
{
|
||||
Provider = Settings.Get.Provider,
|
||||
Provider = Enum.GetName(Settings.Get.Provider.Provider),
|
||||
UserName = user.Username,
|
||||
Expiration = user.Expiration,
|
||||
CurrentVersion = UpdateChecker.CurrentVersion,
|
||||
|
|
@ -354,19 +354,19 @@ public class Torrents
|
|||
var torrent = torrents.FirstOrDefault(m => m.RdId == rdTorrent.Id);
|
||||
|
||||
// Auto import torrents only torrents that have their files selected
|
||||
if (torrent == null && Settings.Get.ProviderAutoImport == 1)
|
||||
if (torrent == null && Settings.Get.Provider.AutoImport)
|
||||
{
|
||||
var newTorrent = new Torrent
|
||||
{
|
||||
Category = Settings.Get.ProviderAutoImportCategory,
|
||||
DownloadAction = TorrentDownloadAction.DownloadManual,
|
||||
FinishedAction = TorrentFinishedAction.None,
|
||||
DownloadMinSize = 0,
|
||||
TorrentRetryAttempts = 0,
|
||||
DownloadRetryAttempts = Settings.Get.DownloadRetryAttempts,
|
||||
DeleteOnError = Settings.Get.DeleteOnError,
|
||||
Lifetime = Settings.Get.TorrentLifetime,
|
||||
Priority = 0,
|
||||
Category = Settings.Get.Provider.Default.Category,
|
||||
DownloadAction = Settings.Get.Provider.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||
FinishedAction = Settings.Get.Provider.Default.FinishedAction,
|
||||
DownloadMinSize = Settings.Get.Provider.Default.MinFileSize,
|
||||
TorrentRetryAttempts = Settings.Get.Provider.Default.TorrentRetryAttempts,
|
||||
DownloadRetryAttempts = Settings.Get.Provider.Default.DownloadRetryAttempts,
|
||||
DeleteOnError = Settings.Get.Provider.Default.DeleteOnError,
|
||||
Lifetime = Settings.Get.Provider.Default.TorrentLifetime,
|
||||
Priority = Settings.Get.Provider.Default.Priority > 0 ? Settings.Get.Provider.Default.Priority : null,
|
||||
RdId = rdTorrent.Id
|
||||
};
|
||||
|
||||
|
|
@ -389,7 +389,7 @@ public class Torrents
|
|||
{
|
||||
var rdTorrent = rdTorrents.FirstOrDefault(m => m.Id == torrent.RdId);
|
||||
|
||||
if (rdTorrent == null && Settings.Get.ProviderAutoDelete == 1)
|
||||
if (rdTorrent == null && Settings.Get.Provider.AutoDelete)
|
||||
{
|
||||
await Delete(torrent.TorrentId, true, false, true);
|
||||
}
|
||||
|
|
@ -574,7 +574,7 @@ public class Torrents
|
|||
|
||||
private static String DownloadPath(Torrent torrent)
|
||||
{
|
||||
var settingDownloadPath = Settings.Get.DownloadPath;
|
||||
var settingDownloadPath = Settings.Get.DownloadClient.DownloadPath;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(torrent.Category))
|
||||
{
|
||||
|
|
@ -624,7 +624,7 @@ public class Torrents
|
|||
|
||||
public async Task RunTorrentComplete(Guid torrentId)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(Settings.Get.RunOnTorrentCompleteFileName))
|
||||
if (String.IsNullOrWhiteSpace(Settings.Get.General.RunOnTorrentCompleteFileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -632,8 +632,8 @@ public class Torrents
|
|||
var torrent = await _torrentData.GetById(torrentId);
|
||||
var downloads = await _downloads.GetForTorrent(torrentId);
|
||||
|
||||
var fileName = Settings.Get.RunOnTorrentCompleteFileName;
|
||||
var arguments = Settings.Get.RunOnTorrentCompleteArguments ?? "";
|
||||
var fileName = Settings.Get.General.RunOnTorrentCompleteFileName;
|
||||
var arguments = Settings.Get.General.RunOnTorrentCompleteArguments ?? "";
|
||||
|
||||
Log($"Parsing external program {fileName} with arguments {arguments}", torrent);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service.Services;
|
||||
|
||||
namespace RdtClient.Web.Controllers;
|
||||
|
|
@ -8,10 +10,12 @@ namespace RdtClient.Web.Controllers;
|
|||
public class AuthController : Controller
|
||||
{
|
||||
private readonly Authentication _authentication;
|
||||
private readonly Settings _settings;
|
||||
|
||||
public AuthController(Authentication authentication)
|
||||
public AuthController(Authentication authentication, Settings settings)
|
||||
{
|
||||
_authentication = authentication;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
|
|
@ -33,7 +37,7 @@ public class AuthController : Controller
|
|||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
[Route("Create")]
|
||||
[HttpPost]
|
||||
|
|
@ -58,6 +62,22 @@ public class AuthController : Controller
|
|||
return Ok();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[Route("SetupProvider")]
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> SetupProvider([FromBody] AuthControllerSetupProviderRequest request)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(Settings.Get.Provider.ApiKey))
|
||||
{
|
||||
return StatusCode(401);
|
||||
}
|
||||
|
||||
await _settings.Update("Provider:Provider", request.Provider);
|
||||
await _settings.Update("Provider:ApiKey", request.Token);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[Route("Login")]
|
||||
[HttpPost]
|
||||
|
|
@ -109,6 +129,12 @@ public class AuthControllerLoginRequest
|
|||
public String Password { get; set; }
|
||||
}
|
||||
|
||||
public class AuthControllerSetupProviderRequest
|
||||
{
|
||||
public Int32 Provider { get; set; }
|
||||
public String Token { get; set; }
|
||||
}
|
||||
|
||||
public class AuthControllerUpdateRequest
|
||||
{
|
||||
public String UserName { get; set; }
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RdtClient.Data.Models.Data;
|
||||
using RdtClient.Data.Models.Internal;
|
||||
using RdtClient.Service.Services;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace RdtClient.Web.Controllers;
|
||||
|
||||
|
|
@ -22,25 +20,18 @@ public class SettingsController : Controller
|
|||
|
||||
[HttpGet]
|
||||
[Route("")]
|
||||
public async Task<ActionResult<IList<Setting>>> Get()
|
||||
public ActionResult Get()
|
||||
{
|
||||
var result = await _settings.GetAll();
|
||||
var result = _settings.GetAll();
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
[Route("")]
|
||||
public async Task<ActionResult> Update([FromBody] SettingsControllerUpdateRequest request)
|
||||
public async Task<ActionResult> Update([FromBody] IList<SettingProperty> settings)
|
||||
{
|
||||
await _settings.Update(request.Settings);
|
||||
|
||||
if (!Enum.TryParse<LogEventLevel>(Settings.Get.LogLevel, out var logLevel))
|
||||
{
|
||||
logLevel = LogEventLevel.Information;
|
||||
}
|
||||
|
||||
Settings.LoggingLevelSwitch.MinimumLevel = logLevel;
|
||||
|
||||
await _settings.Update(settings);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
|
|
@ -89,11 +80,6 @@ public class SettingsController : Controller
|
|||
}
|
||||
}
|
||||
|
||||
public class SettingsControllerUpdateRequest
|
||||
{
|
||||
public IList<Setting> Settings { get; set; }
|
||||
}
|
||||
|
||||
public class SettingsControllerTestPathRequest
|
||||
{
|
||||
public String Path { get; set; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue