Index: lib/Audio/FLAC/Header.pm =================================================================== --- lib/Audio/FLAC/Header.pm (revision 12053) +++ lib/Audio/FLAC/Header.pm (working copy) @@ -3,28 +3,39 @@ # $Id$ use strict; -use vars qw($VERSION $HAVE_XS); use File::Basename; -$VERSION = '1.4'; +our $VERSION = '1.6'; +our $HAVE_XS = 0; # First four bytes of stream are always fLaC -use constant FLACHEADERFLAG => 'fLaC'; -use constant ID3HEADERFLAG => 'ID3'; +my $FLACHEADERFLAG = 'fLaC'; +my $ID3HEADERFLAG = 'ID3'; # Masks for METADATA_BLOCK_HEADER -use constant LASTBLOCKFLAG => 0x80000000; -use constant BLOCKTYPEFLAG => 0x7F000000; -use constant BLOCKLENFLAG => 0x00FFFFFF; +my $LASTBLOCKFLAG = 0x80000000; +my $BLOCKTYPEFLAG = 0x7F000000; +my $BLOCKLENFLAG = 0x00FFFFFF; # Enumerated Block Types -use constant BT_STREAMINFO => 0; -use constant BT_PADDING => 1; -use constant BT_APPLICATION => 2; -use constant BT_SEEKTABLE => 3; -use constant BT_VORBIS_COMMENT => 4; -use constant BT_CUESHEET => 5; +my $BT_STREAMINFO = 0; +my $BT_PADDING = 1; +my $BT_APPLICATION = 2; +my $BT_SEEKTABLE = 3; +my $BT_VORBIS_COMMENT = 4; +my $BT_CUESHEET = 5; +my $BT_PICTURE = 6; +my %BLOCK_TYPES = ( + $BT_STREAMINFO => '_parseStreamInfo', + $BT_APPLICATION => '_parseAppBlock', +# The seektable isn't actually useful yet, and is a big performance hit. +# $BT_SEEKTABLE => '_parseSeekTable', + $BT_VORBIS_COMMENT => '_parseVorbisComments', + $BT_CUESHEET => '_parseCueSheet', + $BT_PICTURE => '_parsePicture', +); + XS_BOOT: { # If I inherit DynaLoader then I inherit AutoLoader require DynaLoader; @@ -37,105 +48,63 @@ do {__PACKAGE__->can('bootstrap') || \&DynaLoader::bootstrap}->(__PACKAGE__, $VERSION); return 1; - }; # Try to use the faster code first. - *new = $HAVE_XS ? \&new_XS : \&new_PP; + if ($HAVE_XS) { + *new = \&new_XS; + *write = \&write_XS; + } else { + *new = \&new_PP; + *write = \&write_PP; + } } sub new_PP { - my $class = shift; - my $file = shift; - my $writeHack = shift; - my $errflag = 0; + my ($class, $file) = @_; - my $self = {}; + # open up the file + open(my $fh, $file) or die "[$file] does not exist or cannot be read: $!"; - bless $self, $class; + # make sure dos-type systems can handle it... + binmode($fh); - # open up the file - open(FILE, $file) or do { - warn "[$file] does not exist or cannot be read: $!"; - return undef; + my $self = { + 'fileSize' => -s $file, + 'filename' => $file, }; - # make sure dos-type systems can handle it... - binmode FILE; + bless $self, $class; - $self->{'fileSize'} = -s $file; - $self->{'filename'} = $file; - $self->{'fileHandle'} = \*FILE; + # check the header to make sure this is actually a FLAC file + my $byteCount = $self->_checkHeader($fh) || 0; - # Initialize FLAC analysis - $errflag = $self->_init(); - if ($errflag < 0) { - warn "[$file] does not appear to be a FLAC file!"; - close FILE; - undef $self->{'fileHandle'}; - return undef; - }; + if ($byteCount <= 0) { - # Grab the metadata blocks from the FLAC file - $errflag = $self->_getMetadataBlocks(); - if ($errflag < 0) { - warn "[$file] Unable to read metadata from FLAC!"; - close FILE; - undef $self->{'fileHandle'}; - return undef; - }; + close($fh); + die "[$file] does not appear to be a FLAC file!"; + } - # This is because we don't write out tags in XS yet. - unless ($writeHack) { + $self->{'startMetadataBlocks'} = $byteCount; - # Parse streaminfo - $errflag = $self->_parseStreaminfo(); - if ($errflag < 0) { - warn "[$file] Can't find streaminfo metadata block!"; - close FILE; - undef $self->{'fileHandle'}; - return undef; - }; + # Grab the metadata blocks from the FLAC file + if (!$self->_getMetadataBlocks($fh)) { - # Parse vorbis tags - $errflag = $self->_parseVorbisComments(); - if ($errflag < 0) { - warn "[$file] Can't find/parse vorbis comment metadata block!"; - close FILE; - undef $self->{'fileHandle'}; - return undef; - }; + close($fh); + die "[$file] Unable to read metadata from FLAC!"; + }; - # Parse cuesheet - $errflag = $self->_parseCueSheet(); - if ($errflag < 0) { - warn "[$file] Problem parsing cuesheet metadata block!"; - close FILE; - undef $self->{'fileHandle'}; - return undef; - }; + # Always set to empty hash in the case of no comments. + $self->{'tags'} = {}; - # Parse seekpoint table - $errflag = $self->_parseSeekTable(); - if ($errflag < 0) { - warn "[$file] Problem parsing seekpoint table!"; - close FILE; - undef $self->{'fileHandle'}; - return undef; - }; + for my $block (@{$self->{'metadataBlocks'}}) { - # Parse third-party application metadata block - $errflag = $self->_parseAppBlock(); - if ($errflag < 0) { - warn "[$file] Problem parsing application metadata block!"; - close FILE; - undef $self->{'fileHandle'}; - return undef; - }; + my $method = $BLOCK_TYPES{ $block->{'blockType'} } || next; + + $self->$method($block); } - close FILE; - undef $self->{'fileHandle'}; + close($fh); return $self; } @@ -193,34 +162,32 @@ return undef; } -sub write { +sub picture { my $self = shift; + my $type = shift || 3; # front cover - # XXX - this is a hack until I do metadata writing in XS - # Very ugly, I know. - if ($HAVE_XS) { + # if the picture block exists, return it's content + return $self->{'picture'}->{$type} if exists($self->{'picture'}->{$type}); - # Make a copy of these - otherwise we'll refcnt++ - my %tags = %{$self->{'tags'}}; - my %info = %{$self->{'info'}}; + # otherwise, return nothing + return undef; +} - my $filename = $self->{'filename'}; - my $class = ref($self); +sub vendor_string { + my $self = shift; - undef $self; + return $self->{'vendor'} || "Audio::FLAC::Header $VERSION"; +} - $self = $class->new_PP($filename, 1); +sub write_PP { + my $self = shift; - $self->{'tags'} = \%tags; - $self->{'info'} = \%info; - } - my @tagString = (); my $numTags = 0; my ($idxVorbis,$idxPadding); my $totalAvail = 0; - my $metadataBlocks = FLACHEADERFLAG; + my $metadataBlocks = $FLACHEADERFLAG; my $tmpnum; # Make a list of the tags and lengths for packing into the vorbis metadata block @@ -248,8 +215,8 @@ # Is there enough space for this new header? # Determine the length of the old comment block and the length of the padding available - $idxVorbis = $self->_findMetadataIndex(BT_VORBIS_COMMENT); - $idxPadding = $self->_findMetadataIndex(BT_PADDING); + $idxVorbis = $self->_findMetadataIndex($BT_VORBIS_COMMENT); + $idxPadding = $self->_findMetadataIndex($BT_PADDING); if ($idxVorbis >= 0) { # Add the length of the block @@ -271,7 +238,7 @@ # re-writing entire file (not within scope) if ($totalAvail - length($vorbisComment) < 0) { warn "Unable to write Vorbis tags - not enough header space!"; - return -1; + return 0; } # Modify the metadata blocks to reflect new header sizes @@ -279,7 +246,7 @@ # Is there a Vorbis metadata block? if ($idxVorbis < 0) { # no vorbis block, so add one - _addNewMetadataBlock($self, BT_VORBIS_COMMENT, $vorbisComment); + _addNewMetadataBlock($self, $BT_VORBIS_COMMENT, $vorbisComment); } else { # update the vorbis block _updateMetadataBlock($self, $idxVorbis , $vorbisComment); @@ -289,7 +256,7 @@ # Change the padding to reflect the new vorbis comment size if ($idxPadding < 0) { # no padding block - _addNewMetadataBlock($self, BT_PADDING , "\0" x ($totalAvail - length($vorbisComment))); + _addNewMetadataBlock($self, $BT_PADDING , "\0" x ($totalAvail - length($vorbisComment))); } else { # update the padding block _updateMetadataBlock($self, $idxPadding, "\0" x ($totalAvail - length($vorbisComment))); @@ -305,54 +272,30 @@ } # open FLAC file and write new metadata blocks - open FLACFILE, "+<$self->{'filename'}" or return -1; + open FLACFILE, "+<$self->{'filename'}" or return 0; binmode FLACFILE; # overwrite the existing metadata blocks - print FLACFILE $metadataBlocks or return -1; + print FLACFILE $metadataBlocks or return 0; close FLACFILE; - return 0; + return 1; } # private methods to this class -sub _init { - my $self = shift; - - my $fh = $self->{'fileHandle'}; - - # check the header to make sure this is actually a FLAC file - my $byteCount = $self->_checkHeader() || 0; - - unless ($byteCount > 0) { - # if it's not, we can't do anything - return -1; - } - - $self->{'startMetadataBlocks'} = $byteCount; - - return 0; -} - sub _checkHeader { - my $self = shift; + my ($self, $fh) = @_; - my $fh = $self->{'fileHandle'}; - my $id3size = ''; - - # stores how far into the file we've read, - # so later reads into the file can skip right - # past all of the header stuff - my $byteCount = 0; - # check that the first four bytes are 'fLaC' read($fh, my $buffer, 4) or return -1; - if (substr($buffer,0,3) eq ID3HEADERFLAG) { + if (substr($buffer,0,3) eq $ID3HEADERFLAG) { $self->{'ID3V2Tag'} = 1; + my $id3size = ''; + # How big is the ID3 header? # Skip the next two bytes - major & minor version number. read($fh, $buffer, 2) or return -1; @@ -361,7 +304,7 @@ # Read the next 4 bytes one at a time, unpack each one B7, # and concatenate. When complete, do a bin2dec to determine size for (my $c = 0; $c < 4; $c++) { - read ($fh, $buffer, 1) or return -1; + read($fh, $buffer, 1) or return -1; $id3size .= substr(unpack ("B8", $buffer), 1); } @@ -369,22 +312,18 @@ read($fh, $buffer, 4) or return -1; } - if ($buffer ne FLACHEADERFLAG) { + if ($buffer ne $FLACHEADERFLAG) { warn "Unable to identify $self->{'filename'} as a FLAC bitstream!\n"; return -2; } - $byteCount = tell($fh); - # at this point, we assume the bitstream is valid - return $byteCount; + return tell($fh); } sub _getMetadataBlocks { - my $self = shift; + my ($self, $fh) = @_; - my $fh = $self->{'fileHandle'}; - my $metadataBlockList = []; my $numBlocks = 0; my $lastBlockFlag = 0; @@ -394,20 +333,20 @@ while ($lastBlockFlag == 0) { # Read the next metadata_block_header - read $fh, $buffer, 4 or return -1; + read($fh, $buffer, 4) or return 0; - my $metadataBlockHeader = unpack ('N', $buffer); + my $metadataBlockHeader = unpack('N', $buffer); # Break out the contents of the metadata_block_header - my $metadataBlockType = (BLOCKTYPEFLAG & $metadataBlockHeader)>>24; - my $metadataBlockLength = (BLOCKLENFLAG & $metadataBlockHeader); - $lastBlockFlag = (LASTBLOCKFLAG & $metadataBlockHeader)>>31; + my $metadataBlockType = ($BLOCKTYPEFLAG & $metadataBlockHeader)>>24; + my $metadataBlockLength = ($BLOCKLENFLAG & $metadataBlockHeader); + $lastBlockFlag = ($LASTBLOCKFLAG & $metadataBlockHeader)>>31; # If the block size is zero go to the next block next unless $metadataBlockLength; # Read the contents of the metadata_block - read $fh, my $metadataBlockData, $metadataBlockLength or return -1; + read($fh, my $metadataBlockData, $metadataBlockLength) or return 0; # Store the parts in the list $metadataBlockList->[$numBlocks++] = { @@ -422,44 +361,43 @@ $self->{'metadataBlocks'} = $metadataBlockList; $self->{'startAudioData'} = tell $fh; - return 0; + return 1; } -sub _parseStreaminfo { - my $self = shift; +sub _parseStreamInfo { + my ($self, $block) = @_; + my $info = {}; - my ($totalSeconds,$trackMinutes,$trackSeconds,$trackFrames,$bitRate); - my $idx = $self->_findMetadataIndex(BT_STREAMINFO); - - if ($idx < 0) { - return -1; - } - # Convert to binary string, since there's some unfriendly lengths ahead - my $metaBinString = unpack('B144', $self->{'metadataBlocks'}[$idx]->{'contents'}); + my $metaBinString = unpack('B144', $block->{'contents'}); - $info->{'MINIMUMBLOCKSIZE'} = _bin2dec(substr($metaBinString, 0,16)); - $info->{'MAXIMUMBLOCKSIZE'} = _bin2dec(substr($metaBinString,16,32)); - $info->{'MINIMUMFRAMESIZE'} = _bin2dec(substr($metaBinString,32,24)); - $info->{'MAXIMUMFRAMESIZE'} = _bin2dec(substr($metaBinString,56,24)); + my $x32 = 0 x 32; - $info->{'SAMPLERATE'} = _bin2dec(substr($metaBinString,80,20)); - $info->{'NUMCHANNELS'} = _bin2dec(substr($metaBinString,100,3)) + 1; - $info->{'BITSPERSAMPLE'} = _bin2dec(substr($metaBinString,103,5)) + 1; + $info->{'MINIMUMBLOCKSIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 0, 16), -32))); + $info->{'MAXIMUMBLOCKSIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 16, 32), -32))); + $info->{'MINIMUMFRAMESIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 32, 24), -32))); + $info->{'MINIMUMFRAMESIZE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 56, 24), -32))); + $info->{'SAMPLERATE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 80, 20), -32))); + $info->{'NUMCHANNELS'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 100, 3), -32))) + 1; + $info->{'BITSPERSAMPLE'} = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 100, 5), -32))) + 1; + # Calculate total samples in two parts - my $highBits = _bin2dec(substr($metaBinString,108,4)); - $info->{'TOTALSAMPLES'} = $highBits * 2 ** 32 + _bin2dec(substr($metaBinString,112,32)); + my $highBits = unpack('N', pack('B32', substr($x32 . substr($metaBinString, 108, 4), -32))); + $info->{'TOTALSAMPLES'} = $highBits * 2 ** 32 + + unpack('N', pack('B32', substr($x32 . substr($metaBinString, 112, 32), -32))); + # Return the MD5 as a 32-character hexadecimal string - $info->{'MD5CHECKSUM'} = unpack('H32',substr($self->{'metadataBlocks'}[$idx]->{'contents'},18,16)); + #$info->{'MD5CHECKSUM'} = unpack('H32',substr($self->{'metadataBlocks'}[$idx]->{'contents'},18,16)); + $info->{'MD5CHECKSUM'} = unpack('H32',substr($block->{'contents'}, 18, 16)); # Store in the data hash $self->{'info'} = $info; # Calculate the track times - $totalSeconds = $info->{'TOTALSAMPLES'} / $info->{'SAMPLERATE'}; + my $totalSeconds = $info->{'TOTALSAMPLES'} / $info->{'SAMPLERATE'}; if ($totalSeconds == 0) { warn "totalSeconds is 0 - we couldn't find either TOTALSAMPLES or SAMPLERATE!\n" . @@ -475,41 +413,40 @@ $self->{'trackLengthFrames'} = ($totalSeconds - int($totalSeconds)) * 75; $self->{'bitRate'} = 8 * ($self->{'fileSize'} - $self->{'startAudioData'}) / $totalSeconds; - return 0; + return 1; } sub _parseVorbisComments { - my $self = shift; - my $tags = {}; + my ($self, $block) = @_; + + my $tags = {}; my $rawTags = []; - my $idx = $self->_findMetadataIndex(BT_VORBIS_COMMENT); - # continue parsing, even if we can't find the comment. - return 0 if $idx < 0; - # Parse out the tags from the metadata block - my $tmpBlock = $self->{'metadataBlocks'}[$idx]->{'contents'}; + my $tmpBlock = $block->{'contents'}; + my $offset = 0; # First tag in block is the Vendor String - my $tagLen = _grabInt32(\$tmpBlock); - $tags->{'VENDOR'} = substr($tmpBlock,0,$tagLen); - $tmpBlock = substr($tmpBlock,$tagLen); + my $tagLen = unpack('V', substr($tmpBlock, $offset, 4)); + $tags->{'VENDOR'} = substr($tmpBlock, ($offset += 4), $tagLen); # Now, how many additional tags are there? - my $numTags = _grabInt32(\$tmpBlock); + my $numTags = unpack('V', substr($tmpBlock, ($offset += $tagLen), 4)); + $offset += 4; + for (my $tagi = 0; $tagi < $numTags; $tagi++) { # Read the tag string - $tagLen = _grabInt32(\$tmpBlock); - my $tagStr = substr($tmpBlock,0,$tagLen); - $tmpBlock = substr($tmpBlock,$tagLen); + my $tagLen = unpack('V', substr($tmpBlock, $offset, 4)); + my $tagStr = substr($tmpBlock, ($offset += 4), $tagLen); # Save the raw tag push(@$rawTags, $tagStr); # Match the key and value if ($tagStr =~ /^(.*?)=(.*?)[\r\n]*$/s) { + # Make the key uppercase my $tkey = $1; $tkey =~ tr/a-z/A-Z/; @@ -531,31 +468,27 @@ $tags->{$tkey} = $2; } } + + $offset += $tagLen; } $self->{'tags'} = $tags; $self->{'rawTags'} = $rawTags; - return 0; + return 1; } sub _parseCueSheet { - my $self = shift; + my ($self, $block) = @_; - my $idx = $self->_findMetadataIndex(BT_CUESHEET); - - # No cuesheet block found. - # Not really an error, but no need to continue. - return 0 if $idx < 0; - my $cuesheet = []; # Parse out the tags from the metadata block - my $tmpBlock = $self->{'metadataBlocks'}[$idx]->{'contents'}; + my $tmpBlock = $block->{'contents'}; # First field in block is the Media Catalog Number my $catalog = substr($tmpBlock,0,128); - $catalog =~ s/\x00*.*$//gs; # trim nulls off of the end + $catalog =~ s/\x00+.*$//gs; # trim nulls off of the end push (@$cuesheet, "CATALOG $catalog\n") if length($catalog) > 0; $tmpBlock = substr($tmpBlock,128); @@ -640,7 +573,7 @@ $seenTracknumber{$tracknum} = 1; my $isrc = substr($tmpBlock,9,12); - $isrc =~ s/\x00*.*$//; + $isrc =~ s/\x00+.*$//; if ((length($isrc) != 0) && (length($isrc) != 12)) { warn "Invalid ISRC code $isrc\n"; @@ -732,27 +665,53 @@ $self->{'cuesheet'} = $cuesheet; - return 0; + return 1; } +sub _parsePicture { + my ($self, $block) = @_; + + # Parse out the tags from the metadata block + my $tmpBlock = $block->{'contents'}; + my $offset = 0; + + my $pictureType = unpack('N', substr($tmpBlock, $offset, 4)); + my $mimeLength = unpack('N', substr($tmpBlock, ($offset += 4), 4)); + my $mimeType = substr($tmpBlock, ($offset += 4), $mimeLength); + my $descLength = unpack('N', substr($tmpBlock, ($offset += $mimeLength), 4)); + my $description = substr($tmpBlock, ($offset += 4), $descLength); + my $width = unpack('N', substr($tmpBlock, ($offset += $descLength), 4)); + my $height = unpack('N', substr($tmpBlock, ($offset += 4), 4)); + my $depth = unpack('N', substr($tmpBlock, ($offset += 4), 4)); + my $colorIndex = unpack('N', substr($tmpBlock, ($offset += 4), 4)); + my $imageLength = unpack('N', substr($tmpBlock, ($offset += 4), 4)); + my $imageData = substr($tmpBlock, ($offset += 4), $imageLength); + + $self->{'picture'}->{$pictureType}->{'mimeType'} = $mimeType; + $self->{'picture'}->{$pictureType}->{'description'} = $description; + $self->{'picture'}->{$pictureType}->{'width'} = $width; + $self->{'picture'}->{$pictureType}->{'height'} = $height; + $self->{'picture'}->{$pictureType}->{'depth'} = $depth; + $self->{'picture'}->{$pictureType}->{'colorIndex'} = $colorIndex; + $self->{'picture'}->{$pictureType}->{'imageData'} = $imageData; + + return 1; +} + sub _parseSeekTable { - my $self = shift; + my ($self, $block) = @_; + my $seektable = []; - my $idx = $self->_findMetadataIndex(BT_SEEKTABLE); + # grab the seekpoint table + my $tmpBlock = $block->{'contents'}; + my $offset = 0; - # seekpoint tables are optional, so return 0 if we don't have one - if ($idx < 0) { - return 0; - } + # parse out the seekpoints + while (my $seekpoint = substr($tmpBlock, $offset, 18)) { - #grab the seekpoint table - my $tmpBlock = $self->{'metadataBlocks'}[$idx]->{'contents'}; - - #parse out the seekpoints - while (my $seekpoint = substr($tmpBlock, 0, 18)) { # Sample number of first sample in the target frame - my $highbits = unpack('N', substr($seekpoint,0,4)); + my $highbits = unpack('N', substr($seekpoint,0,4)); my $sampleNumber = $highbits * 2 ** 32 + unpack('N', (substr($seekpoint,4,4))); # Detect placeholder seekpoint @@ -767,40 +726,30 @@ # Number of samples in the target frame my $frameSamples = unpack('n', (substr($seekpoint,16,2))); - # remove this point from the tmpBlock - $tmpBlock = substr($tmpBlock, 18); - # add this point to our copy of the table - push (@$seektable, { "sampleNumber" => $sampleNumber, - "streamOffset" => $streamOffset, - "frameSamples" => $frameSamples }); + push (@$seektable, { + 'sampleNumber' => $sampleNumber, + 'streamOffset' => $streamOffset, + 'frameSamples' => $frameSamples, + }); + + $offset += 18; } - # make it official $self->{'seektable'} = $seektable; - return 0; + return 1; } sub _parseAppBlock { - my $self = shift; + my ($self, $block) = @_; - # there may be multiple application blocks with different ids - # so we need to loop through them all. - my $idx = $self->_findMetadataIndex(BT_APPLICATION); - while ($idx >= 0) { - my $appContent = []; + # Parse out the tags from the metadata block + my $appID = unpack('N', substr($block->{'contents'}, 0, 4, '')); - # Parse out the tags from the metadata block - my $tmpBlock = $self->{'metadataBlocks'}[$idx]->{'contents'}; + $self->{'application'}->{$appID} = $block->{'contents'}; - # Find the application id - my $appID = unpack('N', substr($tmpBlock,0,4)); - - $self->{'application'}->{$appID} = substr($tmpBlock,4); - $idx = $self->_findMetadataIndex(BT_APPLICATION, ++$idx); - } - return 0; + return 1; } # Take an offset as number of flac samples @@ -838,17 +787,9 @@ return unpack ('N', pack ('B32', substr(0 x 32 . shift, -32))); } -sub _grabInt32 { - # Pulls a little-endian unsigned int from a string and returns the remainder - my $data = shift; - my $value = unpack('V',substr($$data,0,4)); - $$data = substr($$data,4); - return $value; -} - sub _packInt32 { # Packs an integer into a little-endian 32-bit unsigned int - return pack('V',shift) + return pack('V', shift) } sub _findMetadataIndex { @@ -860,6 +801,7 @@ # Loop through the metadata_blocks until one of $htype is found while ($idx < @{$self->{'metadataBlocks'}}) { + # Check the type to see if it's a $htype block if ($self->{'metadataBlocks'}[$idx]->{'blockType'} == $htype) { $found++; @@ -1026,9 +968,9 @@ =head1 COPYRIGHT -Pure perl code Copyright (c) 2003-2005, Erik Reckase. +Pure perl code Copyright (c) 2003-2004, Erik Reckase. -XS code Copyright (c) 2004-2005, Dan Sully. +Pure perl code Copyright (c) 2003-2007, Dan Sully & Logitech. XS code Copyright (c) 2004-2007, Dan Sully & Logitech. Index: Slim/Formats/FLAC.pm =================================================================== --- Slim/Formats/FLAC.pm (revision 12053) +++ Slim/Formats/FLAC.pm (working copy) @@ -282,7 +282,13 @@ delete $tags->{'COVERART'}; - } elsif (my $artwork = $flac->application($ESCIENT_ARTWORK)) { + } elsif ($flac->picture) { + + $tags->{'ARTWORK'} = $flac->picture->{'imageData'}; + + } elsif ($flac->application($ESCIENT_ARTWORK)) { + + my $artwork = $flac->application($ESCIENT_ARTWORK); if (substr($artwork, 0, 4, '') eq 'PIC1') { $tags->{'ARTWORK'} = $artwork;