MythTV  master
metaioflacvorbis.cpp
Go to the documentation of this file.
1 
2 // Libmyth
3 #include <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->removeField("MUSICBRAINZ_ALBUMARTISTID");
81  }
82  tag->removeField("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 
129  if (!compilation && tag->contains("MUSICBRAINZ_ALBUMARTISTID"))
130  {
131  QString musicbrainzcode = TStringToQString(
132  tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString()).trimmed();
133  if (musicbrainzcode == MYTH_MUSICBRAINZ_ALBUMARTIST_UUID)
134  compilation = true;
135  }
136 
137  metadata->setCompilation(compilation);
138 
139  if (metadata->Length() <= 0)
140  metadata->setLength(getTrackLength(flacfile));
141 
142  delete flacfile;
143 
144  return metadata;
145 }
146 
155 {
156  auto *picture = new QImage();
157  TagLib::FLAC::File * flacfile = OpenFile(filename);
158 
159  if (flacfile)
160  {
161  TagLib::FLAC::Picture* pic = getPictureFromFile(flacfile, type);
162  if (pic)
163  {
164  picture->loadFromData((const uchar *)pic->data().data(),
165  pic->data().size());
166  }
167  else
168  {
169  delete picture;
170  return nullptr;
171  }
172 
173  delete flacfile;
174  }
175 
176  return picture;
177 }
178 
180  TagLib::FLAC::File *flacfile,
181  ImageType type)
182 {
183  using TagLib::FLAC::Picture;
184 
185  Picture *pic = nullptr;
186 
187  if (flacfile)
188  {
189  Picture::Type artType = PictureTypeFromImageType(type);
190 
191  // From what I can tell, FLAC::File maintains ownership of the Picture pointers, so no need to delete
192  const TagLib::List<Picture *>& picList = flacfile->pictureList();
193 
194  for (auto *entry : picList)
195  {
196  if (entry->type() == artType)
197  {
198  //found the type we were looking for
199  pic = entry;
200  break;
201  }
202  }
203  }
204 
205  return pic;
206 }
207 
208 TagLib::FLAC::Picture::Type MetaIOFLACVorbis::PictureTypeFromImageType(
209  ImageType itype) {
210  using TagLib::FLAC::Picture;
211  Picture::Type artType = Picture::Other;
212  switch (itype)
213  {
214  case IT_UNKNOWN :
215  artType = Picture::Other;
216  break;
217  case IT_FRONTCOVER :
218  artType = Picture::FrontCover;
219  break;
220  case IT_BACKCOVER :
221  artType = Picture::BackCover;
222  break;
223  case IT_CD :
224  artType = Picture::Media;
225  break;
226  case IT_INLAY :
227  artType = Picture::LeafletPage;
228  break;
229  case IT_ARTIST :
230  artType = Picture::Artist;
231  break;
232  default:
233  return Picture::Other;
234  }
235 
236  return artType;
237 }
238 
245 {
246  using TagLib::FLAC::Picture;
247  AlbumArtList artlist;
248  TagLib::FLAC::File * flacfile = OpenFile(filename);
249 
250  if (flacfile)
251  {
252  const TagLib::List<Picture *>& picList = flacfile->pictureList();
253 
254  for (auto *pic : picList)
255  {
256  // Assume a valid image would have at least
257  // 100 bytes of data (1x1 indexed gif is 35 bytes)
258  if (pic->data().size() < 100)
259  {
260  LOG(VB_GENERAL, LOG_NOTICE,
261  "Music Scanner - Discarding picture "
262  "with size less than 100 bytes");
263  continue;
264  }
265 
266  auto *art = new AlbumArtImage();
267 
268  if (pic->description().isEmpty())
269  art->m_description.clear();
270  else
271  art->m_description = TStringToQString(pic->description());
272 
273  art->m_embedded = true;
274  art->m_hostname = gCoreContext->GetHostName();
275 
276  QString ext = getExtFromMimeType(
277  TStringToQString(pic->mimeType()).toLower());
278 
279  switch (pic->type())
280  {
281  case Picture::FrontCover :
282  art->m_imageType = IT_FRONTCOVER;
283  art->m_filename = QString("front") + ext;
284  break;
285  case Picture::BackCover :
286  art->m_imageType = IT_BACKCOVER;
287  art->m_filename = QString("back") + ext;
288  break;
289  case Picture::Media :
290  art->m_imageType = IT_CD;
291  art->m_filename = QString("cd") + ext;
292  break;
293  case Picture::LeafletPage :
294  art->m_imageType = IT_INLAY;
295  art->m_filename = QString("inlay") + ext;
296  break;
297  case Picture::Artist :
298  art->m_imageType = IT_ARTIST;
299  art->m_filename = QString("artist") + ext;
300  break;
301  case Picture::Other :
302  art->m_imageType = IT_UNKNOWN;
303  art->m_filename = QString("unknown") + ext;
304  break;
305  default:
306  LOG(VB_GENERAL, LOG_ERR, "Music Scanner - picture found "
307  "with unsupported type");
308  delete art;
309  continue;
310  }
311 
312  artlist.append(art);
313  }
314  }
315 
316  delete flacfile;
317  return artlist;
318 }
319 
330  const AlbumArtImage *albumart)
331 {
332 #if TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 8
333  using TagLib::FLAC::Picture;
334  if (filename.isEmpty() || !albumart)
335  return false;
336 
338 
339  bool retval = false;
340 
341  // load the image into a QByteArray
342  QImage image(albumart->m_filename);
343  QByteArray imageData;
344  QBuffer buffer(&imageData);
345  buffer.open(QIODevice::WriteOnly);
346  // Write the image data to a file
347  image.save(&buffer, "JPEG");
348 
349  TagLib::FLAC::File * flacfile = OpenFile(filename);
350 
351  // This presumes that there is only one art item of each type
352  if (flacfile)
353  {
354  // Now see if the art is in the FLAC file
355  Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
356 
357  if (pic)
358  {
359  // Remove the embedded image of the matching type
360  flacfile->removePicture(pic, false);
361  }
362  else
363  {
364  // Create a new image of the correct type
365  pic = new Picture();
366  pic->setType(PictureTypeFromImageType(albumart->m_imageType));
367  }
368 
369  TagLib::ByteVector bytevector;
370  bytevector.setData(imageData.data(), imageData.size());
371 
372  pic->setData(bytevector);
373  QString mimetype = "image/jpeg";
374 
375  pic->setMimeType(QStringToTString(mimetype));
376  pic->setDescription(QStringToTString(albumart->m_description));
377 
378  flacfile->addPicture(pic);
379 
380  saveTimeStamps();
381  retval = flacfile->save();
383 
384  delete flacfile;
385  }
386  else
387  {
388  retval = false;
389  }
390 
391  return retval;
392 #else
393  LOG(VB_GENERAL, LOG_WARNING,
394  "TagLib 1.8.0 or later is required to write albumart to flac xiphComment tags");
395  return false;
396 #endif
397 }
398 
407  const AlbumArtImage *albumart)
408 {
409 #if TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 8
410 
411  if (filename.isEmpty() || !albumart)
412  return false;
413 
414  bool retval = false;
415 
416  TagLib::FLAC::File * flacfile = OpenFile(filename);
417 
418  // This presumes that there is only one art item of each type
419  if (flacfile)
420  {
421  // Now see if the art is in the FLAC file
422  TagLib::FLAC::Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
423 
424  if (pic)
425  {
426  // Remove the embedded image of the matching type
427  flacfile->removePicture(pic, false);
428  flacfile->save();
429  retval = true;
430  }
431  else
432  {
433  retval = false;
434  }
435 
436  delete flacfile;
437  }
438  else
439  {
440  retval = false;
441  }
442 
443  return retval;
444 #else
445  LOG(VB_GENERAL, LOG_WARNING,
446  "TagLib 1.8.0 or later is required to remove albumart from flac xiphComment tags");
447  return false;
448 #endif
449 }
450 
452  const AlbumArtImage* albumart,
453  ImageType newType)
454 {
455  if (filename.isEmpty() || !albumart)
456  return false;
457 
458  if (albumart->m_imageType == newType)
459  return true;
460 
461  bool retval = false;
462 
463  TagLib::FLAC::File * flacfile = OpenFile(filename);
464 
465  // This presumes that there is only one art item of each type
466  if (flacfile)
467  {
468  // Now see if the art is in the FLAC file
469  TagLib::FLAC::Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
470 
471  if (pic)
472  {
473  pic->setType(PictureTypeFromImageType(newType));
474  flacfile->save();
475  retval = true;
476  }
477  else
478  {
479  retval = false;
480  }
481 
482  delete flacfile;
483  }
484  else
485  {
486  retval = false;
487  }
488 
489  return retval;
490 }
491 
493 {
494  TagLib::FLAC::File *flacfile = OpenFile(filename);
495 
496  if (!flacfile)
497  return false;
498 
499  TagLib::Ogg::XiphComment *tag = flacfile->xiphComment(false);
500 
501  bool retval = false;
502  if (tag && !tag->isEmpty())
503  retval = true;
504 
505  delete flacfile;
506 
507  return retval;
508 }
509 
510 QString MetaIOFLACVorbis::getExtFromMimeType(const QString &mimeType)
511 {
512  if (mimeType == "image/png")
513  return QString(".png");
514  if (mimeType == "image/jpeg" || mimeType == "image/jpg")
515  return QString(".jpg");
516  if (mimeType == "image/gif")
517  return QString(".gif");
518  if (mimeType == "image/bmp")
519  return QString(".bmp");
520 
521  LOG(VB_GENERAL, LOG_ERR,
522  "Music Scanner - Unknown image mimetype found - " + mimeType);
523 
524  return QString();
525 }
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:56
AlbumArtList getAlbumArtList(const QString &filename) override
Read the albumart images from the file.
QString m_description
Definition: musicmetadata.h:52
MusicMetadata * read(const QString &filename) override
Reads MusicMetadata from a file.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static TagLib::FLAC::Picture::Type PictureTypeFromImageType(ImageType itype)
static TagLib::FLAC::Picture * getPictureFromFile(TagLib::FLAC::File *flacfile, ImageType type)
#define TStringToQString(s)
Definition: musicutils.h:16
static void WriteGenericMetadata(TagLib::Tag *tag, const MusicMetadata *metadata)
Writes metadata common to all tag formats to the tag.
ImageType m_imageType
Definition: musicmetadata.h:51
bool removeAlbumArt(const QString &filename, const AlbumArtImage *albumart) override
Remove the albumart image from the file.
QString m_filename
Definition: musicmetadata.h:49
void saveTimeStamps(void)
Definition: metaio.cpp:222
void ReadGenericMetadata(TagLib::Tag *tag, MusicMetadata *metadata)
Writes metadata common to all tag formats to the tag.
bool TagExists(const QString &filename) override
bool changeImageType(const QString &filename, const AlbumArtImage *albumart, ImageType newType) override
bool Compilation() const
static int getTrackLength(TagLib::File *file)
Find the length of the track (in seconds)
bool writeAlbumArt(const QString &filename, const AlbumArtImage *albumart) override
Write the albumart image to the file.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString m_filename
Definition: metaio.h:181
ImageType
Definition: musicmetadata.h:28
QString CompilationArtist() const
#define MYTH_MUSICBRAINZ_ALBUMARTIST_UUID
Definition: metaio.h:15
static TagLib::FLAC::File * OpenFile(const QString &filename)
Open the file to read the tag.
bool write(const QString &filename, MusicMetadata *mdata) override
Writes all metadata back to a file.
QString GetHostName(void)
#define QStringToTString(s)
Definition: musicutils.h:14
QImage * getAlbumArt(const QString &filename, ImageType type) override
Read the albumart image from the file.
static QString getExtFromMimeType(const QString &mimeType)
void restoreTimeStamps(void)
Definition: metaio.cpp:231