[Slim-Checkins] r11404 - in /trunk/server: HTML/EN/html/docs/cli-api.html Slim/Control/Queries.pm Slim/Control/Request.pm Slim/Plugin/CLI/Plugin.pm
fred at svn.slimdevices.com
fred at svn.slimdevices.com
Sun Feb 11 16:52:54 PST 2007
Author: fred
Date: Sun Feb 11 16:52:54 2007
New Revision: 11404
URL: http://svn.slimdevices.com?rev=11404&view=rev
Log:
Bug: N/A
Description: New query "serverstatus", with a subscribe mode as status.
The subscribe mode is handled in Request.pm as a more generic mechanism.
If all goes well, the subscribe mode of "status" will be handled using
the same mechanism in a couple of days.
Modified:
trunk/server/HTML/EN/html/docs/cli-api.html
trunk/server/Slim/Control/Queries.pm
trunk/server/Slim/Control/Request.pm
trunk/server/Slim/Plugin/CLI/Plugin.pm
Modified: trunk/server/HTML/EN/html/docs/cli-api.html
URL: http://svn.slimdevices.com/trunk/server/HTML/EN/html/docs/cli-api.html?rev=11404&r1=11403&r2=11404&view=diff
==============================================================================
--- trunk/server/HTML/EN/html/docs/cli-api.html (original)
+++ trunk/server/HTML/EN/html/docs/cli-api.html Sun Feb 11 16:52:54 2007
@@ -71,6 +71,15 @@
</li>
<li>
Added a tag to return the artist from the "<a href="#albums">albums</a>" query.
+ </li>
+ <li>
+ Added the "<a href="#serverstatus">serverstatus</a>" query, to return
+ compound server status in a single query.
+ </li>
+ <li>
+ Slightly reorganised this document to introduce a "Compound queries" section to
+ document queries "<a href="#serverstatus">serverstatus</a>" and
+ "<a href="#status">status</a>".
</li>
</ul>
@@ -594,24 +603,26 @@
Supported Commands
</h3>
<p>
- The supported commands are listed below, grouped by their scope:
+ The available commands and queries are listed below, grouped by their scope:
</p>
<ul>
<li>
- <strong><a href="#GC">General</a></strong>: commands for general
- management of the server.
+ <strong><a href="#GC">General</a></strong>: general management of the Command Line
+ Interface and of the SlimServer.
</li>
<li>
- <strong><a href="#PC">Players</a></strong>: commands to manage and
- get information on players.
+ <strong><a href="#PC">Players</a></strong>: management of players.
</li>
<li>
- <strong><a href="#DB">Database</a></strong>: commands to manage
- and browse the music database.
- </li>
- <li>
- <strong><a href="#PL">Playlist</a></strong>: commands to get
- information on and manage the playlist for each player.
+ <strong><a href="#DB">Database</a></strong>: mangement of the music database.
+ </li>
+ <li>
+ <strong><a href="#PL">Playlist</a></strong>: management of the playlist of each player.
+ </li>
+ <li>
+ <strong><a href="#CQ">Coumpound queries</a></strong>: queries to get
+ most of the information about the server or a player in one convenient query, that can
+ be updated by the server automatically.
</li>
<li>
<strong><a href="#NC">Notifications</a></strong>: internal server
@@ -737,15 +748,16 @@
</blockquote>
<br>
-<p id="version">
+<p id="version ?">
<strong>
<code>
version
- </code>
- </strong>
-</p>
-<p>
- The "version" query returns the server version number.<
+ ?
+ </code>
+ </strong>
+</p>
+<p>
+ The "version" query returns version number of SlimServer.
</p>
<p>
Examples:
@@ -951,7 +963,8 @@
</strong>
</p>
<p>
- The "exit" command closes the TCP connection with the server.
+ The "exit" command closes the TCP connection with the server and terminates the
+ Command Line Interface session.
</p>
<p>
Example:
@@ -2009,7 +2022,7 @@
</td>
<td>
Number of players known by SlimServer. Equivalent to
- "player count ?".
+ "<a href="#player count ?">player count ?</a>".
</td>
</tr>
<tr>
@@ -2071,7 +2084,8 @@
</td>
<td>
Player display type. Not returned for streaming connections.
- Equivalent to "player displaytype <playerindex> ?".
+ Equivalent to
+ "<a href="#player displaytype ?">player displaytype <playerindex> ?</a>".
</td>
</tr>
<tr>
@@ -2080,7 +2094,7 @@
</td>
<td>
Connected state. Equivalent to
- "<playerid> connected ?".
+ "<a href="#connected ?"><playerid> connected ?</a>".
</td>
</tr>
</table>
@@ -2095,16 +2109,26 @@
playerindex:0 playerid:a5:41:d2:cd:cd:05 ip:127.0.0.1:60488
name:127.0.0.1 model:softsqueeze displaytype:graphic-280x16 connected:1
playerindex:1 playerid:00:04:20:02:00:c8 ip:192.168.1.22:3483
- name:Movy model:slimp3 displaytype:noritake-katakana connected:1
- <LF>"
- </p>
-</blockquote>
-
-<br><p id="alarm"><strong>
- <code><playerid> alarm <taggedParameters></code>
-</strong></p>
-<p>The "alarm" command allows to manipulate player alarms.</p>
-<p>Accepted tagged parameters:</p>
+ name:Movy model:slimp3 displaytype:noritake-katakana connected:1<LF>"
+ </p>
+</blockquote>
+
+<br>
+<p id="alarm">
+ <strong>
+ <code>
+ <playerid>
+ alarm
+ <taggedParameters>
+ </code>
+ </strong>
+</p>
+<p>
+ The "alarm" command allows to manipulate player alarms.
+</p>
+<p>
+ Accepted tagged parameters:
+</p>
<table border="0" spacing="50">
<tr>
<td width="100"><b>Tag</b></td>
@@ -2181,11 +2205,11 @@
<p><b>Defining a new alarm</b>
<br>Request: "bd:a5:a9:9b:9d:df alarm cmd:set dow:1 enable:1
playlist_id:22 time:9000<LF>"
-<br>Response: "bd:a5:a9:9b:9d:df alarm cmd:update dow:1 enable:1
+<br>Response: "bd:a5:a9:9b:9d:df alarm cmd:set dow:1 enable:1
playlist_id:22 time:9000 count:1<LF>"</p>
<p><b>Clearing an alarm</b>
<br>Request: "bd:a5:a9:9b:9d:df alarm cmd:clear dow:1<LF>"
-<br>Response: "bd:a5:a9:9b:9d:df alarm cmd:update dow:1 count:1
+<br>Response: "bd:a5:a9:9b:9d:df alarm cmd:clear dow:1 count:1
<LF>"</p>
<p><b>Enabling a previously defined alarm</b>
<br>Request: "bd:a5:a9:9b:9d:df alarm cmd:update dow:1 enable:1<LF>"
@@ -4814,9 +4838,6 @@
</li>
<li>
<strong><code><a href="#playlistcontrol">playlistcontrol</a></code></strong>
- </li>
- <li>
- <strong><code><a href="#status">status</a></code></strong>
</li>
</ul>
@@ -5602,12 +5623,30 @@
cmd:load album_id:22 count:12<LF>"</p>
</blockquote>
-<br>
-<p id="status">
- <strong>
- <code>
- <playerid>
- status
+
+<!----------------------------------------------------------------------------->
+<!-- COMPOUND QUERIES -->
+<!----------------------------------------------------------------------------->
+
+<hr>
+<h2 id="CQ">
+ Compound queries
+</h2>
+<ul>
+ <li>
+ <strong><code><a href="#serverstatus">serverstatus</a></code></strong>
+ </li>
+ <li>
+ <strong><code><a href="#status">status</a></code></strong>
+ </li>
+</ul>
+
+
+<br>
+<p id="serverstatus">
+ <strong>
+ <code>
+ serverstatus
<start>
<itemsPerResponse>
<taggedParameters>
@@ -5615,25 +5654,14 @@
</strong>
</p>
<p>
- The "status" request returns the complete status about a given
- player, including the current playlist. Set the <start> parameter to
- "-" to get the playlist data starting from the current song.
+ The "serverstatus" query returns a complete status about the server, including its
+ players.
<br>
- In this "curent" mode and if repeat is on, SlimServer will attempt
- to return <itemsPerResponse> elements, by repeating the playlist at
- most once, unless shuffling is on and SlimServer is configured to re-shuffle
- the playlist at each loop (in which case it is impossible to predict the
- song following the last one in the playlist until this last song has
- finished playing).
- <br>
- Similarly, in the "curent" mode, if repeat
- is one, only the current song is returned, regardless of the value of
- <itemsPerResponse>.
- <br>
- Clients can subscribe to "status" requests, so that the request
+ Clients can subscribe to "serverstatus" queries, so that the query
results are automatically returned asynchronously whenever a change occurs
- to a player. Please note this mechanism is completely distinct from the
- "listen" and "subscribe" commands described above.
+ to the server. Please note this mechanism is completely distinct from the
+ "listen" and "subscribe" commands described elsewhere in this
+ document.
</p>
<p>
Accepted tagged parameters:
@@ -5721,6 +5749,260 @@
rescan
</td>
<td>
+ Returned with value 1 if SlimServer is still scanning the
+ database. The results may therefore be incomplete. Not returned if
+ no scan is in progress.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ version
+ </td>
+ <td>
+ SlimServer version. Equivalent to
+ "<a href="#version ?">version ?</a>"
+ </td>
+ </tr>
+ <tr>
+ <td>
+ info total albums
+ </td>
+ <td>
+ Number of albums known to SlimServer. Equivalent to
+ "<a href="#info total albums ?">info total albums ?</a>"
+ </td>
+ </tr>
+ <tr>
+ <td>
+ info total artists
+ </td>
+ <td>
+ Number of artists known to SlimServer. Equivalent to
+ "<a href="#info total artists ?">info total artists ?</a>"
+ </td>
+ </tr>
+ <tr>
+ <td>
+ info total genres
+ </td>
+ <td>
+ Number of genres known to SlimServer. Equivalent to
+ "<a href="#info total genres ?">info total genres ?</a>"
+ </td>
+ </tr>
+ <tr>
+ <td>
+ info total songs
+ </td>
+ <td>
+ Number of songs known to SlimServer. Equivalent to
+ "<a href="#info total songs ?">info total songs ?</a>"
+ </td>
+ </tr>
+ <tr>
+ <td>
+ player count
+ </td>
+ <td>
+ Number of players known by SlimServer. Equivalent to
+ "<a href="#player count ?">player count ?</a>".
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <em>
+ For each player:
+ </em>
+ </td>
+ <td>
+ <em>
+ Essentially, this list is equivalent to the one returned by
+ "<a href="#players">players</a>".
+ </em>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <b>playerid</b>
+ </td>
+ <td>
+ Player unique identifier. Item delimiter. Equivalent to
+ "<a href="#player id ?">player id ?</a>".
+ </td>
+ </tr>
+ <tr>
+ <td>
+ ip
+ </td>
+ <td>
+ Player IP and port. Equivalent to
+ "<a href="#player ip ?">player ip ?</a>".
+ </td>
+ </tr>
+ <tr>
+ <td>
+ name
+ </td>
+ <td>
+ Player name. Equivalent to
+ "<a href="#player name ?">player name ?</a>".
+ </td>
+ </tr>
+ <tr>
+ <td>
+ model
+ </td>
+ <td>
+ Player model. Equivalent to
+ "<a href="#player model ?">player model ?</a>".
+ </td>
+ </tr>
+ <tr>
+ <td>
+ displaytype
+ </td>
+ <td>
+ Player display type. Not returned for streaming connections.
+ Equivalent to
+ "<a href="#player displaytype ?">player displaytype <playerindex> ?</a>".
+ </td>
+ </tr>
+ <tr>
+ <td>
+ connected
+ </td>
+ <td>
+ Connected state. Equivalent to
+ "<a href="#connected ?"><playerid> connected ?</a>".
+ </td>
+ </tr>
+</table>
+<p>
+ Example:
+</p>
+<blockquote>
+</blockquote>
+
+
+<br>
+<p id="status">
+ <strong>
+ <code>
+ <playerid>
+ status
+ <start>
+ <itemsPerResponse>
+ <taggedParameters>
+ </code>
+ </strong>
+</p>
+<p>
+ The "status" query returns the complete status about a given
+ player, including the current playlist. Set the <start> parameter to
+ "-" to get the playlist data starting from the current song.
+ <br>
+ In this "curent" mode and if repeat is on, SlimServer will attempt
+ to return <itemsPerResponse> elements, by repeating the playlist at
+ most once, unless shuffling is on and SlimServer is configured to re-shuffle
+ the playlist at each loop (in which case it is impossible to predict the
+ song following the last one in the playlist until this last song has
+ finished playing).
+ <br>
+ Similarly, in the "curent" mode, if repeat
+ is one, only the current song is returned, regardless of the value of
+ <itemsPerResponse>.
+ <br>
+ Clients can subscribe to "status" queries, so that the query
+ results are automatically returned asynchronously whenever a change occurs
+ to a player. Please note this mechanism is completely distinct from the
+ "listen" and "subscribe" commands described elsewhere in this document.
+</p>
+<p>
+ Accepted tagged parameters:
+</p>
+<table border="0" spacing="50">
+ <tr>
+ <td width="150">
+ <b>Tag</b>
+ </td>
+ <td>
+ <b>Description</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ tags
+ </td>
+ <td>
+ Determines which tags are returned. Each returned tag is identified
+ by a letter (see command
+ "<a href="#songinfo">songinfo</a>" for a list of possible
+ fields and their identifying letter). The default tags value for
+ this query is "gald".
+ </td>
+ </tr>
+ <tr>
+ <td>
+ charset
+ </td>
+ <td>
+ Character set to use to return data. See
+ <a href="#Notes">charset notes</a> above.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ subscribe
+ </td>
+ <td>
+ This optional parameter controls the subscription to the player
+ status. Only one status subscription is possible per player and
+ connection.
+ <br>
+ Subscription is enabled by using this parameter with a positive
+ integer. It is disabled by using "-". When the
+ subscription is enabled, normal "status" queries (i.e.
+ not using the "subscribe" parameter) can be performed and
+ will have no effect on the subscription in place.
+ <br>
+ When enabled, the "status" request is automatically
+ re-generated on player change (and sent asynchronously to the CLI
+ client). The number indicates the time interval in seconds between
+ automatic generations in case nothing happened to the player in the
+ interval. Use "0" to disable this last feature and only be
+ notified on player or playlist changes. Please see the example.
+ <br>
+ Some situations will lead to multiple status queries
+ generated very close to another. This is a limitation of the
+ change detection "algorithm".
+ <br>
+ If the player is manually (through the web page) or automatically
+ deleted from SlimServer, "client forget" is sent instead
+ of the status query and the subscription is terminated.
+ <br>
+ Please note this mechanism is completely distinct from the
+ "listen" and "subscribe" commands described
+ above.
+ </td>
+ </tr>
+</table>
+<p>
+ Returned tagged parameters:
+</p>
+<table border="0" spacing="50">
+ <tr>
+ <td width="150">
+ <b>Tag</b>
+ </td>
+ <td>
+ <b>Description</b>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ rescan
+ </td>
+ <td>
Returned with value 1 if the SlimServer is still scanning the
database. The results may therefore be incomplete. Not returned if
no scan is in progress.
Modified: trunk/server/Slim/Control/Queries.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Control/Queries.pm?rev=11404&r1=11403&r2=11404&view=diff
==============================================================================
--- trunk/server/Slim/Control/Queries.pm (original)
+++ trunk/server/Slim/Control/Queries.pm Sun Feb 11 16:52:54 2007
@@ -23,7 +23,8 @@
L<Slim::Control::Queries> implements most SlimServer queries and is designed to
be exclusively called through Request.pm and the mechanisms it defines.
- There are no important differences between the code for a query and one for
+ Except for subscribe-able queries (such as status and serverstatus), there are no
+ important differences between the code for a query and one for
a command. Please check the commented command in Commands.pm.
=cut
@@ -39,6 +40,7 @@
use Slim::Utils::Unicode;
my $log = logger('control.queries');
+
sub alarmsQuery {
my $request = shift;
@@ -844,6 +846,7 @@
$request->setStatusDone();
}
+
sub playlistPlaylistsinfoQuery {
my $request = shift;
@@ -874,6 +877,7 @@
$request->setStatusDone();
}
+
sub playlistXQuery {
my $request = shift;
@@ -1251,6 +1255,85 @@
$loopCount++;
}
+ }
+
+ $request->setStatusDone();
+}
+
+
+sub serverstatusQuery {
+ my $request = shift;
+
+ $log->debug("Begin Function");
+
+ # check this is the correct query
+ if ($request->isNotQuery([['serverstatus']])) {
+ $request->setStatusBadDispatch();
+ return;
+ }
+
+ if (Slim::Music::Import->stillScanning()) {
+ $request->addResult('rescan', "1");
+ }
+
+ # add version
+ $request->addResult('version', $::VERSION);
+
+ # add totals
+ $request->addResult("info total albums", Slim::Schema->count('Album'));
+ $request->addResult("info total artists", Slim::Schema->rs('Contributor')->browse->count);
+ $request->addResult("info total genres", Slim::Schema->count('Genre'));
+ $request->addResult("info total songs", Slim::Schema->rs('Track')->browse->count);
+
+ # get our parameters
+ my $index = $request->getParam('_index');
+ my $quantity = $request->getParam('_quantity');
+
+ my $count = Slim::Player::Client::clientCount();
+ $request->addResult('player count', $count);
+
+ my ($valid, $start, $end) = $request->normalize(scalar($index), scalar($quantity), $count);
+
+ if ($valid) {
+ my $cnt = 0;
+ my @players = Slim::Player::Client::clients();
+
+ if (scalar(@players) > 0) {
+
+ for my $eachclient (@players[$start..$end]) {
+ $request->addResultLoop('@players', $cnt,
+ 'playerid', $eachclient->id());
+ $request->addResultLoop('@players', $cnt,
+ 'ip', $eachclient->ipport());
+ $request->addResultLoop('@players', $cnt,
+ 'name', $eachclient->name());
+ $request->addResultLoop('@players', $cnt,
+ 'model', $eachclient->model());
+ $request->addResultLoop('@players', $cnt,
+ 'displaytype', $eachclient->vfdmodel())
+ unless ($eachclient->model() eq 'http');
+ $request->addResultLoop('@players', $cnt,
+ 'connected', ($eachclient->connected() || 0));
+
+ $cnt++;
+ }
+ }
+ }
+
+ # manage the subscription
+ if (defined(my $timeout = $request->getParam('subscribe'))) {
+
+ # the filter function decides, based on a notified request, if the serverstatus
+ # query must be re-executed.
+ sub filter{
+ my $request = shift;
+
+ # we want to know about rescan and all client notifs
+ return $request->isCommand([['rescan', 'client']]);
+ }
+
+ # register ourselves to be automatically re-executed on timeout or filter
+ $request->registerAutoExecute($timeout, \&filter);
}
$request->setStatusDone();
@@ -1615,6 +1698,7 @@
$request->setStatusDone();
}
+
sub timeQuery {
my $request = shift;
@@ -1750,6 +1834,7 @@
$request->setStatusDone();
}
+
sub versionQuery {
my $request = shift;
@@ -1767,6 +1852,7 @@
$request->setStatusDone();
}
+
sub yearsQuery {
my $request = shift;
@@ -1941,6 +2027,7 @@
# add it directly to the result loop
$request->setResultLoopHash($loop, $index, $hashRef);
}
+
sub _songData {
my $pathOrObj = shift; # song path or object
Modified: trunk/server/Slim/Control/Request.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Control/Request.pm?rev=11404&r1=11403&r2=11404&view=diff
==============================================================================
--- trunk/server/Slim/Control/Request.pm (original)
+++ trunk/server/Slim/Control/Request.pm Sun Feb 11 16:52:54 2007
@@ -149,12 +149,16 @@
Y playlist path <index> ?
Y playlist remote <index> ?
- Y status <startindex> <numitems> <tagged parameters>
-
Y playlist name ?
Y playlist url ?
Y playlist modified ?
Y playlist playlistsinfo <tagged parameters>
+
+ COMPOUND
+
+ N serverstatus <startindex> <numitems> <tagged parameters>
+ Y status <startindex> <numitems> <tagged parameters>
+
DEPRECATED (BUT STILL SUPPORTED)
Y mode <play|pause|stop>
@@ -413,9 +417,12 @@
our %dispatchDB; # contains a multi-level hash pointing to
# each command or query subroutine
-our %subscribers = (); # contains the clients to the notification
- # mechanism
-
+our %listeners = (); # contains the clients to the notification
+ # mechanism (internal to the server)
+
+our %subscribers = (); # contains the requests being subscribed to
+ # (generaly by external users/clients)
+
our @notificationQueue; # contains the Requests waiting to be notified
our $requestTask = Slim::Utils::PerfMon->new('Request Task', [0.002, 0.005, 0.010, 0.015, 0.025, 0.050, 0.1, 0.5, 1, 5]);
@@ -549,6 +556,7 @@
addDispatch(['rescan', '?'], [0, 1, 0, \&Slim::Control::Queries::rescanQuery]);
addDispatch(['rescan', '_playlists'], [0, 0, 0, \&Slim::Control::Commands::rescanCommand]);
addDispatch(['search', '_index', '_quantity'], [0, 1, 1, \&Slim::Control::Queries::searchQuery]);
+ addDispatch(['serverstatus', '_index', '_quantity'], [0, 1, 1, \&Slim::Control::Queries::serverstatusQuery]);
addDispatch(['show'], [1, 0, 1, \&Slim::Control::Commands::showCommand]);
addDispatch(['signalstrength', '?'], [1, 1, 0, \&Slim::Control::Queries::signalstrengthQuery]);
addDispatch(['sleep', '?'], [1, 1, 0, \&Slim::Control::Queries::sleepQuery]);
@@ -691,12 +699,12 @@
my $subscriberFuncRef = shift || return;
my $requestsRef = shift;
- $subscribers{$subscriberFuncRef} = [$subscriberFuncRef, $requestsRef];
+ $listeners{$subscriberFuncRef} = [$subscriberFuncRef, $requestsRef];
$log->info(sprintf(
- "Request from: %s - (%d subscribers)\n",
+ "Request from: %s - (%d listeners)\n",
Slim::Utils::PerlRunTime::realNameForCodeRef($subscriberFuncRef),
- scalar(keys %subscribers)
+ scalar(keys %listeners)
));
}
@@ -704,16 +712,16 @@
sub unsubscribe {
my $subscriberFuncRef = shift;
- delete $subscribers{$subscriberFuncRef};
+ delete $listeners{$subscriberFuncRef};
$log->info(sprintf(
- "Request from: %s - (%d subscribers)\n",
+ "Request from: %s - (%d listeners)\n",
Slim::Utils::PerlRunTime::realNameForCodeRef($subscriberFuncRef),
- scalar(keys %subscribers)
+ scalar(keys %listeners)
));
}
-# notify subscribers from an array, useful for notifying w/o execution
+# notify listeners from an array, useful for notifying w/o execution
# (requests must, however, be defined in the dispatch table)
sub notifyFromArray {
my $client = shift; # client, if any, to which the query applies
@@ -763,6 +771,29 @@
return $request;
}
+=head2 unregisterAutoExecute ( $connectionID )
+
+Removes all subscriptions for this $connectionID.
+
+=cut
+sub unregisterAutoExecute{
+ my $connectionID = shift;
+
+ # kill any timers
+ for my $name (keys %{$subscribers{$connectionID}}) {
+ for my $clientid (keys %{$subscribers{$connectionID}{$name}}) {
+
+ my $request = $subscribers{$connectionID}{$name}{$clientid};
+
+ Slim::Utils::Timers::killTimers($request, \&__autoexecute);
+ }
+ }
+
+ # delete everything linked to connection
+ delete $subscribers{$connectionID};
+}
+
+
################################################################################
# Constructors
################################################################################
@@ -777,20 +808,23 @@
tie (my %resultHash, "Tie::LLHash", {lazy => 1});
my $self = {
- '_request' => [],
- '_isQuery' => undef,
- '_clientid' => $clientid,
- '_needClient' => 0,
- '_params' => \%paramHash,
- '_curparam' => 0,
- '_status' => 0,
- '_results' => \%resultHash,
- '_func' => undef,
- '_cb_enable' => 1,
- '_cb_func' => undef,
- '_cb_args' => undef,
- '_source' => undef,
- '_private' => undef,
+ '_request' => [],
+ '_isQuery' => undef,
+ '_clientid' => $clientid,
+ '_needClient' => 0,
+ '_params' => \%paramHash,
+ '_curparam' => 0,
+ '_status' => 0,
+ '_results' => \%resultHash,
+ '_func' => undef,
+ '_cb_enable' => 1,
+ '_cb_func' => undef,
+ '_cb_args' => undef,
+ '_source' => undef,
+ '_connectionid' => undef,
+ '_ae_callback' => undef,
+ '_ae_filter' => undef,
+ '_private' => undef,
};
bless $self, $class;
@@ -817,6 +851,9 @@
$copy->{'_func'} = \&{$self->{'_func'}};
$copy->{'_source'} = $self->{'_source'};
$copy->{'_private'} = $self->{'_private'};
+ $copy->{'_connectionid'} = $self->{'_connectionid'};
+ $copy->{'_ae_callback'} = $self->{'_ae_callback'};
+ $copy->{'_ae_filter'} = $self->{'_ae_filter'};
$copy->{'_curparam'} = $self->{'_curparam'};
# duplicate the arrays and hashes
@@ -940,6 +977,37 @@
return $self->{'_source'};
}
+
+# sets/returns the source connectionid
+sub connectionID {
+ my $self = shift;
+ my $newvalue = shift;
+
+ $self->{'_connectionid'} = $newvalue if defined $newvalue;
+
+ return $self->{'_connectionid'};
+}
+
+# sets/returns the source subscribe callback
+sub autoExecuteCallback {
+ my $self = shift;
+ my $newvalue = shift;
+
+ $self->{'_ae_callback'} = $newvalue if defined $newvalue;
+
+ return $self->{'_ae_callback'};
+}
+
+# sets/returns the source subscribe callback
+sub autoExecuteFilter {
+ my $self = shift;
+ my $newvalue = shift;
+
+ $self->{'_ae_filter'} = $newvalue if defined $newvalue && ref($newvalue) eq 'CODE';
+
+ return $self->{'_ae_filter'};
+}
+
# sets/returns the source private data
sub privateData {
@@ -1602,21 +1670,20 @@
}
}
-# notify subscribers...
+# notify listeners...
sub notify {
my $self = shift || return;
- my $dontcallExecuteCallback = shift;
-
- for my $subscriber (keys %subscribers) {
-
- if ( $subscribers{$subscriber} ) {
+
+ for my $listener (keys %listeners) {
+
+ if ( $listeners{$listener} ) {
# filter based on desired requests
# undef means no filter
- my $notifyFuncRef = $subscribers{$subscriber}->[0];
- my $requestsRef = $subscribers{$subscriber}->[1];
-
- my $funcName = $subscriber;
+ my $notifyFuncRef = $listeners{$listener}->[0];
+ my $requestsRef = $listeners{$listener}->[1];
+
+ my $funcName = $listener;
if ($log->is_debug && ref($notifyFuncRef) eq 'CODE') {
$funcName = Slim::Utils::PerlRunTime::realNameForCodeRef($notifyFuncRef);
@@ -1650,9 +1717,98 @@
}
}
-}
-
-# handle encoding for external commands
+
+ # handle subscriptions
+ # send the notification to all filters...
+ for my $cnxid (keys %subscribers) {
+ for my $name (keys %{$subscribers{$cnxid}}) {
+ for my $clientid (keys %{$subscribers{$cnxid}{$name}}) {
+
+ my $request = $subscribers{$cnxid}{$name}{$clientid};
+
+ my $relevant = 1;
+
+ if (defined(my $funcPtr = $request->autoExecuteFilter())) {
+
+ $relevant = 0;
+
+ if (ref($funcPtr) eq 'CODE') {
+
+ eval { $relevant = &{$funcPtr}($self) };
+
+ if ($@) {
+ my $funcName = Slim::Utils::PerlRunTime::realNameForCodeRef($funcPtr);
+ logError("While trying to run function coderef [$funcName]: [$@]");
+ next;
+ }
+ }
+ }
+
+ if ($relevant) {
+ $request->__autoexecute();
+ }
+ }
+ }
+ }
+}
+
+
+=head2 registerAutoExecute ( timeout, filterFunc )
+
+Register ourself as subscribed to. Autoexecute after timeout
+(if > 0) or if filterFunc returns 1 when sent notifications.
+
+=cut
+sub registerAutoExecute{
+ my $self = shift || return;
+ my $timeout = shift;
+ my $filterFunc = shift;
+
+ # we shall be a query
+ return unless $self->{'_isQuery'};
+
+ # we shall have a defined connectionID
+ my $cnxid = $self->connectionID() || return;
+
+ # requests with a client are remembered by client
+ my $clientid = $self->clientid() || 'global';
+
+ # requests are remembered by kind
+ my $name = $self->getRequestString();
+
+ # store the filterFunc in the request
+ $self->autoExecuteFilter($filterFunc);
+
+ # kill any previous subscription we might have laying around
+ # (for this query/client/connection)
+ my $oldrequest = $subscribers{$cnxid}{$name}{$clientid};
+
+ delete $subscribers{$cnxid}{$name}{$clientid};
+
+ Slim::Utils::Timers::killTimers($oldrequest, \&__autoexecute);
+
+ # store the new subscription if this is what is asked of us
+ if ($timeout ne '-') {
+
+ # copy the request
+ my $request = $self->virginCopy();
+
+ $subscribers{$cnxid}{$name}{$clientid} = $request;
+
+ if ($timeout > 0) {
+ # start the timer
+ Slim::Utils::Timers::setTimer($request,
+ Time::HiRes::time() + $timeout,
+ \&__autoexecute);
+ }
+ }
+}
+
+=head2 fixencoding ( )
+
+Handle encoding for external commands.
+
+=cut
sub fixEncoding {
my $self = shift || return;
@@ -2057,6 +2213,40 @@
}
}
+# callback for the subscriptions.
+sub __autoexecute{
+ my $self = shift;
+
+ $log->debug("__autoexecute()");
+
+ # we shall have somewhere to callback to
+ my $funcPtr = $self->autoExecuteCallback() || return;
+
+ return unless ref($funcPtr) eq 'CODE';
+
+ # we shall have a connection id to send as param
+ my $cnxid = $self->connectionID() || return;
+
+ # execute ourself after some cleanup
+ $self->cleanResults;
+ $self->execute();
+
+ # execute the callback
+ eval { &{$funcPtr}($self, $cnxid) };
+
+ # oops, failed
+ if ($@) {
+ my $funcName = Slim::Utils::PerlRunTime::realNameForCodeRef($funcPtr);
+ logError("While trying to run function coderef [$funcName]: [$@] => deleting subscription");
+
+ my $name = $self->getRequestString();
+ my $clientid = $self->clientid() || 'global';
+ delete $subscribers{$cnxid}{$name}{$clientid};
+ Slim::Utils::Timers::killTimers($self, \&__autoexecute);
+ }
+
+}
+
=head1 SEE ALSO
=cut
Modified: trunk/server/Slim/Plugin/CLI/Plugin.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Plugin/CLI/Plugin.pm?rev=11404&r1=11403&r2=11404&view=diff
==============================================================================
--- trunk/server/Slim/Plugin/CLI/Plugin.pm (original)
+++ trunk/server/Slim/Plugin/CLI/Plugin.pm Sun Feb 11 16:52:54 2007
@@ -277,6 +277,7 @@
close $client_socket;
delete($connections{$client_socket});
+ Slim::Control::Request::unregisterAutoExecute($client_socket);
$log->info("Closed connection with $client_id (" . (keys %connections) . " active connections)");
}
@@ -527,7 +528,9 @@
# remember we're the source and the $client_socket
$request->source('CLI');
- $request->privateData($client_socket);
+ $request->connectionID($client_socket);
+ # set this in case the query can be subscribed to
+ $request->autoExecuteCallback(\&cli_request_write);
my $cmd = $request->getRequest(0);
@@ -550,7 +553,7 @@
if (defined $client) {
$log->info("Request [$cmd] requires client, allocated $clientid");
} else {
- $log->info("Request [$cmd] requires client, none found!");
+ $log->warn("Request [$cmd] requires client, none found!");
}
}
}
@@ -619,7 +622,7 @@
else {
- $log->info("Request [$cmd] unkown or missing client -- will echo as is...");
+ $log->warn("Request [$cmd] unkown or missing client -- will echo as is...");
}
}
@@ -635,7 +638,7 @@
$log->debug($request->getRequestString);
- $client_socket = $request->privateData() unless defined $client_socket;
+ $client_socket = $request->connectionID() unless defined $client_socket;
my $encoding = $request->getParam('charset') || 'utf8';
my @elements = $request->renderAsArray($encoding);
@@ -770,7 +773,7 @@
}
my $param = $request->getParam('_newvalue');
- my $client_socket = $request->privateData();
+ my $client_socket = $request->connectionID();
if (!defined $client_socket) {
$request->setStatusBadParams();
@@ -802,7 +805,7 @@
return;
}
- my $client_socket = $request->privateData();
+ my $client_socket = $request->connectionID();
if (!defined $client_socket) {
$request->setStatusBadParams();
@@ -826,7 +829,7 @@
}
my $param = $request->getParam('_functions');
- my $client_socket = $request->privateData();
+ my $client_socket = $request->connectionID();
if (!defined $client_socket) {
$request->setStatusBadParams();
@@ -980,7 +983,7 @@
# don't echo twice to the sender
if (!($request->source() eq 'CLI' &&
- $request->privateData() eq $client_socket)) {
+ $request->connectionID() eq $client_socket)) {
# assume no array in {'listen'}: we send everything
$sent = 1;
@@ -1057,7 +1060,7 @@
$request->cleanResults();
$request->execute();
- my $client_socket = $request->privateData();
+ my $client_socket = $request->connectionID();
cli_request_write($request);
More information about the checkins
mailing list