2004-10-13 13:39:15 +00:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
# Very sparse docs:
|
|
|
|
# http://search.cpan.org/~cnandor/MP3-Info-1.02/Info.pm
|
|
|
|
|
|
|
|
# MP3::Info is installed on debian using package 'libmp3-info-perl'
|
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
# Rockbox song database docs:
|
|
|
|
# http://www.rockbox.org/twiki/bin/view/Main/TagDatabase
|
|
|
|
|
2004-10-13 13:39:15 +00:00
|
|
|
use MP3::Info;
|
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
my $db;
|
|
|
|
my $dir;
|
|
|
|
my $strip;
|
2004-10-13 13:39:15 +00:00
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
while($ARGV[0]) {
|
|
|
|
if($ARGV[0] eq "--db") {
|
|
|
|
$db = $ARGV[1];
|
|
|
|
shift @ARGV;
|
|
|
|
shift @ARGV;
|
|
|
|
}
|
|
|
|
elsif($ARGV[0] eq "--path") {
|
|
|
|
$dir = $ARGV[1];
|
|
|
|
shift @ARGV;
|
|
|
|
shift @ARGV;
|
|
|
|
}
|
|
|
|
elsif($ARGV[0] eq "--strip") {
|
|
|
|
$strip = $ARGV[1];
|
|
|
|
shift @ARGV;
|
|
|
|
shift @ARGV;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
shift @ARGV;
|
|
|
|
}
|
|
|
|
}
|
2004-10-18 08:08:21 +00:00
|
|
|
my %entries;
|
|
|
|
my %genres;
|
|
|
|
my %albums;
|
|
|
|
my %years;
|
2004-10-20 21:40:04 +00:00
|
|
|
my %filename;
|
|
|
|
|
|
|
|
my $dbver = 1;
|
2004-10-18 08:08:21 +00:00
|
|
|
|
2004-10-13 13:39:15 +00:00
|
|
|
if(! -d $dir) {
|
2004-10-20 21:40:04 +00:00
|
|
|
print "songdb [--db <file>] --path <dir> [--strip <path>]\n";
|
2004-10-13 13:39:15 +00:00
|
|
|
print "given argument is not a directory!\n";
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
# return ALL directory entries in the given dir
|
|
|
|
sub getdir {
|
|
|
|
my ($dir) = @_;
|
|
|
|
|
|
|
|
opendir(DIR, $dir) || die "can't opendir $dir: $!";
|
|
|
|
# my @mp3 = grep { /\.mp3$/ && -f "$dir/$_" } readdir(DIR);
|
|
|
|
my @all = readdir(DIR);
|
|
|
|
closedir DIR;
|
|
|
|
return @all;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub extractmp3 {
|
|
|
|
my ($dir, @files) = @_;
|
|
|
|
my @mp3;
|
|
|
|
for(@files) {
|
|
|
|
if( /\.mp3$/ && -f "$dir/$_" ) {
|
|
|
|
push @mp3, $_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return @mp3;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub extractdirs {
|
|
|
|
my ($dir, @files) = @_;
|
|
|
|
my @dirs;
|
|
|
|
for(@files) {
|
|
|
|
if( -d "$dir/$_" && ($_ !~ /^\.(|\.)$/)) {
|
|
|
|
push @dirs, $_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return @dirs;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub singlefile {
|
|
|
|
my ($file) = @_;
|
|
|
|
|
|
|
|
# print "Check $file\n";
|
|
|
|
|
|
|
|
my $hash = get_mp3tag($file);
|
|
|
|
# my $hash = get_mp3info($file);
|
|
|
|
|
|
|
|
# for(keys %$hash) {
|
|
|
|
# print "Info: $_ ".$hash->{$_}."\n";
|
|
|
|
# }
|
|
|
|
return $hash; # a hash reference
|
|
|
|
}
|
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
my $maxsongperalbum;
|
|
|
|
|
2004-10-13 13:39:15 +00:00
|
|
|
sub dodir {
|
|
|
|
my ($dir)=@_;
|
|
|
|
|
|
|
|
# getdir() returns all entries in the given dir
|
|
|
|
my @a = getdir($dir);
|
|
|
|
|
|
|
|
# extractmp3 filters out only the mp3 files from all given entries
|
|
|
|
my @m = extractmp3($dir, @a);
|
|
|
|
|
|
|
|
my $f;
|
|
|
|
|
|
|
|
for $f (sort @m) {
|
|
|
|
|
|
|
|
my $id3 = singlefile("$dir/$f");
|
|
|
|
|
2004-10-18 08:08:21 +00:00
|
|
|
# ARTIST
|
|
|
|
# COMMENT
|
|
|
|
# ALBUM
|
|
|
|
# TITLE
|
|
|
|
# GENRE
|
|
|
|
# TRACKNUM
|
|
|
|
# YEAR
|
|
|
|
|
|
|
|
#printf "Artist: %s\n", $id3->{'ARTIST'};
|
|
|
|
$entries{"$dir/$f"}= $id3;
|
2004-10-20 21:40:04 +00:00
|
|
|
$filename{$id3}="$dir/$f";
|
2004-10-18 08:08:21 +00:00
|
|
|
$artists{$id3->{'ARTIST'}}++ if($id3->{'ARTIST'});
|
|
|
|
$genres{$id3->{'GENRE'}}++ if($id3->{'GENRE'});
|
|
|
|
$years{$id3->{'YEAR'}}++ if($id3->{'YEAR'});
|
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
$id3->{'FILE'}="$dir/$f"; # store file name
|
|
|
|
|
2004-10-26 14:26:37 +00:00
|
|
|
$$id3{'ARTIST'} = "<no artist tag>" if ($$id3{'ARTIST'} eq "");
|
|
|
|
$$id3{'ALBUM'} = "<no album tag>" if ($$id3{'ALBUM'} eq "");
|
|
|
|
$$id3{'TITLE'} = "<no title tag>" if ($$id3{'TITLE'} eq "");
|
|
|
|
|
2004-10-18 08:08:21 +00:00
|
|
|
# prepend Artist name to handle duplicate album names from other
|
|
|
|
# artists
|
2004-10-21 21:58:15 +00:00
|
|
|
my $albumid = $id3->{'ALBUM'}."___".$id3->{'ARTIST'};
|
2004-10-20 21:40:04 +00:00
|
|
|
if($id3->{'ALBUM'}) {
|
2004-10-21 21:58:15 +00:00
|
|
|
my $num = ++$albums{$albumid};
|
2004-10-20 21:40:04 +00:00
|
|
|
if($num > $maxsongperalbum) {
|
|
|
|
$maxsongperalbum = $num;
|
|
|
|
}
|
2004-10-21 21:58:15 +00:00
|
|
|
$album2songs{$albumid}{$$id3{TITLE}} = $id3;
|
|
|
|
$artist2albums{$$id3{ARTIST}}{$$id3{ALBUM}} = $id3;
|
2004-10-20 21:40:04 +00:00
|
|
|
}
|
2004-10-13 13:39:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# extractdirs filters out only subdirectories from all given entries
|
|
|
|
my @d = extractdirs($dir, @a);
|
|
|
|
|
|
|
|
for $d (sort @d) {
|
2004-10-18 08:08:21 +00:00
|
|
|
#print "Subdir: $d\n";
|
2004-10-13 13:39:15 +00:00
|
|
|
dodir("$dir/$d");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dodir($dir);
|
2004-10-18 08:08:21 +00:00
|
|
|
|
|
|
|
print "File name table\n";
|
2004-10-20 21:40:04 +00:00
|
|
|
my $fc;
|
2004-10-18 08:08:21 +00:00
|
|
|
for(sort keys %entries) {
|
|
|
|
printf(" %s\n", $_);
|
2004-10-20 21:40:04 +00:00
|
|
|
$fc += length($_)+1;
|
2004-10-18 08:08:21 +00:00
|
|
|
}
|
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
my $maxsonglen;
|
2004-10-21 13:54:30 +00:00
|
|
|
my $sc;
|
2004-10-18 08:08:21 +00:00
|
|
|
print "\nSong title table\n";
|
|
|
|
#for(sort {$entries{$a}->{'TITLE'} cmp $entries{$b}->{'TITLE'}} %entries) {
|
|
|
|
for(sort {$entries{$a}->{'TITLE'} cmp $entries{$b}->{'TITLE'}} keys %entries) {
|
|
|
|
printf(" %s\n", $entries{$_}->{'TITLE'} );
|
2004-10-20 21:40:04 +00:00
|
|
|
my $l = length($entries{$_}->{'TITLE'});
|
|
|
|
if($l > $maxsonglen) {
|
|
|
|
$maxsonglen = $l;
|
|
|
|
}
|
|
|
|
}
|
2004-10-21 21:58:15 +00:00
|
|
|
$maxsonglen++; # include zero termination byte
|
2004-10-20 21:40:04 +00:00
|
|
|
while($maxsonglen&3) {
|
|
|
|
$maxsonglen++;
|
2004-10-18 08:08:21 +00:00
|
|
|
}
|
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
my $maxartistlen;
|
2004-10-18 08:08:21 +00:00
|
|
|
print "\nArtist table\n";
|
2004-10-20 21:40:04 +00:00
|
|
|
my $i=0;
|
|
|
|
my %artistcount;
|
2004-10-18 08:08:21 +00:00
|
|
|
for(sort keys %artists) {
|
|
|
|
printf(" %s\n", $_);
|
2004-10-20 21:40:04 +00:00
|
|
|
$artistcount{$_}=$i++;
|
|
|
|
my $l = length($_);
|
2004-10-21 21:58:15 +00:00
|
|
|
if($l > $maxartistlen) {
|
|
|
|
$maxartistlen = $l;
|
|
|
|
}
|
|
|
|
|
|
|
|
$l = scalar keys %{$artist2albums{$_}};
|
|
|
|
if ($l > $maxalbumsperartist) {
|
|
|
|
$maxalbumsperartist = $l;
|
2004-10-20 21:40:04 +00:00
|
|
|
}
|
|
|
|
}
|
2004-10-21 21:58:15 +00:00
|
|
|
$maxartistlen++; # include zero termination byte
|
|
|
|
while($maxartistlen&3) {
|
|
|
|
$maxartistlen++;
|
2004-10-18 08:08:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
print "\nGenre table\n";
|
|
|
|
for(sort keys %genres) {
|
|
|
|
printf(" %s\n", $_);
|
|
|
|
}
|
|
|
|
|
|
|
|
print "\nYear table\n";
|
|
|
|
for(sort keys %years) {
|
|
|
|
printf(" %s\n", $_);
|
|
|
|
}
|
|
|
|
|
|
|
|
print "\nAlbum table\n";
|
2004-10-20 21:40:04 +00:00
|
|
|
my $maxalbumlen;
|
|
|
|
my %albumcount;
|
|
|
|
$i=0;
|
2004-10-18 08:08:21 +00:00
|
|
|
for(sort keys %albums) {
|
|
|
|
my @moo=split(/___/, $_);
|
2004-10-21 21:58:15 +00:00
|
|
|
printf(" %s\n", $moo[0]);
|
|
|
|
$albumcount{$_} = $i++;
|
|
|
|
my $l = length($moo[0]);
|
2004-10-20 21:40:04 +00:00
|
|
|
if($l > $maxalbumlen) {
|
|
|
|
$maxalbumlen = $l;
|
|
|
|
}
|
|
|
|
}
|
2004-10-21 21:58:15 +00:00
|
|
|
$maxalbumlen++; # include zero termination byte
|
2004-10-20 21:40:04 +00:00
|
|
|
while($maxalbumlen&3) {
|
|
|
|
$maxalbumlen++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
sub dumpint {
|
|
|
|
my ($num)=@_;
|
|
|
|
|
|
|
|
# print STDERR "int: $num\n";
|
|
|
|
|
|
|
|
printf DB ("%c%c%c%c",
|
|
|
|
$num>>24,
|
|
|
|
($num&0xff0000)>>16,
|
|
|
|
($num&0xff00)>>8,
|
|
|
|
($num&0xff));
|
|
|
|
}
|
|
|
|
|
|
|
|
if($db) {
|
|
|
|
print STDERR "\nCreating db $db\n";
|
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
my $songentrysize = $maxsonglen + 12;
|
|
|
|
my $albumentrysize = $maxalbumlen + 4 + $maxsongperalbum*4;
|
|
|
|
my $artistentrysize = $maxartistlen + $maxalbumsperartist*4;
|
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
print STDERR "Max song length: $maxsonglen\n";
|
|
|
|
print STDERR "Max album length: $maxalbumlen\n";
|
2004-10-21 21:58:15 +00:00
|
|
|
print STDERR "Max artist length: $maxartistlen\n";
|
2004-10-20 21:40:04 +00:00
|
|
|
print STDERR "Database version: $dbver\n";
|
|
|
|
|
|
|
|
open(DB, ">$db") || die "couldn't make $db";
|
|
|
|
printf DB "RDB%c", $dbver;
|
2004-10-21 13:54:30 +00:00
|
|
|
|
|
|
|
$pathindex = 48; # paths always start at index 48
|
2004-10-20 21:40:04 +00:00
|
|
|
|
2004-10-21 13:54:30 +00:00
|
|
|
$songindex = $pathindex + $fc; # fc is size of all paths
|
2004-10-21 22:49:49 +00:00
|
|
|
$songindex++ while ($songindex & 3); # align to 32 bits
|
|
|
|
|
2004-10-21 13:54:30 +00:00
|
|
|
dumpint($songindex); # file position index of song table
|
2004-10-20 21:40:04 +00:00
|
|
|
dumpint(scalar(keys %entries)); # number of songs
|
|
|
|
dumpint($maxsonglen); # length of song name field
|
|
|
|
|
2004-10-21 13:54:30 +00:00
|
|
|
# set total size of song title table
|
2004-10-21 21:58:15 +00:00
|
|
|
$sc = scalar(keys %entries) * $songentrysize;
|
2004-10-21 13:54:30 +00:00
|
|
|
|
|
|
|
$albumindex = $songindex + $sc; # sc is size of all songs
|
|
|
|
dumpint($albumindex); # file position index of album table
|
2004-10-20 21:40:04 +00:00
|
|
|
dumpint(scalar(keys %albums)); # number of albums
|
|
|
|
dumpint($maxalbumlen); # length of album name field
|
|
|
|
dumpint($maxsongperalbum); # number of entries in songs-per-album array
|
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
my $ac = scalar(keys %albums) * $albumentrysize;
|
2004-10-20 21:40:04 +00:00
|
|
|
|
2004-10-21 13:54:30 +00:00
|
|
|
$artistindex = $albumindex + $ac; # ac is size of all albums
|
|
|
|
dumpint($artistindex); # file position index of artist table
|
2004-10-20 21:40:04 +00:00
|
|
|
dumpint(scalar(keys %artists)); # number of artists
|
2004-10-21 21:58:15 +00:00
|
|
|
dumpint($maxartistlen); # length of artist name field
|
2004-10-20 21:40:04 +00:00
|
|
|
dumpint($maxalbumsperartist); # number of entries in albums-per-artist array
|
|
|
|
|
2004-10-21 13:54:30 +00:00
|
|
|
my $l=0;
|
2004-10-20 21:40:04 +00:00
|
|
|
|
|
|
|
#### TABLE of file names ###
|
|
|
|
# path1
|
|
|
|
|
|
|
|
my %filenamepos;
|
|
|
|
for $f (sort keys %entries) {
|
|
|
|
printf DB ("%s\x00", $f);
|
|
|
|
$filenamepos{$f}= $l;
|
|
|
|
$l += length($f)+1;
|
|
|
|
}
|
2004-10-21 22:49:49 +00:00
|
|
|
while ($l & 3) {
|
|
|
|
print DB "\x00";
|
|
|
|
$l++;
|
|
|
|
}
|
2004-10-20 21:40:04 +00:00
|
|
|
|
|
|
|
#### TABLE of songs ###
|
|
|
|
# title of song1
|
|
|
|
# pointer to artist of song1
|
|
|
|
# pointer to album of song1
|
|
|
|
# pointer to filename of song1
|
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
my $offset = $songindex;
|
2004-10-20 21:40:04 +00:00
|
|
|
for(sort {$entries{$a}->{'TITLE'} cmp $entries{$b}->{'TITLE'}} keys %entries) {
|
|
|
|
my $f = $_;
|
|
|
|
my $id3 = $entries{$f};
|
|
|
|
my $t = $id3->{'TITLE'};
|
|
|
|
my $str = $t."\x00" x ($maxsonglen- length($t));
|
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
print DB $str; # title
|
|
|
|
|
|
|
|
my $a = $artistcount{$id3->{'ARTIST'}} * $artistentrysize;
|
2004-10-21 13:54:30 +00:00
|
|
|
dumpint($a + $artistindex); # pointer to artist of this song
|
2004-10-20 21:40:04 +00:00
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
$a = $albumcount{"$$id3{ALBUM}___$$id3{ARTIST}"} * $albumentrysize;
|
2004-10-21 13:54:30 +00:00
|
|
|
dumpint($a + $albumindex); # pointer to album of this song
|
2004-10-20 21:40:04 +00:00
|
|
|
|
|
|
|
# pointer to filename of this song
|
2004-10-21 22:49:49 +00:00
|
|
|
dumpint($filenamepos{$f} + $pathindex);
|
2004-10-21 21:58:15 +00:00
|
|
|
|
|
|
|
$$id3{'songoffset'} = $offset;
|
|
|
|
$offset += $songentrysize;
|
2004-10-20 21:40:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#### TABLE of albums ###
|
|
|
|
# name of album1
|
|
|
|
# pointers to artists of album1
|
|
|
|
# pointers to songs on album1
|
|
|
|
|
|
|
|
for(sort keys %albums) {
|
2004-10-21 21:58:15 +00:00
|
|
|
my $albumid = $_;
|
2004-10-20 21:40:04 +00:00
|
|
|
my @moo=split(/___/, $_);
|
2004-10-21 21:58:15 +00:00
|
|
|
my $t = $moo[0];
|
2004-10-20 21:40:04 +00:00
|
|
|
my $str = $t."\x00" x ($maxalbumlen - length($t));
|
2004-10-21 21:58:15 +00:00
|
|
|
print DB $str;
|
2004-10-20 21:40:04 +00:00
|
|
|
|
2004-10-23 14:15:00 +00:00
|
|
|
my $aoffset = $artistcount{$moo[0]} * $artistentrysize;
|
|
|
|
dumpint($aoffset + $artistindex); # pointer to artist of this album
|
|
|
|
|
|
|
|
my @songlist = keys %{$album2songs{$albumid}};
|
|
|
|
my $id3 = $album2songs{$albumid}{$songlist[0]};
|
|
|
|
if (defined $id3->{'TRACKNUM'}) {
|
|
|
|
@songlist = sort {
|
|
|
|
$album2songs{$albumid}{$a}->{'TRACKNUM'} <=>
|
|
|
|
$album2songs{$albumid}{$b}->{'TRACKNUM'}
|
|
|
|
} @songlist;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
@songlist = sort @songlist;
|
|
|
|
}
|
2004-10-21 13:54:30 +00:00
|
|
|
|
2004-10-23 14:15:00 +00:00
|
|
|
for (@songlist) {
|
2004-10-21 21:58:15 +00:00
|
|
|
my $id3 = $album2songs{$albumid}{$_};
|
|
|
|
dumpint($$id3{'songoffset'});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (scalar keys %{$album2songs{$albumid}} .. $maxsongperalbum-1) {
|
|
|
|
print DB "\x00\x00\x00\x00";
|
|
|
|
}
|
2004-10-20 21:40:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#### TABLE of artists ###
|
|
|
|
# name of artist1
|
|
|
|
# pointers to albums of artist1
|
|
|
|
|
2004-10-21 21:58:15 +00:00
|
|
|
for (sort keys %artists) {
|
|
|
|
my $artist = $_;
|
|
|
|
my $str = $_."\x00" x ($maxartistlen - length($_));
|
|
|
|
print DB $str;
|
|
|
|
|
|
|
|
for (sort keys %{$artist2albums{$artist}}) {
|
|
|
|
my $id3 = $artist2albums{$artist}{$_};
|
2004-10-21 22:49:49 +00:00
|
|
|
my $a = $albumcount{"$$id3{'ALBUM'}___$$id3{'ARTIST'}"} * $albumentrysize;
|
2004-10-21 21:58:15 +00:00
|
|
|
dumpint($a + $albumindex);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (scalar keys %{$artist2albums{$artist}} .. $maxalbumsperartist-1) {
|
|
|
|
print DB "\x00\x00\x00\x00";
|
|
|
|
}
|
|
|
|
|
2004-10-20 21:40:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
close(DB);
|
2004-10-18 08:08:21 +00:00
|
|
|
}
|