Merge pull request #818 from Cucumberrbob/feat/angular-control-flow

migrate to new Angular Control Flow syntax
This commit is contained in:
Roger Far 2025-05-18 11:13:50 -06:00 committed by GitHub
commit 772816ced0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 899 additions and 760 deletions

View file

@ -84,11 +84,15 @@
</div> </div>
</div> </div>
</div> </div>
<p class="help" *ngIf="provider === 'AllDebrid'"> @if (provider === "AllDebrid") {
<p class="help">
When downloading with AllDebrid it cannot be guaranteed that only files above this limit will be download as When downloading with AllDebrid it cannot be guaranteed that only files above this limit will be download as
some files are grouped together in 1 large archive. some files are grouped together in 1 large archive.
</p> </p>
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p> }
@if (downloadAction === 2) {
<p class="help">This setting does not apply to manually selected files.</p>
}
</div> </div>
<div class="field"> <div class="field">
@ -104,8 +108,12 @@
Select only the files that are matching this regular expression. Only use this setting OR the Exclude files Select only the files that are matching this regular expression. Only use this setting OR the Exclude files
setting, not both. setting, not both.
</p> </p>
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p> @if (downloadAction === 2) {
<p class="help is-danger" *ngIf="includeRegexError">{{ includeRegexError }}</p> <p class="help">This setting does not apply to manually selected files.</p>
}
@if (includeRegexError) {
<p class="help is-danger">{{ includeRegexError }}</p>
}
</div> </div>
<div class="field"> <div class="field">
@ -121,8 +129,12 @@
Ignore files that are matching this regular expression. Only use this setting OR the Include files setting, not Ignore files that are matching this regular expression. Only use this setting OR the Include files setting, not
both. both.
</p> </p>
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p> @if (downloadAction === 2) {
<p class="help is-danger" *ngIf="excludeRegexError">{{ excludeRegexError }}</p> <p class="help">This setting does not apply to manually selected files.</p>
}
@if (excludeRegexError) {
<p class="help is-danger">{{ excludeRegexError }}</p>
}
</div> </div>
<div class="field"> <div class="field">
@ -130,8 +142,10 @@
<div class="control select is-fullwidth"> <div class="control select is-fullwidth">
<select [(ngModel)]="finishedAction"> <select [(ngModel)]="finishedAction">
<option [ngValue]="0">Do nothing</option> <option [ngValue]="0">Do nothing</option>
<option [ngValue]="1" *ngIf="downloadClient !== 2">Remove torrent from provider and client</option> @if (downloadClient !== 2) {
<option [ngValue]="2" *ngIf="downloadClient !== 2">Remove torrent from provider</option> <option [ngValue]="1">Remove torrent from provider and client</option>
<option [ngValue]="2">Remove torrent from provider</option>
}
<option [ngValue]="3">Remove torrent from client</option> <option [ngValue]="3">Remove torrent from client</option>
</select> </select>
</div> </div>
@ -196,7 +210,9 @@
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<div class="notification is-danger is-light" *ngIf="error">{{ error }}</div> @if (error) {
<div class="notification is-danger is-light">{{ error }}</div>
}
</div> </div>
</div> </div>

View file

@ -41,9 +41,11 @@
Login Login
</button> </button>
</div> </div>
<div class="notification is-danger is-light" *ngIf="error?.length > 0"> @if (error?.length > 0) {
<div class="notification is-danger is-light">
{{ error }} {{ error }}
</div> </div>
}
</form> </form>
</div> </div>
</div> </div>

View file

@ -38,13 +38,18 @@
</span> </span>
<span>Settings</span> <span>Settings</span>
</a> </a>
<a class="navbar-item" *ngIf="profile" href="{{ providerLink }}" target="_blank" rel="noopener"> @if (profile) {
<a class="navbar-item" href="{{ providerLink }}" target="_blank" rel="noopener">
<span class="icon"> <span class="icon">
<i class="fas fa-euro-sign" aria-hidden="true"></i> <i class="fas fa-euro-sign" aria-hidden="true"></i>
</span> </span>
<span *ngIf="profile.expiration">Premium Status: {{ profile.expiration | date }}</span> @if (profile.expiration) {
<span *ngIf="!profile.expiration" class="no-premium">Not premium</span> <span>Premium Status: {{ profile.expiration | date }}</span>
} @else {
<span class="no-premium">Not premium</span>
}
</a> </a>
}
</div> </div>
<div class="navbar-end"> <div class="navbar-end">
@ -61,19 +66,20 @@
</div> </div>
</div> </div>
</nav> </nav>
<div
class="notification" @if (
[ngClass]="profile.isInsecure ? 'is-danger' : 'is-warning'"
*ngIf="
profile && profile &&
(!profile.disableUpdateNotification || profile.isInsecure) && (!profile.disableUpdateNotification || profile.isInsecure) &&
profile.latestVersion && profile.latestVersion &&
profile.currentVersion !== profile.latestVersion profile.currentVersion !== profile.latestVersion
" ) {
> <div class="notification" [ngClass]="profile.isInsecure ? 'is-danger' : 'is-warning'">
<span *ngIf="profile.isInsecure"> Your current version is insecure. </span> @if (profile.isInsecure) {
<span> Your current version is insecure. </span>
}
<span> <span>
Version {{ profile.latestVersion }} of RealDebrid Client was found. You are currently on version Version {{ profile.latestVersion }} of RealDebrid Client was found. You are currently on version
{{ profile.currentVersion }}. {{ profile.currentVersion }}.
</span> </span>
</div> </div>
}

View file

@ -19,13 +19,17 @@
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<div class="notification is-danger is-light" *ngIf="error !== null">Error saving: {{ error }}</div> @if (error !== null) {
<div class="notification is-danger is-light">Error saving: {{ error }}</div>
}
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<div class="notification is-success is-light" *ngIf="success">Your profile has been updated</div> @if (success) {
<div class="notification is-success is-light">Your profile has been updated</div>
}
</div> </div>
</div> </div>

View file

@ -1,45 +1,60 @@
<div class="tabs"> <div class="tabs">
<ul> <ul>
<li [ngClass]="{ 'is-active': activeTab === i }" (click)="activeTab = i" *ngFor="let tab of tabs; let i = index"> @for (tab of tabs; track tab.key; let i = $index) {
<li [ngClass]="{ 'is-active': activeTab === i }" (click)="activeTab = i">
<a>{{ tab.displayName }}</a> <a>{{ tab.displayName }}</a>
</li> </li>
}
<li [ngClass]="{ 'is-active': activeTab === 99 }" (click)="activeTab = 99"> <li [ngClass]="{ 'is-active': activeTab === 99 }" (click)="activeTab = 99">
<a>Speed Tests</a> <a>Speed Tests</a>
</li> </li>
</ul> </ul>
</div> </div>
<div *ngFor="let tab of tabs; let i = index" [hidden]="activeTab !== i"> @for (tab of tabs; track tab.key; let i = $index) {
<div [hidden]="activeTab !== i">
<h3>{{ tab.description }}</h3> <h3>{{ tab.description }}</h3>
<ng-container *ngFor="let setting of tab.settings"> @for (setting of tab.settings; track setting.key) {
<h3 *ngIf="setting.type === 'Object'" class="title is-3" style="margin-top: 1.2rem">{{ setting.displayName }}</h3> @if (setting.type === "Object") {
<h3 class="title is-3" style="margin-top: 1.2rem">{{ setting.displayName }}</h3>
}
<div class="field"> <div class="field">
<label class="label" *ngIf="setting.type !== 'Boolean' && setting.type !== 'Object'">{{ @if (setting.type !== "Boolean" && setting.type !== "Object") {
setting.displayName <label class="label">{{ setting.displayName }}</label>
}}</label> }
<ng-container [ngSwitch]="setting.type"> @switch (setting.type) {
<div class="control" *ngSwitchCase="'String'"> @case ("String") {
<div class="control">
<input class="input" type="text" [(ngModel)]="setting.value" /> <input class="input" type="text" [(ngModel)]="setting.value" />
</div> </div>
<div class="control" *ngSwitchCase="'Int32'"> }
@case ("Int32") {
<div class="control">
<input class="input" type="number" [(ngModel)]="setting.value" /> <input class="input" type="number" [(ngModel)]="setting.value" />
</div> </div>
<label class="checkbox" *ngSwitchCase="'Boolean'"> }
@case ("Boolean") {
<label class="checkbox">
<input type="checkbox" [(ngModel)]="setting.value" /> <input type="checkbox" [(ngModel)]="setting.value" />
{{ setting.displayName }} {{ setting.displayName }}
</label> </label>
<div class="control select is-fullwidth" *ngSwitchCase="'Enum'"> }
@case ("Enum") {
<div class="control select is-fullwidth">
<select [(ngModel)]="setting.value"> <select [(ngModel)]="setting.value">
<option [value]="kvp.key" *ngFor="let kvp of setting.enumValues | keyvalue">{{ kvp.value }}</option> @for (kvp of setting.enumValues | keyvalue; track kvp) {
<option [value]="kvp.key">{{ kvp.value }}</option>
}
</select> </select>
</div> </div>
<ng-container *ngSwitchCase="'Object'"></ng-container> }
<div class="control" *ngSwitchDefault>Invalid setting type {{ setting.type }}</div> @case ("Object") {}
</ng-container> @default {
<div class="control">Invalid setting type {{ setting.type }}</div>
}
}
<p class="help" [innerHtml]="setting.description | nl2br"></p> <p class="help" [innerHtml]="setting.description | nl2br"></p>
@if (setting.key === "DownloadClient:Aria2cSecret") {
<ng-container *ngIf="setting.key === 'DownloadClient:Aria2cSecret'">
<button <button
class="button is-warning" class="button is-warning"
(click)="testAria2cConnection()" (click)="testAria2cConnection()"
@ -48,100 +63,111 @@
> >
Test aria2 connection Test aria2 connection
</button> </button>
<div class="notification is-danger is-light" style="margin-top: 1rem" *ngIf="testAria2cConnectionError"> @if (testAria2cConnectionError) {
<div class="notification is-danger is-light" style="margin-top: 1rem">
Could connect to Aria2 client<br /> Could connect to Aria2 client<br />
{{ testAria2cConnectionError }} {{ testAria2cConnectionError }}
</div> </div>
}
<div class="notification is-success is-light" style="margin-top: 1rem" *ngIf="testAria2cConnectionSuccess"> @if (testAria2cConnectionSuccess) {
<div class="notification is-success is-light" style="margin-top: 1rem">
Found Aria2 client version {{ testAria2cConnectionSuccess }} Found Aria2 client version {{ testAria2cConnectionSuccess }}
</div> </div>
</ng-container> }
}
</div> </div>
</ng-container> }
</div> </div>
}
<div *ngIf="activeTab === 99"> @if (activeTab === 99) {
<div>
<div class="field"> <div class="field">
<label class="label">Test download path permissions</label> <label class="label">Test download path permissions</label>
<div class="control"> <div class="control">
@if (!testPathError && !testPathSuccess) {
<button <button
class="button is-warning" class="button is-warning"
(click)="testDownloadPath()" (click)="testDownloadPath()"
[disabled]="saving" [disabled]="saving"
[ngClass]="{ 'is-loading': saving }" [ngClass]="{ 'is-loading': saving }"
*ngIf="!testPathError && !testPathSuccess"
> >
Test permissions Test permissions
</button> </button>
<div class="notification is-danger is-light" *ngIf="testPathError"> }
@if (testPathError) {
<div class="notification is-danger is-light">
Could not test your download path<br /> Could not test your download path<br />
{{ testPathError }} {{ testPathError }}
</div> </div>
}
<div class="notification is-success is-light" *ngIf="testPathSuccess">Your download path looks good!</div> @if (testPathSuccess) {
<div class="notification is-success is-light">Your download path looks good!</div>
}
</div> </div>
<div class="help">This will check if the download folder has write permissions.</div> <div class="help">This will check if the download folder has write permissions.</div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">Test Real-Debrid download speed</label> <label class="label">Test Real-Debrid download speed</label>
<div class="control"> <div class="control">
@if (testDownloadSpeedError) {
<div class="notification is-danger is-light">
Could not test your download speed<br />
{{ testDownloadSpeedError }}
</div>
} @else if (testDownloadSpeedSuccess) {
<div class="notification is-success is-light">Download speed {{ testDownloadSpeedSuccess | filesize }}/s</div>
} @else {
<button <button
class="button is-warning" class="button is-warning"
(click)="testDownloadSpeed()" (click)="testDownloadSpeed()"
[disabled]="saving" [disabled]="saving"
[ngClass]="{ 'is-loading': saving }" [ngClass]="{ 'is-loading': saving }"
*ngIf="!testDownloadSpeedError && !testDownloadSpeedSuccess"
> >
Test download speed Test download speed
</button> </button>
<div class="notification is-danger is-light" *ngIf="testDownloadSpeedError"> }
Could not test your download speed<br />
{{ testDownloadSpeedError }}
</div>
<div class="notification is-success is-light" *ngIf="testDownloadSpeedSuccess">
Download speed {{ testDownloadSpeedSuccess | filesize }}/s
</div>
</div> </div>
<div class="help"> <div class="help">
This will attempt to download a 10GB file from Real-Debrid. When 50MB has been downloaded the test will stop. This will attempt to download a 10GB file from Real-Debrid. When 50MB has been downloaded the test will stop.
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">Test download folder write speed</label> <label class="label">Test download folder write speed</label>
<div class="control"> <div class="control">
@if (testWriteSpeedError) {
<div class="notification is-danger is-light">
Could not test your download speed<br />
{{ testWriteSpeedError }}
</div>
} @else if (testWriteSpeedSuccess) {
<div class="notification is-success is-light">Write speed {{ testWriteSpeedSuccess | filesize }}/s</div>
} @else {
<button <button
class="button is-warning" class="button is-warning"
(click)="testWriteSpeed()" (click)="testWriteSpeed()"
[disabled]="saving" [disabled]="saving"
[ngClass]="{ 'is-loading': saving }" [ngClass]="{ 'is-loading': saving }"
*ngIf="!testWriteSpeedError && !testWriteSpeedSuccess"
> >
Test write speed Test write speed
</button> </button>
<div class="notification is-danger is-light" *ngIf="testWriteSpeedError"> }
Could not test your download speed<br />
{{ testWriteSpeedError }}
</div>
<div class="notification is-success is-light" *ngIf="testWriteSpeedSuccess">
Write speed {{ testWriteSpeedSuccess | filesize }}/s
</div>
</div> </div>
<div class="help">This will write a small file to your download folder to see how fast it can write to it.</div> <div class="help">This will write a small file to your download folder to see how fast it can write to it.</div>
</div> </div>
</div> </div>
}
<div class="field"> <div class="field">
<div class="control"> <div class="control">
<div class="notification is-danger is-light" *ngIf="error?.length > 0">Error saving settings: {{ error }}</div> @if (error?.length > 0) {
<div class="notification is-danger is-light">Error saving settings: {{ error }}</div>
}
</div> </div>
</div> </div>
<div class="field" *ngIf="activeTab === 0"> @if (activeTab === 0) {
<div class="field">
<label class="label">Register client as magnet link handler</label> <label class="label">Register client as magnet link handler</label>
<div class="control"> <div class="control">
<button <button
@ -157,11 +183,15 @@
</div> </div>
<p class="help"> <p class="help">
@if (canRegisterMagnetHandler) { @if (canRegisterMagnetHandler) {
This will attempt to register the client as your browser's default handler for magnet links and automatically open This will attempt to register the client as your browser's default handler for magnet links and automatically
them in the new torrent screen for downloading. open them in the new torrent screen for downloading.
} @else { } @else {
Magnet link registration is unavailable. Your Magnet link registration is unavailable. Your
<a href="https://caniuse.com/mdn-api_navigator_registerprotocolhandler" target="_blank" rel="noopener noreferrer"> <a
href="https://caniuse.com/mdn-api_navigator_registerprotocolhandler"
target="_blank"
rel="noopener noreferrer"
>
browser may not support this feature browser may not support this feature
</a> </a>
, or the client is not being served in a , or the client is not being served in a
@ -176,11 +206,14 @@
} }
</p> </p>
</div> </div>
}
<div class="field" *ngIf="activeTab < 99"> @if (activeTab < 99) {
<div class="field">
<div class="control"> <div class="control">
<button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }"> <button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
Save Settings Save Settings
</button> </button>
</div> </div>
</div> </div>
}

View file

@ -4,7 +4,8 @@
<div class="columns is-centered"> <div class="columns is-centered">
<div class="column is-5-tablet is-5-desktop is-5-widescreen"> <div class="column is-5-tablet is-5-desktop is-5-widescreen">
<img src="assets/logo.png" /> <img src="assets/logo.png" />
<div class="box" *ngIf="step === 1"> @if (step === 1) {
<div class="box">
<div class="notification is-primary"> <div class="notification is-primary">
Welcome to Real-Debrid Client. Please create your account by entering a username and password. Welcome to Real-Debrid Client. Please create your account by entering a username and password.
</div> </div>
@ -32,13 +33,17 @@
Setup Setup
</button> </button>
</div> </div>
<div class="notification is-danger is-light" *ngIf="error?.length > 0"> @if (error?.length > 0) {
<div class="notification is-danger is-light">
{{ error }} {{ error }}
</div> </div>
}
</form> </form>
</div> </div>
}
<div class="box" *ngIf="step === 2"> @if (step === 2) {
<div class="box">
<div class="notification is-primary"> <div class="notification is-primary">
To be able to use the Real-Debrid Client you need a premium subscription to download torrents. To be able to use the Real-Debrid Client you need a premium subscription to download torrents.
<br /><br /> <br /><br />
@ -80,36 +85,46 @@
<div class="control"> <div class="control">
<input type="text" placeholder="" class="input" name="token" [(ngModel)]="token" required /> <input type="text" placeholder="" class="input" name="token" [(ngModel)]="token" required />
</div> </div>
<p class="help" *ngIf="provider === 0"> @if (provider === 0) {
<p class="help">
You can find your API key here: You can find your API key here:
<a href="https://real-debrid.com/apitoken" target="_blank" rel="noopener" <a href="https://real-debrid.com/apitoken" target="_blank" rel="noopener"
>https://real-debrid.com/apitoken</a >https://real-debrid.com/apitoken</a
>. >.
</p> </p>
<p class="help" *ngIf="provider === 1"> }
@if (provider === 1) {
<p class="help">
You can find your API key here: You can find your API key here:
<a href="https://alldebrid.com/apikeys/" target="_blank" rel="noopener" <a href="https://alldebrid.com/apikeys/" target="_blank" rel="noopener"
>https://alldebrid.com/apikeys/</a >https://alldebrid.com/apikeys/</a
>. >.
</p> </p>
<p class="help" *ngIf="provider === 2"> }
@if (provider === 2) {
<p class="help">
You can find your API key here: You can find your API key here:
<a href="https://www.premiumize.me/account" target="_blank" rel="noopener" <a href="https://www.premiumize.me/account" target="_blank" rel="noopener"
>https://www.premiumize.me/account</a >https://www.premiumize.me/account</a
>. >.
</p> </p>
<p class="help" *ngIf="provider === 3"> }
@if (provider === 3) {
<p class="help">
You can find your API key here: You can find your API key here:
<a href="https://torbox.app/settings" target="_blank" rel="noopener">https://torbox.app/settings</a>. <a href="https://torbox.app/settings" target="_blank" rel="noopener">https://torbox.app/settings</a
>.
</p> </p>
<p class="help" *ngIf="provider === 4"> }
@if (provider === 4) {
<p class="help">
You can find your API key here: You can find your API key here:
<a href="https://debrid-link.com/webapp/apikey" target="_blank" rel="noopener" <a href="https://debrid-link.com/webapp/apikey" target="_blank" rel="noopener"
>https://debrid-link.com/webapp/apikey</a >https://debrid-link.com/webapp/apikey</a
>. >.
</p> </p>
}
</div> </div>
<div class="field"> <div class="field">
<button <button
class="button is-success" class="button is-success"
@ -120,12 +135,16 @@
Setup Setup
</button> </button>
</div> </div>
<div class="notification is-danger is-light" *ngIf="error?.length > 0"> @if (error?.length > 0) {
<div class="notification is-danger is-light">
{{ error }} {{ error }}
</div> </div>
}
</div> </div>
}
<div class="box" *ngIf="step === 3"> @if (step === 3) {
<div class="box">
<div class="notification is-primary"> <div class="notification is-primary">
You are now connected to Real-Debrid! To setup Radarr or Sonarr, please following the instructions here: You are now connected to Real-Debrid! To setup Radarr or Sonarr, please following the instructions here:
<a href="https://github.com/rogerfar/rdt-client/" target="_blank" rel="noopener" <a href="https://github.com/rogerfar/rdt-client/" target="_blank" rel="noopener"
@ -143,6 +162,7 @@
</button> </button>
</div> </div>
</div> </div>
}
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,7 +1,9 @@
<div class="notification is-danger is-light" *ngIf="error && error.length > 0"> @if (error && error.length > 0) {
An error has occured: {{ error }}<br /> <div class="notification is-danger is-light">
An error has occurred: {{ error }}<br />
Please refresh the screen after fixing this error. Please refresh the screen after fixing this error.
</div> </div>
}
<div class="table-container"> <div class="table-container">
<table class="table is-fullwidth is-hoverable"> <table class="table is-fullwidth is-hoverable">
<thead> <thead>
@ -25,7 +27,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let torrent of torrents | sort: sortProperty : sortDirection; trackBy: trackByMethod"> @for (torrent of torrents | sort: sortProperty : sortDirection; track torrent.torrentId) {
<tr>
<td> <td>
<input <input
type="checkbox" type="checkbox"
@ -61,36 +64,22 @@
{{ torrent | status }} {{ torrent | status }}
</td> </td>
</tr> </tr>
}
</tbody> </tbody>
</table> </table>
<div class="flex-container"> <div class="flex-container">
<button @if (torrents.length > 0) {
class="button is-danger" <button class="button is-danger" (click)="showDeleteModal()" [disabled]="selectedTorrents.length === 0">
(click)="showDeleteModal()"
[disabled]="selectedTorrents.length === 0"
*ngIf="torrents.length > 0"
>
Delete Selected Delete Selected
</button> </button>
<button class="button is-primary" (click)="showRetryModal()" [disabled]="selectedTorrents.length === 0">
<button
class="button is-primary"
(click)="showRetryModal()"
[disabled]="selectedTorrents.length === 0"
*ngIf="torrents.length > 0"
>
Retry Selected Retry Selected
</button> </button>
<button class="button is-primary" (click)="changeSettingsModal()" [disabled]="selectedTorrents.length === 0">
<button
class="button is-primary"
(click)="changeSettingsModal()"
[disabled]="selectedTorrents.length === 0"
*ngIf="torrents.length > 0"
>
Change Settings Change Settings
</button> </button>
}
</div> </div>
</div> </div>
@ -131,9 +120,9 @@
<div class="notification is-primary"> <div class="notification is-primary">
Deleting a torrent from Real-Debrid will automatically delete it here too. Deleting a torrent from Real-Debrid will automatically delete it here too.
</div> </div>
<div class="notification is-danger is-light" *ngIf="deleteError?.length > 0"> @if (deleteError?.length > 0) {
Error deleting torrent: {{ deleteError }} <div class="notification is-danger is-light">Error deleting torrent: {{ deleteError }}</div>
</div> }
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button <button
@ -164,9 +153,9 @@
This action will delete all the torrent data + all local downloads. Then it will re-add the original magnet link This action will delete all the torrent data + all local downloads. Then it will re-add the original magnet link
or torrent file to the debrid provider. or torrent file to the debrid provider.
</p> </p>
<div class="notification is-danger is-light" *ngIf="retryError?.length > 0"> @if (retryError?.length > 0) {
Error retrying torrent: {{ retryError }} <div class="notification is-danger is-light">Error retrying torrent: {{ retryError }}</div>
</div> }
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button <button
@ -296,9 +285,9 @@
</p> </p>
</div> </div>
<div class="notification is-danger is-light" *ngIf="changeSettingsError?.length > 0"> @if (changeSettingsError?.length > 0) {
Error changing settings: {{ changeSettingsError }} <div class="notification is-danger is-light">Error changing settings: {{ changeSettingsError }}</div>
</div> }
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button <button

View file

@ -71,10 +71,6 @@ export class TorrentTableComponent implements OnInit {
this.router.navigate([`/torrent/${torrentId}`]); this.router.navigate([`/torrent/${torrentId}`]);
} }
public trackByMethod(index: number, el: Torrent): string {
return el.torrentId;
}
public toggleDeleteSelectAll(event: any) { public toggleDeleteSelectAll(event: any) {
this.selectedTorrents = []; this.selectedTorrents = [];

View file

@ -12,13 +12,16 @@
</ul> </ul>
</div> </div>
<div *ngIf="torrent === null || torrent === undefined"> @if (torrent === null || torrent === undefined) {
<div>
<div class="fa-3x"> <div class="fa-3x">
<i class="fas fa-spinner fa-spin"></i> <i class="fas fa-spinner fa-spin"></i>
</div> </div>
</div> </div>
<div *ngIf="torrent !== null && torrent !== undefined"> } @else {
<div class="flex-container" *ngIf="activeTab === 0"> <div>
@if (activeTab === 0) {
<div class="flex-container">
<div style="flex: 1 1 0"> <div style="flex: 1 1 0">
<div class="field is-grouped"> <div class="field is-grouped">
<div class="control"> <div class="control">
@ -53,39 +56,65 @@
</div> </div>
<div class="field"> <div class="field">
<label class="label">Downloader</label> <label class="label">Downloader</label>
<ng-container [ngSwitch]="torrent.downloadClient"> @switch (torrent.downloadClient) {
<ng-container *ngSwitchCase="0">Internal Downloader</ng-container> @case (0) {
<ng-container *ngSwitchCase="1">Bezadd</ng-container> Internal Downloader
<ng-container *ngSwitchCase="2">Aria2c</ng-container> }
<ng-container *ngSwitchCase="3">Symlink Downloader</ng-container> @case (1) {
<ng-component *ngSwitchCase="4">Synology DownloadStation</ng-component> Bezadd
</ng-container> }
@case (2) {
Aria2c
}
@case (3) {
Symlink Downloader
}
@case (4) {
Synology DownloadStation
}
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Post Download Action</label> <label class="label">Post Download Action</label>
<ng-container [ngSwitch]="torrent.hostDownloadAction"> @switch (torrent.hostDownloadAction) {
<ng-container *ngSwitchCase="0">Download all files to host</ng-container> @case (0) {
<ng-container *ngSwitchCase="1">Don't download files to host</ng-container> Download all files to host
</ng-container> }
@case (1) {
Don't download files to host
}
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Post Torrent Download Action</label> <label class="label">Post Torrent Download Action</label>
<ng-container [ngSwitch]="torrent.downloadAction"> @switch (torrent.downloadAction) {
<ng-container *ngSwitchCase="0">Download all files above a certain size</ng-container> @case (0) {
<ng-container *ngSwitchCase="1" Download all files above a certain size
>Download all available files on Real-Debrid above a certain size</ng-container }
> @case (1) {
<ng-container *ngSwitchCase="2">Pick files I want to download</ng-container> Download all available files on Real-Debrid above a certain size
</ng-container> }
@case (2) {
Pick files I want to download
}
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Finished action</label> <label class="label">Finished action</label>
<ng-container [ngSwitch]="torrent.finishedAction"> @switch (torrent.finishedAction) {
<ng-container *ngSwitchCase="0">Do nothing</ng-container> @case (0) {
<ng-container *ngSwitchCase="1">Remove torrent from Real-Debrid and Real-Debrid Client</ng-container> Do nothing
<ng-container *ngSwitchCase="2">Remove torrent from Real-Debrid</ng-container> }
<ng-container *ngSwitchCase="3">Remove torrent from client</ng-container> @case (1) {
</ng-container> Remove torrent from Real-Debrid and Real-Debrid Client
}
@case (2) {
Remove torrent from Real-Debrid
}
@case (3) {
Remove torrent from client
}
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Minimum file size to download</label> <label class="label">Minimum file size to download</label>
@ -99,34 +128,42 @@
<label class="label">Exclude files</label> <label class="label">Exclude files</label>
{{ torrent.excludeRegex }} {{ torrent.excludeRegex }}
</div> </div>
<div class="field" *ngIf="!torrent.isFile"> @if (!torrent.isFile) {
<div class="field">
<label class="label">Magnet</label> <label class="label">Magnet</label>
<span [cdkCopyToClipboard]="torrent.fileOrMagnet" (click)="copied = true" *ngIf="!copied" @if (!copied) {
>Click to copy magnet link to clipboard</span <span [cdkCopyToClipboard]="torrent.fileOrMagnet" (click)="copied = true">
> Click to copy magnet link to clipboard
<span *ngIf="copied">Link copied to clipboard!</span> </span>
} @else {
<span>Link copied to clipboard!</span>
}
</div> </div>
<div class="field" *ngIf="torrent.isFile"> } @else {
<div class="field">
<label class="label">Torrent file</label> <label class="label">Torrent file</label>
<span (click)="download()">Click to download torrent file</span> <span (click)="download()">Click to download torrent file</span>
</div> </div>
}
<div class="field"> <div class="field">
<label class="label">Added on</label> <label class="label">Added on</label>
{{ torrent.added | date: "fullDate" }} {{ torrent.added | date: "mediumTime" }} {{ torrent.added | date: "fullDate" }} {{ torrent.added | date: "mediumTime" }}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Files selected on</label> <label class="label">Files selected on</label>
<ng-container *ngIf="torrent.filesSelected === null">(no files selected yet) </ng-container> @if (torrent.filesSelected === null) {
<ng-container *ngIf="torrent.filesSelected !== null"> (no files selected yet)
} @else {
{{ torrent.filesSelected | date: "fullDate" }} {{ torrent.filesSelected | date: "mediumTime" }} {{ torrent.filesSelected | date: "fullDate" }} {{ torrent.filesSelected | date: "mediumTime" }}
</ng-container> }
</div> </div>
<div class="field"> <div class="field">
<label class="label">Completed on</label> <label class="label">Completed on</label>
<ng-container *ngIf="torrent.completed === null">(not completed yet) </ng-container> @if (torrent.completed === null) {
<ng-container *ngIf="torrent.completed !== null"> (not completed yet)
} @else {
{{ torrent.completed | date: "fullDate" }} {{ torrent.completed | date: "mediumTime" }} {{ torrent.completed | date: "fullDate" }} {{ torrent.completed | date: "mediumTime" }}
</ng-container> }
</div> </div>
</div> </div>
<div style="flex: 1 1 0"> <div style="flex: 1 1 0">
@ -156,13 +193,23 @@
</div> </div>
<div class="field"> <div class="field">
<label class="label">Real-Debrid Status</label> <label class="label">Real-Debrid Status</label>
<ng-container [ngSwitch]="torrent.rdStatus"> @switch (torrent.rdStatus) {
<ng-container *ngSwitchCase="0">Processing</ng-container> @case (0) {
<ng-container *ngSwitchCase="1">Waiting For File Selection</ng-container> Processing
<ng-container *ngSwitchCase="2">Downloading</ng-container> }
<ng-container *ngSwitchCase="3">Finished</ng-container> @case (1) {
<ng-container *ngSwitchCase="99">Error</ng-container> Waiting For File Selection
</ng-container> }
@case (2) {
Downloading
}
@case (3) {
Finished
}
@case (99) {
Error
}
}
({{ torrent.rdStatusRaw }}) ({{ torrent.rdStatusRaw }})
</div> </div>
<div class="field"> <div class="field">
@ -183,7 +230,9 @@
</div> </div>
</div> </div>
</div> </div>
<div *ngIf="activeTab === 1"> }
@if (activeTab === 1) {
<div>
<div class="field"> <div class="field">
<table class="table is-fullwidth"> <table class="table is-fullwidth">
<thead> <thead>
@ -195,7 +244,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let file of torrent.files"> @for (file of torrent.files; track file.id) {
<tr>
<td> <td>
{{ file.id }} {{ file.id }}
</td> </td>
@ -206,15 +256,21 @@
{{ file.bytes | filesize }} {{ file.bytes | filesize }}
</td> </td>
<td> <td>
<i class="fas fa-check" *ngIf="file.selected" style="color: green"></i> @if (file.selected) {
<i class="fas fa-times" *ngIf="!file.selected" style="color: red"></i> <i class="fas fa-check" style="color: green"></i>
} @else {
<i class="fas fa-times" style="color: red"></i>
}
</td> </td>
</tr> </tr>
}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
<div *ngIf="activeTab === 2"> }
@if (activeTab === 2) {
<div>
<div class="field"> <div class="field">
<table class="table is-fullwidth is-hoverable"> <table class="table is-fullwidth is-hoverable">
<thead> <thead>
@ -226,20 +282,23 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<ng-container *ngFor="let download of torrent.downloads"> @for (download of torrent.downloads; track download.downloadId) {
<tr (click)="downloadExpanded[download.downloadId] = !downloadExpanded[download.downloadId]"> @let expanded = downloadExpanded[download.downloadId];
<tr (click)="downloadExpanded[download.downloadId] = !expanded">
<td style="width: 35px"> <td style="width: 35px">
<i class="fas fa-caret-right" *ngIf="!downloadExpanded[download.downloadId]"></i> @if (!expanded) {
<i class="fas fa-caret-down" *ngIf="downloadExpanded[download.downloadId]"></i> <i class="fas fa-caret-right"></i>
} @else {
<i class="fas fa-caret-down"></i>
}
</td> </td>
<td> <td>
<ng-container *ngIf="download.link"> @if (download.link) {
{{ download.link | decodeURI }} {{ download.link | decodeURI }}
</ng-container> }
@if (!download.link) {
<ng-container *ngIf="!download.link">
{{ download.path }} {{ download.path }}
</ng-container> }
</td> </td>
<td> <td>
{{ download.bytesTotal | filesize }} {{ download.bytesTotal | filesize }}
@ -248,7 +307,8 @@
{{ download | downloadStatus }} {{ download | downloadStatus }}
</td> </td>
</tr> </tr>
<tr *ngIf="downloadExpanded[download.downloadId]" class="separator"> @if (expanded) {
<tr class="separator">
<td style="width: 35px"></td> <td style="width: 35px"></td>
<td colspan="5"> <td colspan="5">
<div class="flex-container"> <div class="flex-container">
@ -260,15 +320,17 @@
</button> </button>
</div> </div>
</div> </div>
<div class="field" *ngIf="download.error"> @if (download.error) {
<div class="field">
<label class="label">Error</label> <label class="label">Error</label>
{{ download.error }} {{ download.error }}
</div> </div>
}
<div class="field"> <div class="field">
<label class="label">Real-Debrid Unrestricted Link</label> <label class="label">Real-Debrid Unrestricted Link</label>
<a href="{{ download.link }}" target="_blank" *ngIf="download.link"> @if (download.link) {
{{ download.link | decodeURI }}</a <a href="{{ download.link }}" target="_blank"> {{ download.link | decodeURI }}</a>
> }
</div> </div>
<div class="field"> <div class="field">
<label class="label">Real-Debrid Link</label> <label class="label">Real-Debrid Link</label>
@ -288,77 +350,88 @@
<div style="flex: 1 1 0"> <div style="flex: 1 1 0">
<div class="field"> <div class="field">
<label class="label">Added</label> <label class="label">Added</label>
<ng-container *ngIf="download.added"> @if (download.added) {
{{ download.added | date: "fullDate" }} {{ download.added | date: "mediumTime" }} {{ download.added | date: "fullDate" }} {{ download.added | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.added">(not added yet) </ng-container> (not added yet)
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Download Queued</label> <label class="label">Download Queued</label>
<ng-container *ngIf="download.downloadQueued"> @if (download.downloadQueued) {
{{ download.downloadQueued | date: "fullDate" }} {{ download.downloadQueued | date: "fullDate" }}
{{ download.downloadQueued | date: "mediumTime" }} {{ download.downloadQueued | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.downloadQueued">(not queued for downloading yet) </ng-container> (not queued for downloading yet)
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Download Started</label> <label class="label">Download Started</label>
<ng-container *ngIf="download.downloadStarted"> @if (download.downloadStarted) {
{{ download.downloadStarted | date: "fullDate" }} {{ download.downloadStarted | date: "fullDate" }}
{{ download.downloadStarted | date: "mediumTime" }} {{ download.downloadStarted | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.downloadStarted">(not started downloading yet) </ng-container> (not started downloading yet)
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Download Finished</label> <label class="label">Download Finished</label>
<ng-container *ngIf="download.downloadFinished"> @if (download.downloadFinished) {
{{ download.downloadFinished | date: "fullDate" }} {{ download.downloadFinished | date: "fullDate" }}
{{ download.downloadFinished | date: "mediumTime" }} {{ download.downloadFinished | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.downloadFinished">(not finished yet) </ng-container> (not finished yet)
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Unpacking Queued</label> <label class="label">Unpacking Queued</label>
<ng-container *ngIf="download.unpackingQueued"> @if (download.unpackingQueued) {
{{ download.unpackingQueued | date: "fullDate" }} {{ download.unpackingQueued | date: "fullDate" }}
{{ download.unpackingQueued | date: "mediumTime" }} {{ download.unpackingQueued | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.unpackingQueued">(not queued for unpacking yet) </ng-container> (not queued for unpacking yet)
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Unpacking Started</label> <label class="label">Unpacking Started</label>
<ng-container *ngIf="download.unpackingStarted"> @if (download.unpackingStarted) {
{{ download.unpackingStarted | date: "fullDate" }} {{ download.unpackingStarted | date: "fullDate" }}
{{ download.unpackingStarted | date: "mediumTime" }} {{ download.unpackingStarted | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.unpackingStarted">(not started unpacking yet) </ng-container> (not started unpacking yet)
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Unpacking Finished</label> <label class="label">Unpacking Finished</label>
<ng-container *ngIf="download.unpackingFinished"> @if (download.unpackingFinished) {
{{ download.unpackingFinished | date: "fullDate" }} {{ download.unpackingFinished | date: "fullDate" }}
{{ download.unpackingFinished | date: "mediumTime" }} {{ download.unpackingFinished | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.unpackingFinished">(not finished unpacking yet) </ng-container> (not finished unpacking yet)
}
</div> </div>
<div class="field"> <div class="field">
<label class="label">Completed</label> <label class="label">Completed</label>
<ng-container *ngIf="download.completed"> @if (download.completed) {
{{ download.completed | date: "fullDate" }} {{ download.completed | date: "fullDate" }}
{{ download.completed | date: "mediumTime" }} {{ download.completed | date: "mediumTime" }}
</ng-container> } @else {
<ng-container *ngIf="!download.completed">(not completed yet) </ng-container> (not completed yet)
}
</div> </div>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</ng-container> }
}
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
}
</div> </div>
}
<div class="modal" [class.is-active]="isDeleteModalActive"> <div class="modal" [class.is-active]="isDeleteModalActive">
<div class="modal-background"></div> <div class="modal-background"></div>
@ -391,9 +464,9 @@
<div class="notification is-primary"> <div class="notification is-primary">
Deleting a torrent from Real-Debrid will automatically delete it here too. Deleting a torrent from Real-Debrid will automatically delete it here too.
</div> </div>
<div class="notification is-danger is-light" *ngIf="deleteError?.length > 0"> @if (deleteError?.length > 0) {
Error deleting torrent: {{ deleteError }} <div class="notification is-danger is-light">Error deleting torrent: {{ deleteError }}</div>
</div> }
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button <button
@ -424,9 +497,9 @@
This action will delete all the torrent data + all local downloads. Then it will re-add the original magnet link This action will delete all the torrent data + all local downloads. Then it will re-add the original magnet link
or torrent file to Real-Debrid. or torrent file to Real-Debrid.
</p> </p>
<div class="notification is-danger is-light" *ngIf="retryError?.length > 0"> @if (retryError?.length > 0) {
Error retrying torrent: {{ retryError }} <div class="notification is-danger is-light">Error retrying torrent: {{ retryError }}</div>
</div> }
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button <button
@ -454,9 +527,9 @@
<section class="modal-card-body"> <section class="modal-card-body">
<p>Are you sure you want to retry this download?</p> <p>Are you sure you want to retry this download?</p>
<p>This action will remove the local download and re-download the file from Real-Debrid.</p> <p>This action will remove the local download and re-download the file from Real-Debrid.</p>
<div class="notification is-danger is-light" *ngIf="downloadRetryError?.length > 0"> @if (downloadRetryError?.length > 0) {
Error retrying download: {{ downloadRetryError }} <div class="notification is-danger is-light">Error retrying download: {{ downloadRetryError }}</div>
</div> }
</section> </section>
<footer class="modal-card-foot"> <footer class="modal-card-foot">
<button <button