File indexing completed on 2024-05-12 16:18:07

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  *                                                                         *
0008  *   This program is free software; you can redistribute it and/or modify  *
0009  *   it under the terms of the GNU General Public License as published by  *
0010  *   the Free Software Foundation; either version 2 of the License, or     *
0011  *   (at your option) any later version.                                   *
0012  *                                                                         *
0013  *   This program is distributed in the hope that it will be useful,       *
0014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0016  *   GNU General Public License for more details.                          *
0017  *                                                                         *
0018  *   You should have received a copy of the GNU General Public License     *
0019  *   along with this program; if not, write to the                         *
0020  *   Free Software Foundation, Inc.,                                       *
0021  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0022  ***************************************************************************/
0023 
0024 #include "Track.h"
0025 #include "utils.h"
0026 
0027 #include "MetaTagLib.h"
0028 #include "MetaReplayGain.h"
0029 
0030 #include <QDebug>
0031 #include <QDir>
0032 #include <QFile>
0033 #include <QFileInfo>
0034 #include <QXmlStreamReader>
0035 #include <QXmlStreamWriter>
0036 
0037 bool CollectionScanner::Track::s_useCharsetDetector = false;
0038 
0039 
0040 CollectionScanner::Track::Track( const QString &path, CollectionScanner::Directory* directory )
0041    : m_valid( true )
0042    , m_directory( directory )
0043    , m_filetype( Amarok::Unknown )
0044    , m_compilation( false )
0045    , m_noCompilation( false )
0046    , m_hasCover( false )
0047    , m_year( -1 )
0048    , m_disc( -1 )
0049    , m_track( -1 )
0050    , m_bpm( -1.0 )
0051    , m_bitrate( -1 )
0052    , m_length( -1.0 )
0053    , m_samplerate( -1 )
0054    , m_filesize( -1 )
0055 
0056    , m_trackGain( -1.0 )
0057    , m_trackPeakGain( -1.0 )
0058    , m_albumGain( -1.0 )
0059    , m_albumPeakGain( -1.0 )
0060 
0061    , m_rating( -1.0 )
0062    , m_score( -1.0 )
0063    , m_playcount( -1.0 )
0064 {
0065     static const int MAX_SENSIBLE_LENGTH = 1023; // the maximum length for normal strings.
0066     // in corner cases a longer string might cause problems see BUG:276894
0067 
0068     // for the unit test.
0069     // in a debug build a file called "crash_amarok_here.ogg" will crash the collection
0070     // scanner
0071     if( path.contains(QLatin1String("crash_amarok_here.ogg")) )
0072     {
0073         qDebug() << "Crashing at"<<path;
0074         Q_ASSERT( false );
0075     }
0076 
0077     Meta::FieldHash values = Meta::Tag::readTags( path, s_useCharsetDetector );
0078 
0079     m_valid = !values.empty();
0080     if( values.contains(Meta::valUniqueId) )
0081         m_uniqueid = values.value(Meta::valUniqueId).toString();
0082     m_path = path;
0083     m_rpath = QDir::current().relativeFilePath( path );
0084     if( values.contains(Meta::valFormat) )
0085         m_filetype = Amarok::FileType(values.value(Meta::valFormat).toInt());
0086     if( values.contains(Meta::valTitle) )
0087         m_title = values.value(Meta::valTitle).toString();
0088     if( values.contains(Meta::valArtist) )
0089         m_artist = values.value(Meta::valArtist).toString().left(MAX_SENSIBLE_LENGTH);
0090     if( values.contains(Meta::valAlbum) )
0091         m_album = values.value(Meta::valAlbum).toString().left(MAX_SENSIBLE_LENGTH);
0092     if( values.contains( Meta::valAlbumArtist ) )
0093         m_albumArtist = values.value( Meta::valAlbumArtist ).toString().left(MAX_SENSIBLE_LENGTH);
0094     if( values.contains(Meta::valCompilation) )
0095     {
0096         m_compilation = values.value(Meta::valCompilation).toBool();
0097         m_noCompilation = !values.value(Meta::valCompilation).toBool();
0098     }
0099     if( values.contains(Meta::valHasCover) )
0100         m_hasCover = values.value(Meta::valHasCover).toBool();
0101     if( values.contains(Meta::valComment) )
0102         m_comment = values.value(Meta::valComment).toString();
0103     if( values.contains(Meta::valGenre) )
0104         m_genre = values.value(Meta::valGenre).toString().left(MAX_SENSIBLE_LENGTH);
0105     if( values.contains(Meta::valYear) )
0106         m_year = values.value(Meta::valYear).toInt();
0107     if( values.contains(Meta::valDiscNr) )
0108         m_disc = values.value(Meta::valDiscNr).toInt();
0109     if( values.contains(Meta::valTrackNr) )
0110         m_track = values.value(Meta::valTrackNr).toInt();
0111     if( values.contains(Meta::valBpm) )
0112         m_bpm = values.value(Meta::valBpm).toReal();
0113     if( values.contains(Meta::valBitrate) )
0114         m_bitrate = values.value(Meta::valBitrate).toInt();
0115     if( values.contains(Meta::valLength) )
0116         m_length = values.value(Meta::valLength).toLongLong();
0117     if( values.contains(Meta::valSamplerate) )
0118         m_samplerate = values.value(Meta::valSamplerate).toInt();
0119     if( values.contains(Meta::valFilesize) )
0120         m_filesize = values.value(Meta::valFilesize).toLongLong();
0121     if( values.contains(Meta::valModified) )
0122         m_modified = values.value(Meta::valModified).toDateTime();
0123 
0124     if( values.contains(Meta::valTrackGain) )
0125         m_trackGain = values.value(Meta::valTrackGain).toReal();
0126     if( values.contains(Meta::valTrackGainPeak) )
0127         m_trackPeakGain = values.value(Meta::valTrackGainPeak).toReal();
0128     if( values.contains(Meta::valAlbumGain) )
0129         m_albumGain = values.value(Meta::valAlbumGain).toReal();
0130     if( values.contains(Meta::valAlbumGainPeak) )
0131         m_albumPeakGain = values.value(Meta::valAlbumGainPeak).toReal();
0132     while( m_uniqueid.startsWith(QLatin1Char('/')) )
0133         m_uniqueid = m_uniqueid.mid(1);
0134 
0135     if( values.contains(Meta::valComposer) )
0136         m_composer = values.value(Meta::valComposer).toString().left(MAX_SENSIBLE_LENGTH);
0137 
0138     if( values.contains(Meta::valRating) )
0139         m_rating = values.value(Meta::valRating).toReal();
0140     if( values.contains(Meta::valScore) )
0141         m_score = values.value(Meta::valScore).toReal();
0142     if( values.contains(Meta::valPlaycount) )
0143         m_playcount = values.value(Meta::valPlaycount).toReal();
0144 }
0145 
0146 CollectionScanner::Track::Track( QXmlStreamReader *reader, CollectionScanner::Directory* directory )
0147    : m_valid( true )
0148    , m_directory( directory )
0149    , m_filetype( Amarok::Unknown )
0150    , m_compilation( false )
0151    , m_noCompilation( false )
0152    , m_hasCover( false )
0153    , m_year( -1 )
0154    , m_disc( -1 )
0155    , m_track( -1 )
0156    , m_bpm( -1 )
0157    , m_bitrate( -1 )
0158    , m_length( -1 )
0159    , m_samplerate( -1 )
0160    , m_filesize( -1 )
0161 
0162    , m_trackGain( -1 )
0163    , m_trackPeakGain( -1 )
0164    , m_albumGain( -1 )
0165    , m_albumPeakGain( -1 )
0166 
0167    , m_rating( -1 )
0168    , m_score( -1 )
0169    , m_playcount( -1 )
0170 {
0171     // improve scanner with skipCurrentElement as soon as Amarok requires Qt 4.6
0172     while (!reader->atEnd()) {
0173         reader->readNext();
0174 
0175         if( reader->isStartElement() )
0176         {
0177             QStringRef name = reader->name();
0178             if( name == QLatin1String("uniqueid") )
0179                 m_uniqueid = reader->readElementText(QXmlStreamReader::SkipChildElements);
0180             else if( name == QLatin1String("path") )
0181                 m_path = reader->readElementText(QXmlStreamReader::SkipChildElements);
0182             else if( name == QLatin1String("rpath") )
0183                 m_rpath = reader->readElementText(QXmlStreamReader::SkipChildElements);
0184 
0185             else if( name == QLatin1String("filetype") )
0186                 m_filetype = (Amarok::FileType)reader->readElementText(QXmlStreamReader::SkipChildElements).toInt();
0187             else if( name == QLatin1String("title") )
0188                 m_title = reader->readElementText(QXmlStreamReader::SkipChildElements);
0189             else if( name == QLatin1String("artist") )
0190                 m_artist = reader->readElementText(QXmlStreamReader::SkipChildElements);
0191             else if( name == QLatin1String("albumArtist") )
0192                 m_albumArtist = reader->readElementText(QXmlStreamReader::SkipChildElements);
0193             else if( name == QLatin1String("album") )
0194                 m_album = reader->readElementText();
0195             else if( name == QLatin1String("compilation") )
0196             {
0197                 m_compilation = true;
0198                 reader->skipCurrentElement();
0199             }
0200             else if( name == QLatin1String("noCompilation") )
0201             {
0202                 m_noCompilation = true;
0203                 reader->skipCurrentElement();
0204             }
0205             else if( name == QLatin1String("hasCover") )
0206             {
0207                 m_hasCover = true;
0208                 reader->skipCurrentElement();
0209             }
0210             else if( name == QLatin1String("comment") )
0211                 m_comment = reader->readElementText(QXmlStreamReader::SkipChildElements);
0212             else if( name == QLatin1String("genre") )
0213                 m_genre = reader->readElementText(QXmlStreamReader::SkipChildElements);
0214             else if( name == QLatin1String("year") )
0215                 m_year = reader->readElementText(QXmlStreamReader::SkipChildElements).toInt();
0216             else if( name == QLatin1String("disc") )
0217                 m_disc = reader->readElementText(QXmlStreamReader::SkipChildElements).toInt();
0218             else if( name == QLatin1String("track") )
0219                 m_track = reader->readElementText(QXmlStreamReader::SkipChildElements).toInt();
0220             else if( name == QLatin1String("bpm") )
0221                 m_bpm = reader->readElementText(QXmlStreamReader::SkipChildElements).toFloat();
0222             else if( name == QLatin1String("bitrate") )
0223                 m_bitrate = reader->readElementText(QXmlStreamReader::SkipChildElements).toInt();
0224             else if( name == QLatin1String("length") )
0225                 m_length = reader->readElementText(QXmlStreamReader::SkipChildElements).toLong();
0226             else if( name == QLatin1String("samplerate") )
0227                 m_samplerate = reader->readElementText(QXmlStreamReader::SkipChildElements).toInt();
0228             else if( name == QLatin1String("filesize") )
0229                 m_filesize = reader->readElementText(QXmlStreamReader::SkipChildElements).toLong();
0230             else if( name == QLatin1String("mtime") )
0231                 m_modified = QDateTime::fromSecsSinceEpoch(reader->readElementText(QXmlStreamReader::SkipChildElements).toLong());
0232 
0233             else if( name == QLatin1String("trackGain") )
0234                 m_trackGain = reader->readElementText(QXmlStreamReader::SkipChildElements).toFloat();
0235             else if( name == QLatin1String("trackPeakGain") )
0236                 m_trackPeakGain = reader->readElementText(QXmlStreamReader::SkipChildElements).toFloat();
0237             else if( name == QLatin1String("albumGain") )
0238                 m_albumGain = reader->readElementText(QXmlStreamReader::SkipChildElements).toFloat();
0239             else if( name == QLatin1String("albumPeakGain") )
0240                 m_albumPeakGain = reader->readElementText(QXmlStreamReader::SkipChildElements).toFloat();
0241 
0242             else if( name == QLatin1String("composer") )
0243                 m_composer = reader->readElementText(QXmlStreamReader::SkipChildElements);
0244 
0245             else if( name == QLatin1String("rating") )
0246                 m_rating = reader->readElementText(QXmlStreamReader::SkipChildElements).toFloat();
0247             else if( name == QLatin1String("score") )
0248                 m_score = reader->readElementText(QXmlStreamReader::SkipChildElements).toFloat();
0249             else if( name == QLatin1String("playcount") )
0250                 m_playcount = reader->readElementText(QXmlStreamReader::SkipChildElements).toInt();
0251 
0252             else
0253             {
0254                 qDebug() << "Unexpected xml start element"<<name<<"in input";
0255                 reader->skipCurrentElement();
0256             }
0257         }
0258 
0259         else if( reader->isEndElement() )
0260         {
0261             break;
0262         }
0263     }
0264 }
0265 
0266 void
0267 CollectionScanner::Track::write( QXmlStreamWriter *writer,
0268                                  const QString &tag, const QString &str ) const
0269 {
0270     if( !str.isEmpty() )
0271         writer->writeTextElement( tag, escapeXml10(str) );
0272 }
0273 
0274 
0275 void
0276 CollectionScanner::Track::toXml( QXmlStreamWriter *writer ) const
0277 {
0278     if( !m_valid )
0279         return;
0280 
0281     write( writer, QStringLiteral("uniqueid"), m_uniqueid );
0282     write( writer, QStringLiteral("path"), m_path );
0283     write( writer, QStringLiteral("rpath"), m_rpath );
0284 
0285     write(writer, QStringLiteral("filetype"), QString::number( (int)m_filetype ) );
0286 
0287     write( writer, QStringLiteral("title"), m_title);
0288     write( writer, QStringLiteral("artist"), m_artist);
0289     write( writer, QStringLiteral("albumArtist"), m_albumArtist);
0290     write( writer, QStringLiteral("album"), m_album);
0291     if( m_compilation )
0292         writer->writeEmptyElement( QStringLiteral("compilation") );
0293     if( m_noCompilation )
0294         writer->writeEmptyElement( QStringLiteral("noCompilation") );
0295     if( m_hasCover )
0296         writer->writeEmptyElement( QStringLiteral("hasCover") );
0297     write( writer, QStringLiteral("comment"), m_comment);
0298     write( writer, QStringLiteral("genre"), m_genre);
0299     if( m_year != -1 )
0300         write(writer, QStringLiteral("year"), QString::number( m_year ) );
0301     if( m_disc != -1 )
0302         write(writer, QStringLiteral("disc"), QString::number( m_disc ) );
0303     if( m_track != -1 )
0304         write(writer, QStringLiteral("track"), QString::number( m_track ) );
0305     if( m_bpm != -1 )
0306         write(writer, QStringLiteral("bpm"), QString::number( m_bpm ) );
0307     if( m_bitrate != -1 )
0308         write(writer, QStringLiteral("bitrate"), QString::number( m_bitrate ) );
0309     if( m_length != -1 )
0310         write(writer, QStringLiteral("length"), QString::number( m_length ) );
0311     if( m_samplerate != -1 )
0312         write(writer, QStringLiteral("samplerate"), QString::number( m_samplerate ) );
0313     if( m_filesize != -1 )
0314         write(writer, QStringLiteral("filesize"), QString::number( m_filesize ) );
0315     if( m_modified.isValid() )
0316         write(writer, QStringLiteral("mtime"), QString::number( m_modified.toSecsSinceEpoch() ) );
0317 
0318     if( m_trackGain != 0 )
0319         write(writer, QStringLiteral("trackGain"), QString::number( m_trackGain ) );
0320     if( m_trackPeakGain != 0 )
0321         write(writer, QStringLiteral("trackPeakGain"), QString::number( m_trackPeakGain ) );
0322     if( m_albumGain != 0 )
0323         write(writer, QStringLiteral("albumGain"), QString::number( m_albumGain ) );
0324     if( m_albumPeakGain != 0 )
0325         write(writer, QStringLiteral("albumPeakGain"), QString::number( m_albumPeakGain ) );
0326 
0327     write( writer, QStringLiteral("composer"), m_composer);
0328 
0329     if( m_rating != -1 )
0330         write(writer, QStringLiteral("rating"), QString::number( m_rating ) );
0331     if( m_score != -1 )
0332         write(writer, QStringLiteral("score"), QString::number( m_score ) );
0333     if( m_playcount != -1 )
0334         write(writer, QStringLiteral("playcount"), QString::number( m_playcount ) );
0335 
0336 }
0337 
0338 bool
0339 CollectionScanner::Track::isValid() const
0340 {
0341     return m_valid;
0342 }
0343 
0344 CollectionScanner::Directory*
0345 CollectionScanner::Track::directory() const
0346 {
0347     return m_directory;
0348 }
0349 
0350 QString
0351 CollectionScanner::Track::uniqueid() const
0352 {
0353     return m_uniqueid;
0354 }
0355 
0356 QString
0357 CollectionScanner::Track::path() const
0358 {
0359     return m_path;
0360 }
0361 
0362 QString
0363 CollectionScanner::Track::rpath() const
0364 {
0365     return m_rpath;
0366 }
0367 
0368 Amarok::FileType
0369 CollectionScanner::Track::filetype() const
0370 {
0371     return m_filetype;
0372 }
0373 
0374 QString
0375 CollectionScanner::Track::title() const
0376 {
0377     return m_title;
0378 }
0379 
0380 QString
0381 CollectionScanner::Track::artist() const
0382 {
0383     return m_artist;
0384 }
0385 
0386 QString
0387 CollectionScanner::Track::albumArtist() const
0388 {
0389     return m_albumArtist;
0390 }
0391 
0392 QString
0393 CollectionScanner::Track::album() const
0394 {
0395     return m_album;
0396 }
0397 
0398 bool
0399 CollectionScanner::Track::isCompilation() const
0400 {
0401     return m_compilation;
0402 }
0403 
0404 bool
0405 CollectionScanner::Track::isNoCompilation() const
0406 {
0407     return m_noCompilation;
0408 }
0409 
0410 
0411 bool
0412 CollectionScanner::Track::hasCover() const
0413 {
0414     return m_hasCover;
0415 }
0416 
0417 QString
0418 CollectionScanner::Track::comment() const
0419 {
0420     return m_comment;
0421 }
0422 
0423 QString
0424 CollectionScanner::Track::genre() const
0425 {
0426     return m_genre;
0427 }
0428 
0429 int
0430 CollectionScanner::Track::year() const
0431 {
0432     return m_year;
0433 }
0434 
0435 int
0436 CollectionScanner::Track::disc() const
0437 {
0438     return m_disc;
0439 }
0440 
0441 int
0442 CollectionScanner::Track::track() const
0443 {
0444     return m_track;
0445 }
0446 
0447 int
0448 CollectionScanner::Track::bpm() const
0449 {
0450     return m_bpm;
0451 }
0452 
0453 int
0454 CollectionScanner::Track::bitrate() const
0455 {
0456     return m_bitrate;
0457 }
0458 
0459 qint64
0460 CollectionScanner::Track::length() const
0461 {
0462     return m_length;
0463 }
0464 
0465 int
0466 CollectionScanner::Track::samplerate() const
0467 {
0468     return m_samplerate;
0469 }
0470 
0471 qint64
0472 CollectionScanner::Track::filesize() const
0473 {
0474     return m_filesize;
0475 }
0476 
0477 QDateTime
0478 CollectionScanner::Track::modified() const
0479 {
0480     return m_modified;
0481 }
0482 
0483 
0484 QString
0485 CollectionScanner::Track::composer() const
0486 {
0487     return m_composer;
0488 }
0489 
0490 
0491 qreal
0492 CollectionScanner::Track::replayGain( Meta::ReplayGainTag mode ) const
0493 {
0494     switch( mode )
0495     {
0496     case Meta::ReplayGain_Track_Gain:
0497         return m_trackGain;
0498     case Meta::ReplayGain_Track_Peak:
0499         return m_trackPeakGain;
0500     case Meta::ReplayGain_Album_Gain:
0501         return m_albumGain;
0502     case Meta::ReplayGain_Album_Peak:
0503         return m_albumPeakGain;
0504     }
0505     return 0.0;
0506 }
0507 
0508 qreal
0509 CollectionScanner::Track::rating() const
0510 {
0511     return m_rating;
0512 }
0513 
0514 qreal
0515 CollectionScanner::Track::score() const
0516 {
0517     return m_score;
0518 }
0519 
0520 int
0521 CollectionScanner::Track::playcount() const
0522 {
0523     return m_playcount;
0524 }
0525 
0526 void
0527 CollectionScanner::Track::setUseCharsetDetector( bool value )
0528 {
0529     s_useCharsetDetector = value;
0530 }