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; |