fix(youtube): bake JS-challenge solver into backend image + Gitea CI
Build and Publish Backend Image / build (push) Failing after 14s
Build and Publish Backend Image / build (push) Failing after 14s
YouTube playback relied on manual changes inside the running container (deno install + /etc/yt-dlp.conf) that would be lost on any recreate. Make it permanent and reproducible: - Dockerfile.backend: install pinned deno 2.8.3 as yt-dlp's JS runtime and write a system-wide /etc/yt-dlp.conf enabling the EJS challenge solver (--js-runtimes deno, --remote-components ejs:github, --force-ipv4). Replaces the unreliable node-based config. - .gitea/workflows/build-backend.yml: build Dockerfile.backend and push git.qomar.pw/omar/ts6-manager/backend:latest (amd64). Needs REGISTRY_TOKEN. - docker-compose.yml: pull backend from the Gitea registry; frontend/sidecar stay on upstream clusterzx images. Mount ./secrets read-only for optional YouTube cookies via YT_COOKIE_FILE. - gitignore/dockerignore: never commit cookies (live Google session). - DEPLOY.md: deployment + CI notes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,3 +6,6 @@
|
||||
.git/
|
||||
.claude/
|
||||
.vscode/
|
||||
secrets/
|
||||
youtube-cookies.txt
|
||||
cookies.txt
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
name: Build and Publish Backend Image
|
||||
|
||||
# Builds the backend Docker image (with the baked-in YouTube JS-challenge fix:
|
||||
# deno runtime + yt-dlp EJS solver) and pushes it to Gitea's built-in registry.
|
||||
#
|
||||
# Image: git.qomar.pw/omar/ts6-manager/backend
|
||||
# Triggers: push to main, any vX.Y.Z tag, and manual dispatch.
|
||||
#
|
||||
# Requires repo secret REGISTRY_TOKEN = a PAT with scope `write:package`.
|
||||
# (The auto GITEA_TOKEN has no Packages write permission.)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'Dockerfile.backend'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
- '.gitea/workflows/build-backend.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
REGISTRY: git.qomar.pw
|
||||
# github.repository in Gitea = "omar/ts6-manager" (already lowercase)
|
||||
IMAGE: git.qomar.pw/${{ github.repository }}/backend
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to Gitea registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ secrets.REGISTRY_USERNAME || 'omar' }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels)
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.IMAGE }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=sha,prefix=sha-
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
env:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: "false"
|
||||
DOCKER_BUILD_SUMMARY: "false"
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.backend
|
||||
target: production
|
||||
platforms: linux/amd64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -14,3 +14,9 @@ packages/backend/data/
|
||||
packages/backend/prisma/migrations/
|
||||
.claude/
|
||||
build-and-push.ps1
|
||||
|
||||
# Secrets — NEVER commit. youtube-cookies.txt is a live Google session.
|
||||
youtube-cookies.txt
|
||||
cookies.txt
|
||||
secrets/*
|
||||
!secrets/.gitkeep
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Deploy (omar fork)
|
||||
|
||||
This fork bakes the YouTube playback fix into the **backend** image so it survives
|
||||
container/host restarts. The fix: `deno` JS runtime + a system-wide `/etc/yt-dlp.conf`
|
||||
that enables yt-dlp's EJS challenge solver (`--remote-components ejs:github`,
|
||||
`--js-runtimes deno`, `--force-ipv4`). See `Dockerfile.backend`.
|
||||
|
||||
## How images are built
|
||||
|
||||
Gitea Actions (`.gitea/workflows/build-backend.yml`) builds `Dockerfile.backend` on
|
||||
every push to `main` and pushes it to the Gitea registry:
|
||||
|
||||
```
|
||||
git.qomar.pw/omar/ts6-manager/backend:latest
|
||||
```
|
||||
|
||||
`frontend` and `sidecar` use the upstream `clusterzx/ts6-manager:*` public images
|
||||
unchanged.
|
||||
|
||||
**Required repo secret:** `REGISTRY_TOKEN` — a Gitea PAT with scope `write:package`
|
||||
(Settings → Applications → Generate New Token). Optional `REGISTRY_USERNAME`
|
||||
(defaults to `omar`).
|
||||
|
||||
## Deploy on the server
|
||||
|
||||
```sh
|
||||
cd /opt/ts6-manager
|
||||
# 1. Provide config (JWT_SECRET etc.)
|
||||
cp .env.example .env && $EDITOR .env
|
||||
|
||||
# 2. (Optional) YouTube cookies — a Netscape-format export. Skippable: playback
|
||||
# works without them via the EJS solver. NEVER commit this file.
|
||||
mkdir -p secrets
|
||||
cp /path/to/youtube-cookies.txt secrets/youtube-cookies.txt
|
||||
|
||||
# 3. Pull & run
|
||||
docker login git.qomar.pw # once, with a read:package token
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Update to a fresh build later with `docker compose pull && docker compose up -d`.
|
||||
+12
-2
@@ -2,6 +2,11 @@
|
||||
FROM node:20-slim AS base
|
||||
RUN corepack enable && corepack prepare pnpm@9 --activate
|
||||
|
||||
# Deno: JS runtime yt-dlp uses to solve YouTube's nsig / player-JS challenge via the
|
||||
# EJS remote component. `node` alone is unreliable for this; deno is the proven runtime.
|
||||
# Pinned to the version verified working in production. See /etc/yt-dlp.conf below.
|
||||
COPY --from=denoland/deno:bin-2.8.3 /deno /usr/local/bin/deno
|
||||
|
||||
# Install system deps: openssl (Prisma), ffmpeg (audio), python3+pip (yt-dlp)
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
@@ -41,9 +46,14 @@ COPY --from=build /app/packages/backend/package.json ./packages/backend/package.
|
||||
COPY --from=build /app/packages/backend/node_modules ./packages/backend/node_modules
|
||||
|
||||
WORKDIR /app/packages/backend
|
||||
# System-wide yt-dlp config (works regardless of which user the process runs as).
|
||||
# This is the fix that solves YouTube playback: deno runtime + EJS challenge solver.
|
||||
RUN mkdir -p data /data/music && \
|
||||
mkdir -p /root/.config/yt-dlp && \
|
||||
echo "--js-runtimes node" > /root/.config/yt-dlp/config
|
||||
printf '%s\n' \
|
||||
'--js-runtimes deno' \
|
||||
'--remote-components ejs:github' \
|
||||
'--force-ipv4' \
|
||||
> /etc/yt-dlp.conf
|
||||
|
||||
EXPOSE 3001
|
||||
ENV SIDECAR_URL=http://ts6-sidecar:9800
|
||||
|
||||
+8
-6
@@ -1,8 +1,8 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: clusterzx/ts6-manager:backend
|
||||
# Custom backend with the baked-in YouTube fix (deno + yt-dlp EJS solver),
|
||||
# built by Gitea Actions and published to the Gitea registry.
|
||||
image: git.qomar.pw/omar/ts6-manager/backend:latest
|
||||
container_name: ts6-backend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
@@ -12,7 +12,9 @@ services:
|
||||
- JWT_SECRET=${JWT_SECRET:?Set JWT_SECRET in .env or environment}
|
||||
- ENCRYPTION_KEY=${ENCRYPTION_KEY:-}
|
||||
- TS_ALLOW_SELF_SIGNED=${TS_ALLOW_SELF_SIGNED:-false}
|
||||
- YT_COOKIE_FILE=${YT_COOKIE_FILE:-}
|
||||
# Optional YouTube cookies. Drop the file at ./secrets/youtube-cookies.txt.
|
||||
# If absent, playback still works via the deno + EJS challenge solver.
|
||||
- YT_COOKIE_FILE=${YT_COOKIE_FILE:-/secrets/youtube-cookies.txt}
|
||||
- JWT_ACCESS_EXPIRY=15m
|
||||
- JWT_REFRESH_EXPIRY=7d
|
||||
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:3000}
|
||||
@@ -22,8 +24,8 @@ services:
|
||||
volumes:
|
||||
- backend-data:/app/packages/backend/data
|
||||
- music-data:/data/music
|
||||
# Optional: mount YouTube cookies file for yt-dlp
|
||||
# - ./cookies.txt:/app/cookies.txt
|
||||
# Mounted read-only; the actual cookie file is gitignored (never committed).
|
||||
- ./secrets:/secrets:ro
|
||||
ports:
|
||||
- "3001:3001"
|
||||
depends_on:
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Place runtime secrets here (gitignored). For YouTube cookies, drop a Netscape-format
|
||||
# cookies export as ./secrets/youtube-cookies.txt — it is mounted read-only into the
|
||||
# backend at /secrets/youtube-cookies.txt (see docker-compose.yml / YT_COOKIE_FILE).
|
||||
# Never commit the actual cookie file: it is a live Google session.
|
||||
Reference in New Issue
Block a user