File Coverage

blib/lib/Music/Audioscrobbler/Submit.pm
Criterion Covered Total %
statement 60 311 19.2
branch 10 116 8.6
condition 3 42 7.1
subroutine 14 29 48.2
pod 13 13 100.0
total 100 511 19.5


line stmt bran cond sub pod time code
1             package Music::Audioscrobbler::Submit;
2             our $VERSION = 0.05;
3              
4             # Copyright (c) 2008 Edward J. Allen III
5              
6             #
7             # You may distribute under the terms of either the GNU General Public
8             # License or the Artistic License, as specified in the README file.
9             #
10              
11             =pod
12              
13             =for changes stop
14              
15             =head1 NAME
16              
17             Music::Audioscrobbler::Submit - Module providing routines to submit songs to last.fm using 1.2 protocol.
18              
19             =for readme stop
20              
21             =head1 SYNOPSIS
22              
23             use Music::Audioscrobbler::Submit
24             my $mpds = Music::Audioscrobbler::Submit->new(\%options);
25              
26             $mpds->submit("/path/to/song.mp3");
27              
28             =for readme continue
29              
30             =head1 DESCRIPTION
31              
32             Music::Audioscrobbler::Submit is a scrobbler for MPD implementing the 1.2 protocol, including "Now Playing' feature.
33              
34             Items are submitted and stored in a queue. This queue is stored as a file using Tie::File. When you submit a track,
35             it will add the queue to the track and process the queue. If it submits all items in the queue, the L method
36             will return true. A method called L allows you to try again in case of failure. Do not submit
37             songs more than once!
38              
39             =begin readme
40              
41             =head1 INSTALLATION
42              
43             To install this module type the following:
44              
45             perl Makefile.PL
46             make
47             make test
48             make install
49              
50             =head1 DEPENDENCIES
51              
52             This module requires these other modules and libraries:
53              
54             Encode
55             File::Spec
56             Digest::MD5
57             Config::Options
58             LWP
59             Tie::File
60             Music::Tag
61              
62             =end readme
63              
64             =cut
65              
66 1     1   81340 use strict;
  1         4  
  1         45  
67 1     1   6 use warnings;
  1         2  
  1         113  
68 1     1   807 use File::Spec;
  1         9  
  1         39  
69 1     1   7 use Digest::MD5 qw(md5_hex);
  1         29  
  1         168  
70 1     1   3981 use Encode qw(encode);
  1         47140  
  1         171  
71 1     1   3015 use IO::File;
  1         28794  
  1         448  
72 1     1   4420 use Config::Options;
  1         17920  
  1         33  
73 1     1   1175 use LWP::UserAgent;
  1         81446  
  1         42  
74 1     1   3708 use Tie::File;
  1         29107  
  1         4668  
75              
76              
77             sub default_options {
78 1     1 1 34 { lastfm_username => undef,
79             lastfm_password => undef,
80             mdb_opts => {},
81             musicdb => 0,
82             musictag => 0,
83             musictag_overwrite => 0,
84             verbose => 1,
85             timeout => 15, # Set low to prevent missing a scrobble. Rather retry submit.
86             logfile => undef,
87             scrobble_queue => $ENV{HOME} . "/.musicaudioscrobbler_queue",
88             optionfile => [ "/etc/musicmpdscrobble.conf", $ENV{HOME} . "/.musicmpdscrobble.conf" ],
89             lastfm_client_id => "tst",
90             lastfm_client_version => "1.0",
91             get_mbid_from_mb => 0,
92             proxy_server => undef,
93             #lastfm_client_id => "mam",
94             #lastfm_client_version => "0.1",
95             music_tag_opts => {
96             quiet => 1,
97             verbose => 0,
98             ANSIColor => 0,
99             },
100             };
101             }
102              
103             =pod
104              
105             =head1 METHODS
106              
107             =over 4
108              
109             =item new()
110              
111             my $mas = Music::Audioscrobbler::Submit->new($options);
112              
113             =cut
114              
115             sub new {
116 1     1 1 20 my $class = shift;
117 1   50     9 my $options = shift || {};
118 1         2 my $self = {};
119 1         4 bless $self, $class;
120 1         6 $self->options( $self->default_options );
121 1 50       235 if ($options->{optionfile}) {
122 0         0 $self->options->options("optionfile", $options->{optionfile});
123             }
124 1         4 $self->options->fromfile_perl( $self->options->{optionfile} );
125 1         61 $self->options($options);
126 1         28 $self->{scrobble_ok} = 1;
127              
128 1 50       3 unless ( $self->options('lastfm_md5password') ) {
129 1 50       14 if ( $self->options('lastfm_password') ) {
130 1         13 $self->options->{lastfm_md5password} =
131             Digest::MD5::md5_hex( $self->options->{lastfm_password} );
132 1         10 delete $self->options->{lastfm_password};
133             }
134             else {
135 0         0 $self->status(0, "ERORR: lastfm_password option is not set. Please update config file. This error is fatal.");
136 0         0 die "Bad password info."
137             }
138             }
139              
140 1 50       12 if ($self->options->{lastfm_client_id} eq "tst") {
141 1         14 $self->status(0, "WARNING: Using client id 'tst' is for testing only. Please use an assigned ID");
142             }
143 1         5 return $self;
144             }
145              
146             =pod
147              
148             =item options()
149              
150             Get or set options via hash. Here is a list of available options:
151              
152             =over 4
153              
154             =item lastfm_username
155              
156             lastfm username
157              
158             =item lastfm_password
159              
160             lastfm password. Not needed if lastfm_md5password is set.
161              
162             =item lastfm_md5password
163              
164             MD5 hash of lastfm password.
165              
166             =item lastfm_client_id
167              
168             Client ID provided by last.fm. Defaults to "tst", which is valid for testing only.
169              
170             =item lastfm_client_version
171              
172             Set to the version of your program when setting a valid client_id. Defaults to "1.0"
173              
174             =item verbose
175              
176             Set verbosity level (1 through 4)
177              
178             =item logfile
179              
180             File to output log info to. If set to "STDERR" or undef, will print messages to STDERR. If set to "STDOUT" will print messages to STDOUT.
181              
182             =item scrobble_queue
183              
184             Path to file to queue info to. Defaults to ~/.musicaudioscrobbler_queue
185              
186             =item get_mbid_from_mb
187              
188             Use the Music::Tag::MusicBrainz plugin to get missing "mbid" value. Defaults false.
189              
190             =item musictag
191              
192             True if you want to use L to get info from file. This is important if you wish to use filenames to submit from.
193              
194             =item musictag_overwrite
195              
196             True if you want to Music::Tag info to override file info. Defaults to false, which with the unicode problems with Music::Tag is a good thing.
197              
198             =item music_tag_opts
199              
200             Options for L
201              
202             =item proxy_server
203              
204             URL for proxy_server in the form http://my.proxy.ca:8080
205              
206             =back
207              
208             =cut
209              
210             sub options {
211 12     12 1 37 my $self = shift;
212 12 100       57 if ( exists $self->{_options} ) {
213 11         32 return $self->{_options}->options(@_);
214             }
215             else {
216 1         12 $self->{_options} = Config::Options->new();
217 1         41 return $self->{_options}->options(@_);
218             }
219             }
220              
221             =item default_options()
222              
223             Returns a reference to the default options.
224              
225             =cut
226              
227             =item now_playing()
228              
229             Takes a file, hashref, or Music::Tag object and submits the song to Last.FM now playing info. For example:
230              
231             $mas->now_playing("/path/to/file.mp3");
232              
233             The hash reference is of the form:
234              
235             { artist => "Artist Name", # Mandatory
236             title => "Song Title" # Mandatory
237             secs => 300, # Length of time in seconds (integers only please). Mandatory
238             album => "Album", # Optional
239             tracknum => 12, # Optional
240             mbid => '6299a467-95bc-4bc1-925d-71c4e556770d' # Optional
241             }
242            
243             =cut
244              
245             sub now_playing {
246 0     0 1 0 my $self = shift;
247 0         0 my $info = shift;
248 0         0 my $h = $self->info_to_hash($info);
249 0 0       0 return unless ( defined $h );
250 0 0 0     0 unless ( $self->{session_id} && ( ( time - $self->{timestamp} ) < 3600 ) ) {
251 0         0 my $h = $self->handshake();
252 0 0       0 unless ($h) { return $h; }
  0         0  
253             }
254 0         0 my @sub = ();
255 0         0 push @sub, "s", $self->{session_id};
256 0         0 push @sub, "a", $h->{artist};
257 0         0 push @sub, "t", $h->{title};
258 0         0 push @sub, "b", $h->{album};
259 0         0 push @sub, "l", $h->{secs};
260 0         0 push @sub, "n", $h->{track};
261 0         0 push @sub, "m", $h->{mbid};
262 0         0 my $q = $self->_makequery(@sub);
263 0         0 my $req = HTTP::Request->new( 'POST', $self->{nowplaying_url} );
264              
265 0 0       0 unless ($req) {
266 0         0 die 'Could not create the submission request object';
267             }
268 0         0 $self->status( 2,
269             "Notifying nowplaying info to ",
270             $self->{nowplaying_url},
271             " with query: $q\n" );
272 0         0 $req->content_type('application/x-www-form-urlencoded; charset="UTF-8"');
273 0         0 $req->content($q);
274 0         0 my $resp = $self->ua->request($req);
275 0         0 $self->status( 2, "Response to submission is: ",
276             $resp->content, " and success is ",
277             $resp->is_success );
278 0         0 my @lines = split /[\r\n]+/, $resp->content;
279 0         0 my $status = shift @lines;
280              
281 0 0       0 if ( $status eq "OK" ) {
    0          
282 0         0 $self->status( 1, "Notification OK" );
283 0         0 return 1;
284             }
285             elsif ( $status eq "BADSESSION" ) {
286 0         0 $self->status( 0, "Bad session code: ", @lines );
287 0         0 $self->{session_id} = 0;
288 0         0 return 0;
289             }
290             else {
291 0         0 $self->status( 0, "Unknown Error: ", $status, " ", @lines );
292 0         0 return undef;
293             }
294             }
295              
296             =item submit()
297              
298              
299             To submit a song pass an arrayref whose first entry is a File, Music::Tag object, or hashref (see L) for format) and whose second entry is
300             an integer representing the seconds since epoch (UNIX time). Several songs can be submitted simultaneously. For example:
301              
302             $mas->submit->(["/path/to/file.mp3", time]);
303              
304             or:
305              
306             $mas->submit->( ["/var/mp3s/song1.mp3", time - 600 ],
307             ["/var/mp3s/song2.mp3", time - 300 ],
308             ["/var/mp3s/song3.mp3", time ] );
309              
310             Returns true if song was scrobbled, false otherwise. submit calls L. If it fails, L can be called
311             again.
312              
313             The following is taken from L:
314              
315             The client should monitor the user's interaction with the music playing service to whatever extent the service allows. In order to qualify for submission all of the following criteria must be met:
316              
317             1. The track must be submitted once it has finished playing. Whether it has finished playing naturally or has been manually stopped by the user is irrelevant.
318              
319             2. The track must have been played for a duration of at least 240 seconds or half the track's total length, whichever comes first. Skipping or pausing the track is irrelevant as long as the appropriate amount has been played.
320              
321             3. The total playback time for the track must be more than 30 seconds. Do not submit tracks shorter than this.
322              
323             4. Unless the client has been specially configured, it should not attempt to interpret filename information to obtain metadata instead of tags (ID3, etc).
324              
325             =cut
326              
327             sub submit {
328 0     0 1 0 my $self = shift;
329 0         0 foreach my $s (@_) {
330 0         0 my ( $info, $timestamp ) = @{$s};
  0         0  
331 0         0 my $h = $self->info_to_hash($info);
332 0 0       0 if ($h) {
333 0         0 push @{ $self->scrobble_queue }, $self->_serialize_info( $h, $timestamp );
  0         0  
334             }
335             }
336 0         0 $self->process_scrobble_queue;
337             }
338              
339             =item process_scrobble_queue()
340              
341             Processes the current scrobble queue. Call this if submit fails and you wish to try again. Do not resubmit a song.
342              
343             =cut
344              
345             # Process up to 50 files from scrobble_queue. Recursivly calls itself if necessary / possible to empty scrobble_queue
346             sub process_scrobble_queue {
347 0     0 1 0 my $self = shift;
348 0 0       0 return -1 unless scalar @{ $self->scrobble_queue };
  0         0  
349 0         0 my @submit = ();
350 0         0 foreach ( @{ $self->scrobble_queue } ) {
  0         0  
351 0         0 push @submit, [ $self->_deserialize_info($_) ];
352 0 0       0 if ( scalar @submit >= 50 ) {
353 0         0 last;
354             }
355             }
356 0         0 my $ok = $self->_do_submit(@submit);
357 0 0       0 if ($ok) {
358 0         0 foreach (@submit) {
359 0         0 shift @{ $self->scrobble_queue };
  0         0  
360             }
361 0 0       0 if ( scalar @{ $self->scrobble_queue } ) {
  0         0  
362 0         0 $self->process_scrobble_queue;
363             }
364             }
365 0         0 return $ok;
366             }
367              
368             sub _do_submit {
369 0     0   0 my $self = shift;
370 0 0 0     0 unless ( $self->{session_id} && ( ( time - $self->{timestamp} ) < 3600 ) ) {
371 0         0 my $h = $self->handshake();
372 0 0       0 unless ($h) { return $h; }
  0         0  
373             }
374 0         0 my @sub = ();
375 0         0 push @sub, "s", $self->{session_id};
376 0         0 my $n = 0;
377 0         0 foreach my $s (@_) {
378 0         0 my ( $info, $timestamp ) = @{$s};
  0         0  
379 0         0 my $h = $self->info_to_hash($info);
380 0 0       0 next unless ( defined $h );
381 0         0 push @sub, "a[$n]", $h->{artist};
382 0         0 push @sub, "t[$n]", $h->{title};
383 0         0 push @sub, "i[$n]", $timestamp;
384 0         0 push @sub, "o[$n]", "P"; # Nothing but P supported yet.
385 0         0 push @sub, "r[$n]", ""; # Not supported yet.
386 0         0 push @sub, "l[$n]", $h->{secs};
387 0         0 push @sub, "b[$n]", $h->{album};
388 0         0 push @sub, "n[$n]", $h->{track};
389 0         0 push @sub, "m[$n]", $h->{mbid};
390 0         0 $self->status( 1, "Submitting: ", scalar localtime($timestamp),
391             " ", $h->{artist}, " - ", $h->{title} );
392 0         0 $n++;
393             }
394 0         0 my $q = $self->_makequery(@sub);
395 0         0 my $req = HTTP::Request->new( 'POST', $self->{submission_url} );
396 0 0       0 unless ($req) {
397 0         0 die 'Could not create the submission request object';
398             }
399 0         0 $self->status( 2, "Performing submission to ", $self->{submission_url}, " with query: $q\n" );
400 0         0 $req->content_type('application/x-www-form-urlencoded; charset="UTF-8"');
401 0         0 $req->content($q);
402 0         0 my $resp = $self->ua->request($req);
403 0         0 $self->status( 2, "Response to submission is: ",
404             $resp->content, " and success is ",
405             $resp->is_success );
406              
407 0         0 my @lines = split /[\r\n]+/, $resp->content;
408              
409 0         0 my $status = shift @lines;
410 0 0       0 if ( $status eq "OK" ) {
    0          
411 0         0 $self->status( 1, "Submission OK" );
412 0         0 return 1;
413             }
414             elsif ( $status eq "BADSESSION" ) {
415 0         0 $self->status( 0, "Bad session code: ", @lines );
416 0         0 $self->{session_id} = 0;
417 0         0 return 0;
418             }
419             else {
420 0         0 $self->status( 0, "Unknown Error: ", $status, " ", @lines );
421 0         0 return undef;
422             }
423             }
424              
425             sub _serialize_info {
426 0     0   0 my $self = shift;
427 0         0 my ( $h, $timestamp ) = @_;
428 0         0 my $ret = join( "\0", timestamp => $timestamp, %{$h} );
  0         0  
429             }
430              
431             sub _deserialize_info {
432 0     0   0 my $self = shift;
433 0         0 my $in = shift;
434 0         0 my %ret = split( "\0", $in );
435 0         0 return ( \%ret, $ret{timestamp} );
436             }
437              
438             sub _get_mbid {
439 0     0   0 my $self = shift;
440 0         0 my $info = shift;
441 0 0       0 unless ($info->mb_trackid) {
442 0         0 my $mb = $info->add_plugin("MusicBrainz");
443 0         0 $mb->get_tag();
444             }
445             }
446              
447             =item handshake()
448              
449             Perform handshake with Last.FM. You don't need to call this, it will be called by L or L when necessary.
450              
451             =cut
452              
453             sub handshake {
454 0     0 1 0 my $self = shift;
455 0         0 my $timestamp = time;
456 0         0 my $auth = md5_hex( $self->options->{lastfm_md5password} . $timestamp );
457 0         0 my @query = ( 'hs' => "true",
458             'p' => "1.2",
459             'c' => $self->options->{lastfm_client_id},
460             'v' => $self->options->{lastfm_client_version},
461             'u' => $self->options->{lastfm_username},
462             't' => $timestamp,
463             'a' => $auth
464             );
465 0         0 my $q = $self->_makequery(@query);
466              
467 0         0 $self->status( 2, "Performing Handshake with query: $q\n" );
468              
469 0         0 my $req = new HTTP::Request( 'GET', "http://post.audioscrobbler.com/?$q" );
470 0 0       0 unless ($req) {
471 0         0 die 'Could not create the handshake request object';
472             }
473 0         0 my $resp = $self->ua->request($req);
474 0         0 $self->status( 2, "Response to handshake is: ",
475             $resp->content, " and success is ",
476             $resp->status_line );
477 0 0       0 unless ( $resp->is_success ) {
478 0         0 $self->status( 0, "Response failed: ", $resp->status_line );
479 0         0 return 0;
480             }
481              
482 0         0 my @lines = split /[\r\n]+/, $resp->content;
483              
484 0         0 my $status = shift @lines;
485 0 0       0 if ( $status eq "OK" ) {
    0          
    0          
    0          
486 0         0 $self->{session_id} = shift @lines;
487 0         0 $self->{nowplaying_url} = shift @lines;
488 0         0 $self->{submission_url} = shift @lines;
489 0         0 $self->{timestamp} = $timestamp;
490 0         0 return $self->{session_id};
491             }
492             elsif ( $status eq "FAILED" ) {
493 0         0 $self->status( 0, "Temporary Failure: ", @lines );
494 0         0 return 0;
495             }
496             elsif ( $status eq "BADAUTH" ) {
497 0         0 $self->status( 0, "Bad authorization code (I have the wrong password): ", @lines);
498 0         0 die "Bad password\n";
499             }
500             elsif ( $status eq "BADTIME" ) {
501 0         0 $self->status( 0, "Bad time stamp: ", @lines );
502 0         0 return undef;
503             }
504             else {
505 0         0 $self->status( 0, "Unknown Error: ", $status, " ", @lines );
506 0         0 return undef;
507             }
508             }
509              
510              
511              
512             =item music_tag_opts()
513              
514             Get or set the current options for new Music::Tag objects.
515              
516             =cut
517              
518             sub music_tag_opts {
519 0     0 1 0 my $self = shift;
520 0   0     0 my $options = shift || {};
521 0         0 my $mt_opts = { ( %{ $self->options->{music_tag_opts} }, %{$options} ) };
  0         0  
  0         0  
522 0         0 return $mt_opts;
523             }
524              
525              
526             =item logfileout()
527              
528             Glob reference (or IO::File) to current log file. If passed a value, will use this instead of what the logfile option is set to. Any glob reference that can be printed to will work (that's all we ever do).
529              
530             =cut
531              
532             sub logfileout {
533 1     1 1 3 my $self = shift;
534 1         2 my $fh = shift;
535 1 50       4 if ($fh) {
536 0         0 $self->{logfile} = $fh;
537             }
538 1 50 33     20 unless ( ( exists $self->{logfile} ) && ( $self->{logfile} ) ) {
539 1 50 33     4 if ((not $self->options->{logfile}) or ($self->options->{logfile} eq "STDERR" )) {
    0          
540 1         19 return \*STDERR;
541             }
542             elsif ($self->options->{logfile} eq "STDOUT" ) {
543 0         0 return \*STDOUT;
544             }
545 0         0 my $fh = IO::File->new( $self->options->{logfile}, ">>" );
546 0 0       0 unless ($fh) {
547 0         0 print STDERR "Error opening log, using STDERR: $!";
548 0         0 return \*STDERR;
549             }
550 0         0 $fh->autoflush(1);
551 0         0 $self->{logfile} = $fh;
552             }
553 0         0 return $self->{logfile};
554             }
555              
556              
557             =item status()
558              
559             Print to log. First argument is a level (0 - 4). For example:
560              
561             $mas->status($level, @message);
562              
563             =cut
564              
565             sub status {
566 1     1 1 3 my $self = shift;
567 1         2 my $level = shift;
568 1 50       3 if ( $level <= $self->options->{verbose} ) {
569 1         12 my $out = $self->logfileout;
570 1         339 print $out scalar localtime(), " ", @_, "\n";
571             }
572             }
573              
574             =item scrobble_queue()
575              
576             Returns a reference to the current scrobble_queue. This is a tied hash using Tie::File. Useful to found out how many items still need to be
577             scrobbled after a failed L.
578              
579             =cut
580              
581             sub scrobble_queue {
582 0     0 1   my $self = shift;
583 0 0 0       unless ( ( exists $self->{scrobble_queue} ) && ( $self->{scrobble_queue} ) ) {
584 0           my @q;
585 0 0         tie @q, 'Tie::File', $self->options("scrobble_queue")
586             or die "Couldn't tie array to scrobble_queue: " . $self->options("scrobble_queue");
587 0           $self->{scrobble_queue} = \@q;
588             }
589 0           return $self->{scrobble_queue};
590             }
591              
592              
593             =item ua()
594              
595             Returns the LWP::UserAgent used. If passed a value, will use that as the new LWP::UserAgent object.
596              
597             =cut
598              
599             sub ua {
600 0     0 1   my $self = shift;
601 0           my $ua = shift;
602 0 0 0       unless ( ( exists $self->{ua} ) && ( ref $self->{ua} ) ) {
603 0           $self->{ua} = LWP::UserAgent->new();
604 0           $self->{ua}->env_proxy();
605 0           $self->{ua}->agent( 'scrobbler-helper/1.0 ' . $self->{ua}->_agent() );
606 0           $self->{ua}->timeout( $self->options->{timeout} );
607 0 0         if ($self->options->{proxy_server}) {
608 0           $self->{ua}->proxy('http', $self->options->{proxy_server})
609             }
610             }
611 0 0         unless ( $self->{'ua'} ) {
612 0           die 'Could not create an LWP UserAgent object?!?';
613             }
614 0           return $self->{'ua'};
615             }
616              
617             sub _URLEncode($) {
618 0     0     my $theURL = shift;
619 0 0         if (defined $theURL) {
620 0           utf8::upgrade($theURL);
621 0           $theURL =~ s/([^a-zA-Z0-9_\.])/'%' . uc(sprintf("%2.2x",ord($1)));/eg;
  0            
622 0           return $theURL;
623             }
624             }
625              
626             sub _makequery {
627 0     0     my $self = shift;
628 0           my @query = @_;
629 0           my $q = "";
630 0           for ( my $i = 0 ; $i < @query ; $i += 2 ) {
631 0 0         if ($q) { $q .= "&" }
  0            
632 0           $q .= $query[$i] . "=" . _URLEncode( $query[ $i + 1 ] );
633             }
634 0           return $q;
635             }
636              
637             =item info_to_hash()
638              
639             Takes a filename, hashref, or Music::Tag object and returns a hash with the structure required by L or L.
640             Normally this is called automatically by L or L. See L for syntax of hash.
641              
642             Examples:
643              
644             my $hash = $mas->info_to_hash("/path/to/mp3/file.mp3");
645              
646             is functionally equivalent to
647              
648             my $hash = $mas->info_to_hash(Music::Tag->new("/path/to/mp3/file.mp3", $mas->music_tag_opts() ));
649              
650             =cut
651              
652             sub info_to_hash {
653 0     0 1   my $self = shift;
654 0           my $info = shift;
655 0 0         if ( ref $info eq "HASH" ) {
    0          
    0          
656 0 0         if ( exists $info->{filename} ) {
657 0           eval {
658 0           my $extra = $self->_get_info_from_file( $info->{filename} );
659 0           while ( my ( $k, $v ) = each %{$extra} ) {
  0            
660 0 0 0       next if ( ( $k eq "secs" ) && ( exists $info->{secs} ) && ( $info->{secs} > 30 ) );
      0        
661 0 0 0       if (($self->options->{musictag_overwrite}) or ( not $info->{$k})) {
662 0           $self->status(4, "Setting $k to $v from Music::Tag\n");
663 0           $info->{$k} = $v;
664             }
665             }
666             }; # eval'd to protect from a bad Music::Tag plugin causing trouble.
667 0 0         if ($@) { $self->status( 0, "Error with Music::Tag: ", $@ ) }
  0            
668             }
669 0           foreach (qw(artist title secs album track mbid tracknum)) {
670 0 0         unless ( exists $info->{$_} ) {
671 0           $info->{$_} = "";
672             }
673 0 0         if ( exists $info->{mb_trackid} ) {
674 0           $info->{mbid} = $info->{mb_trackid};
675             }
676 0 0         if ( exists $info->{length} ) {
677 0           $info->{secs} = $info->{length};
678             }
679 0 0         unless ( $info->{secs} ) {
680 0           $info->{secs} = 300;
681             }
682             }
683 0           return $info;
684             }
685             elsif ( ref $info ) {
686 0           my $ret = {};
687 0           $ret->{artist} = $info->artist;
688 0           $ret->{title} = $info->title;
689 0   0       $ret->{secs} = int( $info->secs ) || 300;
690 0   0       $ret->{album} = $info->album || "";
691 0   0       $ret->{track} = $info->track || "";
692 0 0 0       if (($self->options->{get_mbid_from_mb}) && (not $info->mb_trackid)) {
693 0           $self->status(2, "Attempting to get mbid from MusicBrainz");
694 0           $self->_get_mbid($info, {quiet => 1, verbose => 0});
695 0 0         if ($info->mb_trackid) {
696 0           $self->status(2, "Got mbid: ", $info->mb_trackid);
697             }
698             else {
699 0           $self->status(2, "Failed to get mbid from MusicBrainz");
700             }
701             }
702 0   0       $ret->{mbid} = $info->mb_trackid || "";
703 0           return $ret;
704             }
705             elsif ( -f $info ) {
706 0           return $self->_get_info_from_file($info);
707             }
708 0           $self->status( 0, "Hash or Music::Tag object or filename required!" );
709 0           return undef;
710             }
711              
712              
713             sub _get_info_from_file {
714 0     0     my $self = shift;
715 0           my $file = shift;
716 0 0         return unless ( $self->options->{musictag} );
717 0           require Music::Tag;
718 0           $self->status( 3, "Filename $file detected" );
719 0           my $minfo = Music::Tag->new( $file, $self->music_tag_opts() );
720 0 0         if ($minfo) {
721 0 0         if ( $self->options->{musicdb} ) {
722 0           $minfo->add_plugin("MusicDB");
723             }
724 0           $minfo->get_tag;
725 0           $self->status( 4, "Filename $file is really " . $minfo->title );
726 0           return $self->info_to_hash($minfo);
727             }
728             }
729              
730             =back
731              
732             =head1 SEE ALSO
733              
734             L, L
735              
736             =for changes continue
737              
738             =head1 CHANGES
739              
740             =over 4
741              
742             =item Release Name: 0.05
743              
744             =over 4
745              
746             =item *
747              
748             Added new option: proxy_server to set proxy_server. Also now reads proxy server from enviroment.
749              
750             =back
751              
752             =back
753              
754             =over 4
755              
756             =item Release Name: 0.04
757              
758             =over 4
759              
760             =item *
761              
762             I noticed that Music::Tag was called with a use function. Removed this line to remove Music::Tag requirement.
763              
764             =item *
765              
766             Added some more level 4 debuging messages.
767              
768             =back
769              
770             =back
771              
772             =over 4
773              
774             =item Release Name: 0.03
775              
776             =over 4
777              
778             =item *
779              
780             Added musictag_overwrite option. This is false by default. It is a workaround for problems with Music::Tag and unicode. Setting this to
781             true allows Music::Tag info to overwrite info from MPD. Do not set this to true until Music::Tag returns proper unicode consistantly.
782              
783             =back
784              
785             =back
786              
787             =over 4
788              
789             =item Release Name: 0.02
790              
791             =over 4
792              
793             =item *
794              
795             Will print error and die if lastfm_password is not set.
796              
797             =item *
798              
799             Will print error and die if BADAUTH is received.
800              
801             =back
802              
803             =item Release Name: 0.01
804              
805             =over 4
806              
807             =item *
808              
809             Initial Release
810              
811             =back
812              
813             =back
814              
815             =for changes stop
816              
817             =for readme continue
818              
819             =head1 AUTHOR
820              
821             Edward Allen III
822              
823             =head1 COPYRIGHT
824              
825             Copyright (c) 2007,2008 Edward Allen III. Some rights reserved.
826              
827             =head1 LICENSE
828              
829             This program is free software; you can redistribute it and/or modify
830             it under the same terms as Perl itself, either:
831              
832             a) the GNU General Public License as published by the Free
833             Software Foundation; either version 1, or (at your option) any
834             later version, or
835              
836             b) the "Artistic License" which comes with Perl.
837              
838             This program is distributed in the hope that it will be useful,
839             but WITHOUT ANY WARRANTY; without even the implied warranty of
840             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either
841             the GNU General Public License or the Artistic License for more details.
842              
843             You should have received a copy of the Artistic License with this
844             Kit, in the file named "Artistic". If not, I'll be glad to provide one.
845              
846             You should also have received a copy of the GNU General Public License
847             along with this program in the file named "Copying". If not, write to the
848             Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
849             Boston, MA 02110-1301, USA or visit their web page on the Internet at
850             http://www.gnu.org/copyleft/gpl.html.
851              
852              
853             =cut
854              
855             1;