bool CTagLoaderTagLib::ParseXiphComment(Ogg::XiphComment *xiph, EmbeddedArt *art, CMusicInfoTag& tag) { if (!xiph) return false; const Ogg::FieldListMap& fieldListMap = xiph->fieldListMap(); for (Ogg::FieldListMap::ConstIterator it = fieldListMap.begin(); it != fieldListMap.end(); ++it) { if (it->first == "ARTIST") SetArtist(tag, StringListToVectorString(it->second)); else if (it->first == "ALBUMARTIST") SetAlbumArtist(tag, StringListToVectorString(it->second)); else if (it->first == "ALBUM ARTIST") SetAlbumArtist(tag, StringListToVectorString(it->second)); else if (it->first == "ALBUM") tag.SetAlbum(it->second.front().to8Bit(true)); else if (it->first == "TITLE") tag.SetTitle(it->second.front().to8Bit(true)); else if (it->first == "TRACKNUMBER") tag.SetTrackNumber(it->second.front().toInt()); else if (it->first == "DISCNUMBER") tag.SetPartOfSet(it->second.front().toInt()); else if (it->first == "YEAR") tag.SetYear(it->second.front().toInt()); else if (it->first == "DATE") tag.SetYear(it->second.front().toInt()); else if (it->first == "GENRE") SetGenre(tag, StringListToVectorString(it->second)); else if (it->first == "COMMENT") tag.SetComment(it->second.front().to8Bit(true)); else if (it->first == "ENCODEDBY") {} else if (it->first == "ENSEMBLE") {} else if (it->first == "COMPILATION") tag.SetCompilation(it->second.front().toInt() == 1); else if (it->first == "LYRICS") tag.SetLyrics(it->second.front().to8Bit(true)); else if (it->first == "REPLAYGAIN_TRACK_GAIN") tag.SetReplayGainTrackGain((int)(atof(it->second.front().toCString(true)) * 100 + 0.5)); else if (it->first == "REPLAYGAIN_ALBUM_GAIN") tag.SetReplayGainAlbumGain((int)(atof(it->second.front().toCString(true)) * 100 + 0.5)); else if (it->first == "REPLAYGAIN_TRACK_PEAK") tag.SetReplayGainTrackPeak((float)atof(it->second.front().toCString(true))); else if (it->first == "REPLAYGAIN_ALBUM_PEAK") tag.SetReplayGainAlbumPeak((float)atof(it->second.front().toCString(true))); else if (it->first == "MUSICBRAINZ_ARTISTID") tag.SetMusicBrainzArtistID(it->second.front().to8Bit(true)); else if (it->first == "MUSICBRAINZ_ALBUMARTISTID") tag.SetMusicBrainzAlbumArtistID(it->second.front().to8Bit(true)); else if (it->first == "MUSICBRAINZ_ALBUMID") tag.SetMusicBrainzAlbumID(it->second.front().to8Bit(true)); else if (it->first == "MUSICBRAINZ_TRACKID") tag.SetMusicBrainzTrackID(it->second.front().to8Bit(true)); else if (it->first == "RATING") { // Vorbis ratings are a mess because the standard forgot to mention anything about them. // If you want to see how emotive the issue is and the varying standards, check here: // http://forums.winamp.com/showthread.php?t=324512 // The most common standard in that thread seems to be a 0-100 scale for 1-5 stars. // So, that's what we'll support for now. int iRating = it->second.front().toInt(); if (iRating > 0 && iRating <= 100) tag.SetRating((iRating / 20) + '0'); } else if (g_advancedSettings.m_logLevel == LOG_LEVEL_MAX) CLog::Log(LOGDEBUG, "unrecognized XipComment name: %s", it->first.toCString(true)); } return true; }
bool CMusicInfoTagLoaderApe::Load(const CStdString& strFileName, CMusicInfoTag& tag, EmbeddedArt *art) { try { // retrieve the APE Tag info from strFileName // and put it in tag tag.SetURL(strFileName); DVDPlayerCodec codec; if (codec.Init(strFileName, 4096)) { tag.SetDuration((int)(codec.m_TotalTime/1000)); codec.DeInit(); } CAPEv2Tag myTag; if (myTag.ReadTag((char*)strFileName.c_str())) // true to check ID3 tag as well { tag.SetTitle(myTag.GetTitle()); tag.SetAlbum(myTag.GetAlbum()); tag.SetArtist(myTag.GetArtist()); tag.SetAlbumArtist(myTag.GetAlbumArtist()); tag.SetGenre(myTag.GetGenre()); tag.SetTrackNumber(myTag.GetTrackNum()); tag.SetPartOfSet(myTag.GetDiscNum()); tag.SetComment(myTag.GetComment()); tag.SetLyrics(myTag.GetLyrics()); tag.SetMusicBrainzAlbumArtistID(myTag.GetMusicBrainzAlbumArtistID()); tag.SetMusicBrainzAlbumID(myTag.GetMusicBrainzAlbumID()); tag.SetMusicBrainzArtistID(myTag.GetMusicBrainzArtistID()); tag.SetMusicBrainzTrackID(myTag.GetMusicBrainzTrackID()); tag.SetMusicBrainzTRMID(myTag.GetMusicBrainzTRMID()); SYSTEMTIME dateTime; ZeroMemory(&dateTime, sizeof(SYSTEMTIME)); dateTime.wYear = atoi(myTag.GetYear()); tag.SetRating(myTag.GetRating()); tag.SetReleaseDate(dateTime); tag.SetLoaded(); return true; } } catch (...) { CLog::Log(LOGERROR, "Tag loader ape: exception in file %s", strFileName.c_str()); } tag.SetLoaded(false); return false; }
bool CMusicInfoTagLoaderApe::Load(const CStdString& strFileName, CMusicInfoTag& tag) { try { // retrieve the APE Tag info from strFileName // and put it in tag tag.SetURL(strFileName); CAPEv2Tag myTag; if (myTag.ReadTag((char*)strFileName.c_str())) // true to check ID3 tag as well { tag.SetTitle(myTag.GetTitle()); tag.SetAlbum(myTag.GetAlbum()); tag.SetArtist(myTag.GetArtist()); tag.SetAlbumArtist(myTag.GetAlbumArtist()); tag.SetGenre(myTag.GetGenre()); tag.SetTrackNumber(myTag.GetTrackNum()); tag.SetPartOfSet(myTag.GetDiscNum()); tag.SetComment(myTag.GetComment()); tag.SetLyrics(myTag.GetLyrics()); SYSTEMTIME dateTime; ZeroMemory(&dateTime, sizeof(SYSTEMTIME)); dateTime.wYear = atoi(myTag.GetYear()); tag.SetRating(myTag.GetRating()); tag.SetReleaseDate(dateTime); tag.SetLoaded(); return true; } } catch (...) { CLog::Log(LOGERROR, "Tag loader ape: exception in file %s", strFileName.c_str()); } tag.SetLoaded(false); return false; }
bool CTagLoaderTagLib::ParseID3v2Tag(ID3v2::Tag *id3v2, EmbeddedArt *art, CMusicInfoTag& tag) { // tag.SetURL(strFile); if (!id3v2) return false; ID3v2::AttachedPictureFrame *pictures[3] = {}; const ID3v2::FrameListMap& frameListMap = id3v2->frameListMap(); for (ID3v2::FrameListMap::ConstIterator it = frameListMap.begin(); it != frameListMap.end(); ++it) { if (it->first == "TPE1") SetArtist(tag, GetID3v2StringList(it->second)); else if (it->first == "TALB") tag.SetAlbum(it->second.front()->toString().to8Bit(true)); else if (it->first == "TPE2") SetAlbumArtist(tag, GetID3v2StringList(it->second)); else if (it->first == "TIT2") tag.SetTitle(it->second.front()->toString().to8Bit(true)); else if (it->first == "TCON") SetGenre(tag, GetID3v2StringList(it->second)); else if (it->first == "TRCK") tag.SetTrackNumber(strtol(it->second.front()->toString().toCString(true), NULL, 10)); else if (it->first == "TPOS") tag.SetPartOfSet(strtol(it->second.front()->toString().toCString(true), NULL, 10)); else if (it->first == "TYER") tag.SetYear(strtol(it->second.front()->toString().toCString(true), NULL, 10)); else if (it->first == "TCMP") tag.SetCompilation((strtol(it->second.front()->toString().toCString(true), NULL, 10) == 0) ? false : true); else if (it->first == "TENC") {} // EncodedBy else if (it->first == "TCOP") {} // Copyright message else if (it->first == "TDRC") tag.SetYear(strtol(it->second.front()->toString().toCString(true), NULL, 10)); else if (it->first == "TDRL") tag.SetYear(strtol(it->second.front()->toString().toCString(true), NULL, 10)); else if (it->first == "TDTG") {} // Tagging time else if (it->first == "TLAN") {} // Languages else if (it->first == "USLT") // Loop through any lyrics frames. Could there be multiple frames, how to choose? for (ID3v2::FrameList::ConstIterator lt = it->second.begin(); lt != it->second.end(); ++lt) { ID3v2::UnsynchronizedLyricsFrame *lyricsFrame = dynamic_cast<ID3v2::UnsynchronizedLyricsFrame *> (*lt); if (lyricsFrame) tag.SetLyrics(lyricsFrame->text().to8Bit(true)); } else if (it->first == "COMM") // Loop through and look for the main (no description) comment for (ID3v2::FrameList::ConstIterator ct = it->second.begin(); ct != it->second.end(); ++ct) { ID3v2::CommentsFrame *commentsFrame = dynamic_cast<ID3v2::CommentsFrame *> (*ct); if (commentsFrame && commentsFrame->description().isEmpty()) tag.SetComment(commentsFrame->text().to8Bit(true)); } else if (it->first == "TXXX") // Loop through and process the UserTextIdentificationFrames for (ID3v2::FrameList::ConstIterator ut = it->second.begin(); ut != it->second.end(); ++ut) { ID3v2::UserTextIdentificationFrame *frame = dynamic_cast<ID3v2::UserTextIdentificationFrame *> (*ut); if (!frame) continue; // First field is the same as the description StringList stringList = frame->fieldList(); stringList.erase(stringList.begin()); if (frame->description() == "MusicBrainz Artist Id") tag.SetMusicBrainzArtistID(stringList.front().to8Bit(true)); else if (frame->description() == "MusicBrainz Album Id") tag.SetMusicBrainzAlbumID(stringList.front().to8Bit(true)); else if (frame->description() == "MusicBrainz Album Artist Id") tag.SetMusicBrainzAlbumArtistID(stringList.front().to8Bit(true)); else if (frame->description() == "replaygain_track_gain") tag.SetReplayGainTrackGain((int)(atof(stringList.front().toCString(true)) * 100 + 0.5)); else if (frame->description() == "replaygain_album_gain") tag.SetReplayGainAlbumGain((int)(atof(stringList.front().toCString(true)) * 100 + 0.5)); else if (frame->description() == "replaygain_track_peak") tag.SetReplayGainTrackPeak((float)atof(stringList.front().toCString(true))); else if (frame->description() == "replaygain_album_peak") tag.SetReplayGainAlbumPeak((float)atof(stringList.front().toCString(true))); else if (g_advancedSettings.m_logLevel == LOG_LEVEL_MAX) CLog::Log(LOGDEBUG, "unrecognized user text tag detected: TXXX:%s", frame->description().toCString(true)); } else if (it->first == "UFID") // Loop through any UFID frames and set them for (ID3v2::FrameList::ConstIterator ut = it->second.begin(); ut != it->second.end(); ++ut) { ID3v2::UniqueFileIdentifierFrame *ufid = reinterpret_cast<ID3v2::UniqueFileIdentifierFrame*> (*ut); if (ufid->owner() == "http://musicbrainz.org") { // MusicBrainz pads with a \0, but the spec requires binary, be cautious char cUfid[64]; int max_size = std::min((int)ufid->identifier().size(), 63); strncpy(cUfid, ufid->identifier().data(), max_size); cUfid[max_size] = '\0'; tag.SetMusicBrainzTrackID(cUfid); } } else if (it->first == "APIC") // Loop through all pictures and store the frame pointers for the picture types we want for (ID3v2::FrameList::ConstIterator pi = it->second.begin(); pi != it->second.end(); ++pi) { ID3v2::AttachedPictureFrame *pictureFrame = dynamic_cast<ID3v2::AttachedPictureFrame *> (*pi); if (!pictureFrame) continue; if (pictureFrame->type() == ID3v2::AttachedPictureFrame::FrontCover) pictures[0] = pictureFrame; else if (pictureFrame->type() == ID3v2::AttachedPictureFrame::Other) pictures[1] = pictureFrame; else if (pi == it->second.begin()) pictures[2] = pictureFrame; } else if (it->first == "POPM") // Loop through and process ratings for (ID3v2::FrameList::ConstIterator ct = it->second.begin(); ct != it->second.end(); ++ct) { ID3v2::PopularimeterFrame *popFrame = dynamic_cast<ID3v2::PopularimeterFrame *> (*ct); if (!popFrame) continue; // @xbmc.org ratings trump others (of course) if (popFrame->email() == "*****@*****.**") tag.SetRating(popFrame->rating() / 51 + '0'); else if (tag.GetRating() == '0') { if (popFrame->email() != "Windows Media Player 9 Series" && popFrame->email() != "no@email" && popFrame->email() != "*****@*****.**" && popFrame->email() != "*****@*****.**") CLog::Log(LOGDEBUG, "unrecognized ratings schema detected: %s", popFrame->email().toCString(true)); tag.SetRating(POPMtoXBMC(popFrame->rating())); } } else if (g_advancedSettings.m_logLevel == LOG_LEVEL_MAX) CLog::Log(LOGDEBUG, "unrecognized ID3 frame detected: %c%c%c%c", it->first[0], it->first[1], it->first[2], it->first[3]); } // for // Process the extracted picture frames; 0 = CoverArt, 1 = Other, 2 = First Found picture for (int i = 0; i < 3; ++i) if (pictures[i]) { string mime = pictures[i]->mimeType().to8Bit(true); TagLib::uint size = pictures[i]->picture().size(); tag.SetCoverArtInfo(size, mime); if (art) art->set((const uint8_t*)pictures[i]->picture().data(), size, mime); // Stop after we find the first picture for now. break; } return true; }
bool CTagLoaderTagLib::ParseXiphComment(Ogg::XiphComment *xiph, EmbeddedArt *art, CMusicInfoTag& tag) { if (!xiph) return false; FLAC::Picture pictures[3]; const Ogg::FieldListMap& fieldListMap = xiph->fieldListMap(); for (Ogg::FieldListMap::ConstIterator it = fieldListMap.begin(); it != fieldListMap.end(); ++it) { if (it->first == "ARTIST") SetArtist(tag, StringListToVectorString(it->second)); else if (it->first == "ALBUMARTIST") SetAlbumArtist(tag, StringListToVectorString(it->second)); else if (it->first == "ALBUM ARTIST") SetAlbumArtist(tag, StringListToVectorString(it->second)); else if (it->first == "ALBUM") tag.SetAlbum(it->second.front().to8Bit(true)); else if (it->first == "TITLE") tag.SetTitle(it->second.front().to8Bit(true)); else if (it->first == "TRACKNUMBER") tag.SetTrackNumber(it->second.front().toInt()); else if (it->first == "DISCNUMBER") tag.SetPartOfSet(it->second.front().toInt()); else if (it->first == "YEAR") tag.SetYear(it->second.front().toInt()); else if (it->first == "DATE") tag.SetYear(it->second.front().toInt()); else if (it->first == "GENRE") SetGenre(tag, StringListToVectorString(it->second)); else if (it->first == "COMMENT") tag.SetComment(it->second.front().to8Bit(true)); else if (it->first == "ENCODEDBY") {} else if (it->first == "ENSEMBLE") {} else if (it->first == "COMPILATION") tag.SetCompilation(it->second.front().toInt() == 1); else if (it->first == "LYRICS") tag.SetLyrics(it->second.front().to8Bit(true)); else if (it->first == "REPLAYGAIN_TRACK_GAIN") tag.SetReplayGainTrackGain((int)(atof(it->second.front().toCString(true)) * 100 + 0.5)); else if (it->first == "REPLAYGAIN_ALBUM_GAIN") tag.SetReplayGainAlbumGain((int)(atof(it->second.front().toCString(true)) * 100 + 0.5)); else if (it->first == "REPLAYGAIN_TRACK_PEAK") tag.SetReplayGainTrackPeak((float)atof(it->second.front().toCString(true))); else if (it->first == "REPLAYGAIN_ALBUM_PEAK") tag.SetReplayGainAlbumPeak((float)atof(it->second.front().toCString(true))); else if (it->first == "MUSICBRAINZ_ARTISTID") tag.SetMusicBrainzArtistID(StringListToVectorString(it->second)); else if (it->first == "MUSICBRAINZ_ALBUMARTISTID") tag.SetMusicBrainzAlbumArtistID(StringListToVectorString(it->second)); else if (it->first == "MUSICBRAINZ_ALBUMARTIST") SetAlbumArtist(tag, StringListToVectorString(it->second)); else if (it->first == "MUSICBRAINZ_ALBUMID") tag.SetMusicBrainzAlbumID(it->second.front().to8Bit(true)); else if (it->first == "MUSICBRAINZ_TRACKID") tag.SetMusicBrainzTrackID(it->second.front().to8Bit(true)); else if (it->first == "RATING") { // Vorbis ratings are a mess because the standard forgot to mention anything about them. // If you want to see how emotive the issue is and the varying standards, check here: // http://forums.winamp.com/showthread.php?t=324512 // The most common standard in that thread seems to be a 0-100 scale for 1-5 stars. // So, that's what we'll support for now. int iRating = it->second.front().toInt(); if (iRating > 0 && iRating <= 100) tag.SetRating((iRating / 20) + '0'); } else if (it->first == "METADATA_BLOCK_PICTURE") { const char* b64 = it->second.front().toCString(); std::string decoded_block = Base64::Decode(b64, it->second.front().size()); ByteVector bv(decoded_block.data(), decoded_block.size()); TagLib::FLAC::Picture* pictureFrame = new TagLib::FLAC::Picture(bv); if (pictureFrame->type() == FLAC::Picture::FrontCover) pictures[0].parse(bv); else if (pictureFrame->type() == FLAC::Picture::Other) pictures[1].parse(bv); delete pictureFrame; } else if (it->first == "COVERART") { const char* b64 = it->second.front().toCString(); std::string decoded_block = Base64::Decode(b64, it->second.front().size()); ByteVector bv(decoded_block.data(), decoded_block.size()); pictures[2].setData(bv); // Assume jpeg if (pictures[2].mimeType().isEmpty()) pictures[2].setMimeType("image/jpeg"); } else if (it->first == "COVERARTMIME") { pictures[2].setMimeType(it->second.front()); } else if (g_advancedSettings.m_logLevel == LOG_LEVEL_MAX) CLog::Log(LOGDEBUG, "unrecognized XipComment name: %s", it->first.toCString(true)); } // Process the extracted picture frames; 0 = CoverArt, 1 = Other, 2 = COVERART/COVERARTMIME for (int i = 0; i < 3; ++i) if (pictures[i].data().size()) { string mime = pictures[i].mimeType().toCString(); if (mime.find("image/") != 0) continue; TagLib::uint size = pictures[i].data().size(); tag.SetCoverArtInfo(size, mime); if (art) art->set((const uint8_t*)pictures[i].data().data(), size, mime); break; } return true; }