File indexing completed on 2024-04-28 04:45:52

0001 /****************************************************************************************
0002  * Copyright    (C) 2003-2005 Max Howell <max.howell@methylblue.com>                    *
0003  *              (C) 2003-2010 Mark Kretschmann <kretschmann@kde.org>                    *
0004  *              (C) 2005-2007 Alexandre Oliveira <aleprj@gmail.com>                     *
0005  *              (C) 2008 Dan Meltzer <parallelgrapefruit@gmail.com>                     *
0006  *              (C) 2008-2009 Jeff Mitchell <mitchell@kde.org>                          *
0007  *              (C) 2010 Ralf Engels <ralf-engels@gmx.de>                               *
0008  *              (c) 2010 Sergey Ivanov <123kash@gmail.com>                              *
0009  *                                                                                      *
0010  * This program is free software; you can redistribute it and/or modify it under        *
0011  * the terms of the GNU General Public License as published by the Free Software        *
0012  * Foundation; either version 2 of the License, or (at your option) any later           *
0013  * version.                                                                             *
0014  *                                                                                      *
0015  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0016  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0017  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0018  *                                                                                      *
0019  * You should have received a copy of the GNU General Public License along with         *
0020  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0021  ****************************************************************************************/
0022 
0023 
0024 #include "MP4TagHelper.h"
0025 
0026 #include "StringHelper.h"
0027 
0028 #include <QBuffer>
0029 #include <QImage>
0030 
0031 using namespace Meta::Tag;
0032 
0033 MP4TagHelper::MP4TagHelper( TagLib::Tag *tag, TagLib::MP4::Tag *mp4Tag, Amarok::FileType fileType )
0034             : TagHelper( tag, fileType )
0035             , m_tag( mp4Tag )
0036 {
0037     m_fieldMap.insert( Meta::valAlbumArtist, TagLib::String( "aART" ) );
0038     m_fieldMap.insert( Meta::valBpm,         TagLib::String( "tmpo" ) );
0039     m_fieldMap.insert( Meta::valCompilation, TagLib::String( "cpil" ) );
0040     m_fieldMap.insert( Meta::valComposer,    TagLib::String( "\xA9wrt" ) );
0041     m_fieldMap.insert( Meta::valDiscNr,      TagLib::String( "disk" ) );
0042     m_fieldMap.insert( Meta::valHasCover,    TagLib::String( "covr" ) );
0043     m_fieldMap.insert( Meta::valPlaycount,   TagLib::String( "----:com.apple.iTunes:FMPS_Playcount" ) );
0044     m_fieldMap.insert( Meta::valRating,      TagLib::String( "----:com.apple.iTunes:FMPS_Rating" ) );
0045     m_fieldMap.insert( Meta::valScore,       TagLib::String( "----:com.apple.iTunes:FMPS_Rating_Amarok_Score" ) );
0046     m_fieldMap.insert( Meta::valLyrics,      TagLib::String( "\xa9lyr" ) );
0047 
0048     m_uidFieldMap.insert( UIDAFT,            TagLib::String( "----:com.apple.iTunes:Amarok 2 AFTv1 - amarok.kde.org" ) );
0049 }
0050 
0051 Meta::FieldHash
0052 MP4TagHelper::tags() const
0053 {
0054     Meta::FieldHash data = TagHelper::tags();
0055 
0056     TagLib::MP4::ItemListMap map = m_tag->itemListMap();
0057     for( TagLib::MP4::ItemListMap::ConstIterator it = map.begin(); it != map.end(); ++it )
0058     {
0059         qint64 field;
0060         QString value = TStringToQString( it->second.toStringList().toString( '\n' ) );
0061         if( ( field = fieldName( it->first ) ) )
0062         {
0063             if( field == Meta::valHasCover )
0064             {
0065                 TagLib::MP4::CoverArtList coverList = it->second.toCoverArtList();
0066                 for( TagLib::MP4::CoverArtList::ConstIterator it = coverList.begin(); it != coverList.end(); ++it )
0067                     if( it->data().size() > MIN_COVER_SIZE )
0068                     {
0069                         data.insert( field, true );
0070                         break;
0071                     }
0072             }
0073 
0074             // http://gitorious.org/~jefferai/xdg-specs/jefferais-xdg-specs/blobs/mediaspecs/specifications/FMPSpecs/specification.txt sais that mp4 tags should be saved as strings
0075             else if( field == Meta::valPlaycount )
0076                 data.insert( field, value.toInt() );
0077             else if( field == Meta::valRating )
0078                 data.insert( field, qRound( value.toFloat() * 10.0 ) );
0079             else if( field == Meta::valScore )
0080                 data.insert( field, value.toFloat() * 100.0 );
0081 
0082             else if( field == Meta::valBpm )
0083                 data.insert( field, it->second.toInt() );
0084             else if( field == Meta::valDiscNr )
0085                 data.insert( field, it->second.toIntPair().first );
0086 
0087             else if( field == Meta::valCompilation )
0088                 data.insert( field, it->second.toBool() );
0089             else
0090                 data.insert( field, value );
0091         }
0092         else if( it->first == uidFieldName( UIDAFT ) && isValidUID( value, UIDAFT ) )
0093             data.insert( Meta::valUniqueId, value );
0094     }
0095 
0096     return data;
0097 }
0098 
0099 bool
0100 MP4TagHelper::setTags( const Meta::FieldHash &changes )
0101 {
0102     bool modified = TagHelper::setTags( changes );
0103 
0104     foreach( const qint64 key, changes.keys() )
0105     {
0106         QVariant value = changes.value( key );
0107         TagLib::String field = fieldName( key );
0108 
0109         if( !field.isNull() && !field.isEmpty() )
0110         {
0111             // http://gitorious.org/~jefferai/xdg-specs/jefferais-xdg-specs/blobs/mediaspecs/specifications/FMPSpecs/specification.txt sais that mp4 tags should be saved as strings
0112             if( key == Meta::valHasCover )
0113                 continue;
0114             else if( key == Meta::valRating )
0115                 m_tag->itemListMap()[field] = TagLib::StringList( Qt4QStringToTString( QString::number( value.toFloat() / 10.0 ) ) );
0116             else if( key == Meta::valScore )
0117                 m_tag->itemListMap()[field] = TagLib::StringList( Qt4QStringToTString( QString::number( value.toFloat() / 100.0 ) ) );
0118             else if( key == Meta::valBpm || key == Meta::valDiscNr )
0119                 m_tag->itemListMap()[field] = TagLib::MP4::Item( value.toInt(), 0 );
0120             else if( key == Meta::valCompilation )
0121                 m_tag->itemListMap()[field] = TagLib::MP4::Item( value.toBool() );
0122             else
0123                 m_tag->itemListMap()[field] = TagLib::StringList( Qt4QStringToTString( value.toString() ) );
0124 
0125             modified = true;
0126         }
0127         else if( key == Meta::valUniqueId )
0128         {
0129             QPair < UIDType, QString > uidPair = splitUID( value.toString() );
0130             if( uidPair.first == UIDInvalid )
0131                 continue;
0132 
0133             m_tag->itemListMap()[uidFieldName( uidPair.first )] = TagLib::StringList( Qt4QStringToTString( uidPair.second ) );
0134             modified = true;
0135         }
0136     }
0137 
0138     return modified;
0139 }
0140 
0141 bool
0142 MP4TagHelper::hasEmbeddedCover() const
0143 {
0144     TagLib::MP4::ItemListMap map = m_tag->itemListMap();
0145     TagLib::String name = fieldName( Meta::valHasCover );
0146     for( TagLib::MP4::ItemListMap::ConstIterator it = map.begin(); it != map.end(); ++it )
0147     {
0148         if( it->first == name )
0149         {
0150             TagLib::MP4::CoverArtList coverList = it->second.toCoverArtList();
0151             for( TagLib::MP4::CoverArtList::ConstIterator cover = coverList.begin(); cover != coverList.end(); ++cover )
0152             {
0153                 if( cover->data().size() > MIN_COVER_SIZE )
0154                     return true;
0155             }
0156         }
0157     }
0158 
0159     return false;
0160 }
0161 
0162 QImage
0163 MP4TagHelper::embeddedCover() const
0164 {
0165     TagLib::MP4::ItemListMap map = m_tag->itemListMap();
0166     TagLib::String name = fieldName( Meta::valHasCover );
0167     for( TagLib::MP4::ItemListMap::ConstIterator it = map.begin(); it != map.end(); ++it )
0168     {
0169         if( it->first == name )
0170         {
0171             TagLib::MP4::CoverArtList coverList = it->second.toCoverArtList();
0172             for( TagLib::MP4::CoverArtList::Iterator cover = coverList.begin(); cover != coverList.end(); ++cover )
0173             {
0174                 if( cover->data().size() > MIN_COVER_SIZE )
0175                     return QImage::fromData( ( uchar * ) cover->data().data(), cover->data().size() );
0176             }
0177         }
0178     }
0179 
0180     return QImage();
0181 }
0182 
0183 bool
0184 MP4TagHelper::setEmbeddedCover( const QImage &cover )
0185 {
0186     QByteArray bytes;
0187     QBuffer buffer( &bytes );
0188 
0189     buffer.open( QIODevice::WriteOnly );
0190 
0191     if( !cover.save( &buffer, "JPEG" ) )
0192     {
0193         buffer.close();
0194         return false;
0195     }
0196 
0197     buffer.close();
0198 
0199     TagLib::MP4::CoverArtList covers;
0200 
0201     covers.append( TagLib::MP4::CoverArt( TagLib::MP4::CoverArt::JPEG, TagLib::ByteVector( bytes.data(), bytes.count() ) ) );
0202 
0203     m_tag->itemListMap()[fieldName( Meta::valHasCover )] = TagLib::MP4::Item( covers );
0204 
0205     return true;
0206 }