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") {
When downloading with AllDebrid it cannot be guaranteed that only files above this limit will be download as <p class="help">
some files are grouped together in 1 large archive. When downloading with AllDebrid it cannot be guaranteed that only files above this limit will be download as
</p> some files are grouped together in 1 large archive.
<p class="help" *ngIf="downloadAction === 2">This setting does not apply to manually selected files.</p> </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) {
{{ error }} <div class="notification is-danger is-light">
</div> {{ error }}
</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) {
<span class="icon"> <a class="navbar-item" href="{{ providerLink }}" target="_blank" rel="noopener">
<i class="fas fa-euro-sign" aria-hidden="true"></i> <span class="icon">
</span> <i class="fas fa-euro-sign" aria-hidden="true"></i>
<span *ngIf="profile.expiration">Premium Status: {{ profile.expiration | date }}</span> </span>
<span *ngIf="!profile.expiration" class="no-premium">Not premium</span> @if (profile.expiration) {
</a> <span>Premium Status: {{ profile.expiration | date }}</span>
} @else {
<span class="no-premium">Not premium</span>
}
</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'" profile &&
*ngIf=" (!profile.disableUpdateNotification || profile.isInsecure) &&
profile && profile.latestVersion &&
(!profile.disableUpdateNotification || profile.isInsecure) && profile.currentVersion !== profile.latestVersion
profile.latestVersion && ) {
profile.currentVersion !== profile.latestVersion <div class="notification" [ngClass]="profile.isInsecure ? 'is-danger' : 'is-warning'">
" @if (profile.isInsecure) {
> <span> Your current version is insecure. </span>
<span *ngIf="profile.isInsecure"> 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,186 +1,219 @@
<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) {
<a>{{ tab.displayName }}</a> <li [ngClass]="{ 'is-active': activeTab === i }" (click)="activeTab = i">
</li> <a>{{ tab.displayName }}</a>
</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) {
<h3>{{ tab.description }}</h3> <div [hidden]="activeTab !== i">
<ng-container *ngFor="let setting of tab.settings"> <h3>{{ tab.description }}</h3>
<h3 *ngIf="setting.type === 'Object'" class="title is-3" style="margin-top: 1.2rem">{{ setting.displayName }}</h3> @for (setting of tab.settings; track setting.key) {
@if (setting.type === "Object") {
<h3 class="title is-3" style="margin-top: 1.2rem">{{ setting.displayName }}</h3>
}
<div class="field">
@if (setting.type !== "Boolean" && setting.type !== "Object") {
<label class="label">{{ setting.displayName }}</label>
}
@switch (setting.type) {
@case ("String") {
<div class="control">
<input class="input" type="text" [(ngModel)]="setting.value" />
</div>
}
@case ("Int32") {
<div class="control">
<input class="input" type="number" [(ngModel)]="setting.value" />
</div>
}
@case ("Boolean") {
<label class="checkbox">
<input type="checkbox" [(ngModel)]="setting.value" />
{{ setting.displayName }}
</label>
}
@case ("Enum") {
<div class="control select is-fullwidth">
<select [(ngModel)]="setting.value">
@for (kvp of setting.enumValues | keyvalue; track kvp) {
<option [value]="kvp.key">{{ kvp.value }}</option>
}
</select>
</div>
}
@case ("Object") {}
@default {
<div class="control">Invalid setting type {{ setting.type }}</div>
}
}
<p class="help" [innerHtml]="setting.description | nl2br"></p>
@if (setting.key === "DownloadClient:Aria2cSecret") {
<button
class="button is-warning"
(click)="testAria2cConnection()"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
>
Test aria2 connection
</button>
@if (testAria2cConnectionError) {
<div class="notification is-danger is-light" style="margin-top: 1rem">
Could connect to Aria2 client<br />
{{ testAria2cConnectionError }}
</div>
}
@if (testAria2cConnectionSuccess) {
<div class="notification is-success is-light" style="margin-top: 1rem">
Found Aria2 client version {{ testAria2cConnectionSuccess }}
</div>
}
}
</div>
}
</div>
}
@if (activeTab === 99) {
<div>
<div class="field"> <div class="field">
<label class="label" *ngIf="setting.type !== 'Boolean' && setting.type !== 'Object'">{{ <label class="label">Test download path permissions</label>
setting.displayName <div class="control">
}}</label> @if (!testPathError && !testPathSuccess) {
<ng-container [ngSwitch]="setting.type"> <button
<div class="control" *ngSwitchCase="'String'"> class="button is-warning"
<input class="input" type="text" [(ngModel)]="setting.value" /> (click)="testDownloadPath()"
</div> [disabled]="saving"
<div class="control" *ngSwitchCase="'Int32'"> [ngClass]="{ 'is-loading': saving }"
<input class="input" type="number" [(ngModel)]="setting.value" /> >
</div> Test permissions
<label class="checkbox" *ngSwitchCase="'Boolean'"> </button>
<input type="checkbox" [(ngModel)]="setting.value" /> }
{{ setting.displayName }} @if (testPathError) {
</label> <div class="notification is-danger is-light">
<div class="control select is-fullwidth" *ngSwitchCase="'Enum'"> Could not test your download path<br />
<select [(ngModel)]="setting.value"> {{ testPathError }}
<option [value]="kvp.key" *ngFor="let kvp of setting.enumValues | keyvalue">{{ kvp.value }}</option> </div>
</select> }
</div> @if (testPathSuccess) {
<ng-container *ngSwitchCase="'Object'"></ng-container> <div class="notification is-success is-light">Your download path looks good!</div>
<div class="control" *ngSwitchDefault>Invalid setting type {{ setting.type }}</div> }
</ng-container>
<p class="help" [innerHtml]="setting.description | nl2br"></p>
<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="notification is-success is-light" style="margin-top: 1rem" *ngIf="testAria2cConnectionSuccess">
Found Aria2 client version {{ testAria2cConnectionSuccess }}
</div>
</ng-container>
</div>
</ng-container>
</div>
<div *ngIf="activeTab === 99">
<div class="field">
<label class="label">Test download path permissions</label>
<div class="control">
<button
class="button is-warning"
(click)="testDownloadPath()"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
*ngIf="!testPathError && !testPathSuccess"
>
Test permissions
</button>
<div class="notification is-danger is-light" *ngIf="testPathError">
Could not test your download path<br />
{{ testPathError }}
</div> </div>
<div class="help">This will check if the download folder has write permissions.</div>
<div class="notification is-success is-light" *ngIf="testPathSuccess">Your download path looks good!</div>
</div> </div>
<div class="help">This will check if the download folder has write permissions.</div> <div class="field">
</div> <label class="label">Test Real-Debrid download speed</label>
<div class="control">
<div class="field"> @if (testDownloadSpeedError) {
<label class="label">Test Real-Debrid download speed</label> <div class="notification is-danger is-light">
<div class="control"> Could not test your download speed<br />
<button {{ testDownloadSpeedError }}
class="button is-warning" </div>
(click)="testDownloadSpeed()" } @else if (testDownloadSpeedSuccess) {
[disabled]="saving" <div class="notification is-success is-light">Download speed {{ testDownloadSpeedSuccess | filesize }}/s</div>
[ngClass]="{ 'is-loading': saving }" } @else {
*ngIf="!testDownloadSpeedError && !testDownloadSpeedSuccess" <button
> class="button is-warning"
Test download speed (click)="testDownloadSpeed()"
</button> [disabled]="saving"
<div class="notification is-danger is-light" *ngIf="testDownloadSpeedError"> [ngClass]="{ 'is-loading': saving }"
Could not test your download speed<br /> >
{{ testDownloadSpeedError }} Test download speed
</button>
}
</div> </div>
<div class="help">
<div class="notification is-success is-light" *ngIf="testDownloadSpeedSuccess"> This will attempt to download a 10GB file from Real-Debrid. When 50MB has been downloaded the test will stop.
Download speed {{ testDownloadSpeedSuccess | filesize }}/s
</div> </div>
</div> </div>
<div class="help"> <div class="field">
This will attempt to download a 10GB file from Real-Debrid. When 50MB has been downloaded the test will stop. <label class="label">Test download folder write speed</label>
<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
class="button is-warning"
(click)="testWriteSpeed()"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
>
Test write speed
</button>
}
</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">
<label class="label">Test download folder write speed</label>
<div class="control">
<button
class="button is-warning"
(click)="testWriteSpeed()"
[disabled]="saving"
[ngClass]="{ 'is-loading': saving }"
*ngIf="!testWriteSpeedError && !testWriteSpeedSuccess"
>
Test write speed
</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 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 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> <div class="notification is-danger is-light">Error saving settings: {{ error }}</div>
</div>
<div class="field" *ngIf="activeTab === 0">
<label class="label">Register client as magnet link handler</label>
<div class="control">
<button
class="button is-info"
type="button"
(click)="registerMagnetHandler()"
[disabled]="!canRegisterMagnetHandler"
>
<span>
{{ canRegisterMagnetHandler ? "Register Handler" : "Unavailable in This Browser" }}
</span>
</button>
</div>
<p class="help">
@if (canRegisterMagnetHandler) {
This will attempt to register the client as your browser's default handler for magnet links and automatically open
them in the new torrent screen for downloading.
} @else {
Magnet link registration is unavailable. Your
<a href="https://caniuse.com/mdn-api_navigator_registerprotocolhandler" target="_blank" rel="noopener noreferrer">
browser may not support this feature
</a>
, or the client is not being served in a
<a
href="https://developer.mozilla.org/en-US/docs/Glossary/Secure_Context/"
target="_blank"
rel="noopener noreferrer"
>
secure context
</a>
.
} }
</p>
</div>
<div class="field" *ngIf="activeTab < 99">
<div class="control">
<button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
Save Settings
</button>
</div> </div>
</div> </div>
@if (activeTab === 0) {
<div class="field">
<label class="label">Register client as magnet link handler</label>
<div class="control">
<button
class="button is-info"
type="button"
(click)="registerMagnetHandler()"
[disabled]="!canRegisterMagnetHandler"
>
<span>
{{ canRegisterMagnetHandler ? "Register Handler" : "Unavailable in This Browser" }}
</span>
</button>
</div>
<p class="help">
@if (canRegisterMagnetHandler) {
This will attempt to register the client as your browser's default handler for magnet links and automatically
open them in the new torrent screen for downloading.
} @else {
Magnet link registration is unavailable. Your
<a
href="https://caniuse.com/mdn-api_navigator_registerprotocolhandler"
target="_blank"
rel="noopener noreferrer"
>
browser may not support this feature
</a>
, or the client is not being served in a
<a
href="https://developer.mozilla.org/en-US/docs/Glossary/Secure_Context/"
target="_blank"
rel="noopener noreferrer"
>
secure context
</a>
.
}
</p>
</div>
}
@if (activeTab < 99) {
<div class="field">
<div class="control">
<button class="button is-success" (click)="ok()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
Save Settings
</button>
</div>
</div>
}

View file

@ -4,145 +4,165 @@
<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="notification is-primary"> <div class="box">
Welcome to Real-Debrid Client. Please create your account by entering a username and password. <div class="notification is-primary">
Welcome to Real-Debrid Client. Please create your account by entering a username and password.
</div>
<form (ngSubmit)="setup()">
<div class="field">
<label for="" class="label">Username</label>
<div class="control has-icons-left">
<input type="text" placeholder="" class="input" name="userName" [(ngModel)]="userName" required />
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
</div>
</div>
<div class="field">
<label for="" class="label" name="password">Password</label>
<div class="control has-icons-left">
<input type="password" class="input" required name="password" [(ngModel)]="password" />
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div>
</div>
<div class="field">
<button class="button is-success" [ngClass]="{ 'is-loading': working }" [disabled]="working">
Setup
</button>
</div>
@if (error?.length > 0) {
<div class="notification is-danger is-light">
{{ error }}
</div>
}
</form>
</div> </div>
<form (ngSubmit)="setup()"> }
@if (step === 2) {
<div class="box">
<div class="notification is-primary">
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 5 providers:
<br />
<a href="https://real-debrid.com/?id=1348683" target="_blank" rel="noopener"
>Use this link to sign up to Real-Debrid.</a
>
<br />
<a href="https://alldebrid.com/?uid=2v91l&lang=en" target="_blank" rel="noopener"
>Use this link to sign up to AllDebrid.</a
>
<br />
<a href="https://www.premiumize.me/" target="_blank" rel="noopener"
>Use this link to sign up to Premiumize.</a
>
<br />
<a href="https://torbox.app/" target="_blank" rel="noopener">Use this link to sign up to TorBox.</a>
<br />
<a href="https://debrid-link.com/" target="_blank" rel="noopener"
>Use this link to sign up to DebridLink.</a
>
<small>(Referal links)</small>
</div>
<div class="field"> <div class="field">
<label for="" class="label">Username</label> <label class="label">Provider</label>
<div class="control has-icons-left"> <div class="control select is-fullwidth">
<input type="text" placeholder="" class="input" name="userName" [(ngModel)]="userName" required /> <select [(ngModel)]="provider">
<span class="icon is-small is-left"> <option [ngValue]="0">Real-Debrid</option>
<i class="fa fa-envelope"></i> <option [ngValue]="1">AllDebrid</option>
</span> <option [ngValue]="2">Premiumize</option>
<option [ngValue]="3">TorBox</option>
<option [ngValue]="4">DebridLink</option>
</select>
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label for="" class="label" name="password">Password</label> <label for="" class="label">API Private Token</label>
<div class="control has-icons-left"> <div class="control">
<input type="password" class="input" required name="password" [(ngModel)]="password" /> <input type="text" placeholder="" class="input" name="token" [(ngModel)]="token" required />
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
</div> </div>
@if (provider === 0) {
<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>
}
@if (provider === 1) {
<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>
}
@if (provider === 2) {
<p class="help">
You can find your API key here:
<a href="https://www.premiumize.me/account" target="_blank" rel="noopener"
>https://www.premiumize.me/account</a
>.
</p>
}
@if (provider === 3) {
<p class="help">
You can find your API key here:
<a href="https://torbox.app/settings" target="_blank" rel="noopener">https://torbox.app/settings</a
>.
</p>
}
@if (provider === 4) {
<p class="help">
You can find your API key here:
<a href="https://debrid-link.com/webapp/apikey" target="_blank" rel="noopener"
>https://debrid-link.com/webapp/apikey</a
>.
</p>
}
</div> </div>
<div class="field"> <div class="field">
<button class="button is-success" [ngClass]="{ 'is-loading': working }" [disabled]="working"> <button
class="button is-success"
(click)="setToken()"
[ngClass]="{ 'is-loading': working }"
[disabled]="working"
>
Setup Setup
</button> </button>
</div> </div>
<div class="notification is-danger is-light" *ngIf="error?.length > 0"> @if (error?.length > 0) {
{{ error }} <div class="notification is-danger is-light">
</div> {{ error }}
</form> </div>
</div> }
<div class="box" *ngIf="step === 2">
<div class="notification is-primary">
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 5 providers:
<br />
<a href="https://real-debrid.com/?id=1348683" target="_blank" rel="noopener"
>Use this link to sign up to Real-Debrid.</a
>
<br />
<a href="https://alldebrid.com/?uid=2v91l&lang=en" target="_blank" rel="noopener"
>Use this link to sign up to AllDebrid.</a
>
<br />
<a href="https://www.premiumize.me/" target="_blank" rel="noopener"
>Use this link to sign up to Premiumize.</a
>
<br />
<a href="https://torbox.app/" target="_blank" rel="noopener">Use this link to sign up to TorBox.</a>
<br />
<a href="https://debrid-link.com/" target="_blank" rel="noopener"
>Use this link to sign up to DebridLink.</a
>
<small>(Referal links)</small>
</div> </div>
<div class="field"> }
<label class="label">Provider</label>
<div class="control select is-fullwidth"> @if (step === 3) {
<select [(ngModel)]="provider"> <div class="box">
<option [ngValue]="0">Real-Debrid</option> <div class="notification is-primary">
<option [ngValue]="1">AllDebrid</option> You are now connected to Real-Debrid! To setup Radarr or Sonarr, please following the instructions here:
<option [ngValue]="2">Premiumize</option> <a href="https://github.com/rogerfar/rdt-client/" target="_blank" rel="noopener"
<option [ngValue]="3">TorBox</option> >https://github.com/rogerfar/rdt-client/</a
<option [ngValue]="4">DebridLink</option> >.
</select> </div>
<div class="field">
<button
class="button is-success"
(click)="close()"
[ngClass]="{ 'is-loading': working }"
[disabled]="working"
>
OK
</button>
</div> </div>
</div> </div>
<div class="field"> }
<label for="" class="label">API Private Token</label>
<div class="control">
<input type="text" placeholder="" class="input" name="token" [(ngModel)]="token" required />
</div>
<p class="help" *ngIf="provider === 0">
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>
<p class="help" *ngIf="provider === 1">
You can find your API key here:
<a href="https://alldebrid.com/apikeys/" target="_blank" rel="noopener"
>https://alldebrid.com/apikeys/</a
>.
</p>
<p class="help" *ngIf="provider === 2">
You can find your API key here:
<a href="https://www.premiumize.me/account" target="_blank" rel="noopener"
>https://www.premiumize.me/account</a
>.
</p>
<p class="help" *ngIf="provider === 3">
You can find your API key here:
<a href="https://torbox.app/settings" target="_blank" rel="noopener">https://torbox.app/settings</a>.
</p>
<p class="help" *ngIf="provider === 4">
You can find your API key here:
<a href="https://debrid-link.com/webapp/apikey" target="_blank" rel="noopener"
>https://debrid-link.com/webapp/apikey</a
>.
</p>
</div>
<div class="field">
<button
class="button is-success"
(click)="setToken()"
[ngClass]="{ 'is-loading': working }"
[disabled]="working"
>
Setup
</button>
</div>
<div class="notification is-danger is-light" *ngIf="error?.length > 0">
{{ error }}
</div>
</div>
<div class="box" *ngIf="step === 3">
<div class="notification is-primary">
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"
>https://github.com/rogerfar/rdt-client/</a
>.
</div>
<div class="field">
<button
class="button is-success"
(click)="close()"
[ngClass]="{ 'is-loading': working }"
[disabled]="working"
>
OK
</button>
</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">
Please refresh the screen after fixing this error. An error has occurred: {{ error }}<br />
</div> Please refresh the screen after fixing this error.
</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,72 +27,59 @@
</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) {
<td> <tr>
<input <td>
type="checkbox" <input
(click)="toggleSelect(torrent.torrentId)" type="checkbox"
[checked]="selectedTorrents.contains(torrent.torrentId)" (click)="toggleSelect(torrent.torrentId)"
/> [checked]="selectedTorrents.contains(torrent.torrentId)"
</td> />
<td (click)="openTorrent(torrent.torrentId)" class="break-all"> </td>
{{ torrent.rdName }} <td (click)="openTorrent(torrent.torrentId)" class="break-all">
</td> {{ torrent.rdName }}
<td> </td>
{{ torrent.category }} <td>
</td> {{ torrent.category }}
<td> </td>
{{ torrent.priority }} <td>
</td> {{ torrent.priority }}
<td> </td>
{{ torrent.rdSeeders }} <td>
</td> {{ torrent.rdSeeders }}
<td> </td>
{{ torrent.files.length | number }} <td>
</td> {{ torrent.files.length | number }}
<td> </td>
{{ torrent.downloads.length | number }} <td>
</td> {{ torrent.downloads.length | number }}
<td> </td>
{{ torrent.rdSize | filesize }} <td>
</td> {{ torrent.rdSize | filesize }}
<td> </td>
{{ torrent.added | date: "medium" }} <td>
</td> {{ torrent.added | date: "medium" }}
<td> </td>
{{ torrent | status }} <td>
</td> {{ torrent | status }}
</tr> </td>
</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()" Delete Selected
[disabled]="selectedTorrents.length === 0" </button>
*ngIf="torrents.length > 0" <button class="button is-primary" (click)="showRetryModal()" [disabled]="selectedTorrents.length === 0">
> Retry Selected
Delete Selected </button>
</button> <button class="button is-primary" (click)="changeSettingsModal()" [disabled]="selectedTorrents.length === 0">
Change Settings
<button </button>
class="button is-primary" }
(click)="showRetryModal()"
[disabled]="selectedTorrents.length === 0"
*ngIf="torrents.length > 0"
>
Retry Selected
</button>
<button
class="button is-primary"
(click)="changeSettingsModal()"
[disabled]="selectedTorrents.length === 0"
*ngIf="torrents.length > 0"
>
Change Settings
</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,353 +12,426 @@
</ul> </ul>
</div> </div>
<div *ngIf="torrent === null || torrent === undefined"> @if (torrent === null || torrent === undefined) {
<div class="fa-3x"> <div>
<i class="fas fa-spinner fa-spin"></i> <div class="fa-3x">
</div> <i class="fas fa-spinner fa-spin"></i>
</div>
<div *ngIf="torrent !== null && torrent !== undefined">
<div class="flex-container" *ngIf="activeTab === 0">
<div style="flex: 1 1 0">
<div class="field is-grouped">
<div class="control">
<button class="button is-danger" (click)="showDeleteModal()">Delete Torrent</button>
</div>
<div class="control">
<button class="button is-primary" (click)="showRetryModal()">Retry Torrent</button>
</div>
<div class="control">
<button class="button is-light" (click)="showUpdateSettingsModal()">Change Settings</button>
</div>
</div>
<div class="field">
<label class="label">Status</label>
{{ torrent | status }}
</div>
<div class="field">
<label class="label">Retry count</label>
{{ torrent.retryCount }} / {{ torrent.torrentRetryAttempts }}
</div>
<div class="field">
<label class="label">Hash</label>
{{ torrent.hash }}
</div>
<div class="field">
<label class="label">Priority</label>
{{ torrent.priority || "" }}
</div>
<div class="field">
<label class="label">Category</label>
{{ torrent.category || "(no category set)" }}
</div>
<div class="field">
<label class="label">Downloader</label>
<ng-container [ngSwitch]="torrent.downloadClient">
<ng-container *ngSwitchCase="0">Internal Downloader</ng-container>
<ng-container *ngSwitchCase="1">Bezadd</ng-container>
<ng-container *ngSwitchCase="2">Aria2c</ng-container>
<ng-container *ngSwitchCase="3">Symlink Downloader</ng-container>
<ng-component *ngSwitchCase="4">Synology DownloadStation</ng-component>
</ng-container>
</div>
<div class="field">
<label class="label">Post Download Action</label>
<ng-container [ngSwitch]="torrent.hostDownloadAction">
<ng-container *ngSwitchCase="0">Download all files to host</ng-container>
<ng-container *ngSwitchCase="1">Don't download files to host</ng-container>
</ng-container>
</div>
<div class="field">
<label class="label">Post Torrent Download Action</label>
<ng-container [ngSwitch]="torrent.downloadAction">
<ng-container *ngSwitchCase="0">Download all files above a certain size</ng-container>
<ng-container *ngSwitchCase="1"
>Download all available files on Real-Debrid above a certain size</ng-container
>
<ng-container *ngSwitchCase="2">Pick files I want to download</ng-container>
</ng-container>
</div>
<div class="field">
<label class="label">Finished action</label>
<ng-container [ngSwitch]="torrent.finishedAction">
<ng-container *ngSwitchCase="0">Do nothing</ng-container>
<ng-container *ngSwitchCase="1">Remove torrent from Real-Debrid and Real-Debrid Client</ng-container>
<ng-container *ngSwitchCase="2">Remove torrent from Real-Debrid</ng-container>
<ng-container *ngSwitchCase="3">Remove torrent from client</ng-container>
</ng-container>
</div>
<div class="field">
<label class="label">Minimum file size to download</label>
{{ torrent.downloadMinSize }}MB
</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">
<label class="label">Magnet</label>
<span [cdkCopyToClipboard]="torrent.fileOrMagnet" (click)="copied = true" *ngIf="!copied"
>Click to copy magnet link to clipboard</span
>
<span *ngIf="copied">Link copied to clipboard!</span>
</div>
<div class="field" *ngIf="torrent.isFile">
<label class="label">Torrent file</label>
<span (click)="download()">Click to download torrent file</span>
</div>
<div class="field">
<label class="label">Added on</label>
{{ torrent.added | date: "fullDate" }} {{ torrent.added | date: "mediumTime" }}
</div>
<div class="field">
<label class="label">Files selected on</label>
<ng-container *ngIf="torrent.filesSelected === null">(no files selected yet) </ng-container>
<ng-container *ngIf="torrent.filesSelected !== null">
{{ torrent.filesSelected | date: "fullDate" }} {{ torrent.filesSelected | date: "mediumTime" }}
</ng-container>
</div>
<div class="field">
<label class="label">Completed on</label>
<ng-container *ngIf="torrent.completed === null">(not completed yet) </ng-container>
<ng-container *ngIf="torrent.completed !== null">
{{ torrent.completed | date: "fullDate" }} {{ torrent.completed | date: "mediumTime" }}
</ng-container>
</div>
</div>
<div style="flex: 1 1 0">
<div class="field">
<label class="label">Real-Debrid ID</label>
{{ torrent.rdId }}
</div>
<div class="field">
<label class="label">Real-Debrid Name</label>
{{ torrent.rdName }}
</div>
<div class="field">
<label class="label">Real-Debrid Size</label>
{{ torrent.rdSize | filesize }}
</div>
<div class="field">
<label class="label">Real-Debrid Host</label>
{{ torrent.rdHost }}
</div>
<div class="field">
<label class="label">Real-Debrid Split</label>
{{ torrent.rdSplit }}
</div>
<div class="field">
<label class="label">Real-Debrid Progress</label>
{{ torrent.rdProgress || 0 }}%
</div>
<div class="field">
<label class="label">Real-Debrid Status</label>
<ng-container [ngSwitch]="torrent.rdStatus">
<ng-container *ngSwitchCase="0">Processing</ng-container>
<ng-container *ngSwitchCase="1">Waiting For File Selection</ng-container>
<ng-container *ngSwitchCase="2">Downloading</ng-container>
<ng-container *ngSwitchCase="3">Finished</ng-container>
<ng-container *ngSwitchCase="99">Error</ng-container>
</ng-container>
({{ torrent.rdStatusRaw }})
</div>
<div class="field">
<label class="label">Real-Debrid Added</label>
{{ torrent.rdAdded | date: "fullDate" }} {{ torrent.rdAdded | date: "mediumTime" }}
</div>
<div class="field">
<label class="label">Real-Debrid Ended</label>
{{ torrent.rdEnded | date: "fullDate" }} {{ torrent.rdEnded | date: "mediumTime" }}
</div>
<div class="field">
<label class="label">Real-Debrid Speed</label>
{{ torrent.rdSpeed || 0 }}
</div>
<div class="field">
<label class="label">Real-Debrid Seeders</label>
{{ torrent.rdSeeders || "0" }}
</div>
</div> </div>
</div> </div>
<div *ngIf="activeTab === 1"> } @else {
<div class="field"> <div>
<table class="table is-fullwidth"> @if (activeTab === 0) {
<thead> <div class="flex-container">
<tr> <div style="flex: 1 1 0">
<th>ID</th> <div class="field is-grouped">
<th>Path</th> <div class="control">
<th>Size</th> <button class="button is-danger" (click)="showDeleteModal()">Delete Torrent</button>
<th>Selected</th> </div>
</tr> <div class="control">
</thead> <button class="button is-primary" (click)="showRetryModal()">Retry Torrent</button>
<tbody> </div>
<tr *ngFor="let file of torrent.files"> <div class="control">
<td> <button class="button is-light" (click)="showUpdateSettingsModal()">Change Settings</button>
{{ file.id }} </div>
</td> </div>
<td> <div class="field">
{{ file.path }} <label class="label">Status</label>
</td> {{ torrent | status }}
<td> </div>
{{ file.bytes | filesize }} <div class="field">
</td> <label class="label">Retry count</label>
<td> {{ torrent.retryCount }} / {{ torrent.torrentRetryAttempts }}
<i class="fas fa-check" *ngIf="file.selected" style="color: green"></i> </div>
<i class="fas fa-times" *ngIf="!file.selected" style="color: red"></i> <div class="field">
</td> <label class="label">Hash</label>
</tr> {{ torrent.hash }}
</tbody> </div>
</table> <div class="field">
</div> <label class="label">Priority</label>
</div> {{ torrent.priority || "" }}
<div *ngIf="activeTab === 2"> </div>
<div class="field"> <div class="field">
<table class="table is-fullwidth is-hoverable"> <label class="label">Category</label>
<thead> {{ torrent.category || "(no category set)" }}
<tr> </div>
<th style="width: 35px"></th> <div class="field">
<th>Link</th> <label class="label">Downloader</label>
<th>Size</th> @switch (torrent.downloadClient) {
<th>Status</th> @case (0) {
</tr> Internal Downloader
</thead> }
<tbody> @case (1) {
<ng-container *ngFor="let download of torrent.downloads"> Bezadd
<tr (click)="downloadExpanded[download.downloadId] = !downloadExpanded[download.downloadId]"> }
<td style="width: 35px"> @case (2) {
<i class="fas fa-caret-right" *ngIf="!downloadExpanded[download.downloadId]"></i> Aria2c
<i class="fas fa-caret-down" *ngIf="downloadExpanded[download.downloadId]"></i> }
</td> @case (3) {
<td> Symlink Downloader
<ng-container *ngIf="download.link"> }
{{ download.link | decodeURI }} @case (4) {
</ng-container> Synology DownloadStation
}
<ng-container *ngIf="!download.link"> }
{{ download.path }} </div>
</ng-container> <div class="field">
</td> <label class="label">Post Download Action</label>
<td> @switch (torrent.hostDownloadAction) {
{{ download.bytesTotal | filesize }} @case (0) {
</td> Download all files to host
<td> }
{{ download | downloadStatus }} @case (1) {
</td> Don't download files to host
</tr> }
<tr *ngIf="downloadExpanded[download.downloadId]" class="separator"> }
<td style="width: 35px"></td> </div>
<td colspan="5"> <div class="field">
<div class="flex-container"> <label class="label">Post Torrent Download Action</label>
<div style="flex: 1 1 0"> @switch (torrent.downloadAction) {
<div class="field is-grouped"> @case (0) {
<div class="control"> Download all files above a certain size
<button class="button is-primary" (click)="showDownloadRetryModal(download.downloadId)"> }
Retry Download @case (1) {
</button> Download all available files on Real-Debrid above a certain size
</div> }
</div> @case (2) {
<div class="field" *ngIf="download.error"> Pick files I want to download
<label class="label">Error</label> }
{{ download.error }} }
</div> </div>
<div class="field"> <div class="field">
<label class="label">Real-Debrid Unrestricted Link</label> <label class="label">Finished action</label>
<a href="{{ download.link }}" target="_blank" *ngIf="download.link"> @switch (torrent.finishedAction) {
{{ download.link | decodeURI }}</a @case (0) {
> Do nothing
</div> }
<div class="field"> @case (1) {
<label class="label">Real-Debrid Link</label> Remove torrent from Real-Debrid and Real-Debrid Client
}
@case (2) {
Remove torrent from Real-Debrid
}
@case (3) {
Remove torrent from client
}
}
</div>
<div class="field">
<label class="label">Minimum file size to download</label>
{{ torrent.downloadMinSize }}MB
</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>
@if (!torrent.isFile) {
<div class="field">
<label class="label">Magnet</label>
@if (!copied) {
<span [cdkCopyToClipboard]="torrent.fileOrMagnet" (click)="copied = true">
Click to copy magnet link to clipboard
</span>
} @else {
<span>Link copied to clipboard!</span>
}
</div>
} @else {
<div class="field">
<label class="label">Torrent file</label>
<span (click)="download()">Click to download torrent file</span>
</div>
}
<div class="field">
<label class="label">Added on</label>
{{ torrent.added | date: "fullDate" }} {{ torrent.added | date: "mediumTime" }}
</div>
<div class="field">
<label class="label">Files selected on</label>
@if (torrent.filesSelected === null) {
(no files selected yet)
} @else {
{{ torrent.filesSelected | date: "fullDate" }} {{ torrent.filesSelected | date: "mediumTime" }}
}
</div>
<div class="field">
<label class="label">Completed on</label>
@if (torrent.completed === null) {
(not completed yet)
} @else {
{{ torrent.completed | date: "fullDate" }} {{ torrent.completed | date: "mediumTime" }}
}
</div>
</div>
<div style="flex: 1 1 0">
<div class="field">
<label class="label">Real-Debrid ID</label>
{{ torrent.rdId }}
</div>
<div class="field">
<label class="label">Real-Debrid Name</label>
{{ torrent.rdName }}
</div>
<div class="field">
<label class="label">Real-Debrid Size</label>
{{ torrent.rdSize | filesize }}
</div>
<div class="field">
<label class="label">Real-Debrid Host</label>
{{ torrent.rdHost }}
</div>
<div class="field">
<label class="label">Real-Debrid Split</label>
{{ torrent.rdSplit }}
</div>
<div class="field">
<label class="label">Real-Debrid Progress</label>
{{ torrent.rdProgress || 0 }}%
</div>
<div class="field">
<label class="label">Real-Debrid Status</label>
@switch (torrent.rdStatus) {
@case (0) {
Processing
}
@case (1) {
Waiting For File Selection
}
@case (2) {
Downloading
}
@case (3) {
Finished
}
@case (99) {
Error
}
}
({{ torrent.rdStatusRaw }})
</div>
<div class="field">
<label class="label">Real-Debrid Added</label>
{{ torrent.rdAdded | date: "fullDate" }} {{ torrent.rdAdded | date: "mediumTime" }}
</div>
<div class="field">
<label class="label">Real-Debrid Ended</label>
{{ torrent.rdEnded | date: "fullDate" }} {{ torrent.rdEnded | date: "mediumTime" }}
</div>
<div class="field">
<label class="label">Real-Debrid Speed</label>
{{ torrent.rdSpeed || 0 }}
</div>
<div class="field">
<label class="label">Real-Debrid Seeders</label>
{{ torrent.rdSeeders || "0" }}
</div>
</div>
</div>
}
@if (activeTab === 1) {
<div>
<div class="field">
<table class="table is-fullwidth">
<thead>
<tr>
<th>ID</th>
<th>Path</th>
<th>Size</th>
<th>Selected</th>
</tr>
</thead>
<tbody>
@for (file of torrent.files; track file.id) {
<tr>
<td>
{{ file.id }}
</td>
<td>
{{ file.path }}
</td>
<td>
{{ file.bytes | filesize }}
</td>
<td>
@if (file.selected) {
<i class="fas fa-check" style="color: green"></i>
} @else {
<i class="fas fa-times" style="color: red"></i>
}
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
@if (activeTab === 2) {
<div>
<div class="field">
<table class="table is-fullwidth is-hoverable">
<thead>
<tr>
<th style="width: 35px"></th>
<th>Link</th>
<th>Size</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@for (download of torrent.downloads; track download.downloadId) {
@let expanded = downloadExpanded[download.downloadId];
<tr (click)="downloadExpanded[download.downloadId] = !expanded">
<td style="width: 35px">
@if (!expanded) {
<i class="fas fa-caret-right"></i>
} @else {
<i class="fas fa-caret-down"></i>
}
</td>
<td>
@if (download.link) {
{{ download.link | decodeURI }}
}
@if (!download.link) {
{{ download.path }} {{ download.path }}
</div> }
<div class="field"> </td>
<label class="label">Download</label> <td>
{{ download.bytesDone | filesize }} / {{ download.bytesTotal | filesize }} ({{ {{ download.bytesTotal | filesize }}
download.speed | filesize </td>
}}/s) <td>
</div> {{ download | downloadStatus }}
<div class="field"> </td>
<label class="label">Retry Count</label> </tr>
{{ download.retryCount }} / {{ torrent.downloadRetryAttempts }} @if (expanded) {
</div> <tr class="separator">
</div> <td style="width: 35px"></td>
<div style="flex: 1 1 0"> <td colspan="5">
<div class="field"> <div class="flex-container">
<label class="label">Added</label> <div style="flex: 1 1 0">
<ng-container *ngIf="download.added"> <div class="field is-grouped">
{{ download.added | date: "fullDate" }} {{ download.added | date: "mediumTime" }} <div class="control">
</ng-container> <button class="button is-primary" (click)="showDownloadRetryModal(download.downloadId)">
<ng-container *ngIf="!download.added">(not added yet) </ng-container> Retry Download
</div> </button>
<div class="field"> </div>
<label class="label">Download Queued</label> </div>
<ng-container *ngIf="download.downloadQueued"> @if (download.error) {
{{ download.downloadQueued | date: "fullDate" }} <div class="field">
{{ download.downloadQueued | date: "mediumTime" }} <label class="label">Error</label>
</ng-container> {{ download.error }}
<ng-container *ngIf="!download.downloadQueued">(not queued for downloading yet) </ng-container> </div>
</div> }
<div class="field"> <div class="field">
<label class="label">Download Started</label> <label class="label">Real-Debrid Unrestricted Link</label>
<ng-container *ngIf="download.downloadStarted"> @if (download.link) {
{{ download.downloadStarted | date: "fullDate" }} <a href="{{ download.link }}" target="_blank"> {{ download.link | decodeURI }}</a>
{{ download.downloadStarted | date: "mediumTime" }} }
</ng-container> </div>
<ng-container *ngIf="!download.downloadStarted">(not started downloading yet) </ng-container> <div class="field">
</div> <label class="label">Real-Debrid Link</label>
<div class="field"> {{ download.path }}
<label class="label">Download Finished</label> </div>
<ng-container *ngIf="download.downloadFinished"> <div class="field">
{{ download.downloadFinished | date: "fullDate" }} <label class="label">Download</label>
{{ download.downloadFinished | date: "mediumTime" }} {{ download.bytesDone | filesize }} / {{ download.bytesTotal | filesize }} ({{
</ng-container> download.speed | filesize
<ng-container *ngIf="!download.downloadFinished">(not finished yet) </ng-container> }}/s)
</div> </div>
<div class="field"> <div class="field">
<label class="label">Unpacking Queued</label> <label class="label">Retry Count</label>
<ng-container *ngIf="download.unpackingQueued"> {{ download.retryCount }} / {{ torrent.downloadRetryAttempts }}
{{ download.unpackingQueued | date: "fullDate" }} </div>
{{ download.unpackingQueued | date: "mediumTime" }} </div>
</ng-container> <div style="flex: 1 1 0">
<ng-container *ngIf="!download.unpackingQueued">(not queued for unpacking yet) </ng-container> <div class="field">
</div> <label class="label">Added</label>
<div class="field"> @if (download.added) {
<label class="label">Unpacking Started</label> {{ download.added | date: "fullDate" }} {{ download.added | date: "mediumTime" }}
<ng-container *ngIf="download.unpackingStarted"> } @else {
{{ download.unpackingStarted | date: "fullDate" }} (not added yet)
{{ download.unpackingStarted | date: "mediumTime" }} }
</ng-container> </div>
<ng-container *ngIf="!download.unpackingStarted">(not started unpacking yet) </ng-container> <div class="field">
</div> <label class="label">Download Queued</label>
<div class="field"> @if (download.downloadQueued) {
<label class="label">Unpacking Finished</label> {{ download.downloadQueued | date: "fullDate" }}
<ng-container *ngIf="download.unpackingFinished"> {{ download.downloadQueued | date: "mediumTime" }}
{{ download.unpackingFinished | date: "fullDate" }} } @else {
{{ download.unpackingFinished | date: "mediumTime" }} (not queued for downloading yet)
</ng-container> }
<ng-container *ngIf="!download.unpackingFinished">(not finished unpacking yet) </ng-container> </div>
</div> <div class="field">
<div class="field"> <label class="label">Download Started</label>
<label class="label">Completed</label> @if (download.downloadStarted) {
<ng-container *ngIf="download.completed"> {{ download.downloadStarted | date: "fullDate" }}
{{ download.completed | date: "fullDate" }} {{ download.downloadStarted | date: "mediumTime" }}
{{ download.completed | date: "mediumTime" }} } @else {
</ng-container> (not started downloading yet)
<ng-container *ngIf="!download.completed">(not completed yet) </ng-container> }
</div> </div>
</div> <div class="field">
</div> <label class="label">Download Finished</label>
</td> @if (download.downloadFinished) {
</tr> {{ download.downloadFinished | date: "fullDate" }}
</ng-container> {{ download.downloadFinished | date: "mediumTime" }}
</tbody> } @else {
</table> (not finished yet)
</div> }
</div>
<div class="field">
<label class="label">Unpacking Queued</label>
@if (download.unpackingQueued) {
{{ download.unpackingQueued | date: "fullDate" }}
{{ download.unpackingQueued | date: "mediumTime" }}
} @else {
(not queued for unpacking yet)
}
</div>
<div class="field">
<label class="label">Unpacking Started</label>
@if (download.unpackingStarted) {
{{ download.unpackingStarted | date: "fullDate" }}
{{ download.unpackingStarted | date: "mediumTime" }}
} @else {
(not started unpacking yet)
}
</div>
<div class="field">
<label class="label">Unpacking Finished</label>
@if (download.unpackingFinished) {
{{ download.unpackingFinished | date: "fullDate" }}
{{ download.unpackingFinished | date: "mediumTime" }}
} @else {
(not finished unpacking yet)
}
</div>
<div class="field">
<label class="label">Completed</label>
@if (download.completed) {
{{ download.completed | date: "fullDate" }}
{{ download.completed | date: "mediumTime" }}
} @else {
(not completed yet)
}
</div>
</div>
</div>
</td>
</tr>
}
}
</tbody>
</table>
</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