MythTV  master
metaioid3.cpp
Go to the documentation of this file.
1 #include <set>
2 
3 // C++ headers
4 #include <cmath>
5 
6 // qt
7 #include <QBuffer>
8 
9 // Libmythbase
10 #include <mythlogging.h>
11 #include <mythcorecontext.h>
12 
13 // Taglib
14 #include <flacfile.h>
15 #include <mpegfile.h>
16 
17 // libmythmetadata
18 #include "metaioid3.h"
19 #include "musicmetadata.h"
20 #include "musicutils.h"
21 
22 const String email = "music@mythtv.org"; // TODO username/ip/hostname?
23 
31 bool MetaIOID3::OpenFile(const QString &filename, bool forWriting)
32 {
33  // Check if file is already opened
34  if (m_file && (m_filename == filename) &&
35  (!forWriting || !m_file->readOnly()))
36  return true;
37 
38  if (m_file)
39  {
40  LOG(VB_FILE, LOG_DEBUG,
41  QString("MetaIO switch file: %1 New File: %2 Type: %3")
42  .arg(m_filename)
43  .arg(filename)
44  .arg(m_fileType));
45  }
46 
47  // If a file is open but it's not the requested file then close it first
48  if (m_file)
49  CloseFile();
50 
52 
53  QString extension = m_filename.section('.', -1);
54 
55  if (extension.toLower() == "flac")
56  m_fileType = kFLAC;
57  else if (extension.toLower() == "mp3" || extension.toLower() == "mp2")
58  m_fileType = kMPEG;
59  else
60  return false;
61 
62  QByteArray fname = m_filename.toLocal8Bit();
63  // Open the file
64  switch (m_fileType)
65  {
66  case kMPEG :
67  m_file = new TagLib::MPEG::File(fname.constData());
68  break;
69  case kFLAC :
70  m_file = new TagLib::FLAC::File(fname.constData());
71  break;
72  }
73 
74  // If the requested file could not be opened then close it and return false
75  if (!m_file->isOpen() || (forWriting && m_file->readOnly()))
76  {
77  if (m_file->isOpen())
78  {
79  LOG(VB_FILE, LOG_NOTICE,
80  QString("Could not open file for writing: %1").arg(m_filename));
81  }
82  else
83  {
84  LOG(VB_FILE, LOG_ERR,
85  QString("Could not open file: %1").arg(m_filename));
86  }
87 
88  CloseFile();
89  return false;
90  }
91 
92  return true;
93 }
94 
96 {
97  if (!m_file)
98  return false;
99 
100  saveTimeStamps();
101  bool retval = m_file->save();
103 
104  return retval;
105 }
106 
108 {
109  LOG(VB_FILE, LOG_DEBUG, QString("MetaIO Close file: %1") .arg(m_filename));
110  delete m_file;
111  m_file = nullptr;
112  m_fileType = kMPEG;
113  m_filename.clear();
114 }
115 
116 TagLib::ID3v2::Tag* MetaIOID3::GetID3v2Tag(bool create)
117 {
118  if (!m_file)
119  return nullptr;
120 
121  if (m_fileType == kMPEG)
122  {
123  auto *file = dynamic_cast<TagLib::MPEG::File*>(m_file);
124  return (file != nullptr) ? file->ID3v2Tag(create) : nullptr;
125  }
126 
127  if (m_fileType == kFLAC)
128  {
129  auto *file = dynamic_cast<TagLib::FLAC::File*>(m_file);
130  return (file != nullptr) ? file->ID3v2Tag(create) : nullptr;
131  }
132 
133  return nullptr;
134 }
135 
136 TagLib::ID3v1::Tag* MetaIOID3::GetID3v1Tag(bool create)
137 {
138  if (!m_file)
139  return nullptr;
140 
141  if (m_fileType == kMPEG)
142  {
143  auto *file = dynamic_cast<TagLib::MPEG::File*>(m_file);
144  return (file != nullptr) ? file->ID3v1Tag(create) : nullptr;
145  }
146 
147  return nullptr;
148 }
149 
153 bool MetaIOID3::write(const QString &filename, MusicMetadata* mdata)
154 {
155  if (filename.isEmpty())
156  return false;
157 
158  if (!OpenFile(filename, true))
159  return false;
160 
161  TagLib::ID3v2::Tag *tag = GetID3v2Tag();
162 
163  if (!tag)
164  return false;
165 
166  WriteGenericMetadata(tag, mdata);
167 
168  // MythTV rating and playcount, stored in POPM frame
169  writeRating(tag, mdata->Rating());
170  writePlayCount(tag, mdata->PlayCount());
171  writeLastPlay(tag, mdata->LastPlay());
172 
173  // MusicBrainz ID
174  UserTextIdentificationFrame *musicbrainz = nullptr;
175  musicbrainz = find(tag, "MusicBrainz Album Artist Id");
176 
177  if (mdata->Compilation())
178  {
179 
180  if (!musicbrainz)
181  {
182  musicbrainz = new UserTextIdentificationFrame(TagLib::String::UTF8);
183  tag->addFrame(musicbrainz);
184  musicbrainz->setDescription("MusicBrainz Album Artist Id");
185  }
186 
187  musicbrainz->setText(MYTH_MUSICBRAINZ_ALBUMARTIST_UUID);
188  }
189  else if (musicbrainz)
190  tag->removeFrame(musicbrainz);
191 
192  // Compilation Artist Frame (TPE4/2)
193  if (!mdata->CompilationArtist().isEmpty())
194  {
195  TextIdentificationFrame *tpe4frame = nullptr;
196  TagLib::ID3v2::FrameList tpelist = tag->frameListMap()["TPE4"];
197  if (!tpelist.isEmpty())
198  tpe4frame = dynamic_cast<TextIdentificationFrame *>(tpelist.front());
199 
200  if (!tpe4frame)
201  {
202  tpe4frame = new TextIdentificationFrame(TagLib::ByteVector("TPE4"),
203  TagLib::String::UTF8);
204  tag->addFrame(tpe4frame);
205  }
206  tpe4frame->setText(QStringToTString(mdata->CompilationArtist()));
207 
208 
209  TextIdentificationFrame *tpe2frame = nullptr;
210  tpelist = tag->frameListMap()["TPE2"];
211  if (!tpelist.isEmpty())
212  tpe2frame = dynamic_cast<TextIdentificationFrame *>(tpelist.front());
213 
214  if (!tpe2frame)
215  {
216  tpe2frame = new TextIdentificationFrame(TagLib::ByteVector("TPE2"),
217  TagLib::String::UTF8);
218  tag->addFrame(tpe2frame);
219  }
220  tpe2frame->setText(QStringToTString(mdata->CompilationArtist()));
221  }
222 
223  return SaveFile();
224 }
225 
230 {
231  if (!OpenFile(filename))
232  return nullptr;
233 
234  TagLib::ID3v2::Tag *tag = GetID3v2Tag(true); // Create tag if none are found
235 
236  // if there is no ID3v2 tag, try to read the ID3v1 tag and copy it to
237  // the ID3v2 tag structure
238  if (tag->isEmpty())
239  {
240  TagLib::ID3v1::Tag *tag_v1 = GetID3v1Tag();
241 
242  if (!tag_v1)
243  return nullptr;
244 
245  if (!tag_v1->isEmpty())
246  {
247  tag->setTitle(tag_v1->title());
248  tag->setArtist(tag_v1->artist());
249  tag->setAlbum(tag_v1->album());
250  tag->setTrack(tag_v1->track());
251  tag->setYear(tag_v1->year());
252  tag->setGenre(tag_v1->genre());
253  }
254  }
255 
256  auto *metadata = new MusicMetadata(filename);
257 
258  ReadGenericMetadata(tag, metadata);
259 
260  bool compilation = false;
261 
262  // Compilation Artist (TPE4 Remix) or fallback to (TPE2 Band)
263  // N.B. The existance of a either frame is NOT an indication that this
264  // is a compilation, but if it is then one of them will probably hold
265  // the compilation artist.
266  TextIdentificationFrame *tpeframe = nullptr;
267  TagLib::ID3v2::FrameList tpelist = tag->frameListMap()["TPE4"];
268  if (tpelist.isEmpty() || tpelist.front()->toString().isEmpty())
269  tpelist = tag->frameListMap()["TPE2"];
270  if (!tpelist.isEmpty())
271  tpeframe = dynamic_cast<TextIdentificationFrame *>(tpelist.front());
272 
273  if (tpeframe && !tpeframe->toString().isEmpty())
274  {
275  QString compilation_artist = TStringToQString(tpeframe->toString())
276  .trimmed();
277  metadata->setCompilationArtist(compilation_artist);
278  }
279 
280  // Rating and playcount, stored in POPM frame
281  PopularimeterFrame *popm = findPOPM(tag, ""); // Global (all apps) tag
282 
283  // If no 'global' tag exists, look for the MythTV specific one
284  if (!popm)
285  {
286  popm = findPOPM(tag, email);
287  }
288 
289  // Fallback to using any POPM tag we can find
290  if (!popm)
291  {
292  if (!tag->frameListMap()["POPM"].isEmpty())
293  popm = dynamic_cast<PopularimeterFrame *>
294  (tag->frameListMap()["POPM"].front());
295  }
296 
297  if (popm)
298  {
299  int rating = popm->rating();
300  rating = lroundf(static_cast<float>(rating) / 255.0F * 10.0F);
301  metadata->setRating(rating);
302  metadata->setPlaycount(popm->counter());
303  }
304 
305  // Look for MusicBrainz Album+Artist ID in TXXX Frame
306  UserTextIdentificationFrame *musicbrainz = find(tag,
307  "MusicBrainz Album Artist Id");
308 
309  if (musicbrainz)
310  {
311  // If the MusicBrainz ID is the special "Various Artists" ID
312  // then compilation is TRUE
313  if (!compilation && !musicbrainz->fieldList().isEmpty())
314  {
315  for (auto & field : musicbrainz->fieldList())
316  {
317  QString ID = TStringToQString(field);
318 
320  {
321  compilation = true;
322  break;
323  }
324  }
325  }
326  }
327 
328  // TLEN - Ignored intentionally, some encoders write bad values
329  // e.g. Lame under certain circumstances will always write a length of
330  // 27 hours
331 
332  // Length
333  if (!tag->frameListMap()["TLEN"].isEmpty())
334  {
335  int length = tag->frameListMap()["TLEN"].front()->toString().toInt();
336  LOG(VB_FILE, LOG_DEBUG,
337  QString("MetaIOID3::read: Length for '%1' from tag is '%2'\n").arg(filename).arg(length));
338  }
339 
340  metadata->setCompilation(compilation);
341 
342  metadata->setLength(getTrackLength(m_file));
343 
344  // The number of tracks on the album, if supplied
345  if (!tag->frameListMap()["TRCK"].isEmpty())
346  {
347  QString trackFrame = TStringToQString(
348  tag->frameListMap()["TRCK"].front()->toString())
349  .trimmed();
350  int trackCount = trackFrame.section('/', -1).toInt();
351  if (trackCount > 0)
352  metadata->setTrackCount(trackCount);
353  }
354 
355  LOG(VB_FILE, LOG_DEBUG,
356  QString("MetaIOID3::read: Length for '%1' from properties is '%2'\n").arg(filename).arg(metadata->Length()));
357 
358  // Look for MythTVLastPlayed in TXXX Frame
359  UserTextIdentificationFrame *lastplayed = find(tag, "MythTVLastPlayed");
360  if (lastplayed)
361  {
362  QString lastPlayStr = TStringToQString(lastplayed->toString());
363  metadata->setLastPlay(QDateTime::fromString(lastPlayStr, Qt::ISODate));
364  }
365 
366  // Part of a set
367  if (!tag->frameListMap()["TPOS"].isEmpty())
368  {
369  QString pos = TStringToQString(
370  tag->frameListMap()["TPOS"].front()->toString()).trimmed();
371 
372  int discNumber = pos.section('/', 0, 0).toInt();
373  int discCount = pos.section('/', -1).toInt();
374 
375  if (discNumber > 0)
376  metadata->setDiscNumber(discNumber);
377  if (discCount > 0)
378  metadata->setDiscCount(discCount);
379  }
380 
381  return metadata;
382 }
383 
392 {
393  auto *picture = new QImage();
394 
395  AttachedPictureFrame::Type apicType
396  = AttachedPictureFrame::FrontCover;
397 
398  switch (type)
399  {
400  case IT_UNKNOWN :
401  apicType = AttachedPictureFrame::Other;
402  break;
403  case IT_FRONTCOVER :
404  apicType = AttachedPictureFrame::FrontCover;
405  break;
406  case IT_BACKCOVER :
407  apicType = AttachedPictureFrame::BackCover;
408  break;
409  case IT_CD :
410  apicType = AttachedPictureFrame::Media;
411  break;
412  case IT_INLAY :
413  apicType = AttachedPictureFrame::LeafletPage;
414  break;
415  case IT_ARTIST :
416  apicType = AttachedPictureFrame::Artist;
417  break;
418  default:
419  return picture;
420  }
421 
422  if (OpenFile(filename))
423  {
424  TagLib::ID3v2::Tag *tag = GetID3v2Tag();
425  if (tag && !tag->frameListMap()["APIC"].isEmpty())
426  {
427  TagLib::ID3v2::FrameList apicframes = tag->frameListMap()["APIC"];
428 
429  for (auto & apicframe : apicframes)
430  {
431  auto *frame = dynamic_cast<AttachedPictureFrame *>(apicframe);
432  if (frame && frame->type() == apicType)
433  {
434  picture->loadFromData((const uchar *)frame->picture().data(),
435  frame->picture().size());
436  return picture;
437  }
438  }
439  }
440  }
441 
442  delete picture;
443 
444  return nullptr;
445 }
446 
447 
454 {
455  AlbumArtList imageList;
456  if (OpenFile(filename))
457  {
458  TagLib::ID3v2::Tag *tag = GetID3v2Tag();
459 
460  if (!tag)
461  return imageList;
462 
463  imageList = readAlbumArt(tag);
464  }
465 
466  return imageList;
467 }
468 
476 AlbumArtList MetaIOID3::readAlbumArt(TagLib::ID3v2::Tag *tag)
477 {
478  AlbumArtList artlist;
479 
480  if (!tag->frameListMap()["APIC"].isEmpty())
481  {
482  TagLib::ID3v2::FrameList apicframes = tag->frameListMap()["APIC"];
483 
484  for (auto & apicframe : apicframes)
485  {
486  auto *frame = dynamic_cast<AttachedPictureFrame *>(apicframe);
487  if (frame == nullptr)
488  {
489  LOG(VB_GENERAL, LOG_DEBUG,
490  "Music Scanner - Cannot convert APIC frame");
491  continue;
492  }
493 
494  // Assume a valid image would have at least
495  // 100 bytes of data (1x1 indexed gif is 35 bytes)
496  if (frame->picture().size() < 100)
497  {
498  LOG(VB_GENERAL, LOG_NOTICE,
499  "Music Scanner - Discarding APIC frame "
500  "with size less than 100 bytes");
501  continue;
502  }
503 
504  auto *art = new AlbumArtImage();
505 
506  if (frame->description().isEmpty())
507  art->m_description.clear();
508  else
509  art->m_description = TStringToQString(frame->description());
510 
511  art->m_embedded = true;
512  art->m_hostname = gCoreContext->GetHostName();
513 
514  QString ext = getExtFromMimeType(
515  TStringToQString(frame->mimeType()).toLower());
516 
517  switch (frame->type())
518  {
519  case AttachedPictureFrame::FrontCover :
520  art->m_imageType = IT_FRONTCOVER;
521  art->m_filename = QString("front") + ext;
522  break;
523  case AttachedPictureFrame::BackCover :
524  art->m_imageType = IT_BACKCOVER;
525  art->m_filename = QString("back") + ext;
526  break;
527  case AttachedPictureFrame::Media :
528  art->m_imageType = IT_CD;
529  art->m_filename = QString("cd") + ext;
530  break;
531  case AttachedPictureFrame::LeafletPage :
532  art->m_imageType = IT_INLAY;
533  art->m_filename = QString("inlay") + ext;
534  break;
535  case AttachedPictureFrame::Artist :
536  art->m_imageType = IT_ARTIST;
537  art->m_filename = QString("artist") + ext;
538  break;
539  case AttachedPictureFrame::Other :
540  art->m_imageType = IT_UNKNOWN;
541  art->m_filename = QString("unknown") + ext;
542  break;
543  default:
544  LOG(VB_GENERAL, LOG_ERR, "Music Scanner - APIC tag found "
545  "with unsupported type");
546  delete art;
547  continue;
548  }
549 
550  artlist.append(art);
551  }
552  }
553 
554  return artlist;
555 }
556 
557 QString MetaIOID3::getExtFromMimeType(const QString &mimeType)
558 {
559  if (mimeType == "image/png")
560  return QString(".png");
561  if (mimeType == "image/jpeg" || mimeType == "image/jpg")
562  return QString(".jpg");
563  if (mimeType == "image/gif")
564  return QString(".gif");
565  if (mimeType == "image/bmp")
566  return QString(".bmp");
567 
568  LOG(VB_GENERAL, LOG_ERR,
569  "Music Scanner - Unknown image mimetype found - " + mimeType);
570 
571  return QString();
572 }
573 
582 AttachedPictureFrame* MetaIOID3::findAPIC(TagLib::ID3v2::Tag *tag,
583  const AttachedPictureFrame::Type &type,
584  const String &description)
585 {
586  TagLib::ID3v2::FrameList l = tag->frameList("APIC");
587  for (auto & frame : l)
588  {
589  auto *f = dynamic_cast<AttachedPictureFrame *>(frame);
590  if (f && f->type() == type &&
591  (description.isNull() || f->description() == description))
592  return f;
593  }
594  return nullptr;
595 }
596 
606 bool MetaIOID3::writeAlbumArt(const QString &filename,
607  const AlbumArtImage *albumart)
608 {
609  if (filename.isEmpty() || !albumart)
610  return false;
611 
612  // load the image into a QByteArray
613  QImage image(albumart->m_filename);
614  QByteArray imageData;
615  QBuffer buffer(&imageData);
616  buffer.open(QIODevice::WriteOnly);
617  image.save(&buffer, "JPEG");
618 
619  AttachedPictureFrame::Type type = AttachedPictureFrame::Other;
620  switch (albumart->m_imageType)
621  {
622  case IT_FRONTCOVER:
623  type = AttachedPictureFrame::FrontCover;
624  break;
625  case IT_BACKCOVER:
626  type = AttachedPictureFrame::BackCover;
627  break;
628  case IT_CD:
629  type = AttachedPictureFrame::Media;
630  break;
631  case IT_INLAY:
632  type = AttachedPictureFrame::LeafletPage;
633  break;
634  case IT_ARTIST:
635  type = AttachedPictureFrame::Artist;
636  break;
637  default:
638  type = AttachedPictureFrame::Other;
639  break;
640  }
641 
642  if (!OpenFile(filename, true))
643  return false;
644 
645  TagLib::ID3v2::Tag *tag = GetID3v2Tag();
646 
647  if (!tag)
648  return false;
649 
650  AttachedPictureFrame *apic = findAPIC(tag, type,
651  QStringToTString(albumart->m_description));
652 
653  if (!apic)
654  {
655  apic = new AttachedPictureFrame();
656  tag->addFrame(apic);
657  apic->setType(type);
658  }
659 
660  QString mimetype = "image/jpeg";
661 
662  TagLib::ByteVector bytevector;
663  bytevector.setData(imageData.data(), imageData.size());
664 
665  apic->setMimeType(QStringToTString(mimetype));
666  apic->setPicture(bytevector);
667  apic->setDescription(QStringToTString(albumart->m_description));
668 
669  return SaveFile();
670 }
671 
680  const AlbumArtImage *albumart)
681 {
682  if (filename.isEmpty() || !albumart)
683  return false;
684 
685  AttachedPictureFrame::Type type = AttachedPictureFrame::Other;
686  switch (albumart->m_imageType)
687  {
688  case IT_FRONTCOVER:
689  type = AttachedPictureFrame::FrontCover;
690  break;
691  case IT_BACKCOVER:
692  type = AttachedPictureFrame::BackCover;
693  break;
694  case IT_CD:
695  type = AttachedPictureFrame::Media;
696  break;
697  case IT_INLAY:
698  type = AttachedPictureFrame::LeafletPage;
699  break;
700  case IT_ARTIST:
701  type = AttachedPictureFrame::Artist;
702  break;
703  default:
704  type = AttachedPictureFrame::Other;
705  break;
706  }
707 
708  if (!OpenFile(filename, true))
709  return false;
710 
711  TagLib::ID3v2::Tag *tag = GetID3v2Tag();
712 
713  if (!tag)
714  return false;
715 
716  AttachedPictureFrame *apic = findAPIC(tag, type,
717  QStringToTString(albumart->m_description));
718  if (!apic)
719  return false;
720 
721  tag->removeFrame(apic);
722 
723  return SaveFile();
724 }
725 
727  const AlbumArtImage* albumart,
728  ImageType newType)
729 {
730  if (!albumart)
731  return false;
732 
733  if (albumart->m_imageType == newType)
734  return true;
735 
736  AttachedPictureFrame::Type type = AttachedPictureFrame::Other;
737  switch (albumart->m_imageType)
738  {
739  case IT_FRONTCOVER:
740  type = AttachedPictureFrame::FrontCover;
741  break;
742  case IT_BACKCOVER:
743  type = AttachedPictureFrame::BackCover;
744  break;
745  case IT_CD:
746  type = AttachedPictureFrame::Media;
747  break;
748  case IT_INLAY:
749  type = AttachedPictureFrame::LeafletPage;
750  break;
751  case IT_ARTIST:
752  type = AttachedPictureFrame::Artist;
753  break;
754  default:
755  type = AttachedPictureFrame::Other;
756  break;
757  }
758 
759  if (!OpenFile(filename, true))
760  return false;
761 
762  TagLib::ID3v2::Tag *tag = GetID3v2Tag();
763 
764  if (!tag)
765  return false;
766 
767  AttachedPictureFrame *apic = findAPIC(tag, type,
768  QStringToTString(albumart->m_description));
769  if (!apic)
770  return false;
771 
772  // set the new image type
773  switch (newType)
774  {
775  case IT_FRONTCOVER:
776  apic->setType(AttachedPictureFrame::FrontCover);
777  break;
778  case IT_BACKCOVER:
779  apic->setType(AttachedPictureFrame::BackCover);
780  break;
781  case IT_CD:
782  apic->setType(AttachedPictureFrame::Media);
783  break;
784  case IT_INLAY:
785  apic->setType(AttachedPictureFrame::LeafletPage);
786  break;
787  case IT_ARTIST:
788  apic->setType(AttachedPictureFrame::Artist);
789  break;
790  default:
791  apic->setType(AttachedPictureFrame::Other);
792  break;
793  }
794 
795  return SaveFile();
796 }
797 
808 UserTextIdentificationFrame* MetaIOID3::find(TagLib::ID3v2::Tag *tag,
809  const String &description)
810 {
811  TagLib::ID3v2::FrameList l = tag->frameList("TXXX");
812  for (auto & frame : l)
813  {
814  auto *f = dynamic_cast<UserTextIdentificationFrame *>(frame);
815  if (f && f->description() == description)
816  return f;
817  }
818  return nullptr;
819 }
820 
828 PopularimeterFrame* MetaIOID3::findPOPM(TagLib::ID3v2::Tag *tag,
829  const String &_email)
830 {
831  TagLib::ID3v2::FrameList l = tag->frameList("POPM");
832  for (auto & frame : l)
833  {
834  auto *f = dynamic_cast<PopularimeterFrame *>(frame);
835  if (f && f->email() == _email)
836  return f;
837  }
838  return nullptr;
839 }
840 
841 bool MetaIOID3::writePlayCount(TagLib::ID3v2::Tag *tag, int playcount)
842 {
843  if (!tag)
844  return false;
845 
846  // MythTV Specific playcount Tag
847  PopularimeterFrame *popm = findPOPM(tag, email);
848 
849  if (!popm)
850  {
851  popm = new PopularimeterFrame();
852  tag->addFrame(popm);
853  popm->setEmail(email);
854  }
855 
856  int prevCount = popm->counter();
857  int countDiff = playcount - prevCount;
858  // Allow for situations where the user has rolled back to an old DB backup
859  if (countDiff > 0)
860  {
861  popm->setCounter(playcount);
862 
863  // Global playcount Tag - Updated by all apps/hardware that support it
864  PopularimeterFrame *gpopm = findPOPM(tag, "");
865  if (!gpopm)
866  {
867  gpopm = new PopularimeterFrame();
868  tag->addFrame(gpopm);
869  gpopm->setEmail("");
870  }
871  gpopm->setCounter((gpopm->counter() > 0) ? gpopm->counter() + countDiff : playcount);
872  }
873 
874  return true;
875 }
876 
878 {
879  if (filename.isEmpty())
880  return false;
881 
882  int rating = mdata->Rating();
883  int playcount = mdata->PlayCount();
884  QDateTime lastPlay = mdata->LastPlay();
885 
886  if (!OpenFile(filename, true))
887  return false;
888 
889  TagLib::ID3v2::Tag *tag = GetID3v2Tag();
890 
891  if (!tag)
892  return false;
893 
894  bool result = (writeRating(tag, rating) && writePlayCount(tag, playcount) &&
895  writeLastPlay(tag, lastPlay));
896 
897  if (!SaveFile())
898  return false;
899 
900  return result;
901 }
902 
903 bool MetaIOID3::writeRating(TagLib::ID3v2::Tag *tag, int rating)
904 {
905  if (!tag)
906  return false;
907 
908  int popmrating = lroundf(static_cast<float>(rating) / 10.0F * 255.0F);
909 
910  // MythTV Specific Rating Tag
911  PopularimeterFrame *popm = findPOPM(tag, email);
912 
913  if (!popm)
914  {
915  popm = new PopularimeterFrame();
916  tag->addFrame(popm);
917  popm->setEmail(email);
918  }
919  popm->setRating(popmrating);
920 
921  // Global Rating Tag
922  PopularimeterFrame *gpopm = findPOPM(tag, "");
923  if (!gpopm)
924  {
925  gpopm = new PopularimeterFrame();
926  tag->addFrame(gpopm);
927  gpopm->setEmail("");
928  }
929  gpopm->setRating(popmrating);
930 
931  return true;
932 }
933 
934 bool MetaIOID3::writeLastPlay(TagLib::ID3v2::Tag *tag, QDateTime lastPlay)
935 {
936  if (!tag)
937  return false;
938 
939  // MythTV Specific Rating Tag
940  UserTextIdentificationFrame *txxx = find(tag, "MythTVLastPlayed");
941 
942  if (!txxx)
943  {
944  txxx = new UserTextIdentificationFrame();
945  tag->addFrame(txxx);
946  txxx->setDescription("MythTVLastPlayed");
947  }
948  lastPlay.setTimeSpec(Qt::UTC);
949  txxx->setText(QStringToTString(lastPlay.toString(Qt::ISODate)));
950 
951  return true;
952 }
953 
954 bool MetaIOID3::TagExists(const QString &filename)
955 {
956  if (!OpenFile(filename))
957  return false;
958 
959  TagLib::ID3v1::Tag *v1_tag = GetID3v1Tag();
960  TagLib::ID3v2::Tag *v2_tag = GetID3v2Tag();
961 
962  bool retval = false;
963 
964  if ((v2_tag && !v2_tag->isEmpty()) ||
965  (v1_tag && !v1_tag->isEmpty()))
966  retval = true;
967 
968  return retval;
969 }
AlbumArtImage::m_imageType
ImageType m_imageType
Definition: musicmetadata.h:51
MetaIOTagLib::WriteGenericMetadata
static void WriteGenericMetadata(TagLib::Tag *tag, const MusicMetadata *metadata)
Writes metadata common to all tag formats to the tag.
Definition: metaiotaglib.cpp:24
MetaIOID3::GetID3v1Tag
TagLib::ID3v1::Tag * GetID3v1Tag(bool create=false)
Definition: metaioid3.cpp:136
MetaIOID3::kFLAC
@ kFLAC
Definition: metaioid3.h:79
MetaIOID3::kMPEG
@ kMPEG
Definition: metaioid3.h:79
MusicMetadata::CompilationArtist
QString CompilationArtist() const
Definition: musicmetadata.h:138
MetaIOID3::writeAlbumArt
bool writeAlbumArt(const QString &filename, const AlbumArtImage *albumart) override
Write the albumart image to the file.
Definition: metaioid3.cpp:606
arg
arg(title).arg(filename).arg(doDelete))
FrameList
QList< MPEG2frame * > FrameList
Definition: mpeg2fix.h:113
MYTH_MUSICBRAINZ_ALBUMARTIST_UUID
#define MYTH_MUSICBRAINZ_ALBUMARTIST_UUID
Definition: metaio.h:15
MetaIO::m_filename
QString m_filename
Definition: metaio.h:181
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
IT_BACKCOVER
@ IT_BACKCOVER
Definition: musicmetadata.h:32
MusicMetadata
Definition: musicmetadata.h:81
build_compdb.file
file
Definition: build_compdb.py:55
MetaIOID3::find
static UserTextIdentificationFrame * find(TagLib::ID3v2::Tag *tag, const String &description)
Find the a custom comment tag by description. This is a copy of the same function in the TagLib::ID3v...
Definition: metaioid3.cpp:808
MetaIOID3::write
bool write(const QString &filename, MusicMetadata *mdata) override
Writes all metadata back to a file.
Definition: metaioid3.cpp:153
musicutils.h
MetaIOID3::getAlbumArt
QImage * getAlbumArt(const QString &filename, ImageType type) override
Read the albumart image from the file.
Definition: metaioid3.cpp:391
MetaIOID3::writeLastPlay
static bool writeLastPlay(TagLib::ID3v2::Tag *tag, QDateTime lastPlay)
Definition: metaioid3.cpp:934
MetaIOID3::readAlbumArt
static AlbumArtList readAlbumArt(TagLib::ID3v2::Tag *tag)
Read the albumart images from the file.
Definition: metaioid3.cpp:476
MetaIOTagLib::ReadGenericMetadata
void ReadGenericMetadata(TagLib::Tag *tag, MusicMetadata *metadata)
Writes metadata common to all tag formats to the tag.
Definition: metaiotaglib.cpp:54
MetaIO::restoreTimeStamps
void restoreTimeStamps(void)
Definition: metaio.cpp:231
mythlogging.h
hardwareprofile.scan.rating
def rating(profile, smoonURL, gate)
Definition: scan.py:39
MetaIOID3::removeAlbumArt
bool removeAlbumArt(const QString &filename, const AlbumArtImage *albumart) override
Remove the albumart image from the file.
Definition: metaioid3.cpp:679
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:634
MetaIOID3::TagExists
bool TagExists(const QString &filename) override
Definition: metaioid3.cpp:954
MetaIOID3::findPOPM
static PopularimeterFrame * findPOPM(TagLib::ID3v2::Tag *tag, const String &email)
Find the POPM tag associated with MythTV.
Definition: metaioid3.cpp:828
f
QTextStream t & f
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:600
MetaIOID3::read
MusicMetadata * read(const QString &filename) override
Reads MusicMetadata from a file.
Definition: metaioid3.cpp:229
QStringToTString
#define QStringToTString(s)
Definition: musicutils.h:14
IT_ARTIST
@ IT_ARTIST
Definition: musicmetadata.h:35
MetaIOID3::findAPIC
static AttachedPictureFrame * findAPIC(TagLib::ID3v2::Tag *tag, const AttachedPictureFrame::Type &type, const String &description=String::null)
Find an APIC tag by type and optionally description.
Definition: metaioid3.cpp:582
IT_CD
@ IT_CD
Definition: musicmetadata.h:33
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:57
MetaIOID3::getExtFromMimeType
static QString getExtFromMimeType(const QString &mimeType)
Definition: metaioid3.cpp:557
MetaIOID3::writeVolatileMetadata
bool writeVolatileMetadata(const QString &filename, MusicMetadata *mdata) override
Writes rating and playcount back to a file.
Definition: metaioid3.cpp:877
MetaIOID3::getAlbumArtList
AlbumArtList getAlbumArtList(const QString &filename) override
Read the albumart images from the file.
Definition: metaioid3.cpp:453
MythDate::fromString
QDateTime fromString(const QString &dtstr)
Converts kFilename && kISODate formats to QDateTime.
Definition: mythdate.cpp:30
MetaIOID3::m_file
TagLib::File * m_file
Definition: metaioid3.h:77
MetaIOID3::writePlayCount
static bool writePlayCount(TagLib::ID3v2::Tag *tag, int playcount)
Definition: metaioid3.cpp:841
IT_UNKNOWN
@ IT_UNKNOWN
Definition: musicmetadata.h:30
TStringToQString
#define TStringToQString(s)
Definition: musicutils.h:16
mythcorecontext.h
AlbumArtList
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:56
MetaIOTagLib::getTrackLength
static int getTrackLength(TagLib::File *file)
Find the length of the track (in seconds)
Definition: metaiotaglib.cpp:89
MetaIOID3::GetID3v2Tag
TagLib::ID3v2::Tag * GetID3v2Tag(bool create=false)
Definition: metaioid3.cpp:116
MythDate::ISODate
@ ISODate
Default UTC.
Definition: mythdate.h:14
MusicMetadata::LastPlay
QDateTime LastPlay() const
Definition: musicmetadata.h:243
MetaIOID3::writeRating
static bool writeRating(TagLib::ID3v2::Tag *tag, int rating)
Definition: metaioid3.cpp:903
AlbumArtImage
Definition: musicmetadata.h:40
MusicMetadata::Compilation
bool Compilation() const
Definition: musicmetadata.h:251
MetaIOID3::m_fileType
TagType m_fileType
Definition: metaioid3.h:80
MusicMetadata::PlayCount
int PlayCount() const
Definition: musicmetadata.h:247
metaioid3.h
IT_FRONTCOVER
@ IT_FRONTCOVER
Definition: musicmetadata.h:31
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:856
MusicMetadata::Rating
int Rating() const
Definition: musicmetadata.h:238
MetaIOID3::CloseFile
void CloseFile()
Definition: metaioid3.cpp:107
MetaIOID3::changeImageType
bool changeImageType(const QString &filename, const AlbumArtImage *albumart, ImageType newType) override
Definition: metaioid3.cpp:726
email
const String email
Definition: metaioid3.cpp:22
IT_INLAY
@ IT_INLAY
Definition: musicmetadata.h:34
AlbumArtImage::m_description
QString m_description
Definition: musicmetadata.h:52
MetaIOID3::SaveFile
bool SaveFile()
Definition: metaioid3.cpp:95
ImageType
ImageType
Definition: musicmetadata.h:29
musicmetadata.h
MetaIOID3::OpenFile
bool OpenFile(const QString &filename, bool forWriting=false)
Open the file to read the tag.
Definition: metaioid3.cpp:31
MetaIO::saveTimeStamps
void saveTimeStamps(void)
Definition: metaio.cpp:222
AlbumArtImage::m_filename
QString m_filename
Definition: musicmetadata.h:49