Files
metube/ui/src/app/app.component.ts

215 lines
7.0 KiB
TypeScript
Raw Normal View History

2019-12-03 22:32:07 +02:00
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { faTrashAlt, faCheckCircle, faTimesCircle } from '@fortawesome/free-regular-svg-icons';
import { faRedoAlt, faSun, faMoon, faExternalLinkAlt, faDownload } from '@fortawesome/free-solid-svg-icons';
2021-09-13 20:25:32 +03:00
import { CookieService } from 'ngx-cookie-service';
import { map, Observable, of } from 'rxjs';
2019-11-29 19:31:34 +02:00
import { Download, DownloadsService, Status } from './downloads.service';
2019-12-03 22:32:07 +02:00
import { MasterCheckboxComponent } from './master-checkbox.component';
import { Formats, Format, Quality } from './formats';
import {KeyValue} from "@angular/common";
2019-11-29 19:31:34 +02:00
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
2022-08-29 21:52:54 -04:00
styleUrls: ['./app.component.sass'],
2019-11-29 19:31:34 +02:00
})
2019-12-03 22:32:07 +02:00
export class AppComponent implements AfterViewInit {
2019-11-29 19:31:34 +02:00
addUrl: string;
formats: Format[] = Formats;
2021-10-28 11:19:17 +01:00
qualities: Quality[];
quality: string;
2021-09-13 20:25:32 +03:00
format: string;
folder: string;
2023-04-09 11:27:41 +08:00
customNamePrefix: string;
2019-11-29 19:31:34 +02:00
addInProgress = false;
2021-12-16 21:52:56 +00:00
darkMode: boolean;
customDirs$: Observable<string[]>;
2021-07-29 11:12:40 +03:00
@ViewChild('queueMasterCheckbox') queueMasterCheckbox: MasterCheckboxComponent;
@ViewChild('queueDelSelected') queueDelSelected: ElementRef;
@ViewChild('doneMasterCheckbox') doneMasterCheckbox: MasterCheckboxComponent;
@ViewChild('doneDelSelected') doneDelSelected: ElementRef;
@ViewChild('doneClearCompleted') doneClearCompleted: ElementRef;
@ViewChild('doneClearFailed') doneClearFailed: ElementRef;
2019-12-03 22:32:07 +02:00
2019-11-29 19:31:34 +02:00
faTrashAlt = faTrashAlt;
2019-12-03 22:32:07 +02:00
faCheckCircle = faCheckCircle;
faTimesCircle = faTimesCircle;
2021-07-29 11:12:40 +03:00
faRedoAlt = faRedoAlt;
2021-12-17 19:30:20 +02:00
faSun = faSun;
faMoon = faMoon;
faDownload = faDownload;
2022-01-04 22:04:53 +00:00
faExternalLinkAlt = faExternalLinkAlt;
2019-11-29 19:31:34 +02:00
2021-09-13 20:25:32 +03:00
constructor(public downloads: DownloadsService, private cookieService: CookieService) {
this.format = cookieService.get('metube_format') || 'any';
2021-10-28 11:19:17 +01:00
// Needs to be set or qualities won't automatically be set
this.setQualities()
this.quality = cookieService.get('metube_quality') || 'best';
2021-12-16 21:52:56 +00:00
this.setupTheme(cookieService)
2019-12-03 22:32:07 +02:00
}
ngOnInit() {
this.customDirs$ = this.getMatchingCustomDir();
}
2022-08-29 21:52:54 -04:00
2019-12-03 22:32:07 +02:00
ngAfterViewInit() {
this.downloads.queueChanged.subscribe(() => {
this.queueMasterCheckbox.selectionChanged();
});
this.downloads.doneChanged.subscribe(() => {
this.doneMasterCheckbox.selectionChanged();
let completed: number = 0, failed: number = 0;
this.downloads.done.forEach(dl => {
if (dl.status === 'finished')
completed++;
else if (dl.status === 'error')
failed++;
});
this.doneClearCompleted.nativeElement.disabled = completed === 0;
this.doneClearFailed.nativeElement.disabled = failed === 0;
});
2019-11-29 19:31:34 +02:00
}
// workaround to allow fetching of Map values in the order they were inserted
// https://github.com/angular/angular/issues/31420
asIsOrder(a, b) {
return 1;
}
2021-09-13 20:25:32 +03:00
qualityChanged() {
this.cookieService.set('metube_quality', this.quality, { expires: 3650 });
// Re-trigger custom directory change
this.downloads.customDirsChanged.next(this.downloads.customDirs);
2021-09-13 20:25:32 +03:00
}
showAdvanced() {
return this.downloads.configuration['CUSTOM_DIRS'];
}
allowCustomDir(tag: string) {
if (this.downloads.configuration['CREATE_CUSTOM_DIRS']) {
return tag;
}
return false;
}
isAudioType() {
2023-02-19 21:48:18 +01:00
return this.quality == 'audio' || this.format == 'mp3' || this.format == 'm4a' || this.format == 'opus' || this.format == 'wav'
}
getMatchingCustomDir() : Observable<string[]> {
return this.downloads.customDirsChanged.asObservable().pipe(map((output) => {
// Keep logic consistent with app/ytdl.py
if (this.isAudioType()) {
console.debug("Showing audio-specific download directories");
return output["audio_download_dir"];
} else {
console.debug("Showing default download directories");
return output["download_dir"];
}
}));
2021-09-13 20:25:32 +03:00
}
2021-12-16 21:52:56 +00:00
setupTheme(cookieService) {
if (cookieService.check('metube_dark')) {
this.darkMode = cookieService.get('metube_dark') === "true"
} else {
this.darkMode = window.matchMedia("prefers-color-scheme: dark").matches
}
this.setTheme()
}
themeChanged() {
this.darkMode = !this.darkMode
this.cookieService.set('metube_dark', this.darkMode.toString(), { expires: 3650 });
this.setTheme()
}
setTheme() {
2023-10-01 13:16:00 +02:00
const theme = this.darkMode ? 'dark' : 'light';
document.documentElement.setAttribute('data-bs-theme', theme);
2021-12-16 21:52:56 +00:00
}
2021-09-13 20:25:32 +03:00
formatChanged() {
this.cookieService.set('metube_format', this.format, { expires: 3650 });
2021-10-28 11:19:17 +01:00
// Updates to use qualities available
this.setQualities()
// Re-trigger custom directory change
this.downloads.customDirsChanged.next(this.downloads.customDirs);
2021-09-13 20:25:32 +03:00
}
2019-12-03 22:32:07 +02:00
queueSelectionChanged(checked: number) {
this.queueDelSelected.nativeElement.disabled = checked == 0;
2019-11-29 19:31:34 +02:00
}
2019-12-03 22:32:07 +02:00
doneSelectionChanged(checked: number) {
this.doneDelSelected.nativeElement.disabled = checked == 0;
2019-11-29 19:31:34 +02:00
}
2021-10-28 11:19:17 +01:00
setQualities() {
// qualities for specific format
this.qualities = this.formats.find(el => el.id == this.format).qualities
const exists = this.qualities.find(el => el.id === this.quality)
this.quality = exists ? this.quality : 'best'
2021-10-28 11:19:17 +01:00
}
2023-04-09 11:27:41 +08:00
addDownload(url?: string, quality?: string, format?: string, folder?: string, customNamePrefix?: string) {
2021-07-29 11:12:40 +03:00
url = url ?? this.addUrl
quality = quality ?? this.quality
format = format ?? this.format
folder = folder ?? this.folder
2023-04-09 11:27:41 +08:00
customNamePrefix = customNamePrefix ?? this.customNamePrefix
2021-07-29 11:12:40 +03:00
2023-04-09 11:27:41 +08:00
console.debug('Downloading: url='+url+' quality='+quality+' format='+format+' folder='+folder+' customNamePrefix='+customNamePrefix);
2019-11-29 19:31:34 +02:00
this.addInProgress = true;
2023-04-09 11:27:41 +08:00
this.downloads.add(url, quality, format, folder, customNamePrefix).subscribe((status: Status) => {
2019-11-29 19:31:34 +02:00
if (status.status === 'error') {
alert(`Error adding URL: ${status.msg}`);
} else {
this.addUrl = '';
}
this.addInProgress = false;
});
}
retryDownload(key: string, download: Download) {
this.addDownload(download.url, download.quality, download.format, download.folder, download.custom_name_prefix);
this.downloads.delById('done', [key]).subscribe();
2021-07-29 11:12:40 +03:00
}
2019-12-03 22:32:07 +02:00
delDownload(where: string, id: string) {
this.downloads.delById(where, [id]).subscribe();
}
delSelectedDownloads(where: string) {
this.downloads.delByFilter(where, dl => dl.checked).subscribe();
}
clearCompletedDownloads() {
this.downloads.delByFilter('done', dl => dl.status === 'finished').subscribe();
2019-11-29 19:31:34 +02:00
}
2019-12-03 22:32:07 +02:00
clearFailedDownloads() {
this.downloads.delByFilter('done', dl => dl.status === 'error').subscribe();
2019-11-29 19:31:34 +02:00
}
buildDownloadLink(download: Download) {
let baseDir = 'download/';
if (download.quality == 'audio' || download.filename.endsWith('.mp3')) {
baseDir = 'audio_download/';
}
if (download.folder) {
baseDir += download.folder + '/';
}
return baseDir + encodeURIComponent(download.filename);
}
identifyDownloadRow(index: number, row: KeyValue<string, Download>) {
return row.key;
}
2019-11-29 19:31:34 +02:00
}