Upgrade packages.
Migrate Angular to new injector constructors.
This commit is contained in:
parent
351e1eb7c8
commit
683ac44c12
34 changed files with 1094 additions and 1788 deletions
|
|
@ -1,14 +1,14 @@
|
|||
const minorOnly = ['eslint'];
|
||||
const minorOnly = ["eslint"];
|
||||
const patchOnly = [];
|
||||
|
||||
module.exports = {
|
||||
target: (name) => {
|
||||
if (minorOnly.indexOf(name) > -1) {
|
||||
return 'minor';
|
||||
return "minor";
|
||||
}
|
||||
if (patchOnly.indexOf(name) > -1) {
|
||||
return 'patch';
|
||||
return "patch";
|
||||
}
|
||||
return 'latest';
|
||||
}
|
||||
return "latest";
|
||||
},
|
||||
};
|
||||
|
|
|
|||
2548
client/package-lock.json
generated
2548
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -8,19 +8,20 @@
|
|||
"watch": "ng build --watch --configuration development",
|
||||
"update": "ng update --force --allow-dirty @angular/cli @angular/core @angular/cdk",
|
||||
"lint": "ng lint",
|
||||
"prettier": "prettier --write \"./**/*.{ts,html,json}\""
|
||||
"prettier": "prettier --write .",
|
||||
"precommit": "npm run prettier && npm run lint"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^21.1.4",
|
||||
"@angular/cdk": "^21.1.4",
|
||||
"@angular/common": "^21.1.4",
|
||||
"@angular/compiler": "^21.1.4",
|
||||
"@angular/core": "^21.1.4",
|
||||
"@angular/forms": "^21.1.4",
|
||||
"@angular/platform-browser": "^21.1.4",
|
||||
"@angular/platform-browser-dynamic": "^21.1.4",
|
||||
"@angular/router": "^21.1.4",
|
||||
"@angular/animations": "^21.2.4",
|
||||
"@angular/cdk": "^21.2.2",
|
||||
"@angular/common": "^21.2.4",
|
||||
"@angular/compiler": "^21.2.4",
|
||||
"@angular/core": "^21.2.4",
|
||||
"@angular/forms": "^21.2.4",
|
||||
"@angular/platform-browser": "^21.2.4",
|
||||
"@angular/platform-browser-dynamic": "^21.2.4",
|
||||
"@angular/router": "^21.2.4",
|
||||
"@fortawesome/fontawesome-free": "^7.2.0",
|
||||
"@microsoft/signalr": "^10.0.0",
|
||||
"bulma": "^1.0.4",
|
||||
|
|
@ -29,24 +30,24 @@
|
|||
"filesize": "^11.0.13",
|
||||
"rxjs": "^7.8.2",
|
||||
"tslib": "^2.8.1",
|
||||
"zone.js": "^0.16.0"
|
||||
"zone.js": "^0.16.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-eslint/builder": "21.2.0",
|
||||
"@angular-eslint/eslint-plugin": "21.2.0",
|
||||
"@angular-eslint/eslint-plugin-template": "21.2.0",
|
||||
"@angular-eslint/schematics": "21.2.0",
|
||||
"@angular-eslint/template-parser": "21.2.0",
|
||||
"@angular/build": "^21.1.4",
|
||||
"@angular/cli": "^21.1.4",
|
||||
"@angular/compiler-cli": "^21.1.4",
|
||||
"@angular/language-service": "^21.1.4",
|
||||
"@angular-eslint/builder": "21.3.0",
|
||||
"@angular-eslint/eslint-plugin": "21.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "21.3.0",
|
||||
"@angular-eslint/schematics": "21.3.0",
|
||||
"@angular-eslint/template-parser": "21.3.0",
|
||||
"@angular/build": "^21.2.2",
|
||||
"@angular/cli": "^21.2.2",
|
||||
"@angular/compiler-cli": "^21.2.4",
|
||||
"@angular/language-service": "^21.2.4",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/file-saver-es": "^2.0.3",
|
||||
"@types/node": "^25.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
||||
"@typescript-eslint/parser": "^8.55.0",
|
||||
"eslint": "^9.39.2",
|
||||
"@types/node": "^25.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.57.0",
|
||||
"@typescript-eslint/parser": "^8.57.0",
|
||||
"eslint": "^9.39.4",
|
||||
"prettier": "^3.8.1",
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@
|
|||
<div class="field" style="margin-top: 10px">
|
||||
<div class="control">
|
||||
<button class="button is-success" [disabled]="saving" [ngClass]="{ 'is-loading': saving }" (click)="ok()">
|
||||
@if (type === 'torrent') {
|
||||
@if (type === "torrent") {
|
||||
<span>Add Torrent</span>
|
||||
} @else {
|
||||
<span>Add NZB</span>
|
||||
|
|
@ -109,9 +109,7 @@
|
|||
<option [ngValue]="3">Synology DownloadStation</option>
|
||||
</select>
|
||||
</div>
|
||||
<p class="help">
|
||||
Select which downloader is used to download this from the debrid provider to your host.
|
||||
</p>
|
||||
<p class="help">Select which downloader is used to download this from the debrid provider to your host.</p>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
|
|
@ -123,8 +121,8 @@
|
|||
</select>
|
||||
</div>
|
||||
<p class="help">
|
||||
When a download is finished on the provider, perform this action. Use this setting if you only want
|
||||
to add files to your debrid provider but not download them to the host.
|
||||
When a download is finished on the provider, perform this action. Use this setting if you only want to add files
|
||||
to your debrid provider but not download them to the host.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -213,8 +211,8 @@
|
|||
<input class="input" type="number" [(ngModel)]="finishedActionDelay" />
|
||||
</div>
|
||||
<p class="help">
|
||||
When a download is finished on the provider, perform this action. Use this setting if you only want
|
||||
to add files to your debrid provider but not download them to the host.
|
||||
When a download is finished on the provider, perform this action. Use this setting if you only want to add files
|
||||
to your debrid provider but not download them to the host.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -247,9 +245,7 @@
|
|||
<div class="control">
|
||||
<input class="input" type="number" step="1" [(ngModel)]="priority" />
|
||||
</div>
|
||||
<p class="help">
|
||||
Set the priority where 1 is the highest. When empty it will be assigned the lowest priority.
|
||||
</p>
|
||||
<p class="help">Set the priority where 1 is the highest. When empty it will be assigned the lowest priority.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -289,8 +285,8 @@
|
|||
<input class="input" type="number" max="100000" min="0" step="1" [(ngModel)]="torrentLifetime" />
|
||||
</div>
|
||||
<p class="help">
|
||||
The maximum lifetime of a download in minutes. When this time has passed, mark it as error. If the
|
||||
download is completed, the lifetime setting will not apply. 0 to disable.
|
||||
The maximum lifetime of a download in minutes. When this time has passed, mark it as error. If the download is
|
||||
completed, the lifetime setting will not apply. 0 to disable.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -303,4 +299,3 @@
|
|||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { TorrentService } from 'src/app/torrent.service';
|
||||
import { DownloadType, Torrent, TorrentFileAvailability } from '../models/torrent.model';
|
||||
|
|
@ -15,6 +15,11 @@ import { NgClass } from '@angular/common';
|
|||
standalone: true,
|
||||
})
|
||||
export class AddNewTorrentComponent implements OnInit {
|
||||
private router = inject(Router);
|
||||
private torrentService = inject(TorrentService);
|
||||
private settingsService = inject(SettingsService);
|
||||
private activatedRoute = inject(ActivatedRoute);
|
||||
|
||||
public type: 'torrent' | 'nzb' = 'torrent';
|
||||
public fileName: string;
|
||||
public magnetLink: string;
|
||||
|
|
@ -53,13 +58,6 @@ export class AddNewTorrentComponent implements OnInit {
|
|||
|
||||
private selectedFile: File;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private torrentService: TorrentService,
|
||||
private settingsService: SettingsService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.activatedRoute.queryParams.subscribe((params) => {
|
||||
if (params['type'] === 'nzb') {
|
||||
|
|
@ -89,9 +87,7 @@ export class AddNewTorrentComponent implements OnInit {
|
|||
.map((c) => c.trim())
|
||||
.filter((c) => c.length > 0)
|
||||
.filter((c, i, arr) => arr.findIndex((a) => a.toLowerCase() === c.toLowerCase()) === i);
|
||||
const matchedCategory = this.categories.find(
|
||||
(c) => c.toLowerCase() === (this.category ?? '').toLowerCase(),
|
||||
);
|
||||
const matchedCategory = this.categories.find((c) => c.toLowerCase() === (this.category ?? '').toLowerCase());
|
||||
if (matchedCategory) {
|
||||
this.category = matchedCategory;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { Component } from '@angular/core';
|
|||
import { RouterOutlet } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: '<router-outlet></router-outlet>',
|
||||
styles: [],
|
||||
imports: [RouterOutlet],
|
||||
selector: 'app-root',
|
||||
template: '<router-outlet></router-outlet>',
|
||||
styles: [],
|
||||
imports: [RouterOutlet],
|
||||
})
|
||||
export class AppComponent {}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class AuthInterceptor implements HttpInterceptor {
|
||||
constructor(private router: Router) {}
|
||||
private router = inject(Router);
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return next.handle(req).pipe(
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@Inject(APP_BASE_HREF) private baseHref: string,
|
||||
) {}
|
||||
private http = inject(HttpClient);
|
||||
private baseHref = inject(APP_BASE_HREF);
|
||||
|
||||
public isLoggedIn(): Observable<boolean> {
|
||||
return this.http.get<boolean>(`${this.baseHref}Api/Authentication/IsLoggedIn`);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({ name: 'decodeURI', })
|
||||
@Pipe({ name: 'decodeURI' })
|
||||
export class DecodeURIPipe implements PipeTransform {
|
||||
transform(input: any) {
|
||||
return decodeURI(input);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Pipe, PipeTransform, inject } from '@angular/core';
|
||||
import { Download } from './models/download.model';
|
||||
import { FileSizePipe } from './filesize.pipe';
|
||||
|
||||
@Pipe({ name: 'downloadStatus' })
|
||||
export class DownloadStatusPipe implements PipeTransform {
|
||||
constructor(private pipe: FileSizePipe) {}
|
||||
private pipe = inject(FileSizePipe);
|
||||
|
||||
transform(value: Download): string {
|
||||
if (!value) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { filesize } from 'filesize';
|
||||
|
||||
@Pipe({ name: 'filesize', })
|
||||
@Pipe({ name: 'filesize' })
|
||||
export class FileSizePipe implements PipeTransform {
|
||||
private static transformOne(value: number, options?: any): string {
|
||||
return filesize(value, options).toString();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AuthService } from '../auth.service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
|
@ -12,16 +12,14 @@ import { NgClass } from '@angular/common';
|
|||
standalone: true,
|
||||
})
|
||||
export class LoginComponent {
|
||||
private authService = inject(AuthService);
|
||||
private router = inject(Router);
|
||||
|
||||
public userName: string;
|
||||
public password: string;
|
||||
public error: string;
|
||||
public loggingIn: boolean;
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
public setUserName(event: Event): void {
|
||||
this.userName = (event.target as any).value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
.notification {
|
||||
margin-top: 52px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { NavigationEnd, Router, RouterLink } from '@angular/router';
|
||||
import { AuthService } from '../auth.service';
|
||||
import { Profile } from '../models/profile.model';
|
||||
|
|
@ -13,17 +13,17 @@ import { NgClass, DatePipe } from '@angular/common';
|
|||
standalone: true,
|
||||
})
|
||||
export class NavbarComponent implements OnInit {
|
||||
private settingsService = inject(SettingsService);
|
||||
private authService = inject(AuthService);
|
||||
private router = inject(Router);
|
||||
|
||||
public showMobileMenu = false;
|
||||
|
||||
public profile: Profile;
|
||||
public providerLink: string;
|
||||
public version: string;
|
||||
|
||||
constructor(
|
||||
private settingsService: SettingsService,
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
) {
|
||||
constructor() {
|
||||
this.router.events.subscribe((event) => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.showMobileMenu = false;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Pipe, PipeTransform, SecurityContext, VERSION } from '@angular/core';
|
||||
import { Pipe, PipeTransform, SecurityContext, VERSION, inject } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Pipe({ name: 'nl2br', })
|
||||
@Pipe({ name: 'nl2br' })
|
||||
export class Nl2BrPipe implements PipeTransform {
|
||||
constructor(private sanitizer: DomSanitizer) {}
|
||||
private sanitizer = inject(DomSanitizer);
|
||||
|
||||
transform(value: string, sanitizeBeforehand?: boolean): string {
|
||||
if (typeof value !== 'string') {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { AuthService } from '../auth.service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NgClass } from '@angular/common';
|
||||
|
|
@ -11,7 +11,7 @@ import { NgClass } from '@angular/common';
|
|||
standalone: true,
|
||||
})
|
||||
export class ProfileComponent {
|
||||
constructor(private authService: AuthService) {}
|
||||
private authService = inject(AuthService);
|
||||
|
||||
public username: string;
|
||||
public password: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Profile } from './models/profile.model';
|
||||
import { Setting } from './models/setting.model';
|
||||
|
|
@ -10,10 +10,8 @@ import { Version } from './models/version.model';
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class SettingsService {
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@Inject(APP_BASE_HREF) private baseHref: string,
|
||||
) {}
|
||||
private http = inject(HttpClient);
|
||||
private baseHref = inject(APP_BASE_HREF);
|
||||
|
||||
public get(): Observable<Setting[]> {
|
||||
return this.http.get<Setting[]>(`${this.baseHref}Api/Settings`);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { SettingsService } from 'src/app/settings.service';
|
||||
import { Setting } from '../models/setting.model';
|
||||
import { NgClass, KeyValuePipe } from '@angular/common';
|
||||
|
|
@ -14,6 +14,8 @@ import { FileSizePipe } from '../filesize.pipe';
|
|||
standalone: true,
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
private settingsService = inject(SettingsService);
|
||||
|
||||
public activeTab = 0;
|
||||
|
||||
public tabs: Setting[] = [];
|
||||
|
|
@ -35,8 +37,6 @@ export class SettingsComponent implements OnInit {
|
|||
|
||||
public canRegisterMagnetHandler = false;
|
||||
|
||||
constructor(private settingsService: SettingsService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.reset();
|
||||
this.canRegisterMagnetHandler = !!(window.isSecureContext && 'registerProtocolHandler' in navigator);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { AuthService } from '../auth.service';
|
||||
import { NgClass } from '@angular/common';
|
||||
|
|
@ -12,6 +12,9 @@ import { FormsModule } from '@angular/forms';
|
|||
standalone: true,
|
||||
})
|
||||
export class SetupComponent {
|
||||
private authService = inject(AuthService);
|
||||
private router = inject(Router);
|
||||
|
||||
public userName: string;
|
||||
public password: string;
|
||||
public provider = 0;
|
||||
|
|
@ -22,11 +25,6 @@ export class SetupComponent {
|
|||
|
||||
public step: number = 1;
|
||||
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
public setup(): void {
|
||||
this.error = null;
|
||||
this.working = true;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({ name: 'sort', })
|
||||
@Pipe({ name: 'sort' })
|
||||
export class SortPipe implements PipeTransform {
|
||||
transform(array: any[], field: string, order: 'asc' | 'desc' = 'asc'): any[] {
|
||||
if (!Array.isArray(array)) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Pipe, PipeTransform, inject } from '@angular/core';
|
||||
import { RealDebridStatus, Torrent } from './models/torrent.model';
|
||||
import { FileSizePipe } from './filesize.pipe';
|
||||
|
||||
@Pipe({ name: 'status' })
|
||||
export class TorrentStatusPipe implements PipeTransform {
|
||||
constructor(private pipe: FileSizePipe) {}
|
||||
private pipe = inject(FileSizePipe);
|
||||
|
||||
transform(torrent: Torrent): string {
|
||||
if (torrent.error) {
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@
|
|||
<br />
|
||||
Available: {{ diskSpaceStatus.availableSpaceGB }} GB | Resume at: {{ diskSpaceStatus.thresholdGB * 2 }} GB
|
||||
<br />
|
||||
<small>Last check: {{ diskSpaceStatus.lastCheckTime | date: 'short' }}</small>
|
||||
<small>Last check: {{ diskSpaceStatus.lastCheckTime | date: "short" }}</small>
|
||||
</div>
|
||||
}
|
||||
@if (rateLimitStatus?.nextDequeueTime) {
|
||||
<div class="notification is-warning">
|
||||
<strong>Debrid provider rate limit reached</strong>
|
||||
<br />
|
||||
Processing paused until {{ rateLimitStatus.nextDequeueTime | date: 'medium' }}
|
||||
Processing paused until {{ rateLimitStatus.nextDequeueTime | date: "medium" }}
|
||||
@if (rateLimitStatus.secondsRemaining > 0) {
|
||||
({{ rateLimitStatus.secondsRemaining | number: '1.0-0' }} seconds remaining)
|
||||
({{ rateLimitStatus.secondsRemaining | number: "1.0-0" }} seconds remaining)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -107,10 +107,16 @@
|
|||
}
|
||||
</div>
|
||||
<div class="mobile-card-details">
|
||||
<span class="mobile-card-detail"><span class="detail-label">Size</span> {{ torrent.rdSize | filesize }}</span>
|
||||
<span class="mobile-card-detail"
|
||||
><span class="detail-label">Size</span> {{ torrent.rdSize | filesize }}</span
|
||||
>
|
||||
<span class="mobile-card-detail"><span class="detail-label">Seeders</span> {{ torrent.rdSeeders }}</span>
|
||||
<span class="mobile-card-detail"><span class="detail-label">Files</span> {{ torrent.files.length | number }}</span>
|
||||
<span class="mobile-card-detail"><span class="detail-label">Downloads</span> {{ torrent.downloads.length | number }}</span>
|
||||
<span class="mobile-card-detail"
|
||||
><span class="detail-label">Files</span> {{ torrent.files.length | number }}</span
|
||||
>
|
||||
<span class="mobile-card-detail"
|
||||
><span class="detail-label">Downloads</span> {{ torrent.downloads.length | number }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ table {
|
|||
flex-shrink: 0;
|
||||
padding-top: 0.125rem;
|
||||
|
||||
input[type='checkbox'] {
|
||||
input[type="checkbox"] {
|
||||
width: 1.1rem;
|
||||
height: 1.1rem;
|
||||
cursor: pointer;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Torrent } from '../models/torrent.model';
|
||||
import { DiskSpaceStatus } from '../models/disk-space-status.model';
|
||||
|
|
@ -19,6 +19,9 @@ import { FileSizePipe } from '../filesize.pipe';
|
|||
standalone: true,
|
||||
})
|
||||
export class TorrentTableComponent implements OnInit, OnDestroy {
|
||||
private router = inject(Router);
|
||||
private torrentService = inject(TorrentService);
|
||||
|
||||
public torrents: Torrent[] = [];
|
||||
public selectedTorrents: string[] = [];
|
||||
public error: string;
|
||||
|
|
@ -57,11 +60,6 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
|||
private mobileQuery: MediaQueryList;
|
||||
private mobileQueryListener: (e: MediaQueryListEvent) => void;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private torrentService: TorrentService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.mobileQuery = window.matchMedia('(max-width: 768px)');
|
||||
this.isMobile = this.mobileQuery.matches;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import * as signalR from '@microsoft/signalr';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { Torrent, TorrentFileAvailability } from './models/torrent.model';
|
||||
|
|
@ -11,16 +11,16 @@ import { APP_BASE_HREF } from '@angular/common';
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class TorrentService {
|
||||
private http = inject(HttpClient);
|
||||
private baseHref = inject(APP_BASE_HREF);
|
||||
|
||||
public update$: Subject<Torrent[]> = new Subject();
|
||||
public diskSpaceStatus$: Subject<DiskSpaceStatus> = new Subject();
|
||||
public rateLimitStatus$: Subject<RateLimitStatus> = new Subject();
|
||||
|
||||
private connection: signalR.HubConnection;
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
@Inject(APP_BASE_HREF) private baseHref: string,
|
||||
) {
|
||||
constructor() {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { saveAs } from 'file-saver-es';
|
||||
import { Torrent } from '../models/torrent.model';
|
||||
|
|
@ -28,6 +28,10 @@ import { FileSizePipe } from '../filesize.pipe';
|
|||
standalone: true,
|
||||
})
|
||||
export class TorrentComponent implements OnInit {
|
||||
private activatedRoute = inject(ActivatedRoute);
|
||||
private router = inject(Router);
|
||||
private torrentService = inject(TorrentService);
|
||||
|
||||
public torrent: Torrent;
|
||||
|
||||
public activeTab: number = 0;
|
||||
|
|
@ -66,12 +70,6 @@ export class TorrentComponent implements OnInit {
|
|||
|
||||
public updating: boolean;
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private torrentService: TorrentService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.activatedRoute.params.subscribe((params) => {
|
||||
const torrentId = params['id'];
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { enableProdMode, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
|
||||
import { environment } from './environments/environment';
|
||||
import { FileSizePipe } from './app/filesize.pipe';
|
||||
import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
||||
|
|
@ -18,12 +17,12 @@ if (environment.production) {
|
|||
}
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
provideZoneChangeDetection(),importProvidersFrom(BrowserModule, AppRoutingModule, FormsModule, ClipboardModule),
|
||||
FileSizePipe,
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||
{ provide: APP_BASE_HREF, useValue: (window as any)['_app_base'] || '/' },
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
]
|
||||
})
|
||||
.catch((err) => console.error(err));
|
||||
providers: [
|
||||
provideZoneChangeDetection(),
|
||||
importProvidersFrom(BrowserModule, AppRoutingModule, FormsModule, ClipboardModule),
|
||||
FileSizePipe,
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||
{ provide: APP_BASE_HREF, useValue: (window as any)['_app_base'] || '/' },
|
||||
provideHttpClient(withInterceptorsFromDi()),
|
||||
],
|
||||
}).catch((err) => console.error(err));
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.3">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="SharpCompress" Version="0.42.1" />
|
||||
<PackageReference Include="SharpCompress" Version="[0.42.1]" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions" Version="22.1.0" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.1.0" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.0" />
|
||||
|
|
|
|||
|
|
@ -330,7 +330,8 @@ public class TorBoxDebridClientTest
|
|||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient", ItExpr.IsAny<String>()).Returns(torBoxClientMock.Object);
|
||||
|
||||
usenetApiMock.Setup(m => m.AddFileAsync(bytes, It.IsAny<Int32>(), name, It.IsAny<String?>(), It.IsAny<Boolean>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<UsenetAddResult> { Data = new UsenetAddResult { Hash = "new-hash" } });
|
||||
.ReturnsAsync(new Response<UsenetAddResult> { Data = new()
|
||||
{ Hash = "new-hash" } });
|
||||
|
||||
// Act
|
||||
var result = await clientMock.Object.AddNzbFile(bytes, name);
|
||||
|
|
@ -605,7 +606,9 @@ public class TorBoxDebridClientTest
|
|||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient", ItExpr.IsAny<String>()).Returns(torBoxClientMock.Object);
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User> { Data = new()
|
||||
{ Settings = new()
|
||||
{ SeedTorrents = 5 } } });
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new TorBoxException("active_limit", "Active limit reached"));
|
||||
|
|
@ -632,10 +635,12 @@ public class TorBoxDebridClientTest
|
|||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient", ItExpr.IsAny<String>()).Returns(torBoxClientMock.Object);
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User> { Data = new()
|
||||
{ Settings = new()
|
||||
{ SeedTorrents = 5 } } });
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new Exception("slow_down"));
|
||||
.ThrowsAsync(new("slow_down"));
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<RateLimitException>(() => clientMock.Object.AddTorrentMagnet(magnetLink));
|
||||
|
|
@ -657,7 +662,9 @@ public class TorBoxDebridClientTest
|
|||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient", ItExpr.IsAny<String>()).Returns(torBoxClientMock.Object);
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User> { Data = new()
|
||||
{ Settings = new()
|
||||
{ SeedTorrents = 5 } } });
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new RateLimitException("TorBox rate limit exceeded", TimeSpan.FromMinutes(60)));
|
||||
|
|
@ -683,10 +690,12 @@ public class TorBoxDebridClientTest
|
|||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient", ItExpr.IsAny<String>()).Returns(torBoxClientMock.Object);
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User> { Data = new()
|
||||
{ Settings = new()
|
||||
{ SeedTorrents = 5 } } });
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddMagnetAsync(magnetLink, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new Exception("Wrapped", new RateLimitException("TorBox rate limit exceeded", TimeSpan.FromMinutes(60))));
|
||||
.ThrowsAsync(new("Wrapped", new RateLimitException("TorBox rate limit exceeded", TimeSpan.FromMinutes(60))));
|
||||
|
||||
// Act & Assert
|
||||
var ex = await Assert.ThrowsAsync<RateLimitException>(() => clientMock.Object.AddTorrentMagnet(magnetLink));
|
||||
|
|
@ -708,7 +717,9 @@ public class TorBoxDebridClientTest
|
|||
clientMock.Protected().Setup<ITorBoxNetClient>("GetClient", ItExpr.IsAny<String>()).Returns(torBoxClientMock.Object);
|
||||
|
||||
userApiMock.Setup(m => m.GetAsync(true, It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new Response<User> { Data = new User { Settings = new UserSettings { SeedTorrents = 5 } } });
|
||||
.ReturnsAsync(new Response<User> { Data = new()
|
||||
{ Settings = new()
|
||||
{ SeedTorrents = 5 } } });
|
||||
|
||||
torrentsApiMock.Setup(m => m.AddFileAsync(bytes, 5, It.IsAny<Boolean>(), It.IsAny<String?>(), false, It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(new TorBoxException("active_limit", "Active limit reached"));
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public static class DiConfig
|
|||
|
||||
if (delay >= timeout)
|
||||
{
|
||||
return new ValueTask<TimeSpan?>((TimeSpan?)null);
|
||||
return new((TimeSpan?)null);
|
||||
}
|
||||
|
||||
return new(delay);
|
||||
|
|
|
|||
|
|
@ -12,18 +12,18 @@
|
|||
<PackageReference Include="AllDebrid.NET" Version="1.0.18" />
|
||||
<PackageReference Include="Aria2.NET" Version="1.0.6" />
|
||||
<PackageReference Include="DebridLinkFr.NET" Version="1.0.4" />
|
||||
<PackageReference Include="Downloader" Version="4.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.3.0" />
|
||||
<PackageReference Include="Downloader" Version="5.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.4.0" />
|
||||
<PackageReference Include="MonoTorrent" Version="3.0.2" />
|
||||
<PackageReference Include="Polly" Version="8.6.5" />
|
||||
<PackageReference Include="Polly" Version="8.6.6" />
|
||||
<PackageReference Include="Premiumize.NET" Version="1.0.10" />
|
||||
<PackageReference Include="RateLimitHeaders.Polly" Version="1.0.0" />
|
||||
<PackageReference Include="RD.NET" Version="2.1.11" />
|
||||
<PackageReference Include="Serilog" Version="4.3.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.42.1" />
|
||||
<PackageReference Include="Synology.Api.Client" Version="0.3.93" />
|
||||
<PackageReference Include="SharpCompress" Version="[0.42.1]" />
|
||||
<PackageReference Include="Synology.Api.Client" Version="[0.3.93]" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions" Version="22.1.0" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.0" />
|
||||
<PackageReference Include="TorBox.NET" Version="1.6.2" />
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="SharpCompress" Version="0.42.1" />
|
||||
<PackageReference Include="SharpCompress" Version="[0.42.1]" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
|||
|
|
@ -30,18 +30,18 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.3">
|
||||
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="10.0.5" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.5" />
|
||||
<PackageReference Include="Serilog" Version="4.3.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.42.1" />
|
||||
<PackageReference Include="SharpCompress" Version="[0.42.1]" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in a new issue