Fixed status columns, separate downloads and files in the detail list.
Add UnpackLimit setting.
This commit is contained in:
parent
21ad2ebfd6
commit
67532645aa
26 changed files with 253 additions and 236 deletions
|
|
@ -1,24 +1,24 @@
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { NgxFilesizeModule, FileSizePipe } from 'ngx-filesize';
|
||||
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { curray } from 'curray';
|
||||
import { FileSizePipe, NgxFilesizeModule } from 'ngx-filesize';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { MainLayoutComponent } from './main-layout/main-layout.component';
|
||||
import { NavbarComponent } from './navbar/navbar.component';
|
||||
import { AddNewTorrentComponent } from './navbar/add-new-torrent/add-new-torrent.component';
|
||||
import { TorrentTableComponent } from './torrent-table/torrent-table.component';
|
||||
import { TorrentRowComponent } from './torrent-row/torrent-row.component';
|
||||
import { TorrentFileComponent } from './torrent-file/torrent-file.component';
|
||||
import { SettingsComponent } from './navbar/settings/settings.component';
|
||||
import { TorrentStatusPipe } from './torrent-status.pipe';
|
||||
import { FileStatusPipe } from './file-status.pipe';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { AuthInterceptor } from './auth.interceptor';
|
||||
import { curray } from 'curray';
|
||||
import { DownloadStatusPipe } from './download-status.pipe';
|
||||
import { LoginComponent } from './login/login.component';
|
||||
import { MainLayoutComponent } from './main-layout/main-layout.component';
|
||||
import { AddNewTorrentComponent } from './navbar/add-new-torrent/add-new-torrent.component';
|
||||
import { NavbarComponent } from './navbar/navbar.component';
|
||||
import { SettingsComponent } from './navbar/settings/settings.component';
|
||||
import { SetupComponent } from './setup/setup.component';
|
||||
import { TorrentDownloadComponent } from './torrent-download/torrent-download.component';
|
||||
import { TorrentFileComponent } from './torrent-file/torrent-file.component';
|
||||
import { TorrentRowComponent } from './torrent-row/torrent-row.component';
|
||||
import { TorrentStatusPipe } from './torrent-status.pipe';
|
||||
import { TorrentTableComponent } from './torrent-table/torrent-table.component';
|
||||
|
||||
curray();
|
||||
|
||||
|
|
@ -33,21 +33,13 @@ curray();
|
|||
TorrentFileComponent,
|
||||
SettingsComponent,
|
||||
TorrentStatusPipe,
|
||||
FileStatusPipe,
|
||||
DownloadStatusPipe,
|
||||
LoginComponent,
|
||||
SetupComponent,
|
||||
TorrentDownloadComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
NgxFilesizeModule,
|
||||
],
|
||||
providers: [
|
||||
FileSizePipe,
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||
],
|
||||
imports: [BrowserModule, AppRoutingModule, FormsModule, HttpClientModule, NgxFilesizeModule],
|
||||
providers: [FileSizePipe, { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
|||
54
client/src/app/download-status.pipe.ts
Normal file
54
client/src/app/download-status.pipe.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { FileSizePipe } from 'ngx-filesize';
|
||||
import { Download } from './models/download.model';
|
||||
|
||||
@Pipe({
|
||||
name: 'downloadStatus',
|
||||
})
|
||||
export class DownloadStatusPipe implements PipeTransform {
|
||||
constructor(private pipe: FileSizePipe) {}
|
||||
|
||||
transform(value: Download): string {
|
||||
if (!value) {
|
||||
return 'Pending';
|
||||
}
|
||||
|
||||
if (value.error) {
|
||||
return `Error: ${value.error}`;
|
||||
}
|
||||
|
||||
if (value.completed != null) {
|
||||
return 'Finished';
|
||||
}
|
||||
|
||||
if (value.unpackingFinished) {
|
||||
return 'Unpacking finished';
|
||||
}
|
||||
|
||||
if (value.unpackingStarted) {
|
||||
const progress = ((value.bytesDone / value.bytesTotal) * 100).toFixed(2);
|
||||
return `Unpacking ${progress || 0}%`;
|
||||
}
|
||||
|
||||
if (value.unpackingQueued) {
|
||||
return 'Unpacking queued';
|
||||
}
|
||||
|
||||
if (value.downloadFinished) {
|
||||
return 'Download finished';
|
||||
}
|
||||
|
||||
if (value.downloadStarted) {
|
||||
const progress = ((value.bytesDone / value.bytesTotal) * 100).toFixed(2);
|
||||
const speed = this.pipe.transform(value.speed, 'filesize');
|
||||
|
||||
return `Downloading ${progress || 0}% (${speed}/s)`;
|
||||
}
|
||||
|
||||
if (value.downloadQueued) {
|
||||
return 'Download queued';
|
||||
}
|
||||
|
||||
return 'Pending';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { FileSizePipe } from 'ngx-filesize';
|
||||
import { TorrentFile } from './models/torrent.model';
|
||||
|
||||
@Pipe({
|
||||
name: 'fileStatus',
|
||||
})
|
||||
export class FileStatusPipe implements PipeTransform {
|
||||
constructor(private pipe: FileSizePipe) {}
|
||||
|
||||
transform(value: TorrentFile): string {
|
||||
if (!value.download) {
|
||||
return 'Pending download';
|
||||
}
|
||||
|
||||
if (value.download.error) {
|
||||
return `Error: ${value.download.error}`;
|
||||
}
|
||||
|
||||
if (value.download.completed != null) {
|
||||
return 'Finished';
|
||||
}
|
||||
|
||||
if (value.download.unpackingFinished) {
|
||||
return 'Unpacking finished';
|
||||
}
|
||||
|
||||
if (value.download.unpackingStarted) {
|
||||
const progress = ((value.download.bytesDone / value.download.bytesTotal) * 100).toFixed(2);
|
||||
return `Unpacking ${progress || 0}%`;
|
||||
}
|
||||
|
||||
if (value.download.unpackingQueued) {
|
||||
return 'Unpacking queued';
|
||||
}
|
||||
|
||||
if (value.download.downloadFinished) {
|
||||
return 'Download finished';
|
||||
}
|
||||
|
||||
if (value.download.downloadStarted) {
|
||||
const progress = ((value.download.bytesDone / value.download.bytesTotal) * 100).toFixed(2);
|
||||
const speed = this.pipe.transform(value.download.speed, 'filesize');
|
||||
|
||||
return `Downloading ${progress || 0}% (${speed}/s)`;
|
||||
}
|
||||
|
||||
if (value.download.downloadQueued) {
|
||||
return 'Download queued';
|
||||
}
|
||||
|
||||
if (value.download.added) {
|
||||
return 'Pending';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
|
@ -56,8 +56,8 @@
|
|||
Remove torrent when finished downloading on host
|
||||
</label>
|
||||
</div>
|
||||
<div class="notification is-danger is-light" *ngIf="error?.length > 0">
|
||||
Cannot add torrent: {{ error }}
|
||||
<div class="notification is-danger is-light" *ngIf="error">
|
||||
Cannot add torrent: {{ error | json }}
|
||||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@
|
|||
<span class="icon">
|
||||
<i class="fas fa-euro-sign" aria-hidden="true"></i>
|
||||
</span>
|
||||
<span>Premium Status: {{ profile.expiration | date }}</span>
|
||||
<span *ngIf="profile.expiration">Premium Status: {{ profile.expiration | date }}</span>
|
||||
<span *ngIf="!profile.expiration" class="no-premium">Not premium</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
.no-premium {
|
||||
color: red;
|
||||
}
|
||||
|
|
@ -9,59 +9,38 @@
|
|||
<div class="field">
|
||||
<label class="label">Real-Debrid API Key</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
maxlength="100"
|
||||
[(ngModel)]="settingRealDebridApiKey"
|
||||
/>
|
||||
<input class="input" type="text" maxlength="100" [(ngModel)]="settingRealDebridApiKey" />
|
||||
</div>
|
||||
<p class="help">
|
||||
You can find your API key here:
|
||||
<a
|
||||
href="https://real-debrid.com/apitoken"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>https://real-debrid.com/apitoken</a
|
||||
<a href="https://real-debrid.com/apitoken" target="_blank" rel="noopener">https://real-debrid.com/apitoken</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Download folder</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
maxlength="1000"
|
||||
[(ngModel)]="settingDownloadFolder"
|
||||
/>
|
||||
<input class="input" type="text" maxlength="1000" [(ngModel)]="settingDownloadFolder" />
|
||||
</div>
|
||||
<p class="help">Path on the host where RealDebrid Client is hosted.</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Maximum parallel downloads</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
max="100"
|
||||
min="1"
|
||||
[(ngModel)]="settingDownloadLimit"
|
||||
/>
|
||||
<input class="input" type="number" max="100" min="1" [(ngModel)]="settingDownloadLimit" />
|
||||
</div>
|
||||
<p class="help">
|
||||
Maximum amount of torrents that get downloaded to your host at the
|
||||
same time.
|
||||
</p>
|
||||
<p class="help">Maximum amount of torrents that get downloaded to your host at the same time.</p>
|
||||
</div>
|
||||
<div class="notification is-danger is-light" *ngIf="error?.length > 0">
|
||||
Error saving settings: {{ error }}
|
||||
<div class="field">
|
||||
<label class="label">Maximum unpack processes</label>
|
||||
<div class="control">
|
||||
<input class="input" type="number" max="100" min="1" [(ngModel)]="settingUnpackLimit" />
|
||||
</div>
|
||||
<p class="help">Maximum amount of downloads that get unpacked on your host at the same time.</p>
|
||||
</div>
|
||||
<div class="notification is-danger is-light" *ngIf="error?.length > 0">Error saving settings: {{ error }}</div>
|
||||
|
||||
<div
|
||||
class="notification is-danger is-light"
|
||||
*ngIf="testFolderError?.length > 0"
|
||||
>
|
||||
<div class="notification is-danger is-light" *ngIf="testFolderError?.length > 0">
|
||||
Could not test your download folder<br />
|
||||
{{ testFolderError }}
|
||||
</div>
|
||||
|
|
@ -71,30 +50,13 @@
|
|||
</div>
|
||||
</section>
|
||||
<footer class="modal-card-foot">
|
||||
<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
|
||||
</button>
|
||||
<button
|
||||
class="button is-warning"
|
||||
(click)="test()"
|
||||
[disabled]="saving"
|
||||
[ngClass]="{ 'is-loading': saving }"
|
||||
>
|
||||
<button class="button is-warning" (click)="test()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">
|
||||
Test permissions
|
||||
</button>
|
||||
<button
|
||||
class="button"
|
||||
(click)="cancel()"
|
||||
[disabled]="saving"
|
||||
[ngClass]="{ 'is-loading': saving }"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button class="button" (click)="cancel()" [disabled]="saving" [ngClass]="{ 'is-loading': saving }">Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export class SettingsComponent implements OnInit {
|
|||
public settingRealDebridApiKey: string;
|
||||
public settingDownloadFolder: string;
|
||||
public settingDownloadLimit: number;
|
||||
public settingUnpackLimit: number;
|
||||
|
||||
constructor(private settingsService: SettingsService) {}
|
||||
|
||||
|
|
@ -45,6 +46,7 @@ export class SettingsComponent implements OnInit {
|
|||
this.settingRealDebridApiKey = this.getSetting(results, 'RealDebridApiKey');
|
||||
this.settingDownloadFolder = this.getSetting(results, 'DownloadFolder');
|
||||
this.settingDownloadLimit = parseInt(this.getSetting(results, 'DownloadLimit'), 10);
|
||||
this.settingUnpackLimit = parseInt(this.getSetting(results, 'UnpackLimit'), 10);
|
||||
},
|
||||
(err) => {
|
||||
this.error = err.error;
|
||||
|
|
@ -69,6 +71,10 @@ export class SettingsComponent implements OnInit {
|
|||
settingId: 'DownloadLimit',
|
||||
value: (this.settingDownloadLimit ?? 10).toString(),
|
||||
},
|
||||
{
|
||||
settingId: 'UnpackLimit',
|
||||
value: (this.settingUnpackLimit ?? 1).toString(),
|
||||
},
|
||||
];
|
||||
|
||||
this.settingsService.update(settings).subscribe(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<td colspan="4">
|
||||
<i class="fas fa-download" aria-hidden="true"></i>
|
||||
{{ download.link }}
|
||||
</td>
|
||||
<td>
|
||||
{{ download.bytesTotal | filesize }}
|
||||
</td>
|
||||
<td>
|
||||
{{ download | downloadStatus }}
|
||||
</td>
|
||||
<td></td>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
td {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Download } from '../models/download.model';
|
||||
|
||||
@Component({
|
||||
selector: '[app-torrent-download]',
|
||||
templateUrl: './torrent-download.component.html',
|
||||
styleUrls: ['./torrent-download.component.scss'],
|
||||
})
|
||||
export class TorrentDownloadComponent implements OnInit {
|
||||
@Input()
|
||||
public download: Download;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,6 @@
|
|||
<td>
|
||||
<td colspan="4">
|
||||
<i class="fas fa-file" aria-hidden="true"></i>
|
||||
{{ file.path }}
|
||||
</td>
|
||||
<td>{{ file.bytes | filesize }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>
|
||||
{{ file | fileStatus }}
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td colspan="10"></td>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Torrent, TorrentFile } from '../models/torrent.model';
|
||||
import { TorrentService } from '../torrent.service';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { TorrentFile } from '../models/torrent.model';
|
||||
|
||||
@Component({
|
||||
selector: '[app-torrent-file]',
|
||||
|
|
@ -11,7 +10,7 @@ export class TorrentFileComponent implements OnInit {
|
|||
@Input()
|
||||
public file: TorrentFile;
|
||||
|
||||
constructor(private torrentService: TorrentService) {}
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<td>{{ torrent.rdName }}</td>
|
||||
<td>
|
||||
{{ torrent.rdSize | filesize }}
|
||||
</td>
|
||||
<td>
|
||||
{{ torrent.files.length | number }}
|
||||
</td>
|
||||
|
|
@ -13,6 +10,9 @@
|
|||
<i class="fas fa-box-open" *ngIf="torrent.autoUnpack" title="Auto unpack"></i>
|
||||
<i class="fas fa-trash" *ngIf="torrent.autoDelete" title="Auto delete"></i>
|
||||
</td>
|
||||
<td>
|
||||
{{ torrent.rdSize | filesize }}
|
||||
</td>
|
||||
<td>
|
||||
{{ torrent | status }}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -13,59 +13,69 @@ export class TorrentStatusPipe implements PipeTransform {
|
|||
return 'Finished';
|
||||
}
|
||||
|
||||
if (torrent.downloads && torrent.downloads.length > 0) {
|
||||
const allFinished = torrent.downloads.all((m) => m.completed != null);
|
||||
if (allFinished) {
|
||||
return 'Finished';
|
||||
const allFinished = torrent.downloads.all((m) => m.completed != null);
|
||||
|
||||
if (allFinished) {
|
||||
return 'Finished';
|
||||
}
|
||||
|
||||
const errors = torrent.downloads.where((m) => m.error != null);
|
||||
|
||||
if (errors.length > 0) {
|
||||
return 'Error';
|
||||
}
|
||||
|
||||
const downloading = torrent.downloads.where((m) => m.downloadStarted && !m.downloadFinished);
|
||||
|
||||
if (downloading.length > 0) {
|
||||
const bytesDone = downloading.sum((m) => m.bytesDone);
|
||||
const bytesTotal = downloading.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = downloading.sum((m) => m.speed) / downloading.length;
|
||||
|
||||
let speed: string | string[] = '0';
|
||||
if (allSpeeds > 0) {
|
||||
speed = this.pipe.transform(allSpeeds, 'filesize');
|
||||
|
||||
return `Downloading (${progress.toFixed(2)}% - ${speed}/s)`;
|
||||
}
|
||||
}
|
||||
|
||||
const errors = torrent.downloads.where((m) => m.error != null);
|
||||
const downloading = torrent.downloads.where((m) => m.downloadStarted && !m.downloadFinished);
|
||||
const downloaded = torrent.downloads.where((m) => m.downloadFinished != null);
|
||||
const unpacking = torrent.downloads.where((m) => m.unpackingStarted && !m.unpackingFinished);
|
||||
const queuedForDownload = torrent.downloads.where((m) => m.downloadQueued && !m.downloadStarted);
|
||||
const queuedForUnpacking = torrent.downloads.where((m) => m.unpackingQueued && !m.unpackingStarted);
|
||||
const unpacking = torrent.downloads.where((m) => m.unpackingStarted && !m.unpackingFinished);
|
||||
|
||||
if (errors.length > 0) {
|
||||
return 'Error';
|
||||
if (unpacking.length > 0) {
|
||||
const bytesDone = unpacking.sum((m) => m.bytesDone);
|
||||
const bytesTotal = unpacking.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = unpacking.sum((m) => m.speed) / unpacking.length;
|
||||
|
||||
if (allSpeeds > 0) {
|
||||
return `Extracting (${progress.toFixed(2)}%)`;
|
||||
}
|
||||
}
|
||||
|
||||
if (downloading.length > 0) {
|
||||
const bytesDone = downloading.sum((m) => m.bytesDone);
|
||||
const bytesTotal = downloading.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = downloading.sum((m) => m.speed) / downloading.length;
|
||||
const queuedForUnpacking = torrent.downloads.where((m) => m.unpackingQueued && !m.unpackingStarted);
|
||||
|
||||
let speed: string | string[] = '0';
|
||||
if (allSpeeds > 0) {
|
||||
speed = this.pipe.transform(allSpeeds, 'filesize');
|
||||
if (queuedForUnpacking.length > 0) {
|
||||
return `Queued for unpacking`;
|
||||
}
|
||||
|
||||
return `Downloading (${progress.toFixed(2)}% - ${speed}/s)`;
|
||||
}
|
||||
}
|
||||
const queuedForDownload = torrent.downloads.where((m) => m.downloadQueued && !m.downloadStarted);
|
||||
|
||||
if (unpacking.length > 0) {
|
||||
const bytesDone = unpacking.sum((m) => m.bytesDone);
|
||||
const bytesTotal = unpacking.sum((m) => m.bytesTotal);
|
||||
let progress = (bytesDone / bytesTotal) * 100;
|
||||
let allSpeeds = unpacking.sum((m) => m.speed) / unpacking.length;
|
||||
if (queuedForDownload.length > 0) {
|
||||
return `Queued for downloading`;
|
||||
}
|
||||
|
||||
if (allSpeeds > 0) {
|
||||
return `Extracting (${progress.toFixed(2)}%)`;
|
||||
}
|
||||
}
|
||||
const unpacked = torrent.downloads.where((m) => m.unpackingFinished != null);
|
||||
|
||||
if (queuedForUnpacking.length > 0) {
|
||||
return `Queued for unpacking`;
|
||||
}
|
||||
if (unpacked.length > 0) {
|
||||
return `Files unpacked`;
|
||||
}
|
||||
|
||||
if (queuedForDownload.length > 0) {
|
||||
return `Queued for downloading`;
|
||||
}
|
||||
const downloaded = torrent.downloads.where((m) => m.downloadFinished != null);
|
||||
|
||||
if (downloaded.length > 0) {
|
||||
return `Files downloaded to host`;
|
||||
}
|
||||
if (downloaded.length > 0) {
|
||||
return `Files downloaded to host`;
|
||||
}
|
||||
|
||||
switch (torrent.rdStatus) {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
<div class="table-container">
|
||||
<table class="table is-fullwidth is-hoverable">
|
||||
<colgroup>
|
||||
<col style="width: calc(40% - 550px)" />
|
||||
<col style="width: 15%" />
|
||||
<col style="width: calc(60% - 650px)" />
|
||||
<col style="width: 100px" />
|
||||
<col style="width: 150px" />
|
||||
<col style="width: 150px" />
|
||||
<col style="width: 150px" />
|
||||
|
|
@ -16,10 +16,10 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Size</th>
|
||||
<th>Files</th>
|
||||
<th>Downloads</th>
|
||||
<th>Auto</th>
|
||||
<th>Size</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
|
|
@ -33,6 +33,13 @@
|
|||
(delete)="showDeleteModal($event)"
|
||||
></tr>
|
||||
<ng-container *ngIf="showFiles[torrent.torrentId]">
|
||||
<tr class="separator">
|
||||
<td colspan="10">Downloads</td>
|
||||
</tr>
|
||||
<tr app-torrent-download [download]="download" *ngFor="let download of torrent.downloads"></tr>
|
||||
<tr class="separator">
|
||||
<td colspan="10">Files in torrent</td>
|
||||
</tr>
|
||||
<tr app-torrent-file [file]="file" *ngFor="let file of torrent.files"></tr>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
|
|
|||
|
|
@ -2,4 +2,11 @@ table {
|
|||
tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
tr.separator {
|
||||
td {
|
||||
font-size: smaller;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,21 +45,6 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
|||
|
||||
public selectTorrent(torrent: Torrent): void {
|
||||
this.showFiles[torrent.torrentId] = !this.showFiles[torrent.torrentId];
|
||||
|
||||
if (this.showFiles[torrent.torrentId]) {
|
||||
this.torrentService.getDetails(torrent.torrentId).subscribe((result) => {
|
||||
torrent.files = result.files;
|
||||
torrent.downloads = result.downloads;
|
||||
|
||||
torrent.files.forEach((file) => {
|
||||
const downloads = torrent.downloads.filter((m) => m.link === file.path);
|
||||
|
||||
if (downloads.length > 0) {
|
||||
file.download = downloads[0];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public trackByMethod(index: number, el: Torrent): string {
|
||||
|
|
|
|||
|
|
@ -31,10 +31,6 @@ export class TorrentService {
|
|||
return this.http.get<Torrent[]>(`/Api/Torrents`);
|
||||
}
|
||||
|
||||
public getDetails(torrentId: string): Observable<Torrent> {
|
||||
return this.http.get<Torrent>(`/Api/Torrents/${torrentId}`);
|
||||
}
|
||||
|
||||
public uploadMagnet(
|
||||
magnetLink: string,
|
||||
autoDownload: boolean,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ namespace RdtClient.Data.Data
|
|||
SettingId = "DownloadLimit",
|
||||
Type = "Int32",
|
||||
Value = "10"
|
||||
},
|
||||
new Setting
|
||||
{
|
||||
SettingId = "UnpackLimit",
|
||||
Type = "Int32",
|
||||
Value = "1"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ namespace RdtClient.Data.Data
|
|||
Task UpdateUnpackingQueued(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateUnpackingStarted(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateUnpackingFinished(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateError(Guid downloadId, String error);
|
||||
Task DeleteForTorrent(Guid torrentId);
|
||||
}
|
||||
|
|
@ -122,13 +123,22 @@ namespace RdtClient.Data.Data
|
|||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime)
|
||||
{
|
||||
var dbDownload = await _dataContext.Downloads
|
||||
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
|
||||
|
||||
dbDownload.Completed = dateTime;
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateError(Guid downloadId, String error)
|
||||
{
|
||||
var dbDownload = await _dataContext.Downloads
|
||||
.FirstOrDefaultAsync(m => m.DownloadId == downloadId);
|
||||
|
||||
dbDownload.Completed = DateTimeOffset.UtcNow;
|
||||
dbDownload.Error = error;
|
||||
|
||||
await _dataContext.SaveChangesAsync();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ namespace RdtClient.Service.Models
|
|||
public class Profile
|
||||
{
|
||||
public String UserName { get; set; }
|
||||
public DateTimeOffset Expiration { get; set; }
|
||||
public DateTimeOffset? Expiration { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace RdtClient.Service.Services
|
|||
Task UpdateUnpackingQueued(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateUnpackingStarted(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateUnpackingFinished(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime);
|
||||
Task UpdateError(Guid downloadId, String error);
|
||||
Task DeleteForTorrent(Guid torrentId);
|
||||
}
|
||||
|
|
@ -75,6 +76,11 @@ namespace RdtClient.Service.Services
|
|||
await _downloadData.UpdateUnpackingFinished(downloadId, dateTime);
|
||||
}
|
||||
|
||||
public async Task UpdateCompleted(Guid downloadId, DateTimeOffset? dateTime)
|
||||
{
|
||||
await _downloadData.UpdateCompleted(downloadId, dateTime);
|
||||
}
|
||||
|
||||
public async Task UpdateError(Guid downloadId, String error)
|
||||
{
|
||||
await _downloadData.UpdateError(downloadId, error);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ namespace RdtClient.Service.Services
|
|||
{
|
||||
// When starting up reset any pending downloads or unpackings so that they are restarted.
|
||||
var torrents = await _torrents.Get();
|
||||
|
||||
|
||||
torrents = torrents.Where(m => m.Completed == null).ToList();
|
||||
|
||||
var downloads = torrents.SelectMany(m => m.Downloads)
|
||||
.Where(m => m.DownloadQueued != null && m.DownloadStarted != null && m.DownloadFinished == null)
|
||||
.OrderBy(m => m.DownloadQueued);
|
||||
|
|
@ -75,6 +77,7 @@ namespace RdtClient.Service.Services
|
|||
if (downloadClient.Error != null)
|
||||
{
|
||||
await _downloads.UpdateError(downloadId, downloadClient.Error);
|
||||
await _downloads.UpdateCompleted(downloadId, DateTimeOffset.UtcNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -95,10 +98,12 @@ namespace RdtClient.Service.Services
|
|||
if (unpackClient.Error != null)
|
||||
{
|
||||
await _downloads.UpdateError(downloadId, unpackClient.Error);
|
||||
await _downloads.UpdateCompleted(downloadId, DateTimeOffset.UtcNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _downloads.UpdateUnpackingFinished(downloadId, DateTimeOffset.UtcNow);
|
||||
await _downloads.UpdateCompleted(downloadId, DateTimeOffset.UtcNow);
|
||||
}
|
||||
|
||||
ActiveUnpackClients.TryRemove(downloadId, out _);
|
||||
|
|
|
|||
|
|
@ -234,8 +234,7 @@ namespace RdtClient.Service.Services
|
|||
|
||||
public async Task Unpack(Guid downloadId)
|
||||
{
|
||||
//var settingUnpackLimit = await _settings.GetNumber("UnpackLimit");
|
||||
var settingUnpackLimit = 1;
|
||||
var settingUnpackLimit = await _settings.GetNumber("UnpackLimit");
|
||||
var settingDownloadFolder = await _settings.GetString("DownloadFolder");
|
||||
|
||||
var download = await _downloads.GetById(downloadId);
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ namespace RdtClient.Web.Controllers
|
|||
{
|
||||
private readonly ITorrents _torrents;
|
||||
private readonly IDownloads _downloads;
|
||||
private readonly ITorrentRunner _torrentRunner;
|
||||
|
||||
public TorrentsController(ITorrents torrents, IDownloads downloads)
|
||||
public TorrentsController(ITorrents torrents, IDownloads downloads, ITorrentRunner torrentRunner)
|
||||
{
|
||||
_torrents = torrents;
|
||||
_downloads = downloads;
|
||||
_torrentRunner = torrentRunner;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
|
@ -40,19 +42,18 @@ namespace RdtClient.Web.Controllers
|
|||
return Ok(results);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for debugging only. Force a tick.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[Route("{id}")]
|
||||
public async Task<ActionResult<Torrent>> Get(Guid id)
|
||||
[Route("Tick")]
|
||||
public async Task<ActionResult> Tick()
|
||||
{
|
||||
var result = await _torrents.GetById(id);
|
||||
await _torrentRunner.Tick();
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
throw new Exception("Torrent not found");
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("UploadFile")]
|
||||
|
|
|
|||
Loading…
Reference in a new issue