#!/bin/sh # Copyright (c) 2023 Emma Tebibyte # 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