yt/yt

186 lines
4.3 KiB
Plaintext
Raw Normal View History

2023-11-03 04:09:01 +00:00
#!/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: Video already in playlist.\n" "$argv0" "$1"
exit
else
archive "$1"
printf "%s\n" "$1" >> "$2"
fi
shift
done
}
archive() { # archives a video to the Wayback Machine
while test -n "$1"; do
curl -s -d "url=$1" https://web.archive.org/{url} >/dev/null
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 in m3u format
yt-dlp --flat-playlist "$1" --print url > "$2.m3u"
verify "$2.m3u"
}
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 $(cat "$1"); do
cache "$line"
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 playlist or video after caching its title
while test -n "$1"; do
cache "$1"
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 $(cat "$1"); do
# test for unavailability
if test -n "$(yt-dlp -s "$video" 2>&1 \
| sed -n -e '/Video unavailable/p' -e '/Private video/p' )"
then
printf "%s: %s: Video unavailable.\n" "$argv0" "$video" 1>&2
wayback_url="$(curl -s "$WBAPI$video" \
| jq .archived_snapshots.closest.url \
| tr -d '"' | sed 's/^http:/https:/g')"
if [ "$wayback_url" = "null" ]; then
printf "%s: %s is 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 the 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
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='
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 2>/dev/null || usage 'add uri file'
add "$@"
;;
cache)
shift 2>/dev/null || usage 'cache uri...'
cache "$@"
;;
clone)
shift 2 2>/dev/null || usage 'clone uri file'
clone "$@"
;;
music)
shift 1 2>/dev/null || usage 'music uri...'
music "$@"
;;
pick)
shift 1 2>/dev/null || usage 'pick file...'
pick "$@"
;;
play)
shift 1 2>/dev/null || usage 'uri...'
play "$@"
;;
verify)
shift 1 2>/dev/null || usage 'file...'
verify "$@"
;;
*)
usage 'add | clone | music | pick | play | verify'
;;
esac