#!/bin/bash
# yt-dlp: written by Wren Linux extras/yt-dlp.sh, installed as /usr/local/bin/yt-dlp.
# Downloads videos with yt-dlp, which runs as its own locked-down user (ytdlp),
# never as you: it can't read your files and is kept off your local network.
# Use it like yt-dlp, e.g.  yt-dlp URL   or  yt-dlp -x URL  (sound only).
# What it downloads is copied into your Downloads folder when it is done (as
# you, so the copies are yours); ytdlp never gets into your home.
#
# Updating yt-dlp (sites change, and an old yt-dlp stops working):
#   wrenctl install yt-dlp
# Treat what it downloads as untrusted; play it with mpv, which Wren Linux's media
# extra runs in a sandbox.
# (The WREN_ ones: a test points these elsewhere)
set -u

SUDO=${WREN_SUDO:-sudo}
SESSION=${WREN_YTDLP_SESSION:-/usr/local/libexec/yt-dlp-session}
OUT=${WREN_YTDLP_OUT:-/var/lib/wren-ytdlp-out}

die() { printf 'yt-dlp: %s\n' "$*" >&2; exit 1; }

[ "$(id -u)" -ne 0 ] || die "run it as yourself, not as root."

dest=$(xdg-user-dir DOWNLOAD 2>/dev/null) || dest=
[ -n "$dest" ] && [ "$dest" != "$HOME" ] || dest=$HOME/Downloads
mkdir -p "$dest" || die "couldn't make $dest"

id=$(od -An -N12 -tx1 /dev/urandom | tr -d ' \n')
"$SUDO" -n -u ytdlp "$SESSION" "$id" "$@"
rc=$?

# copy_out RUN DEST REL: RUN/REL into DEST/REL (or "NAME (2).ext" and so on
# if that is taken), and prints where. Both sides are walked a folder at a
# time without following links, and the copy is made with O_EXCL|O_NOFOLLOW:
# the sandbox users can write in ~/Downloads (the sandboxed Firefox) and in
# RUN (ytdlp), and a link put in the way, even at the last moment, would
# otherwise send your user's copy into one of your folders (~/.config/autostart,
# say), or bring one of your files out. Such a file isn't copied.
COPY_OUT='
import os, sys
run, dest, rel = sys.argv[1:4]
parts = rel.split("/")
if any(p in ("", ".", "..") for p in parts):
    sys.exit("odd name: " + rel)
D = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW
src = os.open(run, D)
for p in parts[:-1]:
    src = os.open(p, D, dir_fd=src)
inp = os.open(parts[-1], os.O_RDONLY | os.O_NOFOLLOW, dir_fd=src)
out = os.open(dest, os.O_RDONLY | os.O_DIRECTORY)
for p in parts[:-1]:
    try:
        os.mkdir(p, 0o777, dir_fd=out)
    except FileExistsError:
        pass
    out = os.open(p, D, dir_fd=out)
name = parts[-1]
base, dot, ext = name.rpartition(".")
if not base:
    base, dot, ext = name, "", ""
n = 1
while True:
    cand = name if n == 1 else "%s (%d)%s%s" % (base, n, dot, ext)
    try:
        fd = os.open(cand, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o666, dir_fd=out)
        break
    except FileExistsError:
        n += 1
while True:
    b = os.read(inp, 1 << 20)
    if not b:
        break
    os.write(fd, b)
os.close(fd)
print(os.path.join(dest, *parts[:-1], cand))
'

run=$OUT/$id
copied=0 failed=0
if [ -d "$run" ]; then
    # Regular files only: a link (to one of your files, say) is left behind, and
    # so are unfinished downloads; copy_out follows no link on either side.
    while IFS= read -r -d '' f; do
        rel=${f#"$run"/}
        case $rel in *.part | *.ytdl | *.part-Frag*) continue ;; esac
        if python3 -I -c "$COPY_OUT" "$run" "$dest" "$rel" >/dev/null 2>&1; then
            copied=$((copied + 1))
        else
            failed=$((failed + 1))
        fi
    done < <(find "$run" -type f -print0)
    # Kept if anything couldn't be copied, so nothing downloaded is lost
    [ "$failed" -gt 0 ] || rm -rf -- "$run"
fi
[ "$copied" -eq 0 ] || printf 'yt-dlp: %d file(s) in %s\n' "$copied" "$dest" >&2
if [ "$failed" -gt 0 ]; then
    printf 'yt-dlp: %d file(s) could not be copied; they are still in %s\n' "$failed" "$run" >&2
    printf 'yt-dlp: please report this (ls -la %s shows their owner and mode)\n' "$run" >&2
fi

if [ "$rc" -eq 1 ] && [ "$copied" -eq 0 ]; then
    printf 'yt-dlp: if a site stopped working, a newer yt-dlp may fix it: wrenctl install yt-dlp\n' >&2
fi
exit "$rc"
