Prep the Download Center epic (Phase 1 + editor + share) for prod/self-host:
- Dockerfile: create /downloads owned by appuser so a named-volume mount is writable (prod Linux).
- docker-compose.{home,selfhost,yml}: add the 'worker' (yt-dlp/ffmpeg job loop) + 'bgutil-pot'
(PO-token) services + a downloads mount (DOWNLOAD_ROOT, WORKER_ENABLED). Media defaults to a
named volume; DOWNLOAD_HOST_PATH points it at a host dir (e.g. a Plex-readable folder).
- README / docs/self-hosting.md / .env.example / install.{sh,ps1}: document the Download Center,
the two extra containers, and DOWNLOAD_HOST_PATH.
- VERSION 0.22.0 + releaseNotes entry.
67 lines
2.5 KiB
Docker
67 lines
2.5 KiB
Docker
# Build/version info. APP_VERSION comes from the committed VERSION file (always correct,
|
|
# even for a plain `docker compose build`); GIT_SHA/BUILD_DATE are best-effort build-args
|
|
# the deploy scripts pass. An explicit APP_VERSION build-arg still overrides the file.
|
|
ARG APP_VERSION=
|
|
ARG GIT_SHA=unknown
|
|
ARG BUILD_DATE=
|
|
|
|
# Stage 1: build the frontend SPA
|
|
FROM node:20-alpine AS frontend
|
|
ARG APP_VERSION
|
|
ARG GIT_SHA
|
|
ARG BUILD_DATE
|
|
WORKDIR /fe
|
|
COPY frontend/package.json ./
|
|
RUN npm install
|
|
COPY frontend/ ./
|
|
COPY VERSION ./.version
|
|
# Vite inlines VITE_*-prefixed env at build time; version falls back to the VERSION file.
|
|
RUN VITE_APP_VERSION="${APP_VERSION:-$(cat ./.version)}" \
|
|
VITE_GIT_SHA="$GIT_SHA" \
|
|
VITE_BUILD_DATE="$BUILD_DATE" \
|
|
npm run build
|
|
|
|
# Stage 2: backend runtime, serving the built SPA
|
|
FROM python:3.12-slim
|
|
ARG GIT_SHA
|
|
ARG BUILD_DATE
|
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
PYTHONUNBUFFERED=1 \
|
|
PIP_NO_CACHE_DIR=1 \
|
|
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
GIT_SHA=$GIT_SHA \
|
|
BUILD_DATE=$BUILD_DATE
|
|
|
|
WORKDIR /app
|
|
|
|
# ffmpeg: merge separate video+audio streams + postprocessing. Deno: the JavaScript runtime
|
|
# yt-dlp uses to solve YouTube's "n" signature challenge for the web player clients (required
|
|
# alongside the PO token, else only storyboards are offered). yt-dlp auto-detects deno on PATH.
|
|
ARG DENO_VERSION=v2.9.1
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends ffmpeg unzip curl ca-certificates \
|
|
&& curl -fsSL "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-x86_64-unknown-linux-gnu.zip" -o /tmp/deno.zip \
|
|
&& unzip -o /tmp/deno.zip -d /usr/local/bin \
|
|
&& chmod +x /usr/local/bin/deno \
|
|
&& rm /tmp/deno.zip \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY backend/requirements.txt .
|
|
RUN pip install --upgrade pip && pip install -r requirements.txt
|
|
|
|
COPY backend/ .
|
|
# Read at runtime by config.py as the app_version (a committed single source of truth).
|
|
COPY VERSION ./VERSION
|
|
COPY --from=frontend /fe/dist ./app/static_spa
|
|
|
|
# Create the download-center mount point owned by the app user, so a fresh named volume mounted
|
|
# at /downloads inherits appuser ownership (Docker copies the image dir's ownership into a new
|
|
# empty volume) — the worker/API can then write there without a manual chown. A bind mount to a
|
|
# host path instead needs that path writable by this uid (see docs/self-hosting.md).
|
|
RUN adduser --disabled-password --gecos "" appuser \
|
|
&& mkdir -p /downloads \
|
|
&& chown -R appuser /app /downloads
|
|
USER appuser
|
|
|
|
EXPOSE 8000
|
|
CMD ["sh", "entrypoint.sh"]
|