mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-28 15:40:56 +03:00
Fixed downloads not sorting properly
Confirm dialog can now be a selection list
This commit is contained in:
@@ -1669,9 +1669,15 @@ app.post('/api/download', optionalJwt, async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/api/clearFinishedDownloads', optionalJwt, async (req, res) => {
|
app.post('/api/clearDownloads', optionalJwt, async (req, res) => {
|
||||||
const user_uid = req.isAuthenticated() ? req.user.uid : null;
|
const user_uid = req.isAuthenticated() ? req.user.uid : null;
|
||||||
const success = db_api.removeAllRecords('download_queue', {finished: true, user_uid: user_uid});
|
const clear_finished = req.body.clear_finished;
|
||||||
|
const clear_paused = req.body.clear_paused;
|
||||||
|
const clear_errors = req.body.clear_errors;
|
||||||
|
let success = true;
|
||||||
|
if (clear_finished) success &= await db_api.removeAllRecords('download_queue', {finished: true, user_uid: user_uid});
|
||||||
|
if (clear_paused) success &= await db_api.removeAllRecords('download_queue', {paused: true, user_uid: user_uid});
|
||||||
|
if (clear_errors) success &= await db_api.removeAllRecords('download_queue', {error: {$ne: null}, user_uid: user_uid});
|
||||||
res.send({success: success});
|
res.send({success: success});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<mat-table style="overflow: hidden" [ngClass]="uids ? 'rounded-top' : null" matSort [dataSource]="dataSource">
|
<mat-table style="overflow: hidden" [ngClass]="uids ? 'rounded-top' : null" matSort [dataSource]="dataSource">
|
||||||
|
|
||||||
<!-- Date Column -->
|
<!-- Date Column -->
|
||||||
<ng-container matColumnDef="date">
|
<ng-container matColumnDef="timestamp_start">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Date">Date</ng-container> </mat-header-cell>
|
||||||
<mat-cell *matCellDef="let element"> {{element.timestamp_start | date: 'short'}} </mat-cell>
|
<mat-cell *matCellDef="let element"> {{element.timestamp_start | date: 'short'}} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Subscription Column -->
|
<!-- Subscription Column -->
|
||||||
<ng-container matColumnDef="subscription">
|
<ng-container matColumnDef="sub_name">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Subscription">Subscription</ng-container> </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Subscription">Subscription</ng-container> </mat-header-cell>
|
||||||
<mat-cell *matCellDef="let element">
|
<mat-cell *matCellDef="let element">
|
||||||
<ng-container *ngIf="element.sub_name">
|
<ng-container *ngIf="element.sub_name">
|
||||||
@@ -32,13 +32,13 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Stage Column -->
|
<!-- Stage Column -->
|
||||||
<ng-container matColumnDef="stage">
|
<ng-container matColumnDef="step_index">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Stage">Stage</ng-container> </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Stage">Stage</ng-container> </mat-header-cell>
|
||||||
<mat-cell *matCellDef="let element"> {{STEP_INDEX_TO_LABEL[element.step_index]}} </mat-cell>
|
<mat-cell *matCellDef="let element"> {{STEP_INDEX_TO_LABEL[element.step_index]}} </mat-cell>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<!-- Progress Column -->
|
<!-- Progress Column -->
|
||||||
<ng-container matColumnDef="progress">
|
<ng-container matColumnDef="percent_complete">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Progress">Progress</ng-container> </mat-header-cell>
|
<mat-header-cell *matHeaderCellDef mat-sort-header> <ng-container i18n="Progress">Progress</ng-container> </mat-header-cell>
|
||||||
<mat-cell *matCellDef="let element">
|
<mat-cell *matCellDef="let element">
|
||||||
<ng-container *ngIf="element.percent_complete">
|
<ng-container *ngIf="element.percent_complete">
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<div *ngIf="!uids" class="downloads-action-button-div">
|
<div *ngIf="!uids" class="downloads-action-button-div">
|
||||||
<button [disabled]="!running_download_exists" mat-stroked-button (click)="pauseAllDownloads()"><ng-container i18n="Pause all downloads">Pause all downloads</ng-container></button>
|
<button [disabled]="!running_download_exists" mat-stroked-button (click)="pauseAllDownloads()"><ng-container i18n="Pause all downloads">Pause all downloads</ng-container></button>
|
||||||
<button style="margin-left: 10px;" [disabled]="!paused_download_exists" mat-stroked-button (click)="resumeAllDownloads()"><ng-container i18n="Resume all downloads">Resume all downloads</ng-container></button>
|
<button style="margin-left: 10px;" [disabled]="!paused_download_exists" mat-stroked-button (click)="resumeAllDownloads()"><ng-container i18n="Resume all downloads">Resume all downloads</ng-container></button>
|
||||||
<button color="warn" style="margin-left: 10px;" mat-stroked-button (click)="clearFinishedDownloads()"><ng-container i18n="Clear finished downloads">Clear finished downloads</ng-container></button>
|
<button color="warn" style="margin-left: 10px;" mat-stroked-button (click)="clearDownloadsByType()"><ng-container i18n="Clear downloads">Clear downloads</ng-container></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
|
|||||||
3: $localize`Complete`
|
3: $localize`Complete`
|
||||||
}
|
}
|
||||||
|
|
||||||
displayedColumns: string[] = ['date', 'title', 'stage', 'subscription', 'progress', 'actions'];
|
displayedColumns: string[] = ['timestamp_start', 'title', 'step_index', 'sub_name', 'percent_complete', 'actions'];
|
||||||
dataSource = null; // new MatTableDataSource<Download>();
|
dataSource = null; // new MatTableDataSource<Download>();
|
||||||
downloads_retrieved = false;
|
downloads_retrieved = false;
|
||||||
|
|
||||||
@@ -104,7 +104,6 @@ export class DownloadsComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
getCurrentDownloads(): void {
|
getCurrentDownloads(): void {
|
||||||
this.postsService.getCurrentDownloads(this.uids).subscribe(res => {
|
this.postsService.getCurrentDownloads(this.uids).subscribe(res => {
|
||||||
this.downloads_retrieved = true;
|
|
||||||
if (res['downloads'] !== null
|
if (res['downloads'] !== null
|
||||||
&& res['downloads'] !== undefined
|
&& res['downloads'] !== undefined
|
||||||
&& JSON.stringify(this.downloads) !== JSON.stringify(res['downloads'])) {
|
&& JSON.stringify(this.downloads) !== JSON.stringify(res['downloads'])) {
|
||||||
@@ -114,12 +113,12 @@ export class DownloadsComponent implements OnInit, OnDestroy {
|
|||||||
this.dataSource = new MatTableDataSource<Download>(this.downloads);
|
this.dataSource = new MatTableDataSource<Download>(this.downloads);
|
||||||
this.dataSource.paginator = this.paginator;
|
this.dataSource.paginator = this.paginator;
|
||||||
this.dataSource.sort = this.sort;
|
this.dataSource.sort = this.sort;
|
||||||
|
|
||||||
this.paused_download_exists = this.downloads.find(download => download['paused'] && !download['error']);
|
this.paused_download_exists = this.downloads.find(download => download['paused'] && !download['error']);
|
||||||
this.running_download_exists = this.downloads.find(download => !download['paused'] && !download['finished']);
|
this.running_download_exists = this.downloads.find(download => !download['paused'] && !download['finished']);
|
||||||
} else {
|
} else {
|
||||||
// failed to get downloads
|
// failed to get downloads
|
||||||
}
|
}
|
||||||
|
this.downloads_retrieved = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ export class DownloadsComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
dialogRef.afterClosed().subscribe(confirmed => {
|
dialogRef.afterClosed().subscribe(confirmed => {
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
this.postsService.clearFinishedDownloads().subscribe(res => {
|
this.postsService.clearDownloads(true, false, false).subscribe(res => {
|
||||||
if (!res['success']) {
|
if (!res['success']) {
|
||||||
this.postsService.openSnackBar('Failed to clear finished downloads!');
|
this.postsService.openSnackBar('Failed to clear finished downloads!');
|
||||||
}
|
}
|
||||||
@@ -143,6 +142,47 @@ export class DownloadsComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearDownloadsByType(): void {
|
||||||
|
const clearEmitter = new EventEmitter<boolean>();
|
||||||
|
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
|
||||||
|
data: {
|
||||||
|
dialogType: 'selection_list',
|
||||||
|
dialogTitle: $localize`Clear downloads`,
|
||||||
|
dialogText: $localize`Select downloads to clear`,
|
||||||
|
submitText: $localize`Clear`,
|
||||||
|
doneEmitter: clearEmitter,
|
||||||
|
warnSubmitColor: true,
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
title: $localize`Finished downloads`,
|
||||||
|
key: 'clear_finished'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: $localize`Paused downloads`,
|
||||||
|
key: 'clear_paused'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: $localize`Errored downloads`,
|
||||||
|
key: 'clear_errors'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clearEmitter.subscribe((done: boolean) => {
|
||||||
|
if (done) {
|
||||||
|
const selected_items = dialogRef.componentInstance.selected_items;
|
||||||
|
this.postsService.clearDownloads(selected_items.includes('clear_finished'), selected_items.includes('clear_paused'), selected_items.includes('clear_errors')).subscribe(res => {
|
||||||
|
if (!res['success']) {
|
||||||
|
this.postsService.openSnackBar($localize`Failed to clear finished downloads!`);
|
||||||
|
} else {
|
||||||
|
this.postsService.openSnackBar($localize`Cleared downloads!`);
|
||||||
|
dialogRef.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pauseDownload(download_uid: string): void {
|
pauseDownload(download_uid: string): void {
|
||||||
this.postsService.pauseDownload(download_uid).subscribe(res => {
|
this.postsService.pauseDownload(download_uid).subscribe(res => {
|
||||||
if (!res['success']) {
|
if (!res['success']) {
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
<h4 mat-dialog-title>{{dialogTitle}}</h4>
|
<h4 mat-dialog-title>{{dialogTitle}}</h4>
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<div style="margin-bottom: 10px;">
|
<div style="margin-bottom: 10px;">
|
||||||
{{dialogText}}
|
<!-- We can support text dialogs or dialogs where users must select items from a list -->
|
||||||
|
<ng-container *ngIf="dialogType === 'text'">
|
||||||
|
{{dialogText}}
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngIf="dialogType === 'selection_list'">
|
||||||
|
<mat-selection-list [(ngModel)]="selected_items">
|
||||||
|
<mat-list-option *ngFor="let item of list" [value]="item.key">
|
||||||
|
{{item.title}}
|
||||||
|
</mat-list-option>
|
||||||
|
</mat-selection-list>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
<!-- The mat-dialog-close directive optionally accepts a value as a result for the dialog. -->
|
||||||
<button [color]="warnSubmitColor ? 'warn' : 'primary'" mat-flat-button type="submit" (click)="confirmClicked()">{{submitText}}</button>
|
<button [disabled]="dialogType === 'selection_list' && selected_items.length === 0" [color]="warnSubmitColor ? 'warn' : 'primary'" mat-flat-button type="submit" (click)="confirmClicked()">{{submitText}}</button>
|
||||||
<div class="mat-spinner" *ngIf="submitClicked">
|
<div class="mat-spinner" *ngIf="submitClicked">
|
||||||
<mat-spinner [diameter]="25"></mat-spinner>
|
<mat-spinner [diameter]="25"></mat-spinner>
|
||||||
</div>
|
</div>
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<button style="float: right;" mat-stroked-button mat-dialog-close>
|
<button style="float: right;" mat-stroked-button mat-dialog-close>
|
||||||
<ng-container *ngIf="cancelText">{{cancelText}}</ng-container>
|
{{cancelText}}
|
||||||
<ng-container *ngIf="!cancelText" i18n="Cancel">Cancel</ng-container>
|
|
||||||
</button>
|
</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
@@ -8,10 +8,13 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|||||||
})
|
})
|
||||||
export class ConfirmDialogComponent implements OnInit {
|
export class ConfirmDialogComponent implements OnInit {
|
||||||
|
|
||||||
|
dialogType = 'text';
|
||||||
dialogTitle = 'Confirm';
|
dialogTitle = 'Confirm';
|
||||||
dialogText = 'Would you like to confirm?';
|
dialogText = 'Would you like to confirm?';
|
||||||
submitText = 'Yes'
|
submitText = 'Yes'
|
||||||
cancelText = null;
|
cancelText = $localize`Cancel`;
|
||||||
|
list: { key: string, title: string }[] = [];
|
||||||
|
selected_items = [];
|
||||||
submitClicked = false;
|
submitClicked = false;
|
||||||
closeOnSubmit = true;
|
closeOnSubmit = true;
|
||||||
|
|
||||||
@@ -21,13 +24,15 @@ export class ConfirmDialogComponent implements OnInit {
|
|||||||
warnSubmitColor = false;
|
warnSubmitColor = false;
|
||||||
|
|
||||||
constructor(@Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef<ConfirmDialogComponent>) {
|
constructor(@Inject(MAT_DIALOG_DATA) public data: any, public dialogRef: MatDialogRef<ConfirmDialogComponent>) {
|
||||||
if (this.data.dialogTitle !== undefined) { this.dialogTitle = this.data.dialogTitle }
|
if (this.data.dialogTitle !== undefined) { this.dialogTitle = this.data.dialogTitle }
|
||||||
if (this.data.dialogText !== undefined) { this.dialogText = this.data.dialogText }
|
if (this.data.dialogType !== undefined) { this.dialogType = this.data.dialogType }
|
||||||
if (this.data.submitText !== undefined) { this.submitText = this.data.submitText }
|
if (this.data.dialogText !== undefined) { this.dialogText = this.data.dialogText }
|
||||||
if (this.data.cancelText !== undefined) { this.cancelText = this.data.cancelText }
|
if (this.data.list !== undefined) { this.list = this.data.list }
|
||||||
|
if (this.data.submitText !== undefined) { this.submitText = this.data.submitText }
|
||||||
|
if (this.data.cancelText !== undefined) { this.cancelText = this.data.cancelText }
|
||||||
if (this.data.warnSubmitColor !== undefined) { this.warnSubmitColor = this.data.warnSubmitColor }
|
if (this.data.warnSubmitColor !== undefined) { this.warnSubmitColor = this.data.warnSubmitColor }
|
||||||
if (this.data.warnSubmitColor !== undefined) { this.warnSubmitColor = this.data.warnSubmitColor }
|
if (this.data.warnSubmitColor !== undefined) { this.warnSubmitColor = this.data.warnSubmitColor }
|
||||||
if (this.data.closeOnSubmit !== undefined) { this.closeOnSubmit = this.data.closeOnSubmit }
|
if (this.data.closeOnSubmit !== undefined) { this.closeOnSubmit = this.data.closeOnSubmit }
|
||||||
|
|
||||||
// checks if emitter exists, if so don't autoclose as it should be handled by caller
|
// checks if emitter exists, if so don't autoclose as it should be handled by caller
|
||||||
if (this.data.doneEmitter) {
|
if (this.data.doneEmitter) {
|
||||||
@@ -36,7 +41,7 @@ export class ConfirmDialogComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmClicked() {
|
confirmClicked(): void {
|
||||||
if (this.onlyEmitOnDone) {
|
if (this.onlyEmitOnDone) {
|
||||||
this.doneEmitter.emit(true);
|
this.doneEmitter.emit(true);
|
||||||
if (this.closeOnSubmit) this.submitClicked = true;
|
if (this.closeOnSubmit) this.submitClicked = true;
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ import {
|
|||||||
UpdateTaskDataRequest,
|
UpdateTaskDataRequest,
|
||||||
RestoreDBBackupRequest,
|
RestoreDBBackupRequest,
|
||||||
Schedule,
|
Schedule,
|
||||||
|
ClearDownloadsRequest
|
||||||
} from '../api-types';
|
} from '../api-types';
|
||||||
import { isoLangs } from './settings/locales_list';
|
import { isoLangs } from './settings/locales_list';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
@@ -176,7 +177,7 @@ export class PostsService implements CanActivate {
|
|||||||
const redirect_not_required = window.location.href.includes('/player') || window.location.href.includes('/login');
|
const redirect_not_required = window.location.href.includes('/player') || window.location.href.includes('/login');
|
||||||
|
|
||||||
// get config
|
// get config
|
||||||
this.loadNavItems().subscribe(res => {
|
this.getConfig().subscribe(res => {
|
||||||
const result = !this.debugMode ? res['config_file'] : res;
|
const result = !this.debugMode ? res['config_file'] : res;
|
||||||
if (result) {
|
if (result) {
|
||||||
this.config = result['YoutubeDLMaterial'];
|
this.config = result['YoutubeDLMaterial'];
|
||||||
@@ -252,7 +253,7 @@ export class PostsService implements CanActivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadConfig() {
|
reloadConfig() {
|
||||||
this.loadNavItems().subscribe(res => {
|
this.getConfig().subscribe(res => {
|
||||||
const result = !this.debugMode ? res['config_file'] : res;
|
const result = !this.debugMode ? res['config_file'] : res;
|
||||||
if (result) {
|
if (result) {
|
||||||
this.config = result['YoutubeDLMaterial'];
|
this.config = result['YoutubeDLMaterial'];
|
||||||
@@ -313,7 +314,7 @@ export class PostsService implements CanActivate {
|
|||||||
return this.http.post<SuccessObject>(this.path + 'restartServer', {}, this.httpOptions);
|
return this.http.post<SuccessObject>(this.path + 'restartServer', {}, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadNavItems() {
|
getConfig() {
|
||||||
if (isDevMode()) {
|
if (isDevMode()) {
|
||||||
return this.http.get('./assets/default.json');
|
return this.http.get('./assets/default.json');
|
||||||
} else {
|
} else {
|
||||||
@@ -579,8 +580,9 @@ export class PostsService implements CanActivate {
|
|||||||
return this.http.post<SuccessObject>(this.path + 'clearDownload', body, this.httpOptions);
|
return this.http.post<SuccessObject>(this.path + 'clearDownload', body, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearFinishedDownloads() {
|
clearDownloads(clear_finished: boolean, clear_paused: boolean, clear_errors: boolean) {
|
||||||
return this.http.post<SuccessObject>(this.path + 'clearFinishedDownloads', {}, this.httpOptions);
|
const body: ClearDownloadsRequest = {clear_finished: clear_finished, clear_paused: clear_paused, clear_errors: clear_errors};
|
||||||
|
return this.http.post<SuccessObject>(this.path + 'clearDownloads', body, this.httpOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTasks() {
|
getTasks() {
|
||||||
|
|||||||
Reference in New Issue
Block a user