MythTV  master
metaioflacvorbis.cpp
Go to the documentation of this file.
1 
2 // Libmyth
3 #include "libmyth/mythcontext.h"
4 
5 // qt
6 #include <QBuffer>
7 
8 // Taglib
9 #include <xiphcomment.h>
10 
11 // libmythmetadata
12 #include "metaioflacvorbis.h"
13 #include "musicmetadata.h"
14 #include "musicutils.h"
15 
22 TagLib::FLAC::File *MetaIOFLACVorbis::OpenFile(const QString &filename)
23 {
24  QByteArray fname = filename.toLocal8Bit();
25  auto *flacfile = new TagLib::FLAC::File(fname.constData());
26 
27  if (!flacfile->isOpen())
28  {
29  delete flacfile;
30  flacfile = nullptr;
31  }
32 
33  return flacfile;
34 }
35 
36 
40 bool MetaIOFLACVorbis::write(const QString &filename, MusicMetadata* mdata)
41 {
42  if (!mdata)
43  return false;
44 
45  if (filename.isEmpty())
46  return false;
47 
49 
50  TagLib::FLAC::File *flacfile = OpenFile(m_filename);
51 
52  if (!flacfile)
53  return false;
54 
55  TagLib::Ogg::XiphComment *tag = flacfile->xiphComment(true);
56 
57  if (!tag)
58  {
59  delete flacfile;
60  return false;
61  }
62 
63  WriteGenericMetadata(tag, mdata);
64 
65  // Compilation
66  if (mdata->Compilation())
67  {
68  tag->addField("MUSICBRAINZ_ALBUMARTISTID",
70  tag->addField("COMPILATION_ARTIST",
71  QStringToTString(mdata->CompilationArtist()), true);
72  }
73  else
74  {
75  // Don't remove the musicbrainz field unless it indicated a compilation
76  if (tag->contains("MUSICBRAINZ_ALBUMARTISTID") &&
77  (tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString() ==
79  {
80  tag->removeFields("MUSICBRAINZ_ALBUMARTISTID");
81  }
82  tag->removeFields("COMPILATION_ARTIST");
83  }
84 
86  bool result = flacfile->save();
88 
89  delete flacfile;
90 
91  return (result);
92 }
93 
98 {
99  TagLib::FLAC::File *flacfile = OpenFile(filename);
100 
101  if (!flacfile)
102  return nullptr;
103 
104  TagLib::Ogg::XiphComment *tag = flacfile->xiphComment();
105 
106  if (!tag)
107  {
108  delete flacfile;
109  return nullptr;
110  }
111 
112  auto *metadata = new MusicMetadata(filename);
113 
114  ReadGenericMetadata(tag, metadata);
115 
116  bool compilation = false;
117 
118  if (tag->contains("COMPILATION_ARTIST"))
119  {
120  QString compilation_artist = TStringToQString(
121  tag->fieldListMap()["COMPILATION_ARTIST"].toString()).trimmed();
122  if (compilation_artist != metadata->Artist())
123  {
124  metadata->setCompilationArtist(compilation_artist);
125  compilation = true;
126  }
127  }
128  else if (tag->contains("ALBUMARTIST"))
129  {
130  QString compilation_artist = TStringToQString(
131  tag->fieldListMap()["ALBUMARTIST"].toString()).trimmed();
132  if (compilation_artist != metadata->Artist())
133  {
134  metadata->setCompilationArtist(compilation_artist);
135  compilation = true;
136  }
137  }
138 
139  if (!compilation && tag->contains("MUSICBRAINZ_ALBUMARTISTID"))
140  {
141  QString musicbrainzcode = TStringToQString(
142  tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString()).trimmed();
143  if (musicbrainzcode == MYTH_MUSICBRAINZ_ALBUMARTIST_UUID)
144  compilation = true;
145  }
146 
147  metadata->setCompilation(compilation);
148 
149  if (metadata->Length() <= 0ms)
150  metadata->setLength(getTrackLength(flacfile));
151 
152  if (tag->contains("DISCNUMBER"))
153  {
154  bool valid = false;
155  int n = tag->fieldListMap()["DISCNUMBER"].toString().toInt(&valid);
156  if (valid)
157  metadata->setDiscNumber(n);
158  }
159 
160  if (tag->contains("TOTALTRACKS"))
161  {
162  bool valid = false;
163  int n = tag->fieldListMap()["TOTALTRACKS"].toString().toInt(&valid);
164  if (valid)
165  metadata->setTrackCount(n);
166  }
167 
168  if (tag->contains("TOTALDISCS"))
169  {
170  bool valid = false;
171  int n = tag->fieldListMap()["TOTALDISCS"].toString().toInt(&valid);
172  if (valid)
173  metadata->setDiscCount(n);
174  }
175 
176  delete flacfile;
177 
178  return metadata;
179 }
180 
189 {
190  auto *picture = new QImage();
191  TagLib::FLAC::File * flacfile = OpenFile(filename);
192 
193  if (flacfile)
194  {
195  TagLib::FLAC::Picture* pic = getPictureFromFile(flacfile, type);
196  if (pic)
197  {
198  picture->loadFromData((const uchar *)pic->data().data(),
199  pic->data().size());
200  }
201  else
202  {
203  delete picture;
204  return nullptr;
205  }
206 
207  delete flacfile;
208  }
209 
210  return picture;
211 }
212 
214  TagLib::FLAC::File *flacfile,
215  ImageType type)
216 {
217  using TagLib::FLAC::Picture;
218 
219  Picture *pic = nullptr;
220 
221  if (flacfile)
222  {
223  Picture::Type artType = PictureTypeFromImageType(type);
224 
225  // From what I can tell, FLAC::File maintains ownership of the Picture pointers, so no need to delete
226  const TagLib::List<Picture *>& picList = flacfile->pictureList();
227 
228  for (auto *entry : picList)
229  {
230  if (entry->type() == artType)
231  {
232  //found the type we were looking for
233  pic = entry;
234  break;
235  }
236  }
237  }
238 
239  return pic;
240 }
241 
242 TagLib::FLAC::Picture::Type MetaIOFLACVorbis::PictureTypeFromImageType(
243  ImageType itype) {
244  using TagLib::FLAC::Picture;
245  Picture::Type artType = Picture::Other;
246  switch (itype)
247  {
248  case IT_UNKNOWN :
249  artType = Picture::Other;
250  break;
251  case IT_FRONTCOVER :
252  artType = Picture::FrontCover;
253  break;
254  case IT_BACKCOVER :
255  artType = Picture::BackCover;
256  break;
257  case IT_CD :
258  artType = Picture::Media;
259  break;
260  case IT_INLAY :
261  artType = Picture::LeafletPage;
262  break;
263  case IT_ARTIST :
264  artType = Picture::Artist;
265  break;
266  default:
267  return Picture::Other;
268  }
269 
270  return artType;
271 }
272 
279 {
280  using TagLib::FLAC::Picture;
281  AlbumArtList artlist;
282  TagLib::FLAC::File * flacfile = OpenFile(filename);
283 
284  if (flacfile)
285  {
286  const TagLib::List<Picture *>& picList = flacfile->pictureList();
287 
288  for (auto *pic : picList)
289  {
290  // Assume a valid image would have at least
291  // 100 bytes of data (1x1 indexed gif is 35 bytes)
292  if (pic->data().size() < 100)
293  {
294  LOG(VB_GENERAL, LOG_NOTICE,
295  "Music Scanner - Discarding picture "
296  "with size less than 100 bytes");
297  continue;
298  }
299 
300  auto *art = new AlbumArtImage();
301 
302  if (pic->description().isEmpty())
303  art->m_description.clear();
304  else
305  art->m_description = TStringToQString(pic->description());
306 
307  art->m_embedded = true;
308  art->m_hostname = gCoreContext->GetHostName();
309 
310  QString ext = getExtFromMimeType(
311  TStringToQString(pic->mimeType()).toLower());
312 
313  switch (pic->type())
314  {
315  case Picture::FrontCover :
316  art->m_imageType = IT_FRONTCOVER;
317  art->m_filename = QString("front") + ext;
318  break;
319  case Picture::BackCover :
320  art->m_imageType = IT_BACKCOVER;
321  art->m_filename = QString("back") + ext;
322  break;
323  case Picture::Media :
324  art->m_imageType = IT_CD;
325  art->m_filename = QString("cd") + ext;
326  break;
327  case Picture::LeafletPage :
328  art->m_imageType = IT_INLAY;
329  art->m_filename = QString("inlay") + ext;
330  break;
331  case Picture::Artist :
332  art->m_imageType = IT_ARTIST;
333  art->m_filename = QString("artist") + ext;
334  break;
335  case Picture::Other :
336  art->m_imageType = IT_UNKNOWN;
337  art->m_filename = QString("unknown") + ext;
338  break;
339  default:
340  LOG(VB_GENERAL, LOG_ERR, "Music Scanner - picture found "
341  "with unsupported type");
342  delete art;
343  continue;
344  }
345 
346  artlist.append(art);
347  }
348  }
349 
350  delete flacfile;
351  return artlist;
352 }
353 
364  const AlbumArtImage *albumart)
365 {
366 #if TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 8
367  using TagLib::FLAC::Picture;
368  if (filename.isEmpty() || !albumart)
369  return false;
370 
372 
373  bool retval = false;
374 
375  // load the image into a QByteArray
376  QImage image(albumart->m_filename);
377  QByteArray imageData;
378  QBuffer buffer(&imageData);
379  buffer.open(QIODevice::WriteOnly);
380  // Write the image data to a file
381  image.save(&buffer, "JPEG");
382 
383  TagLib::FLAC::File * flacfile = OpenFile(filename);
384 
385  // This presumes that there is only one art item of each type
386  if (flacfile)
387  {
388  // Now see if the art is in the FLAC file
389  Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
390 
391  if (pic)
392  {
393  // Remove the embedded image of the matching type
394  flacfile->removePicture(pic, false);
395  }
396  else
397  {
398  // Create a new image of the correct type
399  pic = new Picture();
400  pic->setType(PictureTypeFromImageType(albumart->m_imageType));
401  }
402 
403  TagLib::ByteVector bytevector;
404  bytevector.setData(imageData.data(), imageData.size());
405 
406  pic->setData(bytevector);
407  QString mimetype = "image/jpeg";
408 
409  pic->setMimeType(QStringToTString(mimetype));
410  pic->setDescription(QStringToTString(albumart->m_description));
411 
412  flacfile->addPicture(pic);
413 
414  saveTimeStamps();
415  retval = flacfile->save();
417 
418  delete flacfile;
419  }
420  else
421  {
422  retval = false;
423  }
424 
425  return retval;
426 #else
427  LOG(VB_GENERAL, LOG_WARNING,
428  "TagLib 1.8.0 or later is required to write albumart to flac xiphComment tags");
429  return false;
430 #endif
431 }
432 
441  const AlbumArtImage *albumart)
442 {
443 #if TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 8
444 
445  if (filename.isEmpty() || !albumart)
446  return false;
447 
448  bool retval = false;
449 
450  TagLib::FLAC::File * flacfile = OpenFile(filename);
451 
452  // This presumes that there is only one art item of each type
453  if (flacfile)
454  {
455  // Now see if the art is in the FLAC file
456  TagLib::FLAC::Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
457 
458  if (pic)
459  {
460  // Remove the embedded image of the matching type
461  flacfile->removePicture(pic, false);
462  flacfile->save();
463  retval = true;
464  }
465  else
466  {
467  retval = false;
468  }
469 
470  delete flacfile;
471  }
472  else
473  {
474  retval = false;
475  }
476 
477  return retval;
478 #else
479  LOG(VB_GENERAL, LOG_WARNING,
480  "TagLib 1.8.0 or later is required to remove albumart from flac xiphComment tags");
481  return false;
482 #endif
483 }
484 
486  const AlbumArtImage* albumart,
487  ImageType newType)
488 {
489  if (filename.isEmpty() || !albumart)
490  return false;
491 
492  if (albumart->m_imageType == newType)
493  return true;
494 
495  bool retval = false;
496 
497  TagLib::FLAC::File * flacfile = OpenFile(filename);
498 
499  // This presumes that there is only one art item of each type
500  if (flacfile)
501  {
502  // Now see if the art is in the FLAC file
503  TagLib::FLAC::Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
504 
505  if (pic)
506  {
507  pic->setType(PictureTypeFromImageType(newType));
508  flacfile->save();
509  retval = true;
510  }
511  else
512  {
513  retval = false;
514  }
515 
516  delete flacfile;
517  }
518  else
519  {
520  retval = false;
521  }
522 
523  return retval;
524 }
525 
527 {
528  TagLib::FLAC::File *flacfile = OpenFile(filename);
529 
530  if (!flacfile)
531  return false;
532 
533  TagLib::Ogg::XiphComment *tag = flacfile->xiphComment(false);
534 
535  bool retval = false;
536  if (tag && !tag->isEmpty())
537  retval = true;
538 
539  delete flacfile;
540 
541  return retval;
542 }
543 
544 QString MetaIOFLACVorbis::getExtFromMimeType(const QString &mimeType)
545 {
546  if (mimeType == "image/png")
547  return {".png"};
548  if (mimeType == "image/jpeg" || mimeType == "image/jpg")
549  return {".jpg"};
550  if (mimeType == "image/gif")
551  return {".gif"};
552  if (mimeType == "image/bmp")
553  return {".bmp"};
554 
555  LOG(VB_GENERAL, LOG_ERR,
556  "Music Scanner - Unknown image mimetype found - " + mimeType);
557 
558  return {};
559 }
MetaIOFLACVorbis::removeAlbumArt
bool removeAlbumArt(const QString &filename, const AlbumArtImage *albumart) override
Remove the albumart image from the file.
Definition: metaioflacvorbis.cpp:440
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
MusicMetadata::CompilationArtist
QString CompilationArtist() const
Definition: musicmetadata.h:138
MetaIO::m_filename
QString m_filename
Definition: metaio.h:182
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
IT_BACKCOVER
@ IT_BACKCOVER
Definition: musicmetadata.h:32
MusicMetadata
Definition: musicmetadata.h:80
metaioflacvorbis.h
MetaIOFLACVorbis::write
bool write(const QString &filename, MusicMetadata *mdata) override
Writes all metadata back to a file.
Definition: metaioflacvorbis.cpp:40
MetaIOFLACVorbis::getAlbumArt
QImage * getAlbumArt(const QString &filename, ImageType type) override
Read the albumart image from the file.
Definition: metaioflacvorbis.cpp:188
musicutils.h
MetaIOFLACVorbis::getAlbumArtList
AlbumArtList getAlbumArtList(const QString &filename) override
Read the albumart images from the file.
Definition: metaioflacvorbis.cpp:278
MetaIOFLACVorbis::read
MusicMetadata * read(const QString &filename) override
Reads MusicMetadata from a file.
Definition: metaioflacvorbis.cpp:97
MetaIOTagLib::getTrackLength
static std::chrono::milliseconds getTrackLength(TagLib::File *file)
Find the length of the track (in milliseconds)
Definition: metaiotaglib.cpp:89
MetaIOTagLib::ReadGenericMetadata
void ReadGenericMetadata(TagLib::Tag *tag, MusicMetadata *metadata)
Writes metadata common to all tag formats to the tag.
Definition: metaiotaglib.cpp:54
MetaIOFLACVorbis::PictureTypeFromImageType
static TagLib::FLAC::Picture::Type PictureTypeFromImageType(ImageType itype)
Definition: metaioflacvorbis.cpp:242
MetaIO::restoreTimeStamps
void restoreTimeStamps(void)
Definition: metaio.cpp:224
MetaIOFLACVorbis::writeAlbumArt
bool writeAlbumArt(const QString &filename, const AlbumArtImage *albumart) override
Write the albumart image to the file.
Definition: metaioflacvorbis.cpp:363
MetaIOFLACVorbis::TagExists
bool TagExists(const QString &filename) override
Definition: metaioflacvorbis.cpp:526
MetaIOFLACVorbis::OpenFile
static TagLib::FLAC::File * OpenFile(const QString &filename)
Open the file to read the tag.
Definition: metaioflacvorbis.cpp:22
IT_ARTIST
@ IT_ARTIST
Definition: musicmetadata.h:35
IT_CD
@ IT_CD
Definition: musicmetadata.h:33
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
MetaIOFLACVorbis::getPictureFromFile
static TagLib::FLAC::Picture * getPictureFromFile(TagLib::FLAC::File *flacfile, ImageType type)
Definition: metaioflacvorbis.cpp:213
IT_UNKNOWN
@ IT_UNKNOWN
Definition: musicmetadata.h:30
AlbumArtList
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:56
MYTH_MUSICBRAINZ_ALBUMARTIST_UUID
static constexpr const char * MYTH_MUSICBRAINZ_ALBUMARTIST_UUID
Definition: metaio.h:15
AlbumArtImage
Definition: musicmetadata.h:39
MusicMetadata::Compilation
bool Compilation() const
Definition: musicmetadata.h:252
mythcontext.h
MetaIOFLACVorbis::changeImageType
bool changeImageType(const QString &filename, const AlbumArtImage *albumart, ImageType newType) override
Definition: metaioflacvorbis.cpp:485
IT_FRONTCOVER
@ IT_FRONTCOVER
Definition: musicmetadata.h:31
MetaIOFLACVorbis::getExtFromMimeType
static QString getExtFromMimeType(const QString &mimeType)
Definition: metaioflacvorbis.cpp:544
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:836
IT_INLAY
@ IT_INLAY
Definition: musicmetadata.h:34
AlbumArtImage::m_description
QString m_description
Definition: musicmetadata.h:52
build_compdb.filename
filename
Definition: build_compdb.py:21
ImageType
ImageType
Definition: musicmetadata.h:28
musicmetadata.h
MetaIO::saveTimeStamps
void saveTimeStamps(void)
Definition: metaio.cpp:215
AlbumArtImage::m_filename
QString m_filename
Definition: musicmetadata.h:49