4 # http://search.cpan.org/~cnandor/MP3-Info-1.02/Info.pm
6 # MP3::Info is installed on debian using package 'libmp3-info-perl'
8 # Rockbox song database docs:
9 # http://www.rockbox.org/twiki/bin/view/Main/TagDatabase
18 if($ARGV[0] eq "--db") {
23 elsif($ARGV[0] eq "--path") {
28 elsif($ARGV[0] eq "--strip") {
46 print "songdb [--db <file>] --path <dir> [--strip <path>]\n";
47 print "given argument is not a directory!\n";
51 # return ALL directory entries in the given dir
55 opendir(DIR
, $dir) || die "can't opendir $dir: $!";
56 # my @mp3 = grep { /\.mp3$/ && -f "$dir/$_" } readdir(DIR);
57 my @all = readdir(DIR
);
63 my ($dir, @files) = @_;
66 if( /\.mp3$/ && -f
"$dir/$_" ) {
74 my ($dir, @files) = @_;
77 if( -d
"$dir/$_" && ($_ !~ /^\
.(|\
.)$/)) {
87 # print "Check $file\n";
89 my $hash = get_mp3tag
($file);
90 # my $hash = get_mp3info($file);
93 # print "Info: $_ ".$hash->{$_}."\n";
95 return $hash; # a hash reference
103 # getdir() returns all entries in the given dir
104 my @a = getdir
($dir);
106 # extractmp3 filters out only the mp3 files from all given entries
107 my @m = extractmp3
($dir, @a);
113 my $id3 = singlefile
("$dir/$f");
123 #printf "Artist: %s\n", $id3->{'ARTIST'};
124 $entries{"$dir/$f"}= $id3;
125 $filename{$id3}="$dir/$f";
126 $artists{$id3->{'ARTIST'}}++ if($id3->{'ARTIST'});
127 $genres{$id3->{'GENRE'}}++ if($id3->{'GENRE'});
128 $years{$id3->{'YEAR'}}++ if($id3->{'YEAR'});
130 $id3->{'FILE'}="$dir/$f"; # store file name
132 $$id3{'ARTIST'} = "<no artist tag>" if ($$id3{'ARTIST'} eq "");
133 $$id3{'ALBUM'} = "<no album tag>" if ($$id3{'ALBUM'} eq "");
134 $$id3{'TITLE'} = "<no title tag>" if ($$id3{'TITLE'} eq "");
136 # prepend Artist name to handle duplicate album names from other
138 my $albumid = $id3->{'ALBUM'}."___".$id3->{'ARTIST'};
139 if($id3->{'ALBUM'}) {
140 my $num = ++$albums{$albumid};
141 if($num > $maxsongperalbum) {
142 $maxsongperalbum = $num;
144 $album2songs{$albumid}{$$id3{TITLE
}} = $id3;
145 $artist2albums{$$id3{ARTIST
}}{$$id3{ALBUM
}} = $id3;
149 # extractdirs filters out only subdirectories from all given entries
150 my @d = extractdirs
($dir, @a);
153 #print "Subdir: $d\n";
161 print "File name table\n";
163 for(sort keys %entries) {
170 print "\nSong title table\n";
171 #for(sort {$entries{$a}->{'TITLE'} cmp $entries{$b}->{'TITLE'}} %entries) {
172 for(sort {$entries{$a}->{'TITLE'} cmp $entries{$b}->{'TITLE'}} keys %entries) {
173 printf(" %s\n", $entries{$_}->{'TITLE'} );
174 my $l = length($entries{$_}->{'TITLE'});
175 if($l > $maxsonglen) {
179 $maxsonglen++; # include zero termination byte
180 while($maxsonglen&3) {
185 print "\nArtist table\n";
188 for(sort keys %artists) {
190 $artistcount{$_}=$i++;
192 if($l > $maxartistlen) {
196 $l = scalar keys %{$artist2albums{$_}};
197 if ($l > $maxalbumsperartist) {
198 $maxalbumsperartist = $l;
201 $maxartistlen++; # include zero termination byte
202 while($maxartistlen&3) {
206 print "\nGenre table\n";
207 for(sort keys %genres) {
211 print "\nYear table\n";
212 for(sort keys %years) {
216 print "\nAlbum table\n";
220 for(sort keys %albums) {
221 my @moo=split(/___/, $_);
222 printf(" %s\n", $moo[0]);
223 $albumcount{$_} = $i++;
224 my $l = length($moo[0]);
225 if($l > $maxalbumlen) {
229 $maxalbumlen++; # include zero termination byte
230 while($maxalbumlen&3) {
239 # print STDERR "int: $num\n";
241 printf DB
("%c%c%c%c",
249 print STDERR
"\nCreating db $db\n";
251 my $songentrysize = $maxsonglen + 12;
252 my $albumentrysize = $maxalbumlen + 4 + $maxsongperalbum*4;
253 my $artistentrysize = $maxartistlen + $maxalbumsperartist*4;
255 print STDERR
"Max song length: $maxsonglen\n";
256 print STDERR
"Max album length: $maxalbumlen\n";
257 print STDERR
"Max artist length: $maxartistlen\n";
258 print STDERR
"Database version: $dbver\n";
260 open(DB
, ">$db") || die "couldn't make $db";
261 printf DB
"RDB%c", $dbver;
263 $pathindex = 48; # paths always start at index 48
265 $songindex = $pathindex + $fc; # fc is size of all paths
266 $songindex++ while ($songindex & 3); # align to 32 bits
268 dumpint
($songindex); # file position index of song table
269 dumpint
(scalar(keys %entries)); # number of songs
270 dumpint
($maxsonglen); # length of song name field
272 # set total size of song title table
273 $sc = scalar(keys %entries) * $songentrysize;
275 $albumindex = $songindex + $sc; # sc is size of all songs
276 dumpint
($albumindex); # file position index of album table
277 dumpint
(scalar(keys %albums)); # number of albums
278 dumpint
($maxalbumlen); # length of album name field
279 dumpint
($maxsongperalbum); # number of entries in songs-per-album array
281 my $ac = scalar(keys %albums) * $albumentrysize;
283 $artistindex = $albumindex + $ac; # ac is size of all albums
284 dumpint
($artistindex); # file position index of artist table
285 dumpint
(scalar(keys %artists)); # number of artists
286 dumpint
($maxartistlen); # length of artist name field
287 dumpint
($maxalbumsperartist); # number of entries in albums-per-artist array
291 #### TABLE of file names ###
295 for $f (sort keys %entries) {
296 printf DB
("%s\x00", $f);
297 $filenamepos{$f}= $l;
305 #### TABLE of songs ###
307 # pointer to artist of song1
308 # pointer to album of song1
309 # pointer to filename of song1
311 my $offset = $songindex;
312 for(sort {$entries{$a}->{'TITLE'} cmp $entries{$b}->{'TITLE'}} keys %entries) {
314 my $id3 = $entries{$f};
315 my $t = $id3->{'TITLE'};
316 my $str = $t."\x00" x
($maxsonglen- length($t));
318 print DB
$str; # title
320 my $a = $artistcount{$id3->{'ARTIST'}} * $artistentrysize;
321 dumpint
($a + $artistindex); # pointer to artist of this song
323 $a = $albumcount{"$$id3{ALBUM}___$$id3{ARTIST}"} * $albumentrysize;
324 dumpint
($a + $albumindex); # pointer to album of this song
326 # pointer to filename of this song
327 dumpint
($filenamepos{$f} + $pathindex);
329 $$id3{'songoffset'} = $offset;
330 $offset += $songentrysize;
333 #### TABLE of albums ###
335 # pointers to artists of album1
336 # pointers to songs on album1
338 for(sort keys %albums) {
340 my @moo=split(/___/, $_);
342 my $str = $t."\x00" x
($maxalbumlen - length($t));
345 my $aoffset = $artistcount{$moo[0]} * $artistentrysize;
346 dumpint
($aoffset + $artistindex); # pointer to artist of this album
348 my @songlist = keys %{$album2songs{$albumid}};
349 my $id3 = $album2songs{$albumid}{$songlist[0]};
350 if (defined $id3->{'TRACKNUM'}) {
352 $album2songs{$albumid}{$a}->{'TRACKNUM'} <=>
353 $album2songs{$albumid}{$b}->{'TRACKNUM'}
357 @songlist = sort @songlist;
361 my $id3 = $album2songs{$albumid}{$_};
362 dumpint
($$id3{'songoffset'});
365 for (scalar keys %{$album2songs{$albumid}} .. $maxsongperalbum-1) {
366 print DB
"\x00\x00\x00\x00";
370 #### TABLE of artists ###
372 # pointers to albums of artist1
374 for (sort keys %artists) {
376 my $str = $_."\x00" x
($maxartistlen - length($_));
379 for (sort keys %{$artist2albums{$artist}}) {
380 my $id3 = $artist2albums{$artist}{$_};
381 my $a = $albumcount{"$$id3{'ALBUM'}___$$id3{'ARTIST'}"} * $albumentrysize;
382 dumpint
($a + $albumindex);
385 for (scalar keys %{$artist2albums{$artist}} .. $maxalbumsperartist-1) {
386 print DB
"\x00\x00\x00\x00";