tmetadataloader.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #include <QtCore>
  2. #include <taglib/mpegfile.h>
  3. #include <taglib/fileref.h>
  4. #include <taglib/id3v2tag.h>
  5. #include <taglib/id3v2frame.h>
  6. #include "tmetadataloader.h"
  7. #include "urlutils.h"
  8. using namespace TagLib;
  9. /** Given a filename, fetches metadata.
  10. A functor for using TagLib in a with functional algorithm.
  11. Unfortunately, it is not thread-safe so we don't use it with QtConcurrent. */
  12. class MetaDataFunctor : public std::unary_function<QUrl, MetaDataValue> {
  13. public:
  14. MetaDataValue operator()(QUrl url);
  15. };
  16. QString toQString(const TagLib::String &s) {
  17. #ifdef Q_OS_WIN
  18. return QString::fromAscii(s.toCString());
  19. #else
  20. return QString::fromStdWString(s.toWString());
  21. #endif
  22. }
  23. const QString FMPSPREFIX="[FMPS_Rating] FMPS_Rating ";
  24. QRegExp fmpsRx("FMPS_Rating ([\\d\\.]+)");
  25. //start id="functor"
  26. MetaDataValue MetaDataFunctor::operator ()(QUrl url) {
  27. using namespace TagLib;
  28. MetaDataValue retval;
  29. retval.setUrl(url);
  30. QString path = UrlUtils::toLocalFile(url);
  31. FileRef f(path.toLocal8Bit().constData());
  32. const Tag* t = f.tag();
  33. if (t == NULL) {
  34. qDebug() << "error: no tag available: " << path << "\n from URL: " << url.toEncoded();
  35. return retval;
  36. }
  37. retval.setTrackTitle(toQString(t->title()));
  38. retval.setArtist(toQString(t->artist()));
  39. retval.setAlbumTitle(toQString(t->album()));
  40. //end
  41. QString trackNumber = QString("%1").arg(t->track());
  42. retval.setTrackNumber(trackNumber);
  43. retval.setGenre(toQString(t->genre()));
  44. retval.setComment(toQString(t->comment()));
  45. /*
  46. #ifdef FMPS_RATING
  47. // http://www.freedesktop.org/wiki/Specifications/free-media-player-specs
  48. MPEG::File mpegFile(path.toLocal8Bit());
  49. const ID3v2::Tag *id3v2 = mpegFile.ID3v2Tag();
  50. if (id3v2 != 0) {
  51. ID3v2::FrameListMap map = id3v2->frameListMap();
  52. ID3v2::FrameList fl = map["TXXX"];
  53. if (!fl.isEmpty()) {
  54. QString v = toQString(fl.front()->toString());
  55. if (v.contains(fmpsRx)) {
  56. QString ratingStr = fmpsRx.cap(1);
  57. double dr = ratingStr.toDouble() * 10.0;
  58. int rating = (int)dr;
  59. Preference p(rating);
  60. retval.setPreference(p);
  61. }
  62. }
  63. //ID3v2::FrameList::ConstIterator litr = fl.begin();
  64. // while (litr != fl.end()) {
  65. // ++litr;
  66. // }
  67. }
  68. #else
  69. // musicmatch style preference
  70. retval.setPreference(retval.comment());
  71. #endif
  72. */
  73. //start id="functor"
  74. QTime time(0,0,0,0);
  75. const AudioProperties* ap = f.audioProperties();
  76. time = time.addSecs(ap->length());
  77. retval.setTrackTime(time);
  78. return retval;
  79. }
  80. //end
  81. const QStringList & TagLib::MetaDataLoader::supportedExtensions() {
  82. static QStringList sl;
  83. if (sl.isEmpty()) // there might be more but this is all I care about for now...
  84. sl << "*.mp3" << "*.mp4" << "*.asf" << "*.wma" << "*.flac" << "*.ogg" << "*.aiff";
  85. return sl;
  86. }
  87. TagLib::MetaDataLoader* TagLib::MetaDataLoader::instance() {
  88. static TagLib::MetaDataLoader* inst = 0;
  89. if (inst == 0) {
  90. inst = new TagLib::MetaDataLoader(qApp);
  91. }
  92. return inst;
  93. }
  94. //start id="metadataloader"
  95. TagLib::MetaDataLoader::MetaDataLoader(QObject *parent) :
  96. SUPER(parent) {
  97. m_processingMax = 0;
  98. m_running = false;
  99. qDebug() << "TagLib::MetaDataLoader created.";
  100. connect (this, SIGNAL(finished()), this, SLOT(checkForWork()),
  101. Qt::QueuedConnection);
  102. }
  103. void TagLib::MetaDataLoader::get(QUrl path) {
  104. m_queue << path;
  105. m_timer.singleShot(2000, this, SLOT(checkForWork()));
  106. }
  107. MetaDataLoader* MetaDataLoader::clone(QObject *parent) {
  108. return new MetaDataLoader(parent);
  109. }
  110. void TagLib::MetaDataLoader::checkForWork() {
  111. MetaDataFunctor functor;
  112. if (m_queue.isEmpty() && !m_running) {
  113. m_processingMax = 0;
  114. return;
  115. }
  116. if (m_running ) return;
  117. m_running = true;
  118. m_canceled = false;
  119. QList<QUrl> sl = m_queue;
  120. m_queue = QList<QUrl>();
  121. m_processingMax = sl.length();
  122. emit progressRangeChanged(0, m_processingMax);
  123. for (int i=0; i<m_processingMax;++i) {
  124. if (m_canceled) break;
  125. emit fetched(functor(sl[i]));
  126. emit progressValueChanged(i);
  127. qApp->processEvents(); /* Allow the GUI to process events
  128. (and our signals to be delivered) */
  129. }
  130. m_running = false;
  131. emit finished();
  132. }
  133. //end
  134. void TagLib::MetaDataLoader::cancel() {
  135. m_canceled = true;
  136. }
  137. void TagLib::MetaDataLoader::get(QList<QUrl> paths) {
  138. m_queue.append(paths);
  139. checkForWork();
  140. }