void FileHelper::setFlacAttribute(const std::string &attribute, const QString &value) { if (TagLib::FLAC::File *flacFile = static_cast<TagLib::FLAC::File*>(_file)) { if (flacFile->hasID3v2Tag()) { TagLib::ID3v2::Tag *tag = flacFile->ID3v2Tag(); QString key = this->convertKeyToID3v2Key(attribute.data()); TagLib::ID3v2::FrameList l = tag->frameListMap()[key.toStdString().data()]; if (!l.isEmpty()) { tag->removeFrame(l.front()); } TagLib::ID3v2::TextIdentificationFrame *tif = new TagLib::ID3v2::TextIdentificationFrame(TagLib::ByteVector("ARTISTALBUM")); tif->setText(value.toStdString().data()); tag->addFrame(tif); } else if (flacFile->hasID3v1Tag()) { qDebug() << Q_FUNC_INFO << "Not implemented (FLAC ID3v1)"; } else if (flacFile->hasXiphComment()) { TagLib::Ogg::XiphComment *xiph = flacFile->xiphComment(); if (value.isEmpty()) { xiph->removeField(attribute.data()); } else { xiph->addField(attribute.data(), value.toStdString().data()); } } } }
unsigned TagWriter::Write(const std::string& filename, const db::Recordset *tags) { util::Mutex::Lock lock(s_taglib_mutex); // TRACE << "Opening '" << filename << "'\n"; typedef std::map<std::string, std::string> tagmap_t; tagmap_t tagmap; for (unsigned int i=0; i<mediadb::FIELD_COUNT; ++i) { const char *s = import::GetVorbisTagForField(i); if (s) { std::string val = tags->GetString(i); if (!val.empty()) tagmap[s] = val; } } TagLib::FLAC::File tff(filename.c_str()); TagLib::Ogg::XiphComment *oxc = tff.xiphComment(true); for (tagmap_t::const_iterator i = tagmap.begin(); i != tagmap.end(); ++i) { if (!i->second.empty()) oxc->addField(i->first, TagLib::String(i->second, TagLib::String::UTF8)); } tff.save(); TRACE << "FLAC file tagged\n"; return 0; }
/** Set or remove any rating. */ void FileHelper::setRating(int rating) { switch (_fileType) { case EXT_MP3: { TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file); if (mpegFile->hasID3v2Tag()) { this->setRatingForID3v2(rating, mpegFile->ID3v2Tag()); } 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); if (flacFile->hasID3v2Tag()) { this->setRatingForID3v2(rating, flacFile->ID3v2Tag()); } else if (flacFile->hasID3v1Tag()) { qDebug() << Q_FUNC_INFO << "hasID3v1Tag"; } else if (flacFile->hasXiphComment()) { TagLib::Ogg::XiphComment *xiph = flacFile->xiphComment(); if (rating == 0) { xiph->removeField("RATING"); } else { xiph->addField("RATING", QString::number(rating).toStdString()); } } break; } default: break; } this->save(); }
/** * Get the various artist information form a tag. * * @param filetype the type-id for the file that is being processed * @param tagtype the tag-type to use (if more than one are used per filetype) * @param tag the tag-data from taglib * * @return a proper multi-artist data field or a NULL pointer. * @sideeffects none */ const char * taggit_tag_va(enum file_type filetype, int tagtype, TagLib_Tag *tag) { /* * At this point, we already figured out the file type *and* decided which * tag to read from the file. `filetype' and `tagtype' give us hints into * the right direction. We *must* follow these hints or things will blow * up. */ TagLib::APE::Tag *ape; TagLib::ID3v2::Tag *v2; TagLib::Ogg::XiphComment *ogg; const char *ret; ret = (const char *)NULL; switch (filetype) { case FT_MPEG: if (tagtype & MP3_ID3V2) { /** * @TODO Add support for TCMP (and maybe TCP) frames. * It's probably best to only check for those if TPE2 is empty, * because otherwise we already consider the track part of a * compilation. */ v2 = reinterpret_cast<TagLib::ID3v2::Tag *>(tag); if (v2->frameListMap()["TPE2"].isEmpty()) break; ret = xstrdup( v2->frameListMap()["TPE2"].front()->toString().toCString()); } else if (tagtype & MP3_APE) { ape = reinterpret_cast<TagLib::APE::Tag *>(tag); if (ape->itemListMap()["ALBUMARTIST"].isEmpty()) break; ret = xstrdup( ape->itemListMap()["ALBUMARTIST"].toString().toCString()); } /* Don't care about ID3v1 */ break; case FT_OGGVORBIS: /* @FALLTHROUGH@ */ case FT_OGGFLAC: /* ogg* files share the same tag format */ ogg = reinterpret_cast<TagLib::Ogg::XiphComment *>(tag); if (ogg->fieldListMap()["ALBUMARTIST"].isEmpty()) break; ret = xstrdup( ogg->fieldListMap()["ALBUMARTIST"].toString().toCString()); break; case FT_FLAC: /* AFAIK, there is no native FLAC tag format */ break; default: break; } return ret; }
/*! * \copydoc MetaIO::read() */ Metadata* MetaIOFLACVorbis::read(QString filename) { TagLib::FLAC::File *flacfile = OpenFile(filename); if (!flacfile) return NULL; TagLib::Ogg::XiphComment *tag = flacfile->xiphComment(); if (!tag) { delete flacfile; return NULL; } Metadata *metadata = new Metadata(filename); ReadGenericMetadata(tag, metadata); bool compilation = false; if (tag->contains("COMPILATION_ARTIST")) { QString compilation_artist = TStringToQString( tag->fieldListMap()["COMPILATION_ARTIST"].toString()).trimmed(); if (compilation_artist != metadata->Artist()) { metadata->setCompilationArtist(compilation_artist); compilation = true; } } if (!compilation && tag->contains("MUSICBRAINZ_ALBUMARTISTID")) { QString musicbrainzcode = TStringToQString( tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString()).trimmed(); if (musicbrainzcode == MYTH_MUSICBRAINZ_ALBUMARTIST_UUID) compilation = true; } metadata->setCompilation(compilation); if (metadata->Length() <= 0) { TagLib::FileRef *fileref = new TagLib::FileRef(flacfile); metadata->setLength(getTrackLength(fileref)); // FileRef takes ownership of flacfile, and is responsible for it's // deletion. Messy. delete fileref; } else delete flacfile; return metadata; }
void FileHelper::setArtistAlbum(const QString &artistAlbum) { switch (_fileType) { case EXT_FLAC: { this->setFlacAttribute("ALBUMARTIST", artistAlbum); break; } case EXT_MP4:{ TagLib::StringList l; l.append(artistAlbum.toStdString().data()); TagLib::MP4::Item item(l); this->setMp4Attribute("aART", item); break; } case EXT_MPC: //mpcFile = static_cast<MPC::File*>(f); qDebug() << Q_FUNC_INFO << "Not implemented for MPC"; break; case EXT_MP3:{ TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file); if (mpegFile->hasID3v2Tag()) { TagLib::ID3v2::Tag *tag = mpegFile->ID3v2Tag(); QString convertedKey = this->convertKeyToID3v2Key("ARTISTALBUM"); TagLib::ID3v2::FrameList l = tag->frameListMap()[convertedKey.toStdString().data()]; if (!l.isEmpty()) { tag->removeFrame(l.front()); } TagLib::ID3v2::TextIdentificationFrame *tif = new TagLib::ID3v2::TextIdentificationFrame(TagLib::ByteVector(convertedKey.toStdString().data())); tif->setText(artistAlbum.toStdString().data()); tag->addFrame(tif); } else if (mpegFile->hasID3v1Tag()) { qDebug() << Q_FUNC_INFO << "Not implemented for ID3v1Tag"; } break; } case EXT_OGG: { TagLib::Ogg::XiphComment *xiphComment = static_cast<TagLib::Ogg::XiphComment*>(_file->tag()); if (xiphComment) { xiphComment->addField("ALBUMARTIST", artistAlbum.toStdString().data()); } else { qDebug() << Q_FUNC_INFO << "Not implemented for this OGG file"; } break; } default: qDebug() << Q_FUNC_INFO << "Not implemented for this type of file"; break; } }
/*! * \copydoc MetaIO::read() */ Metadata* MetaIOOggVorbis::read(const QString &filename) { TagLib::Ogg::Vorbis::File *oggfile = OpenFile(filename); if (!oggfile) return NULL; TagLib::Ogg::XiphComment *tag = oggfile->tag(); if (!tag) { delete oggfile; return NULL; } Metadata *metadata = new Metadata(filename); ReadGenericMetadata(tag, metadata); bool compilation = false; if (tag->contains("COMPILATION_ARTIST")) { QString compilation_artist = TStringToQString( tag->fieldListMap()["COMPILATION_ARTIST"].toString()).trimmed(); if (compilation_artist != metadata->Artist()) { metadata->setCompilationArtist(compilation_artist); compilation = true; } } if (!compilation && tag->contains("MUSICBRAINZ_ALBUMARTISTID")) { QString musicbrainzcode = TStringToQString( tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString()).trimmed(); if (musicbrainzcode == MYTH_MUSICBRAINZ_ALBUMARTIST_UUID) compilation = true; } metadata->setCompilation(compilation); if (metadata->Length() <= 0) metadata->setLength(getTrackLength(oggfile)); else delete oggfile; return metadata; }
/** * Set the file-type specific `compilation' tag. * * See the discussion on top of this file about which tag is used * for which file-type. * * @param file taggit's file-data structure for the processed file * @param tag the tag-data decoded by `taglib_file_tag()' * @param value the string the user wants to set this tag to * * @return void * @sideeffects none */ void taggit_tag_set_compilation(struct taggit_file *file, TagLib_Tag *tag, const char *value) { TagLib::APE::Tag *ape; TagLib::ID3v2::Tag *v2; TagLib::Ogg::XiphComment *ogg; const char *ret; TagLib::MPEG::File::File *f; int mask; ret = (const char *)NULL; switch (file->type) { case FT_MPEG: f = reinterpret_cast<TagLib::MPEG::File::File *>(file->data); mask = setup_get_write_mask(FT_MPEG); if (mask & MP3_ID3V2) { v2 = f->ID3v2Tag(); v2->removeFrames("TPE2"); /* * @TODO: I don't know how to figure out the proper `encoding' * value here. Should we look at the `unicodeStrings' * variable from taglib? */ TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TPE2", TagLib::String::Latin1); frame->setText(value); v2->addFrame(frame); } if (mask & MP3_APE) { ape = f->APETag(); ape->addValue("ALBUMARTIST", value, true); } break; case FT_OGGVORBIS: /* @FALLTHROUGH@ */ case FT_OGGFLAC: ogg = reinterpret_cast<TagLib::Ogg::XiphComment *>(tag); ogg->addField("ALBUMARTIST", value, true); break; default: break; } }
/** Set or remove any disc number. */ void FileHelper::setDiscNumber(const QString &disc) { switch (_fileType) { case EXT_FLAC: { this->setFlacAttribute("DISCNUMBER", disc); break; } case EXT_OGG: { TagLib::Ogg::XiphComment *xiphComment = static_cast<TagLib::Ogg::XiphComment*>(_file->tag()); if (xiphComment) { xiphComment->addField("DISCNUMBER", disc.toStdString().data()); } else { qDebug() << Q_FUNC_INFO << "Not implemented for this OGG file"; } break; } case EXT_MP3: { TagLib::MPEG::File *mpegFile = static_cast<TagLib::MPEG::File*>(_file); if (mpegFile && mpegFile->hasID3v2Tag()) { // Remove existing disc number if one has set an empty string if (disc.isEmpty()) { mpegFile->ID3v2Tag()->removeFrames(TagLib::ByteVector("TPOS")); } else { TagLib::ID3v2::TextIdentificationFrame *f = new TagLib::ID3v2::TextIdentificationFrame(TagLib::ByteVector("TPOS")); f->setText(disc.toStdString()); mpegFile->ID3v2Tag()->addFrame(f); } } break; } case EXT_MP4: { TagLib::MP4::Item item(disc.toUInt()); this->setMp4Attribute("disk", item); break; } default: qDebug() << Q_FUNC_INFO << "Not implemented for this file type"; break; } }
QPixmap VorbisMetaDataModel::cover() { TagLib::Ogg::Vorbis::File file(m_path.toLocal8Bit().constData()); TagLib::Ogg::XiphComment *tag = file.tag(); if(!tag) return QPixmap(); TagLib::StringList list = tag->fieldListMap()["METADATA_BLOCK_PICTURE"]; if(list.isEmpty()) return QPixmap(); for(uint i = 0; i < list.size(); ++i) { TagLib::String value = list[i]; QByteArray block = QByteArray::fromBase64(TStringToQString_qt4(value).toAscii()); if(block.size() < 32) continue; qint64 pos = 0; if(readPictureBlockField(block, pos) != 3) //picture type, use front cover only continue; pos += 4; int mimeLength = readPictureBlockField(block, pos); //mime type length pos += 4; pos += mimeLength; //skip mime type int descLength = readPictureBlockField(block, pos); //description length pos += 4; pos += descLength; //skip description pos += 4; //width pos += 4; //height pos += 4; //color depth pos += 4; //the number of colors used int length = readPictureBlockField(block, pos); //picture size pos += 4; QPixmap cover; cover.loadFromData(block.mid(pos, length)); //read binary picture data return cover; } return QPixmap(); }
/*! * \copydoc MetaIO::write() */ bool MetaIOFLACVorbis::write(Metadata* mdata) { if (!mdata) return false; TagLib::FLAC::File *flacfile = OpenFile(mdata->Filename()); if (!flacfile) return false; TagLib::Ogg::XiphComment *tag = flacfile->xiphComment(); if (!tag) { delete flacfile; return false; } WriteGenericMetadata(tag, mdata); // Compilation if (mdata->Compilation()) { tag->addField("MUSICBRAINZ_ALBUMARTISTID", MYTH_MUSICBRAINZ_ALBUMARTIST_UUID, true); tag->addField("COMPILATION_ARTIST", QStringToTString(mdata->CompilationArtist()), true); } else { // Don't remove the musicbrainz field unless it indicated a compilation if (tag->contains("MUSICBRAINZ_ALBUMARTISTID") && (tag->fieldListMap()["MUSICBRAINZ_ALBUMARTISTID"].toString() == MYTH_MUSICBRAINZ_ALBUMARTIST_UUID)) { tag->removeField("MUSICBRAINZ_ALBUMARTISTID"); } tag->removeField("COMPILATION_ARTIST"); } bool result = flacfile->save(); if (flacfile) delete flacfile; return (result); }
int main(int argc, char *argv[]) { for(int i = 1; i < argc; i++) { cout << "******************** \"" << argv[i] << "\" ********************" << endl; TagLib::FileRef f(argv[i]); if(!f.isNull() && f.tag()) { TagLib::Tag *tag = f.tag(); cout << "-- TAG --" << endl; cout << "title - \"" << tag->title() << "\"" << endl; cout << "artist - \"" << tag->artist() << "\"" << endl; cout << "album artist - \"" << tag->albumArtist() << "\"" << endl; cout << "album - \"" << tag->album() << "\"" << endl; cout << "year - \"" << tag->year() << "\"" << endl; cout << "comment - \"" << tag->comment() << "\"" << endl; cout << "track - \"" << tag->track() << "\"" << endl; cout << "genre - \"" << tag->genre() << "\"" << endl; cout << "grouping - \"" << tag->grouping() << "\"" << endl; TagLib::Ogg::XiphComment *comment = NULL; TagLib::FLAC::File *flac = dynamic_cast<TagLib::FLAC::File *>(f.file()); if (flac) { cout << "flac:" << endl; cout << "id3v1 - \"" << flac->ID3v1Tag() << "\"" << endl; cout << "id3v2 - \"" << flac->ID3v2Tag() << "\"" << endl; cout << "xiph - \"" << flac->xiphComment() << "\"" << endl; comment = flac->xiphComment(); } if (!comment) { comment = dynamic_cast<TagLib::Ogg::XiphComment *>(tag); } if (comment) { TagLib::Ogg::FieldListMap fields = comment->fieldListMap(); for(TagLib::Ogg::FieldListMap::ConstIterator it = fields.begin(), end = fields.end(); it != end; it++) { if (!it->second.isEmpty()) cout << "xiph:" << it->first << " \"" << it->second[0].substr(0,3) << "\"" << endl; } } cout << "pictures- \"" << f.file()->pictures().size() << "\"" << endl; TagLib::File::PictureList l = f.file()->pictures(); for (TagLib::File::_PictureList::ConstIterator i = l.begin(), end = l.end(); i != end; i++) { cout << "\t" << (*i)->typeName() << ' ' << (*i)->mimeType() << ' ' << (*i)->base64data().size() << endl; } cout << "pictures- \"" << tag->pictures().size() << "\"" << endl; } if(!f.isNull() && f.audioProperties()) { TagLib::AudioProperties *properties = f.audioProperties(); int seconds = properties->length() % 60; int minutes = (properties->length() - seconds) / 60; cout << "-- AUDIO --" << endl; cout << "bitrate - " << properties->bitrate() << endl; cout << "sample rate - " << properties->sampleRate() << endl; cout << "channels - " << properties->channels() << endl; cout << "length - " << minutes << ":" << formatSeconds(seconds) << endl; } } return 0; }