removed deprecated features and flags
This commit is contained in:
parent
669f70d9b8
commit
f490c78179
25 changed files with 146 additions and 363 deletions
|
|
@ -26,7 +26,7 @@ live streams, and includes features like scheduling downloads, sending notificat
|
|||
* Supports `curl-cffi`. See [yt-dlp documentation](https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#impersonation)
|
||||
* Bundled `pot provider plugin`. See [yt-dlp documentation](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide)
|
||||
* Automatic updates for `yt-dlp` and custom `pip` packages.
|
||||
* Conditions feature.
|
||||
* Conditions feature to apply custom options based on `yt-dlp` returned info.
|
||||
* Custom browser extensions, bookmarklets and iOS shortcuts to send links to YTPTube instance.
|
||||
* A bundled executable version for Windows, macOS and Linux. `For non-docker users`.
|
||||
|
||||
|
|
|
|||
|
|
@ -489,9 +489,7 @@ class Download:
|
|||
and self.info.downloaded_bytes
|
||||
and self.info.downloaded_bytes > 0
|
||||
):
|
||||
self.logger.warning(
|
||||
f"Keeping temp folder '{self.temp_path}', as the reported status is not finished '{self.info.status}'."
|
||||
)
|
||||
self.logger.warning(f"Keeping temp folder '{self.temp_path}'. {self.info.status=}.")
|
||||
return
|
||||
|
||||
tmp_dir = Path(self.temp_path)
|
||||
|
|
|
|||
|
|
@ -152,9 +152,6 @@ class YTDLPOpts:
|
|||
self._item_cli = []
|
||||
|
||||
merge: list[str] = []
|
||||
if self._config._ytdlp_cli_mutable and len(self._config._ytdlp_cli_mutable) > 1:
|
||||
merge.append(self._config._ytdlp_cli_mutable)
|
||||
|
||||
if self._preset_cli and len(self._preset_cli) > 1:
|
||||
merge.append(self._preset_cli)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ import time
|
|||
from logging.handlers import TimedRotatingFileHandler
|
||||
from multiprocessing.managers import SyncManager
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import coloredlogs
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from .Singleton import Singleton
|
||||
from .Utils import FileLogFormatter, arg_converter
|
||||
from .Utils import FileLogFormatter
|
||||
from .version import APP_BRANCH, APP_BUILD_DATE, APP_COMMIT_SHA, APP_VERSION
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -96,9 +96,6 @@ class Config(metaclass=Singleton):
|
|||
debugpy_port: int = 5678
|
||||
"""The port to use for the debugpy server."""
|
||||
|
||||
socket_timeout: int = 30
|
||||
"""The socket timeout to use for yt-dlp."""
|
||||
|
||||
extract_info_timeout: int = 70
|
||||
"""The timeout to use for extracting video information."""
|
||||
|
||||
|
|
@ -138,9 +135,6 @@ class Config(metaclass=Singleton):
|
|||
ignore_ui: bool = False
|
||||
"Ignore the UI and run the application in the background."
|
||||
|
||||
basic_mode: bool = False
|
||||
"Run the frontend in basic mode."
|
||||
|
||||
default_preset: str = "default"
|
||||
"The default preset to use when no preset is specified."
|
||||
|
||||
|
|
@ -159,21 +153,12 @@ class Config(metaclass=Singleton):
|
|||
console_enabled: bool = False
|
||||
"Enable direct access to yt-dlp console."
|
||||
|
||||
browser_enabled: bool = True
|
||||
"Enable file browser access."
|
||||
|
||||
browser_control_enabled: bool = False
|
||||
"Enable file browser control access."
|
||||
|
||||
ytdlp_auto_update: bool = True
|
||||
"""Enable in-place auto update of yt-dlp package."""
|
||||
|
||||
ytdlp_cli: str = ""
|
||||
"""The command line options to use for yt-dlp."""
|
||||
|
||||
_ytdlp_cli_mutable: str = ""
|
||||
"""The command line options to use for yt-dlp."""
|
||||
|
||||
ytdlp_version: str | None = None
|
||||
"""The version of yt-dlp to use, if not set, the latest version will be used."""
|
||||
|
||||
|
|
@ -206,8 +191,6 @@ class Config(metaclass=Singleton):
|
|||
_immutable: tuple = (
|
||||
"ytdl_options",
|
||||
"started",
|
||||
"ytdlp_cli",
|
||||
"_ytdlp_cli_mutable",
|
||||
"is_native",
|
||||
"app_version",
|
||||
"app_commit_sha",
|
||||
|
|
@ -219,7 +202,6 @@ class Config(metaclass=Singleton):
|
|||
_int_vars: tuple = (
|
||||
"port",
|
||||
"max_workers",
|
||||
"socket_timeout",
|
||||
"extract_info_timeout",
|
||||
"debugpy_port",
|
||||
"playlist_items_concurrency",
|
||||
|
|
@ -238,10 +220,8 @@ class Config(metaclass=Singleton):
|
|||
"ignore_ui",
|
||||
"ui_update_title",
|
||||
"pip_ignore_updates",
|
||||
"basic_mode",
|
||||
"file_logging",
|
||||
"console_enabled",
|
||||
"browser_enabled",
|
||||
"browser_control_enabled",
|
||||
"ytdlp_auto_update",
|
||||
"prevent_premiere_live",
|
||||
|
|
@ -257,13 +237,10 @@ class Config(metaclass=Singleton):
|
|||
"remove_files",
|
||||
"ui_update_title",
|
||||
"max_workers",
|
||||
"basic_mode",
|
||||
"default_preset",
|
||||
"instance_title",
|
||||
"console_enabled",
|
||||
"browser_enabled",
|
||||
"browser_control_enabled",
|
||||
"ytdlp_cli",
|
||||
"file_logging",
|
||||
"base_path",
|
||||
"is_native",
|
||||
|
|
@ -351,7 +328,7 @@ class Config(metaclass=Singleton):
|
|||
if not self.base_path.endswith("/"):
|
||||
self.base_path += "/"
|
||||
|
||||
numeric_level = getattr(logging, self.log_level.upper(), None)
|
||||
numeric_level: int | None = getattr(logging, self.log_level.upper(), None)
|
||||
if not isinstance(numeric_level, int):
|
||||
msg = f"Invalid log level '{self.log_level}' specified."
|
||||
raise TypeError(msg)
|
||||
|
|
@ -372,55 +349,26 @@ class Config(metaclass=Singleton):
|
|||
debugpy.listen(("0.0.0.0", self.debugpy_port), in_process_debug_adapter=True)
|
||||
LOG.info(f"starting debugpy server on '0.0.0.0:{self.debugpy_port}'.")
|
||||
except ImportError:
|
||||
LOG.error("debugpy package not found, please install it with 'pip install debugpy'.")
|
||||
LOG.error("debugpy package not found, install it with 'uv sync'.")
|
||||
except Exception as e:
|
||||
LOG.error(f"Error starting debugpy server at '0.0.0.0:{self.debugpy_port}'. {e}")
|
||||
|
||||
opts_file: Path = Path(self.config_path) / "ytdlp.cli"
|
||||
if opts_file.exists() and opts_file.stat().st_size > 2:
|
||||
LOG.warning(
|
||||
"Usage of 'ytdlp.cli' global config file is deprecated and will be removed in future releases. please migrate to presets."
|
||||
)
|
||||
with open(opts_file) as f:
|
||||
self.ytdlp_cli = f.read().strip()
|
||||
if self.ytdlp_cli:
|
||||
self._ytdlp_cli_mutable = self.ytdlp_cli
|
||||
try:
|
||||
arg_converter(args=self.ytdlp_cli, level=True)
|
||||
except Exception as e:
|
||||
msg = f"Failed to parse yt-dlp cli options from '{opts_file}'. '{e!s}'."
|
||||
raise ValueError(msg) from e
|
||||
|
||||
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}'.")
|
||||
archive_file.touch(exist_ok=True)
|
||||
|
||||
LOG.info(f"keep archive option is enabled. Using archive file '{archive_file}' by default.")
|
||||
self._ytdlp_cli_mutable += f"\n--download-archive {archive_file.as_posix()!s}"
|
||||
if (Path(self.config_path) / "ytdlp.cli").exists():
|
||||
LOG.error("Support for ./ytdlp.cli file is removed, migrate to presets and remove the file.")
|
||||
|
||||
if self.temp_keep:
|
||||
LOG.info("Keep temp files option is enabled.")
|
||||
LOG.warning("Keep temp files option is enabled.")
|
||||
|
||||
if self.auth_password and self.auth_username:
|
||||
LOG.warning(f"Basic authentication enabled with username '{self.auth_username}'.")
|
||||
|
||||
if self.basic_mode:
|
||||
LOG.info("The frontend is running in basic mode.")
|
||||
LOG.info(f"Authentication enabled with username '{self.auth_username}'.")
|
||||
|
||||
if self.file_logging:
|
||||
log_level_file = getattr(logging, self.log_level_file.upper(), None)
|
||||
log_level_file: int | None = getattr(logging, self.log_level_file.upper(), None)
|
||||
if not isinstance(log_level_file, int):
|
||||
msg = f"Invalid file log level '{self.log_level_file}' specified."
|
||||
raise TypeError(msg)
|
||||
|
||||
loggingPath = Path(self.config_path) / "logs"
|
||||
loggingPath: Path = Path(self.config_path) / "logs"
|
||||
if not loggingPath.exists():
|
||||
loggingPath.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
|
@ -448,15 +396,17 @@ class Config(metaclass=Singleton):
|
|||
|
||||
self.started = time.time()
|
||||
|
||||
logging.getLogger("httpcore").setLevel(logging.INFO)
|
||||
for _tool in ("httpx", "urllib3.connectionpool", "apprise"):
|
||||
logging.getLogger(_tool).setLevel(logging.WARNING)
|
||||
_log_levels = (
|
||||
("httpx", logging.WARNING),
|
||||
("urllib3.connectionpool", logging.WARNING),
|
||||
("apprise", logging.WARNING),
|
||||
("httpcore", logging.INFO),
|
||||
)
|
||||
for _tool, _level in _log_levels:
|
||||
logging.getLogger(_tool).setLevel(_level)
|
||||
|
||||
# check env
|
||||
if self.app_env not in ("production", "development"):
|
||||
msg: str = (
|
||||
f"Invalid application environment '{self.app_env}' specified. Must be 'production' or 'development'."
|
||||
)
|
||||
msg: str = f"Invalid app environment '{self.app_env}' specified. Must be 'production' or 'development'."
|
||||
raise ValueError(msg)
|
||||
|
||||
if "dev-master" == self.app_version:
|
||||
|
|
@ -470,7 +420,7 @@ class Config(metaclass=Singleton):
|
|||
if attribute.startswith("_"):
|
||||
continue
|
||||
|
||||
value = getattr(vClass, attribute)
|
||||
value: Any = getattr(vClass, attribute)
|
||||
if not callable(value):
|
||||
attrs[attribute] = value
|
||||
|
||||
|
|
@ -496,23 +446,6 @@ class Config(metaclass=Singleton):
|
|||
"""
|
||||
return "production" == self.app_env
|
||||
|
||||
def get_ytdlp_args(self) -> dict:
|
||||
"""
|
||||
Get the yt-dlp command line options as a dictionary.
|
||||
|
||||
Returns:
|
||||
dict: The yt-dlp command line options.
|
||||
|
||||
Deprecated:
|
||||
Usage of global ytdlp.cli file is deprecated, please use presets instead.
|
||||
|
||||
"""
|
||||
try:
|
||||
return arg_converter(args=self._ytdlp_cli_mutable, level=True)
|
||||
except Exception as e:
|
||||
msg = f"Invalid ytdlp.cli options for yt-dlp. '{e!s}'."
|
||||
raise ValueError(msg) from e
|
||||
|
||||
def frontend(self) -> dict:
|
||||
"""
|
||||
Returns configuration variables relevant to the frontend.
|
||||
|
|
@ -521,23 +454,17 @@ class Config(metaclass=Singleton):
|
|||
dict: A dictionary with the frontend configuration
|
||||
|
||||
"""
|
||||
data = {k: getattr(self, k) for k in self._frontend_vars}
|
||||
|
||||
ytdlp_args = self.get_ytdlp_args()
|
||||
|
||||
# TODO: this doesn't make sense, as each item might have it's own archive file or none at all.
|
||||
if not data.get("keep_archive", False) and ytdlp_args.get("download_archive", None):
|
||||
data["keep_archive"] = True
|
||||
data: dict[str, Any] = {k: getattr(self, k) for k in self._frontend_vars}
|
||||
|
||||
data["ytdlp_version"] = Config._ytdlp_version()
|
||||
return data
|
||||
|
||||
def get_replacers(self) -> dict:
|
||||
def get_replacers(self) -> dict[str, str]:
|
||||
"""
|
||||
Get the variables that can be used in Command options for yt-dlp.
|
||||
|
||||
Returns:
|
||||
dict: The replacer variables.
|
||||
dict[str, str]: The replacer variables.
|
||||
|
||||
"""
|
||||
keys: tuple[str] = ("download_path", "temp_path", "config_path")
|
||||
|
|
|
|||
|
|
@ -129,9 +129,6 @@ async def file_browser(request: Request, config: Config, encoder: Encoder) -> Re
|
|||
Response: The response object.
|
||||
|
||||
"""
|
||||
if not config.browser_enabled:
|
||||
return web.json_response(data={"error": "File browser is disabled."}, status=web.HTTPForbidden.status_code)
|
||||
|
||||
req_path: str = request.match_info.get("path")
|
||||
req_path: str = "/" if not req_path else unquote_plus(req_path)
|
||||
|
||||
|
|
@ -190,9 +187,6 @@ async def path_actions(request: Request, config: Config) -> Response:
|
|||
Response: The response object.
|
||||
|
||||
"""
|
||||
if not config.browser_enabled:
|
||||
return web.json_response(data={"error": "File browser is disabled."}, status=web.HTTPForbidden.status_code)
|
||||
|
||||
if not config.browser_control_enabled:
|
||||
return web.json_response(
|
||||
data={"error": "File browser actions is disabled."}, status=web.HTTPForbidden.status_code
|
||||
|
|
|
|||
|
|
@ -153,13 +153,8 @@ async def conditions_test(request: Request, encoder: Encoder, cache: Cache, conf
|
|||
preset: str = params.get("preset", config.default_preset)
|
||||
key: str = cache.hash(url + str(preset))
|
||||
if not cache.has(key):
|
||||
opts = {}
|
||||
if ytdlp_proxy := config.get_ytdlp_args().get("proxy", None):
|
||||
opts["proxy"] = ytdlp_proxy
|
||||
ytdlp_opts = YTDLPOpts.get_instance().preset(name=preset).add(opts).get_all()
|
||||
|
||||
data = extract_info(
|
||||
config=ytdlp_opts,
|
||||
data: dict = extract_info(
|
||||
config=YTDLPOpts.get_instance().preset(name=preset).get_all(),
|
||||
url=url,
|
||||
debug=False,
|
||||
no_archive=True,
|
||||
|
|
@ -182,7 +177,7 @@ async def conditions_test(request: Request, encoder: Encoder, cache: Cache, conf
|
|||
)
|
||||
|
||||
try:
|
||||
status = match_str(cond, data)
|
||||
status: bool = match_str(cond, data)
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
return web.json_response(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import logging
|
|||
import random
|
||||
import time
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import httpx
|
||||
|
|
@ -14,6 +15,7 @@ from app.library.cache import Cache
|
|||
from app.library.config import Config
|
||||
from app.library.router import route
|
||||
from app.library.Utils import validate_url
|
||||
from app.library.YTDLPOpts import YTDLPOpts
|
||||
|
||||
LOG: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ async def get_thumbnail(request: Request, config: Config) -> Response:
|
|||
Response: The response object.
|
||||
|
||||
"""
|
||||
url = request.query.get("url")
|
||||
url: str | None = request.query.get("url")
|
||||
if not url:
|
||||
return web.json_response(data={"error": "URL is required."}, status=web.HTTPForbidden.status_code)
|
||||
|
||||
|
|
@ -43,15 +45,28 @@ async def get_thumbnail(request: Request, config: Config) -> Response:
|
|||
return web.json_response(data={"error": str(e)}, status=web.HTTPForbidden.status_code)
|
||||
|
||||
try:
|
||||
|
||||
ytdlp_args = config.get_ytdlp_args()
|
||||
opts = {
|
||||
"proxy": ytdlp_args.get("proxy", None),
|
||||
ytdlp_args: dict = YTDLPOpts.get_instance().preset(name=config.default_preset).get_all()
|
||||
opts: dict[str, Any] = {
|
||||
"headers": {
|
||||
"User-Agent": ytdlp_args.get("user_agent", request.headers.get("User-Agent", random_user_agent())),
|
||||
"User-Agent": request.headers.get("User-Agent", ytdlp_args.get("user_agent", random_user_agent())),
|
||||
},
|
||||
}
|
||||
|
||||
if proxy := ytdlp_args.get("proxy"):
|
||||
opts["proxy"] = proxy
|
||||
|
||||
try:
|
||||
from httpx_curl_cffi import AsyncCurlTransport, CurlOpt
|
||||
|
||||
opts["transport"] = AsyncCurlTransport(
|
||||
impersonate="chrome",
|
||||
default_headers=True,
|
||||
curl_options={CurlOpt.FRESH_CONNECT: True},
|
||||
)
|
||||
opts.pop("headers", None)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async with httpx.AsyncClient(**opts) as client:
|
||||
LOG.debug(f"Fetching thumbnail from '{url}'.")
|
||||
response = await client.request(method="GET", url=url, follow_redirects=True)
|
||||
|
|
@ -115,13 +130,16 @@ async def get_background(request: Request, config: Config, cache: Cache) -> Resp
|
|||
},
|
||||
)
|
||||
|
||||
ytdlp_args = config.get_ytdlp_args()
|
||||
opts = {
|
||||
"proxy": ytdlp_args.get("proxy", None),
|
||||
ytdlp_args: dict = YTDLPOpts.get_instance().preset(name=config.default_preset).get_all()
|
||||
opts: dict[str, Any] = {
|
||||
"headers": {
|
||||
"User-Agent": ytdlp_args.get("user_agent", random_user_agent()),
|
||||
"User-Agent": request.headers.get("User-Agent", ytdlp_args.get("user_agent", random_user_agent())),
|
||||
},
|
||||
}
|
||||
|
||||
if proxy := ytdlp_args.get("proxy"):
|
||||
opts["proxy"] = proxy
|
||||
|
||||
try:
|
||||
from httpx_curl_cffi import AsyncCurlTransport, CurlOpt
|
||||
|
||||
|
|
@ -166,7 +184,7 @@ async def get_background(request: Request, config: Config, cache: Cache) -> Resp
|
|||
status=web.HTTPInternalServerError.status_code,
|
||||
)
|
||||
|
||||
data = {
|
||||
data: dict[str, Any] = {
|
||||
"content": response.content,
|
||||
"backend": urlparse(backend).netloc,
|
||||
"headers": {
|
||||
|
|
|
|||
|
|
@ -150,9 +150,6 @@ async def get_info(request: Request, cache: Cache, config: Config) -> Response:
|
|||
}
|
||||
return web.json_response(body=json.dumps(data, indent=4, default=str), status=web.HTTPOk.status_code)
|
||||
|
||||
if ytdlp_proxy := config.get_ytdlp_args().get("proxy", None):
|
||||
opts = opts.add({"proxy": ytdlp_proxy})
|
||||
|
||||
logs: list = []
|
||||
|
||||
ytdlp_opts: dict = {
|
||||
|
|
@ -203,13 +200,14 @@ async def get_info(request: Request, cache: Cache, config: Config) -> Response:
|
|||
"expires": time.time() + 300,
|
||||
}
|
||||
|
||||
is_archived = False
|
||||
if (archive_file := ytdlp_opts.get("download_archive")) and (
|
||||
archive_id := get_archive_id(url=url).get("archive_id")
|
||||
):
|
||||
is_archived: bool = len(archive_read(archive_file, [archive_id])) > 0
|
||||
data["is_archived"] = False
|
||||
|
||||
data["is_archived"] = is_archived
|
||||
archive_file: str | None = ytdlp_opts.get("download_archive")
|
||||
data["archive_file"] = archive_file if archive_file else None
|
||||
|
||||
if archive_file and (archive_id := get_archive_id(url=url).get("archive_id")):
|
||||
data["archive_id"] = archive_id
|
||||
data["is_archived"] = len(archive_read(archive_file, [archive_id])) > 0
|
||||
|
||||
data = OrderedDict(sorted(data.items(), key=lambda item: len(str(item[1]))))
|
||||
|
||||
|
|
|
|||
|
|
@ -209,4 +209,9 @@ testpaths = ["app/tests"]
|
|||
addopts = "-v --tb=short"
|
||||
|
||||
[dependency-groups]
|
||||
dev = ["pytest>=8.4.2", "pytest-asyncio>=1.1.0", "ruff>=0.13.0"]
|
||||
dev = [
|
||||
"debugpy>=1.8.16",
|
||||
"pytest>=8.4.2",
|
||||
"pytest-asyncio>=1.1.0",
|
||||
"ruff>=0.13.0",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@
|
|||
</div>
|
||||
<span class="help is-bold">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>yt-dlp <code>[--match-filters]</code> logic.</span>
|
||||
<span>yt-dlp <code>--match-filters</code> logic with <code>OR</code>, <code>||</code>
|
||||
support.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -211,7 +212,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="help is-bold">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>The url to test the filter against.</span>
|
||||
</span>
|
||||
|
|
@ -226,9 +227,10 @@
|
|||
<input type="text" class="input" id="filter" v-model="form.filter" :disabled="test_data.in_progress"
|
||||
placeholder="availability = 'needs_auth' & channel_id = 'channel_id'" required>
|
||||
</div>
|
||||
<span class="help">
|
||||
<span class="help is-bold">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>The yt-dlp <code>[--match-filters]</code> filter logic.</span><br>
|
||||
<span>yt-dlp <code>--match-filters</code> logic with <code>OR</code>, <code>||</code>
|
||||
support.</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@
|
|||
<span class="icon"><i class="fa-solid fa-trash-can" /></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control is-expanded" v-if="item.url && !config.app.basic_mode">
|
||||
<div class="control is-expanded" v-if="item.url">
|
||||
<Dropdown icons="fa-solid fa-cogs" @open_state="(s: boolean) => table_container = !s"
|
||||
:button_classes="'is-small'" label="Actions">
|
||||
<template v-if="'finished' === item.status && item.filename">
|
||||
|
|
@ -347,7 +347,7 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div class="column" v-if="!config.app.basic_mode">
|
||||
<div class="column">
|
||||
<Dropdown icons="fa-solid fa-cogs" label="Actions">
|
||||
<template v-if="'finished' === item.status && item.filename">
|
||||
<NuxtLink @click="playVideo(item)" class="dropdown-item">
|
||||
|
|
@ -366,14 +366,12 @@
|
|||
<hr class="dropdown-divider" />
|
||||
</template>
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)"
|
||||
v-if="!config.app.basic_mode">
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>yt-dlp Information</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)"
|
||||
v-if="!config.app.basic_mode">
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)">
|
||||
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
||||
<span>Local Information</span>
|
||||
</NuxtLink>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-4-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||
<div class="column is-4-tablet is-12-mobile">
|
||||
<div class="field has-addons">
|
||||
<div class="control" @click="show_description = !show_description">
|
||||
<label class="button is-static">
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile" v-if="!config.app.basic_mode">
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="field has-addons" v-tooltip="'Folder relative to ' + config.app.download_path">
|
||||
<div class="control">
|
||||
<label class="button is-static">
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column" v-if="!config.app.basic_mode">
|
||||
<div class="column">
|
||||
<button type="button" class="button is-info" @click="showAdvanced = !showAdvanced"
|
||||
:class="{ 'is-loading': !socket.isConnected }" :disabled="!socket.isConnected">
|
||||
<span class="icon"><i class="fa-solid fa-cog" /></span>
|
||||
|
|
@ -79,7 +79,7 @@
|
|||
</div>
|
||||
|
||||
<div class="column is-12"
|
||||
v-if="show_description && !config.app.basic_mode && !hasFormatInConfig && get_preset(form.preset)?.description">
|
||||
v-if="show_description && !hasFormatInConfig && get_preset(form.preset)?.description">
|
||||
<div class="is-overflow-auto" style="max-height: 150px;">
|
||||
<div class="is-ellipsis is-clickable" @click="expand_description">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span> {{ get_preset(form.preset)?.description }}
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="columns is-multiline is-mobile" v-if="showAdvanced && !config.app.basic_mode">
|
||||
<div class="columns is-multiline is-mobile" v-if="showAdvanced">
|
||||
<div class="column is-3-tablet is-12-mobile">
|
||||
<DLInput id="force_download" type="bool" label="Force download"
|
||||
v-model="dlFields['--no-download-archive']" icon="fa-solid fa-download"
|
||||
|
|
@ -137,7 +137,7 @@
|
|||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<DLInput id="ytdlpCookies" type="text" label="Cookies for yt-dlp" v-model="form.cookies"
|
||||
<DLInput id="ytdlpCookies" type="text" label="Cookies" v-model="form.cookies"
|
||||
icon="fa-solid fa-cookie" :disabled="!socket.isConnected || addInProgress"
|
||||
:placeholder="getDefault('cookies', '')">
|
||||
<template #help>
|
||||
|
|
@ -290,37 +290,35 @@ const addDownload = async () => {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (false === config.app.basic_mode) {
|
||||
if (dlFields.value && Object.keys(dlFields.value).length > 0) {
|
||||
const joined = []
|
||||
for (const [key, value] of Object.entries(dlFields.value)) {
|
||||
if (false === is_valid(key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ([undefined, null, '', false].includes(value as any)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const keyRegex = new RegExp(`(^|\\s)${key}(\\s|$)`);
|
||||
if (form_cli && keyRegex.test(form_cli)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
joined.push(true === value ? `${key}` : `${key} ${value}`)
|
||||
if (dlFields.value && Object.keys(dlFields.value).length > 0) {
|
||||
const joined = []
|
||||
for (const [key, value] of Object.entries(dlFields.value)) {
|
||||
if (false === is_valid(key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (joined.length > 0) {
|
||||
form_cli = form_cli ? `${form_cli} ${joined.join(' ')}` : joined.join(' ')
|
||||
if ([undefined, null, '', false].includes(value as any)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const keyRegex = new RegExp(`(^|\\s)${key}(\\s|$)`);
|
||||
if (form_cli && keyRegex.test(form_cli)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
joined.push(true === value ? `${key}` : `${key} ${value}`)
|
||||
}
|
||||
|
||||
if (form_cli && form_cli.trim()) {
|
||||
const options = await convertOptions(form_cli)
|
||||
if (null === options) {
|
||||
return
|
||||
}
|
||||
if (joined.length > 0) {
|
||||
form_cli = form_cli ? `${form_cli} ${joined.join(' ')}` : joined.join(' ')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (form_cli && form_cli.trim()) {
|
||||
const options = await convertOptions(form_cli)
|
||||
if (null === options) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -333,12 +331,12 @@ const addDownload = async () => {
|
|||
|
||||
const data = {
|
||||
url: url,
|
||||
preset: config.app.basic_mode ? config.app.default_preset : form.value.preset,
|
||||
folder: config.app.basic_mode ? null : form.value.folder,
|
||||
template: config.app.basic_mode ? null : form.value.template,
|
||||
cookies: config.app.basic_mode ? '' : form.value.cookies,
|
||||
cli: config.app.basic_mode ? null : form_cli,
|
||||
auto_start: config.app.basic_mode ? true : auto_start.value
|
||||
preset: form.value.preset || config.app.default_preset,
|
||||
folder: form.value.folder,
|
||||
template: form.value.template,
|
||||
cookies: form.value.cookies,
|
||||
cli: form_cli,
|
||||
auto_start: auto_start.value
|
||||
} as item_request
|
||||
|
||||
if (form.value?.extras && Object.keys(form.value.extras).length > 0) {
|
||||
|
|
|
|||
|
|
@ -147,19 +147,17 @@
|
|||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<template v-if="!config.app.basic_mode">
|
||||
<hr class="dropdown-divider" />
|
||||
<hr class="dropdown-divider" />
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>yt-dlp Information</span>
|
||||
</NuxtLink>
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>yt-dlp Information</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)">
|
||||
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
||||
<span>Local Information</span>
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)">
|
||||
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
||||
<span>Local Information</span>
|
||||
</NuxtLink>
|
||||
</Dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -272,17 +270,15 @@
|
|||
<span class="icon"><i class="fa-solid fa-play" /></span>
|
||||
<span>Play video</span>
|
||||
</NuxtLink>
|
||||
<hr class="dropdown-divider" v-if="!config.app.basic_mode" />
|
||||
<hr class="dropdown-divider" />
|
||||
</template>
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)"
|
||||
v-if="!config.app.basic_mode">
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getInfo', item.url, item.preset, item.cli)">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>yt-dlp Information</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)"
|
||||
v-if="!config.app.basic_mode">
|
||||
<NuxtLink class="dropdown-item" @click="emitter('getItemInfo', item._id)">
|
||||
<span class="icon"><i class="fa-solid fa-info-circle" /></span>
|
||||
<span>Local Information</span>
|
||||
</NuxtLink>
|
||||
|
|
|
|||
|
|
@ -24,9 +24,8 @@
|
|||
</div>
|
||||
|
||||
<div class="navbar-menu is-unselectable" :class="{ 'is-active': showMenu }">
|
||||
<div class="navbar-start" v-if="!config.app.basic_mode">
|
||||
<NuxtLink class="navbar-item" to="/browser" @click.prevent="(e: MouseEvent) => changeRoute(e)"
|
||||
v-if="config.app.browser_enabled">
|
||||
<div class="navbar-start">
|
||||
<NuxtLink class="navbar-item" to="/browser" @click.prevent="(e: MouseEvent) => changeRoute(e)">
|
||||
<span class="icon"><i class="fa-solid fa-folder-tree" /></span>
|
||||
<span>Files</span>
|
||||
</NuxtLink>
|
||||
|
|
@ -55,7 +54,7 @@
|
|||
|
||||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item has-dropdown" v-if="!config.app.basic_mode">
|
||||
<div class="navbar-item has-dropdown">
|
||||
<a class="navbar-link" @click="(e: MouseEvent) => openMenu(e)">
|
||||
<span class="icon"><i class="fas fa-tools" /></span>
|
||||
<span>Other</span>
|
||||
|
|
|
|||
|
|
@ -49,9 +49,6 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-hidden-mobile">
|
||||
<span class="subtitle">Files Browser</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -278,14 +275,6 @@ watch(masterSelectAll, v => {
|
|||
}
|
||||
})
|
||||
|
||||
watch(() => config.app.basic_mode, async v => {
|
||||
if (!config.isLoaded() || !v) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
}, { immediate: true })
|
||||
|
||||
|
||||
const filteredItems = computed<FileItem[]>(() => {
|
||||
if (!search.value) {
|
||||
return sortedItems(items.value)
|
||||
|
|
@ -335,20 +324,6 @@ const sortedItems = (items: FileItem[]): FileItem[] => {
|
|||
const model_item = ref<any>()
|
||||
const closeModel = (): void => { model_item.value = null }
|
||||
|
||||
watch(() => config.app.basic_mode, async () => {
|
||||
if (!config.app.basic_mode) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
})
|
||||
|
||||
watch(() => config.app.browser_enabled, async () => {
|
||||
if (config.app.browser_enabled) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
})
|
||||
|
||||
watch(() => socket.isConnected, async () => {
|
||||
if (socket.isConnected && initialLoad.value) {
|
||||
await reloadContent(path.value, true)
|
||||
|
|
|
|||
|
|
@ -104,8 +104,10 @@
|
|||
<div class="column is-12" v-if="items && items.length > 0 && !toggleForm">
|
||||
<Message message_class="has-background-info-90 has-text-dark" title="Tips" icon="fas fa-info-circle">
|
||||
<ul>
|
||||
<li>The filtering rely on yt-dlp <code>--match-filter</code> logic, whatever works there works here as well
|
||||
and uses the same logic boolean and operators.</li>
|
||||
<li>Filtering is based on yt-dlp’s <code>--match-filter</code> logic. Any expression that works with yt-dlp
|
||||
will also work here, including the same boolean operators. We added extended support for the <code>OR</code>
|
||||
( <code>||</code> ) operator, which yt-dlp does not natively support. This allows you to combine multiple
|
||||
conditions more flexibly.</li>
|
||||
<li>
|
||||
The primary use case for this feature is to apply custom cli arguments to specific returned info.
|
||||
</li>
|
||||
|
|
@ -116,7 +118,8 @@
|
|||
</li>
|
||||
<li>
|
||||
The data which the filter is applied on is the same data that yt-dlp returns, simply, click on the
|
||||
information button, and check the data to craft your filter.
|
||||
information button, and check the data to craft your filter. You will get instant feedback if the
|
||||
filter matches or not.
|
||||
</li>
|
||||
</ul>
|
||||
</Message>
|
||||
|
|
@ -130,7 +133,6 @@ import type { ConditionItem, ImportedConditionItem } from '~/types/conditions'
|
|||
type ConditionItemWithUI = ConditionItem & { raw?: boolean }
|
||||
|
||||
const toast = useNotification()
|
||||
const config = useConfigStore()
|
||||
const socket = useSocketStore()
|
||||
const box = useConfirm()
|
||||
const isMobile = useMediaQuery({ maxWidth: 1024 })
|
||||
|
|
@ -144,13 +146,6 @@ const initialLoad = ref(true)
|
|||
const addInProgress = ref(false)
|
||||
const remove_keys = ['in_progress', 'raw']
|
||||
|
||||
watch(() => config.app.basic_mode, async v => {
|
||||
if (!config.isLoaded() || !v) {
|
||||
return
|
||||
}
|
||||
await navigateTo("/")
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => socket.isConnected, async () => {
|
||||
if (socket.isConnected && initialLoad.value) {
|
||||
await reloadContent(true)
|
||||
|
|
|
|||
|
|
@ -81,13 +81,6 @@ watch(() => isLoading.value, async value => {
|
|||
focusInput()
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => config.app.basic_mode, async () => {
|
||||
if (!config.isLoaded() || !config.app.basic_mode) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => config.app.console_enabled, async () => {
|
||||
if (config.app.console_enabled) {
|
||||
return
|
||||
|
|
@ -108,7 +101,7 @@ const runCommand = async () => {
|
|||
return
|
||||
}
|
||||
|
||||
if (config.app.basic_mode || !config.app.console_enabled) {
|
||||
if (true !== config.app.console_enabled) {
|
||||
await navigateTo('/')
|
||||
toast.error('Console is disabled in the configuration. Please enable it to use this feature.')
|
||||
return
|
||||
|
|
@ -183,13 +176,6 @@ const writer = (s: string) => {
|
|||
|
||||
const loader = () => isLoading.value = false
|
||||
|
||||
watch(() => config.app.basic_mode, async () => {
|
||||
if (!config.app.basic_mode) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
document.addEventListener('resize', handle_event);
|
||||
focusInput()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
</button>
|
||||
</p>
|
||||
|
||||
<p class="control" v-if="!config.app.basic_mode && false === config.app.basic_mode">
|
||||
<p class="control">
|
||||
<button class="button is-warning" @click="pauseDownload" v-if="false === config.paused">
|
||||
<span class="icon"><i class="fas fa-pause" /></span>
|
||||
<span v-if="!isMobile">Pause</span>
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
</button>
|
||||
</p>
|
||||
|
||||
<p class="control" v-if="!config.app.basic_mode && false === config.app.basic_mode">
|
||||
<p class="control">
|
||||
<button class="button is-primary has-tooltip-bottom" @click="config.showForm = !config.showForm">
|
||||
<span class="icon"><i class="fa-solid fa-plus" /></span>
|
||||
<span v-if="!isMobile">New Download</span>
|
||||
|
|
@ -62,54 +62,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="config.is_loaded" class="columns is-multiline">
|
||||
<div class="column is-12">
|
||||
<DeprecatedNotice :version="config.app.app_version" title="Deprecation Notice" tone="warning"
|
||||
icon="fas fa-exclamation-triangle fa-fade fa-spin-10">
|
||||
<p>
|
||||
The following environment variables and features are deprecated and will be removed in
|
||||
<strong class="has-text-danger">v0.10.x</strong>
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
The following ENVs <strong>YTP_KEEP_ARCHIVE</strong> and <strong>YTP_SOCKET_TIMEOUT</strong> will be
|
||||
removed.
|
||||
Their behavior will be part of the <strong>default presets</strong>. To keep your current behavior
|
||||
<strong>and avoid re-downloading</strong>, please add the following <strong>Command options for
|
||||
yt-dlp</strong> to your presets:
|
||||
<code>--socket-timeout 30 --download-archive %(config_path)s/archive.log</code>
|
||||
</li>
|
||||
<li>
|
||||
The global yt-dlp config file <strong>/config/ytdlp.cli</strong> will be removed. Please migrate to
|
||||
presets.
|
||||
</li>
|
||||
<li>The <strong>archive.manual.log</strong> feature has been removed.</li>
|
||||
</ul>
|
||||
<p>
|
||||
These changes help reduce confusion from multiple sources of truth. Going forward, <strong>presets</strong>
|
||||
and the <strong>Command options for yt-dlp</strong> will be the single source of truth.
|
||||
</p>
|
||||
<p>
|
||||
Notable changes in <strong>v0.10.x</strong>:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
The file browser feature is going to be enabled by default. and the associated ENV
|
||||
<strong>YTP_BROWSER_ENABLED</strong> will be removed, <strong>YTP_BROWSER_CONTROL_ENABLED</strong> will
|
||||
remain and
|
||||
will default to <strong>false</strong>.
|
||||
</li>
|
||||
<li>
|
||||
The <strong>Basic mode</strong> (which limited the interface to just the new download form) along it's
|
||||
associated ENV <strong>YTP_BASIC_MODE</strong> is being removed. Everything except what is available
|
||||
behind configurable flag will become part of the standard interface.
|
||||
</li>
|
||||
</ul>
|
||||
</DeprecatedNotice>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NewDownload v-if="config.showForm || config.app.basic_mode"
|
||||
<NewDownload v-if="config.showForm"
|
||||
@getInfo="(url: string, preset: string = '', cli: string = '') => view_info(url, false, preset, cli)"
|
||||
:item="item_form" @clear_form="item_form = {}" />
|
||||
<Queue @getInfo="(url: string, preset: string = '', cli: string = '') => view_info(url, false, preset, cli)"
|
||||
|
|
@ -128,7 +81,6 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import DeprecatedNotice from '~/components/DeprecatedNotice.vue'
|
||||
import type { item_request } from '~/types/item'
|
||||
import type { StoreItem } from '~/types/store'
|
||||
|
||||
|
|
|
|||
|
|
@ -155,13 +155,6 @@ watch(toggleFilter, () => {
|
|||
}
|
||||
});
|
||||
|
||||
watch(() => config.app.basic_mode, async v => {
|
||||
if (!config.isLoaded() || !v) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => config.app.file_logging, async v => {
|
||||
if (v) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -221,7 +221,6 @@ import { useStorage } from '@vueuse/core'
|
|||
import type { notification, notificationImport } from '~/types/notification'
|
||||
|
||||
const toast = useNotification()
|
||||
const config = useConfigStore()
|
||||
const socket = useSocketStore()
|
||||
const box = useConfirm()
|
||||
const display_style = useStorage<string>("tasks_display_style", "cards")
|
||||
|
|
@ -244,13 +243,6 @@ const isLoading = ref(false)
|
|||
const initialLoad = ref(true)
|
||||
const addInProgress = ref(false)
|
||||
|
||||
watch(() => config.app.basic_mode, async v => {
|
||||
if (!config.isLoaded() || !v) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => socket.isConnected, async () => {
|
||||
if (socket.isConnected && initialLoad.value) {
|
||||
await reloadContent(true)
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="is-hidden-mobile">
|
||||
<span class="subtitle">Custom presets. The presets are simply pre-defined yt-dlp settings
|
||||
that you want to apply to given download.</span>
|
||||
<span class="subtitle">Presets are pre-defined command options for yt-dlp that you want to apply to given
|
||||
download.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -192,16 +192,7 @@
|
|||
<div class="column is-12">
|
||||
<Message message_class="has-background-info-90 has-text-dark" title="Tips" icon="fas fa-info-circle">
|
||||
<ul>
|
||||
<li>
|
||||
When you export preset, it doesn't include <code>Cookies</code> field for security reasons.
|
||||
</li>
|
||||
<li>
|
||||
If you have created a global <code>config/ytdlp.cli</code> file, it will be appended to your exported
|
||||
preset
|
||||
<code><i class="fa-solid fa-terminal" /> Command options for yt-dlp</code> field for better
|
||||
compatibility
|
||||
and completeness.
|
||||
</li>
|
||||
<li>When you export preset, it doesn't include <code>Cookies</code> field for security reasons.</li>
|
||||
</ul>
|
||||
</Message>
|
||||
</div>
|
||||
|
|
@ -233,13 +224,6 @@ const remove_keys = ['raw', 'toggle_description']
|
|||
|
||||
const presetsNoDefault = computed(() => presets.value.filter((t) => !t.default))
|
||||
|
||||
watch(() => config.app.basic_mode, async v => {
|
||||
if (!config.isLoaded() || !v) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => socket.isConnected, async () => {
|
||||
if (socket.isConnected && initialLoad.value) {
|
||||
await reloadContent(true)
|
||||
|
|
@ -391,11 +375,6 @@ const exportItem = (item: Preset) => {
|
|||
}
|
||||
}
|
||||
|
||||
if (config?.app?.ytdlp_cli) {
|
||||
const val = `# exported from ytdlp.cli #\n${config.app.ytdlp_cli}\n# exported from ytdlp.cli #\n`
|
||||
userData.cli = userData.cli ? val + '\n' + userData.cli : val
|
||||
}
|
||||
|
||||
userData['_type'] = 'preset'
|
||||
userData['_version'] = '2.5'
|
||||
|
||||
|
|
|
|||
|
|
@ -444,13 +444,6 @@ watch(masterSelectAll, value => {
|
|||
}
|
||||
})
|
||||
|
||||
watch(() => config.app.basic_mode, async v => {
|
||||
if (!config.isLoaded() || !v) {
|
||||
return
|
||||
}
|
||||
await navigateTo('/')
|
||||
}, { immediate: true })
|
||||
|
||||
watch(() => socket.isConnected, async () => {
|
||||
if (socket.isConnected && initialLoad.value) {
|
||||
socket.on('item_status', statusHandler)
|
||||
|
|
|
|||
|
|
@ -11,13 +11,10 @@ export const useConfigStore = defineStore('config', () => {
|
|||
output_template: '',
|
||||
ytdlp_version: '',
|
||||
max_workers: 1,
|
||||
basic_mode: true,
|
||||
default_preset: 'default',
|
||||
instance_title: null,
|
||||
console_enabled: false,
|
||||
browser_enabled: false,
|
||||
browser_control_enabled: false,
|
||||
ytdlp_cli: '',
|
||||
file_logging: false,
|
||||
is_native: false,
|
||||
app_version: '',
|
||||
|
|
@ -30,7 +27,7 @@ export const useConfigStore = defineStore('config', () => {
|
|||
presets: [
|
||||
{
|
||||
'name': 'default',
|
||||
'description': 'Default preset for downloads',
|
||||
'description': 'Default preset',
|
||||
'folder': '',
|
||||
'template': '',
|
||||
'cookies': '',
|
||||
|
|
|
|||
6
ui/app/types/config.d.ts
vendored
6
ui/app/types/config.d.ts
vendored
|
|
@ -14,20 +14,14 @@ type AppConfig = {
|
|||
ytdlp_version: string
|
||||
/** Maximum number of concurrent download workers */
|
||||
max_workers: number
|
||||
/** Indicates if the app is in basic mode, which may limit some features */
|
||||
basic_mode: boolean
|
||||
/** Default preset name, e.g. "default" */
|
||||
default_preset: string
|
||||
/** Instance title for the app, null if not set */
|
||||
instance_title: string | null
|
||||
/** Indicates if the console is enabled */
|
||||
console_enabled: boolean
|
||||
/** Indicates if the file browser is enabled */
|
||||
browser_enabled: boolean
|
||||
/** Indicates if the file browser control is enabled */
|
||||
browser_control_enabled: boolean
|
||||
/** Command options for yt-dlp */
|
||||
ytdlp_cli: string
|
||||
/** Indicates if file logging is enabled */
|
||||
file_logging: boolean
|
||||
/** Indicates if the app is running in a native environment (e.g., Electron) */
|
||||
|
|
|
|||
2
uv.lock
2
uv.lock
|
|
@ -1513,6 +1513,7 @@ installer = [
|
|||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "debugpy" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "ruff" },
|
||||
|
|
@ -1556,6 +1557,7 @@ provides-extras = ["installer"]
|
|||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "debugpy", specifier = ">=1.8.16" },
|
||||
{ name = "pytest", specifier = ">=8.4.2" },
|
||||
{ name = "pytest-asyncio", specifier = ">=1.1.0" },
|
||||
{ name = "ruff", specifier = ">=0.13.0" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue