3 # Rockbox song database docs:
4 # http://www.rockbox.org/twiki/bin/view/Main/DataBase
10 # configuration settings
19 my $dbver = 0x54434806;
25 if($ARGV[0] eq "--path") {
30 elsif($ARGV[0] eq "--db") {
35 elsif($ARGV[0] eq "--strip") {
40 elsif($ARGV[0] eq "--add") {
45 elsif($ARGV[0] eq "--dirisalbum") {
49 elsif($ARGV[0] eq "--littleendian") {
53 elsif($ARGV[0] eq "--verbose") {
57 elsif($ARGV[0] eq "--help" or ($ARGV[0] eq "-h")) {
66 if(! -d
$dir or $help) {
67 print "'$dir' is not a directory\n" if ($dir ne "" and ! -d
$dir);
70 songdb --path <dir> [--db <file>] [--strip <path>] [--add <path>] [--dirisalbum] [--littleendian] [--verbose] [--help]
74 --path <dir> Where your music collection is found
75 --db <file> Prefix for output files. Defaults to database.
76 --strip <path> Removes this string from the left of all file names
77 --add <path> Adds this string to the left of all file names
78 --dirisalbum Use dir name as album name if the album name is missing in the
80 --littleendian Write out data as little endian (for x86 simulators and ARM-
81 based targets such as iPods and iriver H10)
82 --verbose Shows more details while working
93 my $ogg = vorbiscomm
->new($fn);
97 # Convert this format into the same format used by the id3 parser hash
99 foreach my $k ($ogg->comment_tags())
101 foreach my $cmmt ($ogg->comment($k))
104 if($k =~ /^artist$/i) {
107 elsif($k =~ /^album$/i) {
110 elsif($k =~ /^title$/i) {
113 $hash{$n}=$cmmt if($n);
124 my $ogg = vorbiscomm
->new($fn);
128 return $ogg->{'INFO'};
131 # return ALL directory entries in the given dir
135 $dir =~ s
|/$|| if ($dir ne "/");
137 if (opendir(DIR, $dir)) {
138 my @all = readdir(DIR);
143 warn "can
't opendir $dir: $!\n";
148 my ($dir, @files) = @_;
151 if( (/\.mp[23]$/i || /\.ogg$/i) && -f "$dir/$_" ) {
159 my ($dir, @files) = @_;
163 if( -d "$dir/$_" && ($_ !~ /^\.(|\.)$/)) {
175 if($file =~ /\.ogg$/i) {
176 $hash = get_oggtag($file);
177 $info = get_ogginfo($file);
180 $hash = get_mp3tag($file);
181 $info = get_mp3info($file);
182 if (defined $$info{'BITRATE
'}) {
183 $$hash{'BITRATE
'} = $$info{'BITRATE
'};
186 if (defined $$info{'SECS
'}) {
187 $$hash{'SECS
'} = $$info{'SECS
'};
202 # getdir() returns all entries in the given dir
203 my @a = getdir($dir);
205 # extractmp3 filters out only the mp3 files from all given entries
206 my @m = extractmp3($dir, @a);
212 my $id3 = singlefile("$dir/$f");
214 if (not defined $$id3{'ARTIST
'} or $$id3{'ARTIST
'} eq "") {
215 $$id3{'ARTIST
'} = "<Untagged>";
218 # Only use one case-variation of each artist
219 if (exists($lcartists{lc($$id3{'ARTIST
'})})) {
220 $$id3{'ARTIST
'} = $lcartists{lc($$id3{'ARTIST
'})};
223 $lcartists{lc($$id3{'ARTIST
'})} = $$id3{'ARTIST
'};
225 #printf "Artist: %s\n", $$id3{'ARTIST
'};
227 if (not defined $$id3{'ALBUM
'} or $$id3{'ALBUM
'} eq "") {
228 $$id3{'ALBUM
'} = "<Untagged>";
230 $$id3{'ALBUM
'} = $dir;
234 # Only use one case-variation of each album
235 if (exists($lcalbums{lc($$id3{'ALBUM
'})})) {
236 $$id3{'ALBUM
'} = $lcalbums{lc($$id3{'ALBUM
'})};
239 $lcalbums{lc($$id3{'ALBUM
'})} = $$id3{'ALBUM
'};
241 #printf "Album: %s\n", $$id3{'ALBUM
'};
243 if (not defined $$id3{'GENRE
'} or $$id3{'GENRE
'} eq "") {
244 $$id3{'GENRE
'} = "<Untagged>";
246 #printf "Genre: %s\n", $$id3{'GENRE
'};
248 if (not defined $$id3{'TITLE
'} or $$id3{'TITLE
'} eq "") {
249 # fall back on basename of the file if no title tag.
250 ($$id3{'TITLE
'} = $f) =~ s/\.\w+$//;
252 #printf "Title: %s\n", $$id3{'TITLE
'};
254 my $path = "$dir/$f";
255 if ($strip ne "" and $path =~ /^$strip(.*)/) {
260 $path = $add . $path;
262 #printf "Path: %s\n", $path;
264 if (not defined $$id3{'COMPOSER
'} or $$id3{'COMPOSER
'} eq "") {
265 $$id3{'COMPOSER
'} = "<Untagged>";
267 #printf "Composer: %s\n", $$id3{'COMPOSER
'};
269 if (not defined $$id3{'YEAR
'} or $$id3{'YEAR
'} eq "") {
270 $$id3{'YEAR
'} = "-1";
272 #printf "Year: %s\n", $$id3{'YEAR
'};
274 if (not defined $$id3{'TRACKNUM
'} or $$id3{'TRACKNUM
'} eq "") {
275 $$id3{'TRACKNUM
'} = "-1";
277 #printf "Track num: %s\n", $$id3{'TRACKNUM
'};
279 if (not defined $$id3{'BITRATE
'} or $$id3{'BITRATE
'} eq "") {
280 $$id3{'BITRATE
'} = "-1";
282 #printf "Bitrate: %s\n", $$id3{'BITRATE
'};
284 if (not defined $$id3{'SECS
'} or $$id3{'SECS
'} eq "") {
285 $$id3{'SECS
'} = "-1";
287 #printf "Length: %s\n", $$id3{'SECS
'};
289 $$id3{'PATH
'} = $path;
290 $entries{$path} = $id3;
293 # extractdirs filters out only subdirectories from all given entries
294 my @d = extractdirs($dir, @a);
309 # print "int: $num\n";
312 print DB pack "v", $num;
315 print DB pack "n", $num;
322 # print "int: $num\n";
325 print DB pack "V", $num;
328 print DB pack "N", $num;
332 sub dump_tag_string {
333 my ($s, $index) = @_;
335 my $strlen = length($s)+1;
336 my $padding = $strlen%4;
338 $padding = 4 - $padding;
346 for (my $i = 0; $i < $padding; $i++) {
351 sub dump_tag_header {
352 my ($entry_count) = @_;
354 my $size = tell(DB) - 12;
359 dumpint($entry_count);
364 open(DB, "> $f") || die "couldn't
open $f";
368 sub create_tagcache_index_file {
369 my ($index, $key, $unique) = @_;
375 openfile $db ."_
".$index.".tcd
";
378 for(sort {uc($entries{$a}->{$key}) cmp uc($entries{$b}->{$key})} keys %entries) {
379 if (!$unique || !($entries{$_}->{$key} eq $prev)) {
383 $prev = $entries{$_}->{$key};
385 printf(" %s\n", $prev) if ($verbose);
391 $index = $entries{$_}->{'INDEX'};
393 dump_tag_string($prev, $index);
395 $entries{$_}->{$key."_OFFSET
"} = $offset;
398 dump_tag_header($num);
402 if (!scalar keys %entries) {
403 print "No songs found
. Did you specify the right
--path ?
\n";
404 print "Use the
--help parameter to see all options
.\n";
409 for (sort keys %entries) {
410 $entries{$_}->{'INDEX'} = $i;
415 # tagcache index files
416 create_tagcache_index_file(0, 'ARTIST', 1);
417 create_tagcache_index_file(1, 'ALBUM', 1);
418 create_tagcache_index_file(2, 'GENRE', 1);
419 create_tagcache_index_file(3, 'TITLE', 0);
420 create_tagcache_index_file(4, 'PATH', 0);
421 create_tagcache_index_file(5, 'COMPOSER', 1);
424 openfile $db ."_idx
.tcd
";
430 for (sort keys %entries) {
431 dumpint($entries{$_}->{'ARTIST_OFFSET'});
432 dumpint($entries{$_}->{'ALBUM_OFFSET'});
433 dumpint($entries{$_}->{'GENRE_OFFSET'});
434 dumpint($entries{$_}->{'TITLE_OFFSET'});
435 dumpint($entries{$_}->{'PATH_OFFSET'});
436 dumpint($entries{$_}->{'COMPOSER_OFFSET'});
437 dumpint($entries{$_}->{'YEAR'});
438 dumpint($entries{$_}->{'TRACKNUM'});
439 dumpint($entries{$_}->{'BITRATE'});
440 dumpint($entries{$_}->{'SECS'});
451 dump_tag_header(scalar keys %entries);