#!/bin/bash # ClipForge Helper (macOS / Linux) # Downloads a YouTube (or any yt-dlp supported) link over YOUR internet # connection, uploads the video to ClipForge, and opens the job page. # Also accepts a local video file (drag it onto the prompt). # # Usage: clipforge-helper.sh [URL-or-file] # Env: CLIPFORGE_SERVER (default https://clips.thegridreport.news) # CLIPFORGE_KEEP=1 keeps the downloaded video in ~/ClipForgeHelper/downloads # CLIPFORGE_NO_OPEN=1 don't open the browser (testing) # Written for bash 3.2 (the version macOS ships). SERVER="${CLIPFORGE_SERVER:-https://clips.thegridreport.news}" SERVER="${SERVER%/}" HOME_DIR="${CLIPFORGE_HOME:-$HOME/ClipForgeHelper}" BIN="$HOME_DIR/bin" export PATH="$BIN:$PATH" say() { printf '%s\n' "$*"; } die() { printf '\n❌ %s\n\n' "$*"; pause_exit 1; } pause_exit() { if [ -t 0 ] && [ -z "$CLIPFORGE_NO_PAUSE" ]; then printf 'Press Enter to close this window… '; read -r _ ; fi exit "${1:-0}" } open_url() { [ -n "$CLIPFORGE_NO_OPEN" ] && return 0 if command -v open >/dev/null 2>&1; then open "$1" elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$1" >/dev/null 2>&1 & fi } YTDLP="$BIN/yt-dlp"; [ -x "$YTDLP" ] || YTDLP="$(command -v yt-dlp)" say "" say "⚡ ClipForge Helper → $SERVER" say "------------------------------------------------------------" INPUT="$1" if [ -z "$INPUT" ]; then CLIP="" command -v pbpaste >/dev/null 2>&1 && CLIP="$(pbpaste 2>/dev/null | head -n 1)" case "$CLIP" in http://*|https://*) say "Link on your clipboard:" say " $CLIP" printf 'Press Enter to use it, or paste a different link / drag a video file here: ' read -r INPUT [ -z "$INPUT" ] && INPUT="$CLIP" ;; *) printf 'Paste a YouTube link (or drag a video file here), then press Enter:\n> ' read -r INPUT ;; esac fi # tidy input: trim spaces/quotes, undo Terminal's "\ " escaping of dragged files INPUT="$(printf '%s' "$INPUT" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e "s/^['\"]//" -e "s/['\"]\$//")" [ -z "$INPUT" ] && die "Nothing entered." WORK="" cleanup() { [ -n "$WORK" ] && [ -z "$CLIPFORGE_KEEP" ] && rm -rf "$WORK"; } trap cleanup EXIT case "$INPUT" in http://*|https://*) [ -n "$YTDLP" ] && [ -x "$YTDLP" ] || die "yt-dlp is missing. Re-run the installer from $SERVER/helper" mkdir -p "$HOME_DIR/downloads" WORK="$(mktemp -d "$HOME_DIR/downloads/job.XXXXXX")" # keep yt-dlp current - YouTube changes often (quiet, best effort) if [ -z "$CLIPFORGE_NO_UPDATE" ] && [ -w "$YTDLP" ]; then say "Checking for yt-dlp updates…" "$YTDLP" -U >/dev/null 2>&1 || true fi # with ffmpeg: best video+audio up to 1080p; without it: best single file FMT="bv*[height<=1080]+ba/b[height<=1080]/b" if ! ffmpeg -version >/dev/null 2>&1; then say "(ffmpeg isn't working here, so using a single-file format - quality may be lower)" FMT="b[height<=1080]/b" fi say "⬇️ Downloading on your computer (this uses your home internet)…" "$YTDLP" --no-playlist \ -f "$FMT" \ -S "vcodec:h264,res,acodec:m4a" \ --merge-output-format mp4 \ --newline --progress-template "download: %(progress._percent_str)s of %(progress._total_bytes_str)s at %(progress._speed_str)s" \ -o "$WORK/%(title).80B.%(ext)s" "$INPUT" RC=$? [ $RC -eq 0 ] || die "The download failed (yt-dlp exit $RC). If YouTube asks you to sign in, try again in a few minutes or try another video." FILE="$(ls -1 "$WORK"/* 2>/dev/null | grep -v '\.part$' | head -n 1)" [ -n "$FILE" ] && [ -f "$FILE" ] || die "Download finished but no video file was found." ;; *) FILE="$(printf '%s' "$INPUT" | sed -e 's/\\\(.\)/\1/g')" [ -f "$FILE" ] || die "That's not a link and I can't find a file at: $FILE" ;; esac NAME="$(basename "$FILE")" SIZE="$(wc -c < "$FILE" | tr -d ' ')" say "" say "⬆️ Uploading \"$NAME\" ($((SIZE / 1048576)) MB) to ClipForge…" # JSON-escape the filename (quotes and backslashes) JNAME="$(printf '%s' "$NAME" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g')" RESP="$(curl -sS -X POST "$SERVER/api/uploads" \ -H 'Content-Type: application/json' \ -d "{\"filename\":\"$JNAME\",\"size\":$SIZE}" 2>&1)" \ || die "ClipForge refused the upload: $RESP" UPID="$(printf '%s' "$RESP" | sed -n 's/.*"upload_id":"\([0-9a-f]*\)".*/\1/p')" CHUNK="$(printf '%s' "$RESP" | sed -n 's/.*"chunk_size":\([0-9]*\).*/\1/p')" [ -n "$UPID" ] && [ -n "$CHUNK" ] || die "Unexpected reply from ClipForge: $RESP" OFF=0; I=0 while [ "$OFF" -lt "$SIZE" ]; do TRY=0; OK="" while [ $TRY -lt 6 ] && [ -z "$OK" ]; do TRY=$((TRY + 1)) R="$(dd if="$FILE" bs="$CHUNK" skip="$I" count=1 2>/dev/null | \ curl -sS -X PUT --data-binary @- \ -H 'Content-Type: application/octet-stream' \ "$SERVER/api/uploads/$UPID?offset=$OFF" 2>&1)" NEW="$(printf '%s' "$R" | sed -n 's/.*"received":\([0-9]*\).*/\1/p')" if [ -n "$NEW" ]; then OK=1; OFF="$NEW" else sleep $((TRY * 2)); fi done [ -n "$OK" ] || die "Upload kept failing at $((OFF / 1048576)) MB: $R" I=$((OFF / CHUNK)) printf '\r %3d%% (%d of %d MB)' $((OFF * 100 / SIZE)) $((OFF / 1048576)) $((SIZE / 1048576)) done printf '\n' RESP="$(curl -sS -X POST "$SERVER/api/uploads/$UPID/finish" 2>&1)" \ || die "Couldn't start the clip job: $RESP" JOB="$(printf '%s' "$RESP" | sed -n 's/.*"job_id":"\([0-9a-f]*\)".*/\1/p')" [ -n "$JOB" ] || die "Unexpected reply from ClipForge: $RESP" URL="$SERVER/?job=$JOB" say "" say "✅ Uploaded! ClipForge job $JOB is making your clips." say " Opening $URL" say " (Keep that browser tab open - clips appear there in a few minutes.)" open_url "$URL" [ -n "$CLIPFORGE_KEEP" ] && [ -n "$WORK" ] && say " Video kept at: $FILE" say "" pause_exit 0