diff --git a/app/library/HttpAPI.py b/app/library/HttpAPI.py
index 74177928..f2cc37c6 100644
--- a/app/library/HttpAPI.py
+++ b/app/library/HttpAPI.py
@@ -872,15 +872,12 @@ class HttpAPI(Common):
if not item.get("timer", None) or str(item.get("timer")).strip() == "":
item["timer"] = f"{random.randint(1,59)} */1 * * *" # noqa: S311
- if not item.get("cookies", None):
- item["cookies"] = ""
-
- if not item.get("config", None) or str(item.get("config")).strip() == "":
- item["config"] = {}
-
if not item.get("template", None):
item["template"] = ""
+ if not item.get("cli", None):
+ item["cli"] = ""
+
try:
ins.validate(item)
except ValueError as e:
diff --git a/app/library/Tasks.py b/app/library/Tasks.py
index b3ac7a33..c5633ee9 100644
--- a/app/library/Tasks.py
+++ b/app/library/Tasks.py
@@ -3,13 +3,15 @@ import json
import logging
import os
import time
-from dataclasses import dataclass, field
+from dataclasses import dataclass
from datetime import UTC, datetime
from typing import Any
import httpx
from aiohttp import web
+from app.library.Utils import arg_converter
+
from .config import Config
from .encoder import Encoder
from .Events import EventBus, Events, error, info, success
@@ -28,8 +30,7 @@ class Task:
preset: str = ""
timer: str = ""
template: str = ""
- cookies: str = ""
- config: dict[str, str] = field(default_factory=dict)
+ cli: str = ""
def serialize(self) -> dict:
return self.__dict__
@@ -134,16 +135,20 @@ class Tasks(metaclass=Singleton):
with open(self._file) as f:
tasks = json.load(f)
except Exception as e:
- LOG.error(f"Failed to parse tasks from '{self._file}'. '{e}'.")
+ LOG.error(f"Failed to parse tasks from '{self._file}'. '{e!s}'.")
return self
if not tasks or len(tasks) < 1:
LOG.info(f"No tasks were defined in '{self._file}'.")
return self
+ need_update = False
for i, task in enumerate(tasks):
try:
+ task, task_status = self.clean_task(task)
task = Task(**task)
+ if task_status:
+ need_update = True
except Exception as e:
LOG.error(f"Failed to parse task at list position '{i}'. '{e!s}'.")
continue
@@ -157,6 +162,10 @@ class Tasks(metaclass=Singleton):
LOG.exception(e)
LOG.error(f"Failed to queue task '{i}: {task.name}'. '{e!s}'.")
+ if need_update:
+ LOG.info("Updating tasks file to remove old keys.")
+ self.save(self.get_all())
+
return self
def clear(self, shutdown: bool = False) -> "Tasks":
@@ -213,17 +222,12 @@ class Tasks(metaclass=Singleton):
msg = "No URL found."
raise ValueError(msg)
- if not isinstance(task.get("cookies"), str):
- msg = "Invalid cookies type."
- raise ValueError(msg) # noqa: TRY004
-
- if not isinstance(task.get("config"), dict):
- msg = "Invalid config type."
- raise ValueError(msg) # noqa: TRY004
-
- if not isinstance(task.get("template"), str):
- msg = "Invalid template type."
- raise ValueError(msg) # noqa: TRY004
+ if task.get("cli"):
+ try:
+ arg_converter(args=task.get("cli"))
+ except Exception as e:
+ msg = f"Invalid cli options. '{e!s}'."
+ raise ValueError(msg) from e
return True
@@ -263,6 +267,27 @@ class Tasks(metaclass=Singleton):
return self
+ def clean_task(self, task: dict) -> tuple[dict, bool]:
+ """
+ Clean the task from old keys.
+
+ Args:
+ task (dict): The task to clean.
+
+ Returns:
+ tuple[dict, bool]: The cleaned task and a status if the task was cleaned.
+
+ """
+ status = False
+ removedKeys = ["cookies", "config"]
+
+ for key in removedKeys:
+ if key in task:
+ status = True
+ task.pop(key)
+
+ return task, status
+
async def _runner(self, task: Task):
"""
Run the task.
@@ -283,16 +308,8 @@ class Tasks(metaclass=Singleton):
preset: str = str(task.preset or self._default_preset)
folder: str = task.folder if task.folder else ""
- cookies: str = str(task.cookies) if task.cookies else ""
template: str = task.template if task.template else ""
-
- config = task.config if task.config else {}
- if isinstance(config, str) and config:
- try:
- config = json.loads(config)
- except Exception as e:
- LOG.error(f"Failed to parse json yt-dlp config for '{task.name}'. {e!s}")
- return
+ cli: str = task.cli if task.cli else ""
LOG.info(f"Task '{task.id}: {task.name}' dispatched at '{timeNow}'.")
@@ -307,9 +324,8 @@ class Tasks(metaclass=Singleton):
"url": task.url,
"preset": preset,
"folder": folder,
- "cookies": cookies,
- "config": config,
"template": template,
+ "cli": cli,
},
id=task.id,
),
diff --git a/app/library/Utils.py b/app/library/Utils.py
index 4251bb5e..0bddc15b 100644
--- a/app/library/Utils.py
+++ b/app/library/Utils.py
@@ -434,12 +434,13 @@ def validate_url(url: str) -> bool:
return True
-def arg_converter(args: str) -> dict:
+def arg_converter(args: str, remove_options: bool = False) -> dict:
"""
Convert yt-dlp options to a dictionary.
Args:
args (str): yt-dlp options string.
+ remove_options (bool): Remove default options.
Returns:
dict: yt-dlp options dictionary.
@@ -472,6 +473,14 @@ def arg_converter(args: str) -> dict:
if isinstance(matchFilter, set):
diff["match_filter"] = {"filters": list(matchFilter)}
+ if remove_options:
+ for key in diff.copy():
+ if key in IGNORED_KEYS:
+ diff.pop(key, None)
+
+ if "_warnings" in diff:
+ diff.pop("_warnings", None)
+
from .encoder import Encoder
return json.loads(json.dumps(diff, cls=Encoder))
diff --git a/app/library/common.py b/app/library/common.py
index 371ca22a..a89d3815 100644
--- a/app/library/common.py
+++ b/app/library/common.py
@@ -4,6 +4,7 @@ import logging
from .config import Config
from .DownloadQueue import DownloadQueue
from .encoder import Encoder
+from .Utils import arg_converter
LOG = logging.getLogger("common")
@@ -94,6 +95,14 @@ class Common:
msg = f"Failed to parse json yt-dlp config for '{url}'. {e!s}"
raise ValueError(msg) from e
+ cli = item.get("cli")
+ if cli and len(cli) > 1:
+ try:
+ config = arg_converter(args=cli, remove_options=True)
+ except Exception as e:
+ msg = f"Failed to parse yt-dlp cli options. {e!s}"
+ raise ValueError(msg) from e
+
return {
"url": url,
"preset": preset,
diff --git a/ui/components/TaskForm.vue b/ui/components/TaskForm.vue
index 63580dd7..5ed51093 100644
--- a/ui/components/TaskForm.vue
+++ b/ui/components/TaskForm.vue
@@ -50,7 +50,6 @@
-
@@ -81,14 +80,11 @@
-
+
- Select the preset to use for this URL. The preset will be ignored if format key is present in
- config.
+ Select the preset to use for this URL. If the
+ -f, --format argument is present in the command line options, the preset and all
+ it's options will be ignored.
+
@@ -126,7 +124,7 @@
-
+
- Downloads are relative to download path, defaults to root path if not set.
+ Paths are relative to global download path, defaults to preset download path if set otherwise,
+ fallback root path if not set.
-
+
-
+
- The output template to use, if not set, it will defaults to
+ Use this output template if non are given with URL. if not set, it will defaults to
{{ config.app.output_template }}.
For more information visit this url.
@@ -164,41 +163,24 @@
-
+
-
-
-
-
-
- Cookies
-
-
-
-
-
- Use the
- Recommended addon by yt-dlp to export cookies. The cookies MUST be in Netscape HTTP
- Cookie format.
+ yt-dlp cli arguments. Check this page.
+ For more info. Some arguments are ignored by default. Warning: Use with
+ caution some of those options can break yt-dlp or the frontend. If
+ -f, --format argument is present, the preset and all it's options will be
+ ignored.
+
- The task runner is simple queue system that allows you to schedule downloads. The tasks are run at the
- scheduled time.
+ The task runner is simple queue system that allows you to schedule downloads to run at the specific time.
- When you export task, the preset settings is automatically merged into the task and the preset set to
- default to be more portable. The exporter doesn't include Cookies field for
- security reasons.
-