Dockerfile

FROM python:3.12-slim

ARG USER_ID=1000
ARG GROUP_ID=1000

RUN apt-get update &&    apt-get install -y --no-install-recommends ffmpeg curl nodejs git &&    apt-get clean &&    rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir --upgrade pip setuptools wheel packaging

RUN pip install --no-cache-dir    "yt-dlp[default] @ https://github.com/yt-dlp/yt-dlp/archive/master.zip"    bgutil-ytdlp-pot-provider    sentry-sdk

RUN groupadd -g ${GROUP_ID} appuser &&    useradd -u ${USER_ID} -g appuser -m appuser

RUN mkdir -p /cookies /downloads &&    chown -R appuser:appuser /cookies /downloads

COPY archive.py /usr/local/bin/archive.py
RUN chmod +x /usr/local/bin/archive.py

USER appuser
WORKDIR /downloads

ENTRYPOINT ["python3", "/usr/local/bin/archive.py"]

archive.py

#!/usr/bin/env python3
import subprocess
import time
import signal
import sys
import os
from datetime import datetime
import sentry_sdk

# Configuration
CHANNEL = os.getenv("CHANNEL_NAME")
TIMEOUT = os.getenv("TIMEOUT", "60")
COOKIES_FILE = "/cookies/cookies.txt"
DOWNLOAD_DIR = "/downloads"
POT_PROVIDER = os.getenv("POT_PROVIDER_URL", "http://pot-provider:4416")
SENTRY_DSN = os.getenv("SENTRY_DSN")
WAIT_INTERVAL = 10 

# State
running = True
proc = None
alert_history = {} 
ALERT_COOLDOWN = 60

# Initialize Sentry
if SENTRY_DSN:
    print(f"[Wrapper] Sentry monitoring enabled.")
    sentry_sdk.init(dsn=SENTRY_DSN, traces_sample_rate=1.0)

def graceful_exit(signum, frame):
    global running, proc
    print(f"\n[Wrapper] Signal {signum} received. Stopping...")
    running = False
    if proc:
        proc.terminate()

def check_log_line(line):
    """Analyze a log line to see if we should alert Sentry."""
    global alert_history

    if "WARNING" not in line and "ERROR" not in line:
        return

    ignore_phrases = [
        "channel is not currently live",
        "Press Ctrl+C to try now",
    ]

    for phrase in ignore_phrases:
        if phrase in line:
            return

    msg_key = line.strip()
    now = time.time()
    last_time = alert_history.get(msg_key, 0)

    if now - last_time > ALERT_COOLDOWN:
        if SENTRY_DSN:
            with sentry_sdk.push_scope() as scope:
                scope.set_tag("channel", CHANNEL)
                sentry_sdk.capture_message(f"yt-dlp Warning: {msg_key}", level="warning")
        alert_history[msg_key] = now

def main():
    global proc, running
    signal.signal(signal.SIGINT, graceful_exit)
    signal.signal(signal.SIGTERM, graceful_exit)

    channel_dir = os.path.join(DOWNLOAD_DIR, CHANNEL)
    os.makedirs(channel_dir, exist_ok=True)

    while running:
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        output_template = f"{channel_dir}/{CHANNEL}-{timestamp}-[%(title)s][%(id)s].%(ext)s"

        cmd = [
            "yt-dlp",
            # Standard monitoring URL
            f"https://www.youtube.com/@{CHANNEL}/live",            
            "--live-from-start",
            "--wait-for-video", TIMEOUT,
            "--cookies", COOKIES_FILE,            
            "--remote-components", "ejs:github",
            "--js-runtimes", "node",           
            "--extractor-args", f"youtubepot-bgutilhttp:base_url={POT_PROVIDER}",
            "-o", output_template,
        ]

        try:
            print(f"[Wrapper] Monitoring @{CHANNEL}...")

            proc = subprocess.Popen(
                cmd, 
                stdout=subprocess.PIPE, 
                stderr=subprocess.STDOUT, 
                text=True, 
                bufsize=1
            )

            for line in proc.stdout:
                print(line, end='') 
                sys.stdout.flush()
                check_log_line(line)

                if not running:
                    break

            proc.wait()

        except Exception as e:
            print(f"[Wrapper] Critical Python Error: {e}")
            if SENTRY_DSN:
                sentry_sdk.capture_exception(e)

        if running:
            time.sleep(WAIT_INTERVAL)

if __name__ == "__main__":
    main()

docker-compose.yml

services:
  pot-provider:
    image: brainicism/bgutil-ytdlp-pot-provider:latest
    container_name: youtube-pot-provider
    restart: always
    ports:
      - "4416:4416"

  youtube-archive:
    build:
      context: .
      args:
        - USER_ID=${USER_ID:-1000}
        - GROUP_ID=${GROUP_ID:-1000}
    container_name: youtube-archive
    depends_on:
      - pot-provider
    restart: unless-stopped
    volumes:
      - ./downloads:/downloads
      - ./cookies:/cookies
    environment:
      - CHANNEL_NAME=${CHANNEL_NAME}
      - TIMEOUT=${TIMEOUT:-60}
      - POT_PROVIDER_URL=http://pot-provider:4416
      - SENTRY_DSN=${SENTRY_DSN}
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "10" 
Edit

Pub: 25 Jan 2026 00:13 UTC

Views: 21