[project @ 6186]
[poe-component-client-mpd.git] / lib / POE / Component / Client / MPD.pm
blobe4d8cc6bc7aefdd4bc905e083a10a558d641f737
2 # This program is free software; you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation; either version 2 of the License, or
5 # (at your option) any later version.
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
12 # You should have received a copy of the GNU General Public License
13 # along with this program; if not, write to the Free Software
14 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 package POE::Component::Client::MPD;
20 use strict;
21 use warnings;
23 use List::MoreUtils qw[ firstidx ];
24 use POE;
25 use POE::Component::Client::MPD::Collection;
26 use POE::Component::Client::MPD::Commands;
27 use POE::Component::Client::MPD::Connection;
28 use POE::Component::Client::MPD::Message;
29 use POE::Component::Client::MPD::Playlist;
30 use base qw[ Class::Accessor::Fast ];
31 __PACKAGE__->mk_accessors( qw[ _host _password _port _version ] );
34 our $VERSION = '0.4.1';
38 # my $id = spawn( \%params )
40 # This method will create a POE session responsible for communicating
41 # with mpd. It will return the poe id of the session newly created.
43 # You can tune the pococm by passing some arguments as a hash reference,
44 # where the hash keys are:
45 # - host: the hostname of the mpd server. If none given, defaults to
46 # MPD_HOST env var. If this var isn't set, defaults to localhost.
47 # - port: the port of the mpd server. If none given, defaults to
48 # MPD_PORT env var. If this var isn't set, defaults to 6600.
49 # - password: the password to sent to mpd to authenticate the client.
50 # If none given, defaults to C<MPD_PASSWORD> env var. If this var
51 # isn't set, defaults to empty string.
52 # - alias: an optional string to alias the newly created POE session.
54 sub spawn {
55 my ($type, $args) = @_;
57 my $collection = POE::Component::Client::MPD::Collection->new;
58 my $commands = POE::Component::Client::MPD::Commands->new;
59 my $playlist = POE::Component::Client::MPD::Playlist->new;
61 my $session = POE::Session->create(
62 args => [ $args ],
63 inline_states => {
64 # private events
65 '_start' => \&_onpriv_start,
66 '_send' => \&_onpriv_send,
67 # protected events
68 '_mpd_data' => \&_onprot_mpd_data,
69 '_mpd_error' => \&_onprot_mpd_error,
70 '_mpd_version' => \&_onprot_mpd_version,
71 # public events
72 'disconnect' => \&_onpub_disconnect,
74 object_states => [
75 $commands => { # general purpose commands
76 # -- MPD interaction: general commands
77 'version' => '_onpub_version',
78 'kill' => '_onpub_kill',
79 # # 'password' => '_onpub_password',
80 'updatedb' => '_onpub_updatedb',
81 'urlhandlers' => '_onpub_urlhandlers',
82 # -- MPD interaction: handling volume & output
83 'volume' => '_onpub_volume',
84 '_volume_status' => '_onpriv_volume_status',
85 'output_enable' => '_onpub_output_enable',
86 'output_disable' => '_onpub_output_disable',
87 # -- MPD interaction: retrieving info from current state
88 'stats' => '_onpub_stats',
89 'status' => '_onpub_status',
90 'current' => '_onpub_current',
91 'song' => '_onpub_song',
92 'songid' => '_onpub_songid',
93 # -- MPD interaction: altering settings
94 'repeat' => '_onpub_repeat',
95 '_repeat_status' => '_onpriv_repeat_status',
96 # # random
97 # # fade
98 # -- MPD interaction: controlling playback
99 'play' => '_onpub_play',
100 'playid' => '_onpub_playid',
101 'pause' => '_onpub_pause',
102 'stop' => '_onpub_stop',
103 'next' => '_onpub_next',
104 'prev' => '_onpub_prev',
105 'seek' => '_onpub_seek',
106 '_seek_need_current' => '_onpriv_seek_need_current',
107 'seekid' => '_onpub_seekid',
108 '_seekid_need_current' => '_onpriv_seek_need_current',
110 $collection => { # collection related commands
111 # -- Collection: retrieving songs & directories
112 # # all_items
113 # # all_items_simple
114 # # items_in_dir
115 # -- Collection: retrieving the whole collection
116 # # all_songs
117 # # all_albums
118 # # all_artists
119 # # all_titles
120 'coll.all_files' => '_onpub_all_files',
121 # -- Collection: picking songs
122 # # song
123 # # songs_with_filename_partial
124 # -- Collection: songs, albums & artists relations
125 # # albums_by_artist
126 # # songs_by_artist
127 # # songs_by_artist_partial
128 # # songs_from_album
129 # # songs_from_album_partial
130 # # songs_with_title
131 # # songs_with_title_partial
133 $playlist => { # playlist related commands
134 # -- Playlist: retrieving information
135 # # as_items
136 # # items_changed_since
137 # -- Playlist: adding / removing songs
138 'pl.add' => '_onpub_add',
139 'pl.delete' => '_onpub_delete',
140 # # deleteid
141 'pl.clear' => '_onpub_clear',
142 # # crop
143 # -- Playlist: changing playlist order
144 # # shuffle
145 # # swap
146 # # swapid
147 # # move
148 # # moveid
149 # -- Playlist: managing playlists
150 # # load
151 # # save
152 # # rm
157 return $session->ID;
162 # public events
165 # event: disconnect()
167 # Request the pococm to be shutdown. Leave mpd running.
169 sub _onpub_disconnect {
170 my ($k,$h) = @_[KERNEL, HEAP];
171 $k->alias_remove( $h->{alias} ) if defined $h->{alias}; # refcount--
172 $k->post( $h->{_socket}, 'disconnect' ); # pococm-conn
177 # protected events.
180 # Event: _mpd_data( $msg )
182 # Received when mpd finished to send back some data.
184 sub _onprot_mpd_data {
185 my ($k, $h, $msg) = @_[KERNEL, HEAP, ARG0];
187 TRANSFORM:
189 # transform data if needed.
190 my $transform = $msg->_transform;
191 last TRANSFORM unless defined $msg->_transform;
193 $transform == $AS_SCALAR and do {
194 my $data = $msg->data->[0];
195 $msg->data($data);
196 last TRANSFORM;
198 $transform == $AS_STATS and do {
199 my %stats = @{ $msg->data };
200 my $stats = POE::Component::Client::MPD::Stats->new( \%stats );
201 $msg->data($stats);
202 last TRANSFORM;
204 $transform == $AS_STATUS and do {
205 my %status = @{ $msg->data };
206 my $status = POE::Component::Client::MPD::Status->new( \%status );
207 $msg->data($status);
208 last TRANSFORM;
213 # check for post-callback.
214 # need to be before pre-callback, since a pre-event may need to have
215 # a post-callback.
216 if ( defined $msg->_post ) {
217 $k->yield( $msg->_post, $msg ); # need a post-treatment...
218 $msg->_post( undef ); # remove postback.
219 return;
222 # check for pre-callback.
223 my $preidx = firstidx { $msg->_request eq $_->_pre_event } @{ $h->{pre_messages} };
224 if ( $preidx != -1 ) {
225 my $pre = splice @{ $h->{pre_messages} }, $preidx, 1;
226 $k->yield( $pre->_pre_from, $pre, $msg ); # call post pre-event
227 $pre->_pre_from ( undef ); # remove pre-callback
228 $pre->_pre_event( undef ); # remove pre-event
229 return;
232 return if $msg->_answer == $DISCARD;
234 # send result.
235 $k->post( $msg->_from, 'mpd_result', $msg );
238 sub _onprot_mpd_error {
239 # send error.
240 my $msg = $_[ARG0];
241 $_[KERNEL]->post( $msg->_from, 'mpd_error', $msg );
246 # Event: _mpd_version( $vers )
248 # Event received during connection, when mpd server sends its version.
249 # Store it for later usage if needed.
251 sub _onprot_mpd_version {
252 $_[HEAP]->{version} = $_[ARG0];
257 # private events
260 # Event: _start( \%params )
262 # Called when the poe session gets initialized. Receive a reference
263 # to %params, same as spawn() received.
265 sub _onpriv_start {
266 my ($h, $args) = @_[HEAP, ARG0];
268 # set up connection details.
269 $args = {} unless defined $args;
270 my %params = (
271 host => $ENV{MPD_HOST} || 'localhost',
272 port => $ENV{MPD_PORT} || '6600',
273 password => $ENV{MPD_PASSWORD} || '',
274 %$args, # overwrite previous defaults
275 id => $_[SESSION]->ID, # required for connection
278 # set an alias (for easier communication) if requested.
279 $h->{alias} = delete $params{alias};
280 $_[KERNEL]->alias_set($h->{alias}) if defined $h->{alias};
282 $h->{password} = delete $params{password};
283 $h->{_socket} = POE::Component::Client::MPD::Connection->spawn(\%params);
284 $h->{pre_messages} = [];
288 =begin FIXME
291 # _connected()
293 # received when the poe::component::client::tcp is (re-)connected to the
294 # mpd server.
296 sub _connected {
297 my ($self, $k) = @_[OBJECT, KERNEL];
298 $k->post($_[HEAP]{_socket}, 'send', 'status' );
299 # send password information
302 =end FIXME
304 =cut
309 # event: _send( $msg )
311 # Event received to request message sending over tcp to mpd server.
312 # $msg is a pococm-message partially filled.
314 sub _onpriv_send {
315 my ($k, $h, $msg) = @_[KERNEL, HEAP, ARG0];
316 if ( defined $msg->_pre_event ) {
317 $k->yield( $msg->_pre_event ); # fire wanted pre-event
318 push @{ $h->{pre_messages} }, $msg; # store message
319 return;
321 $k->post( $_[HEAP]->{_socket}, 'send', $msg );
328 __END__
331 =head1 NAME
333 POE::Component::Client::MPD - a full-blown mpd client library
336 =head1 SYNOPSIS
338 use POE qw[ Component::Client::MPD ];
339 POE::Component::Client::MPD->spawn( {
340 host => 'localhost',
341 port => 6600,
342 password => 's3kr3t', # mpd password
343 alias => 'mpd', # poe alias
344 } );
346 # ... later on ...
347 $_[KERNEL]->post( 'mpd', 'next' );
350 =head1 DESCRIPTION
352 POCOCM gives a clear message-passing interface (sitting on top of POE)
353 for talking to and controlling MPD (Music Player Daemon) servers. A
354 connection to the MPD server is established as soon as a new POCOCM
355 object is created.
357 Commands are then sent to the server as messages are passed.
360 =head1 PUBLIC PACKAGE METHODS
362 =head2 spawn( \%params )
364 This method will create a POE session responsible for communicating with mpd.
365 It will return the poe id of the session newly created.
367 You can tune the pococm by passing some arguments as a hash reference, where
368 the hash keys are:
370 =over 4
372 =item * host
374 The hostname of the mpd server. If none given, defaults to C<MPD_HOST>
375 environment variable. If this var isn't set, defaults to C<localhost>.
378 =item * port
380 The port of the mpd server. If none given, defaults to C<MPD_PORT>
381 environment variable. If this var isn't set, defaults to C<6600>.
384 =item * password
386 The password to sent to mpd to authenticate the client. If none given, defaults
387 to C<MPD_PASSWORD> environment variable. If this var isn't set, defaults to C<>.
390 =item * alias
392 An optional string to alias the newly created POE session.
395 =back
398 =head1 PUBLIC EVENTS
400 For a list of public events that you can send to a POCOCM session, check:
402 =over 4
404 =item *
406 C<POCOCM::Commands> for general commands
408 =item *
410 C<POCOCM::Playlist> for playlist-related commands
412 =item *
414 C<POCOCM::Collection> for collection-related commands
416 =back
419 =head1 BUGS
421 Please report any bugs or feature requests to C<bug-poe-component-client-mpd at
422 rt.cpan.org>, or through the web interface at
423 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=POE-Component-Client-MPD>.
424 I will be notified, and then you'll automatically be notified of progress on
425 your bug as I make changes.
428 =head1 SEE ALSO
430 You can find more information on the mpd project on its homepage at
431 L<http://www.musicpd.org>, or its wiki L<http://mpd.wikia.com>.
433 C<POE::Component::Client::MPD development> takes place on C<< <audio-mpd
434 at googlegroups.com> >>: feel free to join us. (use
435 L<http://groups.google.com/group/audio-mpd> to sign in). Our subversion
436 repository is located at L<https://svn.musicpd.org>.
439 You can also look for information on this module at:
441 =over 4
443 =item * AnnoCPAN: Annotated CPAN documentation
445 L<http://annocpan.org/dist/POE-Component-Client-MPD>
447 =item * CPAN Ratings
449 L<http://cpanratings.perl.org/d/POE-Component-Client-MPD>
451 =item * RT: CPAN's request tracker
453 L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Component-Client-MPD>
455 =back
458 =head1 AUTHOR
460 Jerome Quelin, C<< <jquelin at cpan.org> >>
463 =head1 COPYRIGHT & LICENSE
465 Copyright 2007 Jerome Quelin, all rights reserved.
467 This program is free software; you can redistribute it and/or modify
468 it under the terms of the GNU General Public License as published by
469 the Free Software Foundation; either version 2 of the License, or
470 (at your option) any later version.
472 This program is distributed in the hope that it will be useful,
473 but WITHOUT ANY WARRANTY; without even the implied warranty of
474 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
475 GNU General Public License for more details.
477 You should have received a copy of the GNU General Public License
478 along with this program; if not, write to the Free Software
479 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
481 =cut