metube/YTDLP_PRIVATE_BUILD.md

127 lines
3.9 KiB
Markdown

# Private yt-dlp build
MeTube uses [yt-dlp](https://github.com/yt-dlp/yt-dlp) as its core downloader,
pinned to the official PyPI release via `pyproject.toml` / `uv.lock`.
There is currently an extractor bug in the official release that is not yet
fixed upstream. Until it is, the Docker image bakes a wheel built from a local,
patched yt-dlp checkout (`~/Dev/github/yt-dlp`) instead of the pinned release.
This document explains how the override works, how to build/update the private
wheel, and — most importantly — **how to switch back to the official build once
the upstream fix lands**.
---
## How the override works
1. `./build-ytdlp.sh` builds a wheel from a local yt-dlp checkout and stages it
as `vendor/yt_dlp-*.whl` (the versioned filename is required by `uv`).
2. The `Dockerfile`, right after `uv sync`, force-reinstalls that wheel over the
version pinned in `uv.lock`:
```dockerfile
COPY vendor ./vendor
RUN if [ -n "$(ls /app/vendor/yt_dlp-*.whl 2>/dev/null || true)" ]; then \
uv pip install --python /usr/local/bin/python --force-reinstall --no-deps /app/vendor/yt_dlp-*.whl; \
else \
echo "No private yt-dlp wheel in vendor/ - using pinned yt-dlp from uv.lock"; \
fi
```
3. `publish.sh` runs `./build-ytdlp.sh` before every build, so published images
always carry the patched build (and the publish aborts loudly if the local
checkout is missing).
4. The wheel is **git-ignored** (`/vendor/yt_dlp-*.whl`). `vendor/.gitkeep` and
`vendor/.gitignore` are committed so the directory always exists for
`COPY vendor ./vendor`.
## Building / updating the private wheel
```sh
./build-ytdlp.sh # builds from ~/Dev/github/yt-dlp
YTDLP_REPO=/path/to/yt-dlp ./build-ytdlp.sh # custom checkout path
```
Rebuild whenever the local checkout changes, then rebuild the image:
```sh
docker compose up -d --build --force-recreate
```
The script fails with a clear error if the configured checkout is missing.
---
## Switching back to the official build (when upstream fixes it)
### 1. Remove the Dockerfile override
Delete the `COPY vendor` / `RUN uv pip install` block from `Dockerfile`
(added in the "Private yt-dlp build" section, right after `uv sync`).
### 2. Remove the build step from `publish.sh`
Delete the block:
```sh
# Bake the private yt-dlp build (see build-ytdlp.sh) into the image until the
# upstream fix lands. Fails loudly if the local checkout is missing.
./build-ytdlp.sh
```
### 3. Update the pinned version
Bump `yt-dlp` to the fixed release and re-resolve the lockfile:
```sh
uv add "yt-dlp[default,curl-cffi]@latest" # or pin the exact fixed version
uv lock
```
Commit the updated `pyproject.toml` and `uv.lock`.
### 4. Clean up the override artifacts
```sh
rm build-ytdlp.sh # no longer needed
rm -f vendor/yt_dlp-*.whl # delete any staged wheel
```
You may also remove the `vendor/` directory (`.gitkeep`, `.gitignore`) and the
`/vendor/yt_dlp-*.whl` entry from `.gitignore`.
### 5. Update the docs
Remove the "Private yt-dlp build" section from `DEPLOY.md` (and delete this file
if you no longer want the guide around).
### 6. Rebuild and verify
```sh
docker compose up -d --build --force-recreate
```
Confirm the installed version is the official release:
```sh
docker compose exec metube python3 -c "import yt_dlp; print(yt_dlp.version.__version__)"
```
### 7. Publish
When you next publish, `./publish.sh` will no longer stage a private wheel and
the image will use the official yt-dlp release.
---
## Verifying which build is running
The `/version` endpoint reports `METUBE_VERSION`; the yt-dlp version is logged
by the app at startup. To check inside a running container:
```sh
docker compose exec metube python3 -c \
"import yt_dlp, yt_dlp.extractor.spankbang as s; import inspect; print(yt_dlp.version.__version__, 'patched:', 'spankbang' in inspect.getsource(s).lower())"
```