#! /bin/bash # # jptgdp - Just Play the Goddamn Playlist # Copyright (C) 2019 Vintage Salt # # Distributed under terms of the MIT license. # # TODO: # * Provide integration with other players, like mpd # * Add a quick way to append to a playlist # Variables _name="$(basename -- "$0")" _tmpdir="${XDG_CACHE_HOME:-$HOME/.cache}/$_name" _tmpfile="$_tmpdir/tmpfile" _xdguserdirs="${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" [ -f "$_xdguserdirs" ] && source "$_xdguserdirs" _musicdir="${XDG_MUSIC_DIR:-$HOME/Music}/JPTGDP Songs" # Helper functions log() { [ -z "$1" ] && return 1 printf "%s: %s\\n" \ "$_name" \ "$1" } error() { [ -z "$1" ] && return 1 [ -z "$2" ] && return 2 log "$1" exit "${2:-1}" } validatedeps() { for prog in "$@"; do if ! command -v "$prog" > /dev/null 2>&1; then _return="$prog" return 1 fi done return 0 } sanitize() { [ -z "$1" ] && return 1 _return="${1//[^ a-zA-Z0-9\[\]|()_-]/}" } # Traps trapexit() { kill $(jobs -p) > /dev/null 2>&1 [ -n "$_tmpdir" ] && rm "$_tmpfile"* > /dev/null 2>&1 } # Critical functions clearcache() { [ -n "$_musicdir" ] && rm "$_musicdir"/* > /dev/null 2>&1 log "Cache has been emptied" } helptext() { cat << EOF Usage: $_name [OPTION] Use youtube-dl and audacious to queue up a playlist given a file of only search queries. The first result found is the one that will be downloaded. Downloaded files are cached in your Music folder under "JPTGDP Songs" for offline use. -f The playlist file to load. The file should be plaintext containing a YouTube search query on each line -c Clears the cache (which can become quite large) -d Download only; don't queue anything up Conflicts with -p -p Play the playlist after it is enqueued. Conflicts with -d -s Shuffle the playlist -r Start up rofi, if installed, and present a listing of all .gdp files in the given directory -h Print this help text Copyright (c) 2019 rehashedsalt@cock.li Licensed under the MIT license EOF } rofimenu() { validatedeps rofi || error "$_return is not currently installed" 1 [ -d "$_optrofi" ] || error "Could not open directory \"$_optrofi\"" 2 files=$(find "$_optrofi" -type f -name \*.gdp) if [ -n "$files" ]; then # Strip file suffixes for a cleaner menu playlists="" while read file; do filebase="$(basename -- "$file")" filebase="${filebase%.gdp}" [ -n "$playlists" ] && playlists+=$'\n' playlists+="$filebase" done <<< "$files" else error "No playlists found" 61 fi choice="$(rofi -dmenu -i -p "$_name" <<< "$playlists")" [ -z "$choice" ] && error "User aborted at selection" 62 playlist "$_optrofi"/"$choice".gdp return 0 } playlist() { [ -z "$1" ] && return 1 [ -f "$1" ] || error "Attempted to access non-file playlist \"$1\"" 50 [ -r "$1" ] || error "Cannot read playlist \"$1\"" 51 while read line; do [ -z "$line" ] && continue rm "$_tmpfile"* > /dev/null 2>&1 sanitize "$line" filename="$_musicdir/$_return" if ! [ -f "$filename" ]; then log "Finding a song for \"$line\"" youtube-dl \ --add-metadata \ --audio-format "best" \ --geo-bypass \ --playlist-items 1 \ -x \ -o "$_tmpfile.%(ext)s" \ ytsearch:"$line" \ > /dev/null 2>&1 & if wait $!; then mv "$_tmpfile"* "$filename" else log "No results found for \"$line\"" fi fi [ -z "$_optdownloadonly" ] && audacious -e "$filename" if [ -n "$_optautoplay" ]; then audacious -p unset _optautoplay fi done < <(if [ -n "$_optshuffle" ]; then shuf "$1"; else cat "$1"; fi) if [ -z "$_optdownloadonly" ]; then log "Finished building queue" else log "Finished downloading" fi } # Main main() { # Boostrapping and setup validatedeps youtube-dl basename || error "Critical dependency $_return was not met" 1 mkdir -p "$_tmpdir" mkdir -p "$_musicdir" trap trapexit EXIT # Actual program stuff while getopts ":cdf:pr:sh" opt; do case $opt in c) clearcache exit $? ;; d) _optdownloadonly=1 ;; f) _optfile="$OPTARG" ;; p) _optautoplay=1 ;; r) _optrofi="$OPTARG" ;; s) _optshuffle=1 ;; h) helptext exit $? ;; :) error "Option requires argument: -$OPTARG" 2 ;; *) error "Invalid option: -$OPTARG" 2 ;; esac done [ -n "$_optfile" ] && [ -n "$_optrofi" ] && error "Flags -f and -r conflict" 2 [ -n "$_optdownloadonly" ] && [ -n "$_optautoplay" ] && error "Flags -d and -p conflict" 2 if [ -n "$_optrofi" ]; then rofimenu; exit $?; fi if [ -n "$_optfile" ]; then playlist "$_optfile"; exit $?; fi error "Nothing to do" 0 } main "$@"