Commit 717342ed authored by Daniel Faust's avatar Daniel Faust
Browse files

Support embedded covers for ogg (read only) and flac files

BUG:142579
parent 8d8404a9
......@@ -4,6 +4,7 @@ Amarok ChangeLog
Version 2.6-Beta 1
FEATURES:
* Support embedded covers for ogg (read only) and flac files. (BR 288752)
* Track meta-data can be edited in USB Mass Storage collection.
* Support for album artists in USB Mass Storage collection.
* album artist (if differs from artist), BPM, labels, last played time and
......
......@@ -276,7 +276,7 @@ Meta::Tag::selectHelper( const TagLib::FileRef fileref, bool forceCreation )
else if( TagLib::FLAC::File *file = dynamic_cast< TagLib::FLAC::File * >( fileref.file() ) )
{
if( file->xiphComment() )
tagHelper = new VorbisCommentTagHelper( fileref.tag(), file->xiphComment(), Amarok::Flac );
tagHelper = new VorbisCommentTagHelper( fileref.tag(), file->xiphComment(), file, Amarok::Flac );
else if( file->ID3v2Tag() )
tagHelper = new ID3v2TagHelper( fileref.tag(), file->ID3v2Tag(), Amarok::Flac );
else if( file->ID3v1Tag() )
......
......@@ -25,11 +25,42 @@
#include "StringHelper.h"
#include <taglib/flacpicture.h>
#ifndef UTILITIES_BUILD
#include <QBuffer>
#endif //UTILITIES_BUILD
// Taglib added support for FLAC pictures in 1.7.0
#if (TAGLIB_MAJOR_VERSION > 1) || (TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 7)
# define TAGLIB_HAS_FLAC_PICTURELIST
#endif
using namespace Meta::Tag;
VorbisCommentTagHelper::VorbisCommentTagHelper( TagLib::Tag *tag, TagLib::Ogg::XiphComment *commentsTag, Amarok::FileType fileType )
: TagHelper( tag, fileType )
, m_tag( commentsTag )
, m_flacFile( 0 )
{
m_fieldMap.insert( Meta::valAlbumArtist, TagLib::String( "ALBUMARTIST" ) );
m_fieldMap.insert( Meta::valBpm, TagLib::String( "BPM" ) );
m_fieldMap.insert( Meta::valCompilation, TagLib::String( "COMPILATION" ) );
m_fieldMap.insert( Meta::valComposer, TagLib::String( "COMPOSER" ) );
m_fieldMap.insert( Meta::valDiscNr, TagLib::String( "DISCNUMBER" ) );
m_fieldMap.insert( Meta::valHasCover, TagLib::String( "COVERART" ) ); // non-standard but frequently used
m_fieldMap.insert( Meta::valPlaycount, TagLib::String( "FMPS_PLAYCOUNT" ) );
m_fieldMap.insert( Meta::valRating, TagLib::String( "FMPS_RATING" ) );
m_fieldMap.insert( Meta::valScore, TagLib::String( "FMPS_RATING_AMAROK_SCORE" ) );
m_uidFieldMap.insert( UIDAFT, TagLib::String( "AMAROK 2 AFTV1 - AMAROK.KDE.ORG" ) );
m_uidFieldMap.insert( UIDMusicBrainz, TagLib::String( "MUSICBRAINZ_TRACKID" ) );
}
VorbisCommentTagHelper::VorbisCommentTagHelper( TagLib::Tag *tag, TagLib::Ogg::XiphComment *commentsTag, TagLib::FLAC::File *file, Amarok::FileType fileType )
: TagHelper( tag, fileType )
, m_tag( commentsTag )
, m_flacFile( file )
{
m_fieldMap.insert( Meta::valAlbumArtist, TagLib::String( "ALBUMARTIST" ) );
m_fieldMap.insert( Meta::valBpm, TagLib::String( "BPM" ) );
......@@ -67,6 +98,8 @@ VorbisCommentTagHelper::tags() const
data.insert( field, qRound( value.toFloat() * 10.0 ) );
else if( field == Meta::valScore )
data.insert( field, value.toFloat() * 100.0 );
else if( field == Meta::valHasCover )
data.insert( Meta::valHasCover, true );
else
data.insert( field, value );
}
......@@ -79,6 +112,25 @@ VorbisCommentTagHelper::tags() const
}
}
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
if( m_flacFile )
{
const TagLib::List<TagLib::FLAC::Picture*> picturelist = m_flacFile->pictureList();
for( TagLib::List<TagLib::FLAC::Picture*>::ConstIterator it = picturelist.begin(); it != picturelist.end(); it++ )
{
TagLib::FLAC::Picture* picture = *it;
if( ( picture->type() == TagLib::FLAC::Picture::FrontCover ||
picture->type() == TagLib::FLAC::Picture::Other ) &&
picture->data().size() > 1024 ) // must be at least 1kb
{
data.insert( Meta::valHasCover, true );
break;
}
}
}
#endif // TAGLIB_HAS_FLAC_PICTURELIST
return data;
}
......@@ -94,7 +146,9 @@ VorbisCommentTagHelper::setTags( const Meta::FieldHash &changes )
if( !field.isNull() && !field.isEmpty() )
{
if( key == Meta::valRating )
if( key == Meta::valHasCover )
continue;
else if( key == Meta::valRating )
m_tag->addField( field, Qt4QStringToTString( QString::number( value.toFloat() / 10.0 ) ) );
else if( key == Meta::valScore )
m_tag->addField( field, Qt4QStringToTString( QString::number( value.toFloat() / 100.0 ) ) );
......@@ -122,3 +176,156 @@ VorbisCommentTagHelper::render() const
{
return m_tag->render();
}
#ifndef UTILITIES_BUILD
bool
VorbisCommentTagHelper::hasEmbeddedCover() const
{
if( m_flacFile )
{
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
const TagLib::List<TagLib::FLAC::Picture*> picturelist = m_flacFile->pictureList();
for( TagLib::List<TagLib::FLAC::Picture*>::ConstIterator it = picturelist.begin(); it != picturelist.end(); it++ )
{
const TagLib::FLAC::Picture *picture = *it;
if( ( picture->type() == TagLib::FLAC::Picture::FrontCover ||
picture->type() == TagLib::FLAC::Picture::Other ) &&
picture->data().size() > 1024 ) // must be at least 1kb
{
return true;
}
}
#endif // TAGLIB_HAS_FLAC_PICTURELIST
}
else if( !fieldName( Meta::valHasCover ).isEmpty() )
{
TagLib::ByteVector field = fieldName( Meta::valHasCover ).toCString();
return m_tag->fieldListMap().contains( field );
}
return false;
}
QImage
VorbisCommentTagHelper::embeddedCover() const
{
QImage cover;
if( m_flacFile )
{
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
const TagLib::List<TagLib::FLAC::Picture*> picturelist = m_flacFile->pictureList();
for( TagLib::List<TagLib::FLAC::Picture*>::ConstIterator it = picturelist.begin(); it != picturelist.end(); it++ )
{
const TagLib::FLAC::Picture *picture = *it;
if( ( picture->type() == TagLib::FLAC::Picture::FrontCover ||
picture->type() == TagLib::FLAC::Picture::Other ) &&
picture->data().size() > 1024 ) // must be at least 1kb
{
QByteArray image_data( picture->data().data(), picture->data().size() );
cover.loadFromData( image_data );
}
}
#endif // TAGLIB_HAS_FLAC_PICTURELIST
}
else if( !fieldName( Meta::valHasCover ).isEmpty() )
{
TagLib::ByteVector field = fieldName( Meta::valHasCover ).toCString();
if( !m_tag->fieldListMap().contains( field ) )
return cover;
TagLib::StringList coverList = m_tag->fieldListMap()[field];
for( uint i=0; i<coverList.size(); i++ )
{
QByteArray image_data_b64( coverList[i].toCString() );
QByteArray image_data = QByteArray::fromBase64(image_data_b64);
if( image_data.size() <= 1024 ) // must be at least 1kb
continue;
bool success = false;
success = cover.loadFromData( image_data );
if( !success )
success = cover.loadFromData( image_data_b64 );
if( success )
break;
}
}
return cover;
}
bool
VorbisCommentTagHelper::setEmbeddedCover( const QImage &cover )
{
// NOTE since the COVERART tag is not standardized and a proper way has been defined recently [1],
// we should wait until taglib provides an implementation.
// [1] http://wiki.xiph.org/index.php/VorbisComment#Cover_art
#ifdef TAGLIB_HAS_FLAC_PICTURELIST
if( m_flacFile )
{
QByteArray bytes;
QBuffer buffer( &bytes );
buffer.open( QIODevice::WriteOnly );
if( !cover.save( &buffer, "JPEG" ) )
{
buffer.close();
return false;
}
buffer.close();
// back up covers
TagLib::List<TagLib::FLAC::Picture*> backedUpPictures;
const TagLib::List<TagLib::FLAC::Picture*> picturelist = m_flacFile->pictureList();
for( TagLib::List<TagLib::FLAC::Picture*>::ConstIterator it = picturelist.begin(); it != picturelist.end(); it++ )
{
const TagLib::FLAC::Picture *picture = *it;
TagLib::FLAC::Picture *backedUpPicture = new TagLib::FLAC::Picture();
backedUpPicture->setData( picture->data() );
backedUpPicture->setMimeType( picture->mimeType() );
backedUpPicture->setType( picture->type() );
backedUpPicture->setDescription( picture->description() );
backedUpPictures.append( backedUpPicture );
}
// remove all covers
m_flacFile->removePictures();
// add new cover
TagLib::FLAC::Picture *newPicture = new TagLib::FLAC::Picture();
newPicture->setData( TagLib::ByteVector( bytes.data(), bytes.size() ) );
newPicture->setMimeType( "image/jpeg" );
newPicture->setType( TagLib::FLAC::Picture::FrontCover );
m_flacFile->addPicture( newPicture );
// re-add all the backed-up covers except the front cover
for( TagLib::List<TagLib::FLAC::Picture*>::Iterator it = backedUpPictures.begin(); it != backedUpPictures.end(); it++ )
{
TagLib::FLAC::Picture *picture = *it;
if( picture->type() != TagLib::FLAC::Picture::FrontCover )
{
m_flacFile->addPicture( picture );
}
}
return true;
}
#endif // TAGLIB_HAS_FLAC_PICTURELIST
return false;
}
#endif //UTILITIES_BUILD
......@@ -21,6 +21,7 @@
#include "TagHelper.h"
#include <taglib/xiphcomment.h>
#include <taglib/flacfile.h>
namespace Meta
{
......@@ -30,14 +31,22 @@ namespace Meta
{
public:
VorbisCommentTagHelper( TagLib::Tag *tag, TagLib::Ogg::XiphComment *commentsTag, Amarok::FileType fileType );
VorbisCommentTagHelper( TagLib::Tag *tag, TagLib::Ogg::XiphComment *commentsTag, TagLib::FLAC::File *file, Amarok::FileType fileType );
virtual Meta::FieldHash tags() const;
virtual bool setTags( const Meta::FieldHash &changes );
virtual TagLib::ByteVector render() const;
#ifndef UTILITIES_BUILD
virtual bool hasEmbeddedCover() const;
virtual QImage embeddedCover() const;
virtual bool setEmbeddedCover( const QImage &cover );
#endif //UTILITIES_BUILD
private:
TagLib::Ogg::XiphComment *m_tag;
TagLib::FLAC::File *m_flacFile;
};
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment