Make it possible to pin specific yt-dlp version
This commit is contained in:
parent
d4e837a805
commit
96149f5e2d
8 changed files with 101 additions and 32 deletions
|
|
@ -41,7 +41,7 @@ ENV PYTHONDONTWRITEBYTECODE=1
|
|||
ENV PYTHONFAULTHANDLER=1
|
||||
|
||||
RUN mkdir /config /downloads && ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime && echo ${TZ} > /etc/timezone && \
|
||||
apk add --update --no-cache bash mkvtoolnix patch aria2 coreutils curl shadow sqlite tzdata libmagic ffmpeg rtmpdump fribidi && \
|
||||
apk add --update --no-cache bash mkvtoolnix patch aria2 coreutils curl shadow sqlite tzdata libmagic ffmpeg rtmpdump fribidi git && \
|
||||
useradd -u ${USER_ID:-1000} -U -d /app -s /bin/bash app && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
|
|
|
|||
10
FAQ.md
10
FAQ.md
|
|
@ -39,3 +39,13 @@ whenever the link includes a playlist id.
|
|||
|
||||
> [!NOTE]
|
||||
> You can also do the same via advanced options `Command options for yt-dlp` field, but presets are more convenient.
|
||||
|
||||
# Install specific yt-dlp version?
|
||||
|
||||
You can force specific version of `yt-dlp` by setting the `YTP_YTDLP_VERSION` environment variable for example
|
||||
|
||||
```env
|
||||
YTP_YTDLP_VERSION=2025.07.21 or master or nightly
|
||||
|
||||
Then restart the container to apply the changes.
|
||||
|
||||
|
|
|
|||
|
|
@ -291,7 +291,6 @@ Certain configuration values can be set via environment variables, using the `-e
|
|||
| YTP_TEMP_PATH | Path to where tmp files are stored. | `/tmp` |
|
||||
| YTP_TEMP_KEEP | Whether to keep the Individual video temp directory or remove it | `false` |
|
||||
| YTP_KEEP_ARCHIVE | Keep history of downloaded videos | `true` |
|
||||
| YTP_YTDL_DEBUG | Whether to turn debug logging for the internal `yt-dlp` package | `false` |
|
||||
| YTP_HOST | Which IP address to bind to | `0.0.0.0` |
|
||||
| YTP_PORT | Which port to bind to | `8081` |
|
||||
| YTP_LOG_LEVEL | Log level | `info` |
|
||||
|
|
@ -312,6 +311,8 @@ Certain configuration values can be set via environment variables, using the `-e
|
|||
| YTP_BROWSER_ENABLED | Whether to enable the file browser | `false` |
|
||||
| YTP_BROWSER_CONTROL_ENABLED | Whether to enable the file browser actions | `false` |
|
||||
| YTP_YTDLP_AUTO_UPDATE | Whether to enable the auto update for yt-dlp | `true` |
|
||||
| YTP_YTDLP_DEBUG | Whether to turn debug logging for the internal `yt-dlp` package | `false` |
|
||||
| YTP_YTDLP_VERSION | The version of yt-dlp to use. Defaults to latest version | `empty string` |
|
||||
| YTP_BASE_PATH | Set this if you are serving YTPTube from sub-folder | `/` |
|
||||
| YTP_PREVENT_LIVE_PREMIERE | Prevents the initial youtube premiere stream from being downloaded | `false` |
|
||||
| YTP_TASKS_HANDLER_TIMER | The cron expression for the tasks handler timer | `15 */1 * * *` |
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ class Download:
|
|||
self.info = info
|
||||
self.id = info._id
|
||||
self.debug = bool(config.debug)
|
||||
self.debug_ytdl = bool(config.ytdl_debug)
|
||||
self.debug_ytdl = bool(config.ytdlp_debug)
|
||||
self.cancelled = False
|
||||
self.tmpfilename = None
|
||||
self.status_queue = None
|
||||
|
|
|
|||
|
|
@ -676,7 +676,7 @@ class DownloadQueue(metaclass=Singleton):
|
|||
extract_info,
|
||||
config=yt_conf,
|
||||
url=item.url,
|
||||
debug=bool(self.config.ytdl_debug),
|
||||
debug=bool(self.config.ytdlp_debug),
|
||||
no_archive=False,
|
||||
follow_redirect=True,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -49,10 +49,18 @@ class PackageInstaller:
|
|||
sys.path.insert(0, str(self.user_site))
|
||||
|
||||
def action(self, pkg: str, upgrade: bool = False):
|
||||
current_version = self._get_installed_version(pkg)
|
||||
version: str | None = None
|
||||
if "==" in pkg:
|
||||
pkg, version = pkg.split("==", 1)
|
||||
|
||||
if upgrade and current_version:
|
||||
latest_version = self._get_latest_version(pkg)
|
||||
current_version: str | None = self._get_installed_version(pkg)
|
||||
|
||||
if current_version and version and self.compare_versions(current_version, version):
|
||||
LOG.info(f"'{pkg}' is already installed with the specified version ({version}). Skipping installation.")
|
||||
return
|
||||
|
||||
if upgrade and current_version and not version:
|
||||
latest_version: str | None = self._get_latest_version(pkg)
|
||||
if latest_version and parse_version(current_version) >= parse_version(latest_version):
|
||||
LOG.info(f"'{pkg}' is already the latest version ({current_version}). Skipping upgrade.")
|
||||
return
|
||||
|
|
@ -62,7 +70,7 @@ class PackageInstaller:
|
|||
else:
|
||||
LOG.info(f"'{pkg}' is not installed. Installing...")
|
||||
|
||||
self._install_pkg(pkg)
|
||||
self._install_pkg(pkg, version=version)
|
||||
|
||||
def _get_installed_version(self, pkg: str) -> str | None:
|
||||
try:
|
||||
|
|
@ -82,23 +90,37 @@ class PackageInstaller:
|
|||
LOG.warning(f"Error while querying PyPI for '{pkg}': {e}")
|
||||
return None
|
||||
|
||||
def _install_pkg(self, pkg: str):
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--upgrade",
|
||||
"--disable-pip-version-check",
|
||||
"--target",
|
||||
str(self.user_site),
|
||||
"--no-warn-script-location",
|
||||
pkg,
|
||||
],
|
||||
check=True,
|
||||
creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0,
|
||||
)
|
||||
def _install_pkg(self, pkg: str, version: str | None = None):
|
||||
cmd: list[str] = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"--no-warn-script-location",
|
||||
"--upgrade",
|
||||
"--target",
|
||||
str(self.user_site),
|
||||
]
|
||||
|
||||
if version:
|
||||
if "nightly" == version and pkg == "yt_dlp":
|
||||
cmd.extend(["--pre", "yt-dlp[default]"])
|
||||
elif "master" == version and pkg == "yt_dlp":
|
||||
cmd.append("git+https://github.com/yt-dlp/yt-dlp.git@master")
|
||||
else:
|
||||
cmd.append(version if str(version).startswith("git+") else f"{pkg}=={version}")
|
||||
else:
|
||||
cmd.extend(["--disable-pip-version-check", pkg])
|
||||
|
||||
try:
|
||||
subprocess.run(
|
||||
cmd,
|
||||
check=True,
|
||||
creationflags=subprocess.CREATE_NO_WINDOW if os.name == "nt" else 0,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
LOG.error(f"Failed to install package '{pkg}'. Error: {e}")
|
||||
raise
|
||||
|
||||
def check(self, pkgs: Packages):
|
||||
"""
|
||||
|
|
@ -118,3 +140,28 @@ class PackageInstaller:
|
|||
except Exception as e:
|
||||
LOG.error(f"Failed to install or upgrade package '{package}'. Error message: {e!s}")
|
||||
LOG.exception(e)
|
||||
|
||||
def compare_versions(self, current: str, target: str) -> bool:
|
||||
"""
|
||||
Compare versions, handling yt-dlp format where pip uses 2025.7.21 but actual is 2025.07.21
|
||||
Returns True if versions match, False otherwise
|
||||
"""
|
||||
if current == target:
|
||||
return True
|
||||
|
||||
# Handle yt-dlp version format differences
|
||||
current_parts = current.split(".")
|
||||
target_parts = target.split(".")
|
||||
|
||||
if len(current_parts) == len(target_parts):
|
||||
# Normalize parts by zero-padding single digits
|
||||
current_normalized: list[str] = [
|
||||
part.zfill(2) if part.isdigit() and len(part) == 1 else part for part in current_parts
|
||||
]
|
||||
target_normalized: list[str] = [
|
||||
part.zfill(2) if part.isdigit() and len(part) == 1 else part for part in target_parts
|
||||
]
|
||||
if current_normalized == target_normalized:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -46,9 +46,6 @@ class Config:
|
|||
keep_archive: bool = True
|
||||
"""Keep the download archive file."""
|
||||
|
||||
ytdl_debug: bool = False
|
||||
"""Enable yt-dlp debugging."""
|
||||
|
||||
host: str = "0.0.0.0"
|
||||
"""The host to bind the server to."""
|
||||
|
||||
|
|
@ -178,6 +175,12 @@ class Config:
|
|||
_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."""
|
||||
|
||||
ytdlp_debug: bool = False
|
||||
"""Enable yt-dlp debugging."""
|
||||
|
||||
is_native: bool = False
|
||||
"Is the application running in webview."
|
||||
|
||||
|
|
@ -228,7 +231,7 @@ class Config:
|
|||
|
||||
_boolean_vars: tuple = (
|
||||
"keep_archive",
|
||||
"ytdl_debug",
|
||||
"ytdlp_debug",
|
||||
"debug",
|
||||
"temp_keep",
|
||||
"access_log",
|
||||
|
|
@ -531,11 +534,11 @@ class Config:
|
|||
if not data.get("keep_archive", False) and ytdlp_args.get("download_archive", None):
|
||||
data["keep_archive"] = True
|
||||
|
||||
data["ytdlp_version"] = Config.ytdlp_version()
|
||||
data["ytdlp_version"] = Config._ytdlp_version()
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def ytdlp_version():
|
||||
def _ytdlp_version():
|
||||
try:
|
||||
from yt_dlp.version import __version__ as YTDLP_VERSION
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ class Upgrader:
|
|||
config_path: Path = Path(__file__).parent.parent / "var" / "config"
|
||||
if env_path := os.environ.get("YTP_CONFIG_PATH", None):
|
||||
config_path = Path(env_path)
|
||||
else:
|
||||
os.environ.update({"YTP_CONFIG_PATH": str(config_path)})
|
||||
|
||||
if config_path.exists():
|
||||
envFile: Path = config_path / ".env"
|
||||
|
|
@ -33,11 +35,17 @@ class Upgrader:
|
|||
pkg_installer = PackageInstaller()
|
||||
|
||||
ytdlp_auto_update: bool = os.environ.get("YTP_YTDLP_AUTO_UPDATE", "true").strip().lower() == "true"
|
||||
ytdlp_version: str | None = os.environ.get("YTP_YTDLP_VERSION", "").strip() or None
|
||||
|
||||
if ytdlp_auto_update:
|
||||
try:
|
||||
LOG.info("Checking for newer versions of 'yt-dlp' package.")
|
||||
pkg_installer.action(pkg="yt_dlp", upgrade=True)
|
||||
pkg_name = "yt_dlp"
|
||||
if ytdlp_version:
|
||||
LOG.info(f"Using specified version '{ytdlp_version}' for '{pkg_name}'.")
|
||||
pkg_name += f"=={ytdlp_version}"
|
||||
|
||||
pkg_installer.action(pkg=pkg_name, upgrade=True)
|
||||
|
||||
from yt_dlp.version import __version__ as YTDLP_VERSION
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue