#!/usr/bin/sh
# 
# $Log: mpdplaylist.in,v $
# Revision 1.23  2025-11-22 18:56:44+05:30  Cprogrammer
# fixed --karma for <= and >= options
# allow <, >, <= and >= options for --new option
#
# Revision 1.22  2024-04-10 21:29:19+05:30  Cprogrammer
# fixed command line arguments with space
#
# Revision 1.21  2022-06-20 01:04:54+05:30  Cprogrammer
# use directories set in ./configure
#
# Revision 1.20  2022-05-10 21:33:16+05:30  Cprogrammer
# added --includeartist, --excludeartist to include, exclude artists in query
#
# Revision 1.19  2021-09-30 20:31:46+05:30  Cprogrammer
# use -noheader option to prevent .sqlitrc settings messing with results
#
# Revision 1.18  2021-04-24 11:56:20+05:30  Cprogrammer
# minor fixes for display and error messages
#
# Revision 1.17  2021-04-18 16:39:41+05:30  Cprogrammer
# fix for space in command line argument values
#
# Revision 1.16  2021-04-16 12:56:50+05:30  Cprogrammer
# added --options-file feature to take command line arguments from an options file
#
# Revision 1.15  2021-04-15 22:06:27+05:30  Cprogrammer
# added --karma option
#
# Revision 1.14  2021-04-14 16:40:22+05:30  Cprogrammer
# added --play option
#
# Revision 1.13  2021-04-14 13:11:29+05:30  Cprogrammer
# added --playcount option to get songs that match play counts
#
# Revision 1.12  2021-04-10 13:05:38+05:30  Cprogrammer
# set default options (command line arguments) from .mpdplaylist.options
#
# Revision 1.11  2021-04-10 11:15:39+05:30  Cprogrammer
# updated usage message for --playlist option
#
# Revision 1.10  2021-01-07 12:20:20+05:30  Cprogrammer
# added last_played is NULL to query daysnotheard
#
# Revision 1.9  2020-09-05 12:32:29+05:30  Cprogrammer
# added --limit option
#
# Revision 1.8  2020-09-03 20:36:39+05:30  Cprogrammer
# fixed --clear option
#
# Revision 1.7  2020-09-03 11:10:32+05:30  Cprogrammer
# fixed stats_db path
#
# Revision 1.6  2020-08-30 11:34:59+05:30  Cprogrammer
# fixed stats.db path
#
# Revision 1.5  2020-08-06 16:15:39+05:30  Cprogrammer
# renamed --dummy option to --query
#
# Revision 1.4  2020-08-05 12:08:02+05:30  Cprogrammer
# renamed --save to --playlist
#
# Revision 1.3  2020-08-02 20:48:38+05:30  Cprogrammer
# added --shuffle option to shuffle playlist
#
# Revision 1.2  2020-08-02 17:52:35+05:30  Cprogrammer
# added --host option to specify mpd host
#
# Revision 1.1  2020-07-19 18:17:52+05:30  Cprogrammer
# Initial revision
#
#
# $Id: mpdplaylist.in,v 1.23 2025-11-22 18:56:44+05:30 Cprogrammer Exp mbhangui $
#

get_mpd_conf_value()
{
  grep "^$1" /etc/mpd.conf|awk '{print $2}' | \
  sed -e 's{"{{g'
}

usage()
{
if [ -f /usr/bin/less ] ; then
	MORE=/usr/bin/less
else
	MORE=/usr/bin/more
fi
echo "Press ENTER for options, Cntrl C to quit" 1>&2
read key
$MORE 1>&2 <<EOF
Usage: mpdplaylist [OPTION]

Known values for OPTION are:

--options-file=filename
  Take extra command line options from filename

--fromyear=From Year
  Year in YYYY format

--toyear=To Year
  Year in YYYY format

--includegenre=Genre List to be included
  comma separated list of genre.

--excludegenre=Genre List to be excluded
  comma separated list of genre

--includeartist=Artist List to be included
  comma separated list of artist.

--excludeartist=Artist List to be excluded
  comma separated list of artist

--minrating=Minimum Rating (value from 0 to 10, or -1 for unrated songs)

--artist=Artist
  Find songs with Artist exactly matching 'Artist'

--artistany=keyword
  Find songs with Artist having 'keyword' anywhere in the Artist field

--new=d
  Find songs added in the last 'd' days

--daysnotheard=d
  Find songs not heard in the last 'd' days, use d=0 for songs never played

--daysheard=d
  Find songs heard in the last 'd' days
  
--oldfirst
  Order playlist in increaing order of Last Played field
  (Song played earlier come at top)
  
--popular
  Order playlist in decreasing order of Rating field
  (Songs with high rating come at top)

--karma
  Find songs with karma as per expression. e.g.
  --karma<50  means songs with karma less than 50
  --karma<=50 means songs with karma less than or equal to 50
  --karma=50  means songs with karma equal to 50
  --karma>=50 means songs with karma greater than or equal to 50
  --karma>50  means songs with karma greater than 50

--playcount=exp
  Find songs with playcounts as per expression. e.g.
  --playcount<5  means songs with play counts less than 5
  --playcount<=5 means songs with play counts less than or equal to 5
  --playcount=5  means songs with play counts equal to 5
  --playcount>=5 means songs with play counts greater than or equal to 5
  --playcount>5  means songs with play counts greater than 5

--playlist=playlist
  save the playlist in the filename $playlist_dir/playlist.
  If the playlist is named now, new playlist will be added to the
  current mpd playlist. If --clear is also specified, the current
  playlist will be cleared and loaded. The 'now' playlist is not
  saved to the playlist directory. Without --playlist option,
  result will be displayed on screen.

--host=host
  mpd host to use

--shuffle
  shuffle playlist

--limit=n
  Limit the playlist to n entries

--play
  Play songs when playlist=now
  
--clear
  clear playlist before adding. See --playlist option

--query
  Display the SQL query and exit

--help

  display this help and exit

--version

  output version information
EOF
exit $1
}

process_daysadded()
{
	case "$1" in
	-*\>=*)
	optarg=$(echo "$1" | cut -d'>' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*>=//')
	optval=">=$optval"
	;;
	-*\<=*)
	optarg=$(echo "$1" | cut -d'<' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*<=//')
	optval="<=$optval"
	;;
	-*=*)
	optarg=$(echo "$1" | cut -d'=' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*=//')
	;;
	-*\>*)
	optarg=$(echo "$1" | cut -d'>' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*>//')
	optval=">$optval"
	;;
	-*\<*)
	optarg=$(echo "$1" | cut -d'<' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*<//')
	optval="<$optval"
	;;
	*)
	optarg=""
	optval=""
	;;
	esac
}

process_cmdline()
{
while test $# -gt 0; do
	case "$1" in
	-*\>=*)
	optarg=$(echo "$1" | cut -d'>' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*>=//')
	optval=">=$optval"
	prog_args="$prog_args $optarg=\"$optval\""
	;;
	-*\<=*)
	optarg=$(echo "$1" | cut -d'<' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*<=//')
	optval="<=$optval"
	prog_args="$prog_args $optarg=\"$optval\""
	;;
	-*=*)
	optarg=$(echo "$1" | cut -d'=' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*=//')
	prog_args="$prog_args $optarg=\"$optval\""
	;;
	-*\>*)
	optarg=$(echo "$1" | cut -d'>' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*>//')
	optval=">$optval"
	prog_args="$prog_args $optarg\"$optval\""
	;;
	-*\<*)
	optarg=$(echo "$1" | cut -d'<' -f1)
	optval=$(echo "$1" | sed 's/[-_a-zA-Z0-9]*<//')
	optval="<$optval"
	prog_args="$prog_args $optarg\"$optval\""
	;;
	*)
	optarg=
	prog_args="$prog_args $1"
	;;
	esac

	case "$1" in
	--options-file=*)
	options_file=$optval
	;;
	--fromyear=*)
	fromyear=$optval
	;;
	--toyear=*)
	toyear=$optval
	;;
	--includegenre=*)
	if [ -n "$excludegenre" ] ; then
		echo "you cannot give both includegenre and excludegenre together" 1>&2
		usage 1
	fi
	includegenre=$(echo $optval| sed "s/ /_/g")
	;;
	--excludegenre=*)
	if [ -n "$includegenre" ] ; then
		echo "you cannot give both includegenre and excludegenre together" 1>&2
		usage 1
	fi
	excludegenre=$(echo $optval| sed "s/ /_/g")
	;;
	--includeartist=*)
	if [ -n "$excludeartist" ] ; then
		echo "you cannot give both includeartist and excludeartist together" 1>&2
		usage 1
	fi
	includeartist=$(echo $optval| sed "s/ /_/g")
	;;
	--excludeartist=*)
	if [ -n "$includeartist" ] ; then
		echo "you cannot give both includeartist and excludeartist together" 1>&2
		usage 1
	fi
	excludeartist=$(echo $optval| sed "s/ /_/g")
	;;
	--minrating=*)
	minrate=$optval
	;;
	--artist=*)
	exact=1
	artist=$(echo "$optval"| sed "s/ /_/g")
	;;
	--artistany=*)
	exact=0
	artist=$(echo $optval| sed "s/ /_/g")
	;;
	--karma=*|--karma\>*|--karma\<*|--karma\>=*|--karma\<=*)
	first_char=$(echo $optval | cut -c1)
	if [ ! "$first_char" = ">" -a ! "$first_char" = "<" ] ; then
		karma="karma=$optval"
	else
		karma="karma$optval"
	fi
	;;
	--playcount=*|--playcount\>*|--playcount\<*)
	first_char=$(echo $optval | cut -c1)
	if [ ! "$first_char" = ">" -a ! "$first_char" = "<" ] ; then
		play_counts="play_count=$optval"
	else
		play_counts="play_count$optval"
	fi
	;;
	--new=*|--new\>*|--new\<*|--new\>=*|--new\<=*)
	first_char=$(echo $optval | cut -c1)
	if [ ! "$first_char" = ">" -a ! "$first_char" = "<" ] ; then
		daysadded="--daysadded=$optval"
	else
		daysadded="--daysadded$optval"
	fi
	;;
	--daysheard=*)
	daysheard=$optval
	;;
	--daysnotheard=*)
	daysnotheard=$optval
	;;
	--playlist=*)
	playlist=$optval
	;;
	--oldfirst)
	oldfirst=1
	;;
	--popular)
	sort_popular=1
	;;
	--query)
	dummy=1
	;;
	--shuffle)
	shuffle=1
	;;
	--clear)
	do_clear=1
	;;
	--limit=*)
	limit=$optval
	;;
	--play)
	do_play=1
	;;
	--verbose)
	verbose="-v"
	;;
	--host=*)
	mpd_host=$optval
	;;
	--help)
	playlist_dir=$(get_mpd_conf_value playlist_dir)
	usage 0
	;;
	*)
	echo "invalid option [$1]" 1>&2
	usage 1
	;;
	esac
	shift
done
}

verbose=""
fromyear=""
toyear=""
includegenre=""
excludegenre=""
includeartist=""
excludeartist=""
artist=""
minrate=""
oldfirst=0
sort_popular=0
dummy=0
shuffle=0
do_clear=0
limit=0
do_play=0
play_counts=""
karma=""
playlist=""
daysadded=""
daysheard=""
daysnotheard=""
mpd_host=""
options_file=""
prog_args="$0"

if [ $# -eq 0 -a -f $HOME/.mpdplaylist.options ] ; then
	options=$(grep -v '^#' -h $HOME/.mpdplaylist.options)
	set -- $options
fi
process_cmdline "$@"
if [ -n "$options_file" ] ; then
	options="$(grep -v '^#' -h $options_file)"
	eval process_cmdline $options
fi

music_dir=$(get_mpd_conf_value music_directory)
if [ $? -ne 0 ] ; then
  echo "could not get music directory from mpd.conf" 1>&2
  exit 1
fi
sticker_file=$(get_mpd_conf_value sticker_file)
if [ $? -ne 0 ] ; then
  echo "could not get sticker database from mpd.conf" 1>&2
  exit 1
fi
playlist_dir=$(get_mpd_conf_value playlist_dir)
if [ $? -ne 0 ] ; then
  echo "could not get playlist directory from mpd.conf" 1>&2
  exit 1
fi
stats_dir=$(dirname $sticker_file)
stats_file=$stats_dir/stats.db
if [ -z "$stats_file" ] ; then
  echo "Unable to get stats.db location" 1>&2
  exit 1
fi
if [ ! -f "$stats_file" ] ; then
  echo "$stats_file: No such file or directory" 1>&2
  exit 1
fi

sql_query="SELECT uri FROM song "
first_condition=""
if [ -n "$fromyear" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	sql_query="$sql_query date >= $fromyear"
fi
if [ -n "$toyear" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	sql_query="$sql_query date <= $toyear"
fi
count=0
if [ -n "$excludegenre" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	for i in $(echo $excludegenre | sed "s/,/ /g")
	do
		f=$(echo $i|sed "s/_/ /g")
		if [ $count -eq 0 ] ; then
			sql_query="$sql_query genre NOT IN ('$f'"
		else
			sql_query="$sql_query, '$f'"
		fi
		count=$(expr $count + 1)
	done
	sql_query="$sql_query)"
fi
count=0
if [ -n "$includegenre" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	for i in $(echo $includegenre | sed "s/,/ /g")
	do
		f=$(echo $i|sed "s/_/ /g")
		if [ $count -eq 0 ] ; then
			sql_query="$sql_query genre IN ('$f'"
		else
			sql_query="$sql_query, '$f'"
		fi
		count=$(expr $count + 1)
	done
	sql_query="$sql_query)"
fi
count=0
if [ -n "$excludeartist" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	for i in $(echo $excludeartist | sed "s/,/ /g")
	do
		f=$(echo $i|sed "s/_/ /g")
		if [ $count -eq 0 ] ; then
			sql_query="$sql_query artist NOT IN ('$f'"
		else
			sql_query="$sql_query, '$f'"
		fi
		count=$(expr $count + 1)
	done
	sql_query="$sql_query)"
fi
count=0
if [ -n "$includeartist" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	for i in $(echo $includeartist | sed "s/,/ /g")
	do
		f=$(echo $i|sed "s/_/ /g")
		if [ $count -eq 0 ] ; then
			sql_query="$sql_query artist IN ('$f'"
		else
			sql_query="$sql_query, '$f'"
		fi
		count=$(expr $count + 1)
	done
	sql_query="$sql_query)"
fi
if [ -n "$minrate" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	if [ "$minrate" -lt 0 ] ; then
		sql_query="$sql_query rating = 0"
	else
		sql_query="$sql_query rating >= $minrate"
	fi
fi
if [ -n "$artist" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	f=$(echo $artist|sed "s/_/ /g")
	if [ $exact -eq 1 ] ; then
		sql_query="$sql_query artist = '$f'"
	else
		sql_query="$sql_query artist like '%$f%'"
	fi
fi
if [ -n "$daysadded" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	process_daysadded $daysadded
	tmval=$(date +'%s')
	char1=$(echo $optval | cut -c1)
	char2=$(echo $optval | cut -c2)
	if [ "$char2" = "=" ] ; then
		if [ "$char1" = ">" ] ; then
			first_char="<="
		elif [ "$char1" = "<" ] ; then
			first_char=">="
		else
			first_char="="
		fi
		optval=$(echo $optval | cut -d'=' -f2-)
	else
		if [ "$char1" = ">" ] ; then
			first_char="<"
			optval=$(echo $optval | cut -d'>' -f2-)
		elif [ "$char1" = "<" ] ; then
			first_char=">"
			optval=$(echo $optval | cut -d'<' -f2-)
		fi
	fi
	tmval=$(expr $tmval - $optval \* 86400)
	sql_query="$sql_query date_added $first_char $tmval"
fi
if [ -n "$karma" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	sql_query="$sql_query $karma"
fi
if [ -n "$play_counts" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	sql_query="$sql_query $play_counts"
fi
if [ -n "$daysheard" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	tmval=$(date +'%s')
	tmval=$(expr $tmval - $daysheard \* 86400)
	sql_query="$sql_query last_played >= $tmval"
fi
if [ -n "$daysnotheard" ] ; then
	if [ -z "$first_condition" ] ; then
		sql_query="$sql_query WHERE"
		first_condition=1
	elif [ -n "$first_condition" ] ; then
		sql_query="$sql_query AND"
	fi
	if [ ! "$daysnotheard" = "0" ] ; then
		tmval=$(date +'%s')
		tmval=$(expr $tmval - $daysnotheard \* 86400)
		sql_query="$sql_query (last_played < $tmval OR last_played IS NULL)"
	else
		sql_query="$sql_query last_played IS NULL"
	fi
fi
order_by=0
if [ $oldfirst -eq 1 ] ; then
	if [ $order_by -eq 0 ] ; then
		sql_query="$sql_query ORDER BY last_played ASC"
		order_by=1
	else
		sql_query="$sql_query, last_played ASC"
	fi
fi
if [ $sort_popular -eq 1 ] ; then
	if [ $order_by -eq 0 ] ; then
		sql_query="$sql_query ORDER BY rating DESC"
		order_by=1
	else
		sql_query="$sql_query, rating DESC"
	fi
fi
if [ -n "$daysadded" ] ; then
	if [ $order_by -eq 0 ] ; then
		sql_query="$sql_query ORDER BY date_added ASC"
		order_by=1
	else
		sql_query="$sql_query, date_added ASC"
	fi
fi
if [ $limit -gt 0 ] ; then
		sql_query="$sql_query limit $limit"
fi
if [ -z "$fromyear" -a -z "$toyear" -a -z "$includegenre" -a -z "$excludegenre" \
	-a -z "$includeartist" -a -z "$excludeartist" -a -z "$minrate" \
	-a -z "$artist" -a -z "$daysadded" -a -z "$daysheard" -a -z "$daysnotheard" \
	-a -z "$play_counts" -a -z "$karma" ] ; then
	echo "No options given and no default usage configuration found" 1>&2
	usage 1
	exit 1
fi
if [ $dummy -eq 0 ] ; then
	if [ -n "$playlist" ] ; then
		if [ "$playlist" = "now" ] ; then
			if [ $shuffle -eq 1 ] ; then
				/usr/bin/sqlite3 -noheader $stats_file "$sql_query;" | shuf > $playlist_dir/now.m3u
			else
				/usr/bin/sqlite3 -noheader $stats_file "$sql_query;" > $playlist_dir/now.m3u
			fi
			if [ -z "$mpd_host" ] ; then
				mpd_host=localhost
			fi
			if [ $do_clear -eq 1 ] ; then
				mpc --host="$mpd_host" clear
			fi
			mpc --host="$mpd_host" load now
			mpc --host="$mpd_host" rm now
			if [ $do_play -eq 1 ] ; then
				mpc --host="$mpd_host" play
			fi
		else
			if [ $shuffle -eq 1 ] ; then
				(
				echo "# $prog_args"
				/usr/bin/sqlite3 -noheader $stats_file "$sql_query;" | shuf
				) | tee $playlist_dir/"$playlist".m3u
			else
				(
				echo "# $prog_args"
				/usr/bin/sqlite3 -noheader $stats_file "$sql_query;" 
				) | tee $playlist_dir/"$playlist".m3u
			fi
		fi
	else
		echo "# $prog_args"
		/usr/bin/sqlite3 -noheader $stats_file "$sql_query;"
	fi
else
	echo "$sql_query;"
fi
exit 0
