From 754a63a43a944ff908189a8af7cf8ada62bc6960 Mon Sep 17 00:00:00 2001 From: apiweb Date: Wed, 2 Aug 2023 22:42:42 +0000 Subject: [PATCH] Add improvements on the Download Manager --- .../hakuneko/frontend@classic-dark/jobs.html | 103 +++++++++++++++++- .../hakuneko/frontend@classic-light/jobs.html | 103 +++++++++++++++++- src/web/mjs/engine/DownloadJob.mjs | 1 + src/web/mjs/engine/DownloadManager.mjs | 56 ++++++++++ 4 files changed, 261 insertions(+), 2 deletions(-) diff --git a/src/web/lib/hakuneko/frontend@classic-dark/jobs.html b/src/web/lib/hakuneko/frontend@classic-dark/jobs.html index 59ad75f06c..2397cfaaae 100644 --- a/src/web/lib/hakuneko/frontend@classic-dark/jobs.html +++ b/src/web/lib/hakuneko/frontend@classic-dark/jobs.html @@ -70,6 +70,24 @@ .hide { display: none; } + .buttonDisabled { + cursor: default; + pointer-events: none; + color: gray; + } + .remove-button { + cursor: pointer; + text-align: center; + padding: 0.25em; + } + .remove-button i { + color: var(--job-list-button-failed-color); + } + + .remove-button:hover i { + color: var(--job-list-button-completed-color); + } +
@@ -79,6 +97,9 @@ + @@ -88,7 +109,18 @@
-
[[ jobList.length ]] Download(s)
+
+ [[ jobList.length ]] Download(s) + + + + + + + +
@@ -113,6 +145,10 @@ notify: true, // enable upward data flow, //readOnly: true, // prevent downward data flow //observer: 'onSelectedMangaChanged' + }, + queuePaused: { + type: Boolean, + value: false, } }; } @@ -132,6 +168,13 @@ this.ipc.on( 'close', this.onClose.bind( this ) ); } + /** + * + */ + isQueueEmpty(length) { + return length < 1 ? 'buttonDisabled' : ''; + } + /** * */ @@ -178,6 +221,41 @@ } } + /** + * + */ + getPlayPauseTooltip( status ) { + if ( status ) { + return 'Resume download queue'; + } else { + return 'Pause download queue'; + } + } + + /** + * + */ + getPlayPauseClass( status ) { + if ( status ) { + return 'fa-play'; + } else { + return 'fa-pause'; + } + } + + /** + * + */ + playPauseQueue() { + if(this.queuePaused) { + Engine.DownloadManager.startQueue(); + this.queuePaused = false; + } else { + Engine.DownloadManager.pauseQueue(); + this.queuePaused = true; + } + } + /** * */ @@ -197,6 +275,29 @@ } } + /** + * + */ + removeFromQueue(e) { + const index = e.model.index; + const job = this.jobList[index]; + if (job) { + Engine.DownloadManager.removeFromQueue(job); + this.splice('jobList', index, 1); + } + } + + /** + * + */ + async clearJobList() { + if(await confirm( 'Do you really want to clear the download queue?' )) { + Engine.DownloadManager.clearQueue(); + this.splice('jobList', 0, this.jobList.length); + this.queuePaused = false; + } + } + /** * */ diff --git a/src/web/lib/hakuneko/frontend@classic-light/jobs.html b/src/web/lib/hakuneko/frontend@classic-light/jobs.html index 59ad75f06c..2397cfaaae 100644 --- a/src/web/lib/hakuneko/frontend@classic-light/jobs.html +++ b/src/web/lib/hakuneko/frontend@classic-light/jobs.html @@ -70,6 +70,24 @@ .hide { display: none; } + .buttonDisabled { + cursor: default; + pointer-events: none; + color: gray; + } + .remove-button { + cursor: pointer; + text-align: center; + padding: 0.25em; + } + .remove-button i { + color: var(--job-list-button-failed-color); + } + + .remove-button:hover i { + color: var(--job-list-button-completed-color); + } +
[[ item.labels.connector ]] [[ item.labels.manga ]] [[ item.labels.chapter ]] + +
@@ -79,6 +97,9 @@ + @@ -88,7 +109,18 @@
-
[[ jobList.length ]] Download(s)
+
+ [[ jobList.length ]] Download(s) + + + + + + + +
@@ -113,6 +145,10 @@ notify: true, // enable upward data flow, //readOnly: true, // prevent downward data flow //observer: 'onSelectedMangaChanged' + }, + queuePaused: { + type: Boolean, + value: false, } }; } @@ -132,6 +168,13 @@ this.ipc.on( 'close', this.onClose.bind( this ) ); } + /** + * + */ + isQueueEmpty(length) { + return length < 1 ? 'buttonDisabled' : ''; + } + /** * */ @@ -178,6 +221,41 @@ } } + /** + * + */ + getPlayPauseTooltip( status ) { + if ( status ) { + return 'Resume download queue'; + } else { + return 'Pause download queue'; + } + } + + /** + * + */ + getPlayPauseClass( status ) { + if ( status ) { + return 'fa-play'; + } else { + return 'fa-pause'; + } + } + + /** + * + */ + playPauseQueue() { + if(this.queuePaused) { + Engine.DownloadManager.startQueue(); + this.queuePaused = false; + } else { + Engine.DownloadManager.pauseQueue(); + this.queuePaused = true; + } + } + /** * */ @@ -197,6 +275,29 @@ } } + /** + * + */ + removeFromQueue(e) { + const index = e.model.index; + const job = this.jobList[index]; + if (job) { + Engine.DownloadManager.removeFromQueue(job); + this.splice('jobList', index, 1); + } + } + + /** + * + */ + async clearJobList() { + if(await confirm( 'Do you really want to clear the download queue?' )) { + Engine.DownloadManager.clearQueue(); + this.splice('jobList', 0, this.jobList.length); + this.queuePaused = false; + } + } + /** * */ diff --git a/src/web/mjs/engine/DownloadJob.mjs b/src/web/mjs/engine/DownloadJob.mjs index 1a6b1a5de1..5dbd074f18 100644 --- a/src/web/mjs/engine/DownloadJob.mjs +++ b/src/web/mjs/engine/DownloadJob.mjs @@ -28,6 +28,7 @@ export default class DownloadJob extends EventTarget { // TODO: initialize requestOptions.headers = new Headers() if not set this.chunkSize = 8388608; // 8 MB this.throttle = chapter.manga.connector.config && chapter.manga.connector.config['throttle'] ? chapter.manga.connector.config['throttle'].value : 0; + this.initialStatus = chapter.status; this.status = undefined; this.progress = 0; this.errors = []; diff --git a/src/web/mjs/engine/DownloadManager.mjs b/src/web/mjs/engine/DownloadManager.mjs index 40cfbbb2e9..0cbb968c4d 100644 --- a/src/web/mjs/engine/DownloadManager.mjs +++ b/src/web/mjs/engine/DownloadManager.mjs @@ -10,6 +10,7 @@ export default class DownloadManager extends EventTarget { super(); this.queue = []; this.worker = setInterval( this.processQueue.bind( this ), 250 ); + this.queuePaused = false; } /** @@ -36,12 +37,53 @@ export default class DownloadManager extends EventTarget { } } + /** + * Returns true if the download queue is paused. + */ + isQueuePaused() { + return this.queuePaused; + } + + /** + * Pause the download queue processing. + */ + pauseQueue() { + this.queuePaused = true; + } + + /** + * Start the download queue processing if it was paused. + */ + startQueue() { + this.queuePaused = false; + this.processQueue(); // Start processing the queue again + } + + /** + * Remove a job from the download queue. + * @param job + */ + removeFromQueue(job) { + if (!job) return; + + const connectorID = job.chapter.manga.connector.id; + const index = this.queue[connectorID].indexOf(job); + if (index !== -1) { + this.queue[connectorID].splice(index, 1); + job.setStatus(job.initialStatus); + } + } + /** * */ processQueue() { // find a connector in queue that has downloads available and is not locked for( let connectorID in this.queue ) { + if (this.isQueuePaused()) { + break; + } + // check if queue is not empty, there are no active jobs for this connector and the connector is not locked internally through other requests if( this.queue[connectorID].length > 0 && this.queue[connectorID].activeCount < 1 && !this.queue[connectorID][0].chapter.manga.connector.isLocked ) { this.queue[connectorID].activeCount++; @@ -52,4 +94,18 @@ export default class DownloadManager extends EventTarget { } } } + + /** + * Clear the download queue. + */ + clearQueue() { + for (let connectorID in this.queue) { + const jobs = this.queue[connectorID]; + for (let i = jobs.length - 1; i >= 0; i--) { + const job = jobs[i]; + job.setStatus(job.initialStatus); + jobs.splice(i, 1); + } + } + } } \ No newline at end of file
[[ item.labels.connector ]] [[ item.labels.manga ]] [[ item.labels.chapter ]] + +