From 902b86c4e1e95ae0be8ee27f6cffc3aca63e7bd5 Mon Sep 17 00:00:00 2001 From: Plom Heller Date: Wed, 18 Mar 2026 04:12:33 +0100 Subject: [PATCH] Enable marking queued downloads for playlist insertion once ready. --- src/templates/_base.js | 22 ++++++++++++++++++---- src/templates/downloads.js | 23 ++++++++++++++++++++++- src/templates/file.js | 13 +++++-------- src/ytplom/http.py | 2 +- src/ytplom/misc.py | 28 +++++++++++++++++++++------- 5 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/templates/_base.js b/src/templates/_base.js index d83b0da..4692e19 100644 --- a/src/templates/_base.js +++ b/src/templates/_base.js @@ -23,7 +23,7 @@ eslint "function-paren-newline": "off", "max-lines": [ "error", - {"max": 450, "skipBlankLines": true, "skipComments": true} + {"max": 463, "skipBlankLines": true, "skipComments": true} ], "max-lines-per-function": [ "error", @@ -89,10 +89,10 @@ export const ], CMD_ADD_NEXT = "inject", CMD_ADD_PLAY = "injectplay", + CMD_NEXT = "next", CMD_PAUSE = "pause", CMD_PLAY = "play", CMD_PREV = "prev", - CMD_NEXT = "next", CMD_RM = "rm", IDX_PATH_ID = 2, IDX_START = 0, @@ -179,6 +179,20 @@ export const addChildTo = ( return el; }; +export const addCheckboxTo = ( + parent, + checked, + onclick +) => addChildTo( + "input", + parent, + { + checked, + onclick, + "type": "checkbox" + } +); + export const addATo = ( parent, textContent, @@ -364,11 +378,11 @@ eventHandlers.player = ( playerDiv.innerHTML = ""; addPlayerBtnToDiv( CMD_PREV, - CMD_PREV, + CMD_PREV ); addPlayerBtnToDiv( CMD_NEXT, - CMD_NEXT, + CMD_NEXT ); addPlayerBtnToDiv( data.is_playing ? CMD_PAUSE : CMD_PLAY, diff --git a/src/templates/downloads.js b/src/templates/downloads.js index 7a1d7ab..d59dd7c 100644 --- a/src/templates/downloads.js +++ b/src/templates/downloads.js @@ -10,7 +10,7 @@ eslint ], "max-lines-per-function": [ "error", - 112 + 146 ], "max-params": [ "error", @@ -50,6 +50,8 @@ import { SYMBOL_UP, addATdTo, addButtonTo, + addChildTo, + addCheckboxTo, addPlayerBtnTo, addTdTo, drawTable, @@ -80,6 +82,17 @@ eventHandlers.downloads = (data) => { command ) ), + addInjectionCheckbox = ( + td, + item + ) => addCheckboxTo( + td, + item.to_inject, + () => wrappedCommand( + PATH_DOWNLOADS_JSON, + `${item.to_inject ? "un" : "in"}ject_${item.yt_id}` + ) + ), drawDownloadTable = ( items, populateRow, @@ -127,6 +140,10 @@ eventHandlers.downloads = (data) => { downloading ) => { const tdEntryControl = addTdTo(tr); // col 1 + addInjectionCheckbox( + tdEntryControl, + data.downloading + ); addCommandButtonTo( tdEntryControl, SYMBOL_RM, @@ -160,6 +177,10 @@ eventHandlers.downloads = (data) => { `${CMD_RM}_${idx}` ); const tdEntryControl = addTdTo(tr); // col 2 + addInjectionCheckbox( + tdEntryControl, + toDownload + ); for ( const [ symbol, diff --git a/src/templates/file.js b/src/templates/file.js index 75e1773..37654c2 100644 --- a/src/templates/file.js +++ b/src/templates/file.js @@ -32,6 +32,7 @@ import { PATH_PREFIX_FILE_JSON, PREFIX_CLICKABLE, addATo, + addCheckboxTo, addChildTo, addPlayerBtnTo, addTdTo, @@ -90,15 +91,11 @@ const updateFileData = async () => { // eslint-disable-line one-var if (allowEdit) { const tdCheckbox = addTdTo(tr); tdCheckbox.classList.add(CLS_TAG_CHECKBOX); - addChildTo( - "input", + addCheckboxTo( tdCheckbox, - { - "checked": true, - /* eslint-disable-next-line no-use-before-define */ - "onclick": sendUpdate, - "type": "checkbox" - } + true, + /* eslint-disable-next-line no-use-before-define */ + sendUpdate ); } addATo( diff --git a/src/ytplom/http.py b/src/ytplom/http.py index 2804176..eefcfc7 100644 --- a/src/ytplom/http.py +++ b/src/ytplom/http.py @@ -41,7 +41,7 @@ class Server(ThreadingMixIn, PlomHttpServer): _TaskHandler, *args, **kwargs) self.config = config self.player = Player() - self.downloads = DownloadsManager() + self.downloads = DownloadsManager(playlist_inject=self.player.inject) self.downloads.clean_unfinished() self.downloads.start_thread() VideoFile.call_forget\ diff --git a/src/ytplom/misc.py b/src/ytplom/misc.py index efd389a..c068b50 100644 --- a/src/ytplom/misc.py +++ b/src/ytplom/misc.py @@ -844,7 +844,9 @@ class Player: class DownloadsManager: """Manages downloading and downloads access.""" - def __init__(self) -> None: + def __init__(self, playlist_inject: Callable[[VideoFile], None]) -> None: + self._playlist_inject = playlist_inject + self._to_inject: set[str] = set() self._inherited: list[YoutubeId] = [] self._downloaded: list[YoutubeId] = [] self._downloading: Optional[YoutubeId] = None @@ -900,9 +902,11 @@ class DownloadsManager: def overview( self, conn: DbConn - ) -> dict[str, list[dict[str, str]] | Optional[dict[str, str]]]: + ) -> dict[str, + list[dict[str, str | bool]] + | Optional[dict[str, str | bool]]]: 'What has been, what will be, and what currently is being downloaded.' - downloaded: list[dict[str, str]] = [] + downloaded: list[dict[str, str | bool]] = [] yt_id: Optional[YoutubeId] for yt_id in self._downloaded: try: @@ -912,7 +916,7 @@ class DownloadsManager: downloaded += [{'yt_id': yt_id, 'path': str(file.rel_path), 'digest': file.digest.b64}] # + title? - to_download: list[dict[str, str]] = [] + to_download: list[dict[str, str | bool]] = [] for idx, yt_id in enumerate(self._to_download): try: yt_video = YoutubeVideo.get_one(conn, yt_id) @@ -920,8 +924,9 @@ class DownloadsManager: continue to_download += [{'yt_id': yt_id, 'title': yt_video.title, - 'idx': str(idx)}] - downloading: Optional[dict[str, str]] = None + 'idx': str(idx), + 'to_inject': yt_id in self._to_inject}] + downloading: Optional[dict[str, str | bool]] = None if (yt_id := self._downloading): try: yt_video = YoutubeVideo.get_one(conn, yt_id) @@ -930,7 +935,8 @@ class DownloadsManager: else: downloading = {'yt_id': yt_id, 'title': yt_video.title, - 'status': self._status} + 'status': self._status, + 'to_inject': yt_id in self._to_inject} return {'downloaded': downloaded, 'downloading': downloading, 'to_download': to_download} @@ -948,6 +954,7 @@ class DownloadsManager: def _queue_download(self, yt_id: YoutubeId) -> None: if yt_id == self._downloading or yt_id in self._downloaded: return + self._to_inject.add(yt_id) self._to_download += [yt_id] if not self._downloading: self.q.put('download_next') @@ -983,6 +990,9 @@ class DownloadsManager: with DbConn() as conn: file.save(conn) conn.commit() + if yt_id in self._to_inject: + self._playlist_inject(file) + self._to_inject.remove(yt_id) self._downloaded += [yt_id] self._downloading = None return yt_id @@ -1081,6 +1091,10 @@ class DownloadsManager: self._queue_download(yt_id) elif command == 'forget': self._forget_file(yt_id) + elif command == 'inject': + self._to_inject.add(yt_id) + elif command == 'unject' and yt_id in self._to_inject: + self._to_inject.remove(yt_id) self._update_timestamp(yt_id) Thread(target=loop, daemon=False).start() -- 2.30.2