my-homelab-configs/infra/youtube-backup/backup-youtube.sh

106 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${YOUTUBE_BACKUP_ENV_FILE:-${SCRIPT_DIR}/.env}"
if [[ -f "${ENV_FILE}" ]]; then
set -a
# shellcheck disable=SC1090
. "${ENV_FILE}"
set +a
fi
BACKUP_ROOT="${YOUTUBE_BACKUP_ROOT:-/data/youtube-backup}"
PLAYLISTS_FILE="${YOUTUBE_BACKUP_PLAYLISTS_FILE:-${BACKUP_ROOT}/playlists.txt}"
ARCHIVE_FILE="${YOUTUBE_BACKUP_ARCHIVE_FILE:-${BACKUP_ROOT}/archive.txt}"
VIDEOS_DIR="${YOUTUBE_BACKUP_VIDEOS_DIR:-${BACKUP_ROOT}/videos}"
LOG_DIR="${YOUTUBE_BACKUP_LOG_DIR:-${BACKUP_ROOT}/logs}"
COOKIES_FILE="${YOUTUBE_BACKUP_COOKIES_FILE:-}"
COOKIES_FROM_BROWSER="${YOUTUBE_BACKUP_COOKIES_FROM_BROWSER:-}"
FORMAT="${YOUTUBE_BACKUP_FORMAT:-bv*+ba/best}"
OUTPUT_TEMPLATE="${YOUTUBE_BACKUP_OUTPUT_TEMPLATE:-%(playlist_title)s/%(upload_date>%Y-%m-%d,release_date>%Y-%m-%d,epoch>%Y-%m-%d)s - %(title)s [%(id)s].%(ext)s}"
MERGE_FORMAT="${YOUTUBE_BACKUP_MERGE_FORMAT:-mp4}"
JS_RUNTIMES="${YOUTUBE_BACKUP_JS_RUNTIMES:-node}"
WRITE_THUMBNAIL="${YOUTUBE_BACKUP_WRITE_THUMBNAIL:-false}"
RATE_LIMIT="${YOUTUBE_BACKUP_RATE_LIMIT:-}"
SLEEP_REQUESTS="${YOUTUBE_BACKUP_SLEEP_REQUESTS:-1}"
EXTRA_ARGS="${YOUTUBE_BACKUP_EXTRA_ARGS:-}"
if ! command -v yt-dlp >/dev/null 2>&1; then
echo "yt-dlp is required. Install it with: pipx install yt-dlp" >&2
exit 1
fi
if [[ ! -f "${PLAYLISTS_FILE}" ]]; then
mkdir -p "$(dirname "${PLAYLISTS_FILE}")"
cat >"${PLAYLISTS_FILE}" <<'EOF'
# Add one YouTube playlist/channel/video URL per line.
# Examples:
# https://www.youtube.com/playlist?list=YOUR_PLAYLIST_ID
# https://www.youtube.com/@YOUR_CHANNEL/videos
EOF
echo "Created ${PLAYLISTS_FILE}. Add your playlist URLs and rerun." >&2
exit 1
fi
if ! grep -Ev '^\s*($|#)' "${PLAYLISTS_FILE}" >/dev/null; then
echo "${PLAYLISTS_FILE} does not contain any playlist URLs." >&2
exit 1
fi
mkdir -p "${VIDEOS_DIR}" "${LOG_DIR}" "$(dirname "${ARCHIVE_FILE}")"
touch "${ARCHIVE_FILE}"
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
log_file="${LOG_DIR}/youtube-backup-${timestamp}.log"
args=(
--batch-file "${PLAYLISTS_FILE}"
--download-archive "${ARCHIVE_FILE}"
--paths "home:${VIDEOS_DIR}"
--output "${OUTPUT_TEMPLATE}"
--format "${FORMAT}"
--merge-output-format "${MERGE_FORMAT}"
--ignore-errors
--continue
--no-overwrites
--restrict-filenames
--write-info-json
--write-description
--embed-metadata
--embed-thumbnail
--js-runtimes "${JS_RUNTIMES}"
--sleep-requests "${SLEEP_REQUESTS}"
)
if [[ "${WRITE_THUMBNAIL}" == "true" ]]; then
args+=(--write-thumbnail --convert-thumbnails jpg)
fi
if [[ -n "${COOKIES_FILE}" ]]; then
args+=(--cookies "${COOKIES_FILE}")
fi
if [[ -n "${COOKIES_FROM_BROWSER}" ]]; then
args+=(--cookies-from-browser "${COOKIES_FROM_BROWSER}")
fi
if [[ -n "${RATE_LIMIT}" ]]; then
args+=(--limit-rate "${RATE_LIMIT}")
fi
if [[ -n "${EXTRA_ARGS}" ]]; then
# Intentionally split for operator-provided yt-dlp flags from a local .env.
# shellcheck disable=SC2206
extra_args_array=(${EXTRA_ARGS})
args+=("${extra_args_array[@]}")
fi
echo "Backing up YouTube playlists from ${PLAYLISTS_FILE}"
echo "Videos: ${VIDEOS_DIR}"
echo "Archive: ${ARCHIVE_FILE}"
echo "Log: ${log_file}"
yt-dlp "${args[@]}" 2>&1 | tee "${log_file}"