diff options
Diffstat (limited to 'tools/songdb.pl')
-rwxr-xr-x | tools/songdb.pl | 317 |
1 files changed, 95 insertions, 222 deletions
diff --git a/tools/songdb.pl b/tools/songdb.pl index 202ab06401..589267b632 100755 --- a/tools/songdb.pl +++ b/tools/songdb.pl @@ -84,7 +84,7 @@ sub get_oggtag { my $ogg = vorbiscomm->new($fn); - $ogg->load; + my $h= $ogg->load; # Convert this format into the same format used by the id3 parser hash @@ -100,7 +100,6 @@ sub get_oggtag { $n = 'ALBUM'; } $hash{$n}=$cmmt if($n); - # print $k, '=', $cmmt, "\n"; } } @@ -147,6 +146,88 @@ sub extractdirs { return @dirs; } +# CRC32 32KB of data (use less if there isn't 32KB available) + +sub crc32 { + my ($filename, $index) = @_; + + my $len = 32*1024; + + if(!open(FILE, "<$filename")) { + print "failed to open \"$filename\" $!\n"; + return -2; + } + + # read $data from index $index to $buffer from the file, may return fewer + # bytes when dealing with a very small file. + # + # TODO: make sure we don't include a trailer with metadata when doing this. + # Like a id3v1 tag. + my $nread = sysread FILE, $buffer, $len, $index; + + close(FILE); + + my @crc_table = + ( # CRC32 lookup table for polynomial 0x04C11DB7 + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, + 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, 0x48D0C6C7, + 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, + 0x709F7B7A, 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, 0xBAEA46EF, + 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, + 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, + 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, + 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, + 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, + 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, + 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, 0x4BC510E1, + 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, + 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, 0xF5EE4BB9, + 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, + 0xCDA1F604, 0xC960EBB3, 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, + 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, + 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, + 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, + 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, + 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, + 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + ); + + my $crc = 0xffffffff; + for ($i = 0; $i < $nread; $i++) { + # get the numeric for the byte of the $i index + $buf = ord(substr($buffer, $i, 1)); + + $crc = ($crc << 8) ^ $crc_table[(($crc >> 24) ^ $buf) & 0xFF]; + + # printf("%08x\n", $crc); + } + + return $crc; + +} + sub singlefile { my ($file) = @_; my $hash; @@ -156,6 +237,10 @@ sub singlefile { } else { $hash = get_mp3tag($file); + + my $info = get_mp3info($file); + + $hash->{FILECRC} = crc32($file, $info->{headersize}); } return $hash; # a hash reference @@ -626,9 +711,10 @@ if ($db) { my $str = $f."\x00" x ($maxfilelen- length($f)); my $id3 = $entries{$f}; print DB $str; - dumpint(0); # TODO: add hashing; 0 for now. - dumpint($id3->{'songoffset'}); - dumpint(-1); + #print STDERR "CRC: ".."\n"; + dumpint($id3->{'FILECRC'}); # CRC32 of the song data + dumpint($id3->{'songoffset'}); # offset to song data + dumpint(-1); # what's this for? } close(DB); @@ -837,223 +923,6 @@ sub use_winamp_genres { =pod -=item remove_mp3tag (FILE [, VERSION, BUFFER]) - -Can remove ID3v1 or ID3v2 tags. VERSION should be C<1> for ID3v1, -C<2> for ID3v2, and C<ALL> for both. - -For ID3v1, removes last 128 bytes from file if those last 128 bytes begin -with the text 'TAG'. File will be 128 bytes shorter. - -For ID3v2, removes ID3v2 tag. Because an ID3v2 tag is at the -beginning of the file, we rewrite the file after removing the tag data. -The buffer for rewriting the file is 4MB. BUFFER (in bytes) ca -change the buffer size. - -Returns the number of bytes removed, or -1 if no tag removed, -or undef if there is an error. - -=cut - -sub remove_mp3tag { - my($file, $version, $buf) = @_; - my($fh, $return); - - $buf ||= 4096*1024; # the bigger the faster - $version ||= 1; - - if (not (defined $file && $file ne '')) { - $@ = "No file specified"; - return undef; - } - - if (not -s $file) { - $@ = "File is empty"; - return undef; - } - - if (ref $file) { # filehandle passed - $fh = $file; - } else { - $fh = gensym; - if (not open $fh, "+< $file\0") { - $@ = "Can't open $file: $!"; - return undef; - } - } - - binmode $fh; - - if ($version eq 1 || $version eq 'ALL') { - seek $fh, -128, 2; - my $tell = tell $fh; - if (<$fh> =~ /^TAG/) { - truncate $fh, $tell or warn "Can't truncate '$file': $!"; - $return += 128; - } - } - - if ($version eq 2 || $version eq 'ALL') { - my $h = _get_v2head($fh); - if ($h) { - local $\; - seek $fh, 0, 2; - my $eof = tell $fh; - my $off = $h->{tag_size}; - - while ($off < $eof) { - seek $fh, $off, 0; - read $fh, my($bytes), $buf; - seek $fh, $off - $h->{tag_size}, 0; - print $fh $bytes; - $off += $buf; - } - - truncate $fh, $eof - $h->{tag_size} - or warn "Can't truncate '$file': $!"; - $return += $h->{tag_size}; - } - } - - _close($file, $fh); - - return $return || -1; -} - - -=pod - -=item set_mp3tag (FILE, TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE [, TRACKNUM]) - -=item set_mp3tag (FILE, $HASHREF) - -Adds/changes tag information in an MP3 audio file. Will clobber -any existing information in file. - -Fields are TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE. All fields have -a 30-byte limit, except for YEAR, which has a four-byte limit, and GENRE, -which is one byte in the file. The GENRE passed in the function is a -case-insensitive text string representing a genre found in C<@mp3_genres>. - -Will accept either a list of values, or a hashref of the type -returned by C<get_mp3tag>. - -If TRACKNUM is present (for ID3v1.1), then the COMMENT field can only be -28 bytes. - -ID3v2 support may come eventually. Note that if you set a tag on a file -with ID3v2, the set tag will be for ID3v1[.1] only, and if you call -C<get_mp3_tag> on the file, it will show you the (unchanged) ID3v2 tags, -unless you specify ID3v1. - -=cut - -sub set_mp3tag { - my($file, $title, $artist, $album, $year, $comment, $genre, $tracknum) = @_; - my(%info, $oldfh, $ref, $fh); - local %v1_tag_fields = %v1_tag_fields; - - # set each to '' if undef - for ($title, $artist, $album, $year, $comment, $tracknum, $genre, - (@info{@v1_tag_names})) - {$_ = defined() ? $_ : ''} - - ($ref) = (overload::StrVal($title) =~ /^(?:.*\=)?([^=]*)\((?:[^\(]*)\)$/) - if ref $title; - # populate data to hashref if hashref is not passed - if (!$ref) { - (@info{@v1_tag_names}) = - ($title, $artist, $album, $year, $comment, $tracknum, $genre); - - # put data from hashref into hashref if hashref is passed - } elsif ($ref eq 'HASH') { - %info = %$title; - - # return otherwise - } else { - warn(<<'EOT'); -Usage: set_mp3tag (FILE, TITLE, ARTIST, ALBUM, YEAR, COMMENT, GENRE [, TRACKNUM]) - set_mp3tag (FILE, $HASHREF) -EOT - return undef; - } - - if (not (defined $file && $file ne '')) { - $@ = "No file specified"; - return undef; - } - - if (not -s $file) { - $@ = "File is empty"; - return undef; - } - - # comment field length 28 if ID3v1.1 - $v1_tag_fields{COMMENT} = 28 if $info{TRACKNUM}; - - - # only if -w is on - if ($^W) { - # warn if fields too long - foreach my $field (keys %v1_tag_fields) { - $info{$field} = '' unless defined $info{$field}; - if (length($info{$field}) > $v1_tag_fields{$field}) { - warn "Data too long for field $field: truncated to " . - "$v1_tag_fields{$field}"; - } - } - - if ($info{GENRE}) { - warn "Genre `$info{GENRE}' does not exist\n" - unless exists $mp3_genres{$info{GENRE}}; - } - } - - if ($info{TRACKNUM}) { - $info{TRACKNUM} =~ s/^(\d+)\/(\d+)$/$1/; - unless ($info{TRACKNUM} =~ /^\d+$/ && - $info{TRACKNUM} > 0 && $info{TRACKNUM} < 256) { - warn "Tracknum `$info{TRACKNUM}' must be an integer " . - "from 1 and 255\n" if $^W; - $info{TRACKNUM} = ''; - } - } - - if (ref $file) { # filehandle passed - $fh = $file; - } else { - $fh = gensym; - if (not open $fh, "+< $file\0") { - $@ = "Can't open $file: $!"; - return undef; - } - } - - binmode $fh; - $oldfh = select $fh; - seek $fh, -128, 2; - # go to end of file if no tag, beginning of file if tag - seek $fh, (<$fh> =~ /^TAG/ ? -128 : 0), 2; - - # get genre value - $info{GENRE} = $info{GENRE} && exists $mp3_genres{$info{GENRE}} ? - $mp3_genres{$info{GENRE}} : 255; # some default genre - - local $\; - # print TAG to file - if ($info{TRACKNUM}) { - print pack "a3a30a30a30a4a28xCC", 'TAG', @info{@v1_tag_names}; - } else { - print pack "a3a30a30a30a4a30C", 'TAG', @info{@v1_tag_names[0..4, 6]}; - } - - select $oldfh; - - _close($file, $fh); - - return 1; -} - =pod =item get_mp3tag (FILE [, VERSION, RAW_V2]) @@ -1402,6 +1271,8 @@ sub get_mp3info { my $vbr = _get_vbr($fh, $h, \$off); + $h->{headersize}=$off; # data size prepending the actual mp3 data + seek $fh, 0, 2; $eof = tell $fh; seek $fh, -128, 2; @@ -1459,6 +1330,8 @@ sub _get_info { $i->{FRAME_LENGTH} = int($h->{size} / $i->{FRAMES}) if $i->{FRAMES}; $i->{FREQUENCY} = $frequency_tbl[3 * $h->{IDR} + $h->{sampling_freq}]; + $i->{headersize} = $h->{headersize}; + return $i; } |