File Coverage

blib/lib/Audio/MPD.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             #
2             # This file is part of Audio-MPD
3             #
4             # This software is copyright (c) 2007 by Jerome Quelin.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9 9     9   12101 use 5.008;
  9         32  
  9         391  
10 9     9   49 use warnings;
  9         18  
  9         238  
11 9     9   45 use strict;
  9         14  
  9         582  
12              
13             package Audio::MPD;
14             # ABSTRACT: class to talk to MPD (Music Player Daemon) servers
15             $Audio::MPD::VERSION = '2.000';
16 9     9   22106 use Audio::MPD::Common::Item;
  0            
  0            
17             use Audio::MPD::Common::Stats;
18             use Audio::MPD::Common::Status;
19             use Audio::MPD::Common::Output;
20             use Encode;
21             use IO::Socket::IP;
22             use Moose;
23             use MooseX::Has::Sugar;
24             use MooseX::SemiAffordanceAccessor;
25              
26             use Audio::MPD::Collection;
27             use Audio::MPD::Playlist;
28             use Audio::MPD::Types;
29              
30              
31              
32             has conntype => ( ro, isa=>'CONNTYPE', default=>'once' );
33             has host => ( ro, lazy_build );
34             has password => ( rw, lazy_build, trigger=>sub { $_[0]->ping } );
35             has port => ( ro, lazy_build );
36              
37             has collection => ( ro, lazy_build, isa=>'Audio::MPD::Collection' );
38             has playlist => ( ro, lazy_build, isa=>'Audio::MPD::Playlist' );
39             has version => ( rw );
40              
41             has _socket => ( rw, isa=>'IO::Socket::IP' );
42              
43              
44             #--
45             # initializer & lazy builders
46              
47              
48             sub BUILD {
49             my $self = shift;
50              
51             # create the connection if conntype is set to $REUSE
52             $self->_connect_to_mpd_server if $self->conntype eq 'reuse';
53              
54             # try to issue a ping to test connection - this can die.
55             $self->ping;
56             }
57              
58             #
59             # my ($passwd, $host, $port) = _parse_env_var();
60             #
61             # parse MPD_HOST environment variable, and extract its components. the
62             # canonical format of MPD_HOST is passwd@host:port.
63             #
64             sub _parse_env_var {
65             return (undef, undef, undef) unless defined $ENV{MPD_HOST};
66             return ($1, $2, $3) if $ENV{MPD_HOST} =~ /^([^@]+)\@([^:@]+):(\d+)$/; # passwd@host:port
67             return ($1, $2, undef) if $ENV{MPD_HOST} =~ /^([^@]+)\@([^:@]+)$/; # passwd@host
68             return (undef, $1, $2) if $ENV{MPD_HOST} =~ /^([^:@]+):(\d+)$/; # host:port
69             return (undef, $ENV{MPD_HOST}, undef);
70             }
71              
72             sub _build_host { return ( _parse_env_var() )[1] || 'localhost'; }
73             sub _build_port { return $ENV{MPD_PORT} || ( _parse_env_var() )[2] || 6600; }
74             sub _build_password { return $ENV{MPD_PASSWORD} || ( _parse_env_var() )[0] || ''; }
75              
76             sub _build_collection { Audio::MPD::Collection->new( _mpd => $_[0] ); }
77             sub _build_playlist { Audio::MPD::Playlist ->new( _mpd => $_[0] ); }
78              
79              
80             #--
81             # Private methods
82              
83              
84             #
85             # $mpd->_connect_to_mpd_server;
86             #
87             # This method connects to the mpd server. It can die on several conditions:
88             # - if the server cannot be reached,
89             # - if it's not an mpd server,
90             # - or if the password is incorrect,
91             #
92             sub _connect_to_mpd_server {
93             my ($self) = @_;
94              
95             # try to connect to mpd.
96             my $socket = IO::Socket::IP->new(
97             PeerAddr => $self->host,
98             PeerPort => $self->port,
99             )
100             or die "Could not create socket: $!\n";
101              
102             # parse version information.
103             my $line = $socket->getline;
104             chomp $line;
105             die "Not a mpd server - welcome string was: [$line]\n"
106             if $line !~ /^OK MPD (.+)$/;
107             $self->set_version($1);
108              
109             # send password.
110             if ( $self->password ) {
111             $socket->print( 'password ' . encode('utf-8', $self->password) . "\n" );
112             $line = $socket->getline;
113             die $line if $line =~ s/^ACK //;
114             }
115              
116             # save socket
117             $self->_set_socket($socket);
118             }
119              
120              
121             #
122             # my @result = $mpd->_send_command( $command );
123             #
124             # This method is central to the module. It is responsible for interacting with
125             # mpd by sending the $command and reading output - that will be returned as an
126             # array of chomped lines (status line will not be returned).
127             #
128             # This method can die on several conditions:
129             # - if the server cannot be reached,
130             # - if it's not an mpd server,
131             # - if the password is incorrect,
132             # - or if the command is an invalid mpd command.
133             # In the latter case, the mpd error message will be returned.
134             #
135             sub _send_command {
136             my ($self, $command) = @_;
137              
138             $self->_connect_to_mpd_server if $self->conntype eq 'once';
139             my $socket = $self->_socket;
140              
141             # ok, now we're connected - let's issue the command.
142             $socket->print( encode('utf-8', $command) );
143             my @output;
144             while (defined ( my $line = $socket->getline ) ) {
145             chomp $line;
146             die $line if $line =~ s/^ACK //; # oops - error.
147             last if $line =~ /^OK/; # end of output.
148             push @output, decode('utf-8', $line);
149             }
150              
151             # close the socket.
152             $socket->close if $self->conntype eq 'once';
153              
154             return @output;
155             }
156              
157              
158             #
159             # my @items = $mpd->_cooked_command_as_items( $command );
160             #
161             # Lots of Audio::MPD methods are using _send_command() and then parse the
162             # output as a collection of AMC::Item. This method is meant to factorize
163             # this code, and will parse the raw output of _send_command() in a cooked
164             # list of items.
165             #
166             sub _cooked_command_as_items {
167             my ($self, $command) = @_;
168              
169             my @lines = $self->_send_command($command);
170             my (@items, %param);
171              
172             # parse lines in reverse order since "file:" or "directory:" lines
173             # come first. therefore, let's first store every other parameter,
174             # and the last line will trigger the object creation.
175             # of course, since we want to preserve the playlist order, this means
176             # that we're going to unshift the objects instead of push.
177             foreach my $line (reverse @lines) {
178             my ($k,$v) = split /:\s/, $line, 2;
179             $param{$k} = $v;
180             next unless $k eq 'file' || $k eq 'directory' || $k eq 'playlist'; # last param of item
181             unshift @items, Audio::MPD::Common::Item->new(%param);
182             %param = ();
183             }
184              
185             return @items;
186             }
187              
188              
189             #
190             # my %hash = $mpd->_cooked_command_as_kv( $command );
191             #
192             # Lots of Audio::MPD methods are using _send_command() and then parse the
193             # output to get a list of key / value (with the colon ":" acting as separator).
194             # This method is meant to factorize this code, and will parse the raw output
195             # of _send_command() in a cooked hash.
196             #
197             sub _cooked_command_as_kv {
198             my ($self, $command) = @_;
199             my %hash =
200             map { split(/:\s/, $_, 2) }
201             $self->_send_command($command);
202             return %hash;
203             }
204              
205             #
206             # my @list = $mpd->_cooked_command_strip_first_field( $command );
207             #
208             # Lots of Audio::MPD methods are using _send_command() and then parse the
209             # output to remove the first field (with the colon ":" acting as separator).
210             # This method is meant to factorize this code, and will parse the raw output
211             # of _send_command() in a cooked list of strings.
212             #
213             sub _cooked_command_strip_first_field {
214             my ($self, $command) = @_;
215              
216             my @list =
217             map { ( split(/:\s+/, $_, 2) )[1] }
218             $self->_send_command($command);
219             return @list;
220             }
221              
222              
223             #--
224             # Public methods
225              
226             # -- MPD interaction: general commands
227              
228              
229             sub ping {
230             my ($self) = @_;
231             $self->_send_command( "ping\n" );
232             }
233              
234              
235              
236              
237             # sub version {} # implemented as an accessor.
238              
239              
240              
241              
242             sub kill {
243             my ($self) = @_;
244             $self->_send_command("kill\n");
245             }
246              
247              
248              
249              
250             # implemented by password trigger (from moose)
251              
252              
253              
254             sub updatedb {
255             my ($self, $path) = @_;
256             $path ||= '';
257             $self->_send_command("update $path\n");
258             }
259              
260              
261              
262             sub urlhandlers {
263             my ($self) = @_;
264             return $self->_cooked_command_strip_first_field("urlhandlers\n");
265             }
266              
267              
268             # -- MPD interaction: handling volume & output
269              
270              
271             sub volume {
272             my ($self, $volume) = @_;
273              
274             if ($volume =~ /^(-|\+)(\d+)/ ) {
275             my $current = $self->status->volume;
276             $volume = $1 eq '+' ? $current + $2 : $current - $2;
277             }
278             $self->_send_command("setvol $volume\n");
279             }
280              
281              
282              
283             sub outputs {
284             my ($self) = @_;
285              
286             my @lines = $self->_send_command("outputs\n");
287             my (@outputs, %param);
288              
289             # parse lines in reverse order since "id" lines come first
290             foreach my $line (reverse @lines) {
291             my ($k,$v) = split /:\s/, $line, 2;
292             $k =~ s/^output//;
293             $param{$k} = $v;
294             next unless $k eq 'id'; # last output param
295             unshift @outputs, Audio::MPD::Common::Output->new(%param);
296             %param = ();
297             }
298              
299             return @outputs;
300             }
301              
302              
303              
304             sub output_enable {
305             my ($self, $output) = @_;
306             $self->_send_command("enableoutput $output\n");
307             }
308              
309              
310              
311             sub output_disable {
312             my ($self, $output) = @_;
313             $self->_send_command("disableoutput $output\n");
314             }
315              
316              
317              
318             # -- MPD interaction: retrieving info from current state
319              
320              
321             sub stats {
322             my ($self) = @_;
323             my %kv = $self->_cooked_command_as_kv( "stats\n" );
324             return Audio::MPD::Common::Stats->new(\%kv);
325             }
326              
327              
328              
329             sub status {
330             my ($self) = @_;
331             my %kv = $self->_cooked_command_as_kv( "status\n" );
332             my $status = Audio::MPD::Common::Status->new( \%kv );
333             return $status;
334             }
335              
336              
337              
338             sub current {
339             my ($self) = @_;
340             my ($item) = $self->_cooked_command_as_items("currentsong\n");
341             return $item;
342             }
343              
344              
345              
346             sub song {
347             my ($self, $song) = @_;
348             return $self->current unless defined $song;
349             my ($item) = $self->_cooked_command_as_items("playlistinfo $song\n");
350             return $item;
351             }
352              
353              
354              
355             sub songid {
356             my ($self, $songid) = @_;
357             return $self->current unless defined $songid;
358             my ($item) = $self->_cooked_command_as_items("playlistid $songid\n");
359             return $item;
360             }
361              
362              
363             # -- MPD interaction: altering settings
364              
365              
366              
367             sub repeat {
368             my ($self, $mode) = @_;
369              
370             $mode = not $self->status->repeat
371             unless defined $mode; # toggle if no param
372             $mode = $mode ? 1 : 0; # force integer
373             $self->_send_command("repeat $mode\n");
374             }
375              
376              
377              
378             sub random {
379             my ($self, $mode) = @_;
380              
381             $mode = not $self->status->random
382             unless defined $mode; # toggle if no param
383             $mode = $mode ? 1 : 0; # force integer
384             $self->_send_command("random $mode\n");
385             }
386              
387              
388              
389             sub fade {
390             my ($self, $value) = @_;
391             $value ||= 0;
392             $self->_send_command("crossfade $value\n");
393             }
394              
395              
396             # -- MPD interaction: controlling playback
397              
398              
399             sub play {
400             my ($self, $number) = @_;
401             $number = '' unless defined $number;
402             $self->_send_command("play $number\n");
403             }
404              
405              
406              
407             sub playid {
408             my ($self, $number) = @_;
409             $number ||= '';
410             $self->_send_command("playid $number\n");
411             }
412              
413              
414              
415             sub pause {
416             my ($self, $state) = @_;
417             $state ||= ''; # default is to toggle
418             $self->_send_command("pause $state\n");
419             }
420              
421              
422              
423             sub stop {
424             my ($self) = @_;
425             $self->_send_command("stop\n");
426             }
427              
428              
429              
430             sub next {
431             my ($self) = @_;
432             $self->_send_command("next\n");
433             }
434              
435              
436              
437             sub prev {
438             my($self) = shift;
439             $self->_send_command("previous\n");
440             }
441              
442              
443              
444             sub seek {
445             my ($self, $time, $song) = @_;
446             $time ||= 0; $time = int $time;
447             $song = $self->status->song if not defined $song; # seek in current song
448             $self->_send_command( "seek $song $time\n" );
449             }
450              
451              
452              
453             sub seekid {
454             my ($self, $time, $song) = @_;
455             $time ||= 0; $time = int $time;
456             $song = $self->status->songid if not defined $song; # seek in current song
457             $self->_send_command( "seekid $song $time\n" );
458             }
459              
460              
461             no Moose;
462             __PACKAGE__->meta->make_immutable;
463             1;
464              
465             __END__
466              
467             =pod
468              
469             =head1 NAME
470              
471             Audio::MPD - class to talk to MPD (Music Player Daemon) servers
472              
473             =head1 VERSION
474              
475             version 2.000
476              
477             =head1 SYNOPSIS
478              
479             use Audio::MPD;
480              
481             my $mpd = Audio::MPD->new;
482             $mpd->play;
483             sleep 10;
484             $mpd->next;
485              
486             =head1 DESCRIPTION
487              
488             L<Audio::MPD> gives a clear object-oriented interface for talking to and
489             controlling MPD (Music Player Daemon) servers. A connection to the MPD
490             server is established as soon as a new L<Audio::MPD> object is created.
491              
492             Since mpd is still in 0.x versions, L<Audio::MPD> sticks to latest mpd
493             (0.15 as time of writing) protocol & behaviour, and does B<not> try to
494             maintain backward compatibility.
495              
496             Note that the module will by default connect to mpd before sending any
497             command, and will disconnect after the command has been issued. This
498             scheme is far from optimal, but allows us not to care about timeout
499             disconnections. Because of that, the C<idle> command (new in mpd 0.14)
500             is B<not> (and will not) be supported in L<Audio::MPD>. This will be
501             implemented in L<POE::Component::Client::MPD>.
502              
503             B</!\> Note that L<Audio::MPD> is using high-level, blocking sockets.
504             This means that if the mpd server is slow, or hangs for whatever reason,
505             or even crash abruptly, the program will be hung forever in this sub.
506             The L<POE::Component::Client::MPD> module is way safer - you're advised
507             to use it instead of L<Audio::MPD>. Or you can try to set C<conntype> to
508             C<$REUSE> (see L<Audio::MPD> constructor for more details), but you
509             would be then on your own to deal with disconnections.
510              
511             =head2 Searching the collection
512              
513             To search the collection, use the C<collection()> accessor, returning the
514             associated L<Audio::MPD::Collection> object. You will then be able to call:
515              
516             $mpd->collection->all_songs;
517              
518             See L<Audio::MPD::Collection> documentation for more details on available
519             methods.
520              
521             =head2 Handling the playlist
522              
523             To update the playlist, use the C<playlist()> accessor, returning the
524             associated L<Audio::MPD::Playlist> object. You will then be able to call:
525              
526             $mpd->playlist->clear;
527              
528             See L<Audio::MPD::Playlist> documentation for more details on available
529             methods.
530              
531             =head1 ATTRIBUTES
532              
533             =head2 host
534              
535             The hostname where MPD is running. Defaults to environment var
536             C<MPD_HOST>, then to 'localhost'. Note that C<MPD_HOST> can be of
537             the form C<password@host:port> (each of C<password@> or C<:port> can
538             be omitted).
539              
540             =head2 port
541              
542             The port that MPD server listens to. Defaults to environment var
543             C<MPD_PORT>, then to parsed C<MPD_HOST> (cf above), then to 6600.
544              
545             =head2 password
546              
547             The password to access special MPD functions. Defaults to environment
548             var C<MPD_PASSWORD>, then to parsed C<MPD_HOST> (cf above), then to
549             empty string.
550              
551             =head2 conntype
552              
553             Change how the connection to mpd server is handled. It should be of a
554             C<CONNTYPE> type (cf L<Audio::MPD::Types>). Use either the C<reuse>
555             string to reuse the same connection or C<once> to open a new connection
556             per command (default).
557              
558             =head1 METHODS
559              
560             =head2 new
561              
562             my $mpd = Audio::MPD->new( \%opts );
563              
564             This is the constructor for L<Audio::MPD>. One can specify any of the
565             attributes (cf above).
566              
567             =head1 CONTROLLING THE SERVER
568              
569             =head2 ping
570              
571             $mpd->ping;
572              
573             Sends a ping command to the mpd server.
574              
575             =head2 version
576              
577             my $version = $mpd->version;
578              
579             Return mpd's version number as advertised during connection. Note that
580             mpd returns B<protocol> version when connected. This protocol version
581             can differ from the real mpd version. eg, mpd version 0.13.2 is
582             "speaking" and thus advertising version 0.13.0.
583              
584             =head2 kill
585              
586             $mpd->kill;
587              
588             Send a message to the MPD server telling it to shut down.
589              
590             =head2 set_password
591              
592             $mpd->set_password( [$password] );
593              
594             Change password used to communicate with MPD server to C<$password>.
595             Empty string is assumed if C<$password> is not supplied.
596              
597             =head2 updatedb
598              
599             $mpd->updatedb( [$path] );
600              
601             Force mpd to recan its collection. If C<$path> (relative to MPD's music
602             directory) is supplied, MPD will only scan it - otherwise, MPD will
603             rescan its whole collection.
604              
605             =head2 urlhandlers
606              
607             my @handlers = $mpd->urlhandlers;
608              
609             Return an array of supported URL schemes.
610              
611             =head1 HANDLING VOLUME & OUTPUT
612              
613             =head2 volume
614              
615             $mpd->volume( [+][-]$volume );
616              
617             Sets the audio output volume percentage to absolute C<$volume>. If
618             C<$volume> is prefixed by '+' or '-' then the volume is changed
619             relatively by that value.
620              
621             =head2 outputs
622              
623             my @outputs = $mpd->outputs( );
624              
625             Return a list of C<Audio::MPD::Common::Outputs> with all outputs
626             available within MPD.
627              
628             =head2 output_enable
629              
630             $mpd->output_enable( $output );
631              
632             Enable the specified audio output. C<$output> is the ID of the audio
633             output.
634              
635             =head2 output_disable
636              
637             $mpd->output_disable( $output );
638              
639             Disable the specified audio output. C<$output> is the ID of the audio
640             output.
641              
642             =head1 RETRIEVING INFO FROM CURRENT STATE
643              
644             =head2 stats
645              
646             my $stats = $mpd->stats;
647              
648             Return an L<Audio::MPD::Common::Stats> object with the current statistics
649             of MPD. See the associated pod for more information.
650              
651             =head2 status
652              
653             my $status = $mpd->status;
654              
655             Return an L<Audio::MPD::Common::Status> object with various information on
656             current MPD server settings. See the associated pod for more information.
657              
658             =head2 current
659              
660             my $song = $mpd->current;
661              
662             Return an L<Audio::MPD::Common::Item::Song> representing the song currently
663             playing.
664              
665             =head2 song
666              
667             my $song = $mpd->song( [$song] );
668              
669             Return an L<Audio::MPD::Common::Item::Song> representing the song number
670             C<$song>. If C<$song> is not supplied, returns the current song.
671              
672             =head2 songid
673              
674             my $song = $mpd->songid( [$songid] );
675              
676             Return an L<Audio::MPD::Common::Item::Song> representing the song with id
677             C<$songid>. If C<$songid> is not supplied, returns the current song.
678              
679             =head1 ALTERING MPD SETTINGS
680              
681             =head2 repeat
682              
683             $mpd->repeat( [$repeat] );
684              
685             Set the repeat mode to C<$repeat> (1 or 0). If C<$repeat> is not
686             specified then the repeat mode is toggled.
687              
688             =head2 random
689              
690             $mpd->random( [$random] );
691              
692             Set the random mode to C<$random> (1 or 0). If C<$random> is not
693             specified then the random mode is toggled.
694              
695             =head2 fade
696              
697             $mpd->fade( [$seconds] );
698              
699             Enable crossfading and set the duration of crossfade between songs. If
700             C<$seconds> is not specified or $seconds is 0, then crossfading is
701             disabled.
702              
703             =head1 CONTROLLING PLAYBACK
704              
705             =head2 play
706              
707             $mpd->play( [$song] );
708              
709             Begin playing playlist at song number C<$song>. If no argument supplied,
710             resume playing.
711              
712             =head2 playid
713              
714             $mpd->playid( [$songid] );
715              
716             Begin playing playlist at song ID C<$songid>. If no argument supplied,
717             resume playing.
718              
719             =head2 pause
720              
721             $mpd->pause( [$state] );
722              
723             Pause playback. If C<$state> is 0 then the current track is unpaused,
724             if C<$state> is 1 then the current track is paused.
725              
726             Note that if C<$state> is not given, pause state will be toggled.
727              
728             =head2 stop
729              
730             $mpd->stop;
731              
732             Stop playback.
733              
734             =head2 next
735              
736             $mpd->next;
737              
738             Play next song in playlist.
739              
740             =head2 prev
741              
742             $mpd->prev;
743              
744             Play previous song in playlist.
745              
746             =head2 seek
747              
748             $mpd->seek( $time, [$song]);
749              
750             Seek to C<$time> seconds in song number C<$song>. If C<$song> number is
751             not specified then the perl module will try and seek to C<$time> in the
752             current song.
753              
754             =head2 seekid
755              
756             $mpd->seekid( $time, $songid );
757              
758             Seek to C<$time> seconds in song ID C<$songid>. If C<$song> number is
759             not specified then the perl module will try and seek to C<$time> in the
760             current song.
761              
762             =for Pod::Coverage BUILD
763              
764             =head1 SEE ALSO
765              
766             You can find more information on the mpd project on its homepage at
767             L<http://www.musicpd.org>.wikia.com>.
768              
769             Original code (2005) by Tue Abrahamsen C<< <tue.abrahamsen@gmail.com> >>,
770             documented in 2006 by Nicholas J. Humfrey C<< <njh@aelius.com> >>.
771              
772             You can look for information on this module at:
773              
774             =over 4
775              
776             =item * Search CPAN
777              
778             L<http://metacpan.org/release/Audio-MPD>
779              
780             =item * See open / report bugs
781              
782             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Audio-MPD>
783              
784             =item * Mailing-list
785              
786             L<http://groups.google.com/group/audio-mpd>
787              
788             =item * Git repository
789              
790             L<http://github.com/jquelin/audio-mpd.git>
791              
792             =item * AnnoCPAN: Annotated CPAN documentation
793              
794             L<http://annocpan.org/dist/Audio-MPD>
795              
796             =item * CPAN Ratings
797              
798             L<http://cpanratings.perl.org/d/Audio-MPD>
799              
800             =back
801              
802             =head1 AUTHOR
803              
804             Jerome Quelin
805              
806             =head1 COPYRIGHT AND LICENSE
807              
808             This software is copyright (c) 2007 by Jerome Quelin.
809              
810             This is free software; you can redistribute it and/or modify it under
811             the same terms as the Perl 5 programming language system itself.
812              
813             =cut