#!/bin/bash
# install-bbs-deps.sh
#
# Run this on the BBS SERVER (not the dev machine) to install every runtime
# dependency sixeldoor needs. Safe to re-run — idempotent.
#
#   sudo ./install-bbs-deps.sh
#
# Optional extras (off by default — see README's "Getting a libjxl-enabled
# ffmpeg" and "Fixing intermittent HTTP 403 errors" sections for why you'd
# want these):
#
#   sudo ./install-bbs-deps.sh --with-jxl-ffmpeg
#       Replaces the distro ffmpeg with a libjxl-enabled static build
#       (BtbN/FFmpeg-Builds) at /usr/local/bin/ffmpeg, which findTool in
#       main.go already checks before /usr/bin/ffmpeg.
#
#   sudo ./install-bbs-deps.sh --with-pot-provider
#       Installs bgutil-ytdlp-pot-provider and runs it as a systemd
#       service, to reduce (not eliminate — see README) intermittent
#       HTTP 403s from YouTube. Runs as $SUDO_USER (the account you ran
#       sudo from), or pass --pot-user=NAME to pick a different one.
#
# Not handled by this script at all, deliberately left to you: keeping
# yt-dlp current against YouTube's own frequent breakage. This package
# also ships update-yt-dlp-nightly.sh (Linux) / update-yt-dlp-nightly.bat
# (Windows) for that — run standalone, on whatever schedule YOU control
# (systemd timer, cron, your BBS software's own event scheduler, or by
# hand). See README's "Fixing intermittent HTTP 403 errors" for why this
# exists and how to wire it up; deliberately not auto-installed or
# auto-scheduled by this script, since how and where to run it is a
# sysop decision, not this script's to make.
#
# Tested on Ubuntu 22.04, 24.04 and Debian 12.

set -e

WITH_JXL_FFMPEG=0
WITH_POT_PROVIDER=0
POT_USER="${SUDO_USER:-}"

for arg in "$@"; do
    case "$arg" in
        --with-jxl-ffmpeg) WITH_JXL_FFMPEG=1 ;;
        --with-pot-provider) WITH_POT_PROVIDER=1 ;;
        --pot-user=*) POT_USER="${arg#*=}" ;;
        -h|--help)
            sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//'
            exit 0
            ;;
        *)
            echo "error: unknown argument: $arg (see --help)" >&2
            exit 1
            ;;
    esac
done

if [ "$EUID" -ne 0 ]; then
    echo "error: must run as root (try:  sudo $0 )" >&2
    exit 1
fi

say() { printf '\n\033[1;36m==\033[0m %s\n' "$*"; }
ok()  { printf '   \033[1;32m✓\033[0m %s\n' "$*"; }
warn(){ printf '   \033[1;33m!\033[0m %s\n' "$*"; }

# ---------- detect package manager ----------
if command -v apt-get >/dev/null 2>&1; then
    PM=apt
elif command -v dnf >/dev/null 2>&1; then
    PM=dnf
elif command -v yum >/dev/null 2>&1; then
    PM=yum
else
    echo "error: no supported package manager (apt/dnf/yum) found" >&2
    exit 1
fi
ok "package manager: $PM"

# ---------- 1. ffmpeg ----------
say "installing ffmpeg"
case "$PM" in
    apt)
        export DEBIAN_FRONTEND=noninteractive
        apt-get update -qq
        apt-get install -y -qq ffmpeg python3-pip python3-venv curl
        ;;
    dnf|yum)
        $PM install -y ffmpeg python3-pip curl
        ;;
esac
ok "ffmpeg: $(ffmpeg -version 2>/dev/null | head -1 | cut -d' ' -f1-3)"

# ---------- 2. purge stale yt-dlp if present ----------
say "removing stale distro yt-dlp (if any)"
case "$PM" in
    apt)
        if dpkg -l yt-dlp 2>/dev/null | grep -q '^ii'; then
            apt-get remove -y -qq yt-dlp
            ok "removed apt yt-dlp"
        else
            ok "no apt yt-dlp to remove"
        fi
        ;;
    dnf|yum)
        if rpm -q yt-dlp >/dev/null 2>&1; then
            $PM remove -y yt-dlp
            ok "removed rpm yt-dlp"
        else
            ok "no rpm yt-dlp to remove"
        fi
        ;;
esac

# ---------- 3. install current yt-dlp from PyPI ----------
say "installing latest yt-dlp from PyPI"
# PEP 668 "externally managed" on newer Ubuntu/Debian: need --break-system-packages.
PIP_ARGS="--upgrade"
if python3 -c 'import sys; exit(0 if sys.version_info >= (3,11) else 1)'; then
    PIP_ARGS="$PIP_ARGS --break-system-packages"
fi
pip3 install $PIP_ARGS yt-dlp >/dev/null
YT_PATH=$(command -v yt-dlp)
YT_VER=$(yt-dlp --version)
ok "yt-dlp $YT_VER installed at $YT_PATH"
warn "this is yt-dlp's STABLE PyPI release — it lags YouTube's own changes,"
warn "sometimes badly enough to fail on 100% of videos for weeks at a time."
warn "Confirmed live: this exact scenario happened right after install."
warn "Right after this script finishes, run ./update-yt-dlp-nightly.sh once"
warn "(see step 6 below) BEFORE assuming the door itself is broken."

# ---------- 4. install Deno (JS runtime yt-dlp needs for reliable extraction) ----------
say "installing Deno"
# Recent yt-dlp needs to execute a bit of JS to resolve some YouTube
# formats' signatures. Without a JS runtime, yt-dlp still runs but silently
# falls back to whatever formats it can resolve unaided, and downloads
# intermittently fail with "HTTP Error 403: Forbidden" — the format it
# picked without JS support turns out to have an invalid/expired URL.
if command -v deno >/dev/null 2>&1; then
    ok "deno already installed: $(command -v deno)"
else
    curl -fsSL https://deno.land/install.sh | sh -s -- >/dev/null 2>&1 \
        && ok "deno installed to ~/.deno/bin" \
        || warn "deno install failed — see https://github.com/yt-dlp/yt-dlp/wiki/EJS for current setup instructions"
fi

# ---------- 5. sanity-check tools are on the default PATH ----------
say "sanity checks"
for bin in ffmpeg yt-dlp deno; do
    p=$(command -v "$bin" || true)
    if [ -z "$p" ]; then
        warn "$bin not in PATH (this shouldn't happen)"
    else
        ok "$bin -> $p"
    fi
done

# Double-check ffmpeg has at least one compressed-audio encoder for the
# native SyncTERM 1.10+ audio fallback path (Opus, Vorbis, MP3 — tried in
# that order if a client can't do the preferred WAV/PCM16, which always
# works since ffmpeg has pcm_s16le built in).
FFMPEG_ENCODERS=$(ffmpeg -hide_banner -encoders 2>/dev/null)
FOUND_AUDIO_ENC=""
for enc in libopus libvorbis libmp3lame; do
    if echo "$FFMPEG_ENCODERS" | grep -q "$enc"; then
        ok "$enc encoder present"
        FOUND_AUDIO_ENC=1
    fi
done
if [ -z "$FOUND_AUDIO_ENC" ]; then
    warn "no libopus/libvorbis/libmp3lame encoder found — native SyncTERM audio will fall back to uncompressed WAV (works, but uses much more bandwidth). install ffmpeg-full for a compressed codec."
fi

# The browser-audio fallback (for terminals without SyncTERM 1.10+'s native
# mixer) always needs libmp3lame specifically — it doesn't negotiate.
if echo "$FFMPEG_ENCODERS" | grep -q libmp3lame; then
    ok "libmp3lame present (browser-audio fallback will work)"
else
    warn "libmp3lame missing — the browser-audio fallback will fail for terminals without native SyncTERM audio. install ffmpeg-full or build with lame."
fi

# Optional: libjxl gets you the preferred video path (see README's "Video
# rendering") — smaller AND better-quality frames than sixel. Not fatal if
# missing (most distro ffmpeg packages don't enable it); the door checks
# this itself at startup and falls back to sixel automatically.
if echo "$FFMPEG_ENCODERS" | grep -q libjxl; then
    ok "libjxl present (JPEG XL video rendering will be used when the client supports it)"
else
    warn "libjxl missing — video will use sixel (still works, just larger/lower-quality frames). Not required, but if you want it: rebuild/reinstall ffmpeg with --enable-libjxl."
fi

# ---------- 6. optional: libjxl-enabled ffmpeg ----------
if [ "$WITH_JXL_FFMPEG" = 1 ]; then
    say "installing libjxl-enabled ffmpeg (BtbN static build)"
    TMPDIR=$(mktemp -d)
    trap 'rm -rf "$TMPDIR"' EXIT
    curl -fsSL -o "$TMPDIR/ffmpeg.tar.xz" \
        https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-linux64-gpl.tar.xz
    tar xf "$TMPDIR/ffmpeg.tar.xz" -C "$TMPDIR"
    FFBIN=$(find "$TMPDIR" -maxdepth 3 -type f -name ffmpeg -path '*/bin/*' | head -1)
    if [ -z "$FFBIN" ]; then
        warn "couldn't find ffmpeg binary in the downloaded archive — skipping"
    else
        cp "$FFBIN" /usr/local/bin/ffmpeg
        # 755, not a bare "+x" — see update-yt-dlp-nightly.sh's identical
        # fix for why: a umask-restricted copy can end up unreadable by
        # anyone but root even after "+x" (execute doesn't require read,
        # but this script always runs as root — see EUID check above —
        # while the door itself runs as a regular user).
        chmod 755 /usr/local/bin/ffmpeg
        if /usr/local/bin/ffmpeg -encoders 2>/dev/null | grep -q libjxl; then
            ok "libjxl-enabled ffmpeg installed at /usr/local/bin/ffmpeg"
        else
            warn "installed /usr/local/bin/ffmpeg but libjxl not detected — check manually"
        fi
    fi
fi

# ---------- 7. optional: bgutil-ytdlp-pot-provider (reduces HTTP 403s) ----------
if [ "$WITH_POT_PROVIDER" = 1 ]; then
    say "installing bgutil-ytdlp-pot-provider"
    if [ -z "$POT_USER" ] || [ "$POT_USER" = "root" ]; then
        warn "no non-root target user known — re-run as: sudo ./install-bbs-deps.sh --with-pot-provider --pot-user=YOURUSER"
    else
        POT_HOME=$(getent passwd "$POT_USER" | cut -d: -f6)
        if [ -z "$POT_HOME" ]; then
            warn "user '$POT_USER' not found — skipping POT provider setup"
        else
            sudo -u "$POT_USER" -H bash -c 'pip3 install --user --upgrade bgutil-ytdlp-pot-provider' >/dev/null \
                && ok "bgutil-ytdlp-pot-provider (yt-dlp plugin) installed for $POT_USER"

            if [ ! -d "$POT_HOME/bgutil-ytdlp-pot-provider" ]; then
                sudo -u "$POT_USER" -H bash -c "cd '$POT_HOME' && git clone -q https://github.com/Brainicism/bgutil-ytdlp-pot-provider.git"
            fi
            sudo -u "$POT_USER" -H bash -c "cd '$POT_HOME/bgutil-ytdlp-pot-provider/server' && '$POT_HOME/.deno/bin/deno' install" \
                && ok "bgutil-ytdlp-pot-provider server deps installed"

            cat > /etc/systemd/system/bgutil-pot-provider.service <<EOF
[Unit]
Description=bgutil yt-dlp PO Token Provider
After=network.target

[Service]
Type=simple
User=$POT_USER
WorkingDirectory=$POT_HOME/bgutil-ytdlp-pot-provider/server
ExecStart=$POT_HOME/.deno/bin/deno run -A src/main.ts
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
            systemctl daemon-reload
            systemctl enable --now bgutil-pot-provider
            sleep 1
            if curl -fs http://127.0.0.1:4416/ping >/dev/null 2>&1; then
                ok "bgutil-pot-provider service running and answering on :4416"
            else
                warn "bgutil-pot-provider service started but isn't answering yet — check: systemctl status bgutil-pot-provider"
            fi
        fi
    fi
fi

# ---------- 8. print next steps ----------
cat <<EOF

$(tput bold 2>/dev/null)All runtime dependencies installed.$(tput sgr0 2>/dev/null)

Remaining steps are sysop decisions — you do these:

  1. Do this NOW, before anything else: run update-yt-dlp-nightly.sh once
     (shipped alongside this script), by hand:
       sudo ./update-yt-dlp-nightly.sh
     The stable yt-dlp just installed above can be — and, confirmed live,
     sometimes IS — already broken against current YouTube (100% failure
     on every video) right out of the box. This one-time run is how you
     find that out before you've wired the door into your BBS and start
     debugging the wrong thing. Afterwards, schedule it to run
     periodically YOUR way (systemd timer, cron, your BBS software's own
     event scheduler) — see README's "Fixing intermittent HTTP 403
     errors" for why and how. Deliberately not automated by this script;
     when and how it runs is your call.

  2. Copy the binary onto this server (run on your dev box):
       scp sixeldoor-static user@$(hostname):/sbbs/xtrn/ytube/sixeldoor
     Then make it executable:
       chmod +x /sbbs/xtrn/ytube/sixeldoor

  3. Create /sbbs/xtrn/ytube/run.sh (wrapper that sets PATH + env):

       #!/bin/bash
       export PATH="/usr/local/bin:/usr/bin:/bin:\$HOME/.deno/bin:\$PATH"
       export SIXELDOOR_HOSTNAME=$(hostname -f 2>/dev/null || hostname)
       export SIXELDOOR_AUDIO_PORT=18080
       exec /sbbs/xtrn/ytube/sixeldoor "\$@"

     chmod +x /sbbs/xtrn/ytube/run.sh

  4. Open port 18080 in your firewall (browser-audio fallback, only used by
     terminals without SyncTERM 1.10+'s native audio mixer).

  5. Register in Synchronet scfg:
       External Programs -> Online Programs -> (Add)
       Command Line:    /sbbs/xtrn/ytube/run.sh %f
       BBS Drop File:   DOOR32.SYS
       Place Drop File: Node Directory
       Intercept I/O:   Yes

  6. Optional but recommended: re-run this script with --with-jxl-ffmpeg
     and --with-pot-provider (see the top of this file, or the README) for
     smaller/better video frames and fewer YouTube HTTP 403s.

  7. Tail the door's log on first use:
       tail -f /tmp/sixeldoor.log
EOF
