yt/yt

186 lines
4.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: 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