mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-03-07 20:10:03 +03:00
Added task settings
Added support for task errors Added support for lt, gt etc. db comparisons Added task to delete old files
This commit is contained in:
@@ -1673,6 +1673,16 @@ components:
|
||||
required:
|
||||
- task_key
|
||||
- new_data
|
||||
UpdateTaskOptionsRequest:
|
||||
type: object
|
||||
properties:
|
||||
task_key:
|
||||
type: string
|
||||
new_options:
|
||||
type: object
|
||||
required:
|
||||
- task_key
|
||||
- new_options
|
||||
GetTaskResponse:
|
||||
type: object
|
||||
properties:
|
||||
@@ -2549,6 +2559,8 @@ components:
|
||||
type: string
|
||||
schedule:
|
||||
type: object
|
||||
options:
|
||||
type: object
|
||||
Schedule:
|
||||
required:
|
||||
- type
|
||||
|
||||
@@ -1814,6 +1814,15 @@ app.post('/api/updateTaskData', optionalJwt, async (req, res) => {
|
||||
res.send({success: success});
|
||||
});
|
||||
|
||||
app.post('/api/updateTaskOptions', optionalJwt, async (req, res) => {
|
||||
const task_key = req.body.task_key;
|
||||
const new_options = req.body.new_options;
|
||||
|
||||
const success = await db_api.updateRecord('tasks', {key: task_key}, {options: new_options});
|
||||
|
||||
res.send({success: success});
|
||||
});
|
||||
|
||||
app.post('/api/getDBBackups', optionalJwt, async (req, res) => {
|
||||
const backup_dir = path.join('appdata', 'db_backup');
|
||||
fs.ensureDirSync(backup_dir);
|
||||
|
||||
@@ -1095,6 +1095,14 @@ exports.applyFilterLocalDB = (db_path, filter_obj, operation) => {
|
||||
filtered &= (record[filter_prop].search(new RegExp(filter_prop_value['$regex'], filter_prop_value['$options'])) !== -1);
|
||||
} else if ('$ne' in filter_prop_value) {
|
||||
filtered &= filter_prop in record && record[filter_prop] !== filter_prop_value['$ne'];
|
||||
} else if ('$lt' in filter_prop_value) {
|
||||
filtered &= filter_prop in record && record[filter_prop] < filter_prop_value['$lt'];
|
||||
} else if ('$gt' in filter_prop_value) {
|
||||
filtered &= filter_prop in record && record[filter_prop] > filter_prop_value['$gt'];
|
||||
} else if ('$lte' in filter_prop_value) {
|
||||
filtered &= filter_prop in record && record[filter_prop] <= filter_prop_value['$lt'];
|
||||
} else if ('$gte' in filter_prop_value) {
|
||||
filtered &= filter_prop in record && record[filter_prop] >= filter_prop_value['$gt'];
|
||||
}
|
||||
} else {
|
||||
// handle case of nested property check
|
||||
|
||||
@@ -34,6 +34,12 @@ const TASKS = {
|
||||
confirm: youtubedl_api.updateYoutubeDL,
|
||||
title: 'Update youtube-dl',
|
||||
job: null
|
||||
},
|
||||
delete_old_files: {
|
||||
run: checkForAutoDeleteFiles,
|
||||
confirm: autoDeleteFiles,
|
||||
title: 'Delete old files',
|
||||
job: null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +130,7 @@ exports.executeTask = async (task_key) => {
|
||||
|
||||
exports.executeRun = async (task_key) => {
|
||||
logger.verbose(`Running task ${task_key}`);
|
||||
await db_api.updateRecord('tasks', {key: task_key}, {error: null})
|
||||
// don't set running to true when backup up DB as it will be stick "running" if restored
|
||||
if (task_key !== 'backup_local_db') await db_api.updateRecord('tasks', {key: task_key}, {running: true});
|
||||
const data = await TASKS[task_key].run();
|
||||
@@ -131,10 +138,15 @@ exports.executeRun = async (task_key) => {
|
||||
logger.verbose(`Finished running task ${task_key}`);
|
||||
const task_obj = await db_api.getRecord('tasks', {key: task_key});
|
||||
await notifications_api.sendTaskNotification(task_obj, false);
|
||||
|
||||
if (task_obj['options'] && task_obj['options']['auto_confirm']) {
|
||||
exports.executeConfirm(task_key);
|
||||
}
|
||||
}
|
||||
|
||||
exports.executeConfirm = async (task_key) => {
|
||||
logger.verbose(`Confirming task ${task_key}`);
|
||||
await db_api.updateRecord('tasks', {key: task_key}, {error: null})
|
||||
if (!TASKS[task_key]['confirm']) {
|
||||
return null;
|
||||
}
|
||||
@@ -197,4 +209,29 @@ async function removeDuplicates(data) {
|
||||
}
|
||||
}
|
||||
|
||||
// auto delete files
|
||||
|
||||
async function checkForAutoDeleteFiles() {
|
||||
const task_obj = await db_api.getRecord('tasks', {key: 'delete_old_files'});
|
||||
if (!task_obj['options'] || !task_obj['options']['threshold_days']) {
|
||||
const error_message = 'Failed to do delete check because no limit was set!';
|
||||
logger.error(error_message);
|
||||
await db_api.updateRecord('tasks', {key: 'delete_old_files'}, {error: error_message})
|
||||
return null;
|
||||
}
|
||||
const delete_older_than_timestamp = Date.now() - task_obj['options']['threshold_days']*86400*1000;
|
||||
const uids = (await db_api.getRecords('files', {registered: {$lt: delete_older_than_timestamp}})).map(file => file.uid);
|
||||
return {uids: uids};
|
||||
}
|
||||
|
||||
async function autoDeleteFiles(data) {
|
||||
if (data['uids']) {
|
||||
logger.info(`Removing ${data['uids'].length} old files!`);
|
||||
for (let i = 0; i < data['uids'].length; i++) {
|
||||
const file_to_remove = data['uids'][i];
|
||||
await db_api.deleteFile(file_to_remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.TASKS = TASKS;
|
||||
@@ -114,6 +114,7 @@ export type { UpdatePlaylistRequest } from './models/UpdatePlaylistRequest';
|
||||
export type { UpdaterStatus } from './models/UpdaterStatus';
|
||||
export type { UpdateServerRequest } from './models/UpdateServerRequest';
|
||||
export type { UpdateTaskDataRequest } from './models/UpdateTaskDataRequest';
|
||||
export type { UpdateTaskOptionsRequest } from './models/UpdateTaskOptionsRequest';
|
||||
export type { UpdateTaskScheduleRequest } from './models/UpdateTaskScheduleRequest';
|
||||
export type { UpdateUserRequest } from './models/UpdateUserRequest';
|
||||
export type { User } from './models/User';
|
||||
|
||||
@@ -12,4 +12,5 @@ export type Task = {
|
||||
data: any;
|
||||
error: string;
|
||||
schedule: any;
|
||||
options?: any;
|
||||
};
|
||||
8
src/api-types/models/UpdateTaskOptionsRequest.ts
Normal file
8
src/api-types/models/UpdateTaskOptionsRequest.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type UpdateTaskOptionsRequest = {
|
||||
task_key: string;
|
||||
new_options: any;
|
||||
};
|
||||
@@ -90,6 +90,7 @@ import { UpdateTaskScheduleDialogComponent } from './dialogs/update-task-schedul
|
||||
import { RestoreDbDialogComponent } from './dialogs/restore-db-dialog/restore-db-dialog.component';
|
||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||
import { NotificationsListComponent } from './components/notifications-list/notifications-list.component';
|
||||
import { TaskSettingsComponent } from './components/task-settings/task-settings.component';
|
||||
|
||||
registerLocaleData(es, 'es');
|
||||
|
||||
@@ -137,7 +138,8 @@ registerLocaleData(es, 'es');
|
||||
UpdateTaskScheduleDialogComponent,
|
||||
RestoreDbDialogComponent,
|
||||
NotificationsComponent,
|
||||
NotificationsListComponent
|
||||
NotificationsListComponent,
|
||||
TaskSettingsComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<h4 mat-dialog-title><ng-container i18n="Task settings">Task settings - {{task.title}}</ng-container></h4>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div *ngIf="task_key === 'delete_old_files'">
|
||||
<mat-form-field color="accent">
|
||||
<mat-label i18n="Delete files older than">Delete files older than</mat-label>
|
||||
<input [(ngModel)]="new_options['threshold_days']" matInput required>
|
||||
<span matTextSuffix>days</span>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-checkbox [(ngModel)]="new_options['auto_confirm']" i18n="Do not ask for confirmation">Do not ask for confirmation</mat-checkbox>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button mat-dialog-close>
|
||||
<ng-container *ngIf="optionsChanged()" i18n="Task settings cancel button">Cancel</ng-container>
|
||||
<ng-container *ngIf="!optionsChanged()" i18n="Task settings close button">Close</ng-container>
|
||||
</button>
|
||||
<button mat-button [disabled]="!optionsChanged()" (click)="saveSettings()"><ng-container i18n="Save button">Save</ng-container></button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TaskSettingsComponent } from './task-settings.component';
|
||||
|
||||
describe('TaskSettingsComponent', () => {
|
||||
let component: TaskSettingsComponent;
|
||||
let fixture: ComponentFixture<TaskSettingsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TaskSettingsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TaskSettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
46
src/app/components/task-settings/task-settings.component.ts
Normal file
46
src/app/components/task-settings/task-settings.component.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { Task } from 'api-types';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
|
||||
@Component({
|
||||
selector: 'app-task-settings',
|
||||
templateUrl: './task-settings.component.html',
|
||||
styleUrls: ['./task-settings.component.scss']
|
||||
})
|
||||
export class TaskSettingsComponent {
|
||||
task_key: string;
|
||||
new_options = {};
|
||||
task: Task = null;
|
||||
|
||||
constructor(private postsService: PostsService, @Inject(MAT_DIALOG_DATA) public data: {task: Task}) {
|
||||
this.task_key = this.data.task.key;
|
||||
this.task = this.data.task;
|
||||
if (!this.task.options) {
|
||||
this.task.options = {};
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
getSettings(): void {
|
||||
this.postsService.getTask(this.task_key).subscribe(res => {
|
||||
this.task = res['task'];
|
||||
this.new_options = JSON.parse(JSON.stringify(this.task['options'])) || {};
|
||||
});
|
||||
}
|
||||
|
||||
saveSettings(): void {
|
||||
this.postsService.updateTaskOptions(this.task_key, this.new_options).subscribe(() => {
|
||||
this.getSettings();
|
||||
}, () => {
|
||||
this.getSettings();
|
||||
});
|
||||
}
|
||||
|
||||
optionsChanged(): boolean {
|
||||
return JSON.stringify(this.new_options) !== JSON.stringify(this.task.options);
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,9 @@
|
||||
<ng-container *ngIf="element.key == 'youtubedl_update_check'">
|
||||
<ng-container i18n="Update binary to">Update binary to:</ng-container> {{element.data}}
|
||||
</ng-container>
|
||||
<ng-container *ngIf="element.key == 'delete_old_files'">
|
||||
<ng-container i18n="Delete old files">Delete old files:</ng-container> {{element.data.uids.length}}
|
||||
</ng-container>
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
@@ -71,6 +74,12 @@
|
||||
<div class="col-3">
|
||||
<button (click)="scheduleTask(element)" mat-icon-button matTooltip="Schedule" i18n-matTooltip="Schedule"><mat-icon>schedule</mat-icon></button>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button (click)="openTaskSettings(element)" mat-icon-button matTooltip="Settings" i18n-matTooltip="Settings"><mat-icon>settings</mat-icon></button>
|
||||
</div>
|
||||
<div *ngIf="element.error" class="col-3">
|
||||
<button (click)="showError(element)" mat-icon-button matTooltip="Show error" i18n-matTooltip="Show error"><mat-icon>warning</mat-icon></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-cell>
|
||||
@@ -80,7 +89,7 @@
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
|
||||
</mat-table>
|
||||
|
||||
<mat-paginator [pageSizeOptions]="[5, 10, 20]"
|
||||
<mat-paginator [pageSizeOptions]="[10, 20]"
|
||||
showFirstLastButtons
|
||||
aria-label="Select page of tasks">
|
||||
</mat-paginator>
|
||||
|
||||
@@ -29,4 +29,8 @@ mat-header-cell, mat-cell {
|
||||
|
||||
.rounded {
|
||||
border-radius: 16px 16px 16px 16px !important;
|
||||
}
|
||||
|
||||
::ng-deep mat-row {
|
||||
height: fit-content !important;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { Component, EventEmitter, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
@@ -8,6 +8,8 @@ import { RestoreDbDialogComponent } from 'app/dialogs/restore-db-dialog/restore-
|
||||
import { UpdateTaskScheduleDialogComponent } from 'app/dialogs/update-task-schedule-dialog/update-task-schedule-dialog.component';
|
||||
import { PostsService } from 'app/posts.services';
|
||||
import { Task } from 'api-types';
|
||||
import { TaskSettingsComponent } from '../task-settings/task-settings.component';
|
||||
import { Clipboard } from '@angular/cdk/clipboard';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tasks',
|
||||
@@ -29,7 +31,7 @@ export class TasksComponent implements OnInit {
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
|
||||
constructor(private postsService: PostsService, private dialog: MatDialog) { }
|
||||
constructor(private postsService: PostsService, private dialog: MatDialog, private clipboard: Clipboard) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.postsService.initialized) {
|
||||
@@ -117,6 +119,14 @@ export class TasksComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
openTaskSettings(task: Task): void {
|
||||
this.dialog.open(TaskSettingsComponent, {
|
||||
data: {
|
||||
task: task
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getDBBackups(): void {
|
||||
this.postsService.getDBBackups().subscribe(res => {
|
||||
this.db_backups = res['db_backups'];
|
||||
@@ -157,4 +167,24 @@ export class TasksComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
showError(task: Task): void {
|
||||
const copyToClipboardEmitter = new EventEmitter<boolean>();
|
||||
this.dialog.open(ConfirmDialogComponent, {
|
||||
data: {
|
||||
dialogTitle: $localize`Error for: ${task['title']}`,
|
||||
dialogText: task['error'],
|
||||
submitText: $localize`Copy to clipboard`,
|
||||
cancelText: $localize`Close`,
|
||||
closeOnSubmit: false,
|
||||
onlyEmitOnDone: true,
|
||||
doneEmitter: copyToClipboardEmitter
|
||||
}
|
||||
});
|
||||
copyToClipboardEmitter.subscribe((done: boolean) => {
|
||||
if (done) {
|
||||
this.postsService.openSnackBar($localize`Copied to clipboard!`);
|
||||
this.clipboard.copy(task['error']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,8 @@ import {
|
||||
GetAllTasksResponse,
|
||||
DeleteNotificationRequest,
|
||||
SetNotificationsToReadRequest,
|
||||
GetNotificationsResponse
|
||||
GetNotificationsResponse,
|
||||
UpdateTaskOptionsRequest
|
||||
} from '../api-types';
|
||||
import { isoLangs } from './settings/locales_list';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
@@ -647,6 +648,11 @@ export class PostsService implements CanActivate {
|
||||
return this.http.post<SuccessObject>(this.path + 'updateTaskData', body, this.httpOptions);
|
||||
}
|
||||
|
||||
updateTaskOptions(task_key: string, options: any) {
|
||||
const body: UpdateTaskOptionsRequest = {task_key: task_key, new_options: options};
|
||||
return this.http.post<SuccessObject>(this.path + 'updateTaskOptions', body, this.httpOptions);
|
||||
}
|
||||
|
||||
getDBBackups() {
|
||||
return this.http.post<SuccessObject>(this.path + 'getDBBackups', {}, this.httpOptions);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user