#!/usr/bin/perl -w # player.pl - Get "now playing" information from SlimServer by command line # Written by/(C) Ralph Bolton, October 2005, licensed how ever you like ;-) # This script uses the Slimserver command line interface to query the server # for "now playing" information. It then either outputs that information or # can save it to a file. # # Broadly speaking, this serves the same purpose as the various XML based # scripts available on http://www.slimdevices.com/dev_plugins.html. I would # assert that this offers a lower load to the system it runs on, although # of course requires access to the command line port, as opposed to simply # using the web interface. # # All server versions (at least up to 6.2) cannot give 100% accurate "now # playing" information to the command line (or status.xml). Instead, one has # to derive the current track playing by asking for the first thing in the # playlist. This is fine for the most part, but doesn't get the current # track from Internet radio stations (instead, it shows the station). # # If you have more than one Squeezebox playing at once, this script will # pick one and use that for it's information. If you're listening to a file # (as opposed to radio) then track, album and artist are shown. Optionally, # the artist can be wrapped in HTML to link to the site of your choice. # Configuration. # # Where is the slimserver? The port number should be the command line port. use constant SERVER => "localhost"; use constant PORT => "9090"; # Do you want HTML links around artist names? If so, set HTML_OUTPUT to 1, # otherwise 0. HTML_LINK is used to construct the link - the artist name # is appended to it. use constant HTML_OUTPUT => 1; #use constant HTML_LINK => "http://en.wikipedia.org/wiki/Special:Search?search="; use constant HTML_LINK => "http://www.upto11.net/recommend.php?q="; # If you run player.pl with any argument, it'll save the output into a file. # TEMPFILE is the name of that file. use constant TEMPFILE => "/tmp/player.txt"; # If you're trying to figure out what's going on, set SERVER_DEBUG to 1 ;-) use constant SERVER_DEBUG => 0; # No edits should be required below here... # ------------------------------------------------------------------------- use IO::Socket; use Socket; use Fcntl; use strict; # ask_server_something - erm, ask the server something. # This routine passes a request to the server socket. It handles # things like newlines properly, and removes the request from the # response (thus making it much easier to interpret the response) sub ask_server_something { my ($socket,$request)=@_; print "Sending request >$request<\n" if(SERVER_DEBUG); print $socket "$request\n\r"; print "Waiting for response\n" if(SERVER_DEBUG); my $response=<$socket>; return undef if(!defined($response)); print "response is >$response<\n" if(SERVER_DEBUG); $response=~s/[\n\r]//g; return undef if($response eq ""); $request=~s/\?*$//; $response=~s/^$request\s*//; return $response; } sub ask_for_players { my ($socket)=@_; my $response=&ask_server_something($socket,"player count"); return undef if(!defined($response)); # Ensure whatever we got is numeric... $response=~s/\D//g; $response=$response + 0; return $response; } # get_player_info - get track, artist and album info from the server. # To do this, this routine has to get the playerid from the server. It # then gets the information, and also the status of the player. That way # if it's stopped or paused the caller can decide not to use this info. sub get_player_info { my ($socket,$playernum)=@_; my %data=(); $data{'playernum'}=$playernum; $data{'name'}=&ask_server_something($socket,"player name $playernum"); return () if(!defined($data{'name'})); $data{'name'}=~ s/%(..)/pack("c",hex($1))/ge; $data{'id'}=&ask_server_something($socket,"player id $playernum"); return () if(!defined($data{'id'})); $data{'artist'}=&ask_server_something($socket,$data{'id'} . " playlist artist"); return () if(!defined($data{'artist'})); $data{'artist'} =~ s/%(..)/pack("c",hex($1))/ge; $data{'album'}=&ask_server_something($socket,$data{'id'} . " playlist album"); return () if(!defined($data{'album'})); $data{'album'} =~ s/%(..)/pack("c",hex($1))/ge; $data{'index'}=&ask_server_something($socket,$data{'id'} . " playlist index ?"); return () if(!defined($data{'index'})); $data{'title'}=&ask_server_something($socket,$data{'id'} . " playlist title " . $data{'index'}); return () if(!defined($data{'title'})); $data{'title'} =~ s/%(..)/pack("c",hex($1))/ge; $data{'mode'}=&ask_server_something($socket,$data{'id'} . " mode ?"); return () if(!defined($data{'mode'})); return %data; } # url_encode - make something URL friendly. This should probably be a # library call, rather than this fairly cheesy substitution ;-) sub url_encode { my ($string)=@_; $string=~s/\s/\+/g; $string=~s/([^\w+-])/sprintf("%%%X",ord($1))/ge; return $string; } MAIN: { my $out=*STDOUT; my $output_type=0; if(defined($ENV{'REQUEST_METHOD'})) { print $out "Content-Type: text/html\n\n"; $output_type=1; } my $socket=IO::Socket::INET->new(SERVER . ":" . PORT); die "Couldn't connect to " . SERVER . " on port " . PORT . ": $!\n" if(!defined($socket)); my $playercount=&ask_for_players($socket); if((!defined($playercount)) || ($playercount eq "") || ($playercount eq "0")) { die "Couldn't get player count on " . SERVER . "\n"; } $playercount--; my $i; my @players=(); for($i=0; $i<=$playercount; $i++) { # Ask for each player's name, it's ID and # get what it's playing, and its mode my %player=&get_player_info($socket,$i); next if(!defined($player{'mode'})); push @players, \%player; } # All done with the server close($socket); # Now sift through and see what's what... my $player; my $output="Not listening to anything at the moment."; foreach $player (@players) { if($$player{'mode'} eq "play") { if(HTML_OUTPUT) { # Do the output with HTML links $output="Listening to \"" . $$player{'title'} . "\""; $output.=" on \"" . $$player{'album'} . "\"" if(($$player{'album'} ne "0") && ($$player{'album'} ne "")); if(($$player{'artist'} ne "0") && ($$player{'artist'} ne "")) { my $temp=&url_encode($$player{'artist'}); $output.=" by " . $$player{'artist'} . ""; } $output.=" in the " . $$player{'name'}; } else { # This one's playing... $output="Listening to \"" . $$player{'title'} . "\""; $output.=" on \"" . $$player{'album'} . "\"" if($$player{'album'} ne "0"); $output.=" by \"" . $$player{'artist'} . "\"" if($$player{'artist'} ne "0"); $output.=" in the " . $$player{'name'}; } last; } } if($output_type==0) { if(defined($ARGV[0])) { unless(open(FH, "> " . TEMPFILE)) { print STDERR "Failed to open temp file\n"; exit 2; } $out=*FH; } } print $out "$output\n"; close($out); }