#! /bin/bash
#
# jptgdp - Just Play the Goddamn Playlist
# Copyright (C) 2019 Vintage Salt <rehashedsalt@cock.li>
#
# 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 <file>		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 <directory>	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 "$@"