File Coverage

blib/lib/MP3/Daemon/Simple.pm
Criterion Covered Total %
statement 36 242 14.8
branch 0 78 0.0
condition 0 15 0.0
subroutine 12 33 36.3
pod 17 20 85.0
total 65 388 16.7


line stmt bran cond sub pod time code
1             package MP3::Daemon::Simple;
2              
3 1     1   937 use strict;
  1         3  
  1         46  
4 1     1   1325 use MP3::Info;
  1         68359  
  1         128  
5 1     1   12 use MP3::Daemon;
  1         2  
  1         33  
6 1     1   3186 use Getopt::Std;
  1         64  
  1         77  
7 1     1   5 use File::Basename;
  1         3  
  1         104  
8              
9 1     1   24 use vars qw(@ISA $VERSION);
  1         2  
  1         327  
10             @ISA = qw(MP3::Daemon);
11             $VERSION = 0.11;
12              
13             # constructor that does NOT daemonize itself
14             #_______________________________________
15             sub new {
16 0     0 1   my $class = shift;
17 0           my $self = $class->SUPER::new(@_);
18              
19 0           $self->{playlist} = [ ]; # list of mp3s
20 0           $self->{n} = undef; # index into playlist
21 0           $self->{random} = 0; # play random songs? or not.
22 0           $self->{loop} = "all"; # looping behaviour (off|single|all)
23              
24 0           return $self;
25             }
26              
27              
28             # valid requests
29             #_______________________________________
30             *_play = \&play;
31             *_next = \&next;
32             *_prev = \&prev;
33             *_pause = \&pause;
34             *_stop = \&stop;
35             *_loop = \&loop;
36             *_jump = \&jump;
37             *_ff = \&ff;
38             *_rw = \&rw;
39             *_add = \&add;
40             *_del = \&del;
41             *_ls = \&ls;
42             *_info = \&info;
43             *_time = \&time;
44             *_rand = \&rand;
45             *_quit = \&quit;
46              
47             # playlist entry indices
48             #_______________________________________
49 1     1   7 use constant URL => 0;
  1         17  
  1         63  
50 1     1   5 use constant TITLE => 1;
  1         1  
  1         40  
51 1     1   5 use constant TIME => 2;
  1         2  
  1         45  
52              
53             # Audio::Play::MPG123 states
54             #_______________________________________
55 1     1   6 use constant STOPPED => 0;
  1         2  
  1         42  
56 1     1   5 use constant PAUSED => 1;
  1         1  
  1         44  
57 1     1   5 use constant PLAYING => 2;
  1         1  
  1         3981  
58              
59             # |>
60             #_______________________________________
61             sub play {
62 0     0 1   my $self = shift;
63 0           my $x = shift;
64 0           my $pl = $self->{playlist};
65 0           my $n = $self->{n};
66              
67 0 0         if (defined $x) {
68 0 0         if ($x =~ /^[-+]?\d+$/) {
69 0           my $end = scalar(@$pl) - 1;
70 0 0         $x = ($x < 0) ? $end+1 + $x : $x;
71 0 0         $x = ($x > $end) ? $end : $x;
72 0           $self->{n} = $n = $x;
73             } else {
74 0           $self->add($x);
75 0           $self->{n} = $n = scalar(@$pl) - 1;
76             }
77             } else {
78 0 0         $self->{n} = $n = 0 unless defined($n);
79             }
80 0           my $mp3 = $pl->[$n][URL];
81 0           $self->{player}->load($mp3);
82 0           $self->{player}->statfreq(1 / $self->{player}->tpf());
83             }
84              
85             # >>
86             #_______________________________________
87             sub next {
88 0     0 1   my $self = shift;
89 0 0         if ($self->{random}) { $self->random(); return }
  0            
  0            
90 0           my $pl = $self->{playlist};
91 0           my $end = scalar(@$pl) - 1;
92 0           my $n = $self->{n};
93              
94 0           my $client = $self->{client};
95              
96 0 0         if (scalar @$pl) {
97 0           my $loop = $self->{loop};
98 0 0 0       if (($loop ne "single") ||
      0        
99             ($loop eq "single" && $self->{player}->state == PLAYING))
100             {
101 0 0         if (not defined $n) {
    0          
102 0           $n = 0;
103             } elsif ($n >= $end) {
104 0           $n = 0;
105             } else {
106 0           $n++;
107             }
108             }
109 0           $self->{n} = $n;
110 0 0         if ($loop eq "off") {
111 0           $self->stop();
112             } else {
113 0           $self->{player}->load($pl->[$n][URL]);
114             }
115             }
116             }
117              
118             # <<
119             #_______________________________________
120             sub prev {
121 0     0 1   my $self = shift;
122 0           my $pl = $self->{playlist};
123 0           my $end = scalar(@$pl) - 1;
124 0           my $n = $self->{n};
125              
126 0           my $client = $self->{client};
127              
128 0 0         if (scalar @$pl) {
129 0 0         if (not defined $n) {
    0          
130 0           $n = 0;
131             } elsif ($n <= 0) {
132 0           $n = $end;
133             } else {
134 0           $n--
135             }
136 0           $self->{n} = $n;
137 0           $self->{player}->load($pl->[$n][URL]);
138             }
139             }
140              
141             # ==
142             #_______________________________________
143             sub pause {
144 0     0 1   my $self = shift;
145 0           my $player = $self->{player};
146 0 0         $player->pause() unless $player->state() == STOPPED;
147             }
148              
149             # []
150             #_______________________________________
151             sub stop {
152 0     0 1   my $self = shift;
153 0           my $player = $self->{player};
154 0 0         $player->stop() unless $player->state == STOPPED;
155             }
156              
157             # 69
158             #_______________________________________
159             sub loop {
160 0     0 1   my $self = shift;
161 0           my $opt = shift;
162 0           my $client = $self->{client};
163 0 0         if ($opt) {
164 0 0         unless (grep { /^$opt$/ } qw(all single off)) {
  0            
165 0           print $client "mp3 loop [all|single|off]\n";
166 0           return;
167             }
168 0           $self->{loop} = $opt;
169             }
170 0           print $client "loop $self->{loop}\n";
171             }
172              
173             # !!
174             #_______________________________________
175             sub jump {
176 0     0 1   my $self = shift;
177 0           my $sec = shift;
178 0           my $player = $self->{player};
179 0           my $tpf = $player->tpf;
180              
181 0           $player->jump($sec / $tpf);
182             }
183              
184             # ->
185             #_______________________________________
186             sub ff {
187 0     0 1   my $self = shift;
188 0           my $sec = shift;
189 0           my $player = $self->{player};
190 0           my $tpf = $player->tpf;
191              
192 0           $player->jump("+" . $sec / $tpf);
193             }
194              
195             # <-
196             #_______________________________________
197             sub rw {
198 0     0 1   my $self = shift;
199 0           my $sec = shift;
200 0           my $player = $self->{player};
201 0           my $tpf = $player->tpf;
202              
203 0           $player->jump(-$sec / $tpf);
204             }
205              
206             # ++
207             #_______________________________________
208             sub add {
209 0     0 1   my $self = shift;
210 0           my $pl = $self->{playlist};
211 0           my $client = $self->{client};
212              
213 0           foreach (@_) {
214 0           my $tag;
215 0           my $info = get_mp3info($_);
216 0 0         if (m|^http://|) {
217 0           $info->{TIME} = "00:00";
218 0           $tag->{TITLE} = (fileparse($_, '\..*$'))[0];
219             }
220 0 0         if ($info) {
221 0   0       $tag ||= get_mp3tag($_);
222 0   0       $tag->{TITLE} ||= (fileparse($_, '\..*$'))[0];
223 0           my $entry = [
224             $_, # URL
225             $tag->{TITLE}, # TITLE
226             $info->{TIME}, # TIME
227             ];
228 0           push(@$pl, $entry);
229             } else {
230 0           print $client qq("$_" does not seem to be an mp3.\n);
231             }
232             }
233             }
234              
235             # --
236             #_______________________________________
237             sub del {
238 0     0 1   my $self = shift;
239 0           my $pl = $self->{playlist};
240 0           my $end = scalar(@$pl) - 1;
241 0           my $n = $self->{n};
242 0           my $x = $n;
243              
244             # no parameter deletes current item from list
245 0 0         push(@_, $n) unless (scalar(@_));
246              
247             # delete 1 or more from list
248 0           my @new_playlist;
249 0           my $adjust = 0;
250 0 0         my %kill =
251 0           map { $adjust++ if $_ < $n; $_ => 1 } # create set
  0            
252 0 0         grep { $_ <= $end } # check range
253 0           map { ($_ < 0) ? $_ = $end+1 + $_ : $_ } # normalize
254 0           grep { /^[-+]?\d+$/ } @_; # integers only
255 0 0         return unless (scalar keys %kill);
256 0           for (my $i = 0; $i <= $end; $i++) {
257 0 0         push(@new_playlist, $pl->[$i]) unless ($kill{$i});
258             }
259              
260             # assign new list and prepare to reindex if necessary
261 0           $self->{playlist} = $pl = \@new_playlist;
262 0           $end = scalar(@$pl) - 1;
263 0           my $player = $self->{player};
264              
265             # nothing left?
266 0 0         if ($end < 0) {
267 0           $self->{n} = 0;
268 0 0         $player->stop if $player->state;
269 0           return;
270             }
271              
272             # before current track
273 0 0         if ($adjust) { $n -= $adjust; $self->{n} = $n; }
  0            
  0            
274              
275             # at and after current track
276 0 0         if (defined $kill{$x}) {
277 0 0         $n = 0 if ($n > $end);
278 0           $self->{n} = $n;
279 0           $self->{player}->load($pl->[$n][URL]);
280             }
281             }
282              
283             #_______________________________________
284             sub ls_short_entry_factory {
285 0     0 0   my $attr = shift;
286             return sub {
287 0     0     my $i = shift;
288 0           my $entry = shift;
289 0           return sprintf('%5s %s', $i, qq("$entry->[$attr]"));
290             }
291 0           }
292              
293             #_______________________________________
294             sub ls_long_entry {
295 0     0 0   my $i = shift;
296 0           my $entry = shift;
297 0           return sprintf(
298             '%5s %5s %-30s "%s"',
299             $i, $entry->[TIME], qq("$entry->[TITLE]"), $entry->[URL]
300             );
301             }
302              
303             # @
304             #_______________________________________
305             sub ls {
306 0     0 1   my $self = shift;
307 0           my $client = $self->{client};
308 0           my $pl = $self->{playlist};
309              
310 0           my %opt;
311 0           local @ARGV = @_;
312 0           getopts('lf', \%opt);
313 0           my $re = shift(@ARGV);
314              
315 0 0         my $attr = defined($opt{f}) ? URL : TITLE;
316 0           my $i;
317 0           my $n = $self->{n};
318 0 0         my $l = defined($opt{l})
319             ? \&ls_long_entry
320             : &ls_short_entry_factory($attr);
321 0           for ($i = 0 ; $i < scalar(@$pl); $i++) {
322 0 0         defined($re) && do { $pl->[$i][TITLE] =~ /$re/ || next };
  0 0          
323 0 0         if ($i == $n) {
324 0           $_ = $l->($i, $pl->[$i]);
325 0           s/^ />/;
326 0           print $client "$_\n";
327             } else {
328 0           print $client $l->($i, $pl->[$i]), "\n";
329             }
330             }
331             }
332              
333             # i
334             #_______________________________________
335             sub info {
336 0     0 1   my $self = shift;
337 0           my $player = $self->{player};
338 0           my $client = $self->{client};
339 0           my $mp3_attribute;
340 0           my @method = qw(
341             artist album title year genre url
342             type layer bitrate samplerate channels mode mode_extension bpf
343             copyrighted error_protected
344             );
345              
346 0           my $format = "%-15s | \%s\n";
347 0           foreach $mp3_attribute (@method) {
348 0           printf $client (
349             $format, $mp3_attribute, $player->$mp3_attribute()
350             );
351             }
352 0           printf $client ($format, "state",
353             (qw(stopped paused playing))[$player->state()]);
354 0           printf $client ($format, "random", $self->{random});
355 0           printf $client ($format, "loop", $self->{loop});
356 0           $self->time;
357             }
358              
359             # $
360             #_______________________________________
361             sub time {
362 0     0 1   my $self = shift;
363 0           my $player = $self->{player};
364 0           my $client = $self->{client};
365 0           my $format = "%-15s | \%s\n";
366              
367 0           my $f = $player->{frame};
368 0           printf $client ($format, "elapsed", $f->[2] . " seconds");
369 0           printf $client ($format, "remaining", $f->[3] . " seconds");
370 0           printf $client ($format, "total", $f->[2] + $f->[3] . " seconds");
371 0           printf $client ($format, "track", $self->{n});
372             }
373              
374             # ?
375             #_______________________________________
376             sub rand {
377 0     0 1   my $self = shift;
378 0           my $client = $self->{client};
379 0   0       my $setting = shift || ("on", "off")[$self->{random}];
380              
381 0 0         if ($setting eq "off") {
    0          
382 0           $self->{random} = 0;
383 0           *_next = \&next;
384 0           *_prev = \&prev;
385 0           print $client "random play off\n";
386             } elsif ($setting eq "on") {
387 0           $self->{random} = 1;
388 0           *_next = \&random;
389 0           *_prev = \&random;
390 0           print $client "random play on\n";
391             } else {
392 0           print $client qq("$setting" is not a valid random state.\n);
393             }
394             }
395              
396             # *
397             #_______________________________________
398             sub random {
399 0     0 0   my $self = shift;
400 0           my $pl = $self->{playlist};
401 0           my $len = scalar @$pl;
402 0           my $n;
403              
404 0 0         if ($len) {
405              
406             # prevent an mp3 from being played twice in a row
407 0 0         if ($len == 1) {
408 0           $n = 0;
409             } else {
410 0           do { $n = int(rand($len)) } until ($n != $self->{n});
  0            
411             }
412 0           $self->{n} = $n;
413 0           $self->{player}->load($pl->[$n][URL]);
414             }
415             }
416              
417             # __
418             #_______________________________________
419             sub quit {
420 0     0 1   my $self = shift;
421 0           unlink($self->{socket_path});
422 0           exit 0;
423             }
424              
425             1;
426              
427             __END__