From bdb6d0e36a1e8cc2a48049b6cfe23e6560315b7c Mon Sep 17 00:00:00 2001 From: arabcoders Date: Wed, 3 Sep 2025 23:49:19 +0300 Subject: [PATCH] FEAT: Update default presets --- app/library/Presets.py | 12 ++++++----- app/library/YTDLPOpts.py | 10 ++++++--- app/library/config.py | 5 ++++- ui/app/pages/index.vue | 41 +++++++++++++++++++++--------------- ui/app/stores/ConfigStore.ts | 1 - ui/app/types/config.d.ts | 2 -- 6 files changed, 42 insertions(+), 29 deletions(-) diff --git a/app/library/Presets.py b/app/library/Presets.py index 08d582d5..425061cf 100644 --- a/app/library/Presets.py +++ b/app/library/Presets.py @@ -19,33 +19,34 @@ DEFAULT_PRESETS: list[dict[int, dict[str, str | bool]]] = [ "id": "3e163c6c-64eb-4448-924f-814b629b3810", "name": "default", "default": True, + "cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log", "description": "Default preset for yt-dlp. It will download whatever yt-dlp decides is the best quality for the video and audio.", }, { "id": "441675ed-b739-40f0-a0b0-1ecfcb9dc48d", "name": "Mobile", - "cli": '-t mp4 --merge-output-format mp4 --add-chapters --remux-video mp4 \n--embed-metadata --embed-thumbnail \n--postprocessor-args "-movflags +faststart"', + "cli": '--socket-timeout 30 --download-archive %(config_path)s/archive.log\n-t mp4 --merge-output-format mp4 --add-chapters --remux-video mp4 \n--embed-metadata --embed-thumbnail \n--postprocessor-args "-movflags +faststart"', "default": True, "description": "This preset is designed for mobile devices. It will download the best quality video and audio in mp4 format, merge them, and add chapters, metadata, and thumbnail.", }, { "id": "441675ed-b739-40f0-a0b0-1ecfcb9dc48b", "name": "1080p", - "cli": "-S vcodec:h264 --format 'bv[height<=1080][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'", + "cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log\n-S vcodec:h264 --format 'bv[height<=1080][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'", "default": True, "description": "Download the best quality video and audio in mp4 format for 1080p resolution.", }, { "id": "9719fcc3-4cf2-4d88-b1e4-74dff3dba00e", "name": "720p", - "cli": "-S vcodec:h264 --format 'bv[height<=720][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'", + "cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log\n-S vcodec:h264 --format 'bv[height<=720][ext=mp4]+ba[ext=m4a]/b[ext=mp4]/b[ext=webm]'", "default": True, "description": "Download the best quality video and audio in mp4 format for 720p resolution.", }, { "id": "a6fd4b25-2b3e-458d-bb57-b75e41cc4330", "name": "Audio Only", - "cli": "--extract-audio --add-chapters --embed-metadata --embed-thumbnail --format 'bestaudio/best'", + "cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log\n--extract-audio --add-chapters --embed-metadata --embed-thumbnail --format 'bestaudio/best'", "default": True, "description": "This preset is designed to download only the audio of the video. It will extract the audio, add chapters, metadata, and thumbnail.", }, @@ -55,7 +56,7 @@ DEFAULT_PRESETS: list[dict[int, dict[str, str | bool]]] = [ "description": 'This preset generate specific filename format and metadata to work with yt-dlp info reader plugins for jellyfin/emby and plex and to make play state sync work for WatchState.\n\nThere is one more step you need to do via Other > Terminal if you have it enabled or directly from container shell\n\nyt-dlp -I0 --write-info-json --write-thumbnail --convert-thumbnails jpg --paths /downloads/youtube -o "%(channel|Unknown_title)s [%(channel_id|Unknown_id)s]/%(title).180B [%(channel_id|Unknown_id)s].%(ext)s" -- https://www.youtube.com/channel/UCClfFsWcT3N2I7VTXXyt84A\n\nChange the url to the channel you want to download.\n\nFor more information please visit \nhttps://github.com/arabcoders/watchstate/blob/master/FAQ.md#how-to-get-watchstate-working-with-youtube-contentlibrary', "folder": "youtube", "template": "%(channel)s %(channel_id|Unknown_id)s/Season %(release_date>%Y,upload_date>%Y|Unknown)s/%(release_date>%Y%m%d,upload_date>%Y%m%d)s - %(title).180B [%(extractor)s-%(id)s].%(ext)s", - "cli": "--windows-filenames --write-info-json --embed-info-json \n--convert-thumbnails jpg --write-thumbnail --embed-metadata", + "cli": "--socket-timeout 30 --download-archive %(config_path)s/archive.log\n--windows-filenames --write-info-json --embed-info-json \n--convert-thumbnails jpg --write-thumbnail --embed-metadata", "default": True, }, ] @@ -92,6 +93,7 @@ class Preset: def json(self) -> str: from .encoder import Encoder + return Encoder().encode(self.serialize()) def get(self, key: str, default: Any = None) -> Any: diff --git a/app/library/YTDLPOpts.py b/app/library/YTDLPOpts.py index ee421357..ab1b6239 100644 --- a/app/library/YTDLPOpts.py +++ b/app/library/YTDLPOpts.py @@ -22,7 +22,7 @@ class YTDLPOpts: """The command options for yt-dlp from preset.""" def __init__(self): - self._config = Config.get_instance() + self._config: Config = Config.get_instance() @staticmethod def get_instance() -> "YTDLPOpts": @@ -96,7 +96,7 @@ class YTDLPOpts: """ preset: Preset | None = Presets.get_instance().get(name) - if not preset or "default" == name: + if not preset: return self if preset.cli: @@ -169,7 +169,11 @@ class YTDLPOpts: if len(self._item_cli) > 0: try: - user_cli: dict = arg_converter(args="\n".join(self._item_cli), level=True) + user_cli = "\n".join(self._item_cli) + for k, v in self._config.get_replacers().items(): + user_cli = user_cli.replace(f"%({k})s", v if isinstance(v, str) else str(v)) + + user_cli: dict = arg_converter(args=user_cli, level=True) except Exception as e: msg = f"Invalid command options for yt-dlp were given. '{e!s}'." raise ValueError(msg) from e diff --git a/app/library/config.py b/app/library/config.py index 3cb54a21..7331629b 100644 --- a/app/library/config.py +++ b/app/library/config.py @@ -402,6 +402,9 @@ class Config: self._ytdlp_cli_mutable += f"\n--socket-timeout {self.socket_timeout}" if self.keep_archive: + LOG.warning( + "The global 'keep_archive' option is deprecated and will be removed in future releases. please use presets instead." + ) archive_file: Path = Path(self.archive_file) if not archive_file.exists(): LOG.info(f"Creating archive file '{archive_file}'.") @@ -537,7 +540,7 @@ class Config: data["ytdlp_version"] = Config._ytdlp_version() return data - def get_replacer(self) -> dict: + def get_replacers(self) -> dict: """ Get the variables that can be used in Command options for yt-dlp. diff --git a/ui/app/pages/index.vue b/ui/app/pages/index.vue index ffd3042d..c42cbf20 100644 --- a/ui/app/pages/index.vue +++ b/ui/app/pages/index.vue @@ -72,25 +72,16 @@

@@ -98,6 +89,22 @@ These changes help reduce confusion from multiple sources of truth. Going forward, presets and the Command options for yt-dlp will be the single source of truth.

+

+ Notable changes in v0.10.x: +

+ diff --git a/ui/app/stores/ConfigStore.ts b/ui/app/stores/ConfigStore.ts index 7b1bb05f..8b61e0af 100644 --- a/ui/app/stores/ConfigStore.ts +++ b/ui/app/stores/ConfigStore.ts @@ -6,7 +6,6 @@ export const useConfigStore = defineStore('config', () => { showForm: useStorage('showForm', false), app: { download_path: '/downloads', - keep_archive: false, remove_files: false, ui_update_title: true, output_template: '', diff --git a/ui/app/types/config.d.ts b/ui/app/types/config.d.ts index 60960563..d6f22f2c 100644 --- a/ui/app/types/config.d.ts +++ b/ui/app/types/config.d.ts @@ -4,8 +4,6 @@ import type { DLField } from "./dl_fields" type AppConfig = { /** Path where downloaded files will be saved */ download_path: string - /** Indicates if the app should keep an archive of downloaded files */ - keep_archive: boolean /** Indicates if files should be removed after download */ remove_files: boolean /** Indicates if the UI should update the title with the current download status */