File Coverage

blib/lib/MP3/TAG/ID3v1.pm
Criterion Covered Total %
statement 52 111 46.8
branch 13 44 29.5
condition 4 12 33.3
subroutine 8 13 61.5
pod 5 8 62.5
total 82 188 43.6


line stmt bran cond sub pod time code
1             package MP3::TAG::ID3v1;
2              
3             # reads ID3v1 and ID3v1.1 mp3-tags
4             # writes ID3v1.1 mp3-tags
5              
6 1     1   3 use strict;
  1         2  
  1         28  
7 1     1   6 use vars qw /@mp3_genres @winamp_genres $AUTOLOAD %ok_length/;
  1         2  
  1         1273  
8             # allowed fields in ID3v1.1 and max length of this fields (expect for track and genre which are coded later)
9             %ok_length = (song => 30, artist => 30, album => 30, comment => 28, track => 3, genre => 30, year=>4, genreID=>1);
10              
11             =pod
12              
13             =head1 NAME
14              
15             MP3::TAG::ID3v1 - Perl extension for reading / writing ID3v1 tags of mp3-files
16              
17             =head1 SYNOPSIS
18              
19             MP3::TAG::ID3v2 is designed to be called from the MP3::Tag module.
20             It then returns a ID3v2-tag-object, which can be used in a users
21             program.
22              
23             use MP3::TAG::ID3v1;
24             $id3v1 = MP3::TAG::ID3v1->new($mp3obj);
25              
26             C<$mp3obj> is a object from MP3::Tag. See according documentation.
27             C<$tag> is undef when no tag is found in the C<$mp3obj>.
28            
29             * Reading the tag
30              
31             print " Song: " .$id3v1->song . "\n";
32             print " Artist: " .$id3v1->artist . "\n";
33             print " Album: " .$id3v1->album . "\n";
34             print "Comment: " .$id3v1->comment . "\n";
35             print " Year: " .$id3v1->year . "\n";
36             print " Genre: " .$id3v1->genre . "\n";
37             print " Track: " .$id3v1->track . "\n";
38             @tagdata = $mp3->all();
39             foreach (@tagdata) {
40             print $_;
41             }
42              
43             * Changing / Writing the tag
44              
45             $id3v1->comment("This is only a Test Tag");
46             $id3v1->song("testing");
47             $id3v1->artist("Artest");
48             $id3v1->album("Test it");
49             $id3v1->year("1965");
50             $id3v1->track("5");
51             $id3v1->genre("Blues");
52             # or at once
53             $id3v1->all("song title","artist","album","1900","comment",10,"Ska");#
54             $id3v1->writeTag;
55              
56             =head1 AUTHOR
57              
58             Thomas Geffert, thg@users.sourceforge.net
59              
60             =head1 DESCRIPTION
61              
62             =item new()
63              
64             $id3v1 = MP3::TAG::ID3v1->new($mp3obj[, $create]);
65              
66             Generally called from MP3::TAG, because a $mp3obj is needed.
67             If $create is true, a new tag is created. Otherwise undef is
68             returned, if now ID3v1 tag is found in the $mp3obj.
69              
70             =cut
71              
72             # create a ID3v1 object
73             sub new {
74 2     2 1 4 my ($class, $mp3obj, $create) = @_;
75 2         4 my $self={mp3=>$mp3obj};
76 2         2 my $buffer;
77              
78 2 50 33     6 if (defined $create && $create) {
79 0         0 $self->{new} = 1;
80             } else {
81 2         6 $mp3obj->seek(-128,2);
82 2         6 $mp3obj->read(\$buffer, 128);
83             }
84            
85 2 50 33     11 if ($create || substr ($buffer,0,3) eq "TAG") {
86 2         5 bless $self, $class;
87 2         4 $self->readTag($buffer);
88              
89 2         7 return $self;
90             } else {
91 0         0 return undef;
92             }
93             }
94              
95             =pod
96              
97             =item song(), artist(), album(), year(), comment(), track(), genre()
98              
99             $artist = $id3v1->artist;
100             $artist = $id3v1->artist($artist);
101             $album = $id3v1->album;
102             $album = $id3v1->album($album);
103             $year = $id3v1->year;
104             $year = $id3v1->year($year);
105             $comment = $id3v1->comment;
106             $comment = $id3v1->comment($comment);
107             $track = $id3v1->track;
108             $track = $id3v1->track($track);
109             $genre = $id3v1->genre;
110             $genre = $id3v1->genre($genre);
111              
112             Use these functions to retrieve the date of these fields,
113             or to set the data.
114              
115             $genre can be a string with the name of the genre, or a number
116             describing the genre.
117              
118             =cut
119              
120             sub AUTOLOAD {
121 6     6   81 my $self = shift;
122 6         8 my $attr = $AUTOLOAD;
123              
124             # is it an allowed field
125 6         23 $attr =~ s/.*:://;
126 6 50       17 return unless $attr =~ /[^A-Z]/;
127 6 50       15 warn "invalid field: ->$attr()" unless $ok_length{$attr};
128              
129 6 100       21 if (my $new = shift) {
130 2         8 $new =~ s/ *$//;
131 2         4 $new = substr $new, 0, $ok_length{$attr};
132 2 50       5 if ($attr eq "genre") {
133 0 0       0 if ($new =~ /^\d+$/) {
134 0         0 $self->{genreID} = $new;
135             } else {
136 0         0 $self->{genreID} = genre2id($new);
137             }
138 0         0 $new = id2genre($self->{genreID});
139             }
140 2         3 $self->{$attr}=$new;
141 2         4 $self->{changed} = 1;
142             }
143 6         20 return $self->{$attr};
144             }
145              
146             =pod
147              
148             =item all()
149              
150             @tagdata = $id3v1->all;
151             @tagdata = $id3v1->all($song, $artist, $album, $year, $comment, $track, $genre);
152              
153             Returns all information of the tag in a list.
154             You can use this sub also to set the data of the complete tag.
155              
156             The order of the data is always song, artist, album, year, comment, track, and genre.
157             genre has to be a string with the name of the genre, or a number identifying the genre.
158              
159             =cut
160              
161             sub all {
162 0     0 1 0 my $self=shift;
163 0 0       0 if ($#_ == 6) {
164 0         0 my $new;
165 0         0 for (qw/song artist album year comment track genre/) {
166 0         0 $new = shift;
167 0         0 $new =~ s/ *$//;
168 0         0 $new = substr $new, 0, $ok_length{$_};
169 0         0 $self->{$_}=$new;
170             }
171 0 0       0 if ($self->{genre} =~ /^\d+$/) {
172 0         0 $self->{genreID} = $self->{genre};
173             } else {
174 0         0 $self->{genreID} = genre2id($self->{genre});
175             }
176 0         0 $self->{genre} = id2genre($self->{genreID});
177 0         0 $self->{changed} = 1;
178             }
179 0         0 return ($self->{song},$self->{artist},$self->{album},
180             $self->{year},$self->{genre},
181             $self->{track}, $self->{comment});
182             }
183              
184             =pod
185              
186             =item writeTag()
187              
188             $id3v1->writeTag;
189              
190             Writes the ID3v1 tag to the file.
191              
192             =cut
193              
194             sub writeTag {
195 2     2 1 6 my $self = shift;
196 2 50 33     10 return undef unless exists $self->{song} && exists $self->{changed};
197 2         13 my $data = pack("a30a30a30a4a28xCC",$self->{song},$self->{artist},$self->{album},
198             $self->{year}, $self->{comment}, $self->{track}, $self->{genreID});
199 2         3 my $mp3obj = $self->{mp3};
200 2         2 my $mp3tag;
201 2         17 $mp3obj->close;
202 2 50       6 if ($mp3obj->open("+<")) {
203 2         6 $mp3obj->seek(-128,2);
204 2         6 $mp3obj->read(\$mp3tag, 3);
205 2 50       6 if ($mp3tag eq "TAG") {
206 2         7 $mp3obj->write($data);
207             } else {
208 0         0 $mp3obj->seek(0,2);
209 0         0 $mp3obj->write("TAG$data");
210             }
211             }
212 2         8 return 1;
213             }
214              
215             =pod
216              
217             =item removeTag()
218              
219             $id3v1->removeTag;
220              
221             Removes the ID3v1 tag from the file.
222              
223             =cut
224              
225             sub removeTag {
226 0     0 1 0 my $self = shift;
227 0         0 my $mp3obj = $self->{mp3};
228 0         0 my $mp3tag;
229 0         0 $mp3obj->seek(-128,2);
230 0         0 $mp3obj->read(\$mp3tag, 3);
231 0 0       0 if ($mp3tag eq "TAG") {
232 0         0 $mp3obj->close;
233 0 0       0 if ($mp3obj->open("+<")) {
234 0         0 $mp3obj->truncate(-128);
235 0         0 $self->all("","","","","",0,255);
236 0         0 $mp3obj->close;
237 0         0 $self->{changed} = 1;
238 0         0 return 1;
239             }
240 0         0 return -1;
241             }
242 0         0 return 0;
243             }
244              
245             =pod
246              
247             =item genres()
248              
249             @allgenres = $id3v1->genres;
250             $genreName = $id3v1->genres($genreID);
251             $genreID = $id3v1->genres($genreName);
252              
253             Returns a list of all genres, or the according name or id to
254             a given id or name.
255              
256             =cut
257              
258             sub genres {
259             # return an array with all genres, of if a parameter is given, the according genre
260 0     0 1 0 my $genre = shift;
261 0 0       0 return \@winamp_genres unless defined $genre;
262 0 0       0 return $winamp_genres[$genre] if $genre =~ /^\d+$/;
263 0         0 my $r;
264 0         0 foreach (@winamp_genres) {
265 0 0       0 if ($_ eq $genre) {
266 0         0 $r=$_;
267 0         0 last;
268             }
269             }
270 0         0 return $r;
271             }
272              
273             #################
274             ##
275             ## internal subs
276              
277             # actually read the tag data
278             sub readTag {
279 2     2 0 4 my ($self, $buffer) = @_;
280 2         5 my $mp3obj = $self->{mp3};
281 2         2 my $id3v1;
282              
283 2 50       5 if ($self->{new}) {
284 0         0 ($self->{song}, $self->{artist}, $self->{album}, $self->{year},
285             $self->{comment}, $self->{track}, $self->{genre}, $self->{genreID}) = ("","","","","",0,"",255);
286 0         0 $self->{changed} = 1;
287             } else {
288 2         21 (undef, $self->{song}, $self->{artist}, $self->{album}, $self->{year},
289             $self->{comment}, $id3v1, $self->{track}, $self->{genreID}) =
290             unpack ("a3Z30Z30Z30Z4Z28CCC", $buffer);
291              
292 2 50       7 if ($id3v1!=0) { # ID3v1 tag found: track is not valid, comment two chars longer
293 0         0 $self->{comment} .= chr($id3v1);
294 0 0       0 $self->{comment} .= chr($self->{track}) if $self->{track}!=32;
295 0         0 $self->{track} = 0;
296             };
297 2         8 $self->{genre} = id2genre($self->{genreID});
298             }
299             }
300              
301             # convert one byte id to genre name
302             sub id2genre {
303 2     2 0 3 my $id=shift;
304 2 50 33     19 return "" unless defined $id && $id<$#winamp_genres;
305 2         5 return $winamp_genres[$id];
306             }
307              
308             # convert genre name to one byte id
309             sub genre2id {
310 0     0 0   my $genre = shift;
311 0           my $i=0;
312 0           foreach (@winamp_genres) {
313 0 0         if (uc $genre eq uc $_) {
314 0           return $i;
315             }
316 0           $i++,
317             }
318 0           return 255;
319             }
320              
321             # nothing to do for destroy
322 0     0     sub DESTROY {
323             }
324              
325             1;
326              
327             ######## define all the genres
328              
329 1     1   17 BEGIN { @mp3_genres = ( 'Blues', 'Classic Rock', 'Country', 'Dance',
330             'Disco', 'Funk', 'Grunge', 'Hip-Hop', 'Jazz', 'Metal', 'New Age',
331             'Oldies', 'Other', 'Pop', 'R&B', 'Rap', 'Reggae', 'Rock', 'Techno',
332             'Industrial', 'Alternative', 'Ska', 'Death Metal', 'Pranks',
333             'Soundtrack', 'Euro-Techno', 'Ambient', 'Trip-Hop', 'Vocal',
334             'Jazz+Funk', 'Fusion', 'Trance', 'Classical', 'Instrumental', 'Acid',
335             'House', 'Game', 'Sound Clip', 'Gospel', 'Noise', 'AlternRock',
336             'Bass', 'Soul', 'Punk', 'Space', 'Meditative', 'Instrumental Pop',
337             'Instrumental Rock', 'Ethnic', 'Gothic', 'Darkwave',
338             'Techno-Industrial', 'Electronic', 'Pop-Folk', 'Eurodance', 'Dream',
339             'Southern Rock', 'Comedy', 'Cult', 'Gangsta', 'Top 40',
340             'Christian Rap', 'Pop/Funk', 'Jungle', 'Native American', 'Cabaret', 'New Wave',
341             'Psychadelic', 'Rave', 'Showtunes', 'Trailer', 'Lo-Fi', 'Tribal',
342             'Acid Punk', 'Acid Jazz', 'Polka', 'Retro', 'Musical', 'Rock & Roll',
343             'Hard Rock', );
344              
345 1         46 @winamp_genres = ( @mp3_genres, 'Folk', 'Folk-Rock',
346             'National Folk', 'Swing', 'Fast Fusion', 'Bebob', 'Latin', 'Revival',
347             'Celtic', 'Bluegrass', 'Avantgarde', 'Gothic Rock',
348             'Progressive Rock', 'Psychedelic Rock', 'Symphonic Rock',
349             'Slow Rock', 'Big Band', 'Chorus', 'Easy Listening',
350             'Acoustic', 'Humour', 'Speech', 'Chanson', 'Opera',
351             'Chamber Music', 'Sonata', 'Symphony', 'Booty Bass', 'Primus',
352             'Porn Groove', 'Satire', 'Slow Jam', 'Club', 'Tango', 'Samba',
353             'Folklore', 'Ballad', 'Power Ballad', 'Rhythmic Soul',
354             'Freestyle', 'Duet', 'Punk Rock', 'Drum Solo', 'Acapella',
355             'Euro-House', 'Dance Hall', );
356             }
357              
358             =pod
359              
360             =head1 SEE ALSO
361              
362             MP3::Tag, MP3::TAG::ID3v2
363              
364             ID3v1 standard - http://www.id3.org
365              
366             =cut