Пример #1
0
/* GET ALBUM COVER
 */
const char * tag_data_get_cover (TagDatac * data, uint * len)
{
    TagLib::ID3v2::AttachedPictureFrame * pic = NULL;
    if (!(reinterpret_cast<TagData *>(data))->id3v2 || (reinterpret_cast<TagData *>(data))->id3v2->frameListMap()["APIC"].isEmpty())
    {
        *len = 0;
        return NULL;
    }

    TagLib::ID3v2::FrameList::ConstIterator it;
    TagLib::ID3v2::FrameList l = (reinterpret_cast<TagData *>(data))->id3v2->frameListMap()["APIC"];
    for(it=l.begin(); it!=l.end(); ++it)
    {
        TagLib::ID3v2::AttachedPictureFrame * t = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it);
        if (pic == NULL)
            pic = t;
        if (t->type () == TagLib::ID3v2::AttachedPictureFrame::FrontCover)
        {
            pic = t;
            break;
        }
    }
    if (pic == NULL)
    {
        *len = 0;
        return NULL;
    }
    *len = pic->picture ().size ();
	const char * out = (const char *) malloc (*len);
    memcpy ((void *) out, pic->picture ().data(), *len);
    return out;
}
Пример #2
0
bool MetaDSF::exportPictures(const char *prefix) const
{
  std::map<std::string, unsigned int> counter;
  TagLib::ID3v2::FrameList l = _i->_file.ID3v2Tag()->frameList("APIC");
  TagLib::ID3v2::FrameList::ConstIterator it;
  for (it = l.begin(); it != l.end(); ++it) {
    TagLib::ID3v2::AttachedPictureFrame *f =
      static_cast<TagLib::ID3v2::AttachedPictureFrame *>(*it);
    std::string fname = prefix;
    std::string tname = picTypeDesc[f->type()].toCString();
    std::string ext = MIMETypeToExtMap[f->mimeType()].toCString();
    if (counter.find(tname) == counter.end())
      counter[tname] = 1;
    else
      counter[tname] += 1;
    fname += "_";
    fname += tname;
    fname += "_";
    fname += std::to_string(counter[tname]);
    fname += ".";
    fname += ext;
    //std::cout << "fname = " << fname << std::endl;
    writeFileFromVector(fname.c_str(), f->picture());
  }
  return true;
}
Пример #3
0
bool MetaDSF::attachPicture(const TagLib::String &path, 
			    const TagLib::String &ptype) 
{
  // Determine MIME type  
  TagLib::String mimeType;
  TagLib::String ext = path.substr(path.rfind(".") + 1);

  transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
  if (extToMIMETypeMap.find(ext) == extToMIMETypeMap.end()) {
    std::cerr << "Unknown image format " << ext << std::endl;
    return false;
  }

  mimeType = extToMIMETypeMap[ext];

  // Load file into memory
  TagLib::ByteVector v;
  if (loadFileIntoVector(path.toCString(), v) <= 0) {
    return false;
  }

  std::string pt = ptype.toCString();
  TagLib::ID3v2::AttachedPictureFrame *apic = 
    new TagLib::ID3v2::AttachedPictureFrame();
  TagLib::ID3v2::AttachedPictureFrame::Type t = 
    static_cast<TagLib::ID3v2::AttachedPictureFrame::Type>(std::stoi(pt, 0, 16));
  apic->setPicture(v);
  apic->setMimeType(mimeType);
  apic->setType(t);
  _i->_file.ID3v2Tag()->addFrame(apic);
  _i->_changed = true;
  return true;
}
Пример #4
0
Tag::Tag(const QString &filename)
    : m_filename(filename)
{
    TagLib::MPEG::File mpegfile(filename.toLocal8Bit().constData());
    TagLib::ID3v2::Tag* id3v2 = mpegfile.ID3v2Tag();
    if (id3v2 && !id3v2->isEmpty()) {
        readRegularTag(id3v2, m_data);

        int picnum = 0;

        TagLib::ID3v2::FrameList frames = id3v2->frameListMap()["APIC"]; // attached picture
        TagLib::ID3v2::FrameList::ConstIterator it = frames.begin();
        while (it != frames.end()) {
            TagLib::ID3v2::AttachedPictureFrame* apic = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it);
            TagLib::ByteVector bytes = apic->picture();
            QImage img = QImage::fromData(reinterpret_cast<const uchar*>(bytes.data()), bytes.size());
            if (!img.isNull()) {
                m_data[QLatin1String("picture") + QString::number(picnum++)] = QVariant(img);
            }
            ++it;
        }

    } else {
        TagLib::FileRef fileref(filename.toLocal8Bit().constData());
        if (fileref.isNull())
            return;
        TagLib::Tag* tag = fileref.tag();
        if (!tag || tag->isEmpty())
            return;

        readRegularTag(tag, m_data);
    }
}
Пример #5
0
/** Check if file has an inner picture. */
bool FileHelper::hasCover() const
{
	bool atLeastOnePicture = false;
	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);
					atLeastOnePicture = atLeastOnePicture || (pictureFrame != nullptr && !pictureFrame->picture().isEmpty());
				}
			}
		}
		break;
	}
	case EXT_FLAC: {
		if (TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file)) {
			atLeastOnePicture = !flacFile->pictureList().isEmpty();
		}
	}
	default:
		break;
	}
	return atLeastOnePicture;
}
Пример #6
0
bool CTagBase::exportImage(const wchar_t* s)
{
	if (!s)	return false;
	if (_tagFile)
	{
		//get picture
		TagLib::MPEG::File *f = (TagLib::MPEG::File *)_tagFile.get();
		if (!f->hasID3v2Tag() || f->ID3v2Tag()->isEmpty())
			return false;
		/*
		TagLib::ID3v2::FrameList::ConstIterator it = f->ID3v2Tag()->frameList().begin();
		for (; it != f->ID3v2Tag()->frameList().end(); it++)
		{
			if ((*it)->frameID().operator == (TagLib::ByteVector("APIC")))
			{
				HANDLE hFile = CreateFile(s, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
				if (INVALID_HANDLE_VALUE == hFile)
					return false;
				int nWriteBytes = 0;
				size_t size = (*it)->size();
				::WriteFile(hFile, (*it)->render().data(), (*it)->size(), (LPDWORD)&nWriteBytes, NULL);
				::CloseHandle(hFile);
				if (nWriteBytes != size)
					return false;
				return true;
			}
		}*/
		if (f->ID3v2Tag()->frameListMap().size() == 0)
			return false;
		if (f->ID3v2Tag()->frameListMap().find("APIC") == f->ID3v2Tag()->frameListMap().end())
			return false;

		TagLib::ID3v2::FrameList Flist = f->ID3v2Tag()->frameListMap()["APIC"];
		if (Flist.isEmpty())
			return false;
		TagLib::ID3v2::AttachedPictureFrame *p = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(Flist.front());
		size_t size = p->picture().size();
		CString strPicType = p->mimeType().toCString(true);
		int nPos = strPicType.Find('/');
		CString strTemp = strPicType.Right(strPicType.GetLength() - nPos - 1);
		//CString strPicPath   = s;

		//if (strTemp == _T("png"))
		//	strPicPath.Append(_T(".png"));
		//else
		//	strPicPath.Append(_T(".jpg"));

		HANDLE hFile = CreateFile(s, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
		if (INVALID_HANDLE_VALUE == hFile)
			return false;
		int nWriteBytes = 0;
		::WriteFile(hFile, p->picture().data(), size, (LPDWORD)&nWriteBytes, NULL);
		::CloseHandle(hFile);
		if (nWriteBytes != size)
			return false;

		return true;
	}
	return false;
}
Пример #7
0
/*
** Extracts cover art embedded in ID3v2 tags.
**
*/
bool CCover::ExtractID3(TagLib::ID3v2::Tag* tag, const std::wstring& target)
{
	const TagLib::ID3v2::FrameList& frameList = tag->frameList("APIC");
	if (!frameList.isEmpty())
	{
		// Grab the first image
		TagLib::ID3v2::AttachedPictureFrame* frame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(frameList.front());
		return WriteCover(frame->picture(), target);
	}

	return false;
}
Пример #8
0
/*
** Extracts cover art embedded in ID3v2 tags.
**
*/
bool QCoverArt::ExtractID3(TagLib::ID3v2::Tag* tag)
{
    const TagLib::ID3v2::FrameList& frameList = tag->frameList("APIC");
    if (!frameList.isEmpty())
    {
        // Grab the first image
        TagLib::ID3v2::AttachedPictureFrame* frame = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(frameList.front());
        img.loadFromData((const unsigned char*)frame->picture().data(), (int)frame->picture().size());

        return true;
    }

    return false;
}
Пример #9
0
bool CTagBase::addImage(wchar_t* s)
{
	if (!_tagFile) return false;
	TagLib::MPEG::File *pAudioFile = (TagLib::MPEG::File *)_tagFile.get();
	CString strFileName = s;
	ImageFile imageFile((CW2A)strFileName.GetBuffer());
	if (!pAudioFile->isValid() || !imageFile.isValid())
		return false;
	TagLib::ID3v2::Tag *tag = pAudioFile->ID3v2Tag(true);
	TagLib::ID3v2::AttachedPictureFrame *frame = new TagLib::ID3v2::AttachedPictureFrame;
	frame->setMimeType(imageFile.mimeType());
	frame->setPicture(imageFile.data());
	tag->addFrame(frame);
	return pAudioFile->save();

}
Пример #10
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;
}
Пример #11
0
/** Sets the inner picture. */
void FileHelper::setCover(Cover *cover)
{
	switch (_fileType) {
	case EXT_MP3: {
		TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file);
		if (mpegFile->hasID3v2Tag()) {
			// Look for picture frames only
			TagLib::ID3v2::FrameList mp3Frames = mpegFile->ID3v2Tag()->frameListMap()["APIC"];
			if (!mp3Frames.isEmpty()) {
				for (TagLib::ID3v2::FrameList::Iterator it = mp3Frames.begin(); it != mp3Frames.end() ; it++) {
					// Removing a frame will invalidate any pointers on the list
					mpegFile->ID3v2Tag()->removeFrame(*it);
					break;
				}
			}
			if (cover != nullptr) {
				TagLib::ByteVector bv(cover->byteArray().data(), cover->byteArray().length());
				TagLib::ID3v2::AttachedPictureFrame *pictureFrame = new TagLib::ID3v2::AttachedPictureFrame();
				pictureFrame->setMimeType(cover->mimeType());
				pictureFrame->setPicture(bv);
				pictureFrame->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
				mpegFile->ID3v2Tag()->addFrame(pictureFrame);
			}
		} else if (mpegFile->hasID3v1Tag()) {
			qDebug() << Q_FUNC_INFO << "Not implemented for ID3v1Tag";
		}
		break;
	}
	case EXT_FLAC: {
		TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file);
		flacFile->removePictures();
		if (cover != nullptr) {
			TagLib::FLAC::Picture *picture = new TagLib::FLAC::Picture;
			picture->setType(TagLib::FLAC::Picture::FrontCover);
			TagLib::ByteVector bv(cover->byteArray().data(), cover->byteArray().length());
			picture->setData(bv);
			flacFile->addPicture(picture);
		}
		break;
	}
	default:
		qDebug() << Q_FUNC_INFO << "Not implemented for" << _fileType;
		break;
	}
}
QImage
ID3v2TagHelper::embeddedCover() const
{
    TagLib::ID3v2::FrameList apicList = m_tag->frameListMap()[fieldName( Meta::valHasCover ).toCString()];
    TagLib::ID3v2::AttachedPictureFrame *cover = NULL;
    TagLib::ID3v2::AttachedPictureFrame *otherCover = NULL;

    for( TagLib::ID3v2::FrameList::ConstIterator it = apicList.begin(); it != apicList.end(); ++it )
    {
        TagLib::ID3v2::AttachedPictureFrame *currFrame =
                dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it );

        if( currFrame->picture().size() < MIN_COVER_SIZE )
            continue;

        if( currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover )
        {
            cover = currFrame;
        }
        else if( currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::Other )
        {
            otherCover = currFrame;
        }
    }

    if( !cover && otherCover )
        cover = otherCover;

    if( !cover )
        return QImage();

    return QImage::fromData( ( uchar * )( cover->picture().data() ), cover->picture().size() );
}
Пример #13
0
bool CoverUtils::coverFromMPEGTags(TagLib::ID3v2::Tag *tag, Album *album) {

    if (!tag) return false;

    TagLib::ID3v2::FrameList list = tag->frameList("APIC");
    if (list.isEmpty()) return false;

    TagLib::ID3v2::AttachedPictureFrame *frame =
            static_cast<TagLib::ID3v2::AttachedPictureFrame *>(list.front());
    if (!frame) return false;
    const int frameSize = frame->picture().size();
    if (frameSize <= 0) return false;

    QImage image;
    image.loadFromData((const uchar *) frame->picture().data(), frame->picture().size());
    if (!isAcceptableImage(image)) return false;

    return saveImage(image, album);
}
bool
ID3v2TagHelper::hasEmbeddedCover() const
{
    TagLib::ID3v2::FrameList apicList = m_tag->frameListMap()[fieldName( Meta::valHasCover ).toCString()];

    for( TagLib::ID3v2::FrameList::ConstIterator it = apicList.begin(); it != apicList.end(); ++it )
    {
        TagLib::ID3v2::AttachedPictureFrame *currFrame =
                dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it );

        if( currFrame->picture().size() < MIN_COVER_SIZE )
            continue;

        if( currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover ||
            currFrame->type() == TagLib::ID3v2::AttachedPictureFrame::Other )
            return true;
    }

    return false;
}
bool
ID3v2TagHelper::setEmbeddedCover( const QImage &cover )
{
    QByteArray bytes;
    QBuffer buffer( &bytes );

    buffer.open( QIODevice::WriteOnly );

    if( !cover.save( &buffer, "JPEG" ) )
    {
        buffer.close();
        return false;
    }

    buffer.close();

    TagLib::ByteVector field = fieldName( Meta::valHasCover ).toCString();
    TagLib::ID3v2::FrameList apicList = m_tag->frameListMap()[field];
    TagLib::ID3v2::AttachedPictureFrame *frontCover = NULL;

    // remove covers
    TagLib::List<TagLib::ID3v2::AttachedPictureFrame*> backedUpPictures;
    for( TagLib::ID3v2::FrameList::ConstIterator it = apicList.begin(); it != apicList.end(); ++it )
    {
        TagLib::ID3v2::AttachedPictureFrame *currFrame =
                dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it );

        m_tag->removeFrame( currFrame, false );
    }

    // add new cover
    frontCover = new TagLib::ID3v2::AttachedPictureFrame( field );
    frontCover->setMimeType( "image/jpeg" );
    frontCover->setPicture( TagLib::ByteVector( bytes.data(), bytes.count() ) );
    frontCover->setType( TagLib::ID3v2::AttachedPictureFrame::FrontCover );
    m_tag->addFrame( frontCover );

    return true;
}
Пример #16
0
int MetaDSF::deletePictures(const TagLib::String &ptype) 
{
  TagLib::ID3v2::FrameList l = _i->_file.ID3v2Tag()->frameList("APIC");
  TagLib::ID3v2::FrameList dl; // a list of frames to be deleted
  TagLib::ID3v2::FrameList::ConstIterator it;
  std::string pt = ptype.toCString();
  // retrieve a list of frames that matches the given type
  for (it = l.begin(); it != l.end(); it++) {
    TagLib::ID3v2::AttachedPictureFrame *f = 
      static_cast<TagLib::ID3v2::AttachedPictureFrame *>(*it);
    TagLib::ID3v2::AttachedPictureFrame::Type t = 
      static_cast<TagLib::ID3v2::AttachedPictureFrame::Type>(std::stoi(pt, 
								       0, 16));
    if (f->type() == t) {
      dl.append(f);
    }
  }  

  _i->deleteFrames(dl);
  _i->_changed = true;
  return dl.size();
}
Пример #17
0
void saveFile(string stdPath, DataEditors* editors) {

    const char* charPath = stdPath.c_str();
    ifstream fileCheck(charPath);
    if(!fileCheck.good()) {

        QWidget *w = new QWidget();
        QLabel *l = new QLabel("Invalid file!", w);
        QPushButton *b = new QPushButton("OK", w);
        QVBoxLayout *lay = new QVBoxLayout();
        lay->addWidget(l);
        lay->addWidget(b);
        w->setLayout(lay);
        QWidget::connect(b, SIGNAL(clicked()), w, SLOT(close()));
        w->show();
        return;
    }
    TagLib::FileName name = charPath;
    TagLib::FileRef file(name);
    if(editors->artist->isEnabled())
        file.tag()->setArtist(editors->artist->text().toStdString());
    if(editors->album->isEnabled())
        file.tag()->setAlbum(editors->album->text().toStdString());
    if(editors->track->isEnabled())
        file.tag()->setTrack(editors->track->text().toInt());
    if(editors->title->isEnabled())
        file.tag()->setTitle(editors->title->text().toStdString());
    if(editors->year->isEnabled())
        file.tag()->setYear(editors->year->text().toInt());
    if(editors->genre->isEnabled())
        file.tag()->setGenre(editors->genre->currentText().toStdString());
    if(editors->comment->isEnabled())
        file.tag()->setComment(editors->comment->toPlainText().toStdString());
    file.save();

    if(editors->picture->isEnabled()) {
        TagLib::MPEG::File mpegFile(name);
        TagLib::ID3v2::Tag *tag = mpegFile.ID3v2Tag(true);
        TagLib::ID3v2::AttachedPictureFrame *frame = new TagLib::ID3v2::AttachedPictureFrame;
        QString picture = editors->picture->text();
        if(picture == "<Attached picture>") {

            TagLib::ID3v2::AttachedPictureFrame *f =
                    static_cast<TagLib::ID3v2::AttachedPictureFrame *>(editors->mpegFile->ID3v2Tag(true)->frameList("APIC").front());
            frame->setMimeType(f->mimeType());
            frame->setPicture(f->picture());
            tag->removeFrames("APIC");
            tag->addFrame(frame);
            mpegFile.save();

        } else if(picture == "") {

            tag->removeFrames("APIC");
            mpegFile.save();

        } else {

            frame->setMimeType("image/jpeg");
            string stdPic = picture.toStdString();
            const char* charPicture = stdPic.c_str();
            ifstream fileCheck(charPicture);
            if(!fileCheck.good()) {

                QWidget *w = new QWidget();
                QLabel *l = new QLabel("Invalid picture!", w);
                QPushButton *b = new QPushButton("OK", w);
                QVBoxLayout *lay = new QVBoxLayout();
                lay->addWidget(l);
                lay->addWidget(b);
                w->setLayout(lay);
                QWidget::connect(b, SIGNAL(clicked()), w, SLOT(close()));
                w->show();
                delete w;
                return;
            }

            ImageFile imageTagLibFile(charPicture);
            frame->setPicture(imageTagLibFile.data());
            tag->removeFrames("APIC");
            tag->addFrame(frame);
            mpegFile.save();
            
        }

    }

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

	// Add the basic tags not specific to ID3v2
	AddTagToDictionary(dictionary, tag);

	// Release date
	auto frameList = tag->frameListMap()["TDRC"];
	if(!frameList.isEmpty()) {
		/*
		 The timestamp fields are based on a subset of ISO 8601. When being as
		 precise as possible the format of a time string is
		 yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of
		 24), ":", minutes, ":", seconds), but the precision may be reduced by
		 removing as many time indicators as wanted. Hence valid timestamps
		 are
		 yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and
		 yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use
		 the slash character as described in 8601, and for multiple non-
		 contiguous dates, use multiple strings, if allowed by the frame
		 definition.
		 */

		TagLib::AddStringToCFDictionary(dictionary, Metadata::kReleaseDateKey, frameList.front()->toString());
	}

	// Extract composer if present
	frameList = tag->frameListMap()["TCOM"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kComposerKey, frameList.front()->toString());

	// Extract album artist
	frameList = tag->frameListMap()["TPE2"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kAlbumArtistKey, frameList.front()->toString());

	// BPM
	frameList = tag->frameListMap()["TBPM"];
	if(!frameList.isEmpty()) {
		bool ok = false;
		int BPM = frameList.front()->toString().toInt(&ok);
		if(ok)
			AddIntToDictionary(dictionary, Metadata::kBPMKey, BPM);
	}

	// Rating
	TagLib::ID3v2::PopularimeterFrame *popularimeter = nullptr;
	frameList = tag->frameListMap()["POPM"];
	if(!frameList.isEmpty() && nullptr != (popularimeter = dynamic_cast<TagLib::ID3v2::PopularimeterFrame *>(frameList.front())))
		AddIntToDictionary(dictionary, Metadata::kRatingKey, popularimeter->rating());

	// Extract total tracks if present
	frameList = tag->frameListMap()["TRCK"];
	if(!frameList.isEmpty()) {
		// Split the tracks at '/'
		TagLib::String s = frameList.front()->toString();

		bool ok;
		size_t pos = s.find("/", 0);
		if(TagLib::String::npos() != pos) {
			int trackNum = s.substr(0, pos).toInt(&ok);
			if(ok)
				AddIntToDictionary(dictionary, Metadata::kTrackNumberKey, trackNum);

			int trackTotal = s.substr(pos + 1).toInt(&ok);
			if(ok)
				AddIntToDictionary(dictionary, Metadata::kTrackTotalKey, trackTotal);
		}
		else if(s.length()) {
			int trackNum = s.toInt(&ok);
			if(ok)
				AddIntToDictionary(dictionary, Metadata::kTrackNumberKey, trackNum);
		}
	}

	// Extract disc number and total discs
	frameList = tag->frameListMap()["TPOS"];
	if(!frameList.isEmpty()) {
		// Split the tracks at '/'
		TagLib::String s = frameList.front()->toString();

		bool ok;
		size_t pos = s.find("/", 0);
		if(TagLib::String::npos() != pos) {
			int discNum = s.substr(0, pos).toInt(&ok);
			if(ok)
				AddIntToDictionary(dictionary, Metadata::kDiscNumberKey, discNum);

			int discTotal = s.substr(pos + 1).toInt(&ok);
			if(ok)
				AddIntToDictionary(dictionary, Metadata::kDiscTotalKey, discTotal);
		}
		else if(s.length()) {
			int discNum = s.toInt(&ok);
			if(ok)
				AddIntToDictionary(dictionary, Metadata::kDiscNumberKey, discNum);
		}
	}

	// Lyrics
	frameList = tag->frameListMap()["USLT"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kLyricsKey, frameList.front()->toString());

	// Extract compilation if present (iTunes TCMP tag)
	frameList = tag->frameListMap()["TCMP"];
	if(!frameList.isEmpty())
		// It seems that the presence of this frame indicates a compilation
		CFDictionarySetValue(dictionary, Metadata::kCompilationKey, kCFBooleanTrue);

	frameList = tag->frameListMap()["TSRC"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kISRCKey, frameList.front()->toString());

	// MusicBrainz
	auto musicBrainzReleaseIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "MusicBrainz Album Id");
	if(musicBrainzReleaseIDFrame)
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kMusicBrainzReleaseIDKey, musicBrainzReleaseIDFrame->fieldList().back());

	auto musicBrainzRecordingIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "MusicBrainz Track Id");
	if(musicBrainzRecordingIDFrame)
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kMusicBrainzRecordingIDKey, musicBrainzRecordingIDFrame->fieldList().back());

	// Sorting and grouping
	frameList = tag->frameListMap()["TSOT"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kTitleSortOrderKey, frameList.front()->toString());

	frameList = tag->frameListMap()["TSOA"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kAlbumTitleSortOrderKey, frameList.front()->toString());

	frameList = tag->frameListMap()["TSOP"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kArtistSortOrderKey, frameList.front()->toString());

	frameList = tag->frameListMap()["TSO2"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kAlbumArtistSortOrderKey, frameList.front()->toString());

	frameList = tag->frameListMap()["TSOC"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kComposerSortOrderKey, frameList.front()->toString());

	frameList = tag->frameListMap()["TIT1"];
	if(!frameList.isEmpty())
		TagLib::AddStringToCFDictionary(dictionary, Metadata::kGroupingKey, frameList.front()->toString());

	// ReplayGain
	bool foundReplayGain = false;

	// Preference is TXXX frames, RVA2 frame, then LAME header
	auto trackGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_TRACK_GAIN");
	auto trackPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_TRACK_PEAK");
	auto albumGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_ALBUM_GAIN");
	auto albumPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "REPLAYGAIN_ALBUM_PEAK");

	if(!trackGainFrame)
		trackGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_track_gain");
	if(trackGainFrame) {
		SFB::CFString str(trackGainFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8);
		double num = CFStringGetDoubleValue(str);

		AddDoubleToDictionary(dictionary, Metadata::kTrackGainKey, num);
		AddDoubleToDictionary(dictionary, Metadata::kReferenceLoudnessKey, 89.0);

		foundReplayGain = true;
	}

	if(!trackPeakFrame)
		trackPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_track_peak");
	if(trackPeakFrame) {
		SFB::CFString str(trackPeakFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8);
		double num = CFStringGetDoubleValue(str);

		AddDoubleToDictionary(dictionary, Metadata::kTrackPeakKey, num);
	}

	if(!albumGainFrame)
		albumGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_album_gain");
	if(albumGainFrame) {
		SFB::CFString str(albumGainFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8);
		double num = CFStringGetDoubleValue(str);

		AddDoubleToDictionary(dictionary, Metadata::kAlbumGainKey, num);
		AddDoubleToDictionary(dictionary, Metadata::kReferenceLoudnessKey, 89.0);

		foundReplayGain = true;
	}

	if(!albumPeakFrame)
		albumPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "replaygain_album_peak");
	if(albumPeakFrame) {
		SFB::CFString str(albumPeakFrame->fieldList().back().toCString(true), kCFStringEncodingUTF8);
		double num = CFStringGetDoubleValue(str);

		AddDoubleToDictionary(dictionary, Metadata::kAlbumPeakKey, num);
	}

	// If nothing found check for RVA2 frame
	if(!foundReplayGain) {
		frameList = tag->frameListMap()["RVA2"];

		for(auto frameIterator : tag->frameListMap()["RVA2"]) {
			TagLib::ID3v2::RelativeVolumeFrame *relativeVolume = dynamic_cast<TagLib::ID3v2::RelativeVolumeFrame *>(frameIterator);
			if(!relativeVolume)
				continue;

			// Attempt to use the master volume if present
			auto channels		= relativeVolume->channels();
			auto channelType	= TagLib::ID3v2::RelativeVolumeFrame::MasterVolume;

			// Fall back on whatever else exists in the frame
			if(!channels.contains(TagLib::ID3v2::RelativeVolumeFrame::MasterVolume))
				channelType = channels.front();

			float volumeAdjustment = relativeVolume->volumeAdjustment(channelType);

			if(TagLib::String("track", TagLib::String::Latin1) == relativeVolume->identification()) {
				if((int)volumeAdjustment)
					AddFloatToDictionary(dictionary, Metadata::kTrackGainKey, volumeAdjustment);
			}
			else if(TagLib::String("album", TagLib::String::Latin1) == relativeVolume->identification()) {
				if((int)volumeAdjustment)
					AddFloatToDictionary(dictionary, Metadata::kAlbumGainKey, volumeAdjustment);
			}
			// Fall back to track gain if identification is not specified
			else {
				if((int)volumeAdjustment)
					AddFloatToDictionary(dictionary, Metadata::kTrackGainKey, volumeAdjustment);
			}
		}
	}

	// Extract album art if present
	for(auto it : tag->frameListMap()["APIC"]) {
		TagLib::ID3v2::AttachedPictureFrame *frame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame *>(it);
		if(frame) {
			SFB::CFData data((const UInt8 *)frame->picture().data(), (CFIndex)frame->picture().size());

			SFB::CFString description;
			if(!frame->description().isEmpty())
				description = CFString(frame->description().toCString(true), kCFStringEncodingUTF8);

			attachedPictures.push_back(std::make_shared<AttachedPicture>(data, (AttachedPicture::Type)frame->type(), description));
		}
	}

	return true;
}
Пример #19
0
/// <summary>
/// Tries to set the cover based on the Tags of the specified file.
/// </summary>
/// <param name="filePath">Path to the file to get the cover from.</param>
bool CoverArt::SetCoverFromTag(LPCWSTR filePath)
{
    LPCWSTR extension = wcsrchr(filePath, L'.');

    if (extension == nullptr)
    {
        return false;
    }

    auto ParseImage = [this] (const BYTE * data, UINT size)
    {
        IWICImagingFactory *factory = nullptr;
        IWICBitmapDecoder *decoder = nullptr;
        IWICBitmapFrameDecode *source = nullptr;
        HRESULT hr = E_FAIL;

        IStream *stream = SHCreateMemStream(data, size);
        if (stream)
        {
            hr = Factories::GetWICFactory(reinterpret_cast<LPVOID*>(&factory));
            if (SUCCEEDED(hr))
            {
                hr = factory->CreateDecoderFromStream(stream, nullptr, WICDecodeMetadataCacheOnDemand, &decoder);
            }
            if (SUCCEEDED(hr))
            {
                hr = decoder->GetFrame(0, &source);
            }
            if (SUCCEEDED(hr))
            {
                SendMessage(gLSModule.GetMessageWindow(), WM_COVERARTUPDATE, (WPARAM)this, (LPARAM)source);
            }

            SAFERELEASE(decoder);
            SAFERELEASE(stream);
        }

        return hr == S_OK;
    };

    ++extension;

    if (_wcsicmp(extension, L"mp3") == 0)
    {
        TagLib::ID3v2::AttachedPictureFrame *pictureFrame = nullptr;
        BYTE picturePriority = 0xFF;

        TagLib::MPEG::File mp3File(filePath);
        auto tag = mp3File.ID3v2Tag();

        if (tag && tag->frameListMap().contains("APIC"))
        {
            for (auto frame : mp3File.ID3v2Tag()->frameListMap()["APIC"])
            {
                auto picFrame = (TagLib::ID3v2::AttachedPictureFrame *)frame;

                BYTE priority = mID3CoverTypePriority[picFrame->type()];
                if (priority < picturePriority)
                {
                    pictureFrame = picFrame;
                    picturePriority = priority;
                }
            }
            if (pictureFrame != nullptr) {
                return ParseImage((const BYTE *)pictureFrame->picture().data(), pictureFrame->picture().size());
            }
        }
    }
    else if (_wcsicmp(extension, L"flac") == 0)
    {
        TagLib::FLAC::File flacFile(filePath);
        for (auto &picture : flacFile.pictureList())
        {
            if (picture->type() == TagLib::FLAC::Picture::FrontCover)
            {
                return ParseImage((const BYTE *)picture->data().data(), picture->data().size());
            }
        }
    }
    else if (_wcsicmp(extension, L"mp4") == 0 || _wcsicmp(extension, L"m4a") == 0)
    {
        TagLib::MP4::File mp4File(filePath);
        if (mp4File.tag()->itemListMap().contains("covr"))
        {
            auto map = mp4File.tag()->itemListMap()["covr"];
            auto list = map.toCoverArtList();
            auto cover = list.front();
            return ParseImage((const BYTE *)cover.data().data(), cover.data().size());
        }
    }

    return false;
}
bool SFB::Audio::SetID3v2TagFromMetadata(const Metadata& metadata, TagLib::ID3v2::Tag *tag, bool setAlbumArt)
{
	if(nullptr == tag)
		return false;
	
	// Use UTF-8 as the default encoding
	(TagLib::ID3v2::FrameFactory::instance())->setDefaultTextEncoding(TagLib::String::UTF8);

	// Album title
	tag->setAlbum(TagLib::StringFromCFString(metadata.GetAlbumTitle()));

	// Artist
	tag->setArtist(TagLib::StringFromCFString(metadata.GetArtist()));
	
	// Composer
	tag->removeFrames("TCOM");
	if(metadata.GetComposer()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TCOM", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(metadata.GetComposer()));
		tag->addFrame(frame);
	}
	
	// Genre
	tag->setGenre(TagLib::StringFromCFString(metadata.GetGenre()));
	
	// Date
#if 1
	int year = 0;
	if(metadata.GetReleaseDate())
		year = CFStringGetIntValue(metadata.GetReleaseDate());
	tag->setYear((TagLib::uint)year);
#else
	// TODO: Parse the release date into components and set the frame appropriately
	tag->removeFrames("TDRC");
	if(metadata.GetReleaseDate()) {
		/*
		 The timestamp fields are based on a subset of ISO 8601. When being as
		 precise as possible the format of a time string is
		 yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of
		 24), ":", minutes, ":", seconds), but the precision may be reduced by
		 removing as many time indicators as wanted. Hence valid timestamps
		 are
		 yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and
		 yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use
		 the slash character as described in 8601, and for multiple non-
		 contiguous dates, use multiple strings, if allowed by the frame
		 definition.
		*/
//		year = CFStringGetIntValue(metadata.GetReleaseDate());
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TDRC", TagLib::String::Latin1);
		frame->setText("");
		tag->addFrame(frame);
	}
#endif

	// Comment
	tag->setComment(TagLib::StringFromCFString(metadata.GetComment()));
	
	// Album artist
	tag->removeFrames("TPE2");
	if(metadata.GetAlbumArtist()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPE2", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(metadata.GetAlbumArtist()));
		tag->addFrame(frame);
	}
	
	// Track title
	tag->setTitle(TagLib::StringFromCFString(metadata.GetTitle()));
	
	// BPM
	tag->removeFrames("TBPM");
	if(metadata.GetBPM()) {
		SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), metadata.GetBPM());

		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TBPM", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(str));
		tag->addFrame(frame);
	}

	// Rating
	tag->removeFrames("POPM");
	CFNumberRef rating = metadata.GetRating();
	if(rating) {
		TagLib::ID3v2::PopularimeterFrame *frame = new TagLib::ID3v2::PopularimeterFrame();

		int i;
		if(CFNumberGetValue(rating, kCFNumberIntType, &i)) {
			frame->setRating(i);
			tag->addFrame(frame);
		}
	}

	// Track number and total tracks
	tag->removeFrames("TRCK");
	CFNumberRef trackNumber	= metadata.GetTrackNumber();
	CFNumberRef trackTotal	= metadata.GetTrackTotal();
	if(trackNumber && trackTotal) {
		SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@/%@"), trackNumber, trackTotal);

		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(str));
		tag->addFrame(frame);
	}
	else if(trackNumber) {		
		SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), trackNumber);
		
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(str));
		tag->addFrame(frame);
	}
	else if(trackTotal) {		
		SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("/%@"), trackTotal);
		
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TRCK", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(str));
		tag->addFrame(frame);
	}
	
	// Compilation
	// iTunes uses the TCMP frame for this, which isn't in the standard, but we'll use it for compatibility
	tag->removeFrames("TCMP");
	if(metadata.GetCompilation()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TCMP", TagLib::String::Latin1);
		frame->setText(CFBooleanGetValue(metadata.GetCompilation()) ? "1" : "0");
		tag->addFrame(frame);
	}
	
	// Disc number and total discs
	tag->removeFrames("TPOS");
	CFNumberRef discNumber	= metadata.GetDiscNumber();
	CFNumberRef discTotal	= metadata.GetDiscTotal();
	if(discNumber && discTotal) {
		SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@/%@"), discNumber, discTotal);
		
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(str));
		tag->addFrame(frame);
	}
	else if(discNumber) {		
		SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("%@"), discNumber);
		
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(str));
		tag->addFrame(frame);
	}
	else if(discTotal) {		
		SFB::CFString str = CFStringCreateWithFormat(kCFAllocatorDefault, nullptr, CFSTR("/%@"), discTotal);
		
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TPOS", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(str));
		tag->addFrame(frame);
	}
	
	// Lyrics
	tag->removeFrames("USLT");
	if(metadata.GetLyrics()) {
		auto frame = new TagLib::ID3v2::UnsynchronizedLyricsFrame(TagLib::String::UTF8);
		frame->setText(TagLib::StringFromCFString(metadata.GetLyrics()));
		tag->addFrame(frame);
	}

	tag->removeFrames("TSRC");
	if(metadata.GetISRC()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSRC", TagLib::String::Latin1);
		frame->setText(TagLib::StringFromCFString(metadata.GetISRC()));
		tag->addFrame(frame);
	}

	// MusicBrainz
	auto musicBrainzReleaseIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "MusicBrainz Album Id");
	if(nullptr != musicBrainzReleaseIDFrame)
		tag->removeFrame(musicBrainzReleaseIDFrame);

	CFStringRef musicBrainzReleaseID = metadata.GetMusicBrainzReleaseID();
	if(musicBrainzReleaseID) {
		auto frame = new TagLib::ID3v2::UserTextIdentificationFrame();
		frame->setDescription("MusicBrainz Album Id");
		frame->setText(TagLib::StringFromCFString(musicBrainzReleaseID));
		tag->addFrame(frame);
	}


	auto musicBrainzRecordingIDFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(const_cast<TagLib::ID3v2::Tag *>(tag), "MusicBrainz Track Id");
	if(nullptr != musicBrainzRecordingIDFrame)
		tag->removeFrame(musicBrainzRecordingIDFrame);

	CFStringRef musicBrainzRecordingID = metadata.GetMusicBrainzRecordingID();
	if(musicBrainzRecordingID) {
		auto frame = new TagLib::ID3v2::UserTextIdentificationFrame();
		frame->setDescription("MusicBrainz Track Id");
		frame->setText(TagLib::StringFromCFString(musicBrainzRecordingID));
		tag->addFrame(frame);
	}

	// Sorting and grouping
	tag->removeFrames("TSOT");
	if(metadata.GetTitleSortOrder()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOT", TagLib::String::UTF8);
		frame->setText(TagLib::StringFromCFString(metadata.GetTitleSortOrder()));
		tag->addFrame(frame);
	}
	
	tag->removeFrames("TSOA");
	if(metadata.GetAlbumTitleSortOrder()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOA", TagLib::String::UTF8);
		frame->setText(TagLib::StringFromCFString(metadata.GetAlbumTitleSortOrder()));
		tag->addFrame(frame);
	}

	tag->removeFrames("TSOP");
	if(metadata.GetArtistSortOrder()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOP", TagLib::String::UTF8);
		frame->setText(TagLib::StringFromCFString(metadata.GetArtistSortOrder()));
		tag->addFrame(frame);
	}

	tag->removeFrames("TSO2");
	if(metadata.GetAlbumArtistSortOrder()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSO2", TagLib::String::UTF8);
		frame->setText(TagLib::StringFromCFString(metadata.GetAlbumArtistSortOrder()));
		tag->addFrame(frame);
	}

	tag->removeFrames("TSOC");
	if(metadata.GetComposerSortOrder()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TSOC", TagLib::String::UTF8);
		frame->setText(TagLib::StringFromCFString(metadata.GetComposerSortOrder()));
		tag->addFrame(frame);
	}

	tag->removeFrames("TIT1");
	if(metadata.GetGrouping()) {
		auto frame = new TagLib::ID3v2::TextIdentificationFrame("TIT1", TagLib::String::UTF8);
		frame->setText(TagLib::StringFromCFString(metadata.GetGrouping()));
		tag->addFrame(frame);
	}

	// ReplayGain
	CFNumberRef trackGain = metadata.GetReplayGainTrackGain();
	CFNumberRef trackPeak = metadata.GetReplayGainTrackPeak();
	CFNumberRef albumGain = metadata.GetReplayGainAlbumGain();
	CFNumberRef albumPeak = metadata.GetReplayGainAlbumPeak();
	
	// Write TXXX frames
	auto trackGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_track_gain");
	auto trackPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_track_peak");
	auto albumGainFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_album_gain");
	auto albumPeakFrame = TagLib::ID3v2::UserTextIdentificationFrame::find(tag, "replaygain_album_peak");
	
	if(nullptr != trackGainFrame)
		tag->removeFrame(trackGainFrame);
	
	if(nullptr != trackPeakFrame)
		tag->removeFrame(trackPeakFrame);
	
	if(nullptr != albumGainFrame)
		tag->removeFrame(albumGainFrame);
	
	if(nullptr != albumPeakFrame)
		tag->removeFrame(albumPeakFrame);
	
	if(trackGain) {
		SFB::CFString str = CreateStringFromNumberWithFormat(trackGain, kCFNumberDoubleType, CFSTR("%+2.2f dB"));
		
		auto frame = new TagLib::ID3v2::UserTextIdentificationFrame();
		frame->setDescription("replaygain_track_gain");
		frame->setText(TagLib::StringFromCFString(str));		
		tag->addFrame(frame);
	}
	
	if(trackPeak) {		
		SFB::CFString str = CreateStringFromNumberWithFormat(trackPeak, kCFNumberDoubleType, CFSTR("%1.8f dB"));
		
		auto frame = new TagLib::ID3v2::UserTextIdentificationFrame();
		frame->setDescription("replaygain_track_peak");
		frame->setText(TagLib::StringFromCFString(str));		
		tag->addFrame(frame);
	}
	
	if(albumGain) {
		SFB::CFString str = CreateStringFromNumberWithFormat(albumGain, kCFNumberDoubleType, CFSTR("%+2.2f dB"));

		auto frame = new TagLib::ID3v2::UserTextIdentificationFrame();		
		frame->setDescription("replaygain_album_gain");
		frame->setText(TagLib::StringFromCFString(str));		
		tag->addFrame(frame);
	}
	
	if(albumPeak) {
		SFB::CFString str = CreateStringFromNumberWithFormat(albumPeak, kCFNumberDoubleType, CFSTR("%1.8f dB"));
		
		auto frame = new TagLib::ID3v2::UserTextIdentificationFrame();		
		frame->setDescription("replaygain_album_peak");
		frame->setText(TagLib::StringFromCFString(str));		
		tag->addFrame(frame);
	}
	
	// Also write the RVA2 frames
	tag->removeFrames("RVA2");
	if(trackGain) {
		auto relativeVolume = new TagLib::ID3v2::RelativeVolumeFrame();
		
		float f;
		CFNumberGetValue(trackGain, kCFNumberFloatType, &f);
		
		relativeVolume->setIdentification(TagLib::String("track", TagLib::String::Latin1));
		relativeVolume->setVolumeAdjustment(f, TagLib::ID3v2::RelativeVolumeFrame::MasterVolume);
		
		tag->addFrame(relativeVolume);
	}
	
	if(albumGain) {
		auto relativeVolume = new TagLib::ID3v2::RelativeVolumeFrame();

		float f;
		CFNumberGetValue(albumGain, kCFNumberFloatType, &f);

		relativeVolume->setIdentification(TagLib::String("album", TagLib::String::Latin1));
		relativeVolume->setVolumeAdjustment(f, TagLib::ID3v2::RelativeVolumeFrame::MasterVolume);
		
		tag->addFrame(relativeVolume);
	}

	// Album art
	if(setAlbumArt) {
		tag->removeFrames("APIC");

		for(auto attachedPicture : metadata.GetAttachedPictures()) {
			SFB::CGImageSource imageSource = CGImageSourceCreateWithData(attachedPicture->GetData(), nullptr);
			if(!imageSource)
				continue;

			TagLib::ID3v2::AttachedPictureFrame *frame = new TagLib::ID3v2::AttachedPictureFrame;

			// Convert the image's UTI into a MIME type
			SFB::CFString mimeType = UTTypeCopyPreferredTagWithClass(CGImageSourceGetType(imageSource), kUTTagClassMIMEType);
			if(mimeType)
				frame->setMimeType(TagLib::StringFromCFString(mimeType));

			frame->setPicture(TagLib::ByteVector((const char *)CFDataGetBytePtr(attachedPicture->GetData()), (TagLib::uint)CFDataGetLength(attachedPicture->GetData())));
			frame->setType((TagLib::ID3v2::AttachedPictureFrame::Type)attachedPicture->GetType());
			if(attachedPicture->GetDescription())
				frame->setDescription(TagLib::StringFromCFString(attachedPicture->GetDescription()));
			tag->addFrame(frame);
		}
	}

	return true;
}
Пример #21
0
bool mediaMP3(Artwork *art, const char *filePath)
{
    TagLib::MPEG::File f(filePath);
    if (!f.tag()) {
        return false;
    }

    if (!mediaTag(art, &f)) {
        return false;
    }
    art->filetype = FILETYPE_MP3;

    TagLib::ID3v2::Tag *id3v2tag = f.ID3v2Tag();
    if (!id3v2tag) {
        return true;
    }

    // Don't know if this is the right way to interact with
    // TagLib, but for now it work!!!
    TagLib::ID3v2::FrameList::ConstIterator it;
    TagLib::ByteVector id;
    QString str;

    for(it = id3v2tag->frameList().begin(); it != id3v2tag->frameList().end(); it ++) {
        id = (*it)->frameID();
        str = TStringToQString((*it)->toString());

        if (id == "APIC") // Album cover
        {
            if (!art->hasCover()) {
                TagLib::ID3v2::AttachedPictureFrame *f = (TagLib::ID3v2::AttachedPictureFrame*) (*it);
                saveImage(art, (const char *) f->picture().data(), f->picture().size());
            }
        }
        else if (id == "TCOM") // Composer
        {
            // composer
        }
        else if (id == "TPE2") // Album Artist
        {
            art->albumArtist = str;
        }
        else if (id == "TCMP" || id == "TCP") // iTunes, compilation
        {
            art->type = (str != "" && str != "0") ? ARTWORK_COMPILATION : ARTWORK_NORMAL;
        }
        else if (id == "TRCK") // iTunes, track # / total
        {
            iTunesSet(str, art->trackNo, art->trackCount);
        }
        else if (id == "TPOS") // iTunes, disk # / total
        {
            iTunesSet(str, art->discNo, art->discCount);
        }
        else if (id == "TSOA") // iTunes, SORT album
        {
            art->sAlbum = str;
        }
        else if (id == "TSPO") // iTunes, SORT artist
        {
            art->sArtist = str;
        }
        else if (id == "TSO2") // iTunes, SORT album artist
        {
//            art->sAlbumArtist = str;
        }
        else if (id == "TSOC") // iTunes, SORT composer
        {
            art->sComposer = str;
        }
        else if (id == "TDAT") // iTunes, ??????
        {
            // ... what is this??
        }
        else if (id == "TSIZ") // iTunes, ??????
        {
            // ... what is this??
        }
    }

    return true;
}
Meta::FieldHash
ID3v2TagHelper::tags() const
{
    Meta::FieldHash data = TagHelper::tags();

    TagLib::ID3v2::FrameList list = m_tag->frameList();
    for( TagLib::ID3v2::FrameList::ConstIterator it = list.begin(); it != list.end(); ++it )
    {
        qint64 field;
        TagLib::String frameName  = TagLib::String( ( *it )->frameID() );
        if( ( field = fieldName( frameName ) ) )
        {
            if( field == Meta::valUniqueId )
            {
                TagLib::ID3v2::UniqueFileIdentifierFrame *frame =
                        dynamic_cast< TagLib::ID3v2::UniqueFileIdentifierFrame * >( *it );

                if( !frame )
                    continue;

                QString identifier = TStringToQString( TagLib::String( frame->identifier() ) );

                if( identifier.isEmpty() )
                    continue;

                if( frame->owner() == uidFieldName( UIDAFT ) && isValidUID( identifier, UIDAFT ) )
                    data.insert( Meta::valUniqueId, identifier );
                continue;
            }
            else if( field == Meta::valHasCover )
            {
                TagLib::ID3v2::AttachedPictureFrame *frame =
                        dynamic_cast< TagLib::ID3v2::AttachedPictureFrame * >( *it );

                if( !frame )
                    continue;

                if( ( frame->type() == TagLib::ID3v2::AttachedPictureFrame::FrontCover ||
                      frame->type() == TagLib::ID3v2::AttachedPictureFrame::Other ) &&
                    frame->picture().size() > MIN_COVER_SIZE ) // must be at least 1kb
                {
                    data.insert( Meta::valHasCover, true );
                }
                continue;
            }

            TagLib::ID3v2::TextIdentificationFrame *frame =
                    dynamic_cast< TagLib::ID3v2::TextIdentificationFrame * >( *it );

            if( !frame )
                continue;

            QString value = TStringToQString( frame->fieldList().toString( '\n' ) );

            if( field == Meta::valDiscNr )
            {
                int disc;
                if( ( disc = splitDiscNr( value ).first ) )
                    data.insert( field, disc );
            }
            else if( field == Meta::valBpm )
                data.insert( field, value.toFloat() );
            else
                data.insert( field, value );
        }
        else if( frameName == POPM_Frame )
        {
            TagLib::ID3v2::PopularimeterFrame *frame =
                    dynamic_cast< TagLib::ID3v2::PopularimeterFrame * >( *it );

            if( !frame )
                continue;

            if( TStringToQString( frame->email() ).isEmpty() ) // only read anonymous ratings
            {
                // FMPS tags have precedence
                if( !data.contains( Meta::valRating ) && frame->rating() != 0 )
                    data.insert( Meta::valRating, qRound( frame->rating() / 256.0 * 10.0 ) );
                if( !data.contains( Meta::valPlaycount ) && frame->counter() < 10000 )
                    data.insert( Meta::valPlaycount, frame->counter() );
            }
        }
        else if( frameName == TXXX_Frame )
        {
            TagLib::ID3v2::UserTextIdentificationFrame *frame =
                    dynamic_cast< TagLib::ID3v2::UserTextIdentificationFrame * >( *it );

            if( !frame )
                continue;

            // the value of the user text frame is stored in the
            // second and following fields.
            TagLib::StringList fields = frame->fieldList();
            if( fields.size() >= 2 )
            {
                QString value = TStringToQString( fields[1] );

                if( fields[0] == fmpsFieldName( FMPSRating ) )
                    data.insert( Meta::valRating, qRound( value.toFloat() * 10.0 ) );
                else if( fields[0] == fmpsFieldName( FMPSScore ) )
                    data.insert( Meta::valScore, value.toFloat() * 100.0 );
                else if( fields[0] == fmpsFieldName( FMPSPlayCount ) )
                    data.insert( Meta::valPlaycount, value.toFloat() );
            }
        }
    }

    return data;
}
Пример #23
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();
}
Пример #24
0
void cMediaInfo::readTagV2(ID3v2::Tag* lpTag)
{
	m_iID3v2Version		= lpTag->header()->majorVersion();
	m_iID3v2Revision	= lpTag->header()->revisionNumber();
	m_iID3v2Size		= lpTag->header()->tagSize();

	for(ID3v2::FrameList::ConstIterator	it = lpTag->frameList().begin();it != lpTag->frameList().end();it++)
	{
		QString	szID	= QString("%1").arg((*it)->frameID().data()).left(4);

		if(!szID.compare("TIT1"))
			m_szContentGroupDescription				= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TIT2"))
			m_szTitle								= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TIT3"))
			m_szSubTitle							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TALB"))
			m_szAlbum								= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TOAL"))
			m_szOriginalAlbum						= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TRCK"))
			m_szTrackNumber							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TPOS"))
			m_szPartOfSet							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TSST"))
			m_szSubTitleOfSet						= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TSRC"))
			m_szInternationalStandardRecordingCode	= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TPE1"))
			m_szLeadArtist							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TPE2"))
			m_szBand								= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TPE3"))
			m_szConductor							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TPE4"))
			m_szInterpret							= QString::fromStdWString((*it)->toString().toWString()).split("\r\n");
		else if(!szID.compare("TOPE"))
			m_szOriginalArtist						= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TEXT"))
			m_szTextWriter							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TOLY"))
			m_szOriginalTextWriter					= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TCOM"))
			m_szComposer							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TENC"))
			m_szEncodedBy							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TBPM"))
			m_iBeatsPerMinute						= QString::fromStdWString((*it)->toString().toWString()).toInt();
		else if(!szID.compare("TLEN") && m_iLength == 0)
			m_iLength								= QString::fromStdWString((*it)->toString().toWString()).toInt();
		else if(!szID.compare("TLAN"))
				m_szLanguage						= QString::fromStdWString((*it)->toString().toWString()).split("\r\n");
		else if(!szID.compare("TCON"))
				m_szContentType						= QString::fromStdWString((*it)->toString().toWString()).split("\r\n");
		else if(!szID.compare("TFLT"))
			m_szFileType							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TMED"))
				m_szMediaType						= QString::fromStdWString((*it)->toString().toWString()).split("\r\n");
		else if(!szID.compare("TMOO"))
			m_szMood								= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TCOP"))
			m_szCopyright							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TPRO"))
			m_szProducedNotice						= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TPUB"))
			m_szPublisher							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TOWN"))
			m_szFileOwner							= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TRSN"))
			m_szInternetRadioStationName			= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TRSO"))
			m_szInternetRadioStationOwner			= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TOFN"))
			m_szOriginalFilename					= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TDLY"))
			m_iPlaylistDelay						= QString::fromStdWString((*it)->toString().toWString()).toInt();
		else if(!szID.compare("TDEN"))
			m_encodingTime							= str2TS(QString::fromStdWString((*it)->toString().toWString()));
		else if(!szID.compare("TDOR"))
			m_originalReleaseTime					= str2TS(QString::fromStdWString((*it)->toString().toWString()));
		else if(!szID.compare("TDRC"))
			m_recordingTime							= str2TS(QString::fromStdWString((*it)->toString().toWString()));
		else if(!szID.compare("TDRL"))
			m_releaseTime							= str2TS(QString::fromStdWString((*it)->toString().toWString()));
		else if(!szID.compare("TDTG"))
			m_taggingTime							= str2TS(QString::fromStdWString((*it)->toString().toWString()));
		else if(!szID.compare("TSSE"))
			m_szswhwSettings						= QString(QString::fromStdWString((*it)->toString().toWString())).split("\r\n");
		else if(!szID.compare("TSOA"))
			m_szAlbumSortOrder						= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TSOP"))
			m_szPerformerSortOrder					= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("TSOT"))
			m_szTitleSortOrder						= QString::fromStdWString((*it)->toString().toWString());
		else if(!szID.compare("SYLT"))
		{
			TagLib::ID3v2::SynchronizedLyricsFrame*	lpLyrics	= static_cast<TagLib::ID3v2::SynchronizedLyricsFrame *> (*it);

			m_szSynchronizedLyricsLanguage			= QString(lpLyrics->language().data()).left(3);
			String::Type	type					= lpLyrics->textEncoding();
			m_szSynchronizedLyricsDescription		= QString::fromStdWString(lpLyrics->description().toWString());

			TagLib::ID3v2::SynchronizedLyricsFrame::SynchedTextList	list	= lpLyrics->synchedText();

			for(ID3v2::SynchronizedLyricsFrame::SynchedTextList::ConstIterator it1 = list.begin(); it1 != list.end();it1++)
			{
				ID3v2::SynchronizedLyricsFrame::SynchedText	t	= *(it1);
				m_szSynchronizedLyrics.add(t.time, QString::fromStdWString(t.text.toWString()));
			}
		}
		else if(!szID.compare("USLT"))
		{
			TagLib::ID3v2::UnsynchronizedLyricsFrame*	lpLyrics	= static_cast<TagLib::ID3v2::UnsynchronizedLyricsFrame *> (*it);

			m_szUnsynchronizedLyricsLanguage		= QString(lpLyrics->language().data()).left(3);
			String::Type	type					= lpLyrics->textEncoding();
			m_szUnsynchronizedLyricsDescription		= QString::fromStdWString(lpLyrics->description().toWString());
			QString			szText					= QString::fromStdWString(lpLyrics->text().toWString());
			if(szText.contains("\r\n"))
				m_szUnsynchronizedLyrics			= szText.split("\r\n");
			else if(szText.contains("\r"))
				m_szUnsynchronizedLyrics			= szText.split("\r");
			else if(szText.contains("\n"))
				m_szUnsynchronizedLyrics			= szText.split("\n");
			else
				m_szUnsynchronizedLyrics.append(szText);
		}
		else if(!szID.compare("APIC"))
		{
			TagLib::ID3v2::AttachedPictureFrame*		lpPicture	= static_cast<TagLib::ID3v2::AttachedPictureFrame *> (*it);
			TagLib::ID3v2::AttachedPictureFrame::Type	t			= lpPicture->type();
			QString	szDescription;
			szDescription	= QString::fromStdWString(lpPicture->description().toWString());

			QByteArray	pictureData	= QByteArray(lpPicture->picture().data(), lpPicture->picture().size());
			m_pixmapList.add(pictureData, m_szFileName, (cPixmap::ImageType)t, szDescription);
		}
	}
}