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
|
2023-11-08 06:33:50 +00:00
|
|
|
|
printf "%s: %s: %s: Video already in playlist.\n" "$argv0" "$2" "$1"
|
2023-11-03 04:09:01 +00:00
|
|
|
|
exit
|
|
|
|
|
else
|
2023-11-15 18:14:39 +00:00
|
|
|
|
cache "$1"
|
2023-11-03 04:09:01 +00:00
|
|
|
|
archive "$1"
|
|
|
|
|
printf "%s\n" "$1" >> "$2"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
shift
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
archive() { # archives a video to the Wayback Machine
|
|
|
|
|
while test -n "$1"; do
|
2023-11-08 06:33:50 +00:00
|
|
|
|
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')"
|
2023-11-03 21:31:56 +00:00
|
|
|
|
|
2023-11-08 06:33:50 +00:00
|
|
|
|
# skips archiving on the Wayback Machine if the video has a snapshot
|
|
|
|
|
if [ "$wayback_url" != "null" ]; then shift; continue; fi
|
2023-11-03 21:31:56 +00:00
|
|
|
|
|
2023-11-08 06:33:50 +00:00
|
|
|
|
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
|
2023-11-03 04:09:01 +00:00
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-03 23:33:44 +00:00
|
|
|
|
clone() { # clones a YouTube playlist to a file
|
|
|
|
|
yt-dlp --flat-playlist "$1" --print url > "$2"
|
2023-11-04 02:10:28 +00:00
|
|
|
|
verify "$2"
|
2023-11-08 06:33:50 +00:00
|
|
|
|
}
|
2023-11-03 04:50:57 +00:00
|
|
|
|
|
2023-11-08 06:33:50 +00:00
|
|
|
|
lines() {
|
|
|
|
|
while test -n "$1"; do
|
|
|
|
|
sed 's/#.*$//g' "$1"
|
|
|
|
|
shift
|
2023-11-03 04:50:57 +00:00
|
|
|
|
done
|
2023-11-03 04:09:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2023-11-08 06:33:50 +00:00
|
|
|
|
for line in $(lines "$1"); do
|
2023-11-03 19:42:15 +00:00
|
|
|
|
if test -n "$(printf '%s\n' "$line" | sed -n '/^\#/p')"
|
|
|
|
|
then
|
|
|
|
|
continue
|
|
|
|
|
else
|
|
|
|
|
cache "$line"
|
|
|
|
|
fi
|
2023-11-03 04:09:01 +00:00
|
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-03 04:50:57 +00:00
|
|
|
|
play() { # play a video after caching its title
|
2023-11-03 06:49:06 +00:00
|
|
|
|
cache $@
|
|
|
|
|
mpv $@
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
queue() {
|
2023-11-03 04:09:01 +00:00
|
|
|
|
while test -n "$1"; do
|
2023-11-03 19:49:18 +00:00
|
|
|
|
mpv "$1"
|
|
|
|
|
shift
|
2023-11-03 04:09:01 +00:00
|
|
|
|
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
|
2023-11-08 06:33:50 +00:00
|
|
|
|
for video in $(lines "$1"); do
|
2023-11-03 04:09:01 +00:00
|
|
|
|
if test -n "$(yt-dlp -s "$video" 2>&1 \
|
2023-11-08 06:33:50 +00:00
|
|
|
|
| grep -i -e 'video unavailable' -e 'private video' -e 'been removed')"
|
2023-11-03 04:09:01 +00:00
|
|
|
|
then
|
|
|
|
|
printf "%s: %s: Video unavailable.\n" "$argv0" "$video" 1>&2
|
|
|
|
|
|
|
|
|
|
wayback_url="$(curl -s "$WBAPI$video" \
|
2023-11-03 23:33:44 +00:00
|
|
|
|
| jq -r .archived_snapshots.closest.url \
|
|
|
|
|
| sed 's/^http:/https:/g')"
|
2023-11-03 04:09:01 +00:00
|
|
|
|
|
2023-11-03 23:33:44 +00:00
|
|
|
|
if [ "$wayback_url" = "null" ] \
|
2023-11-04 02:10:28 +00:00
|
|
|
|
|| test -n "$(yt-dlp -s "$wayback_url" 2>&1 | grep -e 'not archived')"
|
2023-11-03 23:33:44 +00:00
|
|
|
|
then
|
2023-11-04 02:10:28 +00:00
|
|
|
|
printf "%s: %s: Video not available on the Wayback Machine.\n" \
|
2023-11-03 04:09:01 +00:00
|
|
|
|
"$argv0" "$video" 1>&2
|
|
|
|
|
# replace the video link with a comment containing link
|
2023-11-08 06:33:50 +00:00
|
|
|
|
content="$(sed "s;^$video;\# $video;g" "$1")"
|
2023-11-03 04:09:01 +00:00
|
|
|
|
|
2023-11-04 02:10:28 +00:00
|
|
|
|
# write the new content buffer to file
|
2023-11-03 04:09:01 +00:00
|
|
|
|
printf '%s\n' "$content" > "$1"
|
|
|
|
|
else
|
|
|
|
|
printf "%s: %s: Replacing link with Wayback link.\n" \
|
|
|
|
|
"$argv0" "$video" 1>&2
|
|
|
|
|
|
2023-11-04 02:10:28 +00:00
|
|
|
|
content="$(sed "s;^$video;$wayback_url;g" "$1")"
|
2023-11-03 04:09:01 +00:00
|
|
|
|
printf '%s\n' "$content" >"$1"
|
|
|
|
|
cache "$wayback_url"
|
|
|
|
|
fi
|
2023-11-03 04:50:57 +00:00
|
|
|
|
else
|
2023-11-04 02:10:28 +00:00
|
|
|
|
printf '%s: %s: Video available.\n' "$argv0" "$video" 1>&2
|
2023-11-03 04:50:57 +00:00
|
|
|
|
cache "$video"
|
2023-11-08 06:33:50 +00:00
|
|
|
|
archive "$video"
|
2023-11-03 04:09:01 +00:00
|
|
|
|
fi
|
|
|
|
|
done
|
2023-11-04 02:10:28 +00:00
|
|
|
|
|
2023-11-03 04:09:01 +00:00
|
|
|
|
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='
|
|
|
|
|
|
2023-11-04 02:10:28 +00:00
|
|
|
|
if test -n "$DEBUG"; then
|
|
|
|
|
set -x
|
|
|
|
|
fi
|
|
|
|
|
|
2023-11-03 04:09:01 +00:00
|
|
|
|
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)
|
2023-11-03 05:51:42 +00:00
|
|
|
|
shift 2>/dev/null || usage 'add uri file'
|
2023-11-03 04:09:01 +00:00
|
|
|
|
add "$@"
|
|
|
|
|
;;
|
2023-11-03 21:31:56 +00:00
|
|
|
|
archive)
|
|
|
|
|
shift 2>/dev/null || usage 'archive uri...'
|
|
|
|
|
archive "$@"
|
|
|
|
|
;;
|
2023-11-03 04:09:01 +00:00
|
|
|
|
cache)
|
|
|
|
|
shift 2>/dev/null || usage 'cache uri...'
|
|
|
|
|
cache "$@"
|
|
|
|
|
;;
|
|
|
|
|
clone)
|
2023-11-03 05:51:42 +00:00
|
|
|
|
shift 2>/dev/null || usage 'clone uri file'
|
2023-11-03 04:09:01 +00:00
|
|
|
|
clone "$@"
|
|
|
|
|
;;
|
|
|
|
|
music)
|
2023-11-03 05:51:42 +00:00
|
|
|
|
shift 2>/dev/null || usage 'music uri...'
|
2023-11-03 04:09:01 +00:00
|
|
|
|
music "$@"
|
|
|
|
|
;;
|
|
|
|
|
pick)
|
2023-11-03 05:51:42 +00:00
|
|
|
|
shift 2>/dev/null || usage 'pick file...'
|
2023-11-03 04:09:01 +00:00
|
|
|
|
pick "$@"
|
|
|
|
|
;;
|
|
|
|
|
play)
|
2023-11-03 05:51:42 +00:00
|
|
|
|
shift 2>/dev/null || usage 'uri...'
|
2023-11-03 04:09:01 +00:00
|
|
|
|
play "$@"
|
|
|
|
|
;;
|
2023-11-03 06:49:06 +00:00
|
|
|
|
queue)
|
|
|
|
|
shift 2>/dev/null || usage 'file...'
|
|
|
|
|
queue "$@"
|
2023-11-03 19:23:51 +00:00
|
|
|
|
;;
|
2023-11-03 04:09:01 +00:00
|
|
|
|
verify)
|
2023-11-03 05:51:42 +00:00
|
|
|
|
shift 2>/dev/null || usage 'file...'
|
2023-11-03 04:09:01 +00:00
|
|
|
|
verify "$@"
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
usage 'add | clone | music | pick | play | verify'
|
|
|
|
|
;;
|
|
|
|
|
esac
|