remove presets v1.0
This commit is contained in:
parent
ff2139261b
commit
fa9c084397
8 changed files with 112 additions and 237 deletions
|
|
@ -392,7 +392,7 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
{ "status": "text" }
|
{ "status": "text" }
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_preset = Presets.get_instance().get(name=preset)
|
_preset = Presets.get_instance().get(preset)
|
||||||
|
|
||||||
config = config if config else {}
|
config = config if config else {}
|
||||||
if cli:
|
if cli:
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,7 @@ class HttpAPI(Common):
|
||||||
|
|
||||||
preset = request.query.get("preset")
|
preset = request.query.get("preset")
|
||||||
if preset:
|
if preset:
|
||||||
exists = Presets.get_instance().get(name=preset)
|
exists = Presets.get_instance().get(preset)
|
||||||
if not exists:
|
if not exists:
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
data={"status": False, "message": f"Preset '{preset}' does not exist."},
|
data={"status": False, "message": f"Preset '{preset}' does not exist."},
|
||||||
|
|
@ -674,7 +674,7 @@ class HttpAPI(Common):
|
||||||
|
|
||||||
preset = request.query.get("preset")
|
preset = request.query.get("preset")
|
||||||
if preset:
|
if preset:
|
||||||
exists = Presets.get_instance().get(name=preset)
|
exists = Presets.get_instance().get(preset)
|
||||||
if not exists:
|
if not exists:
|
||||||
return web.json_response(
|
return web.json_response(
|
||||||
data={"status": False, "message": f"Preset '{preset}' does not exist."},
|
data={"status": False, "message": f"Preset '{preset}' does not exist."},
|
||||||
|
|
@ -790,18 +790,6 @@ class HttpAPI(Common):
|
||||||
if not item.get("id", None) or not validate_uuid(item.get("id"), version=4):
|
if not item.get("id", None) or not validate_uuid(item.get("id"), version=4):
|
||||||
item["id"] = str(uuid.uuid4())
|
item["id"] = str(uuid.uuid4())
|
||||||
|
|
||||||
if not item.get("args", None) or str(item.get("args")).strip() == "":
|
|
||||||
item["args"] = {}
|
|
||||||
|
|
||||||
if item.get("args", None) and isinstance(item.get("args"), str):
|
|
||||||
item["args"] = json.loads(item.get("args"))
|
|
||||||
|
|
||||||
if not item.get("postprocessors", None) or str(item.get("postprocessors")).strip() == "":
|
|
||||||
item["postprocessors"] = []
|
|
||||||
|
|
||||||
if item.get("postprocessors", None) and isinstance(item.get("postprocessors"), str):
|
|
||||||
item["postprocessors"] = json.loads(item.get("postprocessors"))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cls.validate(item)
|
cls.validate(item)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from .config import Config
|
||||||
from .encoder import Encoder
|
from .encoder import Encoder
|
||||||
from .Events import EventBus, Events
|
from .Events import EventBus, Events
|
||||||
from .Singleton import Singleton
|
from .Singleton import Singleton
|
||||||
|
from .Utils import arg_converter, clean_item
|
||||||
|
|
||||||
LOG = logging.getLogger("presets")
|
LOG = logging.getLogger("presets")
|
||||||
|
|
||||||
|
|
@ -26,12 +27,6 @@ class Preset:
|
||||||
format: str
|
format: str
|
||||||
"""The format of the preset."""
|
"""The format of the preset."""
|
||||||
|
|
||||||
args: dict[str, list[str] | bool] = field(default_factory=dict)
|
|
||||||
"""The arguments of the preset."""
|
|
||||||
|
|
||||||
postprocessors: list = field(default_factory=list)
|
|
||||||
"""The postprocessors of the preset."""
|
|
||||||
|
|
||||||
folder: str = ""
|
folder: str = ""
|
||||||
"""The default download folder to use if non is given."""
|
"""The default download folder to use if non is given."""
|
||||||
|
|
||||||
|
|
@ -83,13 +78,19 @@ class Presets(metaclass=Singleton):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with open(os.path.join(os.path.dirname(__file__), "presets.json")) as f:
|
with open(os.path.join(os.path.dirname(__file__), "presets.json")) as f:
|
||||||
self._default_presets = [Preset(**preset) for preset in json.load(f)]
|
for i, preset in enumerate(json.load(f)):
|
||||||
|
try:
|
||||||
|
self.validate(preset)
|
||||||
|
self._default_presets.append(Preset(**preset))
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(f"Failed to parse default preset '{i}'. '{e!s}'.")
|
||||||
|
continue
|
||||||
|
|
||||||
EventBus.get_instance().subscribe(
|
def event_handler(_, __):
|
||||||
Events.PRESETS_ADD,
|
msg = "Not implemented"
|
||||||
lambda data, _, **kwargs: self.add(**data.data), # noqa: ARG005
|
raise Exception(msg)
|
||||||
f"{__class__.__name__}.save",
|
|
||||||
)
|
EventBus.get_instance().subscribe(Events.PRESETS_ADD, event_handler, f"{__class__.__name__}.save")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_instance() -> "Presets":
|
def get_instance() -> "Presets":
|
||||||
|
|
@ -150,22 +151,27 @@ class Presets(metaclass=Singleton):
|
||||||
LOG.info(f"No presets were defined in '{self._file}'.")
|
LOG.info(f"No presets were defined in '{self._file}'.")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
needSaving = False
|
need_save = False
|
||||||
|
|
||||||
for i, preset in enumerate(presets):
|
for i, preset in enumerate(presets):
|
||||||
try:
|
try:
|
||||||
if "id" not in preset:
|
if "id" not in preset:
|
||||||
preset["id"] = str(uuid.uuid4())
|
preset["id"] = str(uuid.uuid4())
|
||||||
needSaving = True
|
need_save = True
|
||||||
|
|
||||||
|
preset, preset_status = clean_item(preset, keys=("args", "postprocessors"))
|
||||||
preset = Preset(**preset)
|
preset = Preset(**preset)
|
||||||
|
|
||||||
|
if preset_status:
|
||||||
|
need_save = True
|
||||||
|
|
||||||
self._presets.append(preset)
|
self._presets.append(preset)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(f"Failed to parse preset at list position '{i}'. '{e!s}'.")
|
LOG.error(f"Failed to parse preset at list position '{i}'. '{e!s}'.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if needSaving:
|
if need_save:
|
||||||
LOG.info("Saving presets due to missing ids.")
|
LOG.info("Saving presets due to format, or id change.")
|
||||||
self.save(self._presets)
|
self.save(self._presets)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
@ -198,7 +204,7 @@ class Presets(metaclass=Singleton):
|
||||||
"""
|
"""
|
||||||
if not isinstance(preset, dict):
|
if not isinstance(preset, dict):
|
||||||
if not isinstance(preset, Preset):
|
if not isinstance(preset, Preset):
|
||||||
msg = "Invalid preset type."
|
msg = f"Invalid preset type. Was expecting a (Preset|dict), but got '{type(preset).__name__}'."
|
||||||
raise ValueError(msg) # noqa: TRY004
|
raise ValueError(msg) # noqa: TRY004
|
||||||
|
|
||||||
preset = preset.serialize()
|
preset = preset.serialize()
|
||||||
|
|
@ -215,18 +221,8 @@ class Presets(metaclass=Singleton):
|
||||||
msg = "No format found."
|
msg = "No format found."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
if preset.get("args") and not isinstance(preset.get("args"), dict):
|
|
||||||
msg = "Invalid args type. expected dict."
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
if preset.get("postprocessors") and not isinstance(preset.get("postprocessors"), list):
|
|
||||||
msg = "Invalid postprocessors type. expected list."
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
if preset.get("cli"):
|
if preset.get("cli"):
|
||||||
try:
|
try:
|
||||||
from .Utils import arg_converter
|
|
||||||
|
|
||||||
arg_converter(args=preset.get("cli"))
|
arg_converter(args=preset.get("cli"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = f"Invalid cli options. '{e!s}'."
|
msg = f"Invalid cli options. '{e!s}'."
|
||||||
|
|
@ -270,26 +266,24 @@ class Presets(metaclass=Singleton):
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get(self, id: str | None = None, name: str | None = None) -> Preset | None:
|
def get(self, id_or_name: str) -> Preset | None:
|
||||||
"""
|
"""
|
||||||
Get the preset by id or name.
|
Get the preset by id or name.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
id (str|None): The id of the preset.
|
id_or_name (str): The id or name of the preset.
|
||||||
name (str|None): The name of the preset.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Preset|None: The preset if found, None otherwise.
|
Preset|None: The preset if found, None otherwise.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not id and not name:
|
if not id_or_name:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for preset in self.get_all():
|
for preset in self.get_all():
|
||||||
if preset.id == id:
|
if id_or_name not in (preset.id, preset.name):
|
||||||
return preset
|
continue
|
||||||
|
|
||||||
if preset.name == name:
|
return preset
|
||||||
return preset
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from .encoder import Encoder
|
||||||
from .Events import EventBus, Events, error, info, success
|
from .Events import EventBus, Events, error, info, success
|
||||||
from .Scheduler import Scheduler
|
from .Scheduler import Scheduler
|
||||||
from .Singleton import Singleton
|
from .Singleton import Singleton
|
||||||
|
from .Utils import clean_item
|
||||||
|
|
||||||
LOG = logging.getLogger("tasks")
|
LOG = logging.getLogger("tasks")
|
||||||
|
|
||||||
|
|
@ -140,13 +141,13 @@ class Tasks(metaclass=Singleton):
|
||||||
LOG.info(f"No tasks were defined in '{self._file}'.")
|
LOG.info(f"No tasks were defined in '{self._file}'.")
|
||||||
return self
|
return self
|
||||||
|
|
||||||
need_update = False
|
need_save = False
|
||||||
for i, task in enumerate(tasks):
|
for i, task in enumerate(tasks):
|
||||||
try:
|
try:
|
||||||
task, task_status = self.clean_task(task)
|
task, task_status = clean_item(task, keys=("cookies", "config"))
|
||||||
task = Task(**task)
|
task = Task(**task)
|
||||||
if task_status:
|
if task_status:
|
||||||
need_update = True
|
need_save = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(f"Failed to parse task at list position '{i}'. '{e!s}'.")
|
LOG.error(f"Failed to parse task at list position '{i}'. '{e!s}'.")
|
||||||
continue
|
continue
|
||||||
|
|
@ -160,7 +161,7 @@ class Tasks(metaclass=Singleton):
|
||||||
LOG.exception(e)
|
LOG.exception(e)
|
||||||
LOG.error(f"Failed to queue task '{i}: {task.name}'. '{e!s}'.")
|
LOG.error(f"Failed to queue task '{i}: {task.name}'. '{e!s}'.")
|
||||||
|
|
||||||
if need_update:
|
if need_save:
|
||||||
LOG.info("Updating tasks file to remove old keys.")
|
LOG.info("Updating tasks file to remove old keys.")
|
||||||
self.save(self.get_all())
|
self.save(self.get_all())
|
||||||
|
|
||||||
|
|
@ -223,6 +224,7 @@ class Tasks(metaclass=Singleton):
|
||||||
if task.get("cli"):
|
if task.get("cli"):
|
||||||
try:
|
try:
|
||||||
from .Utils import arg_converter
|
from .Utils import arg_converter
|
||||||
|
|
||||||
arg_converter(args=task.get("cli"))
|
arg_converter(args=task.get("cli"))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = f"Invalid cli options. '{e!s}'."
|
msg = f"Invalid cli options. '{e!s}'."
|
||||||
|
|
@ -266,27 +268,6 @@ class Tasks(metaclass=Singleton):
|
||||||
|
|
||||||
return self
|
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):
|
async def _runner(self, task: Task):
|
||||||
"""
|
"""
|
||||||
Run the task.
|
Run the task.
|
||||||
|
|
|
||||||
|
|
@ -522,8 +522,6 @@ def arg_converter(
|
||||||
|
|
||||||
bad_options.update(item.items())
|
bad_options.update(item.items())
|
||||||
|
|
||||||
LOG.debug("Removed %i the following options: '%s'.", level, ", ".join(bad_options.values()))
|
|
||||||
|
|
||||||
for key in diff.copy():
|
for key in diff.copy():
|
||||||
if key not in bad_options:
|
if key not in bad_options:
|
||||||
continue
|
continue
|
||||||
|
|
@ -889,3 +887,41 @@ def get_files(base_path: str, dir: str | None = None):
|
||||||
)
|
)
|
||||||
|
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
|
|
||||||
|
def clean_item(item: dict, keys: list | tuple) -> tuple[dict, bool]:
|
||||||
|
"""
|
||||||
|
Remove given keys from a dictionary.
|
||||||
|
This function modifies the dictionary in place and returns a tuple
|
||||||
|
containing the modified dictionary and a boolean indicating
|
||||||
|
whether any keys were removed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
item (dict): The item to clean.
|
||||||
|
keys (list|tuple): The keys to remove.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[dict, bool]: The cleaned item and a the status of cleaning operation.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If item is not a dictionary or keys is not a list or tuple.
|
||||||
|
|
||||||
|
"""
|
||||||
|
status = False
|
||||||
|
|
||||||
|
if not isinstance(item, dict):
|
||||||
|
msg = "Item must be a dictionary."
|
||||||
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
if not isinstance(keys, list | tuple):
|
||||||
|
msg = "Keys must be a list or tuple."
|
||||||
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
for key in keys:
|
||||||
|
if key not in item:
|
||||||
|
continue
|
||||||
|
|
||||||
|
status = True
|
||||||
|
item.pop(key)
|
||||||
|
|
||||||
|
return item, status
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class YTDLPOpts(metaclass=Singleton):
|
||||||
YTDLPOpts: The instance of the class
|
YTDLPOpts: The instance of the class
|
||||||
|
|
||||||
"""
|
"""
|
||||||
preset = Presets.get_instance().get(name=name)
|
preset = Presets.get_instance().get(name)
|
||||||
if not preset or "default" == name:
|
if not preset or "default" == name:
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
@ -118,25 +118,6 @@ class YTDLPOpts(metaclass=Singleton):
|
||||||
"temp": self._config.temp_path,
|
"temp": self._config.temp_path,
|
||||||
}
|
}
|
||||||
|
|
||||||
# @Deprecated - To be removed in future versions.
|
|
||||||
if not preset.cli:
|
|
||||||
if preset.postprocessors and isinstance(preset.postprocessors, list) and len(preset.postprocessors) > 0:
|
|
||||||
self._preset_opts["postprocessors"] = preset.postprocessors
|
|
||||||
|
|
||||||
if preset.args and isinstance(preset.args, dict) and len(preset.args) > 0:
|
|
||||||
bad_options = {k: v for d in REMOVE_KEYS for k, v in d.items()}
|
|
||||||
removed_options = []
|
|
||||||
for key, value in preset.args.items():
|
|
||||||
if key in bad_options:
|
|
||||||
removed_options.append(bad_options[key])
|
|
||||||
continue
|
|
||||||
self._preset_opts[key] = value
|
|
||||||
|
|
||||||
if len(removed_options) > 0:
|
|
||||||
LOG.warning(
|
|
||||||
"Removed the following options '%s' from '%s' args.", ", ".join(removed_options), preset.name
|
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_all(self, keep: bool = False) -> dict:
|
def get_all(self, keep: bool = False) -> dict:
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,13 @@
|
||||||
|
|
||||||
<div class="column is-12" v-if="showImport || !reference">
|
<div class="column is-12" v-if="showImport || !reference">
|
||||||
<label class="label is-inline" for="import_string">
|
<label class="label is-inline" for="import_string">
|
||||||
|
<span class="icon"><i class="fa-solid fa-file-import" /></span>
|
||||||
Import string
|
Import string
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control has-icons-left is-expanded">
|
<div class="control is-expanded">
|
||||||
<input type="text" class="input" id="import_string" v-model="import_string" autocomplete="off">
|
<input type="text" class="input" id="import_string" v-model="import_string" autocomplete="off">
|
||||||
<span class="icon is-small is-left"><i class="fa-solid fa-t" /></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control">
|
<div class="control">
|
||||||
|
|
@ -51,31 +51,35 @@
|
||||||
|
|
||||||
<div class="column is-6-tablet is-12-mobile">
|
<div class="column is-6-tablet is-12-mobile">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline" for="name" v-text="'Name'" />
|
<label class="label is-inline" for="name">
|
||||||
<div class="control has-icons-left">
|
<span class="icon"><i class="fa-solid fa-tag" /></span>
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<div class="control">
|
||||||
<input type="text" class="input" id="name" v-model="form.name" :disabled="addInProgress">
|
<input type="text" class="input" id="name" v-model="form.name" :disabled="addInProgress">
|
||||||
<span class="icon is-small is-left"><i class="fa-solid fa-n" /></span>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>The name to refers to this custom settings.</span>
|
<span>The name to refers to this preset of settings.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column is-6-tablet is-12-mobile">
|
<div class="column is-6-tablet is-12-mobile">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline" for="format" v-text="'Format'" />
|
<label class="label is-inline" for="format">
|
||||||
<div class="control has-icons-left">
|
<span class="icon"><i class="fa-solid fa-f" /></span>
|
||||||
|
Format
|
||||||
|
</label>
|
||||||
|
<div class="control">
|
||||||
<input type="text" class="input" id="format" v-model="form.format" :disabled="addInProgress">
|
<input type="text" class="input" id="format" v-model="form.format" :disabled="addInProgress">
|
||||||
<span class="icon is-small is-left"><i class="fa-solid fa-f" /></span>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>The yt-dlp <code>[--format, -f]</code> video format code. see <NuxtLink
|
<span>The yt-dlp <code>[--format, -f]</code> video format code. see <NuxtLink
|
||||||
href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#format-selection" target="blank">this
|
href="https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#format-selection" target="blank">this
|
||||||
url</NuxtLink> for more info.</span>. Note, as this key is required, you can set the value to
|
page</NuxtLink> for more info. Note, as this key is required, you can set the value to
|
||||||
<code>default</code> to let <code>yt-dlp</code> choose the best format.
|
<code>default</code> to let <code>yt-dlp</code> choose the best format.</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -83,12 +87,12 @@
|
||||||
<div class="column is-6-tablet is-12-mobile">
|
<div class="column is-6-tablet is-12-mobile">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline" for="folder">
|
<label class="label is-inline" for="folder">
|
||||||
|
<span class="icon"><i class="fa-solid fa-folder" /></span>
|
||||||
Default Download path
|
Default Download path
|
||||||
</label>
|
</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control">
|
||||||
<input type="text" class="input" id="folder" placeholder="Leave empty to use default download path"
|
<input type="text" class="input" id="folder" placeholder="Leave empty to use default download path"
|
||||||
v-model="form.folder" :disabled="addInProgress" list="folders">
|
v-model="form.folder" :disabled="addInProgress" list="folders">
|
||||||
<span class="icon is-small is-left"><i class="fa-solid fa-folder" /></span>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
|
|
@ -101,19 +105,19 @@
|
||||||
<div class="column is-6-tablet is-12-mobile">
|
<div class="column is-6-tablet is-12-mobile">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline" for="output_template">
|
<label class="label is-inline" for="output_template">
|
||||||
|
<span class="icon"><i class="fa-solid fa-file" /></span>
|
||||||
Default Output template
|
Default Output template
|
||||||
</label>
|
</label>
|
||||||
<div class="control has-icons-left">
|
<div class="control">
|
||||||
<input type="text" class="input" id="output_template" :disabled="addInProgress"
|
<input type="text" class="input" id="output_template" :disabled="addInProgress"
|
||||||
placeholder="Leave empty to use default template." v-model="form.template">
|
placeholder="Leave empty to use default template." v-model="form.template">
|
||||||
<span class="icon is-small is-left"><i class="fa-solid fa-file" /></span>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>Use this output template if non are given with URL. if not set, it will defaults to
|
<span>Use this output template if non are given with URL. if not set, it will defaults to
|
||||||
<code>{{ config.app.output_template }}</code>.
|
<code>{{ config.app.output_template }}</code>.
|
||||||
For more information <NuxtLink href="https://github.com/yt-dlp/yt-dlp#output-template"
|
For more information visit <NuxtLink href="https://github.com/yt-dlp/yt-dlp#output-template"
|
||||||
target="_blank">visit this url</NuxtLink>.
|
target="_blank">this page</NuxtLink>.
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -122,11 +126,12 @@
|
||||||
<div class="column is-12">
|
<div class="column is-12">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline" for="cli_options">
|
<label class="label is-inline" for="cli_options">
|
||||||
|
<span class="icon"><i class="fa-solid fa-terminal" /></span>
|
||||||
Command arguments for yt-dlp
|
Command arguments for yt-dlp
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input type="text" class="input" v-model="form.cli" id="cli_options" :disabled="addInProgress"
|
<textarea class="textarea is-pre" v-model="form.cli" id="cli_options" :disabled="addInProgress"
|
||||||
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail">
|
placeholder="command options to use, e.g. --no-embed-metadata --no-embed-thumbnail" />
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
|
|
@ -141,8 +146,10 @@
|
||||||
|
|
||||||
<div class="column is-12">
|
<div class="column is-12">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label is-inline" for="cookies"
|
<label class="label is-inline" for="cookies" v-tooltip="'Netscape HTTP Cookie format.'">
|
||||||
v-tooltip="'Netscape HTTP Cookie format.'">Cookies</label>
|
<span class="icon"><i class="fa-solid fa-cookie" /></span>
|
||||||
|
Cookies
|
||||||
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<textarea class="textarea is-pre" id="cookies" v-model="form.cookies" :disabled="addInProgress"
|
<textarea class="textarea is-pre" id="cookies" v-model="form.cookies" :disabled="addInProgress"
|
||||||
placeholder="Leave empty to use default cookies" />
|
placeholder="Leave empty to use default cookies" />
|
||||||
|
|
@ -157,56 +164,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column is-12" v-if="has_data(form?.args) || has_data(form?.postprocessors)">
|
|
||||||
<Message title="Deprecation Warning" class="is-background-warning-80 has-text-dark"
|
|
||||||
icon="fas fa-exclamation-circle">
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
The <code>JSON yt-dlp config</code> and <code>JSON yt-dlp Post-Processors</code> fields are
|
|
||||||
deprecated and will be removed in the future. Please use the <b>Command arguments for yt-dlp</b>
|
|
||||||
field instead. The deprecated fields will still be working for now but they will stop, we suggest
|
|
||||||
that you migrate to the new field. as soon as possible to avoid any issues. No support will be
|
|
||||||
given for the deprecated fields.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
If both fields are set, the <b>Command arguments for yt-dlp</b> field will take precedence over
|
|
||||||
the deprecated fields. and when you click save it will remove the deprecated fields.
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</Message>
|
|
||||||
</div>
|
|
||||||
<div class="column is-6-tablet is-12-mobile" v-if="has_data(form?.args)">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label is-inline" for="args" v-tooltip="'Extends current global yt-dlp config. (JSON)'">
|
|
||||||
JSON yt-dlp config <span class="has-text-danger">(DEPRECATED)</span>
|
|
||||||
</label>
|
|
||||||
<div class="control">
|
|
||||||
<textarea class="textarea" id="args" v-model="form.args" :disabled="addInProgress"
|
|
||||||
placeholder="{}" />
|
|
||||||
</div>
|
|
||||||
<span class="help has-text-danger">
|
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
|
||||||
<span>Deprecated, use <b>Command arguments for yt-dlp</b> field instead. </span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="column is-6-tablet is-12-mobile" v-if="has_data(form?.postprocessors)">
|
|
||||||
<div class="field">
|
|
||||||
<label class="label is-inline" for="postprocessors"
|
|
||||||
v-tooltip="'Things to do after download is done.'">
|
|
||||||
JSON yt-dlp Post-Processors <span class="has-text-danger">(DEPRECATED)</span>
|
|
||||||
</label>
|
|
||||||
<div class="control">
|
|
||||||
<textarea class="textarea" id="postprocessors" v-model="form.postprocessors"
|
|
||||||
:disabled="addInProgress" placeholder="[]" />
|
|
||||||
</div>
|
|
||||||
<span class="help has-text-danger">
|
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
|
||||||
<span>Deprecated, use <b>Command arguments for yt-dlp</b> field instead. </span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -319,55 +277,11 @@ const checkInfo = async () => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (form?.cli && '' !== form.cli) {
|
|
||||||
if ((has_data(copy?.args) || has_data(copy?.postprocessors))) {
|
|
||||||
if (false === confirm('cli options are set, this will remove the JSON yt-dlp config and Post-Processors. Are you sure?')) {
|
|
||||||
toast.warning('User cancelled the operation.')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy?.args) {
|
|
||||||
delete copy.args
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy?.postprocessors) {
|
|
||||||
delete copy.postprocessors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (typeof copy.args === 'object') {
|
|
||||||
copy.args = JSON.stringify(copy.args, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof copy.postprocessors === 'object') {
|
|
||||||
copy.postprocessors = JSON.stringify(copy.postprocessors, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy?.args) {
|
|
||||||
try {
|
|
||||||
copy.args = JSON.parse(copy.args)
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`Invalid JSON yt-dlp config. ${e.message}`)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (copy?.postprocessors) {
|
|
||||||
try {
|
|
||||||
copy.postprocessors = JSON.parse(copy.postprocessors)
|
|
||||||
} catch (e) {
|
|
||||||
toast.error(`Invalid JSON yt-dlp Post-Processors. ${e.message}`)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// trim all fields in copy only if they are strings
|
|
||||||
for (const key in copy) {
|
for (const key in copy) {
|
||||||
if (typeof copy[key] === 'string') {
|
if (typeof copy[key] !== 'string') {
|
||||||
copy[key] = copy[key].trim()
|
continue
|
||||||
}
|
}
|
||||||
|
copy[key] = copy[key].trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter('submit', { reference: toRaw(props.reference), preset: toRaw(copy) });
|
emitter('submit', { reference: toRaw(props.reference), preset: toRaw(copy) });
|
||||||
|
|
@ -417,18 +331,13 @@ const importItem = async () => {
|
||||||
try {
|
try {
|
||||||
const item = JSON.parse(val)
|
const item = JSON.parse(val)
|
||||||
|
|
||||||
console.log(item)
|
if (item?._type || 'preset' !== item._type) {
|
||||||
|
toast.error(`Invalid import string. Expected type 'preset', got '${item._type ?? 'unknown'}'.`)
|
||||||
if ('preset' !== item._type) {
|
|
||||||
toast.error(`Invalid import string. Expected type 'preset', got '${item._type}'.`)
|
|
||||||
import_string.value = ''
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (form.format || form.cli) {
|
if ((form.format || form.cli) && false === confirm('This will overwrite the current data. Are you sure?')) {
|
||||||
if (false === confirm('This will overwrite the current form fields. Are you sure?')) {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.name) {
|
if (item.name) {
|
||||||
|
|
|
||||||
|
|
@ -71,15 +71,6 @@ div.is-centered {
|
||||||
</header>
|
</header>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<template v-if="has_data(item?.args) || has_data(item.postprocessors)">
|
|
||||||
<p class="has-text-danger is-5 has-text-bold">
|
|
||||||
<span class="icon"><i class="fa-solid fa-triangle-exclamation" /></span>
|
|
||||||
<span>The preset is using deprecated options. It is recommended to update the preset to use the
|
|
||||||
new options. It will cease to work in future versions.</span>
|
|
||||||
</p>
|
|
||||||
<hr>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<p class="is-text-overflow"
|
<p class="is-text-overflow"
|
||||||
v-if="item?.format && false === ['default', 'not_set'].includes(item.format)">
|
v-if="item?.format && false === ['default', 'not_set'].includes(item.format)">
|
||||||
<span class="icon"><i class="fa-solid fa-f" /></span>
|
<span class="icon"><i class="fa-solid fa-f" /></span>
|
||||||
|
|
@ -307,11 +298,6 @@ const exportItem = item => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (has_data(data?.args) || has_data(data?.postprocessors)) {
|
|
||||||
toast.error("v1.0 presets are no longer supported target for export. Please update your preset.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let userData = {}
|
let userData = {}
|
||||||
|
|
||||||
for (const key of Object.keys(data)) {
|
for (const key of Object.keys(data)) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue