Add regex option to include or exclude files.
This commit is contained in:
parent
ed01900f69
commit
b0823f596d
17 changed files with 805 additions and 21 deletions
|
|
@ -31,7 +31,7 @@
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-success" [disabled]="saving" [ngClass]="{ 'is-loading': saving }" (click)="ok()">
|
<button class="button is-success" [disabled]="saving" [ngClass]="{ 'is-loading': saving }" (click)="ok()">
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Downloader</label>
|
<label class="label">Downloader</label>
|
||||||
<div class="control select is-fullwidth">
|
<div class="control select is-fullwidth">
|
||||||
<select [(ngModel)]="downloadClient" (ngModelChange)="setFinishAction() ">
|
<select [(ngModel)]="downloadClient" (ngModelChange)="setFinishAction()">
|
||||||
<option [ngValue]="0">Internal Downloader</option>
|
<option [ngValue]="0">Internal Downloader</option>
|
||||||
<option [ngValue]="1">Aria2c</option>
|
<option [ngValue]="1">Aria2c</option>
|
||||||
<option [ngValue]="2">Symlink Downloader</option>
|
<option [ngValue]="2">Symlink Downloader</option>
|
||||||
|
|
@ -117,6 +117,41 @@
|
||||||
</p>
|
</p>
|
||||||
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p>
|
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Include files</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="field" style="margin-bottom: 0">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<input class="input" type="text" [(ngModel)]="includeRegex" (blur)="verifyRegex()" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help">
|
||||||
|
Select only the files that are matching this regular expression. Only use this setting OR the Exclude files
|
||||||
|
setting, not both.
|
||||||
|
</p>
|
||||||
|
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p>
|
||||||
|
<p class="help is-danger" *ngIf="includeRegexError">{{ includeRegexError }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Exclude files</label>
|
||||||
|
<div class="control">
|
||||||
|
<div class="field" style="margin-bottom: 0">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<input class="input" type="text" [(ngModel)]="excludeRegex" (blur)="verifyRegex()" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="help">
|
||||||
|
Ignore files that are matching this regular expression. Only use this setting OR the Include files setting, not
|
||||||
|
both.
|
||||||
|
</p>
|
||||||
|
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p>
|
||||||
|
<p class="help is-danger" *ngIf="excludeRegexError">{{ excludeRegexError }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Finished action</label>
|
<label class="label">Finished action</label>
|
||||||
<div class="control select is-fullwidth">
|
<div class="control select is-fullwidth">
|
||||||
|
|
@ -210,7 +245,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="field" *ngIf="downloadAction !== 2 && availableFiles !== null">
|
<div class="field" *ngIf="downloadAction !== 2 && availableFiles !== null">
|
||||||
<label class="is-fullwidth-label is-block" *ngFor="let file of availableFiles">
|
<label class="is-fullwidth-label is-block" *ngFor="let file of availableFiles">
|
||||||
{{ file.filename }}
|
<span [ngClass]="{ 'strike-through': isRegexExcluded(file) }">{{ file.filename }}</span>
|
||||||
<span *ngIf="file.filesize > 0">({{ file.filesize | filesize }})</span>
|
<span *ngIf="file.filesize > 0">({{ file.filesize | filesize }})</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,19 @@
|
||||||
|
|
||||||
.separator::before,
|
.separator::before,
|
||||||
.separator::after {
|
.separator::after {
|
||||||
content: '';
|
content: "";
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-bottom: 1px solid darkgray;
|
border-bottom: 1px solid darkgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
.separator:not(:empty)::before {
|
.separator:not(:empty)::before {
|
||||||
margin-right: .25em;
|
margin-right: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.separator:not(:empty)::after {
|
.separator:not(:empty)::after {
|
||||||
margin-left: .25em;
|
margin-left: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.strike-through {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
public downloadAction: number = 0;
|
public downloadAction: number = 0;
|
||||||
public finishedAction: number = 0;
|
public finishedAction: number = 0;
|
||||||
public downloadMinSize: number = 0;
|
public downloadMinSize: number = 0;
|
||||||
|
public includeRegex: string = '';
|
||||||
|
public excludeRegex: string = '';
|
||||||
public torrentRetryAttempts: number = 1;
|
public torrentRetryAttempts: number = 1;
|
||||||
public downloadRetryAttempts: number = 3;
|
public downloadRetryAttempts: number = 3;
|
||||||
public torrentDeleteOnError: number = 0;
|
public torrentDeleteOnError: number = 0;
|
||||||
|
|
@ -35,12 +37,16 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
public saving = false;
|
public saving = false;
|
||||||
public error: string;
|
public error: string;
|
||||||
|
|
||||||
|
public includeRegexError: string;
|
||||||
|
public excludeRegexError: string;
|
||||||
|
public regexSelected: TorrentFileAvailability[];
|
||||||
|
|
||||||
private selectedFile: File;
|
private selectedFile: File;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private torrentService: TorrentService,
|
private torrentService: TorrentService,
|
||||||
private settingsService: SettingsService
|
private settingsService: SettingsService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|
@ -56,6 +62,8 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
settings.first((m) => m.key === 'Gui:Default:OnlyDownloadAvailableFiles')?.value === true ? 1 : 0;
|
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.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.downloadMinSize = settings.first((m) => m.key === 'Gui:Default:MinFileSize')?.value as number;
|
||||||
|
this.includeRegex = settings.first((m) => m.key === 'Gui:Default:IncludeRegex')?.value as string;
|
||||||
|
this.excludeRegex = settings.first((m) => m.key === 'Gui:Default:ExcludeRegex')?.value as string;
|
||||||
this.torrentRetryAttempts = settings.first((m) => m.key === 'Gui:Default:TorrentRetryAttempts')?.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')
|
this.downloadRetryAttempts = settings.first((m) => m.key === 'Gui:Default:DownloadRetryAttempts')
|
||||||
?.value as number;
|
?.value as number;
|
||||||
|
|
@ -68,10 +76,10 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public setFinishAction() {
|
public setFinishAction() {
|
||||||
if (this.downloadClient === 2){
|
if (this.downloadClient === 2) {
|
||||||
if (this.finishedAction === 1){
|
if (this.finishedAction === 1) {
|
||||||
this.finishedAction = 3;
|
this.finishedAction = 3;
|
||||||
} else if (this.finishedAction === 2){
|
} else if (this.finishedAction === 2) {
|
||||||
this.finishedAction = 0;
|
this.finishedAction = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,6 +148,8 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
torrent.downloadAction = this.downloadAction;
|
torrent.downloadAction = this.downloadAction;
|
||||||
torrent.finishedAction = this.finishedAction;
|
torrent.finishedAction = this.finishedAction;
|
||||||
torrent.downloadMinSize = this.downloadMinSize;
|
torrent.downloadMinSize = this.downloadMinSize;
|
||||||
|
torrent.includeRegex = this.includeRegex;
|
||||||
|
torrent.excludeRegex = this.excludeRegex;
|
||||||
torrent.downloadManualFiles = downloadManualFiles;
|
torrent.downloadManualFiles = downloadManualFiles;
|
||||||
torrent.priority = this.priority;
|
torrent.priority = this.priority;
|
||||||
torrent.torrentRetryAttempts = this.torrentRetryAttempts;
|
torrent.torrentRetryAttempts = this.torrentRetryAttempts;
|
||||||
|
|
@ -156,7 +166,7 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
(err) => {
|
(err) => {
|
||||||
this.error = err.error;
|
this.error = err.error;
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
} else if (this.selectedFile) {
|
} else if (this.selectedFile) {
|
||||||
this.torrentService.uploadFile(this.selectedFile, torrent).subscribe(
|
this.torrentService.uploadFile(this.selectedFile, torrent).subscribe(
|
||||||
|
|
@ -166,7 +176,7 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
(err) => {
|
(err) => {
|
||||||
this.error = err.error;
|
this.error = err.error;
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.error = 'No magnet or file uploaded';
|
this.error = 'No magnet or file uploaded';
|
||||||
|
|
@ -204,7 +214,7 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
(err) => {
|
(err) => {
|
||||||
this.error = err.error;
|
this.error = err.error;
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
} else if (this.selectedFile) {
|
} else if (this.selectedFile) {
|
||||||
this.torrentService.checkFiles(this.selectedFile).subscribe(
|
this.torrentService.checkFiles(this.selectedFile).subscribe(
|
||||||
|
|
@ -218,10 +228,34 @@ export class AddNewTorrentComponent implements OnInit {
|
||||||
(err) => {
|
(err) => {
|
||||||
this.error = err.error;
|
this.error = err.error;
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.saving = false;
|
this.saving = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isRegexExcluded(file: TorrentFileAvailability): boolean {
|
||||||
|
if (this.regexSelected == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.regexSelected.find((m) => m.filename === file.filename) == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public verifyRegex(): void {
|
||||||
|
this.includeRegexError = null;
|
||||||
|
this.excludeRegexError = null;
|
||||||
|
this.regexSelected = null;
|
||||||
|
|
||||||
|
this.torrentService.verifyRegex(this.includeRegex, this.excludeRegex, this.magnetLink).subscribe((result) => {
|
||||||
|
this.includeRegexError = result.includeError;
|
||||||
|
this.excludeRegexError = result.excludeError;
|
||||||
|
this.regexSelected = result.selectedFiles;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ export class Torrent {
|
||||||
public downloadAction: number;
|
public downloadAction: number;
|
||||||
public finishedAction: number;
|
public finishedAction: number;
|
||||||
public downloadMinSize: number;
|
public downloadMinSize: number;
|
||||||
|
public includeRegex: string;
|
||||||
|
public excludeRegex: string;
|
||||||
public downloadManualFiles: string;
|
public downloadManualFiles: string;
|
||||||
|
|
||||||
public added: Date;
|
public added: Date;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,10 @@ export class TorrentService {
|
||||||
|
|
||||||
private connection: signalR.HubConnection;
|
private connection: signalR.HubConnection;
|
||||||
|
|
||||||
constructor(private http: HttpClient, @Inject(APP_BASE_HREF) private baseHref: string) {
|
constructor(
|
||||||
|
private http: HttpClient,
|
||||||
|
@Inject(APP_BASE_HREF) private baseHref: string,
|
||||||
|
) {
|
||||||
this.connect();
|
this.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,7 +74,7 @@ export class TorrentService {
|
||||||
torrentId: string,
|
torrentId: string,
|
||||||
deleteData: boolean,
|
deleteData: boolean,
|
||||||
deleteRdTorrent: boolean,
|
deleteRdTorrent: boolean,
|
||||||
deleteLocalFiles: boolean
|
deleteLocalFiles: boolean,
|
||||||
): Observable<void> {
|
): Observable<void> {
|
||||||
return this.http.post<void>(`${this.baseHref}Api/Torrents/Delete/${torrentId}`, {
|
return this.http.post<void>(`${this.baseHref}Api/Torrents/Delete/${torrentId}`, {
|
||||||
deleteData,
|
deleteData,
|
||||||
|
|
@ -91,4 +94,19 @@ export class TorrentService {
|
||||||
public update(torrent: Torrent): Observable<void> {
|
public update(torrent: Torrent): Observable<void> {
|
||||||
return this.http.put<void>(`${this.baseHref}Api/Torrents/Update`, torrent);
|
return this.http.put<void>(`${this.baseHref}Api/Torrents/Update`, torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public verifyRegex(
|
||||||
|
includeRegex: string,
|
||||||
|
excludeRegex: string,
|
||||||
|
magnetLink: string,
|
||||||
|
): Observable<{ includeError: string; excludeError: string; selectedFiles: TorrentFileAvailability[] }> {
|
||||||
|
return this.http.post<{ includeError: string; excludeError: string; selectedFiles: TorrentFileAvailability[] }>(
|
||||||
|
`${this.baseHref}Api/Torrents/VerifyRegex`,
|
||||||
|
{
|
||||||
|
includeRegex,
|
||||||
|
excludeRegex,
|
||||||
|
magnetLink,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,14 @@
|
||||||
<label class="label">Minimum file size to download</label>
|
<label class="label">Minimum file size to download</label>
|
||||||
{{ torrent.downloadMinSize }}MB
|
{{ torrent.downloadMinSize }}MB
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Include files</label>
|
||||||
|
{{ torrent.includeRegex }}
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Exclude files</label>
|
||||||
|
{{ torrent.excludeRegex }}
|
||||||
|
</div>
|
||||||
<div class="field" *ngIf="!torrent.isFile">
|
<div class="field" *ngIf="!torrent.isFile">
|
||||||
<label class="label">Magnet</label>
|
<label class="label">Magnet</label>
|
||||||
<span [cdkCopyToClipboard]="torrent.fileOrMagnet" (click)="copied = true" *ngIf="!copied"
|
<span [cdkCopyToClipboard]="torrent.fileOrMagnet" (click)="copied = true" *ngIf="!copied"
|
||||||
|
|
@ -477,7 +485,10 @@
|
||||||
<button class="delete" aria-label="close" (click)="updateSettingsCancel()"></button>
|
<button class="delete" aria-label="close" (click)="updateSettingsCancel()"></button>
|
||||||
</header>
|
</header>
|
||||||
<section class="modal-card-body">
|
<section class="modal-card-body">
|
||||||
<p>Settings that are blank do not have the same values for each torrent. Updating a setting with a blank value will not update it.</p>
|
<p>
|
||||||
|
Settings that are blank do not have the same values for each torrent. Updating a setting with a blank value will
|
||||||
|
not update it.
|
||||||
|
</p>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Downloader</label>
|
<label class="label">Downloader</label>
|
||||||
<div class="control select is-fullwidth">
|
<div class="control select is-fullwidth">
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,8 @@ public class TorrentData
|
||||||
DownloadAction = torrent.DownloadAction,
|
DownloadAction = torrent.DownloadAction,
|
||||||
FinishedAction = torrent.FinishedAction,
|
FinishedAction = torrent.FinishedAction,
|
||||||
DownloadMinSize = torrent.DownloadMinSize,
|
DownloadMinSize = torrent.DownloadMinSize,
|
||||||
|
IncludeRegex = torrent.IncludeRegex,
|
||||||
|
ExcludeRegex = torrent.ExcludeRegex,
|
||||||
DownloadManualFiles = torrent.DownloadManualFiles,
|
DownloadManualFiles = torrent.DownloadManualFiles,
|
||||||
DownloadClient = downloadClient,
|
DownloadClient = downloadClient,
|
||||||
FileOrMagnet = fileOrMagnetContents,
|
FileOrMagnet = fileOrMagnetContents,
|
||||||
|
|
|
||||||
473
server/RdtClient.Data/Migrations/20240213025207_Torrent_Add_Include_Exclude.Designer.cs
generated
Normal file
473
server/RdtClient.Data/Migrations/20240213025207_Torrent_Add_Include_Exclude.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,473 @@
|
||||||
|
// <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("20240213025207_Torrent_Add_Include_Exclude")]
|
||||||
|
partial class Torrent_Add_Include_Exclude
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||||
|
|
||||||
|
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")
|
||||||
|
.IsRequired()
|
||||||
|
.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<int>("DownloadClient")
|
||||||
|
.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>("ExcludeRegex")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("FileOrMagnet")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("FilesSelected")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("FinishedAction")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Hash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("HostDownloadAction")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("IncludeRegex")
|
||||||
|
.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,38 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace RdtClient.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Torrent_Add_Include_Exclude : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ExcludeRegex",
|
||||||
|
table: "Torrents",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "IncludeRegex",
|
||||||
|
table: "Torrents",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ExcludeRegex",
|
||||||
|
table: "Torrents");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "IncludeRegex",
|
||||||
|
table: "Torrents");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ namespace RdtClient.Data.Migrations
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.1");
|
||||||
|
|
||||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
{
|
{
|
||||||
|
|
@ -314,6 +314,9 @@ namespace RdtClient.Data.Migrations
|
||||||
b.Property<string>("Error")
|
b.Property<string>("Error")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ExcludeRegex")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("FileOrMagnet")
|
b.Property<string>("FileOrMagnet")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
|
@ -330,6 +333,9 @@ namespace RdtClient.Data.Migrations
|
||||||
b.Property<int>("HostDownloadAction")
|
b.Property<int>("HostDownloadAction")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("IncludeRegex")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<bool>("IsFile")
|
b.Property<bool>("IsFile")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ public class Torrent
|
||||||
public TorrentFinishedAction FinishedAction { get; set; }
|
public TorrentFinishedAction FinishedAction { get; set; }
|
||||||
public TorrentHostDownloadAction HostDownloadAction { get; set; }
|
public TorrentHostDownloadAction HostDownloadAction { get; set; }
|
||||||
public Int32 DownloadMinSize { get; set; }
|
public Int32 DownloadMinSize { get; set; }
|
||||||
|
public String? IncludeRegex { get; set; }
|
||||||
|
public String? ExcludeRegex { get; set; }
|
||||||
public String? DownloadManualFiles { get; set; }
|
public String? DownloadManualFiles { get; set; }
|
||||||
public DownloadClient DownloadClient { get; set; }
|
public DownloadClient DownloadClient { get; set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -225,6 +225,14 @@ public class DbSettingsDefaults
|
||||||
[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.")]
|
[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;
|
public Int32 MinFileSize { get; set; } = 0;
|
||||||
|
|
||||||
|
[DisplayName("Include files")]
|
||||||
|
[Description("Select only the files that are matching this regular expression. Only use this setting OR the Exclude files setting, not both.")]
|
||||||
|
public String? IncludeRegex { get; set; }
|
||||||
|
|
||||||
|
[DisplayName("Exclude files")]
|
||||||
|
[Description("Ignore files that are matching this regular expression. Only use this setting OR the Include files setting, not both.")]
|
||||||
|
public String? ExcludeRegex { get; set; }
|
||||||
|
|
||||||
[DisplayName("Automatic retry torrent")]
|
[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.")]
|
[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;
|
public Int32 TorrentRetryAttempts { get; set; } = 1;
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ public class WatchFolderChecker : BackgroundService
|
||||||
: TorrentDownloadAction.DownloadAll,
|
: TorrentDownloadAction.DownloadAll,
|
||||||
FinishedAction = Settings.Get.Watch.Default.FinishedAction,
|
FinishedAction = Settings.Get.Watch.Default.FinishedAction,
|
||||||
DownloadMinSize = Settings.Get.Watch.Default.MinFileSize,
|
DownloadMinSize = Settings.Get.Watch.Default.MinFileSize,
|
||||||
|
IncludeRegex = Settings.Get.Watch.Default.IncludeRegex,
|
||||||
|
ExcludeRegex = Settings.Get.Watch.Default.ExcludeRegex,
|
||||||
TorrentRetryAttempts = Settings.Get.Watch.Default.TorrentRetryAttempts,
|
TorrentRetryAttempts = Settings.Get.Watch.Default.TorrentRetryAttempts,
|
||||||
DownloadRetryAttempts = Settings.Get.Watch.Default.DownloadRetryAttempts,
|
DownloadRetryAttempts = Settings.Get.Watch.Default.DownloadRetryAttempts,
|
||||||
DeleteOnError = Settings.Get.Watch.Default.DeleteOnError,
|
DeleteOnError = Settings.Get.Watch.Default.DeleteOnError,
|
||||||
|
|
|
||||||
|
|
@ -461,6 +461,8 @@ public class QBittorrent
|
||||||
DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||||
FinishedAction = TorrentFinishedAction.None,
|
FinishedAction = TorrentFinishedAction.None,
|
||||||
DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize,
|
DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize,
|
||||||
|
IncludeRegex = Settings.Get.Integrations.Default.IncludeRegex,
|
||||||
|
ExcludeRegex = Settings.Get.Integrations.Default.ExcludeRegex,
|
||||||
TorrentRetryAttempts = Settings.Get.Integrations.Default.TorrentRetryAttempts,
|
TorrentRetryAttempts = Settings.Get.Integrations.Default.TorrentRetryAttempts,
|
||||||
DownloadRetryAttempts = Settings.Get.Integrations.Default.DownloadRetryAttempts,
|
DownloadRetryAttempts = Settings.Get.Integrations.Default.DownloadRetryAttempts,
|
||||||
DeleteOnError = Settings.Get.Integrations.Default.DeleteOnError,
|
DeleteOnError = Settings.Get.Integrations.Default.DeleteOnError,
|
||||||
|
|
@ -483,6 +485,8 @@ public class QBittorrent
|
||||||
DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
DownloadAction = Settings.Get.Integrations.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||||
FinishedAction = TorrentFinishedAction.None,
|
FinishedAction = TorrentFinishedAction.None,
|
||||||
DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize,
|
DownloadMinSize = Settings.Get.Integrations.Default.MinFileSize,
|
||||||
|
IncludeRegex = Settings.Get.Integrations.Default.IncludeRegex,
|
||||||
|
ExcludeRegex = Settings.Get.Integrations.Default.ExcludeRegex,
|
||||||
TorrentRetryAttempts = Settings.Get.Integrations.Default.TorrentRetryAttempts,
|
TorrentRetryAttempts = Settings.Get.Integrations.Default.TorrentRetryAttempts,
|
||||||
DownloadRetryAttempts = Settings.Get.Integrations.Default.DownloadRetryAttempts,
|
DownloadRetryAttempts = Settings.Get.Integrations.Default.DownloadRetryAttempts,
|
||||||
DeleteOnError = Settings.Get.Integrations.Default.DeleteOnError,
|
DeleteOnError = Settings.Get.Integrations.Default.DeleteOnError,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.Extensions.Logging;
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using RDNET;
|
using RDNET;
|
||||||
using RdtClient.Data.Enums;
|
using RdtClient.Data.Enums;
|
||||||
|
|
@ -202,6 +203,51 @@ public class RealDebridTorrentClient : ITorrentClient
|
||||||
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
|
Log($"Found {files.Count} files that match the minimum file size criterea", torrent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!String.IsNullOrWhiteSpace(torrent.IncludeRegex))
|
||||||
|
{
|
||||||
|
Log($"Using regular expression {torrent.IncludeRegex} to include only files matching this regex", torrent);
|
||||||
|
|
||||||
|
var newFiles = new List<TorrentClientFile>();
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (Regex.IsMatch(file.Path, torrent.IncludeRegex))
|
||||||
|
{
|
||||||
|
Log($"* Including {file.Path}", torrent);
|
||||||
|
newFiles.Add(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"* Excluding {file.Path}", torrent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files = newFiles;
|
||||||
|
|
||||||
|
Log($"Found {files.Count} files that match the regex", torrent);
|
||||||
|
}
|
||||||
|
else if (!String.IsNullOrWhiteSpace(torrent.ExcludeRegex))
|
||||||
|
{
|
||||||
|
Log($"Using regular expression {torrent.IncludeRegex} to ignore files matching this regex", torrent);
|
||||||
|
|
||||||
|
var newFiles = new List<TorrentClientFile>();
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (!Regex.IsMatch(file.Path, torrent.ExcludeRegex))
|
||||||
|
{
|
||||||
|
Log($"* Including {file.Path}", torrent);
|
||||||
|
newFiles.Add(file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log($"* Excluding {file.Path}", torrent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files = newFiles;
|
||||||
|
|
||||||
|
Log($"Found {files.Count} files that match the regex", torrent);
|
||||||
|
}
|
||||||
|
|
||||||
if (files.Count == 0)
|
if (files.Count == 0)
|
||||||
{
|
{
|
||||||
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
|
Log($"Filtered all files out! Downloading ALL files instead!", torrent);
|
||||||
|
|
|
||||||
|
|
@ -425,6 +425,8 @@ public class Torrents
|
||||||
DownloadAction = Settings.Get.Provider.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
DownloadAction = Settings.Get.Provider.Default.OnlyDownloadAvailableFiles ? TorrentDownloadAction.DownloadAvailableFiles : TorrentDownloadAction.DownloadAll,
|
||||||
FinishedAction = Settings.Get.Provider.Default.FinishedAction,
|
FinishedAction = Settings.Get.Provider.Default.FinishedAction,
|
||||||
DownloadMinSize = Settings.Get.Provider.Default.MinFileSize,
|
DownloadMinSize = Settings.Get.Provider.Default.MinFileSize,
|
||||||
|
IncludeRegex = Settings.Get.Provider.Default.IncludeRegex,
|
||||||
|
ExcludeRegex = Settings.Get.Provider.Default.ExcludeRegex,
|
||||||
TorrentRetryAttempts = Settings.Get.Provider.Default.TorrentRetryAttempts,
|
TorrentRetryAttempts = Settings.Get.Provider.Default.TorrentRetryAttempts,
|
||||||
DownloadRetryAttempts = Settings.Get.Provider.Default.DownloadRetryAttempts,
|
DownloadRetryAttempts = Settings.Get.Provider.Default.DownloadRetryAttempts,
|
||||||
DeleteOnError = Settings.Get.Provider.Default.DeleteOnError,
|
DeleteOnError = Settings.Get.Provider.Default.DeleteOnError,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MonoTorrent;
|
using MonoTorrent;
|
||||||
|
using RdtClient.Data.Models.TorrentClient;
|
||||||
using RdtClient.Service.Helpers;
|
using RdtClient.Service.Helpers;
|
||||||
using RdtClient.Service.Services;
|
using RdtClient.Service.Services;
|
||||||
using Torrent = RdtClient.Data.Models.Data.Torrent;
|
using Torrent = RdtClient.Data.Models.Data.Torrent;
|
||||||
|
|
@ -216,6 +218,94 @@ public class TorrentsController : Controller
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Route("VerifyRegex")]
|
||||||
|
public async Task<ActionResult> VerifyRegex([FromForm] IFormFile? file, [FromBody] TorrentControllerVerifyRegexRequest? request)
|
||||||
|
{
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
var includeError = "";
|
||||||
|
var excludeError = "";
|
||||||
|
|
||||||
|
IList<TorrentClientAvailableFile> availableFiles;
|
||||||
|
|
||||||
|
if (!String.IsNullOrWhiteSpace(request.MagnetLink))
|
||||||
|
{
|
||||||
|
var magnet = MagnetLink.Parse(request.MagnetLink);
|
||||||
|
|
||||||
|
availableFiles = await _torrents.GetAvailableFiles(magnet.InfoHash.ToHex());
|
||||||
|
}
|
||||||
|
else if (file != null)
|
||||||
|
{
|
||||||
|
var fileStream = file.OpenReadStream();
|
||||||
|
|
||||||
|
await using var memoryStream = new MemoryStream();
|
||||||
|
|
||||||
|
await fileStream.CopyToAsync(memoryStream);
|
||||||
|
|
||||||
|
var bytes = memoryStream.ToArray();
|
||||||
|
|
||||||
|
var torrent = await MonoTorrent.Torrent.LoadAsync(bytes);
|
||||||
|
|
||||||
|
availableFiles = await _torrents.GetAvailableFiles(torrent.InfoHash.ToHex());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedFiles = new List<TorrentClientAvailableFile>();
|
||||||
|
|
||||||
|
if (!String.IsNullOrWhiteSpace(request.IncludeRegex))
|
||||||
|
{
|
||||||
|
foreach (var availableFile in availableFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Regex.IsMatch(availableFile.Filename, request.IncludeRegex))
|
||||||
|
{
|
||||||
|
selectedFiles.Add(availableFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
includeError = ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!String.IsNullOrWhiteSpace(request.ExcludeRegex))
|
||||||
|
{
|
||||||
|
foreach (var availableFile in availableFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Regex.IsMatch(availableFile.Filename, request.ExcludeRegex))
|
||||||
|
{
|
||||||
|
selectedFiles.Add(availableFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
excludeError = ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectedFiles = availableFiles.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(new
|
||||||
|
{
|
||||||
|
includeError,
|
||||||
|
excludeError,
|
||||||
|
selectedFiles
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TorrentControllerUploadFileRequest
|
public class TorrentControllerUploadFileRequest
|
||||||
|
|
@ -239,4 +329,11 @@ public class TorrentControllerDeleteRequest
|
||||||
public class TorrentControllerCheckFilesRequest
|
public class TorrentControllerCheckFilesRequest
|
||||||
{
|
{
|
||||||
public String? MagnetLink { get; set; }
|
public String? MagnetLink { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TorrentControllerVerifyRegexRequest
|
||||||
|
{
|
||||||
|
public String? IncludeRegex { get; set; }
|
||||||
|
public String? ExcludeRegex { get; set; }
|
||||||
|
public String? MagnetLink { get; set;}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue