diff --git a/ui/src/app/app.html b/ui/src/app/app.html
index 8e7741e..6043083 100644
--- a/ui/src/app/app.html
+++ b/ui/src/app/app.html
@@ -456,20 +456,42 @@
}
@if (download.value.status === 'error') {
-
+
}
@if (!!download.value.filename) {
{{ download.value.title }}
} @else {
- {{download.value.title}}
- @if (download.value.msg) {
-
{{download.value.msg}}
- }
- @if (download.value.error) {
-
Error: {{download.value.error}}
- }
+
+ {{download.value.title}}
+ @if (download.value.status === 'error' && !isErrorExpanded(download.key)) {
+
+ Click for details
+
+ }
+
}
+ @if (download.value.status === 'error' && isErrorExpanded(download.key)) {
+
+
+
+ @if (download.value.msg) {
+
Message: {{download.value.msg}}
+ }
+ @if (download.value.error) {
+
Error: {{download.value.error}}
+ }
+
URL: {{download.value.url}}
+
+
+
+
+ }
@if (download.value.size) {
diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts
index e56fd2b..7738c65 100644
--- a/ui/src/app/app.ts
+++ b/ui/src/app/app.ts
@@ -6,7 +6,7 @@ import { FormsModule } from '@angular/forms';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { NgSelectModule } from '@ng-select/ng-select';
-import { faTrashAlt, faCheckCircle, faTimesCircle, faRedoAlt, faSun, faMoon, faCheck, faCircleHalfStroke, faDownload, faExternalLinkAlt, faFileImport, faFileExport, faCopy, faClock, faTachometerAlt } from '@fortawesome/free-solid-svg-icons';
+import { faTrashAlt, faCheckCircle, faTimesCircle, faRedoAlt, faSun, faMoon, faCheck, faCircleHalfStroke, faDownload, faExternalLinkAlt, faFileImport, faFileExport, faCopy, faClock, faTachometerAlt, faChevronRight } from '@fortawesome/free-solid-svg-icons';
import { faGithub } from '@fortawesome/free-brands-svg-icons';
import { CookieService } from 'ngx-cookie-service';
import { DownloadsService } from './services/downloads.service';
@@ -66,6 +66,7 @@ export class App implements AfterViewInit, OnInit {
ytDlpVersion: string | null = null;
metubeVersion: string | null = null;
isAdvancedOpen = false;
+ expandedErrors: Set = new Set();
// Download metrics
activeDownloads = 0;
@@ -100,6 +101,7 @@ export class App implements AfterViewInit, OnInit {
faGithub = faGithub;
faClock = faClock;
faTachometerAlt = faTachometerAlt;
+ faChevronRight = faChevronRight;
subtitleFormats = [
{ id: 'srt', text: 'SRT' },
{ id: 'txt', text: 'TXT (Text only)' },
@@ -699,6 +701,24 @@ export class App implements AfterViewInit, OnInit {
this.isAdvancedOpen = !this.isAdvancedOpen;
}
+ toggleErrorDetail(id: string) {
+ if (this.expandedErrors.has(id)) this.expandedErrors.delete(id);
+ else this.expandedErrors.add(id);
+ }
+
+ copyErrorMessage(download: Download) {
+ const parts: string[] = [];
+ if (download.title) parts.push(`Title: ${download.title}`);
+ if (download.url) parts.push(`URL: ${download.url}`);
+ if (download.msg) parts.push(`Message: ${download.msg}`);
+ if (download.error) parts.push(`Error: ${download.error}`);
+ navigator.clipboard.writeText(parts.join('\n')).catch(() => {});
+ }
+
+ isErrorExpanded(id: string): boolean {
+ return this.expandedErrors.has(id);
+ }
+
private updateMetrics() {
this.activeDownloads = Array.from(this.downloads.queue.values()).filter(d => d.status === 'downloading' || d.status === 'preparing').length;
this.queuedDownloads = Array.from(this.downloads.queue.values()).filter(d => d.status === 'pending').length;
|