mirror of
https://github.com/Tzahi12345/YoutubeDL-Material.git
synced 2026-04-05 22:31:29 +03:00
Missing options are now auto-added to tasks
Added onlyNumber directive for number-only inputs
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
const db_api = require('./db');
|
const db_api = require('./db');
|
||||||
const notifications_api = require('./notifications');
|
const notifications_api = require('./notifications');
|
||||||
const youtubedl_api = require('./youtube-dl');
|
const youtubedl_api = require('./youtube-dl');
|
||||||
|
const subscriptions_api = require('./subscriptions');
|
||||||
|
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const logger = require('./logger');
|
const logger = require('./logger');
|
||||||
@@ -43,6 +44,17 @@ const TASKS = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
all: {
|
||||||
|
auto_confirm: false
|
||||||
|
},
|
||||||
|
delete_old_files: {
|
||||||
|
blacklist_files: false,
|
||||||
|
blacklist_subscription_files: false,
|
||||||
|
threshold_days: ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function scheduleJob(task_key, schedule) {
|
function scheduleJob(task_key, schedule) {
|
||||||
// schedule has to be converted from our format to one node-schedule can consume
|
// schedule has to be converted from our format to one node-schedule can consume
|
||||||
let converted_schedule = null;
|
let converted_schedule = null;
|
||||||
@@ -64,7 +76,7 @@ function scheduleJob(task_key, schedule) {
|
|||||||
logger.verbose(`Skipping running task ${task_state['key']} as it is already in progress.`);
|
logger.verbose(`Skipping running task ${task_state['key']} as it is already in progress.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove schedule if it's a one-time task
|
// remove schedule if it's a one-time task
|
||||||
if (task_state['schedule']['type'] !== 'recurring') await db_api.updateRecord('tasks', {key: task_key}, {schedule: null});
|
if (task_state['schedule']['type'] !== 'recurring') await db_api.updateRecord('tasks', {key: task_key}, {schedule: null});
|
||||||
// we're just "running" the task, any confirmation should be user-initiated
|
// we're just "running" the task, any confirmation should be user-initiated
|
||||||
@@ -84,9 +96,10 @@ exports.setupTasks = async () => {
|
|||||||
const tasks_keys = Object.keys(TASKS);
|
const tasks_keys = Object.keys(TASKS);
|
||||||
for (let i = 0; i < tasks_keys.length; i++) {
|
for (let i = 0; i < tasks_keys.length; i++) {
|
||||||
const task_key = tasks_keys[i];
|
const task_key = tasks_keys[i];
|
||||||
|
const mergedDefaultOptions = Object.assign(defaultOptions['all'], defaultOptions[task_key] || {});
|
||||||
const task_in_db = await db_api.getRecord('tasks', {key: task_key});
|
const task_in_db = await db_api.getRecord('tasks', {key: task_key});
|
||||||
if (!task_in_db) {
|
if (!task_in_db) {
|
||||||
// insert task metadata into table if missing
|
// insert task metadata into table if missing, eventually move title to UI
|
||||||
await db_api.insertRecordIntoTable('tasks', {
|
await db_api.insertRecordIntoTable('tasks', {
|
||||||
key: task_key,
|
key: task_key,
|
||||||
title: TASKS[task_key]['title'],
|
title: TASKS[task_key]['title'],
|
||||||
@@ -97,9 +110,17 @@ exports.setupTasks = async () => {
|
|||||||
data: null,
|
data: null,
|
||||||
error: null,
|
error: null,
|
||||||
schedule: null,
|
schedule: null,
|
||||||
options: {}
|
options: Object.assign(defaultOptions['all'], defaultOptions[task_key] || {})
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// verify all options exist in task
|
||||||
|
for (const key of Object.keys(mergedDefaultOptions)) {
|
||||||
|
if (!(task_in_db.options && task_in_db.options.hasOwnProperty(key))) {
|
||||||
|
const option_key = `options.${key}`
|
||||||
|
await db_api.updateRecord('tasks', {key: task_key}, {[option_key]: mergedDefaultOptions[key]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// reset task if necessary
|
// reset task if necessary
|
||||||
await db_api.updateRecord('tasks', {key: task_key}, {running: false, confirming: false});
|
await db_api.updateRecord('tasks', {key: task_key}, {running: false, confirming: false});
|
||||||
|
|
||||||
@@ -220,16 +241,18 @@ async function checkForAutoDeleteFiles() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const delete_older_than_timestamp = Date.now() - task_obj['options']['threshold_days']*86400*1000;
|
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);
|
const files = (await db_api.getRecords('files', {registered: {$lt: delete_older_than_timestamp}}))
|
||||||
return {uids: uids};
|
const files_to_remove = files.map(file => {return {uid: file.uid, sub_id: file.sub_id}});
|
||||||
|
return {files_to_remove: files_to_remove};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function autoDeleteFiles(data) {
|
async function autoDeleteFiles(data) {
|
||||||
if (data['uids']) {
|
const task_obj = await db_api.getRecord('tasks', {key: 'delete_old_files'});
|
||||||
logger.info(`Removing ${data['uids'].length} old files!`);
|
if (data['files_to_remove']) {
|
||||||
for (let i = 0; i < data['uids'].length; i++) {
|
logger.info(`Removing ${data['files_to_remove'].length} old files!`);
|
||||||
const file_to_remove = data['uids'][i];
|
for (let i = 0; i < data['files_to_remove'].length; i++) {
|
||||||
await db_api.deleteFile(file_to_remove);
|
const file_to_remove = data['files_to_remove'][i];
|
||||||
|
await db_api.deleteFile(file_to_remove['uid'], task_obj['options']['blacklist_files'] || (file_to_remove['sub_id'] && file_to_remove['blacklist_subscription_files']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ import { NotificationsListComponent } from './components/notifications-list/noti
|
|||||||
import { TaskSettingsComponent } from './components/task-settings/task-settings.component';
|
import { TaskSettingsComponent } from './components/task-settings/task-settings.component';
|
||||||
import { GenerateRssUrlComponent } from './dialogs/generate-rss-url/generate-rss-url.component';
|
import { GenerateRssUrlComponent } from './dialogs/generate-rss-url/generate-rss-url.component';
|
||||||
import { SortPropertyComponent } from './components/sort-property/sort-property.component';
|
import { SortPropertyComponent } from './components/sort-property/sort-property.component';
|
||||||
|
import { OnlyNumberDirective } from './directives/only-number.directive';
|
||||||
|
|
||||||
registerLocaleData(es, 'es');
|
registerLocaleData(es, 'es');
|
||||||
|
|
||||||
@@ -143,7 +144,8 @@ registerLocaleData(es, 'es');
|
|||||||
NotificationsListComponent,
|
NotificationsListComponent,
|
||||||
TaskSettingsComponent,
|
TaskSettingsComponent,
|
||||||
GenerateRssUrlComponent,
|
GenerateRssUrlComponent,
|
||||||
SortPropertyComponent
|
SortPropertyComponent,
|
||||||
|
OnlyNumberDirective
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|||||||
@@ -4,9 +4,15 @@
|
|||||||
<div *ngIf="task_key === 'delete_old_files'">
|
<div *ngIf="task_key === 'delete_old_files'">
|
||||||
<mat-form-field color="accent">
|
<mat-form-field color="accent">
|
||||||
<mat-label i18n="Delete files older than">Delete files older than</mat-label>
|
<mat-label i18n="Delete files older than">Delete files older than</mat-label>
|
||||||
<input [(ngModel)]="new_options['threshold_days']" matInput required>
|
<input [(ngModel)]="new_options['threshold_days']" matInput onlyNumber required>
|
||||||
<span matTextSuffix>days</span>
|
<span matTextSuffix>days</span>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox [(ngModel)]="new_options['blacklist_files']" i18n="Blacklist deleted files" placeholder="Archive mode must be enabled" placeholder-i18n>Blacklist all files</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<mat-checkbox [disabled]="new_options['blacklist_files']" [(ngModel)]="new_options['blacklist_subscription_files']" i18n="Blacklist deleted subscription files" placeholder="Archive mode must be enabled" placeholder-i18n>Blacklist deleted subscription files</mat-checkbox>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
8
src/app/directives/only-number.directive.spec.ts
Normal file
8
src/app/directives/only-number.directive.spec.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { OnlyNumberDirective } from './only-number.directive';
|
||||||
|
|
||||||
|
describe('OnlyNumberDirective', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
const directive = new OnlyNumberDirective();
|
||||||
|
expect(directive).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
72
src/app/directives/only-number.directive.ts
Normal file
72
src/app/directives/only-number.directive.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// https://stackoverflow.com/a/58535434/8088021
|
||||||
|
|
||||||
|
import { Directive, ElementRef, HostListener } from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[onlyNumber]'
|
||||||
|
})
|
||||||
|
export class OnlyNumberDirective {
|
||||||
|
|
||||||
|
private navigationKeys = [
|
||||||
|
'Backspace',
|
||||||
|
'Delete',
|
||||||
|
'Tab',
|
||||||
|
'Escape',
|
||||||
|
'Enter',
|
||||||
|
'Home',
|
||||||
|
'End',
|
||||||
|
'ArrowLeft',
|
||||||
|
'ArrowRight',
|
||||||
|
'Clear',
|
||||||
|
'Copy',
|
||||||
|
'Paste'
|
||||||
|
];
|
||||||
|
inputElement: HTMLElement;
|
||||||
|
constructor(public el: ElementRef) {
|
||||||
|
this.inputElement = el.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('keydown', ['$event'])
|
||||||
|
onKeyDown(e: KeyboardEvent) {
|
||||||
|
if (
|
||||||
|
this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
|
||||||
|
(e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
|
||||||
|
(e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
|
||||||
|
(e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
|
||||||
|
(e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
|
||||||
|
(e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
|
||||||
|
(e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
|
||||||
|
(e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
|
||||||
|
(e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
|
||||||
|
) {
|
||||||
|
// let it happen, don't do anything
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Ensure that it is a number and stop the keypress
|
||||||
|
const key = Number(e.key)
|
||||||
|
if (
|
||||||
|
(e.shiftKey || (isNaN(key) && !(e.key === '.')))
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('paste', ['$event'])
|
||||||
|
onPaste(event: ClipboardEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
const pastedInput: string = event.clipboardData
|
||||||
|
.getData('text/plain')
|
||||||
|
.replace(/\D/g, ''); // get a digit-only string
|
||||||
|
document.execCommand('insertText', false, pastedInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HostListener('drop', ['$event'])
|
||||||
|
onDrop(event: DragEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
const textData = event.dataTransfer.getData('text').replace(/\D/g, '');
|
||||||
|
this.inputElement.focus();
|
||||||
|
document.execCommand('insertText', false, textData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user