All pastes #273118 Raw Edit

Anonymous

public text v1 · immutable
·published 2006-12-09 16:39 UTC
rendered paste body
#!/usr/bin/tclsh
#######################################################################
#
# BlueamaroK - Control basic functions of the Amarok player with a
# Sony Ericsson mobile telephone through a Bluetooth connection.
#
# This script is released under the GPL.
#
#   Pablo Chinea <khertz@gmail.com>
#   ver. 0.51 - 13/01/2006
#   N.Cat <trisk@quasarnet.org>
#   version 0.5.2 - 05/10/2006
#   ChangeLog:
#     * Fix parsing when KDE applications output on stderr
#     * Make configuration easier and more robust
#     * Allow disconnects and reconnect automatically
#     * Improve menu, now just one Play/Pause entry
#     * Show message and set status info on song change
#     * Mark active song in playlist
#     * Improve playlist item formatting
#     * Improve error handling
#
#######################################################################
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
#######################################################################

global CONFIGFILE
set CONFIGFILE "blueamarokrc"

#################
# Configuration #
#################
proc configurar {} {
	global CONFIGFILE
	global DISPOSITIVO
	if { ! [ info exists DISPOSITIVO ] || [ string equal "" $DISPOSITIVO ] } {
		set DISPOSITIVO "/dev/rfcomm0"
	}
	if { [ catch { exec kdialog --inputbox "Bluetooth (RFCOMM) serial port:" $DISPOSITIVO --caption "BlueamaroK Configuration" 2>/dev/null } DISPOSITIVO ] == 1 } {
		return 1
	}
	if { [ catch { set outfile [ open $CONFIGFILE w ] } ] == 1 } {
		exec kdialog --sorry "Can't open config file \"$DISPOSITIVO\" for writing" --caption "BlueamaroK" 2>/dev/null
		exit 1
	}
	puts $outfile $DISPOSITIVO
	close $outfile
	return 0
}

######################
# Load configuration #
######################
proc cargar {} {
	global CONFIGFILE
	global DISPOSITIVO
	if { [ file exists $CONFIGFILE ] } {
		set infile [ open $CONFIGFILE r ]
		set DISPOSITIVO [ gets $infile ]
		close $infile
		if { [ string equal "" $DISPOSITIVO ] } {
			configurar
		}
	} else {
		configurar
	}
}

##########################
# Connect with the phone #
##########################
proc Conectar {} {
	global DISPOSITIVO
	global CANAL
	while { [ catch { set CANAL [ open $DISPOSITIVO r+ ] } ] == 1 } {
		exec kdialog --sorry "Can't open Bluetooth (RFCOMM) device \"$DISPOSITIVO\"" --caption "BlueamaroK" 2>/dev/null
		# Exit if user gives up
		if { [ configurar ] == 1 } {
			exit 0
		}
	}
	fconfigure $CANAL -buffering line
	fileevent $CANAL readable "Leer"
}


################################
# Reconnect if connection lost #
################################
proc Reconectar {} {
	global DISPOSITIVO
	global CANAL
	if { [ catch { set CANAL [ open $DISPOSITIVO r+ ] } ] == 1 } {
		after 10000 Reconectar
	} else {
		fconfigure $CANAL -buffering line
		fileevent $CANAL readable "Leer"
		IniCon
	}
}


#######################################
# Capture the input send by the phone #
#######################################
proc Leer {} {
	global CANAL
	gets $CANAL entrada
	if { [ eof $CANAL ] } {
		# Disconnected?
		close $CANAL
		Reconectar
		return
	}
	puts stderr "<- $entrada"
	Parsear $entrada
}

###################################
# Send an AT command to the phone #
###################################
proc atsend { atcmd } {
	global CANAL
	puts stderr "-> $atcmd"
	puts $CANAL $atcmd
}

#######################################
# Capture the commands sent by amaroK #
#######################################
proc iface {} {
	#global status
	global EST
	gets stdin entrada
	if { [ string equal "configure" $entrada ] } {
		configurar
	} elseif { [ string equal "trackChange" $entrada ] } {
		set artista [ exec dcop amarok player artist 2>/dev/null ]
		set titulo [ exec dcop amarok player artist 2>/dev/null ]
		if { $EST == 0 } {
			atsend "AT*EAID=1,0,\"Now Playing:\\0A\\0A$artista - $titulo\",20"
			vwait OK
			vwait CONT
		}
		set nombre [ song $artista $titulo ]
		atsend "AT*EAST=\">$nombre\""
		vwait OK
	} elseif { [ string match "engineStateChange: *" $entrada ] } {
		if { [ string match "engineStateChange: playing" $entrada ] } {
			#set status 2
			set artista [ exec dcop amarok player artist 2>/dev/null ]
			set titulo [ exec dcop amarok player artist 2>/dev/null ]
			if { $EST == 0 } {
				atsend "AT*EAID=1,0,\"Now Playing:\\0A\\0A$artista - $titulo\",20"
				vwait OK
				vwait CONT
			}
			set nombre [ song $artista $titulo ]
			atsend "AT*EAST=\">$nombre\""
			vwait OK
		} else {
			#set status 0
			atsend "AT*EAST=\"BlueamaroK\""
			vwait OK
		}
	}
}

###############################
# Execute an action in amarok #
###############################
proc kaccion { cmd } {
	exec dcop amarok player $cmd 2>/dev/null
}

###################################################
# Execute an action in amarok (with 2 parameters) #
###################################################
proc kaccion2 { cmd1 cmd2 } {
	exec dcop amarok player $cmd1 $cmd2 2>/dev/null
}

#######################
# Parsing phone input #
#######################
proc Parsear { cadena } {
	global EST
	global CONT
	if { [ string match "OK" $cadena ] } {
		global OK
		set OK 1
	}
	if { [ string match "ERROR" $cadena ] } {
		set EST 1
		atsend "AT*EAID=1,0,\"BlueamaroK error!\\0APlease send debug output to khertz\\80gmail.com\",0"
		vwait OK
		vwait CONT
	}
	switch $EST {
		0 {
			if { [ string match "*EAAI" $cadena ] } {
				set EST 1
				# default menu to "Play"
				kmenu 2
			}
			if { [ string match "*EAII: 0" $cadena ] } {
				set CONT 1
			}
		}
		1 {
			if { [ string match "*EAMI*" $cadena ] } {
				set val [ string range $cadena 7 end ]
				if { $val != 0 } {
					global MENU
					eval [ lindex $MENU [ expr $val * 2 - 1 ] ]
					kmenu $val
				} else {
					set EST 0
				}
				set LASTVAL $val
			}
			if { [ string match "*EAII*" $cadena ] } {
				if { [ string match "*EAII: 1,1" $cadena ] } {
					kmenu 2
				}
				set CONT 1
			}
		}
		2 {
			if { [ string match "*EAII*" $cadena ] } {
				if { [ string match "*EAII: 15*" $cadena ] } {
					set val [ string range $cadena 10 end ]
					kaccion2 setVolume $val
				} else {
					set CONT 1
					set EST 1
				}
			}
		}
		3 {
			if { [ string match "*EAMI*" $cadena ] } {
				set val [ string range $cadena 7 end ]
				if { $val != 0 } {
					global lista
					global pag
					if { ($pag != 0) } {
						if { ($val == 1) } {
							showplaylist -1 0
						} elseif { ($val == 7) } {
							showplaylist 1 0
						} else {
							exec dcop amarok playlist playByIndex [ expr $pag * 5 + $val - 2 ] 2>/dev/null
						}
					} else {
						if { ( $val == 6 ) } {
							showplaylist 1 0
						} else {
							exec dcop amarok playlist playByIndex [ expr $val - 1 ] 2>/dev/null
						}
					}
					set CONT 1
				}
				set EST 1
				set CONT 1
			}
		}
	}
}

#######################
# Volume control menu #
#######################
proc volume {} {
	global EST
	set EST 2
	set vol [ expr [ exec dcop amarok player getVolume 2>/dev/null ] / 10 ]
	atsend "AT*EAID=4,2,\"amaroK Volume\",10,$vol"
	vwait CONT
}

########################
# Create the main menu #
########################
proc kmenu { sel } {
	global MENU
	global status
	set action "Play"
	set status [ exec dcop amarok player status 2>/dev/null ]
	if { $status == 2 } {
		set action "Pause"
	}
	set MENU [list \
		"Previous Track" "kaccion prev" \
		$action "kaccion playPause" \
		"Stop" "kaccion stop" \
		"Next Track" "kaccion next" \
		"Skip Backward" "kaccion2 seekRelative -5" \
		"Skip Forward" "kaccion2 seekRelative 5" \
		"Volume" volume \
		"Mute" "kaccion mute" \
		"Now Playing" Info \
		"Playlist" playlist \
		"About" Acerca \
	]
	set titulos ""
	foreach { elem cmd } $MENU {
		set titulos "$titulos,\"$elem\""
	}
	set long [ expr [ llength $MENU ] / 2 ]
	atsend "AT*EASM=\"amaroK\",1,$sel,$long$titulos"
	vwait OK
}

#########################
# Generate the playlist #
#########################
proc genlist { fich } {
	set infile [open $fich r]
	while { [ gets $infile cadena ] >= 0 } {
		if { [ string match "*<item*" $cadena ] } {
			set artista ""
		}
		if { [ string match "*<Title>*" $cadena ] } {
			regexp "<Title>(.*)</Title>" [ string trim $cadena ] -> titulo
		}
		if { [ string match "*<Artist>*" $cadena ] } {
			regexp "<Artist>(.*)</Artist>" [ string trim $cadena ] -> artista
		}
		if { [ string match "*</item*" $cadena ] } {
			lappend lista [ song $artista $titulo ]
		}
	}
	close $infile
	return $lista
}

###############################
# Produce formatted song name #
###############################
proc song { artista titulo } {
	# Filter strings
	regsub -all {&amp;} $artista {\&} artista
	regsub -all {&amp;} $titulo {\&} titulo
	regsub -all {\[} $artista {(} artista
	regsub -all {\[} $titulo {(} titulo
	regsub -all {\]} $artista {)} artista
	regsub -all {\]} $titulo {)} titulo
	set artista [ string trim $artista ]
	set titulo [ string trim $titulo ]

	# Truncate strings
	if { [ string length $artista ] > 15 } {
		set artista [ string range $artista 0 12 ]
		append artista "..."
	}
	if { [ string length $titulo ] > 24 } {
		set titulo [ string range $titulo 0 21 ]
		append titulo "..."
	}

	if { [ string length $artista ] > 0 } {
		set nombre "$artista - $titulo"
	} else {
		set nombre $titulo
	}

	return $nombre
}


####################
# Get the playlist #
####################
proc playlist {} {
	global EST
	global lista
	if { [ exec dcop amarok playlist getTotalTrackCount 2>/dev/null ] == 0 } {
		atsend "AT*EAID=1,0,\"Playlist is empty.\",20"
		vwait OK
		vwait CONT
	} else {
		global pos
		global pag
		set EST 3
		set lista [ genlist [ exec dcop amarok playlist saveCurrentPlaylist 2>/dev/null ] ]
		exec dcop amarok playlist shortStatusMessage "Default Playlist saved by BlueamaroK"

		set pos [ exec dcop amarok playlist getActiveIndex 2>/dev/null ]
		if { $pos == -1 } {
			set pag 0
			set offset 0
		} else {
			set pag [ expr $pos / 5 ]
			set offset [ expr $pos % 5 + 1 ]
		}
		showplaylist 0 $offset
	}
}

##########################
# Show the playlist menu #
##########################
proc showplaylist { inc offset } {
	global lista
	global pag
	global pos

	set pag [ expr $pag + $inc ]

	set longit [ llength $lista ]
	set ini [ expr $pag * 5 ]
	if { [ expr ($longit - 1) / 5 ] == $pag } {
		set fin [ expr $longit - 1 ]
	} else {
		set fin [ expr $ini + 4 ]
	}

	set titulos ""
	set long [ expr $fin - $ini + 1 ] 

	if { $offset } {
		set sel $offset
	} else {
		set sel 1
	}

	# Does the page contain the active song?
	if { $pag == [ expr $pos / 5 ] } {
		set active [ expr $pos % 5 + 1 ]
	} else {
		set active 0
	}

	if { $pag != 0 } {
		set titulos "$titulos,\" <<< \""
		set long [ expr $long + 1 ]
		set sel [ expr $sel + 1 ]
	}

	foreach elem [ lrange $lista $ini $fin ] {
		# Add ">" prefix for active song
		set active [ expr $active - 1 ]
		if { $active == 0 } {
			set titulos "$titulos,\"> $elem\""
		} else {
			set titulos "$titulos,\"$elem\""
		}
	}

	if { [ expr ($longit - 1) / 5 ] != $pag } {
		set titulos "$titulos,\" >>> \""
		set long [ expr $long + 1 ]
	}

	set npag [ expr $pag + 1 ]
	set tpag [ expr ($longit - 1) / 5 + 1 ]
	
	set head "Playlist ($npag/$tpag)"
	if { [ string length $head ] > 15 } {
		set head "# ($npag/$tpag)"
	}

	atsend "AT*EASM=\"$head\",1,$sel,$long$titulos"
	vwait CONT
}

##########################
# Show track information #
##########################
proc Info {} {
	set nombre [ exec dcop amarok player nowPlaying 2>/dev/null ]
	if { [ string equal "" $nombre ] } {
		atsend "AT*EAID=1,0,\"amaroK is stopped.\",0"
		vwait OK
		vwait CONT
		return
	}
	set duracion [ exec dcop amarok player totalTime 2>/dev/null ]
	set info "$nombre\\0A($duracion)"

	set bitrate [ exec dcop amarok player bitrate 2>/dev/null ]
	set samplerate [ expr [ exec dcop amarok player sampleRate 2>/dev/null ] / 1000 ]
	set info "$info\\0A* $bitrate kbps - $samplerate kHz"
	
	set fichero [ exec dcop amarok player path 2>/dev/null ]
	if { [ file readable $fichero ] == 1 } {
		set tipo [ string trim [ exec file $fichero | cut -d: -f2 ] ]
		set info "$info\\0A* $tipo"
	}
	atsend "AT*EAID=14,2,\"Now Playing\",\"$info\""
	vwait CONT
}

#######################
# Show "About" dialog #
#######################
proc Acerca {} {
	set a_pro "BlueamaroK 0.5.2"
	set a_aut "* Pablo Chinea <khertz\\80gmail.com>\\0A  N.Cat <trisk\\80quasarnet.org>"
	set a_web "* http://www.khertz.net/"
	set a_lic "* This script is released under the GPL."
	set acerca "$a_pro\\0A$a_aut\\0A$a_web\\0A$a_lic"
	atsend "AT*EAID=14,2,\"About\",\"$acerca\""
	vwait CONT
}

#############################
# Initialize the connection #
#############################
proc IniCon {} {
	atsend "ATZ"
	vwait OK
	atsend "ATE=0"
	vwait OK
	atsend "AT*EAM=\"BlueamaroK\""
	vwait OK
	atsend "AT*EAID=1,0,\"Connected to BlueamaroK.\",20"
	vwait OK
	vwait CONT
	atsend "AT*EAST=\"BlueamaroK\""
	vwait OK
}

#################
# Main function #
#################
proc main {} {
	global EST
	set EST 0
	cargar
	Conectar
	IniCon
	fileevent stdin readable "iface"
	vwait BUCLEINF
}

main