From fffba9065acf10f1a2d6a1e2f3bc3aa531b6a8ca Mon Sep 17 00:00:00 2001 From: Rpsl Date: Thu, 29 Jul 2021 11:12:40 +0300 Subject: Added retry button for failed download --- app/ytdl.py | 15 ++++++++------- ui/package-lock.json | 23 +++++++++++++++++++++++ ui/package.json | 1 + ui/src/app/app.component.html | 14 ++++++++++---- ui/src/app/app.component.ts | 15 ++++++++++++--- ui/src/app/downloads.service.ts | 1 + 6 files changed, 55 insertions(+), 14 deletions(-) diff --git a/app/ytdl.py b/app/ytdl.py index d29f108..4475704 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -25,8 +25,9 @@ class DownloadQueueNotifier: raise NotImplementedError class DownloadInfo: - def __init__(self, id, title, url): + def __init__(self, id, title, url, quality): self.id, self.title, self.url = id, title, url + self.quality = quality self.status = self.msg = self.percent = self.speed = self.eta = None class Download: @@ -53,7 +54,7 @@ class Download: self.proc = None self.loop = None self.notifier = None - + def _download(self): try: ret = youtube_dl.YoutubeDL(params={ @@ -69,7 +70,7 @@ class Download: self.status_queue.put({'status': 'finished' if ret == 0 else 'error'}) except youtube_dl.utils.YoutubeDLError as exc: self.status_queue.put({'status': 'error', 'msg': str(exc)}) - + async def start(self, notifier): if Download.manager is None: Download.manager = multiprocessing.Manager() @@ -81,7 +82,7 @@ class Download: self.info.status = 'preparing' await self.notifier.updated(self.info) asyncio.ensure_future(self.update_status()) - return await self.loop.run_in_executor(None, self.proc.join) + return await self.loop.run_in_executor(None, self.proc.join) def cancel(self): if self.running(): @@ -147,7 +148,7 @@ class DownloadQueue: return {'status': 'ok'} elif etype == 'video' or etype.startswith('url') and 'id' in entry: if entry['id'] not in self.queue: - dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url']) + dl = DownloadInfo(entry['id'], entry['title'], entry.get('webpage_url') or entry['url'], quality) dldirectory = self.config.DOWNLOAD_DIR if quality != 'audio' else self.config.AUDIO_DOWNLOAD_DIR self.queue[entry['id']] = Download(dldirectory, self.config.OUTPUT_TEMPLATE, quality, dl) self.event.set() @@ -170,7 +171,7 @@ class DownloadQueue: except youtube_dl.utils.YoutubeDLError as exc: return {'status': 'error', 'msg': str(exc)} return await self.__add_entry(entry, quality, already) - + async def cancel(self, ids): for id in ids: if id not in self.queue: @@ -195,7 +196,7 @@ class DownloadQueue: def get(self): return(list((k, v.info) for k, v in self.queue.items()), list((k, v.info) for k, v in self.done.items())) - + async def __download(self): while True: while not self.queue: diff --git a/ui/package-lock.json b/ui/package-lock.json index ec5bb1b..270de65 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -20,6 +20,7 @@ "@fortawesome/angular-fontawesome": "^0.7.0", "@fortawesome/fontawesome-svg-core": "^1.2.28", "@fortawesome/free-regular-svg-icons": "^5.15.2", + "@fortawesome/free-solid-svg-icons": "^5.15.3", "@ng-bootstrap/ng-bootstrap": "^8.0.4", "bootstrap": "^4.5.0", "ngx-socket-io": "~3.2.0", @@ -1958,6 +1959,18 @@ "node": ">=6" } }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz", + "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.35" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -18662,6 +18675,14 @@ "@fortawesome/fontawesome-common-types": "^0.2.35" } }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz", + "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.35" + } + }, "@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -20257,6 +20278,8 @@ "integrity": "sha512-v3+E0Ucu2xWJMOJ2fA/q9pDT/hlxHftHGPUay1/1cTgyPV5JTHFdO9hqo837Sx2s9vKBMTt5gO+lhF95PO6J+g==", "dev": true, "requires": { + "@angular/compiler": "9.0.0", + "@angular/core": "9.0.0", "app-root-path": "^3.0.0", "aria-query": "^3.0.0", "axobject-query": "2.0.2", diff --git a/ui/package.json b/ui/package.json index 7f713d0..06e491d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,6 +23,7 @@ "@fortawesome/angular-fontawesome": "^0.7.0", "@fortawesome/fontawesome-svg-core": "^1.2.28", "@fortawesome/free-regular-svg-icons": "^5.15.2", + "@fortawesome/free-solid-svg-icons": "^5.15.3", "@ng-bootstrap/ng-bootstrap": "^8.0.4", "bootstrap": "^4.5.0", "ngx-socket-io": "~3.2.0", diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index e1f29d6..2ec41dd 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -39,7 +39,7 @@ - + @@ -69,13 +69,14 @@ - + + @@ -90,9 +91,14 @@ {{ download.value.title }} - + + + + + + - + diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index cc8ceb2..f25bb5e 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; import { faTrashAlt, faCheckCircle, faTimesCircle } from '@fortawesome/free-regular-svg-icons'; +import { faRedoAlt } from '@fortawesome/free-solid-svg-icons'; import { DownloadsService, Status } from './downloads.service'; import { MasterCheckboxComponent } from './master-checkbox.component'; @@ -21,7 +22,7 @@ export class AppComponent implements AfterViewInit { ]; quality: string = "best"; addInProgress = false; - + @ViewChild('queueMasterCheckbox') queueMasterCheckbox: MasterCheckboxComponent; @ViewChild('queueDelSelected') queueDelSelected: ElementRef; @ViewChild('doneMasterCheckbox') doneMasterCheckbox: MasterCheckboxComponent; @@ -32,6 +33,7 @@ export class AppComponent implements AfterViewInit { faTrashAlt = faTrashAlt; faCheckCircle = faCheckCircle; faTimesCircle = faTimesCircle; + faRedoAlt = faRedoAlt; constructor(public downloads: DownloadsService) { } @@ -68,9 +70,12 @@ export class AppComponent implements AfterViewInit { this.doneDelSelected.nativeElement.disabled = checked == 0; } - addDownload() { + addDownload(url?: string, quality?: string) { + url = url ?? this.addUrl + quality = quality ?? this.quality + this.addInProgress = true; - this.downloads.add(this.addUrl, this.quality).subscribe((status: Status) => { + this.downloads.add(url, quality).subscribe((status: Status) => { if (status.status === 'error') { alert(`Error adding URL: ${status.msg}`); } else { @@ -80,6 +85,10 @@ export class AppComponent implements AfterViewInit { }); } + retryDownload(key: string, quality:string){ + this.addDownload(key, quality); + } + delDownload(where: string, id: string) { this.downloads.delById(where, [id]).subscribe(); } diff --git a/ui/src/app/downloads.service.ts b/ui/src/app/downloads.service.ts index 4da30fe..5fd3b5a 100644 --- a/ui/src/app/downloads.service.ts +++ b/ui/src/app/downloads.service.ts @@ -15,6 +15,7 @@ interface Download { url: string, status: string; msg: string; + quality: string; percent: number; speed: number; eta: number; -- cgit