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

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Sergey Ivanov <123kash@gmail.com>                                 *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "TagHelper.h"
0018 
0019 #include <config.h>
0020 
0021 #include <QImage>
0022 #include <QRegExp>
0023 #include <QStringList>
0024 
0025 #pragma GCC diagnostic push
0026 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
0027 #include <fileref.h>
0028 #include <aifffile.h>
0029 #include <asffile.h>
0030 #include <flacfile.h>
0031 #include <mp4file.h>
0032 #include <mpcfile.h>
0033 #include <mpegfile.h>
0034 #include <oggfile.h>
0035 #ifdef TAGLIB_OPUS_FOUND
0036 #include <opusfile.h>
0037 #endif
0038 #include <oggflacfile.h>
0039 #include <rifffile.h>
0040 #include <speexfile.h>
0041 #include <trueaudiofile.h>
0042 #include <vorbisfile.h>
0043 #include <wavfile.h>
0044 #include <wavpackfile.h>
0045 #ifdef TAGLIB_MOD_FOUND
0046 #include <modfile.h>
0047 #include <s3mfile.h>
0048 #include <itfile.h>
0049 #include <xmfile.h>
0050 #endif
0051 #pragma GCC diagnostic pop
0052 
0053 #include "APETagHelper.h"
0054 #include "ASFTagHelper.h"
0055 #include "ID3v2TagHelper.h"
0056 #include "MP4TagHelper.h"
0057 #include "VorbisCommentTagHelper.h"
0058 
0059 #include "StringHelper.h"
0060 
0061 using namespace Meta::Tag;
0062 
0063 TagHelper::TagHelper( TagLib::Tag *tag, Amarok::FileType fileType )
0064          : m_tag( tag )
0065          , m_fileType( fileType )
0066 {
0067 }
0068 
0069 TagHelper::TagHelper( TagLib::ID3v1::Tag *tag, Amarok::FileType fileType )
0070          : m_tag( tag )
0071          , m_fileType( fileType )
0072 {
0073 }
0074 
0075 TagHelper::~TagHelper()
0076 {
0077 }
0078 
0079 Meta::FieldHash
0080 TagHelper::tags() const
0081 {
0082     Meta::FieldHash data;
0083 
0084     data.insert( Meta::valTitle,   TStringToQString( m_tag->title() ) );
0085     data.insert( Meta::valArtist,  TStringToQString( m_tag->artist() ) );
0086     data.insert( Meta::valAlbum,   TStringToQString( m_tag->album() ) );
0087     data.insert( Meta::valTrackNr, m_tag->track() );
0088     data.insert( Meta::valYear,    m_tag->year() );
0089     data.insert( Meta::valGenre,   TStringToQString( m_tag->genre() ) );
0090     data.insert( Meta::valComment, TStringToQString( m_tag->comment() ) );
0091 
0092     return data;
0093 }
0094 
0095 bool
0096 TagHelper::setTags( const Meta::FieldHash &changes )
0097 {
0098     bool modified = false;
0099 
0100     if( changes.contains( Meta::valTitle ) )
0101     {
0102         m_tag->setTitle( Qt4QStringToTString( changes.value( Meta::valTitle ).toString() ) );
0103         modified = true;
0104     }
0105     if( changes.contains( Meta::valArtist ) )
0106     {
0107         m_tag->setArtist( Qt4QStringToTString( changes.value( Meta::valArtist ).toString() ) );
0108         modified = true;
0109     }
0110     if( changes.contains( Meta::valAlbum ) )
0111     {
0112         m_tag->setAlbum( Qt4QStringToTString( changes.value( Meta::valAlbum ).toString() ) );
0113         modified = true;
0114     }
0115     if( changes.contains( Meta::valTrackNr ) )
0116     {
0117         m_tag->setTrack( changes.value( Meta::valTrackNr ).toUInt() );
0118         modified = true;
0119     }
0120     if( changes.contains( Meta::valYear ) )
0121     {
0122         m_tag->setYear( changes.value( Meta::valYear ).toUInt() );
0123         modified = true;
0124     }
0125     if( changes.contains( Meta::valGenre ) )
0126     {
0127         m_tag->setGenre( Qt4QStringToTString( changes.value( Meta::valGenre ).toString() ) );
0128         modified = true;
0129     }
0130     if( changes.contains( Meta::valComment ) )
0131     {
0132         m_tag->setComment( Qt4QStringToTString( changes.value( Meta::valComment ).toString() ) );
0133         modified = true;
0134     }
0135 
0136     return modified;
0137 }
0138 
0139 TagLib::ByteVector
0140 TagHelper::render() const
0141 {
0142     QByteArray byteArray;
0143     QDataStream stream( &byteArray, QIODevice::WriteOnly );
0144     stream << tags();
0145     return TagLib::ByteVector( byteArray.constData(), byteArray.size() );
0146 }
0147 
0148 bool
0149 TagHelper::hasEmbeddedCover() const
0150 {
0151     return false;
0152 }
0153 
0154 QImage
0155 TagHelper::embeddedCover() const
0156 {
0157     return QImage();
0158 }
0159 
0160 bool
0161 TagHelper::setEmbeddedCover( const QImage &cover )
0162 {
0163     Q_UNUSED( cover )
0164     return false;
0165 }
0166 
0167 TagLib::String
0168 TagHelper::fieldName( const qint64 field ) const
0169 {
0170     return m_fieldMap.value( field );
0171 }
0172 
0173 qint64
0174 TagHelper::fieldName( const TagLib::String &field ) const
0175 {
0176     return m_fieldMap.key( field );
0177 }
0178 
0179 QPair< TagHelper::UIDType, QString >
0180 TagHelper::splitUID( const QString &uidUrl ) const
0181 {
0182     TagHelper::UIDType type = UIDInvalid;
0183     QString uid = uidUrl;
0184 
0185     if( uid.startsWith( QLatin1String("amarok-") ) )
0186         uid = uid.remove( QRegExp( QStringLiteral("^(amarok-\\w+://).+$") ) );
0187 
0188     if( isValidUID( uid, UIDAFT ) )
0189         type = UIDAFT;
0190 
0191     return qMakePair( type, uid );
0192 }
0193 
0194 QPair< int, int >
0195 TagHelper::splitDiscNr( const QString &value ) const
0196 {
0197     int disc;
0198     int count = 0;
0199     if( value.indexOf( QLatin1Char('/') ) != -1 )
0200     {
0201         QStringList list = value.split( QLatin1Char('/'), Qt::SkipEmptyParts );
0202         disc = list.value( 0 ).toInt();
0203         count = list.value( 1 ).toInt();
0204     }
0205     else if( value.indexOf( QLatin1Char(':') ) != -1 )
0206     {
0207         QStringList list = value.split( QLatin1Char(':'), Qt::SkipEmptyParts );
0208         disc = list.value( 0 ).toInt();
0209         count = list.value( 1 ).toInt();
0210     }
0211     else
0212         disc = value.toInt();
0213 
0214     return qMakePair( disc, count );
0215 }
0216 
0217 bool
0218 TagHelper::isValidUID( const QString &uid, const TagHelper::UIDType type ) const
0219 {
0220     if( uid.length() >= 127 ) // the database can't handle longer uids
0221         return false;
0222 
0223     QRegExp regexp( QStringLiteral("^$") );
0224 
0225     if( type == UIDAFT )
0226         regexp.setPattern( QStringLiteral("^[0-9a-fA-F]{32}$") );
0227 
0228     return regexp.exactMatch( uid );
0229 }
0230 
0231 TagLib::String
0232 TagHelper::uidFieldName( const TagHelper::UIDType type ) const
0233 {
0234     return m_uidFieldMap.value( type );
0235 }
0236 
0237 TagLib::String
0238 TagHelper::fmpsFieldName( const TagHelper::FMPS field ) const
0239 {
0240     return m_fmpsFieldMap.value( field );
0241 }
0242 
0243 Amarok::FileType
0244 TagHelper::fileType() const
0245 {
0246     return m_fileType;
0247 }
0248 
0249 QByteArray
0250 TagHelper::testString() const
0251 {
0252     TagLib::String string = m_tag->album() + m_tag->artist() + m_tag->comment() +
0253                             m_tag->genre() + m_tag->title();
0254 
0255     return QByteArray( string.toCString( true ) );
0256 }
0257 
0258 
0259 Meta::Tag::TagHelper *
0260 Meta::Tag::selectHelper( const TagLib::FileRef &fileref, bool forceCreation )
0261 {
0262     TagHelper *tagHelper = nullptr;
0263 
0264     if( TagLib::MPEG::File *file = dynamic_cast< TagLib::MPEG::File * >( fileref.file() ) )
0265     {
0266         if( file->ID3v2Tag( forceCreation ) )
0267             tagHelper = new ID3v2TagHelper( fileref.tag(), file->ID3v2Tag(), Amarok::Mp3 );
0268         else if( file->APETag() )
0269             tagHelper = new APETagHelper( fileref.tag(), file->APETag(), Amarok::Mp3 );
0270         else if( file->ID3v1Tag() )
0271             tagHelper = new TagHelper( fileref.tag(), Amarok::Mp3 );
0272     }
0273     else if( TagLib::Ogg::Vorbis::File *file = dynamic_cast< TagLib::Ogg::Vorbis::File * >( fileref.file() ) )
0274     {
0275         if( file->tag() )
0276             tagHelper = new VorbisCommentTagHelper( fileref.tag(), file->tag(), Amarok::Ogg );
0277     }
0278     else if( TagLib::Ogg::FLAC::File *file = dynamic_cast< TagLib::Ogg::FLAC::File * >( fileref.file() ) )
0279     {
0280         if( file->tag() )
0281             tagHelper = new VorbisCommentTagHelper( fileref.tag(), file->tag(), Amarok::Flac );
0282     }
0283     else if( TagLib::Ogg::Speex::File *file = dynamic_cast< TagLib::Ogg::Speex::File * >( fileref.file() ) )
0284     {
0285         if( file->tag() )
0286             tagHelper = new VorbisCommentTagHelper( fileref.tag(), file->tag(), Amarok::Speex );
0287     }
0288 #ifdef TAGLIB_OPUS_FOUND
0289     else if( TagLib::Ogg::Opus::File *file = dynamic_cast< TagLib::Ogg::Opus::File * >( fileref.file() ) )
0290     {
0291         if( file->tag() )
0292             tagHelper = new VorbisCommentTagHelper( fileref.tag(), file->tag(), Amarok::Opus );
0293     }
0294 #endif
0295     else if( TagLib::FLAC::File *file = dynamic_cast< TagLib::FLAC::File * >( fileref.file() ) )
0296     {
0297         if( file->xiphComment() )
0298             tagHelper = new VorbisCommentTagHelper( fileref.tag(), file->xiphComment(), Amarok::Flac, file );
0299         else if( file->ID3v2Tag() )
0300             tagHelper = new ID3v2TagHelper( fileref.tag(), file->ID3v2Tag(), Amarok::Flac );
0301         else if( file->ID3v1Tag() )
0302             tagHelper = new TagHelper( fileref.tag(), Amarok::Flac );
0303     }
0304     else if( TagLib::MP4::File *file = dynamic_cast< TagLib::MP4::File * >( fileref.file() ) )
0305     {
0306         TagLib::MP4::Tag *tag = dynamic_cast< TagLib::MP4::Tag * >( file->tag() );
0307         if( tag )
0308         {
0309             Amarok::FileType specificType = Amarok::Mp4;
0310             QString filename = QString::fromLatin1( fileref.file()->name() );
0311             foreach( Amarok::FileType type, QList<Amarok::FileType>() << Amarok::M4a << Amarok::M4v )
0312             {
0313                 if( filename.endsWith( Amarok::FileTypeSupport::toString( type ), Qt::CaseInsensitive ) )
0314                     specificType = type;
0315             }
0316             tagHelper = new MP4TagHelper( fileref.tag(), tag, specificType );
0317         }
0318     }
0319     else if( TagLib::MPC::File *file = dynamic_cast< TagLib::MPC::File * >( fileref.file() ) )
0320     {
0321         if( file->APETag( forceCreation ) )
0322             tagHelper = new APETagHelper( fileref.tag(), file->APETag(), Amarok::Mpc );
0323         else if( file->ID3v1Tag() )
0324             tagHelper = new TagHelper( fileref.tag(), Amarok::Mpc );
0325     }
0326     else if( TagLib::RIFF::AIFF::File *file = dynamic_cast< TagLib::RIFF::AIFF::File * >( fileref.file() ) )
0327     {
0328         if( file->tag() )
0329             tagHelper = new ID3v2TagHelper( fileref.tag(), file->tag(), Amarok::Aiff );
0330     }
0331     else if( TagLib::RIFF::WAV::File *file = dynamic_cast< TagLib::RIFF::WAV::File * >( fileref.file() ) )
0332     {
0333         if( file->tag() )
0334             tagHelper = new ID3v2TagHelper( fileref.tag(), file->tag(), Amarok::Wav );
0335     }
0336     else if( TagLib::ASF::File *file = dynamic_cast< TagLib::ASF::File * >( fileref.file() ) )
0337     {
0338         TagLib::ASF::Tag *tag = dynamic_cast< TagLib::ASF::Tag * >( file->tag() );
0339         if( tag )
0340             tagHelper = new ASFTagHelper( fileref.tag(), tag, Amarok::Wma );
0341     }
0342     else if( TagLib::TrueAudio::File *file = dynamic_cast< TagLib::TrueAudio::File * >( fileref.file() ) )
0343     {
0344         if( file->ID3v2Tag( forceCreation ) )
0345             tagHelper = new ID3v2TagHelper( fileref.tag(), file->ID3v2Tag(), Amarok::TrueAudio );
0346         else if( file->ID3v1Tag() )
0347             tagHelper = new TagHelper( fileref.tag(), Amarok::TrueAudio );
0348     }
0349     else if( TagLib::WavPack::File *file = dynamic_cast< TagLib::WavPack::File * >( fileref.file() ) )
0350     {
0351         if( file->APETag( forceCreation ) )
0352             tagHelper = new APETagHelper( fileref.tag(), file->APETag(), Amarok::WavPack );
0353         else if( file->ID3v1Tag() )
0354             tagHelper = new TagHelper( fileref.tag(), Amarok::WavPack );
0355     }
0356 #ifdef TAGLIB_MOD_FOUND
0357     else if( TagLib::Mod::File *file = dynamic_cast< TagLib::Mod::File * >( fileref.file() ) )
0358     {
0359         if( file->tag() )
0360             tagHelper = new TagHelper( fileref.tag(), Amarok::Mod );
0361     }
0362     else if( TagLib::S3M::File *file = dynamic_cast< TagLib::S3M::File * >( fileref.file() ) )
0363     {
0364         if( file->tag() )
0365             tagHelper = new TagHelper( fileref.tag(), Amarok::S3M );
0366     }
0367     else if( TagLib::IT::File *file = dynamic_cast< TagLib::IT::File * >( fileref.file() ) )
0368     {
0369         if( file->tag() )
0370             tagHelper = new TagHelper( fileref.tag(), Amarok::IT );
0371     }
0372     else if( TagLib::XM::File *file = dynamic_cast< TagLib::XM::File * >( fileref.file() ) )
0373     {
0374         if( file->tag() )
0375             tagHelper = new TagHelper( fileref.tag(), Amarok::XM );
0376     }
0377 #endif
0378 
0379     return tagHelper;
0380 }
0381