From da84753e2082a1d096b5415ea519630da32e8f03 Mon Sep 17 00:00:00 2001 From: ddmoney420 Date: Sun, 1 Mar 2026 19:02:04 -0700 Subject: [PATCH] feat: sort completed downloads by newest first (closes #610) --- ui/src/app/app.html | 59 +++++++++++++++++++++++++-------------------- ui/src/app/app.ts | 25 ++++++++++++++++++- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 8e7741e..db035b8 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -424,7 +424,14 @@ -
Completed
+
+ Completed + +
@@ -445,58 +452,58 @@ - @for (download of downloads.done | keyvalue: asIsOrder; track download.value.id) { - + @for (entry of sortedDone(); track entry[1].id) { + - +
- @if (download.value.status === 'finished') { + @if (entry[1].status === 'finished') { } - @if (download.value.status === 'error') { + @if (entry[1].status === 'error') { }
- @if (!!download.value.filename) { - {{ download.value.title }} + @if (!!entry[1].filename) { + {{ entry[1].title }} } @else { - {{download.value.title}} - @if (download.value.msg) { -
{{download.value.msg}}
+ {{entry[1].title}} + @if (entry[1].msg) { +
{{entry[1].msg}}
} - @if (download.value.error) { -
Error: {{download.value.error}}
+ @if (entry[1].error) { +
Error: {{entry[1].error}}
} }
- @if (download.value.size) { - {{ download.value.size | fileSize }} + @if (entry[1].size) { + {{ entry[1].size | fileSize }} }
- @if (download.value.status === 'error') { - + @if (entry[1].status === 'error') { + } - @if (download.value.filename) { - + @if (entry[1].filename) { + } - - + +
- @if (download.value.chapter_files && download.value.chapter_files.length > 0) { - @for (chapterFile of download.value.chapter_files; track chapterFile.filename) { - + @if (entry[1].chapter_files && entry[1].chapter_files.length > 0) { + @for (chapterFile of entry[1].chapter_files; track chapterFile.filename) { + @@ -507,7 +514,7 @@
-
diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index e56fd2b..0933347 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, faSortAmountDown, faSortAmountUp } 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; + sortAscending = false; // Download metrics activeDownloads = 0; @@ -100,6 +101,8 @@ export class App implements AfterViewInit, OnInit { faGithub = faGithub; faClock = faClock; faTachometerAlt = faTachometerAlt; + faSortAmountDown = faSortAmountDown; + faSortAmountUp = faSortAmountUp; subtitleFormats = [ { id: 'srt', text: 'SRT' }, { id: 'txt', text: 'TXT (Text only)' }, @@ -178,6 +181,7 @@ export class App implements AfterViewInit, OnInit { if (!allowedSubtitleModes.has(this.subtitleMode)) { this.subtitleMode = 'prefer_manual'; } + this.sortAscending = this.cookieService.get('metube_sort_ascending') === 'true'; this.activeTheme = this.getPreferredTheme(this.cookieService); @@ -699,6 +703,25 @@ export class App implements AfterViewInit, OnInit { this.isAdvancedOpen = !this.isAdvancedOpen; } + toggleSortOrder() { + this.sortAscending = !this.sortAscending; + this.cookieService.set('metube_sort_ascending', this.sortAscending ? 'true' : 'false', { expires: 3650 }); + } + + sortedDone(): [string, Download][] { + const result: [string, Download][] = []; + this.downloads.done.forEach((dl, key) => { + result.push([key, dl]); + }); + result.sort((a, b) => { + const tsA = (a[1] as any).timestamp || 0; + const tsB = (b[1] as any).timestamp || 0; + const cmp = tsA < tsB ? -1 : tsA > tsB ? 1 : 0; + return this.sortAscending ? cmp : -cmp; + }); + return result; + } + 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;