File Coverage

blib/lib/CDDB/File.pm
Criterion Covered Total %
statement 77 78 98.7
branch 14 16 87.5
condition 4 9 44.4
subroutine 33 33 100.0
pod 14 14 100.0
total 142 150 94.6


line stmt bran cond sub pod time code
1             package CDDB::File;
2              
3             $VERSION = '1.05';
4              
5             =head1 NAME
6              
7             CDDB::File - Parse a CDDB/freedb data file
8              
9             =head1 SYNOPSIS
10              
11             my $disc = CDDB::File->new("rock/f4109511");
12              
13             print $disc->id, $disc->all_ids;
14             print $disc->artist, $disc->title;
15             print $disc->year, $disc->genre, $disc->extd;
16             print $disc->length, $disc->track_count;
17            
18             print $disc->revision, $disc->submitted_via, $disc->processed_by;
19              
20             foreach my $track ($disc->tracks) {
21             print $track->number, $track->title, $track->artist;
22             print $track->length, $track->extd;
23             }
24              
25             =head1 DESCRIPTION
26              
27             This module provides an interface for extracting data from CDDB-format
28             data files, as used by freedb.
29              
30             It does not read data from your CD, or submit information to freedb.
31              
32             =head1 METHODS
33              
34             =head2 new
35              
36             my $disc = CDDB::File->new("rock/f4109511");
37              
38             This will create a new object representing the data in the file name
39             specified.
40              
41             =head2 id / all_ids
42              
43             my $discid = $disc->id;
44             my @discid = $disc->all_ids;
45              
46             Due to how freedb works, one CD may have several IDs associated with
47             it. 'id' will return the first of these (not necessarily related to the
48             filename from which this was read), whilst 'all_ids' will return all
49             of them.
50              
51             =head2 title / artist
52              
53             The title and artist of this CD. For eponymous CDs these will be
54             identical, even if the data file leaves the artist field blank.
55              
56             =head2 year
57              
58             The (4-digit) year of release.
59              
60             =head2 genre
61              
62             The genre of this CD. This is the genre as stored in the data file itself,
63             which is not related to the 11 main freedb genres.
64              
65             =head2 extd
66              
67             The "extended data" for the CD. This is used for storing miscellaneous
68             information which has no better storage place, and can be of any length.
69              
70             =head2 length
71              
72             The run time of the CD in seconds.
73              
74             =head2 track_count
75              
76             The number of tracks on the CD.
77              
78             =head2 revision
79              
80             Each time information regarding the CD is updated this revision number
81             is incremented. This returns the revision number of this version.
82              
83             =head2 processed_by / submitted_via
84              
85             The software which submitted this information to freedb and which
86             processed it at the other end.
87              
88             =head2 tracks
89              
90             foreach my $track ($disc->tracks) {
91             print $track->number, $track->title, $track->artist;
92             print $track->length, $track->extd;
93             }
94              
95             Returns a list of Track objects, each of which knows its number (numering
96             from 1), title, length (in seconds), offset, and may also have extended
97             track data.
98              
99             Tracks may also contain an 'artist' field. If this is not set the artist
100             method will return the artist of the CD.
101              
102             =cut
103              
104 1     1   24756 use strict;
  1         3  
  1         37  
105 1     1   1131 use IO::File;
  1         11731  
  1         1323  
106              
107             sub new {
108 3     3 1 398 my ($class, $file) = @_;
109 3 50       28 my $fh = new IO::File $file, "r" or die "Can't read $file\n";
110 3         632 chomp(my @data = <$fh>);
111 3         65 bless {
112             _data => \@data,
113             }, $class;
114             }
115              
116 86     86   92 sub _data { @{shift->{_data}} }
  86         5683  
117              
118 2     2 1 14 sub id { (shift->all_ids)[0] }
119 3     3 1 9 sub all_ids { split /,/, shift->_get_lines("DISCID=") }
120 2     2 1 6 sub year { shift->_get_lines("DYEAR=") }
121 1     1 1 5 sub genre { shift->_get_lines("DGENRE=") }
122 1     1 1 6 sub extd { shift->_get_lines("EXTD=") }
123 1     1 1 4 sub revision { shift->_get_lines("# Revision: ") }
124 1     1 1 4 sub submitted_via { shift->_get_lines("# Submitted via: ") }
125 1     1 1 6 sub processed_by { shift->_get_lines("# Processed by: ") }
126              
127             sub _offsets {
128 2     2   4 my $self = shift;
129 2         7 my $from = $self->_offset_line;
130 2         8 ((grep s/^#\s*//, ($self->_data)[$from + 1 .. $from + $self->track_count]),
131             $self->length * 75);
132             }
133              
134             sub _offset_line {
135 2     2   4 my $self = shift;
136 2         6 my @data = $self->_data;
137 2         12 foreach (0 .. $#data) {
138 7 100       33 return $_ if $data[$_] =~ /^# Track frame offsets/;
139             }
140 0         0 return 2;
141             }
142              
143             sub length {
144 3     3 1 11 my $length = shift->_get_lines("# Disc length: ");
145 3         31 $length =~ s/ sec(ond)?s//;
146 3         22 return $length;
147             }
148              
149             sub _title_line {
150 3     3   5 my $self = shift;
151 3   33     19 $self->{_title_line} ||= $self->_get_lines("DTITLE=")
152             }
153              
154             sub _split_title {
155 3     3   5 my $self = shift;
156 3         8 ($self->{_artist}, $self->{_title}) = split /\s+\/\s+/, $self->_title_line, 2;
157 3   33     20 $self->{_title} ||= $self->{_artist};
158             }
159              
160             sub title {
161 2     2 1 467 my $self = shift;
162 2 100       15 $self->_split_title unless defined $self->{_title};
163 2         10 $self->{_title};
164             }
165              
166             sub artist {
167 10     10 1 12 my $self = shift;
168 10 100       31 $self->_split_title unless defined $self->{_artist};
169 10         41 $self->{_artist};
170             }
171              
172             sub tracks {
173 2     2 1 845 my $self = shift;
174 2         8 my @title = $self->_get_multi_lines("TTITLE");
175 2         16 my @extd = $self->_get_multi_lines("EXTT");
176 2         32 my @offset = $self->_offsets;
177 32         245 return map {
178 2         15 bless {
179             _cd => $self,
180             _number => $_+1,
181             _tline => $title[$_],
182             _extd => $extd[$_],
183             _offset => $offset[$_],
184             _length => int(($offset[$_+1] - $offset[$_]) / 75),
185             }, 'CDDB::File::Track'
186             } 0 .. $self->_highest_track_no;
187             }
188              
189             sub _get_lines {
190 80     80   130 my ($self, $keyword) = @_;
191 80         153 join "", grep s/^${keyword}//, $self->_data;
192             }
193              
194             sub _get_multi_lines {
195 4     4   8 my ($self, $keyword) = @_;
196 4         10 map $self->_get_lines("${keyword}$_="), 0 .. $self->_highest_track_no;
197             }
198              
199             sub _highest_track_no {
200 9     9   13 my $self = shift;
201 9   66     61 $self->{_high} ||= pop @{[ map /^TTITLE(\d+)=/, $self->_data ]}
  2         7  
202             }
203              
204 3     3 1 9 sub track_count { shift->_highest_track_no + 1 }
205              
206             # ==================================================================== #
207              
208             package CDDB::File::Track;
209              
210             use overload
211 1     1   2028 '""' => 'title';
  1         1153  
  1         6  
212              
213 8     8   27 sub cd { shift->{_cd} }
214 1     1   6 sub extd { shift->{_extd} }
215 3     3   13 sub length { shift->{_length}}
216 2     2   1082 sub number { shift->{_number}}
217 2     2   9 sub offset { shift->{_offset}}
218              
219             sub _split_title {
220 5     5   9 my $self = shift;
221 5 100       14 if ($self->cd->artist eq "Various") {
222 3         23 ($self->{_artist}, $self->{_title}) = split /\s+\/\s+/, $self->{_tline}, 2
223             } else {
224 2         5 $self->{_title} = $self->{_tline};
225 2         4 $self->{_artist} = $self->cd->artist;
226             }
227              
228 5 100       16 unless ($self->{_title}) {
229 1         3 $self->{_title} = $self->{_artist};
230 1         4 $self->{_artist} = $self->cd->artist;
231             }
232             }
233              
234             sub title {
235 2     2   3 my $self = shift;
236 2 50       16 $self->_split_title unless defined $self->{_title};
237 2         9 $self->{_title};
238             }
239              
240             sub artist {
241 4     4   12 my $self = shift;
242 4 100       17 $self->_split_title unless defined $self->{_artist};
243 4         19 $self->{_artist};
244             }
245              
246             1;
247              
248             =head1 SEE ALSO
249              
250             http://www.freedb.org/
251              
252             =head1 AUTHOR
253              
254             Tony Bowden
255              
256             =head1 BUGS and QUERIES
257              
258             Please direct all correspondence regarding this module to:
259             bug-CDDB-File@rt.cpan.org
260              
261             =head1 COPYRIGHT
262              
263             Copyright (C) 2001-2005 Tony Bowden. All rights reserved.
264              
265             This program is free software; you can redistribute it and/or modify it under
266             the terms of the GNU General Public License; either version 2 of the License,
267             or (at your option) any later version.
268              
269             This program is distributed in the hope that it will be useful, but WITHOUT
270             ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
271             FOR A PARTICULAR PURPOSE.
272