diff --git a/.vscode/settings.json b/.vscode/settings.json index 3fcb2218..09c5c3df 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -80,6 +80,9 @@ "onefile", "pathex", "platformdirs", + "playlistend", + "playlistrandom", + "playlistreverse", "plexmatch", "postprocessor", "preferredcodec", diff --git a/app/library/DownloadQueue.py b/app/library/DownloadQueue.py index f2d0a8c0..f3cf740f 100644 --- a/app/library/DownloadQueue.py +++ b/app/library/DownloadQueue.py @@ -305,7 +305,7 @@ class DownloadQueue(metaclass=Singleton): entries = entry.get("entries", []) - LOG.info(f"Processing '{entry.get('id')}: {entry.get('title')}' Playlist.") + LOG.info(f"Processing '{entry.get('id')}: {entry.get('title')} ({len(entries)})' Playlist.") playlistCount = int(entry.get("playlist_count", len(entries))) results = [] @@ -325,11 +325,19 @@ class DownloadQueue(metaclass=Singleton): } async def playlist_processor(i: int, etr: dict): - await self.processors.acquire() try: - LOG.info( - f"Processing '{entry.get('title')}: {i}/{playlistCount}' - ID: {etr.get('id')} - Title: {etr.get('title')}" + item_name = ( + f"'{entry.get('title')}: {i}/{playlist_keys['n_entries']}' - '{etr.get('id')}: {etr.get('title')}'" ) + LOG.debug(f"Waiting to acquire lock for {item_name}") + await self.processors.acquire() + LOG.debug(f"Acquired lock for {item_name}") + + if self.is_paused(): + LOG.warning(f"Download is paused. Skipping processing of '{item_name}'.") + return {"status": "ok"} + + LOG.info(f"Processing '{item_name}'.") _status, _msg = ytdlp_reject(entry=etr, yt_params=yt_params) if not _status: diff --git a/app/library/Utils.py b/app/library/Utils.py index 26257ad4..b1f74ad8 100644 --- a/app/library/Utils.py +++ b/app/library/Utils.py @@ -57,7 +57,7 @@ REMOVE_KEYS: list = [ "opt_list_format": "-F, --list-formats", "opt_dump_agent": "--dump-user-agent", "opt_extractor_descriptions": "--extractor-descriptions", - "opt_list_impersonate_targets": "--list-impersonate-targets" + "opt_list_impersonate_targets": "--list-impersonate-targets", }, ] @@ -459,6 +459,7 @@ def arg_converter( level: int | bool | None = None, dumps: bool = False, removed_options: list | None = None, + keep_defaults: bool = False, ) -> dict: """ Convert yt-dlp options to a dictionary. @@ -468,6 +469,7 @@ def arg_converter( level (int|bool|None): Level of options to remove, True for all. dumps (bool): Dump options as JSON. removed_options (list|None): List of removed options. + keep_defaults (bool): Keep default options. Returns: dict: yt-dlp options dictionary. @@ -488,8 +490,7 @@ def arg_converter( default_opts = _default_opts([]).ydl_opts opts = yt_dlp.parse_options(shlex.split(args)).ydl_opts - - diff = {k: v for k, v in opts.items() if default_opts[k] != v} + diff = {k: v for k, v in opts.items() if default_opts[k] != v} if not keep_defaults else opts.items() if "postprocessors" in diff: diff["postprocessors"] = [pp for pp in diff["postprocessors"] if pp not in default_opts["postprocessors"]] diff --git a/app/library/ytdlp.py b/app/library/ytdlp.py index ecbc61e9..b2f8d224 100644 --- a/app/library/ytdlp.py +++ b/app/library/ytdlp.py @@ -1,5 +1,3 @@ -from __future__ import annotations - from typing import Any import yt_dlp @@ -47,13 +45,16 @@ def ytdlp_options() -> list[dict[str, Any]]: ), } for opt in opts - if ( - (getattr(opt, "_short_opts", []) or getattr(opt, "_long_opts", [])) - and getattr(opt, "help", None) - and "SUPPRESSHELP" not in getattr(opt, "help", "") - ) + if (getattr(opt, "_long_opts", []) and getattr(opt, "help", None)) ] - return collect(parser.option_list, "root") + [ + opts = collect(parser.option_list, "root") + [ entry for grp in parser.option_groups for entry in collect(grp.option_list, grp.title or "other") ] + + for opt in opts: + if "SUPPRESSHELP" in opt.get("description", ""): + opt["description"] = "No description available from yt-dlp." + + return opts + diff --git a/ui/app/components/Queue.vue b/ui/app/components/Queue.vue index 53ad7830..e22ef611 100644 --- a/ui/app/components/Queue.vue +++ b/ui/app/components/Queue.vue @@ -523,6 +523,7 @@ const cancelSelected = () => { } cancelItems(selectedElms.value) selectedElms.value = [] + masterSelectAll.value = false return true } diff --git a/ui/app/pages/index.vue b/ui/app/pages/index.vue index e135eb0d..771625c3 100644 --- a/ui/app/pages/index.vue +++ b/ui/app/pages/index.vue @@ -141,10 +141,17 @@ watch(() => stateStore.queue, () => { const pauseDownload = () => { dialog_confirm.value.visible = true - dialog_confirm.value.html_message = `Pause All non-active downloads?
- + dialog_confirm.value.html_message = ` + - This will not stop downloads that are currently in progress. + Pause All non-active downloads? + +
+ + ` dialog_confirm.value.confirm = () => { socket.emit('pause', {})