File Coverage

blib/lib/MP3/Tag/CDDB_File.pm
Criterion Covered Total %
statement 120 130 92.3
branch 67 98 68.3
condition 36 78 46.1
subroutine 18 22 81.8
pod 8 15 53.3
total 249 343 72.5


line stmt bran cond sub pod time code
1             package MP3::Tag::CDDB_File;
2              
3 6     6   19 use strict;
  6         5  
  6         128  
4 6     6   17 use File::Basename;
  6         4  
  6         239  
5 6     6   21 use File::Spec;
  6         5  
  6         104  
6 6     6   17 use vars qw /$VERSION @ISA/;
  6         5  
  6         9608  
7              
8             $VERSION="1.00";
9             @ISA = 'MP3::Tag::__hasparent';
10              
11             =pod
12              
13             =head1 NAME
14              
15             MP3::Tag::CDDB_File - Module for parsing CDDB files.
16              
17             =head1 SYNOPSIS
18              
19             my $db = MP3::Tag::CDDB_File->new($filename, $track); # Name of audio file
20             my $db = MP3::Tag::CDDB_File->new_from($record, $track); # Contents of CDDB
21              
22             ($title, $artist, $album, $year, $comment, $track) = $db->parse();
23              
24             see L
25              
26             =head1 DESCRIPTION
27              
28             MP3::Tag::CDDB_File is designed to be called from the MP3::Tag module.
29              
30             It parses the content of CDDB file.
31              
32             The file is found in the same directory as audio file; the list of possible
33             file names is taken from the field C if set by MP3::Tag config()
34             method.
35              
36             =over 4
37              
38             =cut
39              
40              
41             # Constructor
42              
43             sub new_from {
44 0     0 0 0 my ($class, $data, $track) = @_;
45 0         0 bless {data => [split /\n/, $data], track => $track}, $class;
46             }
47              
48             sub new_setdir {
49 85     85 0 72 my $class = shift;
50 85         79 my $filename = shift;
51 85 50       241 $filename = $filename->filename if ref $filename;
52 85         1887 $filename = dirname($filename);
53 85         207 return bless {dir => $filename}, $class; # bless to enable get_config()
54             }
55              
56             sub new_fromdir {
57 85     85 0 77 my $class = shift;
58 85         62 my $h = shift;
59 85         81 my $dir = $h->{dir};
60 85         63 my ($found, $e);
61 85         166 my $l = $h->get_config('cddb_files');
62 85         133 for my $file (@$l) {
63 219         980 my $f = File::Spec->catdir($dir, $file);
64 219 100       1197 $found = $f, last if -r $f;
65             }
66 85 100       252 return unless $found;
67 36         71 local *F;
68 36 50       625 open F, "< $found" or die "Can't open `$found': $!";
69 36 0 33     145 if ($e = $h->get_config('decode_encoding_cddb_file') and $e->[0]) {
70 0         0 eval "binmode F, ':encoding($e->[0])'"; # old binmode won't compile...
71             }
72 36         941 my @data = ;
73 36 50       246 close F or die "Error closing `$found': $!";
74             bless {filename => $found, data => \@data, track => shift,
75 36         270 parent => $h->{parent}}, $class;
76             }
77              
78             sub new {
79 0     0 0 0 my $class = shift;
80 0         0 my $h = $class->new_setdir(@_);
81 0         0 $class->new_fromdir($h);
82             }
83              
84             sub new_with_parent {
85 85     85 0 98 my ($class, $filename, $parent) = @_;
86 85         145 my $h = $class->new_setdir($filename);
87 85         134 $h->{parent} = $parent;
88 85         152 $class->new_fromdir($h);
89             }
90              
91             # Destructor
92              
93       0     sub DESTROY {}
94              
95             =item parse()
96              
97             ($title, $artist, $album, $year, $comment, $track) =
98             $db->parse($what);
99              
100             parse_filename() extracts information about artist, title, track number,
101             album and year from the CDDB record. $what is optional; it maybe title,
102             track, artist, album, year, genre or comment. If $what is defined parse() will return
103             only this element.
104              
105             Additionally, $what can take values C (returns the value of
106             artist in the disk-info field DTITLE, but only if author is specified in the
107             track-info field TTITLE), C (returns the title specifically from
108             track-info field - the C may fall back to the info from disk-info
109             field), C (processed EXTD comment), C
110             (processed EXTT comment).
111              
112             The returned year and genre is taken from DYEAR, DGENRE, EXTT, EXTD fields;
113             recognized prefixes in the two last fields are YEAR, ID3Y, ID3G.
114             The declarations of this form are stripped from the returned comment.
115              
116             An alternative
117             syntax "Recorded"/"Recorded on"/"Recorded in"/ is also supported; the format
118             of the date recognized by ID3v2::year(), or just a date field without a prefix.
119              
120             =cut
121              
122             sub return_parsed {
123 26     26 0 28 my ($self,$what) = @_;
124 26 50       43 if (defined $what) {
125 26 100       44 return $self->{parsed}{a_in_title} if $what =~/^artist_collection/i;
126 23 50       26 return $self->{parsed}{t_in_track} if $what =~/^title_track/i;
127 23 100       33 return $self->{parsed}{extt} if $what =~/^comment_track/i;
128 21 100       34 return $self->{parsed}{extd} if $what =~/^comment_collection/i;
129 20 50       29 return $self->{parsed}{DISCID} if $what =~/^cddb_id/i;
130 20 100       45 return $self->{parsed}{album} if $what =~/^al/i;
131 18 100       40 return $self->{parsed}{artist} if $what =~/^a/i;
132 14 50       16 return $self->{parsed}{track} if $what =~/^tr/i;
133 14 100       32 return $self->{parsed}{year} if $what =~/^y/i;
134 11 100       23 return $self->{parsed}{comment}if $what =~/^c/i;
135 8 100       16 return $self->{parsed}{genre} if $what =~/^g/i;
136 6         23 return $self->{parsed}{title};
137             }
138            
139 0 0       0 return $self->{parsed} unless wantarray;
140 0         0 return map $self->{parsed}{$_} , qw(title artist album year comment track);
141             }
142              
143             my %r = ( 'n' => "\n", 't' => "\t", '\\' => "\\" );
144              
145             sub parse_lines {
146 8     8 0 7 my ($self) = @_;
147 8 50       16 return if $self->{fields};
148 8         7 for my $l (@{$self->{data}}) {
  8         16  
149 384 100       690 next unless $l =~ /^\s*(\w+)\s*=(\s*(.*))/;
150 224         243 my $app = $2;
151 224 50       559 $self->{fields}{$1} = "", $app = $3 unless exists $self->{fields}{$1};
152 224         249 $self->{fields}{$1} .= $app;
153 224 100       566 $self->{last} = $1 if $1 =~ /\d+$/;
154             }
155 8         10 s/\\([nt\\])/$r{$1}/g for values %{$self->{fields}};
  8         94  
156             }
157              
158             sub parse {
159 26     26 1 28 my ($self,$what) = @_;
160 26 100       54 return $self->return_parsed($what) if exists $self->{parsed};
161 8         14 $self->parse_lines;
162 8         7 my %parsed;
163 8         27 my ($t1, $c1, $t2, $c2) = map $self->{fields}{$_}, qw(DTITLE EXTD);
164 8         16 my $track = $self->track;
165 8 50       16 if ($track) {
166 8         13 my $t = $track - 1;
167 8         38 ($t2, $c2) = map $self->{fields}{$_}, "TTITLE$t", "EXTT$t";
168             }
169 8         8 my ($a, $t, $aa, $tt, $a_in_title, $t_in_track);
170 8 50       39 ($a, $t) = split /\s+\/\s+/, $t1, 2 if defined $t1;
171 8 50       14 ($a, $t) = ($t, $a) unless defined $t;
172 8 50       21 ($aa, $tt) = split /\s+\/\s+/, $t2, 2 if defined $t2;
173 8 100       15 ($aa, $tt) = ($tt, $aa) unless defined $tt;
174 8 50 33     28 undef $a if defined $a and $a =~
175             /^\s*(<<\s*)?(Various Artists|compilation disc)\s*(>>\s*)?$/i;
176 8 50 66     23 undef $aa if defined $aa and $aa =~
177             /^\s*(<<\s*)?(Various Artists|compilation disc)\s*(>>\s*)?$/i;
178 8 100 33     42 $a_in_title = $a if defined $a and length $a and defined $aa and length $aa;
      66        
      66        
179 8 100 66     18 $aa = $a unless defined $aa and length $aa;
180 8         6 $t_in_track = $tt;
181 8 50 33     25 $tt = $t unless defined $tt and length $tt;
182              
183 8         13 my ($y, $cat) = ($self->{fields}{DYEAR}, $self->{fields}{DGENRE});
184 8         10 for my $f ($c2, $c1) {
185 16 50 33     43 if (defined $f and length $f) { # Process old style declarations
186 16   100     129 while ($f =~ s/^\s*((YEAR|ID3Y)|ID3G)\b:?\s*(\d+)\b\s*(([;.,]|\s-\s)\s*)?//i
187             || $f =~ s/(?:\s*(?:[;.,]|\s-\s))?\s*\b((YEAR|ID3Y)|ID3G)\b:?\s*(\d+)\s*([;.,]\s*)?$//i) {
188 6 100 100     20 $y = $3 if $2 and not $y;
189 6 50 66     68 $cat = $3 if not $2 and not $cat;
190             }
191 16 100 33     65 if ($f =~ s{
      66        
192             ((^|[;,.]|\s+-\s) # 1,2
193             \s*
194             (Recorded (\s+[io]n)? \s* (:\s*)? )? # 3, 4, 5
195             (\d{4}([-,][-\d\/,]+)?) # 6, 7
196             \b \s* (?: [.;] \s* )?
197             ((?:[;.,]|\s-\s|$)\s*)) # 8
198             }
199             {
200 2 100 66     9 ((($self->{parent}->get_config('comment_remove_date'))->[0]
    50 33        
201             and not ($2 and $8))
202             ? '' : $1) . ($2 && $8 ? $8 : '')
203             }xeim and not ($2 and $8)) {
204             # Overwrite the disk year for longer forms
205 2 0 33     7 $y = $6 if $3 or $7 or not $y or $c2 and $f eq $c2;
      0        
      0        
      0        
206             }
207 16         18 $f =~ s/^\s+//;
208 16         21 $f =~ s/\s+$//;
209 16 50       28 undef $f unless length $f;
210             }
211             }
212 8         10 my ($cc1, $cc2) = ($c1, $c2);
213 8 50 33     26 if (defined $c2 and length $c2) { # Merge unless one is truncation of another
214 8 50 33     56 if ( defined $c1 and length $c1
      33        
      33        
215             and $c1 ne substr $c2, 0, length $c1
216             and $c1 ne substr $c2, -length $c1 ) {
217 8         14 $c2 =~ s/\s*[.,:;]$//;
218 8 50       15 my $sep = (("$c1$c2" =~ /\n/) ? "\n" : '; ');
219 8         15 $c1 = "$c2$sep$c1";
220             } else {
221 0         0 $c1 = $c2;
222             }
223             }
224 8 100 66     30 if (defined $cat and $cat =~ /^\d+$/) {
225 3         13 require MP3::Tag::ID3v1;
226 3 50       9 $cat = $MP3::Tag::ID3v1::winamp_genres[$cat] if $cat < scalar @MP3::Tag::ID3v1::winamp_genres;
227             }
228              
229 8         40 @parsed{ qw( title artist album year comment track genre
230             a_in_title t_in_track extt extd) } =
231             ($tt, $aa, $t, $y, $c1, $track, $cat, $a_in_title, $t_in_track, $cc2, $cc1);
232 8         12 $parsed{DISCID} = $self->{fields}{DISCID};
233 8         10 $self->{parsed} = \%parsed;
234 8         17 $self->return_parsed($what);
235             }
236              
237              
238             =pod
239              
240             =item title()
241              
242             $title = $db->title();
243              
244             Returns the title, obtained from the C<'Tracktitle'> entry of the file.
245              
246             =cut
247              
248             *song = \&title;
249              
250             sub title {
251 6     6 1 14 return shift->parse("title");
252             }
253              
254             =pod
255              
256             =item artist()
257              
258             $artist = $db->artist();
259              
260             Returns the artist name, obtained from the C<'Performer'> or
261             C<'Albumperformer'> entries (the first which is present) of the file.
262              
263             =cut
264              
265             sub artist {
266 4     4 1 8 return shift->parse("artist");
267             }
268              
269             =pod
270              
271             =item track()
272              
273             $track = $db->track();
274              
275             Returns the track number, stored during object creation, or queried from
276             the parent.
277              
278              
279             =cut
280              
281             sub track {
282 19     19 1 22 my $self = shift;
283 19 50       34 return $self->{track} if defined $self->{track};
284 19 100 66     69 return if $self->{recursive} or not $self->parent_ok;
285 10         21 local $self->{recursive} = 1;
286 10         46 return $self->{parent}->track1;
287             }
288              
289             =item year()
290              
291             $year = $db->year();
292              
293             Returns the year, obtained from the C<'Year'> entry of the file. (Often
294             not present.)
295              
296             =cut
297              
298             sub year {
299 3     3 1 7 return shift->parse("year");
300             }
301              
302             =pod
303              
304             =item album()
305              
306             $album = $db->album();
307              
308             Returns the album name, obtained from the C<'Albumtitle'> entry of the file.
309              
310             =cut
311              
312             sub album {
313 2     2 1 5 return shift->parse("album");
314             }
315              
316             =item comment()
317              
318             $comment = $db->comment();
319              
320             Returns the C<'Trackcomment'> entry of the file. (Often not present.)
321              
322             =cut
323              
324             sub comment {
325 3     3 1 7 return shift->parse("comment");
326             }
327              
328             =item genre()
329              
330             $genre = $db->genre($filename);
331              
332             =cut
333              
334             sub genre {
335 2     2 1 4 return shift->parse("genre");
336             }
337              
338             for my $elt ( qw( cddb_id ) ) {
339 6     6   29 no strict 'refs';
  6         6  
  6         383  
340             *$elt = sub (;$) {
341 0     0     return shift->parse($elt);
342             }
343             }
344              
345             1;