[Tkabber-dev] r57 - trunk/plugins/now_playing

tkabber-svn at jabber.ru tkabber-svn at jabber.ru
Sun Sep 30 04:01:01 MSD 2007


Author: kostix
Date: 2007-09-30 04:01:00 +0400 (Sun, 30 Sep 2007)
New Revision: 57

Modified:
   trunk/plugins/now_playing/mpd.tcl
Log:
now_playing/mpd.tcl:
 * Now the code is fully asynchronous.
 * Implemented "logging into" MPD if it's password-protected.
 * Implemented support for auto reconnections when MPD goes offilne.
 * Implemented preliminary suport for errors reported by MPD.
 * Misc fixes.


Modified: trunk/plugins/now_playing/mpd.tcl
===================================================================
--- trunk/plugins/now_playing/mpd.tcl	2007-09-26 18:06:33 UTC (rev 56)
+++ trunk/plugins/now_playing/mpd.tcl	2007-09-30 00:01:00 UTC (rev 57)
@@ -11,9 +11,16 @@
 	variable lasttime 0
 	variable options
 	array set options {
-		media_player     mpd
-		check_timeout    3
-		update_threshold 5
+		media_player      mpd
+		check_timeout     3
+		update_threshold  5
+
+		sock_host              localhost
+		sock_port              6600
+		sock_connect_timeout   2
+		sock_reconnect_timeout 5
+		sock_user              ""
+		sock_password          zhoppa
 	}
 
 	proc my what {
@@ -32,6 +39,10 @@
 		puts $msg
 	}
 
+	proc monitoring args {
+		debugmsg tune [info level 0]
+	}
+
 	# Schelte Bron's implementation of [lassign] (appeared in Tcl 8.5)
 	# from http://wiki.tclers.tk/1530
 	# Note that Tclx also has [lassign] so we check for the existence of
@@ -83,34 +94,61 @@
 
 #### Socket:
 
-proc now_playing::sock_connect {host port {timeout ""}} {
+proc now_playing::sock_connect {host port args} {
+	set timeout 0
+	set async   [list]
+	foreach {opt val} $args {
+		switch -- $opt {
+			-timeout { set timeout $val }
+			-async   { set async $val }
+			default  {
+				return -code error "Bad option \"$opt\":\
+					must be one of -timeout or -async"
+			}
+		}
+	}
+
 	set sock [socket -async $host $port]
 	variable $sock
 	upvar 0 $sock state
 
-	if {$timeout != ""} {
-		set state(on_timeout) [after [expr {$timeout * 1000}] \
+	if {$timeout > 0} {
+		set state(on_timeout) [after $timeout \
 			[mycmd OnSockTimeout $sock]]
 	}
 
 	fileevent $sock readable [mycmd OnSockConnected $sock]
 
-	vwait [my $sock](result)
+	if {[llength $async] > 0} {
+		set state(async) $async
 
-	lassign $state(result) status msg
-	unset state
+		return $sock
+	} else {
+		vwait [my $sock](connected)
 
-	switch -- $status {
-		OK {
+		if {$state(connected)} {
+			unset state
 			return $sock
-		}
-		ERROR {
+		} else {
+			set msg $state(error)
+			unset state
 			return -code error $msg
 		}
 	}
 }
 
 proc now_playing::OnSockConnected sock {
+	set error [fconfigure $sock -error]
+	set connected [expr {$error == ""}]
+
+	SockFinalizeConnectionAttempt $sock $connected $error
+}
+
+proc now_playing::OnSockTimeout sock {
+	SockFinalizeConnectionAttempt $sock 0 "Connection timed out"
+}
+
+proc now_playing::SockFinalizeConnectionAttempt {sock connected error} {
 	variable $sock
 	upvar 0 $sock state
 
@@ -119,19 +157,23 @@
 		after cancel $state(on_timeout)
 	}
 
-	set err [fconfigure $sock -error]
-	if {$err == ""} {
-		set state(result) [list OK ""]
+	if {!$connected} { close $sock }
+
+	if {[info exists state(async)]} {
+		namespace eval :: $state(async) [list $sock $connected $error]
 	} else {
-		set state(result) [list ERROR $err]
+		set state(connected) $connected
+		set state(error)     $error
 	}
 }
 
-proc now_playing::OnSockTimeout sock {
+proc now_playing::SockShutdown sock {
+	close $sock
+
 	variable $sock
-	upvar 0 $sock state
-
-	set state(result) [list ERROR "Connection timed out"]
+	if {[info exists $sock]} {
+		unset $sock
+	}
 }
 
 #### MPD:
@@ -156,37 +198,120 @@
 	}
 }
 
-proc now_playing::mpd_init {} {
+proc now_playing::mpd_connect {} {
+	debugmsg tune [info level 0]
+
+	variable options
 	variable mpd
-	upvar 0 mpd(sock) sock
 
-	# TODO make host and port tunable:
-	set sock [sock_connect localhost 6600 5]
-	gets $sock ;# read hello string from mpd
+	set mpd(sock) [sock_connect $options(sock_host) $options(sock_port) \
+		-timeout [expr {$options(sock_connect_timeout) * 1000}] \
+		-async [mycmd mpd_on_connected]]
+}
 
-	# TODO provide for login, if needed...
+proc now_playing::mpd_cleanup {} {
+	debugmsg tune [info level 0]
 
-	fconfigure $sock -blocking no -buffering line -translation lf -encoding utf-8
+	variable repollid
+	if {[info exists repollid]} {
+		after cancel $repollid
+	}
 
-	laststate set unknown
-	array set mpd {
-		playlist -1
-		songid   -1
+	variable mpd
+
+	if {[array exists mpd]} {
+		SockShutdown $mpd(sock)
+		unset mpd
 	}
+}
 
-#	poll using [mycmd mpd_yield $sock]
-	mpd_yield $sock
+proc now_playing::mpd_error error {
+	mpd_cleanup
+	monitoring off
+	return -code error $error
 }
 
-proc now_playing::mpd_cleanup {} {
+proc now_playing::mpd_parse_ack {ack vcmd vmsg} {
+	upvar 1 $vcmd cmd $vmsg msg
+	regexp {^ACK.*{(\w+)} (.*)$} $s -> cmd msg
+}
+
+proc now_playing::mpd_schedule_reconnect {} {
 	debugmsg tune [info level 0]
 
+	variable options
+	variable repollid
+
+	set repollid [after [expr {$options(sock_reconnect_timeout) * 1000}] \
+		[mycmd mpd_connect]]
+}
+
+proc now_playing::mpd_on_connected {sock connected error} {
+	debugmsg tune [info level 0]
+
+	if {$connected} {
+		fconfigure $sock -blocking no -buffering line -translation lf -encoding utf-8
+
+		fileevent $sock readable [mycmd mpd_read_hello $sock]
+	} else {
+		mpd_schedule_reconnect
+	}
+}
+
+proc now_playing::mpd_on_disconnected sock {
+	debugmsg tune [info level 0]
+
+	mpd_cleanup
+	mpd_schedule_reconnect
+}
+
+proc now_playing::mpd_read_hello sock {
+	debugmsg tune [info level 0]
+
+	if {[gets $sock line] < 0} {
+		mpd_on_disconnected $sock
+		return
+	}
+
+	variable options
+	if {$options(sock_password) != ""} {
+		puts $sock "password $options(sock_password)"
+		fileevent $sock readable [mycmd mpd_process_login $sock]
+	} else {
+		mpd_start_harvesting $sock
+	}
+}
+
+proc now_playing::mpd_process_login sock {
+	debugmsg tune [info level 0]
+
+	if {[gets $sock line] < 0} {
+		mpd_on_disconnected $sock
+		return
+	}
+
+	switch -- $line {
+		ACK* {
+			mpd_error $line
+		}
+		OK {
+			mpd_start_harvesting $sock
+		}
+	}
+}
+
+proc now_playing::mpd_start_harvesting sock {
+	debugmsg tune [info level 0]
+
 	variable mpd
 
-	if {[array exists mpd]} {
-		close $mpd(sock)
-		unset mpd
+	laststate set unknown
+	array set mpd {
+		playlist -1
+		songid   -1
 	}
+
+	mpd_yield $sock
 }
 
 proc now_playing::mpd_yield sock {
@@ -207,6 +332,8 @@
 }
 
 proc now_playing::mpd_schedule_next_yield sock {
+	debugmsg tune [info level 0]
+
 	variable options
 	variable repollid
 
@@ -218,10 +345,7 @@
 	debugmsg tune [info level 0]
 
 	if {[gets $sock line] < 0} {
-		# EOF => mpd closed
-		# TODO ideally, we should schedule connection retries here
-		# instead of just giving up
-		monitoring off
+		mpd_on_disconnected $sock
 		return
 	}
 
@@ -238,6 +362,9 @@
 		songid:*   {
 			set (songid)   [mpd_getval $line songid]
 		}
+		ACK*       {
+			mpd_error $line
+		}
 		OK         {
 			switch -- [mpd_process_status $sock] {
 				stop {
@@ -310,10 +437,7 @@
 	debugmsg tune [info level 0]
 
 	if {[gets $sock line] < 0} {
-		# EOF => mpd closed
-		# TODO ideally, we should schedule connection retries here
-		# instead of just giving up
-		monitoring off
+		mpd_on_disconnected $sock
 		return
 	}
 
@@ -336,6 +460,9 @@
 		Album:*  {
 			set (source) [mpd_getval $line Album]
 		}
+		ACK*     {
+			mpd_error $line
+		}
 		OK       {
 			mpd_process_songinfo $sock
 		}
@@ -374,7 +501,7 @@
 
 #### Action:
 
-now_playing::mpd_init
+now_playing::mpd_connect
 
 vwait forever
 



More information about the Tkabber-dev mailing list