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  TagLib::FLAC::File *flacfile =
26  new TagLib::FLAC::File(fname.constData());
27 
28  if (!flacfile->isOpen())
29  {
30  delete flacfile;
31  flacfile = nullptr;
32  }
33 
34  return flacfile;
35 }
36 
37 
41 bool MetaIOFLACVorbis::write(const QString &filename, MusicMetadata* mdata)
42 {
43  if (!mdata)
44  return false;
45 
46  if (filename.isEmpty())
47  return false;
48 
49  m_filename = filename;
50 
51  TagLib::FLAC::File *flacfile = OpenFile(m_filename);
52 
53  if (!flacfile)
54  return false;
55 
56  TagLib::Ogg::XiphComment *tag = flacfile->xiphComment(true);
57 
58  if (!tag)
59  {
60  delete flacfile;
61  return false;
62  }
63 
64  WriteGenericMetadata(tag, mdata);
65 
66  // Compilation
67  if (mdata->Compilation())
68  {
69  tag->addField("MUSICBRAINZ_ALBUMARTISTID",
71  tag->addField("COMPILATION_ARTIST",
72  QStringToTString(mdata->CompilationArtist()), true);
73  }
74  else
75  {
76  // Don't remove the musicbrainz field unless it indicated a compilation
77  if (tag->contains("MUSICBRAINZ_ALBUMARTISTID") &&
78  (tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString() ==
80  {
81  tag->removeField("MUSICBRAINZ_ALBUMARTISTID");
82  }
83  tag->removeField("COMPILATION_ARTIST");
84  }
85 
87  bool result = flacfile->save();
89 
90  delete flacfile;
91 
92  return (result);
93 }
94 
98 MusicMetadata* MetaIOFLACVorbis::read(const QString &filename)
99 {
100  TagLib::FLAC::File *flacfile = OpenFile(filename);
101 
102  if (!flacfile)
103  return nullptr;
104 
105  TagLib::Ogg::XiphComment *tag = flacfile->xiphComment();
106 
107  if (!tag)
108  {
109  delete flacfile;
110  return nullptr;
111  }
112 
113  MusicMetadata *metadata = new MusicMetadata(filename);
114 
115  ReadGenericMetadata(tag, metadata);
116 
117  bool compilation = false;
118 
119  if (tag->contains("COMPILATION_ARTIST"))
120  {
121  QString compilation_artist = TStringToQString(
122  tag->fieldListMap()["COMPILATION_ARTIST"].toString()).trimmed();
123  if (compilation_artist != metadata->Artist())
124  {
125  metadata->setCompilationArtist(compilation_artist);
126  compilation = true;
127  }
128  }
129 
130  if (!compilation && tag->contains("MUSICBRAINZ_ALBUMARTISTID"))
131  {
132  QString musicbrainzcode = TStringToQString(
133  tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString()).trimmed();
134  if (musicbrainzcode == MYTH_MUSICBRAINZ_ALBUMARTIST_UUID)
135  compilation = true;
136  }
137 
138  metadata->setCompilation(compilation);
139 
140  if (metadata->Length() <= 0)
141  metadata->setLength(getTrackLength(flacfile));
142 
143  delete flacfile;
144 
145  return metadata;
146 }
147 
155 QImage* MetaIOFLACVorbis::getAlbumArt(const QString &filename, ImageType type)
156 {
157  QImage *picture = new QImage();
158  TagLib::FLAC::File * flacfile = OpenFile(filename);
159 
160  if (flacfile)
161  {
162  TagLib::FLAC::Picture* pic = getPictureFromFile(flacfile, type);
163  if (pic)
164  {
165  picture->loadFromData((const uchar *)pic->data().data(),
166  pic->data().size());
167  }
168  else
169  {
170  delete picture;
171  return nullptr;
172  }
173 
174  delete flacfile;
175  }
176 
177  return picture;
178 }
179 
181  TagLib::FLAC::File *flacfile,
182  ImageType type)
183 {
184  using TagLib::FLAC::Picture;
185 
186  Picture *pic = nullptr;
187 
188  if (flacfile)
189  {
190  Picture::Type artType = PictureTypeFromImageType(type);
191 
192  // From what I can tell, FLAC::File maintains ownership of the Picture pointers, so no need to delete
193  const TagLib::List<Picture *>& picList = flacfile->pictureList();
194 
195  for (auto it = picList.begin(); it != picList.end(); it++)
196  {
197  if ((*it)->type() == artType)
198  {
199  //found the type we were looking for
200  pic = *it;
201  break;
202  }
203  }
204  }
205 
206  return pic;
207 }
208 
209 TagLib::FLAC::Picture::Type MetaIOFLACVorbis::PictureTypeFromImageType(
210  ImageType itype) {
211  using TagLib::FLAC::Picture;
212  Picture::Type artType = Picture::Other;
213  switch (itype)
214  {
215  case IT_UNKNOWN :
216  artType = Picture::Other;
217  break;
218  case IT_FRONTCOVER :
219  artType = Picture::FrontCover;
220  break;
221  case IT_BACKCOVER :
222  artType = Picture::BackCover;
223  break;
224  case IT_CD :
225  artType = Picture::Media;
226  break;
227  case IT_INLAY :
228  artType = Picture::LeafletPage;
229  break;
230  case IT_ARTIST :
231  artType = Picture::Artist;
232  break;
233  default:
234  return Picture::Other;
235  }
236 
237  return artType;
238 }
239 
246 {
247  using TagLib::FLAC::Picture;
248  AlbumArtList artlist;
249  TagLib::FLAC::File * flacfile = OpenFile(filename);
250 
251  if (flacfile)
252  {
253  const TagLib::List<Picture *>& picList = flacfile->pictureList();
254 
255  for(TagLib::List<Picture *>::ConstIterator it = picList.begin();
256  it != picList.end(); it++)
257  {
258  Picture* pic = *it;
259  // Assume a valid image would have at least
260  // 100 bytes of data (1x1 indexed gif is 35 bytes)
261  if (pic->data().size() < 100)
262  {
263  LOG(VB_GENERAL, LOG_NOTICE,
264  "Music Scanner - Discarding picture "
265  "with size less than 100 bytes");
266  continue;
267  }
268 
269  AlbumArtImage *art = new AlbumArtImage();
270 
271  if (pic->description().isEmpty())
272  art->m_description.clear();
273  else
274  art->m_description = TStringToQString(pic->description());
275 
276  art->m_embedded = true;
278 
279  QString ext = getExtFromMimeType(
280  TStringToQString(pic->mimeType()).toLower());
281 
282  switch (pic->type())
283  {
284  case Picture::FrontCover :
285  art->m_imageType = IT_FRONTCOVER;
286  art->m_filename = QString("front") + ext;
287  break;
288  case Picture::BackCover :
289  art->m_imageType = IT_BACKCOVER;
290  art->m_filename = QString("back") + ext;
291  break;
292  case Picture::Media :
293  art->m_imageType = IT_CD;
294  art->m_filename = QString("cd") + ext;
295  break;
296  case Picture::LeafletPage :
297  art->m_imageType = IT_INLAY;
298  art->m_filename = QString("inlay") + ext;
299  break;
300  case Picture::Artist :
301  art->m_imageType = IT_ARTIST;
302  art->m_filename = QString("artist") + ext;
303  break;
304  case Picture::Other :
305  art->m_imageType = IT_UNKNOWN;
306  art->m_filename = QString("unknown") + ext;
307  break;
308  default:
309  LOG(VB_GENERAL, LOG_ERR, "Music Scanner - picture found "
310  "with unsupported type");
311  delete art;
312  continue;
313  }
314 
315  artlist.append(art);
316  }
317  }
318 
319  delete flacfile;
320  return artlist;
321 }
322 
332 bool MetaIOFLACVorbis::writeAlbumArt(const QString &filename,
333  const AlbumArtImage *albumart)
334 {
335 #if TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 8
336  using TagLib::FLAC::Picture;
337  if (filename.isEmpty() || !albumart)
338  return false;
339 
340  m_filename = filename;
341 
342  bool retval = false;
343 
344  // load the image into a QByteArray
345  QImage image(albumart->m_filename);
346  QByteArray imageData;
347  QBuffer buffer(&imageData);
348  buffer.open(QIODevice::WriteOnly);
349  // Write the image data to a file
350  image.save(&buffer, "JPEG");
351 
352  TagLib::FLAC::File * flacfile = OpenFile(filename);
353 
354  // This presumes that there is only one art item of each type
355  if (flacfile)
356  {
357  // Now see if the art is in the FLAC file
358  Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
359 
360  if (pic)
361  {
362  // Remove the embedded image of the matching type
363  flacfile->removePicture(pic, false);
364  }
365  else
366  {
367  // Create a new image of the correct type
368  pic = new Picture();
369  pic->setType(PictureTypeFromImageType(albumart->m_imageType));
370  }
371 
372  TagLib::ByteVector bytevector;
373  bytevector.setData(imageData.data(), imageData.size());
374 
375  pic->setData(bytevector);
376  QString mimetype = "image/jpeg";
377 
378  pic->setMimeType(QStringToTString(mimetype));
379  pic->setDescription(QStringToTString(albumart->m_description));
380 
381  flacfile->addPicture(pic);
382 
383  saveTimeStamps();
384  retval = flacfile->save();
386 
387  delete flacfile;
388  }
389  else
390  {
391  retval = false;
392  }
393 
394  return retval;
395 #else
396  LOG(VB_GENERAL, LOG_WARNING,
397  "TagLib 1.8.0 or later is required to write albumart to flac xiphComment tags");
398  return false;
399 #endif
400 }
401 
409 bool MetaIOFLACVorbis::removeAlbumArt(const QString &filename,
410  const AlbumArtImage *albumart)
411 {
412 #if TAGLIB_MAJOR_VERSION == 1 && TAGLIB_MINOR_VERSION >= 8
413 
414  if (filename.isEmpty() || !albumart)
415  return false;
416 
417  bool retval = false;
418 
419  TagLib::FLAC::File * flacfile = OpenFile(filename);
420 
421  // This presumes that there is only one art item of each type
422  if (flacfile)
423  {
424  // Now see if the art is in the FLAC file
425  TagLib::FLAC::Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
426 
427  if (pic)
428  {
429  // Remove the embedded image of the matching type
430  flacfile->removePicture(pic, false);
431  flacfile->save();
432  retval = true;
433  }
434  else
435  {
436  retval = false;
437  }
438 
439  delete flacfile;
440  }
441  else
442  {
443  retval = false;
444  }
445 
446  return retval;
447 #else
448  LOG(VB_GENERAL, LOG_WARNING,
449  "TagLib 1.8.0 or later is required to remove albumart from flac xiphComment tags");
450  return false;
451 #endif
452 }
453 
454 bool MetaIOFLACVorbis::changeImageType(const QString &filename,
455  const AlbumArtImage* albumart,
456  ImageType newType)
457 {
458  if (filename.isEmpty() || !albumart)
459  return false;
460 
461  if (albumart->m_imageType == newType)
462  return true;
463 
464  bool retval = false;
465 
466  TagLib::FLAC::File * flacfile = OpenFile(filename);
467 
468  // This presumes that there is only one art item of each type
469  if (flacfile)
470  {
471  // Now see if the art is in the FLAC file
472  TagLib::FLAC::Picture *pic = getPictureFromFile(flacfile, albumart->m_imageType);
473 
474  if (pic)
475  {
476  pic->setType(PictureTypeFromImageType(newType));
477  flacfile->save();
478  retval = true;
479  }
480  else
481  {
482  retval = false;
483  }
484 
485  delete flacfile;
486  }
487  else
488  {
489  retval = false;
490  }
491 
492  return retval;
493 }
494 
495 bool MetaIOFLACVorbis::TagExists(const QString &filename)
496 {
497  TagLib::FLAC::File *flacfile = OpenFile(filename);
498 
499  if (!flacfile)
500  return false;
501 
502  TagLib::Ogg::XiphComment *tag = flacfile->xiphComment(false);
503 
504  bool retval = false;
505  if (tag && !tag->isEmpty())
506  retval = true;
507 
508  delete flacfile;
509 
510  return retval;
511 }
512 
513 QString MetaIOFLACVorbis::getExtFromMimeType(const QString &mimeType)
514 {
515  if (mimeType == "image/png")
516  return QString(".png");
517  if (mimeType == "image/jpeg" || mimeType == "image/jpg")
518  return QString(".jpg");
519  if (mimeType == "image/gif")
520  return QString(".gif");
521  if (mimeType == "image/bmp")
522  return QString(".bmp");
523 
524  LOG(VB_GENERAL, LOG_ERR,
525  "Music Scanner - Unknown image mimetype found - " + mimeType);
526 
527  return QString();
528 }
AlbumArtList getAlbumArtList(const QString &filename) override
Read the albumart images from the file.
void setLength(int llength)
QList< AlbumArtImage * > AlbumArtList
Definition: musicmetadata.h:54
QString m_description
Definition: musicmetadata.h:50
MusicMetadata * read(const QString &filename) override
Reads MusicMetadata from a file.
int Length() const
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:49
QString m_hostname
Definition: musicmetadata.h:48
void setCompilation(bool state)
QString Artist() const
bool removeAlbumArt(const QString &filename, const AlbumArtImage *albumart) override
Remove the albumart image from the file.
QString m_filename
Definition: musicmetadata.h:47
void saveTimeStamps(void)
Definition: metaio.cpp:217
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:26
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.
void setCompilationArtist(const QString &lcompilation_artist, const QString &lcompilation_artist_sort=nullptr)
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:226