552 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			552 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#! /bin/bash
 | 
						|
#
 | 
						|
# ptgdp - Play the Goddamned Playlist
 | 
						|
# Copyright (C) 2019 Vintage Salt <rehashedsalt@cock.li>
 | 
						|
#
 | 
						|
# Distributed under terms of the MIT license.
 | 
						|
#
 | 
						|
 | 
						|
# TODO:
 | 
						|
# * Reorganize mpd integration into abstract functions
 | 
						|
 | 
						|
# Variables
 | 
						|
_name="$(basename -- "$0")"
 | 
						|
_tmpdir="${XDG_CACHE_HOME:-$HOME/.cache}/$_name"
 | 
						|
_tmpfile="$_tmpdir/tmpfile-$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 12)"
 | 
						|
_tmpplaylist="$_tmpdir/tmpplaylist-$(cat /dev/urandom | tr -cd 'a-f0-9' | head -c 12)"
 | 
						|
_xdguserdirs="${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
 | 
						|
[ -z "$XDG_MUSIC_DIR" ] && [ -f "$_xdguserdirs" ] && source "$_xdguserdirs"
 | 
						|
_musicdir="${XDG_MUSIC_DIR:-$HOME/Music}"
 | 
						|
_ptgdpmusicdir="$_musicdir/${PTGDP_MUSIC_DIR:-PTGDP Songs}"
 | 
						|
declare -a _queue
 | 
						|
declare -a _playlists
 | 
						|
 | 
						|
# Preprocessor-accessible vars
 | 
						|
declare p_ytdlargs="--geo-bypass"
 | 
						|
declare p_search="ytsearch:"
 | 
						|
 | 
						|
# Helper functions
 | 
						|
log() {
 | 
						|
	[ -z "$1" ] && return 1 # Message body
 | 
						|
	printf "%s\\n" \
 | 
						|
		"$1"
 | 
						|
}
 | 
						|
error() {
 | 
						|
	[ -z "$1" ] && return 1 # Message body
 | 
						|
	# 2: Exit code
 | 
						|
	printf "\\e[31m%s\\e[0m\\n" \
 | 
						|
		"$1"
 | 
						|
	[ -n "$2" ] && exit "${2:-1}"
 | 
						|
}
 | 
						|
notify() {
 | 
						|
	[ -z "$_optrofi" ] && return 0
 | 
						|
	[ -z "$1" ] && return 1 # Title
 | 
						|
	[ -z "$2" ] && return 2 # Body
 | 
						|
	local urg="${3:-normal}" # Urgency
 | 
						|
	local icon="${4:-dialog-information}" # Icon
 | 
						|
	local timeout="${5:-3000}" # Timeout in milliseconds
 | 
						|
	notify-send \
 | 
						|
		-a "$_name" \
 | 
						|
		-i "$icon" \
 | 
						|
		-u "$urg" \
 | 
						|
		-t "$timeout" \
 | 
						|
		"$1" \
 | 
						|
		"$2" > /dev/null 2>&1
 | 
						|
}
 | 
						|
execpreprocessor() {
 | 
						|
	# Execute a preprocessor line
 | 
						|
	# $1: The line, either stripped or not of the leading #:
 | 
						|
	line="${1#\#:}"
 | 
						|
	read -r -a args <<< "$line"
 | 
						|
	case ${args[0]} in
 | 
						|
		ytdlargs)
 | 
						|
			local old="$p_ytdlargs"
 | 
						|
			p_ytdlargs="${args[@]:1}"
 | 
						|
			if ! [ "$p_ytdlargs" = "$old" ]; then
 | 
						|
				log "Setting youtube-dl arguments \"$p_ytdlargs\""
 | 
						|
			fi
 | 
						|
			;;
 | 
						|
		search)
 | 
						|
			local old="$p_search"
 | 
						|
			p_search="${args[@]:1}"
 | 
						|
			if [ "$p_search" = "${p_search%:}" ]; then
 | 
						|
				p_search="${p_search}:"
 | 
						|
			fi
 | 
						|
			if ! [ "$p_search" = "$old" ]; then
 | 
						|
				log "Setting default search method \"$p_search\""
 | 
						|
			fi
 | 
						|
			;;
 | 
						|
		*)
 | 
						|
			error "Preprocessor command \"${args[0]}\" not implemented"
 | 
						|
			;;
 | 
						|
	esac
 | 
						|
}
 | 
						|
checkforsong() {
 | 
						|
	# $1: A song name to validate
 | 
						|
	[ -z "$1" ] && return 1
 | 
						|
	# Very rudimentary implementation
 | 
						|
	# TODO: Make this a lot more thorough, maybe ensure MIME is good
 | 
						|
	sanitize "$1"
 | 
						|
	for file in "$_ptgdpmusicdir/$_return"*; do
 | 
						|
		if [ -f "$file" ]; then
 | 
						|
			_return="$file"
 | 
						|
			return 0
 | 
						|
		fi
 | 
						|
	done
 | 
						|
	unset _return
 | 
						|
	return 1
 | 
						|
}
 | 
						|
cachesong() {
 | 
						|
	# $1: A song query to download
 | 
						|
	# $_return: The relative file path to song from XDG_MUSIC_DIR
 | 
						|
	# If in a dry run, $_return is the title of the video, if extracted
 | 
						|
	# Note that this will return 100 if the file is cached, so do not assume only 0 is success
 | 
						|
	[ -z "$1" ] && return 1
 | 
						|
	# Clean up tmpfile
 | 
						|
	rm "$_tmpfile"* > /dev/null 2>&1
 | 
						|
	sanitize "$1"
 | 
						|
	filename="$_ptgdpmusicdir/$_return"
 | 
						|
	if [ -z "$_optdryrun" ]; then
 | 
						|
		if ! checkforsong "$_return"; then
 | 
						|
			# We need to download the song
 | 
						|
			youtube-dl \
 | 
						|
				--add-metadata \
 | 
						|
				--audio-format "best" \
 | 
						|
				--default-search "$p_search" \
 | 
						|
				--playlist-items 1 \
 | 
						|
				"$p_ytdlargs" \
 | 
						|
				-x \
 | 
						|
				-o "$_tmpfile.%(ext)s" \
 | 
						|
				"$1" \
 | 
						|
				> /dev/null 2>&1 &
 | 
						|
			if wait $!; then
 | 
						|
				for file in "$_tmpfile"*; do
 | 
						|
					[ -f "$file" ] && local extension="$file"
 | 
						|
					break
 | 
						|
				done
 | 
						|
				extension="${extension##*/}"
 | 
						|
				extension="${extension##*.}"
 | 
						|
				filename="$filename.$extension"
 | 
						|
				[ -f "$_tmpfile"* ] || return 1
 | 
						|
				mv "$_tmpfile"* "$filename"
 | 
						|
				_return="$filename"
 | 
						|
				return 0
 | 
						|
			else
 | 
						|
				return 1
 | 
						|
			fi
 | 
						|
		else
 | 
						|
			#_return is already an existing cached file, so just die
 | 
						|
			return 100
 | 
						|
		fi
 | 
						|
	else
 | 
						|
		output="$(
 | 
						|
			youtube-dl \
 | 
						|
				--default-search "ytsearch:" \
 | 
						|
				--get-title \
 | 
						|
				--geo-bypass \
 | 
						|
				--playlist-items 1 \
 | 
						|
				"$1" 2>&1
 | 
						|
		)"
 | 
						|
		exitcode="$?"
 | 
						|
		if [ $exitcode -gt 0 ]; then
 | 
						|
			return 1
 | 
						|
		fi
 | 
						|
		if ! [ "$output" = "${output#*WARNING}" ]; then
 | 
						|
			# Title could not be extracted
 | 
						|
			unset _return
 | 
						|
		else
 | 
						|
			# Title was found
 | 
						|
			_return="$output"
 | 
						|
		fi
 | 
						|
		return 0
 | 
						|
	fi
 | 
						|
}
 | 
						|
queuesong() {
 | 
						|
	# $1: A song query to queue
 | 
						|
	# returns 0 if successful, 100 if cached, 1 if error
 | 
						|
	# Adds a song to $_queue
 | 
						|
	[ -z "$1" ] && return 2
 | 
						|
	cachesong "$1"
 | 
						|
	local errorcode=$?
 | 
						|
	if [ -z "$_optdryrun" ]; then
 | 
						|
		case $errorcode in
 | 
						|
			0)
 | 
						|
				# Downloaded
 | 
						|
				log "Downloaded song \"$1\""
 | 
						|
				;;
 | 
						|
			100)
 | 
						|
				# Cached
 | 
						|
				log "Using cached song \"$1\""
 | 
						|
				;;
 | 
						|
			*)
 | 
						|
				# Failed
 | 
						|
				dlfailure+=1
 | 
						|
				notify "Could not download song" \
 | 
						|
					"youtube-dl did not download a song for \"$1\"" \
 | 
						|
					normal dialog-error 3000
 | 
						|
				error "Could not download song \"$1\""
 | 
						|
				;;
 | 
						|
		esac
 | 
						|
		_queue+=("$_return")
 | 
						|
	else
 | 
						|
		case $errorcode in
 | 
						|
			0)
 | 
						|
				# Success
 | 
						|
				if [ -n "$_return" ]; then
 | 
						|
					log "$1 - \"$output\""
 | 
						|
				else
 | 
						|
					log "$1 parsed, but title could not be extracted"
 | 
						|
				fi
 | 
						|
				;;
 | 
						|
			*)
 | 
						|
				# Failure
 | 
						|
				error "Could not find song \"$1\""
 | 
						|
				;;
 | 
						|
		esac
 | 
						|
	fi
 | 
						|
	return $errorcode
 | 
						|
}
 | 
						|
execqueue() {
 | 
						|
	# Plays all songs in _queue
 | 
						|
	mpc update --wait > /dev/null 2>&1
 | 
						|
	for file in "${_queue[@]}"; do
 | 
						|
		file=${file##$_musicdir/}
 | 
						|
		mpc add "$file" > /dev/null 2>&1 || error "Could not add file: \"$file\""
 | 
						|
	done
 | 
						|
}
 | 
						|
execplaylists() {
 | 
						|
	# Plays all playlists in _playlists
 | 
						|
	if [ -n "$_optquickplay" ]; then
 | 
						|
		for line in "${_playlists[@]}"; do
 | 
						|
			printf "%s\\n" "$line" >> "$_tmpplaylist"
 | 
						|
		done
 | 
						|
		_playlists=("$_tmpplaylist")
 | 
						|
	fi
 | 
						|
	for list in "${_playlists[@]}"; do
 | 
						|
		playlist "$list" || error "Failed to play playlist: \"$list\""
 | 
						|
	done
 | 
						|
	if [ -n "$_optautoplay" ] && [ -z "$_optdryrun" ]; then
 | 
						|
		mpc play > /dev/null 2>&1
 | 
						|
		log "Started playback"
 | 
						|
		unset _optautoplay
 | 
						|
	fi
 | 
						|
}
 | 
						|
validatedeps() {
 | 
						|
	# $@: Dependencies to validate
 | 
						|
	for prog in "$@"; do
 | 
						|
		if ! command -v "$prog" > /dev/null 2>&1; then
 | 
						|
			_return="$prog"
 | 
						|
			return 1
 | 
						|
		fi
 | 
						|
	done
 | 
						|
	return 0
 | 
						|
}
 | 
						|
validateline() {
 | 
						|
	# $1: A line to check for comments or whitespace
 | 
						|
	# Strictly speaking, this removes all whitespace from the line
 | 
						|
	# While not *exactly* what I'm looking for, it's sufficient for trimming whitespace lines
 | 
						|
	local linenows=${1//[[:space:]]}
 | 
						|
	# Preprocessor commands
 | 
						|
	if ! [ "${1#\#:}" = "$1" ]; then
 | 
						|
		return 100
 | 
						|
	fi
 | 
						|
	# Comments and whitespace-only lines
 | 
						|
	if ! [ "${1#\#}" = "$1" ] || [ -z "$linenows" ]; then
 | 
						|
		return 1
 | 
						|
	fi
 | 
						|
	return 0
 | 
						|
}
 | 
						|
validateplaylistenv() {
 | 
						|
	[ -z "$1" ] && return 1
 | 
						|
	[ -e "$1" ] || error "Playlist \"$1\" does not exist" 50
 | 
						|
	[ -f "$1" ] || error "Playlist \"$1\" is not a file" 50
 | 
						|
	[ -r "$1" ] || error "Cannot read playlist \"$1\"" 51
 | 
						|
	if [ -z "$_optdownloadonly" ] && ! mpc status > /dev/null 2>&1; then
 | 
						|
		notify "Could not communicate with MPD" \
 | 
						|
			"MPD connection was refused. Ensure your configuration is correct and the daemon is currently running." \
 | 
						|
			normal dialog-error 3000
 | 
						|
		error "Failed to communicate with MPD" 52
 | 
						|
	fi
 | 
						|
}
 | 
						|
sanitize() {
 | 
						|
	[ -z "$1" ] && return 1 # String to strip special chars from
 | 
						|
	_return="${1//[^ a-zA-Z0-9\[\]|()_-:]/}"
 | 
						|
}
 | 
						|
 | 
						|
# Traps
 | 
						|
trapexit() {
 | 
						|
	kill $(jobs -p) > /dev/null 2>&1
 | 
						|
	[ -n "$_tmpdir" ] && rm "$_tmpfile"* > /dev/null 2>&1
 | 
						|
	[ -f "$_tmpplaylist" ] && rm "$_tmpplaylist" > /dev/null 2>&1
 | 
						|
}
 | 
						|
 | 
						|
# Critical functions
 | 
						|
clearcache() {
 | 
						|
	[ -n "$_ptgdpmusicdir" ] && rm "$_ptgdpmusicdir"/* > /dev/null 2>&1
 | 
						|
	log "Cache has been emptied"
 | 
						|
}
 | 
						|
helptext() {
 | 
						|
	cat << EOF
 | 
						|
Usage: $_name [OPTION]... [FILE]...
 | 
						|
Use youtube-dl and a music player to queue up or download a number of songs
 | 
						|
given plaintext FILEs with only search queries.
 | 
						|
 | 
						|
  -c			Clears the cache (which can become quite large)
 | 
						|
  -d			Download only; don't queue anything up
 | 
						|
  -D			Dry run; parse out all songs, downloaded or not, and
 | 
						|
			print out the resolved names
 | 
						|
  -p			Play the playlist after it is enqueued
 | 
						|
  -q			Interpret all arguments as songs, not playlists. A
 | 
						|
			temporary playlist will be created with all arguments
 | 
						|
			and executed as normal
 | 
						|
  -s			Shuffle the playlist before enqueueing
 | 
						|
  -r <directory>	Start up rofi, if installed, and present a listing of
 | 
						|
			all .gdp files in the given directory. If notify-send
 | 
						|
			is installed, this will also send notifications
 | 
						|
			pertaining to playlist status.
 | 
						|
  -R <directory|file>	As above, but also parse out the selected playlist and
 | 
						|
			present a listing of individual songs. The argument can
 | 
						|
			be a file in this mode
 | 
						|
  -h			Print this help text
 | 
						|
 | 
						|
Preprocessor Commands
 | 
						|
 | 
						|
Any line prefixed with #: is interpreted as a preprocessor command. Commands
 | 
						|
are IFS-separated with everything after the first word interpreted as arguments.
 | 
						|
 | 
						|
  search		Set the default search prefix, with or without a
 | 
						|
			trailing colon.
 | 
						|
  ytdlargs		A set of arguments to pass to youtube-dl
 | 
						|
 | 
						|
Environment Variables
 | 
						|
 | 
						|
In addition to XDG-spec variables (XDG_CACHE_HOME, user-dirs.dirs, etc.),
 | 
						|
$_name also respects an additional variable:
 | 
						|
 | 
						|
  PTGDP_MUSIC_DIR	The subdirectory in XDG_MUSIC_DIR to save music to
 | 
						|
 | 
						|
Copyright (c) 2019 rehashedsalt@cock.li
 | 
						|
Licensed under the MIT license
 | 
						|
EOF
 | 
						|
}
 | 
						|
rofimenu() {
 | 
						|
	validatedeps rofi || error "$_return is not currently installed" 1
 | 
						|
	if ! [ -d "$_optrofi" ]; then
 | 
						|
		if [ -z "$_optrofisong" ]; then
 | 
						|
			error "Could not open directory \"$_optrofi\"" 2
 | 
						|
		else
 | 
						|
			[ -f "$_optrofi" ] || error "Could not open file \"$_optrofi\"" 2
 | 
						|
			rofiplaysong "$_optrofi"
 | 
						|
			return $?
 | 
						|
		fi
 | 
						|
	fi
 | 
						|
	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
 | 
						|
		notify "No playlists found" \
 | 
						|
			"No playlists could be found in directory \"$_optrofi\"." \
 | 
						|
			normal dialog-error 5000
 | 
						|
		error "No playlists found" 61
 | 
						|
	fi
 | 
						|
	prompt="$_name list"
 | 
						|
	[ -n "$_optautplay" ] && prompt="$prompt ap"
 | 
						|
	choice="$(rofi -dmenu -i -p "$prompt" <<< "$playlists" 2>/dev/null)"
 | 
						|
	[ -z "$choice" ] && error "User aborted at selection" 62
 | 
						|
	if [ -z "$_optrofisong" ]; then
 | 
						|
		_playlists+=("$_optrofi"/"$choice".gdp)
 | 
						|
		execplaylists
 | 
						|
	else
 | 
						|
		rofiplaysong "$_optrofi"/"$choice".gdp
 | 
						|
	fi
 | 
						|
	return 0
 | 
						|
}
 | 
						|
rofiplaysong() {
 | 
						|
	validateplaylistenv "$1" || return $?
 | 
						|
	local songs=""
 | 
						|
	while read line; do
 | 
						|
		validateline "$line" || continue
 | 
						|
		if [ -z "$songs" ]; then
 | 
						|
			songs="$line"
 | 
						|
		else
 | 
						|
			songs="$songs"$'\n'"$line"
 | 
						|
		fi
 | 
						|
	done < "$1"
 | 
						|
	IFS=$'\n'
 | 
						|
	prompt="$_name song"
 | 
						|
	[ -n "$_optautplay" ] && prompt="$prompt ap"
 | 
						|
	choice="$(rofi -dmenu -i -p "$prompt" <<< "$songs" 2>/dev/null)"
 | 
						|
	if [ -z "$choice" ]; then
 | 
						|
		# Assume user is looking for another list
 | 
						|
		# Kick him back to the main menu, unless he picked a file
 | 
						|
		if ! [ "$_optrofi" = "$1" ]; then
 | 
						|
			rofimenu
 | 
						|
		else
 | 
						|
			error "User aborted at selection" 62
 | 
						|
		fi
 | 
						|
	fi
 | 
						|
	queuesong "$choice"
 | 
						|
	[ "$?" = "1" ] && return 62
 | 
						|
	execqueue
 | 
						|
	return 0
 | 
						|
}
 | 
						|
playlist() {
 | 
						|
	validateplaylistenv "$1" || return $?
 | 
						|
	[ -z "$_optdryrun" ] && local -a queue # An array of songs to later enqueue into mpd
 | 
						|
	local -i dlexist=0
 | 
						|
	local -i dlsuccess=0
 | 
						|
	local -i dlfailure=0
 | 
						|
	local -i maxlines=0
 | 
						|
	while read line; do
 | 
						|
		validateline "$line" || continue
 | 
						|
		maxlines+=1
 | 
						|
	done < "$1"
 | 
						|
	[ -z "$_optquickplay" ] && log "Parsed playlist \"$1\" with $maxlines songs"
 | 
						|
	# Store old preprocessor settings
 | 
						|
	declare -a oldsettings
 | 
						|
	while read var; do
 | 
						|
		oldsettings+=("$(declare -p "$var")")
 | 
						|
	done < <(compgen -v "p_")
 | 
						|
	# Parse out the playlist
 | 
						|
	while read line; do
 | 
						|
		validateline "$line"
 | 
						|
		case $? in
 | 
						|
			0)
 | 
						|
				rm "$_tmpfile"* > /dev/null 2>&1
 | 
						|
				# Do the do
 | 
						|
				queuesong "$line"
 | 
						|
				# What did we do?
 | 
						|
				local errorcode=$?
 | 
						|
				case $errorcode in
 | 
						|
					0)
 | 
						|
						dlsuccess+=1
 | 
						|
						;;
 | 
						|
					100)
 | 
						|
						dlexist+=1
 | 
						|
						;;
 | 
						|
					*)
 | 
						|
						dlfailure+=1
 | 
						|
						;;
 | 
						|
				esac
 | 
						|
				;;
 | 
						|
			100)
 | 
						|
				execpreprocessor "$line"
 | 
						|
				;;
 | 
						|
			*)
 | 
						|
				continue
 | 
						|
				;;
 | 
						|
		esac
 | 
						|
	done < <(
 | 
						|
	if [ -n "$_optshuffle" ]; then
 | 
						|
		shuf "$1"
 | 
						|
	else
 | 
						|
		cat "$1"
 | 
						|
	fi
 | 
						|
	)
 | 
						|
	# Return to normality
 | 
						|
	for var in "${oldsettings[@]}"; do
 | 
						|
		eval "$var"
 | 
						|
	done
 | 
						|
	# Dump some stats
 | 
						|
	if [ "$dlexist" = "0" ] && [ "$dlsuccess" = "0" ] && [ -z "$_optdryrun" ]; then
 | 
						|
		notify "Failed to download playlist" \
 | 
						|
			"The playlist in its entirety could not be downloaded. Ensure that youtube-dl is up to date, you have a valid internet connection, and your search queries pull up results" \
 | 
						|
			normal dialog-error 10000
 | 
						|
	elif [ -z "$_optdryrun" ]; then
 | 
						|
		if ! [ "$dlfailure" = "0" ]; then
 | 
						|
			error "Some songs failed to download" 0
 | 
						|
		fi
 | 
						|
		if [ -z "$_optdownloadonly" ]; then
 | 
						|
			notify "Finished downloading playlist" \
 | 
						|
				"The playlist has been downloaded and will be added to mpd shortly"
 | 
						|
			log "Finished downloading: $dlexist cached, $dlsuccess downloaded, $dlfailure failed"
 | 
						|
			execqueue
 | 
						|
		else
 | 
						|
			notify "Finished precaching" \
 | 
						|
				"Your songs have been cached and are ready for offline playback"
 | 
						|
			log "Finished downloading: $dlexist cached, $dlsuccess downloaded, $dlfailure failed"
 | 
						|
		fi
 | 
						|
	else
 | 
						|
		log "Finished dry run: $dlsuccess succeeded, $dlfailure failed"
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
# Main
 | 
						|
main() {
 | 
						|
	# Boostrapping and setup
 | 
						|
	validatedeps youtube-dl basename || error "Critical dependency $_return was not met" 1
 | 
						|
	mkdir -p "$_tmpdir"
 | 
						|
	mkdir -p "$_ptgdpmusicdir"
 | 
						|
	trap trapexit EXIT
 | 
						|
 | 
						|
	# Get options
 | 
						|
	while getopts ":cdDf:pqr:R:sh" opt; do
 | 
						|
		case $opt in
 | 
						|
			c)
 | 
						|
				clearcache
 | 
						|
				exit $?
 | 
						|
				;;
 | 
						|
			d)
 | 
						|
				_optdownloadonly=1
 | 
						|
				;;
 | 
						|
			D)
 | 
						|
				_optdryrun=1
 | 
						|
				;;
 | 
						|
			p)
 | 
						|
				_optautoplay=1
 | 
						|
				;;
 | 
						|
			q)
 | 
						|
				_optquickplay=1
 | 
						|
				;;
 | 
						|
			r)
 | 
						|
				_optrofi="$OPTARG"
 | 
						|
				;;
 | 
						|
			R)
 | 
						|
				_optrofi="$OPTARG"
 | 
						|
				_optrofisong=1
 | 
						|
				;;
 | 
						|
			s)
 | 
						|
				_optshuffle=1
 | 
						|
				;;
 | 
						|
			h)
 | 
						|
				helptext
 | 
						|
				exit $?
 | 
						|
				;;
 | 
						|
			:)
 | 
						|
				error "Option requires argument: -$OPTARG" 2
 | 
						|
				;;
 | 
						|
			*)
 | 
						|
				error "Invalid option: -$OPTARG" 2
 | 
						|
				;;
 | 
						|
		esac
 | 
						|
	done
 | 
						|
	# Get program arguments (playlists)
 | 
						|
	shift $((OPTIND - 1))
 | 
						|
	for list in "$@"; do
 | 
						|
		if ! [ -f "$list" ] && [ -z "$_optquickplay" ]; then
 | 
						|
			error "Failed to find playlist: $list"
 | 
						|
			continue
 | 
						|
		fi
 | 
						|
		_playlists+=("$list")
 | 
						|
	done
 | 
						|
	[ -z "$_optdownloadonly" ] && [ -z "$_optdryrun" ] && ! validatedeps mpc && error "$_return is required outside of dry- and download-only runs"
 | 
						|
	[ -n "$_playlists" ] && [ -n "$_optrofi" ] && error "Flag -r cannot be used with playlist arguments" 2
 | 
						|
	if [ -n "$_optrofi" ]; then rofimenu; exit $?; fi
 | 
						|
	if [ -n "$_playlists" ]; then
 | 
						|
		execplaylists
 | 
						|
		exit $?
 | 
						|
	else
 | 
						|
		error "Nothing to do" 1
 | 
						|
	fi
 | 
						|
}
 | 
						|
 | 
						|
main "$@"
 | 
						|
 |