#!/bin/bash
# {{{ Copyright
# Soma - a command line/dialog radio player
#
# Copyright 2010 David Woodfall <dave@dawoodfall.net>
# All rights reserved.
# Copyright tmproot modifications Patrick Volkerding, with thanks.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Thanks to Patrick Volkerding for the mktemp patch.

# #shellcheck --exclude=SC2068,SC2086,SC1117,SC1090
# SC2068,SC2086 we need word splitting
# SC1117 we want literal backslashes
# SC1090 we want to source non-consistent files


# }}}
# {{{ Startup
# {{{ Startup - Add Option to Config
	# Test For Config Option. Add it if not found. Var, Short Desc, Default, Text
add_opt() {
	grep -qE "^$1=" "$SOMAOPTIONS" && return
	cat << EOF >> "$SOMAOPTIONS"

# $1 $2
#$4
#
# Default: $3
$1=$3
EOF
}
# }}}
# {{{ Startup - Init
init() {
# {{{ Global Vars
	trap "cleanup" SEGV ABRT HUP INT QUIT TERM PIPE
	SOMAROOT=$(mktemp -d /tmp/soma-XXXXXX)
	STATIONS=$SOMAROOT/soma-stations
	MSTATUS=$SOMAROOT/soma-mstatus
	STATUS=$SOMAROOT/soma-status
	PLAYING=$SOMAROOT/soma-playing
	TMP=$SOMAROOT/soma-tmp
	FIFO=$SOMAROOT/soma.fifo
	MIXERS=$SOMAROOT/mixers
	SYSSOMADIR=/etc/soma
	DEFSTATIONS=$SYSSOMADIR/stations.conf
	SOMADIR=$HOME/.soma
	SYSOPTIONS=$SYSSOMADIR/options.conf
	SOMAOPTIONS=$SOMADIR/options.conf
	USRSTATIONS=$SOMADIR/stations.conf
	TRACKLIST=$SOMADIR/tracklist
	SCHEDULES=$SOMADIR/schedules.conf
	BOOKMARKS=$SOMADIR/bookmarks.conf
	SYSTHEMEDIR=$SYSSOMADIR/themes
	SYSGENREDIR=$SYSSOMADIR/genres
	USRTHEMEDIR=$SOMADIR/themes
	USRGENREDIR=$SOMADIR/genres
	RECSDIR=$SOMADIR/recordings
	RECSLIST=$SOMAROOT/recordlist
	export DIALOGRC=$SYSTHEMEDIR/defaultsrc
	ORIGTERM=$TERM
	DEFAULT_STAT=" "

	# Make our local dirs
	[ ! -d "$USRTHEMEDIR" ] && mkdir -p "$USRTHEMEDIR"
	[ ! -d "$USRGENREDIR" ] && mkdir -p "$USRGENREDIR"
	[ ! -d "$RECSDIR" ] && mkdir -p "$RECSDIR"

	touch "$STATUS"
	touch "$PLAYING"

	# Copy the global options if local doesn't exist
	[ ! -r "$SOMAOPTIONS" ] && cat "$SYSOPTIONS" > "$SOMAOPTIONS"
	[ ! -r "$SCHEDULES" ] && touch "$SCHEDULES"
	[ ! -r "$USRSTATIONS" ] && touch "$USRSTATIONS"
	[ ! -r "$BOOKMARKS" ] && touch "$BOOKMARKS"

	# Function key shortcuts to bookmarks

	# Most terms - linux, screen, (u)xterm, st, rxvt
	shortcut[OP]=1
	shortcut[OQ]=2
	shortcut[OR]=3
	shortcut[OS]=4
	shortcut[15]=5
	shortcut[17]=6
	shortcut[18]=7
	shortcut[19]=8
	shortcut[20]=9
	shortcut[21]=10
	shortcut[23]=11
	shortcut[24]=12

	# rxvt-unicode
	shortcut[11]=1
	shortcut[12]=2
	shortcut[13]=3
	shortcut[14]=4

	read_bookmarks

	# Various vars
	SCHED_ENABLED=1
	MT=3
	AUTOSETRECEXT=1
	[ -n "$(< $SCHEDULES)" ] && SCHED=1
	DEFIFS=$' \t\n'
	RDASH=("" "[R]")
	SDASH=("" "[S]" "[-]")

	#List types
	__ALL=0
	__GENRE=1
	__BOOKMARKS=2
	__LOCAL=3
	__RECORDINGS=4

	# Checkbox settings
	boxval=("off" "on")

	BLANK='                                                    '
# }}}
# {{{ Config
	add_opt CURSOROFF '(Turn Off Cursor)' 1 '
# Allowed values: 0 or 1'

	add_opt DIALOGOPTS '(Options To Pass To Dialog)' '"--colors"' '
# Dialog options
# Set to "" for no formatting/colour in titles and also unset TITLECOL.
# Add --no-mouse to disable the mouse.'

	add_opt "export DIALOGRC" '(Dialog Theme)' \
			"/etc/soma/themes/defaultsrc" '
# Saved Theme.
# If you use slackrc, you
# should unset TITLECOL below, for the true slack experience.
# defaultsrc uses your terminal default foreground and background.
# Try /dev/null too.'

	add_opt DIALOGFIX '(Try To Fix Dialog Drawing Issues By Setting TERM)' 0 '
# If you are experiencing problems with screen drawing this may help.
# You will probably need to restart soma to see any effect.
#
# Allowed values: 0 or 1'

	add_opt TITLECOL '(Dialog Title Colour Code)' '"\Zb"' '
# This setting controls the dialog title colour separate to the
# DIALOGRC settings if needed.
#
# By default it is set to \Zb (bold default dialog foreground colour).
# You can mix bold and colour by eg: \Zb\Z4 etc. See man dialog for
# more info.
#
# Colours are set by \Zn where n =
#
# 0 black
# 1 red
# 2 green
# 3 yellow
# 4 blue
# 5 magenta
# 6 cyan
# 7 white
# 8 default dialogrc foreground'

	add_opt SETVOL '(Saved Volume From Last Session)' 50 '
# Automatically overwritten'

	add_opt BALANCE '(Saved Volume Balance From Last Session)' 0 '
# Automatically overwritten'

	add_opt SOFTVOL '(MPlayer Softvol)' '"-softvol"' '
# Use MPlayer software mixer.
# Note that MPlayer adds this automatically when needed.'

	add_opt FILTER '(MPlayer Audio Filter)' '""' '
# Set mplayer audio filter (Used with -af)
# Example: "channels=2" can force single channel mono into 2 channel mono.
# See man mplayer for more details.'

	add_opt CACHE '(MPlayer Cache)' 320 '
# Adjust this if you get drop outs.'

	add_opt SAVETRACKLIST '(Keep An Ongoing Track List)' 0 '
# The list is located at ~/.soma/tracklist
# Allowed values: 0 or 1'

	add_opt GENRE '(Your Current Genre)' '"All"' '
# Saved genre'

	add_opt CONNECTTIMEOUT '(Connection Timeout)' 150 '
# This controls how long soma will wait for a stream before giving up.
# If you have problems with your internet you may want to adjust it.'

	add_opt TIMEFORMAT '(Format for time display)' '"%I:%M"' '
# This controls how the time is displayed in the status window.
# It defaults to 12 hour format.
# You can change it here.
# Example: 24 hour format would be "%H:%M"
# See the date man page for full options.'

	add_opt CARET '(Caret Character Used In Menus)' '"-->"' '
# This is the character(s) used in listings.
# Do not use an empty string!
# If you have a utf8 supported terminal, you may want to try "——>"'

	add_opt SETTERMTITLE '(Set The Terminal/Screen Title to "soma")' 1 '
# Allowed values: 0 or 1'

	add_opt AUTORECONNECT '(Autoreconnect to Lost Streams)' 0 '
# Allowed values: 0 or 1'

	add_opt SHOWAUDIOINFO '(Show Audio and Codec in Stream Info)' 1 '
# Allowed values: 0 or 1'

	add_opt SHOWVOLUME '(Show Current Volume in Title Bar)' 1 '
# Allowed values: 0 or 1'

	add_opt MENCODEROPTS '(Mencoder Source Options For Recording)' \
	'"-demuxer rawvideo -rawvideo w=1:h=1 -ovc copy -of rawaudio -audiofile"' '
# See --record-info for info.'

	add_opt MENCODEROAC '(Mencoder Output Audio Codec)' '"-oac copy"' '
# See --record-info for info.'

	add_opt AUTOSETBITRATE '(Try to Set The Bitrate Automatically)' 1 '
# If lavc is used then try to get the source bitrate and apply it
# to the output encoding.'

	add_opt RECEXT '(Mencoder Output File Extension)' '"mp3"' '
# mp3, mp2, ac3, aac etc.'

	add_opt AUTOSETRECEXT '(Try To Set Output File Extension)' 1 '
# Try to set RECEXT automatically.'

	add_opt MT '(Popup Message Timeout)' 3 '
# Timeout for message windows in seconds.'

	add_opt OUTCARD '(Audio Output Device)' '"pulse"' '
# If you need to set a custom audio output it can be done here, but using the
# menu will overwrite it.'

	add_opt MIXER '(Audio Mixer)' 0 '
# If you need to set a custom mixer device it can be done here.'

	add_opt PROXY '(HTTP Proxy)' '""' '
# HTTP Proxy for mplayer and mencoder to use.'
# }}}
# {{{ Source Options
	source "$SYSOPTIONS"
	source "$SOMAOPTIONS"

	# Category names
	category=("All" "$GENRE" "Bookmarks" "User Stations" "Recordings")

	cursor off

	if [ "$TERM" = "linux" ] && [ "$CARET" = "——>" ]; then
		CARET="-->"
	fi

	# Set xterm and screen title
	if [ "$SETTERMTITLE" -eq 1 ]; then
		printf "\033]0;soma\007"
		# Screen
		[ -n "$WINDOW" ] && printf "\033ksoma\033\\"
	fi
# }}}
# {{{ Audio cards
	count=$(aplay -l | grep -c "^card")
	aplay -l | grep "^card" > "$TMP"

	DEVICE[0]="pulse"
	DEVICE[1]="alsa"

	for card in $(seq 2 $((count+1)))
	do
		SOUNDCARD[$card]=$(sed -n "$((card-1))p" "$TMP")
		dev=$(sed -nr "$((card-1)) s/card ([^:]+):.*device (.*):.*/\1,\2/p" "$TMP")
		DEVICE[$card]="device=hw=$dev"
		HWDEVICE[$card]="${SOUNDCARD[card]}"
	done

	NUMDEVICES=$card
	DEVICE[$((card+1))]="jack"
	DEVICE[$((card+2))]="NAS"
# }}}
}
# }}}
# {{{ Startup - Command line
get_commandline() {
	do_bookmarks=0
	do_stations=0
	do_all_stations=0
	do_genres=0
	do_local_genres=0
	DEBUG=0

	until [ -z "$1" ]
	do
		[ "$1" = "-v" ] && version && cleanup
		[ "$1" = "-d" ] && DEBUG=1
		[ "$1" = "-l" ] && do_bookmarks=1
		[ "$1" = "-b" ] && do_stations=1
		[ "$1" = "-B" ] && do_all_stations=1
		[ "$1" = "-g" ] && do_local_genres=1
		[ "$1" = "-G" ] && do_genres=1
		shift
	done

	[ $do_bookmarks -eq 1 ] && station_selector $__BOOKMARKS
	[ $do_stations -eq 1 ] && station_selector $__GENRE
	[ $do_all_stations -eq 1 ] && station_selector $__ALL
	[ $do_genres -eq 1 ] && genre_selector
	[ $do_local_genres -eq 1 ] && local_genre_selector
}
# }}}
# {{{ Startup - Version
version() {
	printf "%s\n" "Soma $VERSION"
}
# }}}
# {{{ Startup - Purge local files
purge_local() {
	if [ -z "$USRTHEMEDIR" ] || [ -z "$(find $USRTHEMEDIR -name '*rc')" ]; then
		return
	fi

	# Purge local files that are identical to global
	# This will be removed at some point in the future
	for th in "$USRTHEMEDIR"/*
	do
		bt=$(basename "$th")
		if [ -r "$SYSTHEMEDIR/$bt" ]; then
			if [ "$(md5sum "$USRTHEMEDIR/$bt" | awk '{print $1}')" \
				= "$(md5sum "$SYSTHEMEDIR/$bt" | awk '{print $1}')" ]; then
				rm -f "$USRTHEMEDIR/$bt"
		fi
	fi
	done

	if [ -z "$USRGENREDIR" ] || \
		[ -z "$(find $USRGENREDIR -name '*conf')" ]; then
		return
	fi

	IFS=$'\n'

	for gn in "$USRGENREDIR"/*.conf
	do
		bg=$(basename "$gn")
		if [ -r "$SYSGENREDIR/$bg" ]; then
			if [ "$(md5sum "$USRGENREDIR/$bg" | awk '{print $1}')" \
				= "$(md5sum "$SYSGENREDIR/$bg" | awk '{print $1}')" ]; then
				rm -f "$USRGENREDIR/$bg"
		fi
	fi
	done

	IFS="$DEFIFS"
}
# }}}
# }}}
# {{{ Cleanup/exit
cleanup() {
	stop_mplayer
	stop_mencoder

	rm -rf "$SOMAROOT"
	cursor on
	exit 0
}
# }}}
# {{{ Help
# {{{ Help - Full Usage
usage() {
cat << EOF
Soma $VERSION - A dialog + mplayer internet radio player.

$(get_flags)

$(get_files)

$(get_keys)
EOF
}
# }}}
# {{{ Help - Files and Dirs
get_files() {
	cat << EOF
Config Files and Directories:

User:                        Global:

~/.soma/options.conf         /etc/soma/options.conf
~/.soma/stations.conf        /etc/soma/stations.conf
~/.soma/themes/*rc           /etc/soma/themes/*rc
~/.soma/genres/*.conf        /etc/soma/genres/*.conf
~/.soma/bookmarks.conf
~/.soma/schedules.conf
~/.soma/recordings/

Custom genre files can be made and will be listed along with the default set.
EOF
}
# }}}
# {{{ Help - Keys
get_keys() {
	cat << EOF
Keys Supported in Status Window:

1 - 0  Volume 10% - 100%              () Volume -5/+5
F1-F12 Bookmarks                      \`  Volume 0
Space  Pause
a      Add schedule                   A  Toggle audio codec info
b      Station browser (genre)        B  Station browser (all)
d      Disconnect/Reconnect/Play/Stop D  Toggle debug mode
g      List genres                    h  List local genres
j      Add station to current genre   k  Bookmark current station
l      Open bookmark list             m  Toggle mute
o      Options menu                   q  Quit
s      Scheduler menu                 S  Toggle schedules on/off
t      Change theme                   ?  Show keys

Recording:

r      Open recording menu             R Start/Stop recording

Dashboard indicators:

[S] Schedules are set                   [-] Schedules are temporarily disabled
[R] A recording is in progress        	[0-100] Volume or [M] Mute
EOF
}
# }}}
# {{{ Help - Startup Flags
get_flags() {
	cat << EOF
Startup Flags:

soma -l             Go straight to bookmarks list
soma -b             Go straight to stations browser for genre
soma -B             Go straight to all stations browser
soma -g             Go straight to local genre browser
soma -G             Go straight to global genre browser
soma -d             To start in debug mode
soma -h             Help
soma --record-info  Detailed recording info
EOF
}
# }}}
# {{{ Help - Show Options From Status
show_opts() {
	clear
	get_keys
	printf "\n %s" "Press any key to return"
	read -rn1
}
# }}}
# {{{ Help - Recording Info
show_recordinfo() {
	cat << EOF
INTRO

Pressing 'R' in the status window will start a recording.  Pressing it again
will stop recording.  Note that if you change channels while recording, the
recording will carry on recording the original stream that it started with.  It
doesn't follow your channel selection.  When a recording is in process you will
see [R] in the status window title.

After starting a recording a confirmation dialog will pop up.  If there is a
mencoder error you will be notified.  Note that it is necessary to have a 2
second delay to allow mencoder to start and test if it has successfully started
recording.

Pressing 'r' in the status window will bring up the recording menu.  There you
can view, playback and delete recordings.

Recordings are made using mencoder, which is part of the MPlayer package.  It
should copy most mp3 streams with no problems.

You can reencode to other formats, like aac etc, if you have the required
encoders and extra libraries installed.  You may need to rebuild MPlayer and
the extra libraries will be auto-detected during configure.  The SlackBuild for
MPlayer can be found on your nearest mirror in the source/xap/MPlayer
directory:

https://mirrors.slackware.com/slackware/slackware64-(release)/source/xap/MPlayer/

By default mencoder supports mp2 and ac3 encoding without any extra external
codecs needed.

Note that mencoder doesn't support the -playlist flag like mplayer does, so
playlist files are downloaded using wget and then sedded for the source audio
URL.  It's possible that the regex could fail so I'd be pleased to have any
problem ones mailed to me at dave@dawoodfall.net so that I can adjust the
regex.  Playlists are saved in a temp dir (/tmp/soma-xxxxxx) but the directory
is deleted on exit.

When playing back a recording, the playback duration is detected by querying
the file with 'soxi -d' (part of the the sox package), but it doesn't work on
all media types (eg ac3 and aac).

RECORDING OPTIONS

Technical Note: Recording audio-only files with mencoder requires a bit of a
hack that makes mencoder believe that it's recording a 1x1 px video.
Therefore, the mencoder options are a bit convoluted and are split up to make
it easier to set custom ouput encodings.  They are then assembled in the
correct order to feed to mencoder.

The defaults are to copy the stream.  For mp3 streams this should work fine.

Slackware's mencoder supports encoding to mp2 or ac3 (Dolby Digital) with no
extra libraries required.  If you use ac3 you should also be able to change the
channel layout and/or add channels.  For other formats you will need to install
extra encoders and rebuild MPlayer.


MENCODEROPTS (Source Stream Options)

This sets up the fake video and a raw audio output format.  You probably won't
need to touch this.

Default: -demuxer rawvideo -rawvideo w=1:h=1 -ovc copy -of rawaudio -audiofile


MENCODEROAC (Output Audio Codec)

This is where the encoder options are set, and controls the output.  This is
what you will be using to set up the desired output format.

Default: -oac copy

Examples:

-oac copy (just copies the stream)
-oac lavc -lavcopts acodec=ac3 (encodes to ac3 aka Dolby Digital)
-oac lavc -lavcopts acodec=mp2 (encodes to mpeg I layer 2)

Most stations use mp3 streams and '-oac copy' works fine, but some use aac (eg
BBC Radio 3) and copying the stream won't work.  For those stations you can
encode to another format and adjust the bit rate to match the source by setting
abitrate=N in -lavcopts (you should see the bitrate in soma's status window.
If it doesn't show, try pressing 'A' to toggle audio info.  Mencoder encodes
ac3 and mp2 at 224k bits by default unless you specifically set a rate).


AUTOSETBITRATE (Try to Set The Bitrate Automatically)

If '-oac lavc' is used then curl is used to grab the header from the stream URL
and if it contains 'icy-br' then the bitrate is set from that (if one isn't
already set manually.)  Note, this isn't guaranteed to work everywhere.

Default: on


RECEXT (Output Filename Extension):

mp3, mp2, ac3, aac etc.

Default: mp3


AUTOSETRECEXT (Try To Set RECEXT Automatically)

If this is set then soma will look to see if there is an 'acodec=' part in
MENCODEROAC.  If there is it will set the file extension to that.

Default: on


A fully assembled mencoder command will look something like this:

mencoder -demuxer rawvideo -rawvideo w=1:h=1 -ovc copy -of rawaudio \\
-audiofile "URL" -o "filename.ext" -oac lavc -lavcopts acodec=ac3:abitrate=N \\
"URL"

Note that the URL is specified twice.

The filename is constructed from the station name + 24 hour date and time and
saved to ~/.soma/recordings/.  Example: 'SF_10-33_20180418_112952.mp3'


ENCODING COMPARISONS

One hour recordings (approx).

Source audio stream: MP3, 128 kbps, 44.1 kHz, Stereo (SF 10-33 on SomaFM):

File Size  Encoder   Settings
54M        MP3       -oac copy
57M        AC3       -oac lavc -lavcopts acodec=ac3:abitrate=128

Source audio stream: AAC, 320 kbps, 48 kHz, Stereo (BBC Radio 3):

File Size  Encoder   Settings
138M       AC3       -oac lavc -lavcopts acodec=ac3:abitrate=320
105M       AAC       -oac faac -faacopts br=320 (mencoder compiled with faac)

Some frame skipping with 320k.

It was hard to notice any difference in audio quality between the ac3 and aac
versions, but as you can see there's a bit of a difference in file size.
MPlayer reports the aac bitrate as 270 kbps rather than 320, and according to
the man page it's an 'average' bitrate setting.

The obvious choice here is to use '-oac copy' on mp3 streams.  That should
match the quality of the source with no extra bits taking up diskspace or CPU
on playback.  Use ac3, mp2 or a custom encoder with lavc on non-mp3 streams,
with the bitrate set automatically if possible.

It's worth doing a test run before a starting long recording session.  Record
10 seconds or so, then press 'r' to bring up the recording menu, select 'View
Recordings' and play back the short test file, checking that the bitrate
matches the source.  If you don't see the codec info, press 'A' to toggle it
on.  If looks OK, reconnect to the station to do the real recording.


DEBUGGING

When a recording is started a ~/.soma/recordings/.mencoder.log will be made.
You can 'tail -f' this in another terminal to watch the output for info and
errors.

There will also be a .mencoder.url and .mencoder.cmd which show the URL and
full command used.
EOF
}
# }}}
# {{{ Help - Inline Help
inline_help() {
	[ -z "$PAGER" ] && export PAGER=less
	clear
	(
		get_keys
		printf "\n%s\n\n" "Press 'q' to return to the status window."
	) | $PAGER
}
# }}}
# }}}
# {{{ Play
# {{{ Play - Log
log() {
	printf "%s\n" "$*" >> "$STATUS"
}
# }}}
# {{{ Play - Get Current title
get_title() {
	unset TITLE

	if [ -n "$PLAYBACK" ]; then
		TITLE="${STATIONURL##*/}"
			if [ "$TITLE" != "$lasttitle" ] && [ -n "$TITLE" ]; then
				log "Title   : $TITLE"
				printf "%s" "$TITLE" > "$PLAYING"
			fi
		lasttitle="$TITLE"
		return
	fi

	TITLE=$(grep -aE "^ICY Info: StreamTitle" "$MSTATUS" |
					tail -n 1 | cut -d"'" -f2- | sed "s/';.*$//")

	# Probably a station ID. Don't print it.
	printf "%s" "$TITLE" | grep -qi "SomaFM" && return

	if [ -z "$TITLE" ]; then
		TITLE=$(grep -a name "$MSTATUS" | cut -d: -f2 | tail -n 1)
	fi

	if [ -z "$TITLE" ]; then
		TITLE=$(grep -a title "$MSTATUS" | grep -v subtitles | cut -d: -f2 \
			| tail -n 1)
	fi

#	TITLE="${TITLE/#\'/}"
#	TITLE="${TITLE/%\'/}"
#	TITLE="${TITLE/#Now playing:/}"
#	TITLE="${TITLE/# /}"
	TITLE="${TITLE//  / }"

	while printf "%s" "$TITLE" | grep -q '| |'; do
		TITLE="${TITLE//| |/|}"
	done

	if grep -q "^BBC" <<<"$NAME" || [ "$NAME" = "Radio 3HD" ]; then
		if [ "$CHANNEL" != "$(< $PLAYING)" ]; then
			printf "%s" "$CHANNEL" > "$PLAYING"
		fi
		return
	fi

	if [ -n "$TITLE" ] && [ "$TITLE" != "$CHANNEL" ]; then
		if [ "Title   : $TITLE" != "$(tail -n 1 "$STATUS")" ]; then
			log "Title   : $TITLE"
			if [ "$SAVETRACKLIST" -eq 1 ]; then
				printf "%s\n" "$TITLE ($(date))" >> "$TRACKLIST"
			fi
			if [ "$CHANNEL:$TITLE" != "$(< $PLAYING)" ]; then
				printf "%s" "$CHANNEL: $TITLE" > "$PLAYING"
			fi
		fi
	fi

}
# }}}
# {{{ Play - Get MPlayer Status
get_mplayer_status() {
	local rows
	local cols
	rows=10
	cols=40

	if [ -n "$MPLAYERPID" ]; then
		# No mplayer
		if ! mp_running; then
			if [ -z "$PLAYBACK" ]; then
				if [ "$AUTORECONNECT" -eq 0 ]; then
					dialog $DIALOGOPTS \
						--title "${TITLECOL}Status" \
						--yes-label "Menu" \
						--no-label "Reconnect" \
						--yesno \
						"Lost the stream!\n
Press R to reconnect (you can set soma to autoreconnect in options).\n
Press Enter/M to go to the menu." \
						"$rows" "$cols"

					if [ "$?" -eq 1 ]; then
						CHANNEL="$OLDCHANNEL"
						unset MPLAYERPID
						play
						setup
						status
					else
						if ! mp_running; then
							CHANNEL="$OLDCHANNEL"
							unset MPLAYERPID
							unset laststation
						fi
						mainmenu
					fi
				else
					CHANNEL="$OLDCHANNEL"
					unset MPLAYERPID
					for n in $(seq 10)
					do
						dialog $DIALOGOPTS \
							--title "${TITLECOL}Autoreconnect" \
							--ok-label "Cancel" \
							--timeout 10 \
							--msgbox \
							"\nLost the stream.\nReconnecting in 10 seconds.\n\nTry #$n." \
							"$rows" "$cols" 2> "$TMP"

						if ! [[ $(tail -n1 $TMP) =~ timeout ]]; then
							CHANNEL="Idle"
							unset laststation
							OLDSTATUS="junk"
							return
						fi
						play
						sleep 1
						if mp_running; then
							setup
							status
							break
						fi
					done
					if [[ $(< $TMP) =~ timeout ]]; then
						do_msg "Autoreconnect" \
							"Tried reconnecting $n times.\nGiving up." 0
						CHANNEL="Idle"
						mainmenu
					fi
				fi
			else
				tail -n1 "$STATUS" | grep -aqx "End" || log "End"
				unset MPLAYERPID
				unset TITLE
				unset laststation
			fi
		fi
	else
		# Idling
		unset MPLAYERPID
		CHANNEL="Idle"
	fi
}
# }}}
# {{{ Play - Play
play() {
	local SIZE
	local rows
	local cols
	local VOL
	SIZE=$(stty size)
	rows=${SIZE% *}
	cols=${SIZE#* }
	source "$SOMAOPTIONS"

	stop_mplayer

	if [ ! -r "$FIFO" ]; then
		mkfifo "$FIFO"
	fi

	OLDCHANNEL="$CHANNEL"
	rm -f "$MSTATUS"

	if printf "%s" "$STATIONURL" | grep -qE '\.(pls|m3u|ram|asx|wvx|m3u8)'; then
		PLAYLIST="-playlist"
	else
		unset PLAYLIST
	fi

	VOL="-volume $SETVOL"

	[ -n "$FILTER" ] && AF="-af $FILTER"

	if [ -n "$PROXY" ]; then
		export http_proxy="$PROXY"
	fi

	mplayer \
		-ao "$OUTCARD" \
		-slave \
		-input file="$FIFO" \
		-msglevel all=-1:decaudio=4:demuxer=4:network=4 \
		-nomsgmodule \
		-nomsgcolor \
		-cache "$CACHE" \
		$SOFTVOL $VOL $AF \
		$PLAYLIST $STATIONURL \
    > "$MSTATUS" 2>/dev/null &
		MPLAYERPID=$!

	[ "$DIALOGFIX" -eq 1 ] && TERM=xterm-color

	if [ ! -e "$RECSDIR/$STATIONURL" ]; then
		if [ "$DEBUG" -eq 0 ]; then
			dialog $DIALOGOPTS \
				--title "${TITLECOL}Status" \
				--ok-label "Menu" \
				--timeout 1 \
				--msgbox "Connecting to $CHANNEL ..." \
				"$rows" "$cols" 2> /dev/null
		else
			TIME=$(date +"$TIMEFORMAT")
			dialog $DIALOGOPTS \
				--exit-label "Menu" \
				--keep-window \
				--timeout 1 \
				--title "${TITLECOL}[$TIME] $CHANNEL - Raw MPlayer Output" \
				--tailboxbg "$MSTATUS" "$rows" "$cols" 2> "$TMP"
		fi
	fi

	if [ "$BALANCE" != "0" ]; then
		BAL=$(printf "%s\n" "$BALANCE/10" | bc -l)
		printf "%s\n" "set_property balance $BAL" > "$FIFO"
	fi
}
# }}}
# {{{ Play - Setup
	# setup the info. loop until we have some
	# this is really to shorten the title loop
setup() {
	local SIZE
	local rows
	local cols
	local COUNT
	local MGENRE
	local WEBSITE
	local AUTHOR
	local COPY
	local LENGTH
	local BITRATE
	local AUDIO
	local CODEC
	unset TITLE
	SIZE=$(stty size)
	rows=${SIZE% *}
	cols=${SIZE#* }

	# wait for info to appear
	while ! grep -qa '^AUDIO' "$MSTATUS"; do
		if [ "$DEBUG" -eq 1 ]; then
			TIME=$(date +"$TIMEFORMAT")
			dialog $DIALOGOPTS \
				--exit-label "Menu" \
				--keep-window --timeout 1 \
				--title "${TITLECOL}[$TIME] $CHANNEL - Raw MPlayer Output" \
				--tailboxbg "$MSTATUS" "$rows" "$cols" 2> "$TMP"
			break
		fi

		if [ "$DEBUG" -eq 0 ]; then
			sleep 0.1
			COUNT=$((COUNT+1))

			if ! pgrep -x mplayer | grep -qw "$MPLAYERPID"; then
				do_msg " Error" "    No stream!    " 0
				sed -i '$d' "$STATUS"
				lasttitle="Idle"
				CHANNEL="Idle"
				unset MPLAYERPID
				unset COUNT
				return
			fi

			if [ "$COUNT" -gt "$CONNECTTIMEOUT" ]; then
				dialog --ok-label "Menu" \
					--msgbox "Connection timeout occurred.\n\n\
If your internet connection is slow, \
you may want to try\nadjusting the CONNECTTIMEOUT \
value in ~/.soma/options.conf\nor through the options menu." \
				"$rows" "$cols"

				sed -i '$d' "$STATUS"
				stop_mplayer
				CHANNEL="Idle"
				unset MPLAYERPID
				unset COUNT
				mainmenu
				return
			fi
	fi
	done

	NAME="$(sed -nr "s/^[nN]ame\s*: (.*)/\1/p" "$MSTATUS")"
	MGENRE="$(sed -nr "s/^Genre\s*: ([^:].*)/\1/p" "$MSTATUS")"
	WEBSITE="$(sed -nr "s/^Website\s*: (.*:.*).*/\1/p" "$MSTATUS")"
	AUTHOR="$(sed -nr "s/^[aA]uthor\s*: ([^:].*)/\1/p" "$MSTATUS")"
	COPY="$(sed -nr "s/^[cC]opyright\s*: ([^:].*)/\1/p" "$MSTATUS")"
	grep -q -e "^BBC" -e "^Radio 3HD" <<<"$NAME" && NAME="$CHANNEL"

	if [ -n "$PLAYBACK" ]; then
		TITLE="${STATIONURL##*/}"
		LENGTH=$(soxi -d "$STATIONURL" 2>/dev/null)

		if [ "$TITLE" != "$lasttitle" ]\
			&& [ "$SHOWAUDIOINFO" -eq 1 ]; then
				[ -n "$(tail -n1 $STATUS)" ] && log ""
				log "Title   : $TITLE"
				if [ "$SAVETRACKLIST" -eq 1 ]; then
					printf "%s\n" "$TITLE ($(date))" >> "$TRACKLIST"
				fi
				[ -n "$LENGTH" ] && log "Length  : $LENGTH"
				printf "%s" "$TITLE" > "$PLAYING"
				lasttitle="$TITLE"
				CHANNEL="$TITLE"
			fi
	fi

	if [ "$STATIONURL" != "$laststation" ]; then
		[ -n "$(tail -n1 $STATUS)" ] && [ -z "$PLAYBACK" ] && log ""
		[ -n "$NAME" ] &&      log "Name    : $NAME"
		[ -n "$MGENRE" ] &&    log "Genre   : $MGENRE"
		[ -n "$WEBSITE" ] &&   log "Website : $WEBSITE"
		[ -n "$AUTHOR" ] &&    log "Author  : $AUTHOR"
		[ -n "$COPY" ] &&      log "(C)     : $COPY"

		if [ "$SHOWAUDIOINFO" -eq 1 ]; then
			BITRATE="$(sed -nr "s/^Bitrate\s*: ([^:].*)/\1/p" "$MSTATUS")"
			AUDIO="$(sed -nr "s/^AUDIO\s*: (.*)/\1/p" "$MSTATUS")"
			CODEC="$(sed -nr "s/.*codec: (.*[^:].*)/\1/p" "$MSTATUS")"
			[ -n "$BITRATE" ] && log "Bitrate : $BITRATE"
			[ -n "$AUDIO" ] &&   log "Audio   : $AUDIO"
			[ -n "$CODEC" ] &&   log "Codec   : $CODEC"
		fi

		if [ "$SAVETRACKLIST" -eq 1 ]; then
			printf "\n%s\n" "$NAME ($(date))" >> "$TRACKLIST"
		fi
	fi
}
# }}}
# {{{ Play - Status
status() {
	local SIZE
	local rows
	local cols
	unset OLDSTATUS
	cursor off
	[ ! -e "$STATUS" ] && touch "$STATUS"

	mp_running || CHANNEL="Idle"
	[ -z "$(< $STATUS)" ] && OLDSTATUS="junk"

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }
		TIME="[$(date +"$TIMEFORMAT")]"
		TIME24=$(date +"%H:%M")
		DATE=$(date +"%Y%m%d")
		DAY=$(date +"%a")
		tmp=$(< "$SCHEDULES")
		SCHED=$((((${#tmp}>0)*3) & SCHED_ENABLED))

		unset VOLUME

		if [ "$SHOWVOLUME" -eq 1 ]; then
			[ -z "$MUTE" ] && VOLUME="[$SETVOL]" || VOLUME="[$MUTE]"
		fi

		DASH="$TIME${SDASH[SCHED]}${RDASH[RECORDING]}$VOLUME"

		mp_running && get_title

		if [ "$SCHED_ENABLED" -eq 1 ]; then
			if [ "$TIME24" != "$lasttime" ]; then
				SCHEDCHAN=$(sed -rn \
					"s%^($DATE|[\*]+${DAY:0:2}|[\*]+),$TIME24,(.*)%\2%p" "$SCHEDULES")

				if [ -n "$SCHEDCHAN" ] && [ "$SCHEDCHAN" != "$CHANNEL" ]; then
					schedule_change_station
				fi
			fi
		fi

		[ "$DEBUG" -eq 0 ] && get_mplayer_status

		if [ "$(< $STATUS)" != "$OLDSTATUS" ] \
			|| [ "$SIZE$DASH$TIME24" != "$TEST" ]; then

			if [ "$DEBUG" -eq 0 ]; then
				dialog $DIALOGOPTS \
					--exit-label "Menu" \
					--keep-window \
					--timeout 1 \
					--title "${TITLECOL}$DASH $CHANNEL" \
					--tailboxbg "$STATUS" \
					"$rows" "$cols" 2> "$TMP"
			else
				dialog $DIALOGOPTS \
					--exit-label "Menu" \
					--keep-window \
					--timeout 1 \
					--title "${TITLECOL}$DASH $CHANNEL - Raw MPlayer Output" \
					--tailboxbg "$MSTATUS" \
					"$rows" "$cols" 2> "$TMP"
			fi
		fi

		OLDSTATUS="$(< $STATUS)"
		TEST="$SIZE$DASH$TIME24"
		lasttitle="$TITLE"
		lasttime="$TIME24"
		input
	done
}
# }}}
# {{{ Play - Input
input() {
	local REPLY
	read -rsn1 -t1 || return

	case "$REPLY" in
		`printf "\e"`)
			read -rsn2 >/dev/null

			if ! [[ $REPLY =~ ^O(P|Q|R|S)$ ]] && ! [[ $REPLY =~ ^\[[[:digit:]]$ ]]; then
				return
			fi

			FN="$REPLY"

			if [[ $REPLY =~ ^\[[[:digit:]]$ ]]; then
				read -rsn1
				[[ $REPLY =~ ^[[:digit:]]$ ]] || return
				FN="$FN$REPLY"
			fi

			FN=$(grep -Eo "[[:alnum:]]+" <<<"$FN")
			[ -z "${bookmark[${shortcut[$FN]}]}" ] && return
			OLDSTATUS="junk"
			CHANNEL="${bookmark[${shortcut[$FN]}]}"
			laststation="$STATIONURL"
			STATIONURL=$(sed -nr "s|^$CHANNEL,(.*)|\1|p" "$BOOKMARKS" | head -n1)
			play
			setup
			status
			;;
		' ')
			if [ -z "$PLAYBACK" ]; then
				if [ "$CHANNEL" != "Pause" ]; then
					OLDCHANNEL="$CHANNEL"
					OLDPLAYING=$(tail -n 1 "$PLAYING")
					CHANNEL="Pause"
					printf "%s" "Pause" > "$PLAYING"
					mp_running && printf "%s\n" "pause" > "$FIFO"
				else
					if [ -n "$OLDCHANNEL" ]; then
						CHANNEL="$OLDCHANNEL"
						printf "%s" "$OLDPLAYING" > "$PLAYING"
						mp_running && printf "%s\n" "pause" > "$FIFO"
					fi
				fi
			else
				mp_running && printf "%s\n" "pause" > "$FIFO"
			fi
			;;
		'`')
			SETVOL="0"
			sed -i "s/^SETVOL=.*/SETVOL=$SETVOL/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property volume $SETVOL" > "$FIFO"
			mp_running && printf "%s\n" "get_property volume" > "$FIFO"
			;;
		1|2|3|4|5|6|7|8|9)
			SETVOL=$((REPLY*10))
			sed -i "s/^SETVOL=.*/SETVOL=$SETVOL/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property volume $SETVOL" > "$FIFO"
			mp_running && printf "%s\n" "get_property volume" > "$FIFO"
			;;
		0)
			SETVOL="100"
			sed -i "s/^SETVOL=.*/SETVOL=$SETVOL/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property volume $SETVOL" > "$FIFO"
			mp_running && printf "%s\n" "get_property volume" > "$FIFO"
			;;
		'(')
			SETVOL=$(((SETVOL-5)+(SETVOL<5)*5))
			sed -i "s/^SETVOL=.*/SETVOL=$SETVOL/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property volume $SETVOL" > "$FIFO"
			mp_running && printf "%s\n" "get_property volume" > "$FIFO"
			;;
		')')
			SETVOL=$(((SETVOL+5)-(SETVOL>95)*5))
			sed -i "s/^SETVOL=.*/SETVOL=$SETVOL/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property volume $SETVOL" > "$FIFO"
			mp_running && printf "%s\n" "get_property volume" > "$FIFO"
			;;
		'-')
			BALANCE=$(((BALANCE-1)+(BALANCE<-9)))
			BAL=$(printf "%s\n" "$BALANCE/10" | bc -l)
			sed -i "s/^BALANCE=.*/BALANCE=$BALANCE/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property balance $BAL" > "$FIFO"
			mp_running && printf "%s\n" "get_property balance" > "$FIFO"
			;;
		'+')
			BALANCE=$(((BALANCE+1)-(BALANCE>9)))
			BAL=$(printf "%s\n" "$BALANCE/10" | bc -l)
			sed -i "s/^BALANCE=.*/BALANCE=$BALANCE/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property balance $BAL" > "$FIFO"
			mp_running && printf "%s\n" "get_property balance" > "$FIFO"
			;;
		'=')
			BALANCE=0
			sed -i "s/^BALANCE=.*/BALANCE=$BALANCE/" "$SOMAOPTIONS"
			mp_running && printf "%s\n" "set_property balance $BALANCE" > "$FIFO"
			mp_running && printf "%s\n" "get_property balance" > "$FIFO"
			;;
		'd')
			if [ -z "$PLAYBACK" ]; then
				if mp_running; then
					stop_mplayer
					CHANNEL="Idle"
					#printf "%s\n" "Idle" > "$PLAYING"
				else
					if [ -n "$OLDCHANNEL" ]; then
						CHANNEL="$OLDCHANNEL"
						play
						setup
						status
					fi
				fi
			else
				if mp_running; then
					stop_mplayer
					OLDCHANNEL="$CHANNEL"
					CHANNEL="Idle"
					#printf "Idle" > "$PLAYING"
				else
					CHANNEL="$OLDCHANNEL"
					play
					setup
					status
				fi
			fi
			;;
		'')
			mainmenu
			;;
		'a')
			cursor on
			schedule_add
			cursor off
			;;
		'A')
			SHOWAUDIOINFO=$((1-SHOWAUDIOINFO))
			if [ "$SHOWAUDIOINFO" -eq 1 ]; then
				BITRATE="$(sed -nr "s/^Bitrate\s*: ([^:].*)/\1/p" "$MSTATUS")"
				AUDIO="$(sed -nr "s/^AUDIO\s*: (.*)/\1/p" "$MSTATUS")"
				CODEC="$(sed -nr "s/.*codec: (.*[^:].*)/\1/p" "$MSTATUS")"
				[ -n "$BITRATE" ] && log "Bitrate : $BITRATE"
				[ -n "$AUDIO" ] &&   log "Audio   : $AUDIO"
				[ -n "$CODEC" ] &&   log "Codec   : $CODEC"
			else
				sed -i "/Bitrate :/d;/Audio   :/d;/Codec   :/d" "$STATUS"
			fi
			sed -i "s#^SHOWAUDIOINFO=.*#SHOWAUDIOINFO=$SHOWAUDIOINFO#" "$SOMAOPTIONS"
			;;
		'b')
			station_selector $__GENRE
			;;
		'B')
			station_selector $__ALL
			;;
		'C')
			[ -x /usr/bin/xclip ] && printf "%s" "$STATIONURL" | xclip -sel clip
			[ -x /usr/bin/xsel ] && printf "%s" "$STATIONURL" | xsel -b
			;;
		'D')
			DEBUG=$((1-DEBUG))
			;;
		'g')
			genre_selector
			;;
		'h')
			local_genre_selector
			;;
		'j')
			add_current_station_to_genre
			;;
		'k')
			add_bookmark
			;;
		'l')
			station_selector $__BOOKMARKS
			;;
		'm')
			if mp_running; then
				printf "%s\n" "mute" > "$FIFO"
				if [ "$MUTE" = "M" ]; then
					unset MUTE
				else
					MUTE="M"
				fi
			fi
			;;
		'o')
			edit_options
			;;
		'q')
			cleanup
			;;
		'R')
			if [ -z "$RECORDING" ]; then
				start_recording
			else
				stop_recording
			fi
			;;
		'r')
			recording_main
			;;
		's')
			schedule_main
			;;
		'S')
			SCHED_ENABLED=$((3-SCHED_ENABLED))
			[ -z "$(< $SCHEDULES)" ] && return

			if [ "$SCHED_ENABLED" -eq 1 ]; then
				do_msg "Schedules" "Schedules have been enabled." $MT
			else
				do_msg "Schedules" "Schedules have been temporarily disabled." $MT
			fi
			;;
		't')
			loop_themes
			;;
		'?')
			inline_help
			;;
		*)
			;;
	esac

	OLDSTATUS="junk"
}
# }}}
# {{{ Play - Stop mplayer
stop_mplayer() {
	mp_running || return
	printf "%s\n" quit > "$FIFO"
	unset MPLAYERPID
	unset PLAYBACK
}
# }}}
# {{{ Play - Stop mencoder
stop_mencoder() {
	if men_running; then
		if ps h -o pid --quick-pid "$MENPID" 1>/dev/null; then
			kill "$MENPID" 1>/dev/null 2>&1
		fi
		sleep 0.5
		if ps h -o pid --ppid "$MENPID" 1>/dev/null; then
			kill -P "$MENPID" 1>/dev/null 2>&1
		fi
	fi
	unset RECORDING
}
# }}}
# {{{ Play - mplayer running?
mp_running() {
	[ -z "$MPLAYERPID" ] && return 1
	ps h -o pid --quick-pid "$MPLAYERPID" 1>/dev/null || return 1
}
# }}}
# {{{ Play - mencoder running?
men_running() {
	[ -z "$MENPID" ] && return 1
	ps h -o pid --quick-pid "$MENPID" 1>/dev/null && return 0
	ps h -o pid --ppid "$MENPID" 1>/dev/null && return 0
	return 1
}
# }}}
# }}}
# {{{ Stations
# TODO: All these need renaming because they are now generic menu builders
# {{{ Stations - Make a Nice List
	# Use TMP for input and output
nice_list() {
	local SIZE
	local rows
	local cols
	local NICELIST
	local len
	unset HELP
	SIZE=$(stty size)
	rows=${SIZE% *}
	cols=${SIZE#* }

	DIALOGTITLE="$1"
	BUTT="$2"
	[ -n "$3" ] && HELP="--help-button --help-status --help-label \"$3\""

	IFS=$'\n'

	while read -r item; do
		if [ -z "$WITHURL" ]; then
			len=$(((cols-20)-${#item}))

			# This gives menu items a blank filler
			NICELIST="$NICELIST\"$item${BLANK:1:$len}\" \"$CARET\" "

			# This just prints menu items with no filler
			#	NICELIST="$NICELIST\"$item\" \"$CARET\" "
		else
			NICELIST="$NICELIST $item"
		fi
	done < "$TMP"


	IFS="$DEFIFS"

	# sh -c "" properly sends the quoted strings to dialog
	MENU="--title \"${TITLECOL}$DIALOGTITLE\" \
		$DEFAULT_STAT \
		--ok-label \"$BUTT\" \
		$HELP \
		--cancel-label 'Back' \
		--menu '' $rows $cols $rows $NICELIST"

	sh -c "dialog $DIALOGOPTS $MENU" 2> "$TMP"
	ret=$?

	return $ret
}
# }}}
# {{{ Stations - Get Stations List
get_station_list() {
	unset TMPLIST

	cat "$USRSTATIONS" "$DEFSTATIONS" \
		| sed -r "
			s|\s+,|,|g
			s|,\s+|,|g
			s|^\s+||g
			s|\s+$||g
			/^$/d" > "$STATIONS"

	case $1 in
		$__ALL)
			TMPLIST="$STATIONS"
			;;
		$__BOOKMARKS)
			TMPLIST="$BOOKMARKS"
			;;
		$__GENRE)
			if [ -r "$SYSGENREDIR/$GENRE.conf" ]; then
				TMPLIST="$SYSGENREDIR/$GENRE.conf"
			fi
			if [ -r "$USRGENREDIR/$GENRE.conf" ]; then
				TMPLIST="$USRGENREDIR/$GENRE.conf"
			fi
			;;
		$__LOCAL)
			TMPLIST="$USRSTATIONS"
			;;
		$__RECORDINGS)
			TMPLIST="$RECSLIST"
			;;
		*)
			TMPLIST="$STATIONS"
			;;
	esac

	if [ $1 -ne $__BOOKMARKS ]&&[ $1 -ne $__LOCAL ]; then
		STATIONLIST=$(sed 's|^|"|g; s|,|" "|g; s|$|"|g' "$TMPLIST" | sort)
	elif [ $1 -eq $__BOOKMARKS ]; then
		STATIONLIST=$(nl -nln -w2 -s' ' "$TMPLIST" \
			| awk '$1>12{$1="    "}1' \
			| sed -r 's`^([0-9]+)`F\1:`g; s|^|"|g; s|,|" "|g; s|$|"|g')
	else
		STATIONLIST=$(sed 's|^|"|g; s|,|" "|g; s|$|"|g' "$TMPLIST")
	fi
}
# }}}
# {{{ Stations - Station Selector
station_selector() {
	local DELETE
	local DESC
	local CHAN
	SIZE=$(stty size)
	cols=${SIZE#* }
	DELETE="Delete"
	SELECTOR=$1

	[ "$1" -eq $__ALL ] && DESC="All"
	[ "$1" -eq $__GENRE ] && DESC="$GENRE"
	[ "$1" -eq $__BOOKMARKS ] && DESC="Bookmarks"
	[ "$1" -eq $__LOCAL ] && DESC="User Stations"
	[ "$1" -eq $__RECORDINGS ] && DESC="Recordings"
	[ "$GENRE" = "All" ] && [ "$1" -eq "$__GENRE" ] && SELECTOR="$__ALL"
	[ "$SELECTOR" -eq "$__ALL" ] && unset DELETE

	if [ "$CHANNEL" != "Idle" ] && [ "$CHANNEL" != "Pause" ]; then
		OLDCHANNEL="$CHANNEL"
	fi

	cursor off

	while :; do
		get_station_list $SELECTOR

		if [ -z "$STATIONLIST" ]; then
			if [ "$SELECTOR" -eq "$__GENRE" ]; then
				do_msg "List Stations" "No stations in current genre." $MT
				return
			else
				return
			fi
		fi

		if [ -z "$WITHURL" ]; then
			if [ "$1" -eq $__RECORDINGS ]; then
				sed -nr 's|"(.*)"$|\1|p' > "$TMP" <<< "$STATIONLIST"
			else
				sed -nr 's|"([^"]*)".*$|\1|p' > "$TMP" <<< "$STATIONLIST"
			fi
		else
			printf "%s\n" "${STATIONLIST//\\/}" > "$TMP"
		fi

		len=$(((cols-20)-${#OLDCHANNEL}))
		DEFAULT_STAT="--default-item \"$OLDCHANNEL${BLANK:1:$len}\""
		nice_list "$DESC" "Play" $DELETE
		retval=$?
		[ "$retval" -ne 0 ] && [ "$retval" -ne 2 ] && return

		if [ "$1" -eq $__BOOKMARKS ]; then
			CHAN=$(sed -r '
			s`^HELP\s``
			s`^\s?F[0-9]+:``
			s`^\s+``g
			s`\s+$``g' "$TMP")
		else
			CHAN=$(sed -r '
			s`^\s+``g
			s`\s+$``g' "$TMP")
		fi

		if [ $retval -eq 2 ]; then
			CHAN=${CHAN#HELP }
			remove_single_station "$CHAN" "$1" || return
		else
			if [ "$1" -eq $__RECORDINGS ]; then
				[ -n "$PLAYBACK" ] && [ "$SHOWAUDIOINFO" -eq 1 ] && log ""
				STATIONURL="$RECSDIR/$CHAN"

				if [ -n "$PLAYBACK" ]; then
					stop_mplayer
					unset laststation
				fi
				break
			else
				CHANNEL="$CHAN"
				STATIONURL=$(sed -nr "s|^$CHAN,(.*)|\1|p" "$TMPLIST")
				break
			fi
		fi
	done
	unset WITHURL

	if [ "$1" -eq $__RECORDINGS ]; then
		play_rec "$STATIONURL"
		laststation="$STATIONURL"
		status
	else
		if [ "$STATIONURL" = "$laststation" ] && [ -z "$PLAYBACK" ]; then
			status
		fi
		play
		setup
		laststation="$STATIONURL"
		status
	fi
}
# }}}
# {{{ Stations - Get Sched Station
get_sched_station() {
	get_station_list $__ALL
	printf "%s\n" "Disconnect" > "$TMP"
	sed -nr 's|"([^"]*)".*$|\1|p' >> "$TMP" <<< "$STATIONLIST"
	nice_list "Scheduler" "Select" || return 1
	sched_station=$(sed -r "s|\s+$||" "$TMP")
	if [ -z "$sched_station" ]; then return 1; fi
}
# }}}
# }}}
# {{{ Output
# {{{ Output - Select Soundcard
selectoutput() {
	local SIZE
	local rows
	local cols
	SIZE=$(stty size)
	rows=${SIZE% *}
	cols=${SIZE#* }
	unset CARDLIST
	tmp=$OUTCARD
	status="off"

	[ "$tmp" = "pulse" ] && status="on" || status="off"
	CARDLIST="0 \"Pulseaudio.\" $status "
	[ "$tmp" = "alsa" ] && status="on" || status="off"
	CARDLIST+="1 \"Alsa default softmix.\" $status "

	for i in $(seq 2 "$NUMDEVICES")
	do
		[ "$OUTCARD" = "alsa:${DEVICE[i]}" ] && status="on" || status="off"
		CARDLIST+="$i \"alsa:${HWDEVICE[i]}\" $status "
	done

	[ "$tmp" = "jack" ] && status="on" || status="off"
	CARDLIST="$CARDLIST $((i+1)) \"Jack.\" $status "

	[ "$tmp" = "NAS" ] && status="on" || status="off"
	CARDLIST="$CARDLIST $((i+2)) \"NAS.\" $status "

	sh -c "dialog $DIALOGOPTS \
		--radiolist \"${TITLECOL}Output Panel\" \
		$rows $cols $rows \
		$CARDLIST 2>$TMP" || return

	RESULT="$(< $TMP)"

	if [ "$RESULT" -eq 0 ]; then
		OUTCARD="pulse"
	elif [ "$RESULT" -eq 1 ]; then
		OUTCARD="alsa"
	elif [ "$RESULT" -eq $((i+1)) ]; then
		OUTCARD="jack"
	elif [ "$RESULT" -eq $((i+2)) ]; then
		OUTCARD="NAS"
	else
		OUTCARD="alsa:${DEVICE[RESULT]}"
	fi

	if ! grep -qE "^OUTCARD=" "$SOMAOPTIONS"; then
		printf "%s\n%s\n" "# Output Card" "OUTCARD=\"$OUTCARD\"" >> "$SOMAOPTIONS"
	else
		sed -i "s/^OUTCARD=.*/OUTCARD=\"$OUTCARD\"/" "$SOMAOPTIONS"
	fi

	[ "$OUTCARD" = "$tmp" ] && return
	if [ -z "$CHANNEL" ] && [ "$CHANNEL" != "Idle" ]; then return; fi
	play
	setup
	status
}
# }}}
# {{{ Output - Select Mixer Card
selectmixer() {
	local SIZE
	local rows
	local cols
	SIZE=$(stty size)
	rows=${SIZE% *}
	cols=${SIZE#* }

	tmp="$MIXER"
	unset MIXLIST
	status="off"

	aplay -l | grep card | cut -d, -f1 | sort -u > "$MIXERS"

	while read -r card; do
		NUMMIXER=$(sed -nr "s%card ([^:]*):.*%\1%p" <<< "$card")
		if [ "$tmp" -eq "$NUMMIXER" ]; then status="on"; else status="off"; fi
		MIXLIST+="$NUMMIXER \"$card\" $status "
	done < "$MIXERS"

	sh -c "dialog $DIALOGOPTS \
		--radiolist \"${TITLECOL}Output Panel\" \
		$rows $cols $rows \
		$MIXLIST 2> $TMP" || return

	MIXER="$(< $TMP)"

	if ! grep -qE "^MIXER=" "$SOMAOPTIONS"; then
		printf "\n%s\n%s\n" "# Mixer Device" "MIXER=$MIXER" >> "$SOMAOPTIONS"
	else
		sed -i "s/^MIXER=.*/MIXER=$MIXER/" "$SOMAOPTIONS"
	fi
}
# }}}
# {{{ Output - Mixer
mixerpanel() {
	alsamixer -c "$MIXER"
}
# }}}
# }}}
# {{{ Dialog and Cursor Routines
# {{{ An inputbox for strings. $1 = title, $2 = description text
# $3 = option name
general_inputbox() {
	dialog $DIALOGOPTS \
		--cancel-label "Back" \
		--title "${TITLECOL}$1" \
		--inputbox "$2" \
		"$rows" "$cols" \
		-- "${!3}" 2> "$TMP" || return

	# Escape slashes
	VALUE="\"$(sed 's%\\%\\\\%g' "$TMP")\""
	sed -i "s#^$3=.*#$3=$VALUE#" "$SOMAOPTIONS"
	source "$SOMAOPTIONS"
}
# }}}
# {{{ An inputbox for numbers. $1 = title, $2 = description text
	# $3 = option name
general_num_inputbox() {
	dialog $DIALOGOPTS \
		--cancel-label "Back" \
		--title "${TITLECOL}$1" \
		--inputbox "$2" \
		"$rows" "$cols" \
		-- "${!3}" 2> "$TMP" || return

	[ -z "$(< $TMP)" ] && return
	sed -i "s#^$3=.*#$3=$(< $TMP)#" "$SOMAOPTIONS"
	source "$SOMAOPTIONS"
}
# }}}
# {{{ A checkbox. $1 = title, $2 = description text, $3 = option name
general_checkbox() {
	cursor off
	dialog $DIALOGOPTS \
		--cancel-label "Back" \
		--no-tags \
		--title "${TITLECOL}$1" \
		--checklist "$2" \
		"$rows" "$cols" 1 \
		"$3" "$3" "${boxval[$3]}" \
		2> "$TMP" || return

	[ -n "$(< $TMP)" ] && val=1 || val=0
	sed -i "s#^$3=.*#$3=$val#" "$SOMAOPTIONS"
	source "$SOMAOPTIONS"
}
# }}}
# {{{ A Simple Msgbox
do_msg() {
	local mrows
	local mcols
	mrows=$(($(printf "%s" "$2" | wc -l)+6))
	mcolstitle=$(($(printf "%s" "$1" | wc -L)+4))
	mcolsbody=$(($(printf "%s" "$2" | wc -L)+4))
	mcols=$mcolsbody
	[ "$mcolstitle" -gt "$mcolsbody" ] && mcols=$mcolstitle

	[ -n "$3" ] && t=$3 || t=0
	dialog $DIALOGOPTS \
		--title "${TITLECOL}$1" \
		--timeout $t \
		--msgbox "$2" \
		$mrows $mcols 2> "$TMP"
}
# }}}
# {{{ Cursor on/off
cursor() {
	[ "$CURSOROFF" -eq 0 ] && return
	# Need to use xterm-color to fix dialog drawing glitches
	# but cursor won't turn off with xterm-color
	# If you are using the linux console with a custom cursor you will probably
	# want to turn off both CURSOROFF and DIALOGFIX
	[ "$DIALOGFIX" -eq 1 ] && TERM=xterm-256color
	setterm -cursor "$1"
	[ "$DIALOGFIX" -eq 1 ] && TERM=xterm-color
}
# }}}
# }}}
# {{{ General Reusable Routines
# {{{ General Sort Routine
	# Slightly ripped off from sbopkg but with modifications
	# Sort entries in $INFILE, output to $TMP
general_sort() {
	local PARTIALSORT
	local TMPLIST
	local TMPSORTQUEUE
	local TMPENTRIES
	local TMPNUMENTRIES
	local CHOICE
	local SELECTED
	local DEFAULTITEM
	local INFILE
	local BMCOUNT
	local SIZE
	local rows
	local cols
	PARTIALSORT="$SOMAROOT"/soma_sort_tempfile
	TMPSORTQUEUE="$SOMAROOT"/soma_tmp_sort_queue
	TMPENTRIES="$SOMAROOT"/tmp-infile
	TMPNUMENTRIES="$SOMAROOT"/tmp-numbered-infile
	INFILE="$SOMAROOT"/sort_infile
	SIZE=$(stty size)
	rows=${SIZE% *}
	cols=${SIZE#* }

	BMCOUNT=$(grep -c "." "$INFILE")
	[ "$BMCOUNT" -lt 2 ] && \
		do_msg "Sort" "Nothing to sort!" $MT && return 1

	get_station_list "$1"
	STATIONLIST=$(sed -r 's`\s?F[0-9]+:\s+``g
		s`^"\s+`"`g' <<<"$STATIONLIST")

	cut -d'"' -f1-2  <<<"$STATIONLIST" | sed 's`$`"`g' > "$TMPENTRIES"

	cat "$TMPENTRIES" > "$TMPSORTQUEUE"
	TMPLIST=$(nl -nln "$TMPENTRIES" | tr '\n' ' ')
	printf "%s\n" "$TMPLIST" > "$TMPNUMENTRIES"

	while :; do
		sh -c "dialog --title 'Sort Stations' \
			--ok-label Up \
			--extra-button \
			--extra-label Down \
			--cancel-label Done \
			--default-item '$DEFAULTITEM' \
			--menu 'Use the <Up/Down> buttons to sort the entries.
Press <OK> when done, or press <ESC> to abort changes.' \
			$rows $cols $rows \
			$(cat $TMPNUMENTRIES)" 2> "$TMP"

		CHOICE=$?
		SELECTED="$(< $TMP)"
		DEFAULTITEM="$SELECTED"

		case "$CHOICE" in
			0 ) # Up
				if [ "$SELECTED" -eq 1 ]; then
					continue
				fi
				head -n $((SELECTED-2)) "$TMPSORTQUEUE" > "$PARTIALSORT"
				head -n "$SELECTED" "$TMPSORTQUEUE" | tail -n 1 >> "$PARTIALSORT"
				head -n $((SELECTED-1)) "$TMPSORTQUEUE" |	tail -n 1 >> "$PARTIALSORT"
				tail -n $((BMCOUNT-SELECTED)) "$TMPSORTQUEUE" >> "$PARTIALSORT"
				mv "$PARTIALSORT" "$TMPSORTQUEUE"
				TMPLIST=$(nl -nln "$TMPSORTQUEUE" | tr '\n' ' ')
				printf "%s\n" "$TMPLIST" > "$TMPNUMENTRIES"
				DEFAULTITEM=$((SELECTED-1))
				continue
				;;
			1 ) # OK
				mv "$TMPSORTQUEUE" "$TMPENTRIES"
				break
				;;
			3 ) # Down
				if [ "$SELECTED" -eq "$BMCOUNT" ]; then
					continue
				fi
				head -n $((SELECTED-1)) "$TMPSORTQUEUE" > "$PARTIALSORT"
				head -n $((SELECTED+1)) "$TMPSORTQUEUE" | tail -n 1 >> "$PARTIALSORT"
				head -n "$SELECTED" "$TMPSORTQUEUE" |	tail -n 1 >> "$PARTIALSORT"
				tail -n $((BMCOUNT-SELECTED-1)) "$TMPSORTQUEUE" >> "$PARTIALSORT"
				mv "$PARTIALSORT" "$TMPSORTQUEUE"
				TMPLIST=$(nl -nln "$TMPSORTQUEUE" | tr '\n' ' ')
				printf "%s\n" "$TMPLIST" > "$TMPNUMENTRIES"
				DEFAULTITEM=$((SELECTED+1))
				continue
				;;
			* ) # Cancel or ESC
				rm -f "$TMPSORTQUEUE"
				break
				;;
		esac
	done

	rm -f "$TMP"

	while read -r station; do
		URL=$(printf "%s\n" "$STATIONLIST" | grep -E "$station" | cut -d'"' -f4)
		printf "%s\n" "$station,$URL" | sed 's#"##g' >> "$TMP"
	done < "$TMPENTRIES"

	rm -f "$TMPENTRIES"
	rm -f "$TMPNUMENTRIES"
	rm -f "$PARTIALSORT"
	rm -f "$TMPSORTQUEUE"
}
# }}}
# {{{ General Add Station
general_add_station() {
	local name
	local URL
	cursor on
	dialog $DIALOGOPTS \
		--title "${TITLECOL}Add Station" \
		--cancel-label "Done" \
		--inputbox "Station name" \
		10 60 2> "$TMP" || { cursor off; return 1; }

	name="$(< $TMP)"
	[ -z "$name" ] && cursor off && return 1

	if grep -qs "^$name," "$1"; then
		do_msg "Add Station" "\"$name\" already in list!" $MT
		cursor off
		return
	fi

	dialog $DIALOGOPTS \
		--title "${TITLECOL}Add Station" \
		--cancel-label "Done" \
		--inputbox "Station URL" \
		10 60 2> "$TMP" || { cursor off; return 1; }

	URL="$(< $TMP)"
	cursor off
	[ -z "$URL" ] && return 1

	printf "%s\n" "$name,$URL" >> "$1"
	do_msg "Add Station" "\"$name\" added." $MT
	if ! grep -q "^$name," "$USRSTATIONS"; then
		printf "%s\n" "$name,$URL" >> "$USRSTATIONS"
	fi
}
# }}}
# {{{ General Delete Routine
general_delete() {
	[ "$1" -eq "$__GENRE" ] && category[$__GENRE]="Stations in $GENRE"

	if [ -z "$(< $TMPLIST)" ]; then
		if [ "$1" = "$__RECORDINGS" ]; then
			do_msg "${category[$1]}" "No recordings found." $MT
		else
			do_msg "${category[$1]}" "Empty list." $MT
		fi
		return
	fi

	while [ -n "$(< $TMPLIST)" ]; do
		get_station_list "$1"
		printf "%s\n" "$STATIONLIST" | \
			cut -d'"' -f2 | \
			sed -r 's`[0-9]+:``g; s`^\s+``g' > "$TMP"
		nice_list "Delete ${category[$1]}" "Delete" || return
		CHAN=$(sed -r "s|\s+$||" "$TMP")

		if [ "$1" -eq $__RECORDINGS ]; then
			rm "$RECSDIR/$CHAN"
			sed -i "/^$CHAN$/d" "$TMPLIST"
		else
			sed -i "/^$CHAN,/d" "$TMPLIST"
		fi
	done
}
# }}}
# {{{ General Edit FIle
general_edit_file() {
	[ -z "$EDITOR" ] && EDITOR=/usr/bin/vim
	cursor on
	[ "$DIALOGFIX" -eq 1 ] && TERM=xterm-256color
	$EDITOR "$1"
	cursor off
}
# }}}
# }}}
# {{{ Schedules
# {{{ Scheduler - Main Menu
schedule_main() {
	local SIZE
	local rows
	local cols

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		dialog $DIALOGOPTS \
			--cancel-label "Back" \
			--default-item "$schedopt" \
			--menu "${TITLECOL}Scheduler" \
			"$rows" "$cols" "$rows" \
			View "View Schedules" \
			Add "Add Schedule" \
			Remove "Remove a Schedule" \
			2> "$TMP" || break

	schedopt="$(< $TMP)"

	case "$schedopt" in
		View)
			schedule_view
			;;
		Add)
			cursor on
			schedule_add
			cursor off
			;;
		Remove)
			schedule_remove
			;;
		esac
	done
}
# }}}
# {{{ Scheduler - View Sched
schedule_view() {
	printf "\n" > "$TMP"
	local SIZE
	local cols
	local rows
	local i
	SIZE=$(stty size)
	cols=${SIZE#* }
	rows=${SIZE% *}

	while read -r line; do
		i=$((i+1))
		ind="$i"
		[ "${#i}" -eq 1 ] && ind="0$i"
		date=${line%%,*}
		date="${date:6:2}/${date:4:2}/${date:0:4}"
		time=${line:9:5}
		channel=${line:15}
		printf "  %s\n" "$ind  $date $time $channel" >> "$TMP"
	done < "$SCHEDULES"

	dialog $DIALOGOPTS \
		--title "${TITLECOL}Schedules" \
		--textbox "$TMP" \
		"$rows" "$cols"
}
# }}}
# {{{ Scheduler - Conflict Error
sched_conflict() {
	local SIZE
	local rows
	local cols
	local arg
	local data
	local conflicts
	SIZE=$(stty size)
	rows=$((${SIZE% *}-10))
	cols=$((${SIZE#* }-10))

	arg="$1"
	[ -z "$arg" ] && arg=".*"

	data=$(sed -nr "s%($arg),($sched_time),(.*)%\1 \2 \3%p" "$TMPSCHED")

	conflicts=$(sed -nr "s%^(....)(..)(..) (.*)%\3/\2/\1 \4%p" <<< "$data")

	if [ "$arg" = "\*\*\*\*\*\*\*\*" ]; then
		conflicts=$(sed -nr "s%^([^ ]*) (.*)%**/**/**** \2%p" <<< "$conflicts")
	fi

	dialog \
		--cr-wrap \
		--title "Conflicting Schedule" \
		--no-label \
		"Cancel" --yesno \
		"\nThis schedule has conflicts with existing schedules
\n$conflicts\n
\nDo you wish to delete the existing one(s)?\n\n
Click yes to delete the old schedule(s) or cancel to drop this one." \
	"$rows" "$cols" 2>/dev/null || return 1
}
# }}}
# {{{ Scheduler - Add Sched
schedule_add() {
	local SIZE
	local rows
	local cols
	local TMPSCHED
	unset sched_time
	unset sched_recur
	unset sched_date
	unset sched_days
	unset sched_station

	SIZE=$(stty size)
	rows=$((${SIZE% *}-10))
	cols=$((${SIZE#* }-10))

	TMPSCHED="$SOMAROOT/tmpschedule"
	cat "$SCHEDULES" > "$TMPSCHED"
	get_schedule_time || return

	if [ "$sched_recur" = "Once" ]; then
		get_schedule_date || return

		if grep -qE "^$sched_date,$sched_time," "$TMPSCHED"; then
			sched_conflict "$sched_date" || return
			sed -i "/^$sched_date,$sched_time,/d" "$TMPSCHED"
		elif grep -qE "^\*\*\*\*\*\*\*\*,$sched_time," "$TMPSCHED"; then
			sched_conflict "\*\*\*\*\*\*\*\*" || return
			sed -i "/^\*\*\*\*\*\*\*\*,$sched_time,/d" "$TMPSCHED"
		fi
		sed -i "/^$sched_date,$sched_time,/d" "$TMPSCHED"
		printf "%s\n" "$sched_date,$sched_time,$sched_station" >> "$TMPSCHED"
		period="Date:    $day/$month/$year (DD/MM/YYYY)"
	elif [ "$sched_recur" = "Daily" ]; then
		get_sched_station || return
		sched_date="********"

		if grep -qE "^.*,$sched_time," "$TMPSCHED"; then
			sched_conflict || return
			sed -i "/^.*,$sched_time,/d" "$TMPSCHED"
		fi

		sed -i "/,$sched_time,/d" "$TMPSCHED"
		printf "%s\n" "$sched_date,$sched_time,$sched_station" >> "$TMPSCHED"
		period="Date:    Every day"
	elif [ "$sched_recur" = "Weekly" ]; then
		get_schedule_days || return

		if grep -qE "^\*\*\*\*\*\*\*\*,$sched_time," "$TMPSCHED"; then
			sched_conflict "\*\*\*\*\*\*\*\*" || return
			sed -i "/^.*,$sched_time,/d" "$TMPSCHED"
		fi

		for day in $sched_days
		do
			j=${day:0:2}
			sched_date="******$j"
			tmp_date="\*\*\*\*\*\*$j"
			if grep -qE "^\*\*\*\*\*\*$j,$sched_time," "$TMPSCHED"; then
				sched_conflict "$tmp_date" || return
				sed -i "/^$tmp_date,$sched_time,/d" "$TMPSCHED"
			fi
			printf "%s\n" "$sched_date,$sched_time,$sched_station" >> "$TMPSCHED"
		done
		period="Date:    Every $sched_days"
	fi

	sched_msg="${TITLECOL}New Schedule:\n\nStation: $sched_station\n$period\n"
	sched_msg="${sched_msg}Time:    $sched_time\n\n"
	sched_msg="${sched_msg}Press OK to accept.\n\n"

	dialog $DIALOGOPTS \
		--cr-wrap \
		--title "Schedule" \
		--yes-label "OK" \
		--no-label "Cancel" \
		--yesno "$sched_msg" \
		"$rows" "$cols" || return

	sort_schedules "$TMPSCHED" > "$SCHEDULES"
	rm -f "$TMPSCHED"
}
# }}}
# {{{ Scheduler - Sort Schedules
sort_schedules() {
	cat "$1" | sed "
		s%:%%
		s%Mo,%01,%
		s%Tu,%02,%
		s%We,%03,%
		s%Th,%04,%
		s%Fr,%05,%
		s%Sa,%06,%
		s%Su,%07,%" \
	| sort -t, -k2n \
	| sed "
		s%\*01,%*Mo,%
		s%\*02,%*Tu,%
		s%\*03,%*We,%
		s%\*04,%*Th,%
		s%\*05,%*Fr,%
		s%\*06,%*Sa,%
		s%\*07,%*Su,%" \
	| sed -nr "s%(........,..)(..)(.*)%\1:\2\3%p"
}
# }}}
# {{{ Scheduler - Remove Sched
schedule_remove() {
	local SIZE
	local cols
	local rows
	SIZE=$(stty size)
	cols=${SIZE#* }
	rows=${SIZE% *}
	TMPSCHED="$SOMAROOT/tmpschedule"
	cat "$SCHEDULES" > "$TMPSCHED"

	while :; do
		unset sched
		while read -r line; do
			# More ape-like readable stuff
			date=${line%%,*}
			date="${date:6:2}/${date:4:2}/${date:0:4}"
			time=${line:9:5}
			channel=${line:15}
			sched="$sched \"$date $time\" \"$channel\""
		done < "$TMPSCHED"

		[ -z "$sched" ] && break

		sh -c "dialog $DIALOGOPTS \
			--ok-label Delete \
			--cancel-label Done \
			--title \
			\"${TITLECOL}Schedules\" \
			--menu \"${TITLECOL}Remove a Schedule\" \
			$rows $cols $rows \
			$sched" 2> "$TMP" || break

		# Now make it machine readable (ish)
		torem="$(sed 's# #,#' "$TMP")"
		year=${torem:6:4}
		month=${torem:3:2}
		day=${torem:0:2}
		time=${torem:11:5}

		[ "$year" = "****" ] && year="\*\*\*\*"
		[ "$month" = "**" ] && month="\*\*"
		[ "$day" = "**" ] && day="\*\*"

		sed -iE "/^$year$month$day,$time/d" "$TMPSCHED"
	done
	sort_schedules "$TMPSCHED" > "$SCHEDULES"
	rm -f "$TMPSCHED"
}
# }}}
# {{{ Scheduler - Sched Station Changer
schedule_change_station() {
	laststation="$STATIONURL"
	STATIONURL=$(sed -nr "s|^$SCHEDCHAN,(.*)|\1|p" "$STATIONS")

	[ "$SCHEDCHAN" = "$OLDCHANNEL" ] && [ "$CHANNEL" != "Idle" ] && return
	sed -i "/^$DATE,$TIME24,/d" "$SCHEDULES"

	if [ "$SCHEDCHAN" = "Disconnect" ]; then
		stop_mplayer
		OLDCHANNEL="$CHANNEL"
		CHANNEL="Idle"
		STATIONURL="$laststation"
	else
		CHANNEL="$SCHEDCHAN"
		play
		setup
		laststation="$STATIONURL"
	fi
}
# }}}
# {{{ Scheduler - Sched Time
get_schedule_time() {
	while :; do
		dialog $DIALOGOPTS \
			--title "${TITLECOL}Schedule Time" \
			--inputbox "Type in a time in 24 hour format (HH:MM)" \
			10 25 2> "$TMP" || return 1

		sched_time="$(< $TMP)"
		h=${sched_time:0:2}
		c=${sched_time:2:1}
		m=${sched_time:3:2}

		if [ "$h" -gt 23 ] || [ "$m" -gt 60 ] || [ "$c" != ":" ]; then
			do_msg "Set Time" \
				"Error in time format\n\nPlease use HH:MM in 24 hour format" 0
		else
			break
		fi
	done

	get_schedule_recur || return 1
}
# }}}
# {{{ Scheduler - Sched Recurring?
get_schedule_recur() {
	dialog $DIALOGOPTS \
		--menu "${TITLECOL}Recurrance" \
		10 30 3 \
		"Once" "Once Only" \
		"Daily" "Daily Schedule" \
		"Weekly" "Weekly Schedule" \
		2> "$TMP" || return 1

	sched_recur="$(< $TMP)"
}
# }}}
# {{{ Scheduler - Sched Days
get_schedule_days() {
	dialog $DIALOGOPTS \
		--separate-output \
		--checklist "${TITLECOL}Select A Day" \
		14 25 8 \
		"Monday"    " " off \
		"Tuesday"   " " off \
		"Wednesday" " " off \
		"Thursday"  " " off \
		"Friday"   " " off \
		"Saturday"  " " off \
		"Sunday"    " " off \
		2> "$TMP" || return 1

	sched_days="$(tr '\n' ' ' < $TMP)"
	get_sched_station || return 1
}
# }}}
# {{{ Scheduler - Sched Date
get_schedule_date() {
	dialog $DIALOGOPTS \
		--title "${TITLECOL}Schedule Date" \
		--calendar "\nUse tab to traverse windows\n" \
		5 50 2> "$TMP" || return 1

	date="$(< $TMP)"
	day=${date:0:2}
	month=${date:3:2}
	year=${date:6:4}
	sched_date="$year$month$day"
	get_sched_station || return 1
}
# }}}
# }}}
# {{{ Options
# {{{ Edit OPTIONS - Main Menu
edit_options() {
	local SIZE
	local rows
	local cols

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		dialog $DIALOGOPTS \
			--cancel-label "Back" \
			--default-item "$editopt" \
			--menu "${TITLECOL}Manually Edit Settings" \
			"$rows" "$cols" "$rows" \
			"CURSOROFF" "Whether to turn cursor off" \
			"DIALOGOPTS" "Dialog Options" \
			"DIALOGFIX" "Fix Dialog Drawing Problems" \
			"OUTCARD" "Output Device" \
			"SOFTVOL" "MPlayer Software Volume" \
			"FILTER" "MPlayer Audio Filter" \
			"CACHE" "MPlayer Cache" \
			"MIXER" "Mixer Device" \
			"TITLECOL" "Dialog Title Colour" \
			"CONNECTTIMEOUT" "Connection Timeout" \
			"TIMEFORMAT" "Time Format" \
			"CARET" "Caret Character Used In Menus" \
			"SAVETRACKLIST" "Whether To Keep An Ongoing Track List" \
			"SETTERMTITLE" "Whether To Set The Term/Screen Title" \
			"AUTORECONNECT" "Autoreconnect To Lost Streams" \
			"SHOWAUDIOINFO" "Show Stream Audio and Codec Info" \
			"SHOWVOLUME" "Show Current Volume in Title Bar" \
			"MENCODEROPTS" "Mencoder Source Options" \
			"MENCODEROAC" "Mencoder Encoder Options" \
			"AUTOSETBITRATE" "Auto Set Recording Bitrate" \
			"RECEXT" "Mencoder Output Format" \
			"AUTOSETRECEXT" "Try To Set RECEXT Automatically" \
			"MT" "Timeout For Message Windows" \
			"PROXY" "HTTP Proxy" \
			2> "$TMP" || break

		editopt="$(< $TMP)"

		cursor on
		[ "$editopt" = "CURSOROFF" ] && cursoroff
		[ "$editopt" = "DIALOGOPTS" ] && dialogopts
		[ "$editopt" = "DIALOGFIX" ] && dialogfix
		[ "$editopt" = "OUTCARD" ] && outcard
		[ "$editopt" = "FILTER" ] && affilter
		[ "$editopt" = "SOFTVOL" ] && softvol
		[ "$editopt" = "CACHE" ] && cache
		[ "$editopt" = "SAVETRACKLIST" ] && edit_savetracklist
		[ "$editopt" = "MIXER" ] && edit_mixer
		[ "$editopt" = "TITLECOL" ] && titcol
		[ "$editopt" = "CONNECTTIMEOUT" ] && timeout
		[ "$editopt" = "TIMEFORMAT" ] && timeformat
		[ "$editopt" = "CARET" ] && edit_caret
		[ "$editopt" = "SETTERMTITLE" ] && set_termtitle
		[ "$editopt" = "AUTORECONNECT" ] && set_autoreconnect
		[ "$editopt" = "SHOWAUDIOINFO" ] && set_showaudioinfo
		[ "$editopt" = "SHOWVOLUME" ] && set_showvolume
		[ "$editopt" = "MENCODEROPTS" ] && edit_mencoderopts
		[ "$editopt" = "MENCODEROAC" ] && edit_mencoderoac
		[ "$editopt" = "AUTOSETBITRATE" ] && edit_autosetbitrate
		[ "$editopt" = "RECEXT" ] && edit_recext
		[ "$editopt" = "AUTOSETRECEXT" ] && edit_autosetrecformat
		[ "$editopt" = "MT" ] && edit_mt
		[ "$editopt" = "PROXY" ] && edit_proxy

		source "$SOMAOPTIONS"
		[ "$SETTERMTITLE" -eq 1 ] && printf "\033ksoma\033\\"
		cursor off
		unset OLDSTATUS
	done
}
# }}}
# {{{ Edit CURSOROFF
cursoroff() {
	general_checkbox "Cursor off" "Whether to turn off the cursor.
Turning off the cursor looks a bit better in dialog menus, but if you are using
the linux console and have set a custom cursor this will reset it back to the
default hardware cursor, which is undesirable.\n\n
Default: on\n " "CURSOROFF"
}
# }}}
# {{{ Edit DIALOGOPTS
dialogopts() {
	general_inputbox "Dialog Options" "Edit dialog options.\n
Note that removing '--colors' only affects the title bar in the dialog\n
command. It doesn't affect the theme colours.
Add --no-mouse to disable the mouse.\n\nDefault: \"--colors\"\n " "DIALOGOPTS"
}
# }}}
# {{{ Edit DIALOGFIX
dialogfix() {
general_checkbox "Dialog Fix" \
	"Whether to try to fix dialog drawing glitches.\n
\nDefault: off\n " "DIALOGFIX"
	[ "$DIALOGFIX" -eq 1 ] && TERM=xterm-256color || TERM=$ORIGTERM
	cursor off
}
# }}}
# {{{ Edit OUTCARD
outcard() {
	tmp=$OUTCARD

	general_inputbox "Output Card" "Manually edit your output device.\n\n
Default: \"pulse\"\n " "OUTCARD"

#	OUTCARD=$(grep -E "^OUTCARD=" "$SOMAOPTIONS" | cut -d'"' -f2)
	[ "$OUTCARD" = "$tmp" ] && return
	[ -z "$TITLE" ] && [ "$TITLE" != "Idle" ] && return
	cursor off
	play
	setup
	status
}
# }}}
# {{{ Edit SOFTVOL
softvol() {
	general_inputbox "MPlayer Softvol" \
		"Use MPlayer software mixer, or card hardware mixer.\n
Note that MPlayer adds this automatically when needed.\n\n
Default: \"-softvol\"\n " "SOFTVOL"
}
# }}}
# {{{ Edit FILTER
affilter() {
	tmp="$FILTER"
	general_inputbox "MPlayer Audio Filter (Used with -af)" \
		"Set an MPlayer audio filter.\n
Example: 'channels=2' can force single channel mono into 2 channel mono.\n
See man mplayer for more details.\n\n
Default: None\n " "FILTER"

	[ "$FILTER" = "$tmp" ] && return
	if [ -z "$CHANNEL" ] && [ "$CHANNEL" != "Idle" ]; then return; fi
	cursor off
	play
	setup
	status
}
# }}}
# {{{ Edit CACHE
cache() {
	tmp="$CACHE"
	general_num_inputbox "MPlayer Cache" "Set MPlayer cache size.\n\n
Default: 320\n " "CACHE"

	[ "$CACHE" = "$tmp" ] && return
	if [ -z "$CHANNEL" ] && [ "$CHANNEL" != "Idle" ]; then return; fi
	cursor off
	play
	setup
	status
}
# }}}
# {{{ Edit MIXER
edit_mixer() {
	general_inputbox "Mixer" "Set mixer device.\n\nDefault: 0\n " "MIXER"
}
# }}}
# {{{ Edit TITLECOL
titcol() {
	general_inputbox "Dialog Title Settings" \
		"This overrides the title colours in the theme.\n\n
By default it is set to \\\ZZZb (bold foreground colour).\n
You can assign colours here too using \\\ZZZn where 'n' is:\n\n
0: black, 1: red, 2: green, 3: yellow, 4: blue, 5: magenta, 6: cyan,\n
7: white, 8: default foreground.\n\n
Example: \\\ZZZb\\\ZZZ1 for bold red.\n\nDefault: \\\ZZZb\n " "TITLECOL"
}
# }}}
# {{{ Edit CONNECTTIMEOUT
timeout() {
	general_num_inputbox "Connection Timeout" "Set connection timeout value.
This is seconds x 10.\n\n
Default: 150\n " "CONNECTTIMEOUT"
}
# }}}
# {{{ Edit TIMEFORMAT
timeformat() {
	general_inputbox "Time Format" \
		"Set time format.\n\nDefault is 24 hour. %H:%M is 12 hour format.\n
See man date for full options.\n\n
Default: %I:%M\n " "TIMEFORMAT"
}
# }}}
# {{{ Edit CARET
edit_caret() {
	general_inputbox "Caret" "Set the caret used in menus.\n\n
Default: \"-->\"\n " "CARET"
}
# }}}
# {{{ Edit SAVETRACKLIST
edit_savetracklist() {
	tmp="$SAVETRACKLIST"
	general_checkbox "Save Playlist" \
		"Whether to save an ongoing track list.\n
The list is located at ~/.soma/tracklist\n\n
Default: off\n " "SAVETRACKLIST"

	if [ "$SAVETRACKLIST" -eq 1 ] && [ "$tmp" -eq 0 ]; then
		grep -hE "^(Name|Title)" "$STATUS" >> "$TRACKLIST"
	fi
}
# }}}
# {{{ Edit SETTERMTITLE
set_termtitle() {
	general_checkbox "Term Title" \
		"Whether to set the terminal/screen title to \"soma\".\n\n
Default: on\n " "SETTERMTITLE"
}
# }}}
# {{{ Edit AUTORECONNECT
set_autoreconnect() {
	general_checkbox "Autoreconnect" "Whether to autoreconnect to a lost stream.
Normally, when you lose connection to a stream, you will get a dialog box popup
which gives you the choice to reconnect or not. If this setting is checked then
soma will try to reconnect automatically 10 times and skip the popup unless it
fails.\n\n
Default: off\n " "AUTORECONNECT"
}
# }}}
# {{{ Edit SHOWAUDIOINFO
set_showaudioinfo() {
	tmp="$SHOWAUDIOINFO"
	general_checkbox "Show Audio Info" \
		"Whether to show the stream audio and codec info.\n\n
Default: on\n " "SHOWAUDIOINFO"

	[ "$SHOWAUDIOINFO" = "$tmp" ] && return

	if [ "$SHOWAUDIOINFO" -eq 1 ]; then
		BITRATE="$(sed -nr "s/^Bitrate\s*: ([^:].*)/\1/p" "$MSTATUS")"
		AUDIO="$(sed -nr "s/^AUDIO\s*: (.*)/\1/p" "$MSTATUS")"
		CODEC="$(sed -nr "s/.*codec: (.*[^:].*)/\1/p" "$MSTATUS")"
		[ -n "$BITRATE" ] && log "Bitrate : $BITRATE"
		[ -n "$AUDIO" ] &&   log "Audio   : $AUDIO"
		[ -n "$CODEC" ] &&   log "Codec   : $CODEC"
	else
		sed -i "/Bitrate :/d;/Audio   :/d;/Codec   :/d" "$STATUS"
	fi
}
# }}}
# {{{ Edit SHOWVOLUME
set_showvolume() {
	general_checkbox "Show Volume in Title Bar" \
		"Whether to show the volume in the title bar: [N]\n\n
Default: on\n " "SHOWVOLUME"
}
# }}}
# {{{ Edit MENCODEROPTS
edit_mencoderopts() {
	general_inputbox "Mencoder Options" \
		"Define custom mencoder options.\n
See --record-info for info.\n\n
Default:
\"-demuxer rawvideo -rawvideo w=1:h=1 -ovc copy -of rawaudio -audiofile\"\n" \
	"MENCODEROPTS"
}
# }}}
# {{{ Edit MENCODEROAC
edit_mencoderoac() {
	general_inputbox "Mencoder Output Audio Codec Options" \
		"Define custom mencoder output audio codec.\n
See --record-info for info.\n\n
Default: \"-oac copy\"\n" "MENCODEROAC"
}
# }}}
# {{{ Edit AUTOSETBITRATE
edit_autosetbitrate() {
	general_checkbox "Automatically Set Recording Bitrate" \
		"If 'lavc' is used then try to get the source bitrate and apply it to the\n
output encoding (only if one isn't already set). Curl is used to grab the\n
header from the stream URL and the bitrate is found if 'icy-br' is set.\n
Warning: This may fail!\n\n
Default: on\n " "AUTOSETBITRATE"
}
# }}}
# {{{ Edit RECEXT
edit_recext() {
	general_inputbox "Mencoder File Extension" \
		"Which file extension to use for recordings. mp3, ac3, mp2 etc.\n
See --record-info for info.\n\n
Default: \"mp3\"\n" "RECEXT"
}
# }}}
# {{{ Edit AUTOSETRECEXT
edit_autosetrecformat() {
	general_checkbox "Try To Set RECEXT Automatically" \
		"Soma will try to find the file extension to use for recordings
when using -oac lavc.\n
See --record-info for info.\n\n
Default: on\n" "AUTOSETRECEXT"
}
# }}}
# {{{ Edit MT
edit_mt() {
	general_num_inputbox "Message Timeout" \
		"Timeout for message windows in seconds.\n\n
Default: 3\n " "MT"
}
# }}}
# {{{ Edit PROXY
edit_proxy() {
	general_inputbox "HTTP Proxy" \
		"HTTP Proxy for mplayer and mencoder to use.\n\n
Default: \"\"\n" "PROXY"
}
# }}}
# }}}
# {{{ Genres
# {{{ Genres - Main menu
main_genre_menu() {
	local SIZE
	local rows
	local cols

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		dialog $DIALOGOPTS \
			--title "${TITLECOL}Genre Options ($GENRE)" \
			--cancel-label "Back" \
			--default-item "$genreopt" \
			--menu "" "$rows" "$cols" "$rows" \
			"Genres" "List Genres" \
			"Local Genres" "List Local Genres" \
			"List Stations" "List Stations in Current Genre" \
			"Add/Remove/Sort" "Add/Remove/Sort Stations in Current Genre" \
			"New" "Create New Genre" \
			"Remove Genre" "Remove a (Local) Genre" \
			"Edit Genre" "Edit Local Genre" \
			2> "$TMP" || break

		genreopt="$(< $TMP)"

		[ "$genreopt" = "Genres" ] && genre_selector
		[ "$genreopt" = "Local Genres" ] && local_genre_selector
		[ "$genreopt" = "List Stations" ] && genre_station_selector
		[ "$genreopt" = "Add/Remove/Sort" ] && add_stations_to_genre
		[ "$genreopt" = "New" ] && new_genre
		[ "$genreopt" = "Remove Genre" ] && remove_genre
		[ "$genreopt" = "Edit Genre" ] && edit_genre
	done
}
# }}}
# {{{ Genres - Genre Selector
genre_selector() {
	[ -z "$GENRE" ] && GENRE="All"
	local oldgenre
	oldgenre="$GENRE"
	cursor off

	find {"$USRGENREDIR","$SYSGENREDIR"} -name "*.conf" \
		| sed -nr "s%^.*/(.*)\.conf%\1%p" | sort -u > "$TMP"

	nice_list "Genres" "Select" || return

	GENRE="$(sed 's# *$##' $TMP)"

	sed -i "s#^GENRE=.*#GENRE=\"$GENRE\"#" "$SOMAOPTIONS"
	if [ "$GENRE" != "$oldgenre" ]; then
		station_selector $__GENRE
	fi
}
# }}}
# {{{ Genres - Local Genre Selector
local_genre_selector() {
	local cols
	local GEN
	local retval
	local oldgenre
	oldgenre="$GENRE"
	cursor off

	while :; do
		find "$USRGENREDIR" -name "*.conf" \
			| sed -nr "s%^.*/(.*)\.conf%\1%p" | sort -u > "$TMP"

		if [ -z "$(< $TMP)" ]; then
			do_msg "List Genres" "No local genres to list." $MT
			return
		fi

		nice_list "Genres ($GENRE)" "Select" "Delete"
		retval=$?
		[ "$retval" -eq 1 ] || [ $retval -eq 255 ] && return

		if [ "$retval" -eq 2 ]; then
			GEN=$(sed -r "s%^HELP %%;s%\s+$%%" "$TMP")
			cols=$((${#GEN}+21))

			dialog --title "Delete Genre" \
				--yesno "Really delete \"$GEN\"?" \
				5 "$cols" 2>/dev/null && rm -f "$USRGENREDIR/$GEN.conf"

			[ -z "$(find "$USRGENREDIR" -name '*.conf')" ] && return
		else
			GENRE="$(sed 's# *$##' $TMP)"
			sed -i "s#^GENRE=.*#GENRE=\"$GENRE\"#" "$SOMAOPTIONS"
			break
		fi
	done
	if [ "$GENRE" != "$oldgenre" ]; then
		station_selector $__GENRE
	fi
}
# }}}
# {{{ Genres - Station Selector
genre_station_selector() {
	WITHURL=1
	station_selector $__GENRE
	unset WITHURL
}
# }}}
# {{{ Genres - Add/Remove Stations
add_stations_to_genre() {
	local SIZE
	local rows
	local cols
	local option

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		dialog $DIALOGOPTS \
			--title "${TITLECOL}Add/Remove Stations ($GENRE)" \
			--cancel-label "Back" \
			--menu "" "$rows" "$cols" "$rows" \
			"Add" "Add From Station List" \
			"New" "Add New Radio Station Manually" \
			"Sort" "Sort Stations" \
			"Remove" "Remove Station From Genre" \
			2> "$TMP" || break

		option="$(< $TMP)"

		[ "$option" = "Add" ] && add_station_from_list
		[ "$option" = "New" ] && man_add_station
		[ "$option" = "Sort" ] && sort_stations
		[ "$option" = "Remove" ] && remove_stations
	done
}
# }}}
# {{{ Genres - Add Station From List
add_station_from_list() {
	local option
	local CHAN
	local STAT
	local SYSTMP
	local USRTMP

	get_station_list $__ALL

	while :; do
		SYSTMP="$SYSGENREDIR/$GENRE.conf"
		USRTMP="$USRGENREDIR/$GENRE.conf"

		if [ ! -r "$USRTMP" ] && [ -r "$SYSTMP" ]; then
			cat "$SYSTMP" > "$USRTMP"
		fi

		printf "%s" "$STATIONLIST" | sed 's| \\||g' | cut -d'"' -f2 > "$TMP"
		nice_list "Stations (All)" "Add" || return

		CHAN=$(sed "s|\s+$||" "$TMP")
		STAT=$(grep -E "^$CHAN," "$STATIONS" | head -n 1)

		IFS=$'\n'

		if [ -r "$USRTMP" ]; then
			if ! grep -qE "^$STAT" "$USRTMP"; then
				printf "%s\n" "$STAT" >> "$USRTMP"
				do_msg "Add New" "$CHAN added." $MT
			else
				do_msg "Add New" "$CHAN already in local \"$GENRE\"" $MT
			fi
		elif [ -r "$SYSTMP" ]; then
			if ! grep -qE "^$STAT" "$SYSTMP"; then
				cat "$SYSTMP" > "$USRTMP"
				printf "%s\n" "$STAT" >> "$USRTMP"
				do_msg "Add New" "$CHAN added." $MT
			else
				do_msg "Add New" "$CHAN already in global \"$GENRE\"." $MT
			fi
		else
			printf "%s\n" "$STAT" >> "$USRTMP"
			do_msg "Add New" "$CHAN added." $MT
		fi

		# If local genre list is identical to the global version then ditch it
		if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
			"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
			rm -f "$USRTMP"
		fi

		IFS="$DEFIFS"
	done
}
# }}}
# {{{ Genres - Add Current Station
add_current_station_to_genre() {
	local SYSTMP
	local USRTMP
	local STAT

	[ "$CHANNEL" = "Idle" ] || [ -z "$CHANNEL" ] && return
	if [ "$GENRE" = "All" ]; then
		do_msg "Error" "You have not selected a genre." $MT
		return
	fi

	SYSTMP=$SYSGENREDIR/"$GENRE.conf"
	USRTMP=$USRGENREDIR/"$GENRE.conf"

	IFS=$'\n'
	STAT=$(grep -E "^$CHANNEL," "$STATIONS")

	if [ -r "$USRTMP" ]; then
		if ! grep -E "^$STAT" "$USRTMP"; then
			printf "%s" "$STAT" >> "$USRTMP"
			do_msg "Add New" "\"$CHANNEL\" added to \"$GENRE\"." $MT
		else
			do_msg "Add New" "\"$CHANNEL\" already in \"$GENRE\"" $MT
		fi
	elif [ -r "$SYSTMP" ]; then
		cat "$SYSTMP" > "$USRTMP"
		if ! grep -E "^$STAT" "$USRTMP"; then
			printf "%s" "$STAT" >> "$USRTMP"
			do_msg "Add New" "\"$CHANNEL\" added to \"$GENRE\"." $MT
		else
			do_msg "Add New" "\"$CHANNEL\" already in \"$GENRE\"." $MT
		fi
	else
		printf "%s" "$STAT" >> "$USRTMP"
		do_msg "Add New" "\"$CHANNEL\" added to \"$GENRE\"." $MT
	fi

	if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
		"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
		rm -f "$USRTMP"
	fi

	IFS="$DEFIFS"
}
# }}}
# {{{ Genres - Manually Add Station
man_add_station() {
	local option
	local name
	local URL
	local SYSTMP
	local USRTMP
	SYSTMP=$SYSGENREDIR/"$GENRE.conf"
	USRTMP=$USRGENREDIR/"$GENRE.conf"

	while :; do
		cursor on
		dialog $DIALOGOPTS \
			--title "${TITLECOL}Add New Station" \
			--inputbox "Name for this station" \
			10 60 2> "$TMP" \
			|| { cursor off; return; }

		name="$(< $TMP)"
		[ -z "$name" ] && { cursor off; return; }

		dialog $DIALOGOPTS \
			--title "${TITLECOL}Add Bookmark" \
			--inputbox "URL" \
			10 60 2> "$TMP" \
			|| { cursor off; return; }

		URL="$(< $TMP)"
		[ -z "$URL" ] && { cursor off; return; }

		cursor off
		IFS=$'\n'

		if [ -r "$USRTMP" ]; then
			if ! grep -E "^$name," "$USRTMP"; then
				printf "%s\n" "$name,$URL" >> "$USRTMP"
				do_msg "Add New" "Station Added." $MT
				if ! grep -E "^$name,$URL$" "$USRSTATIONS"; then
					printf "%s\n" "$name,$URL" >> "$USRSTATIONS"
				fi
			else
				do_msg "Add New" "Station already in local \"$GENRE\"." $MT
			fi
		elif [ -r "$SYSTMP" ]; then
			if ! grep -E "^$name," "$SYSTMP"; then
				cat "$SYSTMP" > "$USRTMP"
				printf "%s\n" "$name,$URL" >> "$USRTMP"
				do_msg "Add New" "Station Added." $MT
				if ! grep -E "^$name,$URL$" "$USRSTATIONS"; then
					printf "%s\n" "$name,$URL" >> "$USRSTATIONS"
				fi
			else
				do_msg "Add New" "Station already in global \"$GENRE\"." $MT
			fi
		fi

		IFS="$DEFIFS"
	done
}
# }}}
# {{{ Genres - Sort Stations
sort_stations() {
	SYSTMP="$SYSGENREDIR/$GENRE.conf"
	USRTMP="$USRGENREDIR/$GENRE.conf"

	if [ ! -r "$USRTMP" ] && [ -r "$SYSTMP" ]; then
			cat "$SYSTMP" > "$USRTMP"
	fi

	cat "$USRTMP" > "$SOMAROOT"/sort_infile
	general_sort $__GENRE && cat "$TMP" > "$USRGENREDIR/$GENRE.conf"

	# If identical to global genre then ditch it
	if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
		"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
		rm -f "$USRTMP"
	fi
}
# }}}
# {{{ Genres - Remove Single Station
remove_single_station() {
	local SYSTMP
	local USRTMP

	if [ "$2" -eq $__BOOKMARKS ]; then
		USRTMP=$BOOKMARKS
	elif [ "$2" -eq $__GENRE ]; then
		SYSTMP=$SYSGENREDIR/"$GENRE.conf"
		USRTMP=$USRGENREDIR/"$GENRE.conf"

		if [ ! -r "$USRTMP" ]; then
			if [ -r "$SYSTMP" ]; then
				cat "$SYSTMP" > "$USRTMP"
			else
				return 1
			fi
		fi
	elif [ "$2" -eq $__LOCAL ]; then
		USRTMP=$USRSTATIONS
	elif [ "$2" -eq $__RECORDINGS ]; then
		if dialog --title "Delete File" \
			--yesno "Really delete\n\n\"$1\"?" \
			8 "$((${#1}+25))" 2>/dev/null; then
			rm "$RECSDIR/$1"
			sed -i "/^$1$/d" "$RECSLIST"
		fi
		return
	fi

	sed -i "/^$1,/d" "$USRTMP"
	if [ "$2" -eq $__BOOKMARKS ]; then
		read_bookmarks
	fi

	IFS=$'\n'

	if [ "$2" -eq $__GENRE ]; then
		if [ "$(wc -l "$USRTMP" | awk '{print $1}')" -eq 0 ]; then
			rm -f "$USRTMP"
			IFS="$DEFIFS"
			return 1
		fi
		if [ "$(md5sum "$USRTMP" | awk '{print $1}')" = \
			"$(md5sum "$SYSTMP" | awk '{print $1}')" ]; then
			rm -f "$USRTMP"
			IFS="$DEFIFS"
			return 1
		fi
	fi

	IFS="$DEFIFS"
}
# }}}
# {{{ Genres - Remove Multiple Stations
remove_stations() {
	unset option
	local SYSTMP
	local USRTMP
	SYSTMP="$SYSGENREDIR/$GENRE.conf"
	USRTMP="$USRGENREDIR/$GENRE.conf"

	if [ ! -r "$USRTMP" ]; then
		if [ -r "$SYSTMP" ]; then
			cat "$SYSTMP" > "$USRTMP"
		else
			do_msg "Delete Station" "$GENRE is empty." $MT
			return
		fi
	fi

	TMPLIST="$USRTMP"
	general_delete $__GENRE
}
# }}}
# {{{ Genres - New Genre
new_genre() {
	local option
	local name

	cursor on
	dialog $DIALOGOPTS \
		--title "${TITLECOL}New Genre" \
		--inputbox "Name for new genre" \
		10 30 2> "$TMP" \
		|| { cursor off; return; }

	cursor off
	name="$(< $TMP)"
	name=${name#"${name%%[![:space:]]*}"}
	name=${name%"${name##*[![:space:]]}"}

	if [ -r "$USRGENREDIR/$name.conf" ]; then
		do_msg "Error" "Local \"$name.conf\" already exists." $MT
	elif [ -r "$SYSGENREDIR/$name.conf" ]; then
		do_msg "Error" "Global \"$name.conf\" already exists." $MT
	else
		touch "$USRGENREDIR/$name.conf"
		do_msg "New Genre" "New genre \"$name\" created." $MT
		GENRE="$name"
	fi
}
# }}}
# {{{ Genres - Remove Local Genre
remove_genre() {
	local SIZE
	local rows
	local cols
	local option
	local GEN
	unset GENRES

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		find "$USRGENREDIR" -name \*\.conf \
			| rev | cut -d/ -f1 | cut -d. -f2- | rev 2>/dev/null > "$TMP"
		[ -z "$(< $TMP)" ] && \
			do_msg "Remove Genre" "No local genres to remove." $MT && return

		nice_list "Delete Genre" "Delete" || return

		GEN=$(sed -r "s%\s+$%%" "$TMP")
		cols=$((${#GEN}+40))

		dialog --title "Delete Genre" \
			--yesno "Really delete \"$GEN\"?" \
			5 "$cols" 2>/dev/null

		if [ $? -eq 0 ]; then
			rm -f "$USRGENREDIR/$GEN".conf
			if [ "$GEN" = "$GENRE" ]; then
				GENRE="All"
				sed -i "s#^GENRE=.*#GENRE=\"$GENRE\"#" "$SOMAOPTIONS"
			fi
		fi

		[ -z "$(ls -1 "$USRGENREDIR"/*conf 2>/dev/null)" ] && return
	done
}
# }}}
# {{{ Genres - Edit Local Genre
edit_genre() {
	local SYSTMP
	local USRTMP
	SYSTMP=$SYSGENREDIR/"$GENRE.conf"
	USRTMP=$USRGENREDIR/"$GENRE.conf"

	if [ "$GENRE" = "All" ]; then
		do_msg "Edit Genre" "You current genre is set to 'All'.\n
You need to select one from the list before you can edit it." $MT
	return
	fi

	if [ ! -r "$USRTMP" ] && [ -r "$SYSTMP" ]; then
		cat "$SYSTMP" > "$USRTMP"
	fi

	general_edit_file "$USRTMP"
}
# }}}
# }}}
# {{{ Themes
# TODO: Add support for styling the various parts of the dash
# {{{ Theme - Main Menu
main_theme() {
	local SIZE
	local rows
	local cols
	local CUR

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }
		CUR=$(grep "^export DIALOGRC=" "$SOMAOPTIONS" | cut -d= -f2)
		[ "$CUR" != "/dev/null" ] && CUR=$(basename "$CUR")

		dialog $DIALOGOPTS \
			--cancel-label "Back" \
			--default-item "$themeopt" \
			--menu "${TITLECOL}Theme Options ($CUR)" "$rows" "$cols" "$rows" \
			"Themes" "Choose a Theme" \
			"Edit" "Edit Current Theme" \
			"Remove" "Remove a (Local) Theme" \
			2> "$TMP" || break

		themeopt="$(< $TMP)"

		[ "$themeopt" = "Themes" ] && loop_themes
		[ "$themeopt" = "Edit" ] && edit_theme
		[ "$themeopt" = "Remove" ] && remove_theme
	done
}
# }}}
# {{{ Theme - Loop themes
loop_themes() {
	RET=0
	while :; do
		choose_theme
		[ "$RET" -gt 0 ] && break
	done
}
# }}}
# {{{ Theme - Choose theme
choose_theme() {
	local CUR
	local THM
	local THEME

	CUR=$(grep -E "^export DIALOGRC=" "$SOMAOPTIONS" | cut -d= -f2)
	[ "$CUR" != "/dev/null" ] && CUR=$(basename "$CUR")

	find {"$SYSTHEMEDIR","$USRTHEMEDIR"} -name \*rc \
		| rev | cut -d/ -f1 | rev | sort -u > "$TMP"
	printf "%s\n" "/dev/null" >> "$TMP"
	nice_list "Change your dialog theme ($CUR)" "Select"

	THM="$(< $TMP)"
	[ -z "$THM" ] && RET=1 && return
	THM=${THM%% *}
	THEME=$THM

	# Look for a global theme first then check to see if a local exists with
	# the same name
	for th in "$SYSTHEMEDIR"/*rc
	do
		[ "$THM" = "$(basename "$th")" ] && THEME="$th"
	done

	for th in "$USRTHEMEDIR"/*rc
	do
		[ "$THM" = "$(basename "$th")" ] && THEME="$th"
	done

	sed -i "s#^export DIALOGRC=.*#export DIALOGRC=$THEME#" "$SOMAOPTIONS"
	source "$SOMAOPTIONS"
}
# }}}
# {{{ Theme - Edit Theme
edit_theme() {
	local FILE

	if [ -n "$DIALOGRC" ]; then
		FILE=$(basename "$DIALOGRC")
	else
		FILE=$LASTTHEME
	fi

	if [ ! -r "$USRTHEMEDIR/$FILE" ]; then
		if [ -r "$SYSTHEMEDIR/$FILE" ]; then
			cat "$SYSTHEMEDIR/$FILE" > "$USRTHEMEDIR/$FILE"
			do_msg "Themes" "Local copy of $FILE created" $MT
			export DIALOGRC="$USRTHEMEDIR/$FILE"
		elif [ "$DIALOGRC" = "/dev/null" ]; then
				FILE="devnullrc"
				if [ ! -r "$USRTHEMEDIR/$FILE" ]; then
					dialog --create-rc "$USRTHEMEDIR/$FILE"
					do_msg "Themes" "Local copy of /dev/null created ($FILE)" $MT
				fi
				export DIALOGRC="$USRTHEMEDIR/$FILE"
		fi
	fi

	[ -z "$EDITOR" ] && EDITOR=/usr/bin/vim

	if [ -f "$USRTHEMEDIR/$FILE" ]; then
		cursor on
		[ "$DIALOGFIX" -eq 1 ] && TERM=xterm-256color
		"$EDITOR" "$USRTHEMEDIR/$FILE"
		cursor off

		if dialog \
			--msgbox "Theme parsed OK. Press enter to continue." \
			5 45; then
			export DIALOGRC="$USRTHEMEDIR/$FILE"
			unset LASTTHEME
		else
			LASTTHEME="$FILE"
			unset DIALOGRC
			do_msg "Error" "Parse error in $FILE.\n
Theme has been unset until you fix the errors or choose another theme." 0
		fi
	fi
}
# }}}
# {{{ Theme - Remove a Theme
remove_theme() {
	local cols
	local option
	local THM

	while :; do
		find "$USRTHEMEDIR" -name "*rc" | rev | cut -d/ -f1 | rev > "$TMP"
		[ -z "$(< $TMP)" ] && do_msg \
			"Remove Theme" "No local themes to remove." $MT && return

		nice_list "Remove a Theme" "Delete" || return

		THM="$(< $TMP)"
		[ -z "$THM" ] && RET=1 && return
		THM=${THM%% *}

		cols=$((${#GEN}+40))

		dialog --title "Delete Theme" \
			--yesno "Really delete $THM?" \
			5 "$cols" 2>/dev/null \
			&& rm -f "$USRTHEMEDIR/$THM"

		! ls -1 "$USRTHEMEDIR"/*rc 2>/dev/null && return
	done
}
# }}}
# }}}
# {{{ Bookmarks
# {{{ Bookmarks - Main Menu
main_bookmarks() {
	local SIZE
	local rows
	local cols

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		dialog $DIALOGOPTS \
			--cancel-label "Back" \
			--default-item "$bookmarkopt" \
			--menu "${TITLECOL}Bookmarks Menu" \
			"$rows" "$cols" "$rows" \
			"View Bookmarks" "View" \
			"Add Bookmark From Station List" "Add" \
			"Add Bookmark Manually" "Add" \
			"Sort Bookmarks" "Sort" \
			"Delete Bookmarks" "Delete" \
			"Edit Bookmarks" "Edit Bookmarks File" \
			2> "$TMP" || return

		bookmarkopt="$(< $TMP)"

		[ "$bookmarkopt" = "View Bookmarks" ] && view_bookmarks
		[ "$bookmarkopt" = "Add Bookmark From Station List" ] && add_bookmark_from_list
		[ "$bookmarkopt" = "Add Bookmark Manually" ] && man_add_bookmark
		[ "$bookmarkopt" = "Sort Bookmarks" ] && sort_bookmarks
		[ "$bookmarkopt" = "Delete Bookmarks" ] && delete_bookmarks
		[ "$bookmarkopt" = "Edit Bookmarks" ] && edit_bookmarks
		read_bookmarks
	done
}
# }}}
# {{{ Bookmarks - View Bookmarks
view_bookmarks() {
	if [ ! -r "$BOOKMARKS" ]; then
		do_msg "Bookmarks" "No bookmarks file." $MT
		return
	fi

	if [ -z "$(< $BOOKMARKS)" ]; then
		do_msg "Bookmarks" "Bookmarks file is empty." $MT
		return
	fi

	station_selector $__BOOKMARKS
	unset WITHURL
}
# }}}
# {{{ Bookmarks - Add Bookmark From List
add_bookmark_from_list() {
	local option
	local STAT
	local CHAN

	get_station_list $__ALL

	while :; do
		printf "%s" "$STATIONLIST" | sed 's| \\||g' | cut -d'"' -f2 > "$TMP"
		nice_list "Stations (All)" "Add" || return

		CHAN=$(sed -r 's|\s+$||g' "$TMP")
		STAT=$(grep "^$CHAN," "$STATIONS" | head -n 1)

		if ! grep -qs "^$STAT$" "$BOOKMARKS"; then
			printf "%s\n" "$STAT" >> "$BOOKMARKS"
			do_msg "Add Bookmark" "$CHAN added." $MT
		else
			do_msg "Add Bookmark" "$CHAN already in bookmark list." $MT
		fi
	done
}
# }}}
# {{{ Bookmarks - Manually Add Bookmark
man_add_bookmark() {
	while :; do
		general_add_station "$BOOKMARKS" || return
	done
}
# }}}
# {{{ Bookmarks - Add Bookmark
add_bookmark() {
	[ "$CHANNEL" = "Idle" ] || [ -z "$CHANNEL" ] && return
	if [ -r "$BOOKMARKS" ]; then
		if grep -qE "^$CHANNEL," "$BOOKMARKS"; then
			do_msg "Add Bookmark" "\"$CHANNEL\" already bookmarked!" $MT
			return
		fi
	fi

	STATIONURL=$(printf "%s" "$STATIONLIST" | grep -w "$CHANNEL" \
		| cut -d'"' -f4 | head -n 1)
	printf "%s\n" "$CHANNEL,$STATIONURL" >> "$BOOKMARKS"
	read_bookmarks

	do_msg "Add Bookmark" "\"$CHANNEL\" bookmarked." $MT
}
# }}}
# {{{ Bookmarks - Delete Bookmarks
delete_bookmarks() {
	TMPLIST="$BOOKMARKS"
	general_delete $__BOOKMARKS
}
# }}}
# {{{ Bookmarks - Sort Bookmarks
sort_bookmarks() {
	if [ ! -r "$BOOKMARKS" ]; then
		do_msg "Sort Bookmarks" "No bookmarks file." $MT
		return
	fi

	cat "$BOOKMARKS" > "$SOMAROOT"/sort_infile
	general_sort $__BOOKMARKS && cat "$TMP" > "$BOOKMARKS"
}
# }}}
# {{{ Bookmarks - Edit File
edit_bookmarks() {
	general_edit_file "$BOOKMARKS"
}
# }}}
# Read Bookmarks{{{
read_bookmarks() {
	unset bookmark[@]
	n=1

	if [ -n "$(< $BOOKMARKS)" ]; then
		while IFS=$'\n,' read -r name book; do
			bookmark[$n]="$name"
			((n++))
			[ $n -eq 13 ] && break
		done <"$BOOKMARKS"
	fi
} #}}}
# }}}
# {{{ User Stations
# {{{ User Stations - Main Menu
user_stations() {
	local SIZE
	local rows
	local cols

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		dialog $DIALOGOPTS \
			--cancel-label "Back" \
			--default-item "$usrstationsopt" \
			--menu "${TITLECOL}User Stations Menu" \
			"$rows" "$cols" "$rows" \
			"View Stations" "View" \
			"Add Station Manually" "Add" \
			"Sort Stations" "Sort" \
			"Delete Stations" "Delete" \
			"Edit Stations" "Edit User Stations File" \
			2> "$TMP" || return

		usrstationsopt="$(< $TMP)"

		[ "$usrstationsopt" = "View Stations" ] && view_user_stations
		[ "$usrstationsopt" = "Add Station Manually" ] && man_add_user_station
		[ "$usrstationsopt" = "Sort Stations" ] && sort_user_stations
		[ "$usrstationsopt" = "Delete Stations" ] && delete_user_stations
		[ "$usrstationsopt" = "Edit Stations" ] && edit_user_stations
	done
}
# }}}
# {{{ User Stations - View Stations
view_user_stations() {
	if [ -z "$(< $USRSTATIONS)" ]; then
		do_msg "User Stations" "User stations file is empty." $MT
		return
	fi

	WITHURL=1
	station_selector $__LOCAL
	unset WITHURL
}
# }}}
# {{{ User Stations - Manually Add Station
man_add_user_station() {
	while :; do
		general_add_station "$USRSTATIONS" || return
	done
}
# }}}
# {{{ User Stations - Sort Stations
sort_user_stations() {
	cat "$USRSTATIONS" > "$SOMAROOT"/sort_infile
	general_sort $__LOCAL && cat "$TMP" > "$USRSTATIONS"
}
# }}}
# {{{ User Stations - Delete Stations
delete_user_stations() {
	TMPLIST="$USRSTATIONS"
	general_delete $__LOCAL
}
# }}}
# {{{ User Stations - Edit File
edit_user_stations() {
	general_edit_file "$USRSTATIONS"
}
# }}}
# }}}
# {{{ Recordings
# {{{ Recording - Main Menu
recording_main() {
	local SIZE
	local rows
	local cols

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }

		dialog $DIALOGOPTS \
			--cancel-label "Back" \
			--default-item "$recordingsopt" \
			--menu "${TITLECOL}Recordings Menu" \
			"$rows" "$cols" "$rows" \
			"View Recordings" "View" \
			"Start Recording" "Start" \
			"Stop Recording" "Stop" \
			"Delete Recordings" "Delete" \
			2> "$TMP" || return

		recordingsopt="$(< $TMP)"

		[ "$recordingsopt" = "Start Recording" ] && start_recording
		[ "$recordingsopt" = "Stop Recording" ] && stop_recording
		[ "$recordingsopt" = "View Recordings" ] && view_recordings
		[ "$recordingsopt" = "Delete Recordings" ] && delete_recordings
	done
}
# }}}
# {{{ Recording - Start Recording
start_recording() {
	local FOUT
	local ENCOPTS
	local RECURL
	local BR
	local MENLOG

	[ -n "$RECORDING" ] && { stop_recording; return; }
	[ -n "$PLAYBACK" ] && return
	mp_running || { do_msg "Error" "Nothing to record" $MT; return; }

	if [ "$AUTOSETRECEXT" -eq 1 ]; then
		FFORMAT=$(sed -nr "s%.*acodec=([^: ]*).*$%\1%p" <<< "$MENCODEROAC")
	fi

	[ -z "$FFORMAT" ] && FFORMAT="$RECEXT"

	RECFILE="${CHANNEL// /_}_$(date +"%Y%m%d_%H%M%S").$FFORMAT"
	FOUT="$RECSDIR/$RECFILE"
	ENCOPTS="$MENCODEROAC"
	MENLOG="$SOMADIR/recordings/.mencoder.log"

	# If we are playing a playlist we need to wget it and find the real URL
	if [ -n "$PLAYLIST" ]; then
		wget -q "$STATIONURL" -O "$SOMAROOT/playlist"
		RECURL=$(sed -nr \
			"0,/http[s]?:/s%^.*(http[s]?://.*)%\1%p" \
			"$SOMAROOT/playlist" | tr -d '\r')
	else
		RECURL="$STATIONURL"
	fi

	if [ "$AUTOSETBITRATE" -eq 1 ]; then
		# Attempt to get source bitrate
		curl -s -D "$SOMAROOT/icy-header" "$RECURL"
		BR=$(sed -nr "s%^icy-br:(.*)%\1%p" \
			"$SOMAROOT/icy-header" | tr -d '\r')

		if [ -n "$BR" ]; then
			if printf "%s" "$MENCODEROAC" | grep -q "lavcopts"; then
				if ! printf "%s" "$MENCODEROAC" | grep -q "abitrate"; then
					IFS=' ' read -ra opts <<< "$MENCODEROAC"

					for index in "${!opts[@]}"
					do
						if [ "${opts[index]}" = "-lavcopts" ]; then
							opts[$((index+1))]="${opts[$((index+1))]}:abitrate=$BR"
							break
						fi
					done
					ENCOPTS="${opts[*]}"
				fi
			fi
		fi
	fi

	printf "%s\n" "$RECURL" > "$SOMADIR/recordings/.mencoder.url"
	printf "%s\n" "mencoder $MENCODEROPTS \"$RECURL\" -o \"$FOUT\" \
		$ENCOPTS \"$RECURL\"" > "$SOMADIR/recordings/.mencoder.cmd"

	mencoder $MENCODEROPTS "$RECURL" -o "$FOUT" \
		$ENCOPTS "$RECURL" 1>$MENLOG 2>&1 &

	MENPID=$!
	# give some time for mencoder to start up and then get the child pid too
	sleep 2

	if ! ps h -o pid --quick-pid "$MENPID" 1>/dev/null; then

		DIALOGOPTS="$DIALOGOPTS --cr-wrap" do_msg "Recording" \
			"Error while trying to record stream:\n\n$(tail $MENLOG)" 0

		MENPID=$(ps wwh | grep "$RECURL" | grep -v grep | cut -d' ' -f1)
		stop_mencoder
		rm -f "$FOUT"
		return
	fi

	RECORDING=1
	do_msg "Recording" "Recording started to $RECFILE" $MT
}
# }}}
# {{{ Recording - Stop Recording
stop_recording() {
	[ -z "$RECORDING" ] && return
	stop_mencoder 1>/dev/null 2>&1
	do_msg "Recording" "Recording stopped to $RECFILE" $MT
}
# }}}
# {{{ Recording - View Recordings
view_recordings() {
	find "$RECSDIR" -name "*" -not -name ".*" -type f \
		| sort | cut -d'/' -f 6 > "$RECSLIST"

	if [ -z "$(< $RECSLIST)" ]; then
		do_msg "Recordings" "No recordings found." $MT
		return
	fi

	if [ -n "$RECORDING" ]; then
		sed -i "s%$RECFILE%&*%" "$RECSLIST"
	fi

	station_selector $__RECORDINGS
}
# }}}
# {{{ Recording - Playback Recording
play_rec() {
	[ -n "$(tail -n1 $STATUS)" ] && [ "$SHOWAUDIOINFO" -eq 1 ] && log ""
	stop_mplayer

	if [ ! -r "$FIFO" ]; then
		mkfifo "$FIFO"
	fi

	OLDCHANNEL="$STATIONURL"
	rm -f "$MSTATUS"
	[ -n "$SETVOL" ] && VOL="-volume $SETVOL"

	mplayer -ao "$OUTCARD" -slave -input file="$FIFO" -msglevel all=4 \
		"$STATIONURL" -cache "$CACHE" -noautosub \
		"$SOFTVOL" $VOL > "$MSTATUS" 2>/dev/null &
		MPLAYERPID=$!

	PLAYBACK=1
	setup
}
# }}}
# {{{ Recording - Delete Recordings
delete_recordings() {
	find "$RECSDIR" -name "*" -not -name ".*" -type f \
		| sort | cut -d'/' -f 6 > "$RECSLIST"

	TMPLIST="$RECSLIST"
	general_delete $__RECORDINGS
}
# }}}
# }}}
# {{{ Mainmenu
mainmenu() {
	local SIZE
	local rows
	local cols

	while :; do
		SIZE=$(stty size)
		rows=${SIZE% *}
		cols=${SIZE#* }
		cursor off

		dialog $DIALOGOPTS \
			--cancel-label "Quit" \
			--default-item "$mainopt" \
			--menu "${TITLECOL}Main Menu" \
			"$rows" "$cols" "$rows" \
			"Stations" "Station Browser (Genre)" \
			"All Stations" "Station Browser (All)" \
			"Status" "Playing Now" \
			"Bookmarks" "Bookmarks" \
			"Genres" "Genre Menu" \
			"Themes" "Theme Options" \
			"User Stations" "Edit User Stations" \
			"Scheduler" "Scheduler" \
			"Recordings" "Recordings" \
			"Mixer" "Mixer" \
			"Output" "Choose Sound Device" \
			"Choose Mixer" "Choose Mixer Device" \
			"Options" "Manually Edit Options" \
			"Quit" "Quit Soma" \
			2> "$TMP"

		[ $? -eq 1 ] && cleanup
		mainopt="$(< $TMP)"

		[ "$mainopt" = "Stations" ] && station_selector $__GENRE
		[ "$mainopt" = "All Stations" ] && station_selector $__ALL
		[ "$mainopt" = "Bookmarks" ] && main_bookmarks
		[ "$mainopt" = "Genres" ] && main_genre_menu
		[ "$mainopt" = "Themes" ] && main_theme
		[ "$mainopt" = "User Stations" ] && user_stations
		[ "$mainopt" = "Scheduler" ] && schedule_main
		[ "$mainopt" = "Recordings" ] && recording_main
		[ "$mainopt" = "Mixer" ] && mixerpanel
		[ "$mainopt" = "Output" ] && selectoutput
		[ "$mainopt" = "Choose Mixer" ] && selectmixer
		[ "$mainopt" = "Options" ] && edit_options
		[ "$mainopt" = "Quit" ] && cleanup
		[ "$mainopt" = "Status" ] && status
	done
}
# }}}
# {{{ Gotta Start Somewhere
VERSION="3.3.7"
DEBUG=0
declare -A shortcut
declare -a bookmark
[ "$1" = "--record-info" ] && { show_recordinfo; exit; }
[ "$1" = "-h" ] && { usage; exit; }
[ "$1" = "--help" ] && { usage; exit; }
init
get_commandline "$@"
mainmenu
# }}}
# vim: foldmethod=marker foldenable foldcolumn=4 noexpandtab
