line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package MP3::Album; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
43172
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
43
|
|
4
|
1
|
|
|
1
|
|
434
|
use MP3::Info; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
use MP3::Album::Track; |
6
|
|
|
|
|
|
|
use MP3::Album::Layout::Fetcher; |
7
|
|
|
|
|
|
|
use MP3::Album::Layout; |
8
|
|
|
|
|
|
|
use File::Basename; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $VERSION = '0.14'; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub new { |
13
|
|
|
|
|
|
|
my $c = shift; |
14
|
|
|
|
|
|
|
my %a = @_; |
15
|
|
|
|
|
|
|
my $s = { }; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
$s = bless $s, $c; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
$s->{tracks} = []; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
if ($a{files}) { |
22
|
|
|
|
|
|
|
$a{files} = [ $a{files} ] if ( ref($a{files}) ne 'ARRAY' ); |
23
|
|
|
|
|
|
|
foreach my $f (@{$a{files}}) { |
24
|
|
|
|
|
|
|
my $tr = MP3::Album::Track->new(filename => $f); |
25
|
|
|
|
|
|
|
unless ($tr) { return undef; } |
26
|
|
|
|
|
|
|
push @{$s->{tracks}}, $tr; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
$s->{current_layout} = undef; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
return $s; |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub rename_files { |
36
|
|
|
|
|
|
|
my $s = shift; |
37
|
|
|
|
|
|
|
my %a = @_; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# %t track title; |
40
|
|
|
|
|
|
|
# %a album name |
41
|
|
|
|
|
|
|
# %n track number |
42
|
|
|
|
|
|
|
# %p artist name |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
$a{format} = '%p - %a - %n - %t.mp3' unless $a{format}; |
45
|
|
|
|
|
|
|
$a{keep_copy} |= 0; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
my $layout = $s->layout(); |
48
|
|
|
|
|
|
|
my $li = $layout->info(); |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
for (my $i = 0; $i < scalar(@{$s->{tracks}}); $i++) { |
51
|
|
|
|
|
|
|
next unless $s->{tracks}->[$i]; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
my $track_number = sprintf("%02d",$i+1); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
my $fn = $a{format}; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
$fn =~ s/\%p/$li->{tracks}->[$i]->{artist}/g; |
58
|
|
|
|
|
|
|
$fn =~ s/\%a/$li->{title}/g; |
59
|
|
|
|
|
|
|
$fn =~ s/\%n/$track_number/g; |
60
|
|
|
|
|
|
|
$fn =~ s/\%t/$li->{tracks}->[$i]->{title}/g; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
$fn =~ s/\//_/g; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
my $dirname = $a{out_dir} ? $a{out_dir} : dirname($s->{tracks}->[$i]->filename()); |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my $r = $s->{tracks}->[$i]->rename(filename=>"$dirname/$fn", keep_copy => $a{keep_copy}); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
return undef unless $r; |
69
|
|
|
|
|
|
|
}; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
return 1; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub generate_tags { |
75
|
|
|
|
|
|
|
my $s = shift; |
76
|
|
|
|
|
|
|
$s->_init unless $s->{_init_done}; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
my $layout = $s->layout(); |
79
|
|
|
|
|
|
|
my $li = $layout->info(); |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
for (my $i = 0; $i < scalar(@{$s->{tracks}}); $i++) { |
82
|
|
|
|
|
|
|
next unless $s->{tracks}->[$i]; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
my $track_number = sprintf("%02d",$i+1); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my $r = $s->{tracks}->[$i]->set_tag( |
87
|
|
|
|
|
|
|
title => $li->{tracks}->[$i]->{title}, |
88
|
|
|
|
|
|
|
artist => $li->{tracks}->[$i]->{artist}, |
89
|
|
|
|
|
|
|
album => $li->{title}, |
90
|
|
|
|
|
|
|
year => $li->{year}, |
91
|
|
|
|
|
|
|
genre => $li->{genre}, |
92
|
|
|
|
|
|
|
comment => $li->{comment}, |
93
|
|
|
|
|
|
|
track_number => $track_number |
94
|
|
|
|
|
|
|
); |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
return undef unless $r; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
return 1; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub info { |
104
|
|
|
|
|
|
|
my $s = shift; |
105
|
|
|
|
|
|
|
$s->_init unless $s->{_init_done}; |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
my %info = ( |
108
|
|
|
|
|
|
|
number_oF_tracks => scalar(@{$s->{tracks}}), |
109
|
|
|
|
|
|
|
bitrates => [ $s->_album_bitrate() ], |
110
|
|
|
|
|
|
|
uniform_bitrate => scalar($s->_album_bitrate()) == 1 ? 1 : 0, |
111
|
|
|
|
|
|
|
frequencies => [ $s->_album_frequencies() ], |
112
|
|
|
|
|
|
|
uniform_frequency => scalar($s->_album_frequency()) == 1 ? 1 : 0, |
113
|
|
|
|
|
|
|
cddb_disc_id => $s->{cddb_query}->{discid} |
114
|
|
|
|
|
|
|
); |
115
|
|
|
|
|
|
|
return wantarray ? %info : \%info; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub frequency_check { |
119
|
|
|
|
|
|
|
my $s = shift; |
120
|
|
|
|
|
|
|
$s->_init unless $s->{_init_done}; |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
return scalar($s->_album_frequencies()) == 1 ? 1 : 0; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub bitrate_check{ |
126
|
|
|
|
|
|
|
my $s = shift; |
127
|
|
|
|
|
|
|
$s->_init unless $s->{_init_done}; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
return scalar($s->_album_bitrate()) == 1 ? 1 : 0; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub tracks { |
133
|
|
|
|
|
|
|
my $s = shift; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
return @{$s->{tracks}}; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub add { |
139
|
|
|
|
|
|
|
my $s = shift; |
140
|
|
|
|
|
|
|
my %a = @_; |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
$s->_init unless $s->{_init_done}; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
unless ( $a{track} ) { |
145
|
|
|
|
|
|
|
$@ = "missing param track"; |
146
|
|
|
|
|
|
|
return undef; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
my $tr = MP3::Album::Track->new(filename => $a{track}); |
150
|
|
|
|
|
|
|
unless ($tr) { return undef } |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
$a{position} = scalar(@{$s->{tracks}})+1 unless $a{position}; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
if ( $a{position} > scalar(@{$s->{tracks}})+1 ) { |
155
|
|
|
|
|
|
|
$@ = "position must be between start and end of the album"; |
156
|
|
|
|
|
|
|
return undef; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
my @tracks = @{$s->{tracks}}; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
my @f = splice(@tracks, 0, ($a{position}-1)); |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
$s->{tracks} = [@f, $tr, @tracks]; |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
delete $s->{_init_done}; |
166
|
|
|
|
|
|
|
return 1; |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
}; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub layout { |
171
|
|
|
|
|
|
|
my $s = shift; |
172
|
|
|
|
|
|
|
$s->_init unless $s->{_init_done}; |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
if (@_) { |
175
|
|
|
|
|
|
|
unless ( $_[0] && (ref $_[0] eq 'MP3::Album::Layout') ) { |
176
|
|
|
|
|
|
|
$@ = "I need a MP3::Album::Layout to set up the album layout"; |
177
|
|
|
|
|
|
|
return undef; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
$s->{current_layout} = $_[0]; |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
} elsif (! $s->{current_layout} ) { |
182
|
|
|
|
|
|
|
my $l = $s->fetch_layout(); |
183
|
|
|
|
|
|
|
return undef if (!$l); |
184
|
|
|
|
|
|
|
$s->{current_layout} = $l->[0]; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
return $s->{current_layout}; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub fetch_layout { |
191
|
|
|
|
|
|
|
my $s = shift; |
192
|
|
|
|
|
|
|
my %a = @_; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
$s->_init unless $s->{_init_done}; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
$a{method} = 'Tag' unless $a{method}; |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
my $layouts = MP3::Album::Layout::Fetcher->fetch(album=>$s, method=>$a{method}); |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
return undef unless $layouts; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
return wantarray ? @$layouts : $layouts; |
203
|
|
|
|
|
|
|
}; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub available_fetchers { |
206
|
|
|
|
|
|
|
return MP3::Album::Layout::Fetcher->available_fetchers(); |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub _album_frequencies { |
210
|
|
|
|
|
|
|
my $s = shift; |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
my %freqs; |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
foreach my $t (@{$s->{tracks}}) { |
215
|
|
|
|
|
|
|
$freqs{$t->bitrate()}=1 if $t->bitrate(); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
return keys(%freqs); |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub _album_bitrate { |
222
|
|
|
|
|
|
|
my $s = shift; |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
my %bitrates; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
foreach my $t (@{$s->{tracks}}) { |
227
|
|
|
|
|
|
|
$bitrates{$t->bitrate()}=1 if $t->bitrate(); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
return keys(%bitrates); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub _generate_toc { |
234
|
|
|
|
|
|
|
my $s = shift; |
235
|
|
|
|
|
|
|
my @toc; |
236
|
|
|
|
|
|
|
foreach my $f (@{$s->{tracks}}) { |
237
|
|
|
|
|
|
|
push @toc, $f->{info}; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
return @toc; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
sub _cddb_query_builder { |
243
|
|
|
|
|
|
|
my $s = shift; |
244
|
|
|
|
|
|
|
my %a = @_; |
245
|
|
|
|
|
|
|
die "need a toc to build a query\n" unless $a{toc}; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
my $discid = $s->_disc_id(toc => $a{toc}); |
248
|
|
|
|
|
|
|
my $disctime = $s->_disc_time(toc => $a{toc}); |
249
|
|
|
|
|
|
|
my $total_tracks = @{$a{toc}}; |
250
|
|
|
|
|
|
|
my $frames = $s->_get_frames(toc=> $a{toc}); |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
return { discid=> $discid, disctime=>$disctime, disctracks=>$total_tracks, frames=>$frames }; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub _get_frames { |
257
|
|
|
|
|
|
|
my $s = shift; |
258
|
|
|
|
|
|
|
my %a = @_; |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
my @frames; |
261
|
|
|
|
|
|
|
my $t = 0; |
262
|
|
|
|
|
|
|
foreach my $track ( @{$a{toc}} ) { |
263
|
|
|
|
|
|
|
push @frames, $t * 75; |
264
|
|
|
|
|
|
|
$t += ( $track->{MM} * 60 + $track->{SS} ); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
return \@frames; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub _disc_time { |
270
|
|
|
|
|
|
|
my $s = shift; |
271
|
|
|
|
|
|
|
my %a = @_; |
272
|
|
|
|
|
|
|
my $total_time = 0; |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
foreach my $track (@{$a{toc}}) { |
275
|
|
|
|
|
|
|
my $track_time = $track->{MM} * 60 + $track->{SS}; |
276
|
|
|
|
|
|
|
$total_time += $track_time; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
return $total_time; |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub _disc_id { |
283
|
|
|
|
|
|
|
my $s = shift; |
284
|
|
|
|
|
|
|
my %a = @_; |
285
|
|
|
|
|
|
|
my $n = 0; |
286
|
|
|
|
|
|
|
my $total_time = 0; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
foreach my $track ( @{$a{toc}} ) { |
289
|
|
|
|
|
|
|
my $track_time = $track->{MM} * 60 + $track->{SS}; |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
$n += $s->_cddb_sum($total_time); |
292
|
|
|
|
|
|
|
$total_time += $track_time; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
return sprintf("%08x", ($n % 0xFF) << 24 | $total_time << 8 | @{$a{toc}}); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
sub _cddb_sum { |
299
|
|
|
|
|
|
|
my $s = shift; |
300
|
|
|
|
|
|
|
my ($n, $ret) = (shift, 0); |
301
|
|
|
|
|
|
|
for (split //, $n) { $ret += $_ } |
302
|
|
|
|
|
|
|
return $ret; |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub _init { |
306
|
|
|
|
|
|
|
my $s = shift; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
@{$s->{toc}} = $s->_generate_toc; |
309
|
|
|
|
|
|
|
$s->{cddb_query} = $s->_cddb_query_builder(toc=>$s->{toc}); |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
$s->{_init_done} = 1; |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
return 1; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
1; |