2 # This file is part of POE::Component::Client::MPD.
3 # Copyright (c) 2007-2008 Jerome Quelin, all rights reserved.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the same terms as Perl itself.
10 package POE
::Component
::Client
::MPD
;
16 use Audio
::MPD
::Common
::Stats
;
17 use Audio
::MPD
::Common
::Status
;
20 use POE
::Component
::Client
::MPD
::Commands
;
21 use POE
::Component
::Client
::MPD
::Collection
;
22 use POE
::Component
::Client
::MPD
::Connection
;
23 use POE
::Component
::Client
::MPD
::Message
;
24 use POE
::Component
::Client
::MPD
::Playlist
;
26 use base
qw{ Class
::Accessor
::Fast
};
28 our $VERSION = '0.9.1';
38 # my $id = POE::Component::Client::MPD->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.
53 # - status_msgs_to: session to whom to send connection status.
54 # optional (although recommended), no default.
57 my ($type, $args) = @_;
59 my $session = POE
::Session
->create(
63 '_start' => \
&_onpriv_start
,
65 'mpd_connect_error_fatal' => \
&_onprot_mpd_connect_error
,
66 'mpd_connect_error_retriable' => \
&_onprot_mpd_connect_error
,
67 'mpd_connected' => \
&_onprot_mpd_connected
,
68 'mpd_disconnected' => \
&_onprot_mpd_disconnected
,
69 'mpd_data' => \
&_onprot_mpd_data
,
70 'mpd_error' => \
&_onprot_mpd_error
,
72 'disconnect' => \
&_onpub_disconnect
,
73 '_default' => \
&POE
::Component
::Client
::MPD
::_onpub_default
,
87 my ($self, $k, $h, $event, $msg) = @_;
94 $h->{playlist
}->$meth($k, $h, $msg);
98 when (/^coll\.(.*)$/) {
100 $h->{collection
}->$meth($k, $h, $msg);
105 my $meth = "_do_$event";
106 $h->{commands
}->$meth($k, $h, $msg);
119 # catch-all handler for pococm events that drive mpd.
122 my ($k, $h, $event, $params) = @_[KERNEL
, HEAP
, ARG0
, ARG1
];
124 # check if event is handled.
125 my @events_commands = qw{
126 version
kill updatedb urlhandlers
127 volume output_enable output_disable
128 stats status current song songid
130 play playid pause stop
next prev
seek seekid
132 my @events_playlist = qw{
133 pl
.as_items pl
.items_changed_since
134 pl
.add pl
.delete pl
.deleteid pl
.clear pl
.crop
135 pl
.shuffle pl
.swap pl
.swapid pl
.move pl
.moveid
136 pl
.load pl
.save pl
.rm
138 my @events_collection = qw{
139 coll
.all_items coll
.all_items_simple coll
.items_in_dir
140 coll
.all_albums coll
.all_artists coll
.all_titles coll
.all_files
141 coll
.song coll
.songs_with_filename_partial
142 coll
.albums_by_artist coll
.songs_by_artist coll
.songs_by_artist_partial
143 coll
.songs_from_album coll
.songs_from_album_partial
144 coll
.songs_with_title coll
.songs_with_title_partial
146 my @ok_events = ( @events_commands, @events_playlist, @events_collection );
147 return unless $event ~~ [ @ok_events ];
149 # create the message that will hold
150 my $msg = POE
::Component
::Client
::MPD
::Message
->new( {
151 _from
=> $_[SENDER
]->ID,
152 request
=> $event, # /!\ $_[STATE] eq 'default'
154 #_commands => <to be set by handler>
155 #_cooking => <to be set by handler>
156 #_transform => <to be set by handler>
157 #_post => <to be set by handler>
160 # dispatch the event so it is handled by the correct object/method.
161 $h->{mpd
}->_dispatch($k, $h, $event, $msg);
166 # event: disconnect()
168 # Request the pococm to be shutdown. Leave mpd running.
170 sub _onpub_disconnect
{
171 my ($k,$h) = @_[KERNEL
, HEAP
];
172 $k->alias_remove( $h->{alias
} ) if defined $h->{alias
}; # refcount--
173 $k->post( $h->{socket}, 'disconnect' ); # pococm-conn
177 # -- protected events.
180 # event: mpd_connect_error_retriable( $reason )
181 # event: mpd_connect_error_fatal( $reason )
183 # Called when pococm-conn could not connect to a mpd server. It can be
184 # either retriable, or fatal. In bth case, we just need to forward the
185 # error to our peer session.
187 sub _onprot_mpd_connect_error
{
188 my ($k, $h, $reason) = @_[KERNEL
, HEAP
, ARG0
];
190 my $peer = $h->{status_msgs_to
};
191 return unless defined $peer;
192 $k->post($peer, 'mpd_connect_error_fatal', $reason);
197 # event: mpd_connected( $version )
199 # Called when pococm-conn made sure we're talking to a mpd server.
201 sub _onprot_mpd_connected
{
202 my ($k, $h, $version) = @_[KERNEL
, HEAP
, ARG0
];
203 $h->{version
} = $version;
205 my $peer = $h->{status_msgs_to
};
206 return unless defined $peer;
207 $k->post($peer, 'mpd_connected');
208 # FIXME: send password information to mpd
209 # FIXME: send status information to peer
215 # event: mpd_disconnected()
217 # Called when pococm-conn got disconnected by mpd.
219 sub _onprot_mpd_disconnected
{
220 my ($k, $h, $version) = @_[KERNEL
, HEAP
, ARG0
];
226 # Event: mpd_data( $msg )
228 # Received when mpd finished to send back some data.
230 sub _onprot_mpd_data
{
231 my ($k, $h, $msg) = @_[KERNEL
, HEAP
, ARG0
];
233 # transform data if needed.
234 given ($msg->_transform) {
236 my $data = $msg->_data->[0];
240 my %stats = @
{ $msg->_data };
241 my $stats = Audio
::MPD
::Common
::Stats
->new( \
%stats );
245 my %status = @
{ $msg->_data };
246 my $status = Audio
::MPD
::Common
::Status
->new( \
%status );
247 $msg->_data($status);
252 # check for post-callback.
253 if ( defined $msg->_post ) {
254 my $event = $msg->_post; # save postback.
255 $msg->_post( undef ); # remove postback.
256 $h->{mpd
}->_dispatch($k, $h, $event, $msg);
261 $k->post($msg->_from, 'mpd_result', $msg, $msg->_data);
266 # Event: mpd_error( $msg, $errstr )
268 # Received when mpd didn't understood a command.
270 sub _onprot_mpd_error
{
271 my ($k, $msg, $errstr) = @_[KERNEL
, ARG0
, ARG1
];
273 $msg->status(0); # failure
274 $k->post( $msg->_from, 'mpd_error', $msg, $errstr );
282 # Event: _start( \%params )
284 # Called when the poe session gets initialized. Receive a reference
285 # to %params, same as spawn() received.
288 my ($k, $h, $args) = @_[KERNEL
, HEAP
, ARG0
];
290 # set up connection details.
291 $args = {} unless defined $args;
293 host
=> $ENV{MPD_HOST
} // 'localhost',
294 port
=> $ENV{MPD_PORT
} // '6600',
295 password
=> $ENV{MPD_PASSWORD
} // '',
296 %$args, # overwrite previous defaults
297 id
=> $_[SESSION
]->ID, # required for connection
300 # set an alias (for easier communication) if requested.
301 $h->{alias
} = delete $params{alias
};
302 $k->alias_set($h->{alias
}) if defined $h->{alias
};
304 # store args for ourself.
305 $h->{status_msgs_to
} = $args->{status_msgs_to
};
306 $h->{socket} = POE
::Component
::Client
::MPD
::Connection
->spawn(\
%params);
308 # create objects to treat dispatched events.
309 $h->{mpd
} = POE
::Component
::Client
::MPD
->new;
310 $h->{commands
} = POE
::Component
::Client
::MPD
::Commands
->new;
311 $h->{playlist
} = POE
::Component
::Client
::MPD
::Playlist
->new;
312 $h->{collection
} = POE
::Component
::Client
::MPD
::Collection
->new;
320 # event: _send( $msg )
322 # Event received to request message sending over tcp to mpd server.
323 # $msg is a pococm-message partially filled.
326 my ($k, $h, $msg) = @_[KERNEL, HEAP, ARG0];
327 if ( defined $msg->_pre_event ) {
328 $k->yield( $msg->_pre_event ); # fire wanted pre-event
329 push @{ $h->{pre_messages} }, $msg; # store message
332 $k->post( $_[HEAP]->{_socket}, 'send', $msg );
347 POE::Component::Client::MPD - a full-blown mpd client library
353 use POE qw{ Component::Client::MPD };
354 POE::Component::Client::MPD->spawn( {
357 password => 's3kr3t', # mpd password
358 alias => 'mpd', # poe alias
362 $_[KERNEL]->post( 'mpd', 'next' );
368 POCOCM gives a clear message-passing interface (sitting on top of POE)
369 for talking to and controlling MPD (Music Player Daemon) servers. A
370 connection to the MPD server is established as soon as a new POCOCM
373 Commands are then sent to the server as messages are passed.
377 =head1 PUBLIC PACKAGE METHODS
380 =head2 my $id = POCOCM->spawn( \%params )
382 This method will create a POE session responsible for communicating with mpd.
383 It will return the poe id of the session newly created.
385 You can tune the pococm by passing some arguments as a hash reference, where
393 The hostname of the mpd server. If none given, defaults to C<MPD_HOST>
394 environment variable. If this var isn't set, defaults to C<localhost>.
399 The port of the mpd server. If none given, defaults to C<MPD_PORT>
400 environment variable. If this var isn't set, defaults to C<6600>.
405 The password to sent to mpd to authenticate the client. If none given, defaults
406 to C<MPD_PASSWORD> environment variable. If this var isn't set, defaults to C<>.
411 An optional string to alias the newly created POE session.
414 =item * status_msgs_to
416 A session (name or id) to whom to send connection status to. Optional,
417 although recommended. No default.
424 =head1 PUBLIC EVENTS ACCEPTED
426 POCOCM accepts two types of events: some are used to drive the mpd
427 server, others will change the pococm status.
430 =head2 MPD-related events
432 The goal of a POCOCM session is to drive a remote MPD server. This can
433 be achieved by a lot of events. Due to their sheer number, they have
434 been regrouped logically in modules.
436 However, note that to use those events, you need to send them to the
437 POCOCM session that you created with C<spawn()> (see above). Indeed, the
438 logical split is only internal: you are to use the same peer.
441 For a list of public events that update and/or query MPD, see embedded
448 C<POCOCM::Commands> for general commands
453 C<POCOCM::Playlist> for playlist-related commands. Those events begin
459 C<POCOCM::Collection> for collection-related commands. Those events
467 =head2 POCOCM-related events
469 Those events allow to drive the POCOCM session.
476 Request the POCOCM to be shutdown. Leave mpd running. Generally sent
477 when one wants to exit her program.
484 =head1 PUBLIC EVENTS FIRED
486 A POCOCM session will fire events, either to answer an incoming event,
487 or to inform about some changes regarding the remote MPD server.
492 For each incoming event received by the POCOCM session, it will fire
493 back one of the following answers:
498 =item * mpd_result( $msg, $answer )
500 Indicates a success. C<$msg> is a C<POCOCM::Message> object with the
501 original request, to identify the issued command (see C<POCOCM::Message>
502 pod for more information). Its C<status()> attribute is true, further
505 C<$answer> is what has been answered by the MPD server. Depending on the
506 command, it can be either:
510 =item * C<undef>: commands C<play>, etc.
512 =item * an C<Audio::MPD::Common::Stats> object: command C<stats>
514 =item * an C<Audio::MPD::Common::Status> object: command C<status>
516 =item * an C<Audio::MPD::Common::Item> object: commands C<song>, etc.
518 =item * an array reference: commands C<coll.files>, etc.
524 Refer to the documentation of each event to know what type of answer you
528 =item * mpd_error( $msg, $errstr )
530 Indicates a failure. C<$msg> is a C<POCOCM::Message> object with the
531 original request, to identify the issued command (see C<POCOCM::Message>
532 pod for more information). Its C<status()> attribute is false, further
535 C<$errstr> is what the error message as returned been answered by the
543 =head2 Auto-generated events
550 Please report any bugs or feature requests to C<bug-poe-component-client-mpd at
551 rt.cpan.org>, or through the web interface at
552 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=POE-Component-Client-MPD>.
553 I will be notified, and then you'll automatically be notified of progress on
554 your bug as I make changes.
560 You can find more information on the mpd project on its homepage at
561 L<http://www.musicpd.org>, or its wiki L<http://mpd.wikia.com>.
563 C<POE::Component::Client::MPD development> takes place on C<< <audio-mpd
564 at googlegroups.com> >>: feel free to join us. (use
565 L<http://groups.google.com/group/audio-mpd> to sign in). Our git
566 repository is located at
567 L<git://repo.or.cz/poe-component-client-mpd.git>, and can be browsed at
568 L<http://repo.or.cz/w/poe-component-client-mpd.git>.
571 You can also look for information on this module at:
575 =item * AnnoCPAN: Annotated CPAN documentation
577 L<http://annocpan.org/dist/POE-Component-Client-MPD>
581 L<http://cpanratings.perl.org/d/POE-Component-Client-MPD>
585 L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Component-Client-MPD>
593 Jerome Quelin, C<< <jquelin@cpan.org> >>
597 =head1 COPYRIGHT & LICENSE
599 Copyright (c) 2007-2008 Jerome Quelin, all rights reserved.
601 This program is free software; you can redistribute it and/or modify
602 it under the same terms as Perl itself.