yt/yt

234 lines
5.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see https://www.gnu.org/licenses/.
set -e
add() { # adds a video to a playlist file
while test -n "$2"; do
if test -z "$(sed -n ";$1;p" "$2")"
then
printf "%s: %s: %s: Video already in playlist.\n" "$argv0" "$2" "$1"
exit
else
cache "$1"
archive "$1"
printf "%s\n" "$1" >> "$2"
fi
shift
done
}
archive() { # archives a video to the Wayback Machine
while test -n "$1"; do
if test -n "$(printf '%s\n' "$1" | grep -e 'web\.archive\.org')"
then
shift
continue
else
wayback_url="$(curl -s "$WBAPI$1" \
| jq .archived_snapshots.closest.url \
| tr -d '"' | sed 's/^http:/https:/g')"
# skips archiving on the Wayback Machine if the video has a snapshot
if [ "$wayback_url" != "null" ]; then shift; continue; fi
printf "%s: %s: Saving video to the Wayback Machine.\n" "$argv0" "$1" 1>&2
curl -s -d "url=$1" https://web.archive.org/{url} >/dev/null
fi
shift
done
}
cache() { # cache the video title for faster retrieval
while test -n "$1"; do
grep "$1" "$cachefile" 2>/dev/null 1>&2 || printf '%s\n' \
"$(yt-dlp -s "$1" --print "$FMT")" >>"$cachefile"
shift
done
}
clone() { # clones a YouTube playlist to a file
yt-dlp --flat-playlist "$1" --print url > "$2"
verify "$2"
}
lines() {
while test -n "$1"; do
sed 's/#.*$//g' "$1"
shift
done
}
music() { # downloads a video, splitting by chapter and only saving the audio
while "$1"; do
yt-dlp -vx --split-chapters -o \
"chapter:%(fulltitle)s - %(section_number)s %(section_title)s.%(ext)s" \
"$1" --audio-quality 0 >> log 2>&1
shift
done
}
pick() { # Pick a video to play from a list of videos
while test -n "$1"; do
for line in $(lines "$1"); do
if test -n "$(printf '%s\n' "$line" | sed -n '/^\#/p')"
then
continue
else
cache "$line"
fi
if test -z "$list"; then
list="$(grep "$line" "$cachefile")"
else
list="$(printf '%s\n%s' "$list" "$(grep "$line" "$cachefile")")"
fi
done
shift
done
chosen="$(printf '%s\n' "$list" | fzf | sed -e 's/.*\[//g' -e 's/\]//g')"
test -n "$chosen" && mpv "$chosen"
}
play() { # play a video after caching its title
cache $@
mpv $@
}
queue() {
while test -n "$1"; do
mpv "$1"
shift
done
}
usage() {
printf "Usage: %s %s\n" "$argv0" "$@" 1>&2
exit 64 # sysexits.h(3) EX_USAGE
}
verify() { # replaces videos with archived versions if they are not available
while test -n "$1"; do
for video in $(lines "$1"); do
if test -n "$(yt-dlp -s "$video" 2>&1 \
| grep -i -e 'video unavailable' -e 'private video' -e 'been removed')"
then
printf "%s: %s: Video unavailable.\n" "$argv0" "$video" 1>&2
wayback_url="$(curl -s "$WBAPI$video" \
| jq -r .archived_snapshots.closest.url \
| sed 's/^http:/https:/g')"
if [ "$wayback_url" = "null" ] \
|| test -n "$(yt-dlp -s "$wayback_url" 2>&1 | grep -e 'not archived')"
then
printf "%s: %s: Video not available on the Wayback Machine.\n" \
"$argv0" "$video" 1>&2
# replace the video link with a comment containing link
content="$(sed "s;^$video;\# $video;g" "$1")"
# write the new content buffer to file
printf '%s\n' "$content" > "$1"
else
printf "%s: %s: Replacing link with Wayback link.\n" \
"$argv0" "$video" 1>&2
content="$(sed "s;^$video;$wayback_url;g" "$1")"
printf '%s\n' "$content" >"$1"
cache "$wayback_url"
fi
else
printf '%s: %s: Video available.\n' "$argv0" "$video" 1>&2
cache "$video"
archive "$video"
fi
done
shift
done
}
argv0="$0"
com="$1"
cachefile="$XDG_CACHE_HOME/yt.cache"
FMT='%(title)s %(channel)s (%(duration>%H:%M:%S)s) [%(webpage_url)s]'
WBAPI='https://archive.org/wayback/available?url='
if test -n "$DEBUG"; then
set -x
fi
for dep in \
curl \
fzf \
jq \
mpv \
yt-dlp
do
if ! command -v "$dep" >/dev/null
then
printf "%s: %s: Missing dependency." "$argv0" "$dep" 1>&2
exit 69 # syexits.h(3) EX_UNAVAILABLE
fi
done
case "$com" in
add)
shift 2>/dev/null || usage 'add uri file'
add "$@"
;;
archive)
shift 2>/dev/null || usage 'archive uri...'
archive "$@"
;;
cache)
shift 2>/dev/null || usage 'cache uri...'
cache "$@"
;;
clone)
shift 2>/dev/null || usage 'clone uri file'
clone "$@"
;;
music)
shift 2>/dev/null || usage 'music uri...'
music "$@"
;;
pick)
shift 2>/dev/null || usage 'pick file...'
pick "$@"
;;
play)
shift 2>/dev/null || usage 'uri...'
play "$@"
;;
queue)
shift 2>/dev/null || usage 'file...'
queue "$@"
;;
verify)
shift 2>/dev/null || usage 'file...'
verify "$@"
;;
*)
usage 'add | clone | music | pick | play | verify'
;;
esac