From 225f369cba108e86a1704e4fb7bd6c41d8efe4d2 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Fri, 26 Jun 2026 18:13:07 +0530 Subject: [PATCH] fix: fix ffmpeg download url --- api/Dockerfile | 93 ++++++++++++++++++------------------- remote_up.sh | 5 ++ scripts/lib/setup_common.sh | 53 +++++++++++++++++++++ 3 files changed, 103 insertions(+), 48 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index 621d1530..1a8d48dc 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -59,56 +59,53 @@ RUN npm ci --omit=dev && npm cache clean --force # Stage 3: Static ffmpeg binary (avoids apt ffmpeg pulling mesa/libllvm for # hardware acceleration we don't use server-side). # -# Resilient download: johnvansickle.com is the primary source but it's a single -# self-hosted host with no CDN and goes down intermittently. Use bounded-timeout -# retries, then fall back to a pinned BtbN/FFmpeg-Builds autobuild. Every archive -# is SHA256-verified before extraction. The two sources have different internal -# layouts, so locate the binaries with `find` rather than a fixed strip path. +# Source: BtbN/FFmpeg-Builds, served from GitHub's release-assets CDN (fast, +# highly available, multi-arch). We pin a specific build for reproducibility, +# but to a *month-end* autobuild tag — not a daily one. BtbN prunes daily +# autobuilds after ~2 weeks (the previous pin was a daily tag and started +# 404ing once GC'd), but keeps one month-end snapshot per month long-term +# (~2 years back). A dated tag's assets are immutable, so the per-arch sha256 +# below never rots: builds stay reproducible AND integrity-verified. +# +# To upgrade ffmpeg: bump BTBN_TAG + BTBN_REV to a newer month-end autobuild +# and refresh the two sha256s. No download needed — read tag, revision and +# per-asset sha256 straight from the GitHub release-asset metadata: +# gh api repos/BtbN/FFmpeg-Builds/releases/tags/ \ +# --jq '.assets[] | select(.name|test("(linux64|linuxarm64)-gpl\\.tar\\.xz$")) | "\(.name) \(.digest)"' +# +# `--speed-limit/--speed-time` aborts a *stalled* transfer after 30s of <1KB/s +# (the cause of "stuck" builds) without killing a slow-but-progressing +# download; `--max-time` is a hard backstop; `--retry` rides out transient CDN +# hiccups. The archive nests binaries under bin/, so locate them with `find`. FROM debian:trixie-slim AS ffmpeg-static ARG TARGETARCH -RUN apt-get update && apt-get install -y --no-install-recommends \ - curl ca-certificates xz-utils \ - && rm -rf /var/lib/apt/lists/* \ - && case "${TARGETARCH}" in \ - amd64) \ - primary_url="https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz" ; \ - primary_sha256="abda8d77ce8309141f83ab8edf0596834087c52467f6badf376a6a2a4c87cf67" ; \ - fallback_url="https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-05-30-13-19/ffmpeg-N-124681-gb8c5376eb4-linux64-gpl.tar.xz" ; \ - fallback_sha256="6cfd689ee95ff128e89080af10c93f16e48760eb2acc124c5c8258dc922cc13b" ; \ - ;; \ - arm64) \ - primary_url="https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-arm64-static.tar.xz" ; \ - primary_sha256="f4149bb2b0784e30e99bdda85471c9b5930d3402014e934a5098b41d0f7201b1" ; \ - fallback_url="https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-05-30-13-19/ffmpeg-N-124681-gb8c5376eb4-linuxarm64-gpl.tar.xz" ; \ - fallback_sha256="b90a31f1d0b030f5d8a3d11cfec736e369bd5a1371b19bf65421a07f72b1d547" ; \ - ;; \ - *) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2; exit 1 ;; \ - esac \ - && mkdir -p /tmp/ffmpeg \ - && ok= \ - && for source in \ - "primary ${primary_sha256} ${primary_url}" \ - "fallback ${fallback_sha256} ${fallback_url}" ; do \ - source_name="${source%% *}" ; \ - source_data="${source#* }" ; \ - sha256="${source_data%% *}" ; \ - url="${source_data#* }" ; \ - echo "Downloading ffmpeg (${source_name}) from ${url}" ; \ - if curl -fsSL --connect-timeout 20 --max-time 300 \ - --retry 3 --retry-delay 5 --retry-all-errors \ - -o /tmp/ffmpeg.tar.xz "${url}" \ - && echo "${sha256} /tmp/ffmpeg.tar.xz" | sha256sum -c - ; then ok=1 ; break ; fi ; \ - rm -f /tmp/ffmpeg.tar.xz ; \ - echo "ffmpeg source failed, trying next: ${url}" >&2 ; \ - done \ - && [ -n "${ok}" ] || { echo "all ffmpeg download sources failed" >&2 ; exit 1 ; } \ - && tar -xJf /tmp/ffmpeg.tar.xz -C /tmp/ffmpeg \ - && ffmpeg_bin="$(find /tmp/ffmpeg -type f -name ffmpeg | head -n1)" \ - && ffprobe_bin="$(find /tmp/ffmpeg -type f -name ffprobe | head -n1)" \ - && [ -n "${ffmpeg_bin}" ] && [ -n "${ffprobe_bin}" ] \ - && mv "${ffmpeg_bin}" "${ffprobe_bin}" /usr/local/bin/ \ - && chmod +x /usr/local/bin/ffmpeg /usr/local/bin/ffprobe \ - && rm -rf /tmp/ffmpeg /tmp/ffmpeg.tar.xz +ARG BTBN_TAG=autobuild-2026-05-31-13-22 +ARG BTBN_REV=N-124714-g49a77d37be +RUN set -eu ; \ + apt-get update && apt-get install -y --no-install-recommends \ + curl ca-certificates xz-utils ; \ + rm -rf /var/lib/apt/lists/* ; \ + case "${TARGETARCH}" in \ + amd64) btbn_arch=linux64 ; \ + sha256=ee052121296e6479325e09c6097d48e72a4af472d18c2b94388b5405dcde6cce ;; \ + arm64) btbn_arch=linuxarm64 ; \ + sha256=e97545305043794cdf7b698d713e29291464e0c35bb8e0f3ff1f62e4c56eedd6 ;; \ + *) echo "unsupported TARGETARCH: ${TARGETARCH}" >&2 ; exit 1 ;; \ + esac ; \ + url="https://github.com/BtbN/FFmpeg-Builds/releases/download/${BTBN_TAG}/ffmpeg-${BTBN_REV}-${btbn_arch}-gpl.tar.xz" ; \ + mkdir -p /tmp/ffmpeg ; cd /tmp/ffmpeg ; \ + echo "Downloading ffmpeg (${BTBN_TAG}) from ${url}" ; \ + curl -fsSL --connect-timeout 20 --speed-limit 1024 --speed-time 30 \ + --max-time 600 --retry 3 --retry-delay 5 --retry-all-errors \ + -o ffmpeg.tar.xz "${url}" ; \ + echo "${sha256} ffmpeg.tar.xz" | sha256sum -c - ; \ + tar -xJf ffmpeg.tar.xz ; \ + ffmpeg_bin="$(find /tmp/ffmpeg -type f -name ffmpeg | head -n1)" ; \ + ffprobe_bin="$(find /tmp/ffmpeg -type f -name ffprobe | head -n1)" ; \ + [ -n "${ffmpeg_bin}" ] && [ -n "${ffprobe_bin}" ] ; \ + mv "${ffmpeg_bin}" "${ffprobe_bin}" /usr/local/bin/ ; \ + chmod +x /usr/local/bin/ffmpeg /usr/local/bin/ffprobe ; \ + rm -rf /tmp/ffmpeg # Stage 4: Runtime - Minimal image with only runtime dependencies FROM python:3.13-slim AS runner diff --git a/remote_up.sh b/remote_up.sh index 4c67405a..01990067 100755 --- a/remote_up.sh +++ b/remote_up.sh @@ -65,6 +65,11 @@ else COMPOSE_CMD=(sudo docker compose) fi +# Reconcile the Postgres role password with .env before starting the API. +# POSTGRES_PASSWORD only applies on first volume init, so an existing volume can +# hold a stale password the API would fail to authenticate against. Idempotent. +dograh_sync_postgres_password "$SCRIPT_DIR" "${COMPOSE_CMD[@]}" + # When SERVER_IP (sourced from .env above) is a private/reserved address the host # has no public IP, so start the cloudflared service (tunnel profile) to make # webhooks reachable. The backend resolves the tunnel's public URL at runtime using diff --git a/scripts/lib/setup_common.sh b/scripts/lib/setup_common.sh index a635209e..6b6c31cf 100644 --- a/scripts/lib/setup_common.sh +++ b/scripts/lib/setup_common.sh @@ -401,6 +401,59 @@ dograh_preflight_remote_init_render() { rm -rf "$tmp_root" } +# Reconcile the running Postgres role password with POSTGRES_PASSWORD in .env. +# +# POSTGRES_PASSWORD only takes effect when the postgres data volume is first +# initialized. If the volume was created before .env had a generated password +# (e.g. an early start used the compose fallback `:-postgres`), or the password +# was later rotated, the role keeps its old password while the API connects with +# the .env value over TCP (pg_hba `scram-sha-256`) and dies with "password +# authentication failed for user postgres". start_docker.sh handles this for the +# OSS quickstart; the remote path (remote_up.sh) needs the same reconciliation. +# +# Bring postgres up on its own, then ALTER the role over the trusted local +# socket (pg_hba trusts `local`, so this works even when the password is +# currently mismatched). Idempotent: on a fresh volume it just re-sets the same +# value. Survives the later `--force-recreate` because the password lives in the +# data volume, not the container. +dograh_sync_postgres_password() { + local project_dir=$1 + shift + local compose=("$@") + local env_file="$project_dir/.env" + local password="" + local ready="" + local i + + [[ ${#compose[@]} -gt 0 ]] || compose=(docker compose) + + if [[ -f "$env_file" ]]; then + password="$(awk -F= '/^POSTGRES_PASSWORD=/{sub(/^POSTGRES_PASSWORD=/, ""); print; exit}' "$env_file")" + fi + + # No explicit password: the compose fallback (`:-postgres`) governs both the + # DB init and the API's DATABASE_URL, so the two already agree — nothing to do. + [[ -n "$password" ]] || return 0 + + dograh_info "Syncing Postgres password from .env..." + ( cd "$project_dir" && "${compose[@]}" up -d postgres ) >/dev/null + + for ((i = 0; i < 30; i++)); do + if ( cd "$project_dir" && "${compose[@]}" exec -T postgres pg_isready -U postgres ) >/dev/null 2>&1; then + ready=1 + break + fi + sleep 1 + done + [[ -n "$ready" ]] || dograh_fail "Postgres did not become ready while syncing POSTGRES_PASSWORD." + + printf '%s\n' "ALTER USER postgres WITH PASSWORD :'pw';" \ + | ( cd "$project_dir" && "${compose[@]}" exec -T postgres \ + psql -U postgres -d postgres -v ON_ERROR_STOP=1 -v "pw=$password" ) >/dev/null \ + || dograh_fail "Failed to sync Postgres password from .env." + dograh_success "✓ Postgres password synced with .env" +} + dograh_prepare_remote_install() { local project_dir=${1:-$(dograh_project_dir)} local env_file="$project_dir/.env"