예제 #1
0
/*
** Extracts cover art embedded in FLAC files.
**
*/
bool QCoverArt::ExtractFLAC(TagLib::FLAC::File* file)
{
    const TagLib::List<TagLib::FLAC::Picture*>& picList = file->pictureList();
    if (!picList.isEmpty())
    {
        // Let's grab the first image
        TagLib::FLAC::Picture* pic = picList[0];
        img.loadFromData((const unsigned char*)pic->data().data(), (int)pic->data().size());

        return true;
    }

    return false;
}
예제 #2
0
Cover* FileHelper::extractCover()
{
	Cover *cover = nullptr;
	switch (_fileType) {
	case EXT_MP3: {
		TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file);
		if (mpegFile && mpegFile->hasID3v2Tag()) {
			// Look for picture frames only
			TagLib::ID3v2::FrameList listOfMp3Frames = mpegFile->ID3v2Tag()->frameListMap()["APIC"];
			// It's possible to have more than one picture per file!
			if (!listOfMp3Frames.isEmpty()) {
				for (TagLib::ID3v2::FrameList::ConstIterator it = listOfMp3Frames.begin(); it != listOfMp3Frames.end() ; it++) {
					// Cast a Frame* to AttachedPictureFrame*
					TagLib::ID3v2::AttachedPictureFrame *pictureFrame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it);
					if (pictureFrame) {
						// Performs a deep copy of the cover
						QByteArray b = QByteArray(pictureFrame->picture().data(), pictureFrame->picture().size());
						cover = new Cover(b, QString(pictureFrame->mimeType().toCString(true)));
					}
				}
			}
		} else if (mpegFile && mpegFile->hasID3v1Tag()) {
			qDebug() << Q_FUNC_INFO << "Not implemented for ID3v1Tag";
		}
		break;
	}
	case EXT_FLAC: {
		if (TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file)) {
			auto list = flacFile->pictureList();
			for (auto it = list.begin(); it != list.end() ; it++) {
				TagLib::FLAC::Picture *p = *it;
				if (p->type() == TagLib::FLAC::Picture::FrontCover) {
					// Performs a deep copy of the cover
					QByteArray b = QByteArray(p->data().data(), p->data().size());
					cover = new Cover(b, QString(p->mimeType().toCString(true)));
					break;
				}
			}
		}
		break;
	}
	default:
		qDebug() << Q_FUNC_INFO << "Not implemented for this file type" << _fileType << _file << _fileInfo.absoluteFilePath();
		break;
	}
	return cover;
}
예제 #3
0
QImage SoundSourceSndFile::parseCoverArt() {
    QImage coverArt;
    QString location = getFilename();
    setType(location.section(".",-1).toLower());
    const QByteArray qBAFilename(getFilename().toLocal8Bit());

    if (getType() == "flac") {
        TagLib::FLAC::File f(qBAFilename.constData());
        TagLib::ID3v2::Tag* id3v2 = f.ID3v2Tag();
        if (id3v2) {
            coverArt = Mixxx::getCoverInID3v2Tag(*id3v2);
        }
        if (coverArt.isNull()) {
            TagLib::Ogg::XiphComment *xiph = f.xiphComment();
            if (xiph) {
                coverArt = Mixxx::getCoverInXiphComment(*xiph);
            }
        }
        if (coverArt.isNull()) {
            TagLib::List<TagLib::FLAC::Picture*> covers = f.pictureList();
            if (!covers.isEmpty()) {
                std::list<TagLib::FLAC::Picture*>::iterator it = covers.begin();
                TagLib::FLAC::Picture* cover = *it;
                coverArt = QImage::fromData(
                    QByteArray(cover->data().data(), cover->data().size()));
            }
        }
    } else if (getType() == "wav") {
        TagLib::RIFF::WAV::File f(qBAFilename.constData());
        TagLib::ID3v2::Tag* id3v2 = f.tag();
        if (id3v2) {
            coverArt = Mixxx::getCoverInID3v2Tag(*id3v2);
        }
    } else {
        // Try AIFF
        TagLib::RIFF::AIFF::File f(qBAFilename.constData());
        TagLib::ID3v2::Tag* id3v2 = f.tag();
        if (id3v2) {
            coverArt = Mixxx::getCoverInID3v2Tag(*id3v2);
        }
    }
    return coverArt;
}
예제 #4
0
QImage SoundSourceFLAC::parseCoverArt() {
    QImage coverArt;
    setType("flac");
    TagLib::FLAC::File f(m_qFilename.toLocal8Bit().constData());
    coverArt = getCoverInID3v2Tag(f.ID3v2Tag());
    if (coverArt.isNull()) {
        coverArt = getCoverInXiphComment(f.xiphComment());
    }
    if (coverArt.isNull()) {
        TagLib::List<TagLib::FLAC::Picture*> covers = f.pictureList();
        if (!covers.isEmpty()) {
            std::list<TagLib::FLAC::Picture*>::iterator it = covers.begin();
            TagLib::FLAC::Picture* cover = *it;
            coverArt = QImage::fromData(
                QByteArray(cover->data().data(), cover->data().size()));
        }
    }
    return coverArt;
}
예제 #5
0
/*
** Extracts cover art embedded in FLAC files.
**
*/
bool CCover::ExtractFLAC(TagLib::FLAC::File* file, const std::wstring& target)
{
	const TagLib::List<TagLib::FLAC::Picture*>& picList = file->pictureList();
	if (!picList.isEmpty())
	{
		// Let's grab the first image
		TagLib::FLAC::Picture* pic = picList[0];
		return WriteCover(pic->data(), target);
	}

	return false;
}
예제 #6
0
bool mediaFLAC(Artwork *art, const char *filePath)
{
    TagLib::FLAC::File f(filePath);
    if (!f.tag()) {
        return false;
    }
    if (!mediaTag(art, &f)) {
        return false;
    }
    art->filetype = FILETYPE_FLAC;

#if 0
    const TagLib::List<TagLib::FLAC::Picture*> picturelist = f->pictureList();
    for(TagLib::List<TagLib::FLAC::Picture*>::ConstIterator it = picturelist.begin();
        it != picturelist.end();
        it++) {
        TagLib::FLAC::Picture *picture = (*it);
        if (picture) {
            if (picture->type() == TagLib::FLAC::Picture::FileIcon ||
                    picture->type() == TagLib::FLAC::Picture::OtherFileIcon ||
                    picture->type( )== TagLib::FLAC::Picture::ColouredFish) {
                // skip!
            }
            else {
                if (!art->hasCover()) {
                    saveImage(art, (const uchar *) picture->data().data(), picture->data().size());
                }
                break;
            }
        }
    }

#endif

    return art;
}
예제 #7
0
QByteArray TagReader::LoadEmbeddedArt(const QString& filename) const {
  if (filename.isEmpty()) return QByteArray();

  qLog(Debug) << "Loading art from" << filename;

#ifdef Q_OS_WIN32
  TagLib::FileRef ref(filename.toStdWString().c_str());
#else
  TagLib::FileRef ref(QFile::encodeName(filename).constData());
#endif

  if (ref.isNull() || !ref.file()) return QByteArray();

  // MP3
  TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(ref.file());
  if (file && file->ID3v2Tag()) {
    TagLib::ID3v2::FrameList apic_frames =
        file->ID3v2Tag()->frameListMap()["APIC"];
    if (apic_frames.isEmpty()) return QByteArray();

    TagLib::ID3v2::AttachedPictureFrame* pic =
        static_cast<TagLib::ID3v2::AttachedPictureFrame*>(apic_frames.front());

    return QByteArray((const char*)pic->picture().data(),
                      pic->picture().size());
  }

  // Ogg vorbis/speex
  TagLib::Ogg::XiphComment* xiph_comment =
      dynamic_cast<TagLib::Ogg::XiphComment*>(ref.file()->tag());

  if (xiph_comment) {
    TagLib::Ogg::FieldListMap map = xiph_comment->fieldListMap();

    // Other than the below mentioned non-standard COVERART,
    // METADATA_BLOCK_PICTURE
    // is the proposed tag for cover pictures.
    // (see http://wiki.xiph.org/VorbisComment#METADATA_BLOCK_PICTURE)
    if (map.contains("METADATA_BLOCK_PICTURE")) {
      TagLib::StringList pict_list = map["METADATA_BLOCK_PICTURE"];
      for (std::list<TagLib::String>::iterator it = pict_list.begin();
           it != pict_list.end(); ++it) {
        QByteArray data(QByteArray::fromBase64(it->toCString()));
        TagLib::ByteVector tdata(data.data(), data.size());
        TagLib::FLAC::Picture p(tdata);
        if (p.type() == TagLib::FLAC::Picture::FrontCover)
          return QByteArray(p.data().data(), p.data().size());
      }
      // If there was no specific front cover, just take the first picture
      QByteArray data(QByteArray::fromBase64(
          map["METADATA_BLOCK_PICTURE"].front().toCString()));
      TagLib::ByteVector tdata(data.data(), data.size());
      TagLib::FLAC::Picture p(tdata);
      return QByteArray(p.data().data(), p.data().size());
    }

    // Ogg lacks a definitive standard for embedding cover art, but it seems
    // b64 encoding a field called COVERART is the general convention
    if (!map.contains("COVERART")) return QByteArray();

    return QByteArray::fromBase64(map["COVERART"].toString().toCString());
  }

#ifdef TAGLIB_HAS_FLAC_PICTURELIST
  // Flac
  TagLib::FLAC::File* flac_file = dynamic_cast<TagLib::FLAC::File*>(ref.file());
  if (flac_file && flac_file->xiphComment()) {
    TagLib::List<TagLib::FLAC::Picture*> pics = flac_file->pictureList();
    if (!pics.isEmpty()) {
      // Use the first picture in the file - this could be made cleverer and
      // pick the front cover if it's present.

      std::list<TagLib::FLAC::Picture*>::iterator it = pics.begin();
      TagLib::FLAC::Picture* picture = *it;

      return QByteArray(picture->data().data(), picture->data().size());
    }
  }
#endif

  // MP4/AAC
  TagLib::MP4::File* aac_file = dynamic_cast<TagLib::MP4::File*>(ref.file());
  if (aac_file) {
    TagLib::MP4::Tag* tag = aac_file->tag();
    const TagLib::MP4::ItemListMap& items = tag->itemListMap();
    TagLib::MP4::ItemListMap::ConstIterator it = items.find("covr");
    if (it != items.end()) {
      const TagLib::MP4::CoverArtList& art_list = it->second.toCoverArtList();

      if (!art_list.isEmpty()) {
        // Just take the first one for now
        const TagLib::MP4::CoverArt& art = art_list.front();
        return QByteArray(art.data().data(), art.data().size());
      }
    }
  }

  return QByteArray();
}
bool SFB::Audio::AddAPETagToDictionary(CFMutableDictionaryRef dictionary, std::vector<std::shared_ptr<AttachedPicture>>& attachedPictures, const TagLib::APE::Tag *tag)
{
	if(nullptr == dictionary || nullptr == tag)
		return false;

	if(tag->isEmpty())
		return true;

	SFB::CFMutableDictionary additionalMetadata(0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
	
	for(auto iterator : tag->itemListMap()) {
		auto item = iterator.second;

		if(item.isEmpty())
			continue;

		if(TagLib::APE::Item::Text == item.type()) {
			SFB::CFString key(item.key().toCString(true), kCFStringEncodingUTF8);
			SFB::CFString value(item.toString().toCString(true), kCFStringEncodingUTF8);

			if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUM"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kAlbumTitleKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ARTIST"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kArtistKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUMARTIST"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kAlbumArtistKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPOSER"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kComposerKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("GENRE"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kGenreKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DATE"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kReleaseDateKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DESCRIPTION"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kCommentKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TITLE"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kTitleKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TRACKNUMBER"), kCFCompareCaseInsensitive))
				AddIntToDictionary(dictionary, Metadata::kTrackNumberKey, CFStringGetIntValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TRACKTOTAL"), kCFCompareCaseInsensitive))
				AddIntToDictionary(dictionary, Metadata::kTrackTotalKey, CFStringGetIntValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPILATION"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kCompilationKey, CFStringGetIntValue(value) ? kCFBooleanTrue : kCFBooleanFalse);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DISCNUMBER"), kCFCompareCaseInsensitive))
				AddIntToDictionary(dictionary, Metadata::kDiscNumberKey, CFStringGetIntValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DISCTOTAL"), kCFCompareCaseInsensitive))
				AddIntToDictionary(dictionary, Metadata::kDiscTotalKey, CFStringGetIntValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("LYRICS"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kLyricsKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("BPM"), kCFCompareCaseInsensitive))
				AddIntToDictionary(dictionary, Metadata::kBPMKey, CFStringGetIntValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("RATING"), kCFCompareCaseInsensitive))
				AddIntToDictionary(dictionary, Metadata::kRatingKey, CFStringGetIntValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ISRC"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kISRCKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("MCN"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kMCNKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("MUSICBRAINZ_ALBUMID"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kMusicBrainzReleaseIDKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("MUSICBRAINZ_TRACKID"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kMusicBrainzRecordingIDKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TITLESORT"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kTitleSortOrderKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUMTITLESORT"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kAlbumTitleSortOrderKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ARTISTSORT"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kArtistSortOrderKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUMARTISTSORT"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kAlbumArtistSortOrderKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPOSERSORT"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kComposerSortOrderKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("GROUPING"), kCFCompareCaseInsensitive))
				CFDictionarySetValue(dictionary, Metadata::kGroupingKey, value);
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_REFERENCE_LOUDNESS"), kCFCompareCaseInsensitive))
				AddDoubleToDictionary(dictionary, Metadata::kReferenceLoudnessKey, CFStringGetDoubleValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_TRACK_GAIN"), kCFCompareCaseInsensitive))
				AddDoubleToDictionary(dictionary, Metadata::kTrackGainKey, CFStringGetDoubleValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_TRACK_PEAK"), kCFCompareCaseInsensitive))
				AddDoubleToDictionary(dictionary, Metadata::kTrackPeakKey, CFStringGetDoubleValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_ALBUM_GAIN"), kCFCompareCaseInsensitive))
				AddDoubleToDictionary(dictionary, Metadata::kAlbumGainKey, CFStringGetDoubleValue(value));
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_ALBUM_PEAK"), kCFCompareCaseInsensitive))
				AddDoubleToDictionary(dictionary, Metadata::kAlbumPeakKey, CFStringGetDoubleValue(value));
#if 0
			else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("METADATA_BLOCK_PICTURE"), kCFCompareCaseInsensitive)) {
				// Handle embedded pictures
				for(auto blockIterator : item.values()) {
					auto encodedBlock = blockIterator.data(TagLib::String::UTF8);

					// Decode the Base-64 encoded data
					auto decodedBlock = TagLib::DecodeBase64(encodedBlock);

					// Create the picture
					TagLib::FLAC::Picture picture;
					picture.parse(decodedBlock);

					SFB::CFData data((const UInt8 *)picture.data().data(), picture.data().size());

					SFB::CFString description = nullptr;
					if(!picture.description().isNull())
						description(picture.description().toCString(true), kCFStringEncodingUTF8);

					attachedPictures.push_back(std::make_shared<AttachedPicture>(data, (AttachedPicture::Type)picture.type(), description));
				}
			}
#endif
			// Put all unknown tags into the additional metadata
			else
				CFDictionarySetValue(additionalMetadata, key, value);
		}
		else if(TagLib::APE::Item::Binary == item.type()) {
			SFB::CFString key(item.key().toCString(true), kCFStringEncodingUTF8);

			// From http://www.hydrogenaudio.org/forums/index.php?showtopic=40603&view=findpost&p=504669
			/*
			 <length> 32 bit
			 <flags with binary bit set> 32 bit
			 <field name> "Cover Art (Front)"|"Cover Art (Back)"
			 0x00
			 <description> UTF-8 string (needs to be a file name to be recognized by AudioShell - meh)
			 0x00
			 <cover data> binary
			 */
			if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("Cover Art (Front)"), kCFCompareCaseInsensitive) || kCFCompareEqualTo == CFStringCompare(key, CFSTR("Cover Art (Back)"), kCFCompareCaseInsensitive)) {
				auto binaryData = item.binaryData();
				size_t pos = binaryData.find('\0');
				if(TagLib::ByteVector::npos() != pos && 3 < binaryData.size()) {
					SFB::CFData data((const UInt8 *)binaryData.mid(pos + 1).data(), (CFIndex)(binaryData.size() - pos - 1));
					SFB::CFString description(TagLib::String(binaryData.mid(0, pos), TagLib::String::UTF8).toCString(true), kCFStringEncodingUTF8);

					attachedPictures.push_back(std::make_shared<AttachedPicture>(data, kCFCompareEqualTo == CFStringCompare(key, CFSTR("Cover Art (Front)"), kCFCompareCaseInsensitive) ? AttachedPicture::Type::FrontCover : AttachedPicture::Type::BackCover, description));
				}
			}
		}
	}

	if(CFDictionaryGetCount(additionalMetadata))
		CFDictionarySetValue(dictionary, Metadata::kAdditionalMetadataKey, additionalMetadata);

	return true;
}
bool SFB::Audio::AddXiphCommentToDictionary(CFMutableDictionaryRef dictionary, std::vector<std::shared_ptr<AttachedPicture>>& attachedPictures, const TagLib::Ogg::XiphComment *tag)
{
	if(nullptr == dictionary || nullptr == tag)
		return false;

	SFB::CFMutableDictionary additionalMetadata = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

	for(auto it : tag->fieldListMap()) {
		// According to the Xiph comment specification keys should only contain a limited subset of ASCII, but UTF-8 is a safer choice
		SFB::CFString key = CFStringCreateWithCString(kCFAllocatorDefault, it.first.toCString(true), kCFStringEncodingUTF8);
		
		// Vorbis allows multiple comments with the same key, but this isn't supported by AudioMetadata
		SFB::CFString value = CFStringCreateWithCString(kCFAllocatorDefault, it.second.front().toCString(true), kCFStringEncodingUTF8);
		
		if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUM"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kAlbumTitleKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ARTIST"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kArtistKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUMARTIST"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kAlbumArtistKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPOSER"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kComposerKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("GENRE"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kGenreKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DATE"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kReleaseDateKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DESCRIPTION"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kCommentKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TITLE"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kTitleKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TRACKNUMBER"), kCFCompareCaseInsensitive))
			AddIntToDictionary(dictionary, Metadata::kTrackNumberKey, CFStringGetIntValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TRACKTOTAL"), kCFCompareCaseInsensitive))
			AddIntToDictionary(dictionary, Metadata::kTrackTotalKey, CFStringGetIntValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPILATION"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kCompilationKey, CFStringGetIntValue(value) ? kCFBooleanTrue : kCFBooleanFalse);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DISCNUMBER"), kCFCompareCaseInsensitive))
			AddIntToDictionary(dictionary, Metadata::kDiscNumberKey, CFStringGetIntValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DISCTOTAL"), kCFCompareCaseInsensitive))
			AddIntToDictionary(dictionary, Metadata::kDiscTotalKey, CFStringGetIntValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("LYRICS"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kLyricsKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("BPM"), kCFCompareCaseInsensitive))
			AddIntToDictionary(dictionary, Metadata::kBPMKey, CFStringGetIntValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("RATING"), kCFCompareCaseInsensitive))
			AddIntToDictionary(dictionary, Metadata::kRatingKey, CFStringGetIntValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ISRC"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kISRCKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("MCN"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kMCNKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("MUSICBRAINZ_ALBUMID"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kMusicBrainzReleaseIDKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("MUSICBRAINZ_TRACKID"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kMusicBrainzRecordingIDKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TITLESORT"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kTitleSortOrderKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUMTITLESORT"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kAlbumTitleSortOrderKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ARTISTSORT"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kArtistSortOrderKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUMARTISTSORT"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kAlbumArtistSortOrderKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPOSERSORT"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kComposerSortOrderKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("GROUPING"), kCFCompareCaseInsensitive))
			CFDictionarySetValue(dictionary, Metadata::kGroupingKey, value);
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_REFERENCE_LOUDNESS"), kCFCompareCaseInsensitive))
			AddDoubleToDictionary(dictionary, Metadata::kReferenceLoudnessKey, CFStringGetDoubleValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_TRACK_GAIN"), kCFCompareCaseInsensitive))
			AddDoubleToDictionary(dictionary, Metadata::kTrackGainKey, CFStringGetDoubleValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_TRACK_PEAK"), kCFCompareCaseInsensitive))
			AddDoubleToDictionary(dictionary, Metadata::kTrackPeakKey, CFStringGetDoubleValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_ALBUM_GAIN"), kCFCompareCaseInsensitive))
			AddDoubleToDictionary(dictionary, Metadata::kAlbumGainKey, CFStringGetDoubleValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_ALBUM_PEAK"), kCFCompareCaseInsensitive))
			AddDoubleToDictionary(dictionary, Metadata::kAlbumPeakKey, CFStringGetDoubleValue(value));
		else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("METADATA_BLOCK_PICTURE"), kCFCompareCaseInsensitive)) {
			// Handle embedded pictures
			for(auto blockIterator : it.second) {
				auto encodedBlock = blockIterator.data(TagLib::String::UTF8);

				// Decode the Base-64 encoded data
				auto decodedBlock = TagLib::DecodeBase64(encodedBlock);

				// Create the picture
				TagLib::FLAC::Picture picture;
				picture.parse(decodedBlock);
				
				SFB::CFData data = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)picture.data().data(), (CFIndex)picture.data().size());

				SFB::CFString description;
				if(!picture.description().isEmpty())
					description = CFStringCreateWithCString(kCFAllocatorDefault, picture.description().toCString(true), kCFStringEncodingUTF8);

				attachedPictures.push_back(std::make_shared<AttachedPicture>(data, (AttachedPicture::Type)picture.type(), description));
			}
		}
		// Put all unknown tags into the additional metadata
		else
			CFDictionarySetValue(additionalMetadata, key, value);
	}
	
	if(CFDictionaryGetCount(additionalMetadata))
		CFDictionarySetValue(dictionary, Metadata::kAdditionalMetadataKey, additionalMetadata);

	return true;
}
예제 #10
-1
void import_flac_file(const std::string& filename)
{
  //std::cerr << "import_flac_file " << filename << std::endl;

  TagLib::FLAC::File file(filename.c_str());

  auto tag = file.tag();

  auto artist_name = tag->artist().to8Bit(true);
  auto album_title = tag->album().to8Bit(true);
  auto track_title = tag->title().to8Bit(true);

  auto artist = dm::artist::find_by_name(artist_name);

  if ( artist.is_null() )
  {
    // Create artist.
    artist.name(artist_name);
    artist.save();
  }

  auto album = artist.find_album_by_title(album_title);

  if ( album.is_null() )
  {
    // Create album.
    album.title(album_title);
    album.member("artist", json::object{ { "id", artist.id() }, { "name", artist.name() } });
    album.save();

    // Add album to artist albums.
    artist.add_album(album);
    artist.save();
  }

  auto track = album.find_track_by_title_and_number(track_title, tag->track());

  // Set/update track attributes.

  track.title(track_title);
  track.track_number(tag->track());
  track.disc_number(1);

  // Create track source object.
  json::object source{ { "name", "local" }, { "uri", filename } };

  TagLib::FLAC::Properties* properties = file.audioProperties();
  if ( properties )
  {
    track.duration(properties->length());
  }

  TagLib::Ogg::XiphComment* xiph_comment = file.xiphComment();
  if ( xiph_comment )
  {
    auto field_map = xiph_comment->fieldListMap();

    json::object replaygain;

    for ( auto& field : field_map )
    {
      if ( field.first == "TRACK NUMBER" || field.first == "TRACKNUMBER" )
      {
        if ( field.second.size() > 0 ) {
          track.track_number(std::stoi(field.second[0].to8Bit()));
        }
        else {
          std::cerr << "field='" << field.first << "' string list size=" << field.second.size() << std::endl;
        }
      }
      else if ( field.first == "DISC NUMBER" || field.first == "DISCNUMBER" )
      {
        if ( field.second.size() > 0 ) {
          track.disc_number(std::stoi(field.second[0].to8Bit()));
        }
        else {
          std::cerr << "field='" << field.first << "' string list size=" << field.second.size() << std::endl;
        }
      }
      else if ( field.first == "REPLAYGAIN_REFERENCE_LOUDNESS" )
      {
        auto ref_loudness = std::stod(field.second[0].to8Bit());
        replaygain["reference_loudness"] = ref_loudness;
      }
      else if ( field.first == "REPLAYGAIN_TRACK_GAIN" )
      {
        auto gain = std::stod(field.second[0].to8Bit());
        replaygain["track_gain"] = gain;
      }
    }

    if ( !replaygain.empty() ) {
      source["replaygain"] = replaygain;
    }
  }

  const TagLib::List<TagLib::FLAC::Picture*>& images = file.pictureList();

  if ( images.size() > 0 )
  {
    TagLib::FLAC::Picture* image = images[0];

    if ( image->mimeType() == "image/jpeg" )
    {
      auto cover = dm::album_cover::find_by_album_id(album.id());

      if ( cover.is_null() )
      {
        cover.format("jpg");
        cover.data(reinterpret_cast<const char*>(image->data().data()), image->data().size());
        cover.save();
      }
    }
    else
    {
      std::cerr << "unhandled image mime type - " << filename << " images=" << images.size() << ", mime type " << image->mimeType() << std::endl;
    }
  }

  track.artist(artist);
  track.album(album);
  track.source(std::move(source));

  if ( track.id_is_null() )
  {
    // Create track id.
    track.save();
    // Add new track to album.
    album.add_track(track);
    album.save();
  }
  else
  {
    track.save();
  }
}