Add YouTube backup helper
This commit is contained in:
parent
dfda7fccfb
commit
e0684a1d1c
|
|
@ -10,6 +10,10 @@ bootstrap/host/inventory/
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
*.zip
|
*.zip
|
||||||
infra/gitea/data/
|
infra/gitea/data/
|
||||||
|
infra/youtube-backup/.env
|
||||||
|
infra/youtube-backup/cookies.txt
|
||||||
|
infra/youtube-backup/archive.txt
|
||||||
|
infra/youtube-backup/logs/
|
||||||
|
|
||||||
# Ignore decrypted secret material
|
# Ignore decrypted secret material
|
||||||
*.dec.yaml
|
*.dec.yaml
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
YOUTUBE_BACKUP_ROOT=/data/youtube-backup
|
||||||
|
YOUTUBE_BACKUP_PLAYLISTS_FILE=/data/youtube-backup/playlists.txt
|
||||||
|
YOUTUBE_BACKUP_ARCHIVE_FILE=/data/youtube-backup/archive.txt
|
||||||
|
YOUTUBE_BACKUP_VIDEOS_DIR=/data/youtube-backup/videos
|
||||||
|
YOUTUBE_BACKUP_LOG_DIR=/data/youtube-backup/logs
|
||||||
|
|
||||||
|
# For private/unlisted uploads, prefer exported cookies on a headless server:
|
||||||
|
# YOUTUBE_BACKUP_COOKIES_FILE=/data/youtube-backup/cookies.txt
|
||||||
|
YOUTUBE_BACKUP_COOKIES_FILE=
|
||||||
|
|
||||||
|
# Or use browser cookies when running interactively on a desktop:
|
||||||
|
# YOUTUBE_BACKUP_COOKIES_FROM_BROWSER=firefox
|
||||||
|
YOUTUBE_BACKUP_COOKIES_FROM_BROWSER=
|
||||||
|
|
||||||
|
YOUTUBE_BACKUP_FORMAT=bv*+ba/best
|
||||||
|
YOUTUBE_BACKUP_MERGE_FORMAT=mp4
|
||||||
|
YOUTUBE_BACKUP_SUB_LANGS=all,-live_chat
|
||||||
|
YOUTUBE_BACKUP_SLEEP_REQUESTS=1
|
||||||
|
YOUTUBE_BACKUP_RATE_LIMIT=
|
||||||
|
YOUTUBE_BACKUP_EXTRA_ARGS=
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
# YouTube Backup
|
||||||
|
|
||||||
|
Standalone helper for backing up your own uploaded YouTube videos with
|
||||||
|
`yt-dlp`. It is intentionally outside `lab.sh`, OpenTofu, Argo CD, and the
|
||||||
|
homelab deployment pipeline.
|
||||||
|
|
||||||
|
The backup root defaults to `/data/youtube-backup`.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pipx install yt-dlp
|
||||||
|
```
|
||||||
|
|
||||||
|
If thumbnail embedding fails, install `ffmpeg` on the host.
|
||||||
|
|
||||||
|
## Configure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo mkdir -p /data/youtube-backup/{videos,logs}
|
||||||
|
sudo chown -R "$USER:$USER" /data/youtube-backup
|
||||||
|
cp infra/youtube-backup/playlists.example.txt /data/youtube-backup/playlists.txt
|
||||||
|
cp infra/youtube-backup/.env.example infra/youtube-backup/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `/data/youtube-backup/playlists.txt` and add one playlist, channel, or
|
||||||
|
video URL per line.
|
||||||
|
|
||||||
|
For private or unlisted uploads, export browser cookies and set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
YOUTUBE_BACKUP_COOKIES_FILE=/data/youtube-backup/cookies.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, when running on a desktop with a browser profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
YOUTUBE_BACKUP_COOKIES_FROM_BROWSER=firefox
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infra/youtube-backup/backup-youtube.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The script uses:
|
||||||
|
|
||||||
|
```text
|
||||||
|
--download-archive /data/youtube-backup/archive.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
so repeated runs skip videos that were already backed up.
|
||||||
|
|
||||||
|
Downloaded videos and sidecar metadata are written under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/data/youtube-backup/videos
|
||||||
|
```
|
||||||
|
|
||||||
|
Each run also writes a timestamped log under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/data/youtube-backup/logs
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
#!/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}"
|
||||||
|
SUB_LANGS="${YOUTUBE_BACKUP_SUB_LANGS:-all,-live_chat}"
|
||||||
|
MERGE_FORMAT="${YOUTUBE_BACKUP_MERGE_FORMAT:-mp4}"
|
||||||
|
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
|
||||||
|
--write-thumbnail
|
||||||
|
--write-subs
|
||||||
|
--write-auto-subs
|
||||||
|
--sub-langs "${SUB_LANGS}"
|
||||||
|
--embed-metadata
|
||||||
|
--embed-thumbnail
|
||||||
|
--sleep-requests "${SLEEP_REQUESTS}"
|
||||||
|
)
|
||||||
|
|
||||||
|
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}"
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Add one playlist/channel/video URL per line.
|
||||||
|
# For backing up your uploaded videos, use your uploads playlist, a public
|
||||||
|
# playlist you maintain, or the channel videos URL if yt-dlp can access it with
|
||||||
|
# your cookies.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# https://www.youtube.com/playlist?list=YOUR_PLAYLIST_ID
|
||||||
|
# https://www.youtube.com/@YOUR_CHANNEL/videos
|
||||||
Loading…
Reference in New Issue