From d4fd4122cc3801ae55523d3f856e1b9768152502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=96nnerby?= Date: Sun, 6 Apr 2008 22:10:36 +0000 Subject: [PATCH] Fixing some issues with old id3v1 genre numbers in some rare mp3s. Added a generic way of getting all of taglibs supported files. --- src/contrib/taglib_plugin/TagReaderTaglib.cpp | 403 ++++++++++++------ src/contrib/taglib_plugin/TagReaderTaglib.h | 24 +- 2 files changed, 286 insertions(+), 141 deletions(-) diff --git a/src/contrib/taglib_plugin/TagReaderTaglib.cpp b/src/contrib/taglib_plugin/TagReaderTaglib.cpp index ca3072f1d..83493453f 100644 --- a/src/contrib/taglib_plugin/TagReaderTaglib.cpp +++ b/src/contrib/taglib_plugin/TagReaderTaglib.cpp @@ -36,19 +36,25 @@ #include "TagReaderTaglib.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include #include #include @@ -65,15 +71,22 @@ TagReaderTaglib::~TagReaderTaglib(void){ } void TagReaderTaglib::Destroy() { - // do nothing! we use a static reference for every call to GetMetaDataReader() delete this; } -bool TagReaderTaglib::CanReadTag(const wchar_t *extension){ +bool TagReaderTaglib::CanReadTag(const utfchar *extension){ if(extension){ -// std::wstring ext(extension); - if( std::wstring(_T("mp3"))==extension ) { + utfstring ext(extension); + + boost::algorithm::to_lower(ext); // Convert to lower case + + if( ext==UTF("mp3") || + ext==UTF("ogg") || + ext==UTF("flac") || + ext==UTF("ape") || + ext==UTF("mpc") + ) { return true; } } @@ -82,133 +95,218 @@ bool TagReaderTaglib::CanReadTag(const wchar_t *extension){ } - bool TagReaderTaglib::ReadTag(musik::core::Track *track){ - TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF16); - if(std::wstring(_T("mp3"))==track->GetValue("extension")){ - TagLib::MPEG::File oFile(track->GetValue("path")); - TagLib::ID3v2::Tag *oTagv2 = oFile.ID3v2Tag(); - TagLib::AudioProperties *oAudioProperties = oFile.audioProperties(); - if(oTagv2){ - // Successfully found ID3V2-tag + const utfchar *extension = track->GetValue("extension"); + if(extension){ + utfstring ext(extension); + boost::algorithm::to_lower(ext); // Convert to lower case + + if(ext==UTF("mp3")) + if(this->GetID3v2Tag(track)) + return true; - ////////////////////////////////////////////////////////////////////////////// - // Get ALL tags in a map - TagLib::ID3v2::FrameListMap aAllTags = oTagv2->frameListMap(); + if(ext==UTF("ogg")) + if(this->GetOGGTag(track)) + return true; - if(!oTagv2->title().isEmpty()){ - this->SetTagValue("title",oTagv2->title(),track); - }else{ - this->SetTagValue("title",track->GetValue("filename"),track); - } - this->SetTagValue("album",oTagv2->album(),track); + } - ////////////////////////////////////////////////////////////////////////////// - // YEAR - if( !aAllTags["TYER"].isEmpty() ) // ID3 2.3 - this->SetTagValue("year",aAllTags["TYER"].front()->toString().substr(0,4),track); - if( !aAllTags["TDRC"].isEmpty() ) // ID3 2.4 - this->SetTagValue("year",aAllTags["TDRC"].front()->toString().substr(0,4),track); + if(this->GetGenericTag(track)) // secondary: try generic way + return true; + + return false; +} + +bool TagReaderTaglib::GetOGGTag(musik::core::Track *track){ +/* + TagLib::Ogg::File file(track->GetValue("path")); +*/ + return false; +} - // TODO: Handle the "totaltracks" part of the TRCK - this->SetTagValues("track",aAllTags["TRCK"],track); - - this->SetTagValues("bpm",aAllTags["TBPM"],track); - this->SetSlashSeparatedValues("composer",aAllTags["TCOM"],track); - this->SetTagValues("copyright",aAllTags["TCOP"],track); - this->SetTagValues("encoder",aAllTags["TENC"],track); - this->SetTagValues("writer",aAllTags["TEXT"],track); - this->SetTagValues("org.writer",aAllTags["TOLY"],track); - this->SetSlashSeparatedValues("publisher",aAllTags["TPUB"],track); - this->SetTagValues("mood",aAllTags["TMOO"],track); - this->SetSlashSeparatedValues("org.artist",aAllTags["TOPE"],track); - this->SetTagValues("language",aAllTags["TLAN"],track); - this->SetTagValues("disc",aAllTags["TPOS"],track); - - ////////////////////////////////////////////////////////////////////////////// - // GENRES - if(!aAllTags["TCON"].isEmpty()){ - TagLib::ID3v2::FrameList genres = aAllTags["TCON"]; - for(TagLib::ID3v2::FrameList::ConstIterator genreFrame=genres.begin();genreFrame!=genres.end();++genreFrame){ - TagLib::String genreString = (*genreFrame)->toString(); - if( !genreString.isEmpty() ){ - // There is no need to worry about duplicates, since the Track will not save duplicates. - // TODO: This should also handle old ID3v1 genre numbers - bool isNumber(true); - for(TagLib::String::ConstIterator charIt=genreString.begin();isNumber && charIt!=genreString.end();++charIt){ - isNumber = *charIt >= '0' && *charIt <= '9'; - } - - if(isNumber){ - int genreNumber = genreString.toInt(); - if(genreNumber>= 0 && genreNumber<= 255) - genreString = TagLib::ID3v1::genre(genreNumber); - } - this->SetTagValue("genre",genreString,track); - - } - } - } - - ////////////////////////////////////////////////////////////////////////////// - // ARTISTS - this->SetSlashSeparatedValues("artist",aAllTags["TPE1"],track); - this->SetSlashSeparatedValues("artist",aAllTags["TPE2"],track); - this->SetSlashSeparatedValues("conductor",aAllTags["TPE3"],track); - this->SetSlashSeparatedValues("interpreted",aAllTags["TPE4"],track); - - ////////////////////////////////////////////////////////////////////////////// - // Audio properties - if(oAudioProperties){ - std::wstring duration = boost::str(boost::wformat(_T("%1%"))%oAudioProperties->length()); - this->SetTagValue("duration",duration,track); - } - - ////////////////////////////////////////////////////////////////////////////// - // Comments - TagLib::ID3v2::FrameList comments = aAllTags["COMM"]; - for(TagLib::ID3v2::FrameList::Iterator commentFrame=comments.begin();commentFrame!=comments.end();++commentFrame){ - TagLib::ID3v2::CommentsFrame *comment = dynamic_cast (*commentFrame); - - TagLib::String temp = comment->description(); - std::wstring description(temp.begin(),temp.end()); - - if(description.empty()){ - this->SetTagValue("comment",comment->toString(),track); - }else if(description==_T("MusicMatch_Mood")){ - this->SetTagValue("mood",comment->toString(),track); - }else if(description==_T("MusicMatch_Preference")){ - this->SetTagValue("textrating",comment->toString(),track); - } - } - - ////////////////////////////////////////////////////////////////////////////// - // Thumbnail - TagLib::ID3v2::FrameList pictures = aAllTags["APIC"]; - if(!pictures.isEmpty()){ - // Thumbnail exists - // Just get the front() picture - TagLib::ID3v2::AttachedPictureFrame *picture = static_cast(pictures.front()); - TagLib::ByteVector pictureData = picture->picture(); - DBINT size = pictureData.size(); - - if(size>32){ // Noticed that some id3tags have like a 4-8 byte size with no thumbnail - char *data = pictureData.data(); - track->SetThumbnail(data,size); - } - - } - - - return true; +bool TagReaderTaglib::GetGenericTag(musik::core::Track *track){ + TagLib::FileRef oFile(track->GetValue("path")); + TagLib::Tag *tag = oFile.tag(); + TagLib::AudioProperties *oAudioProperties = oFile.audioProperties(); + if(tag){ + // TITLE + if(!tag->title().isEmpty()){ + this->SetTagValue("title",tag->title(),track); + }else{ + this->SetTagValue("title",track->GetValue("filename"),track); } + + // ALBUM + this->SetTagValue("album",tag->album(),track); + // ARTISTS + this->SetSlashSeparatedValues("artist",tag->album(),track); + // GENRES + this->SetTagValue("genre",tag->genre(),track); + // COMMENT + this->SetTagValue("comment",tag->comment(),track); + // TRACK + this->SetTagValue("track",tag->track(),track); + // TRACK + this->SetTagValue("year",tag->year(),track); + + this->SetAudioProperties(oAudioProperties,track); + + return true; + } + + return false; + +} + + +bool TagReaderTaglib::GetID3v2Tag(musik::core::Track *track){ + #ifdef UTF_WIDECHAR + TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF16); + #else + TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding(TagLib::String::UTF8); + #endif + + TagLib::MPEG::File oFile(track->GetValue("path")); + TagLib::ID3v2::Tag *oTagv2 = oFile.ID3v2Tag(); + if(oTagv2){ + + TagLib::AudioProperties *oAudioProperties = oFile.audioProperties(); + // Successfully found ID3V2-tag + + ////////////////////////////////////////////////////////////////////////////// + // Get ALL tags in a map + TagLib::ID3v2::FrameListMap aAllTags = oTagv2->frameListMap(); + + if(!oTagv2->title().isEmpty()){ + this->SetTagValue("title",oTagv2->title(),track); + }else{ + this->SetTagValue("title",track->GetValue("filename"),track); + } + this->SetTagValue("album",oTagv2->album(),track); + + ////////////////////////////////////////////////////////////////////////////// + // YEAR + if( !aAllTags["TYER"].isEmpty() ) // ID3 2.3 + this->SetTagValue("year",aAllTags["TYER"].front()->toString().substr(0,4),track); + if( !aAllTags["TDRC"].isEmpty() ) // ID3 2.4 + this->SetTagValue("year",aAllTags["TDRC"].front()->toString().substr(0,4),track); + + + // TODO: Handle the "totaltracks" part of the TRCK + this->SetTagValues("track",aAllTags["TRCK"],track); + + this->SetTagValues("bpm",aAllTags["TBPM"],track); + this->SetSlashSeparatedValues("composer",aAllTags["TCOM"],track); + this->SetTagValues("copyright",aAllTags["TCOP"],track); + this->SetTagValues("encoder",aAllTags["TENC"],track); + this->SetTagValues("writer",aAllTags["TEXT"],track); + this->SetTagValues("org.writer",aAllTags["TOLY"],track); + this->SetSlashSeparatedValues("publisher",aAllTags["TPUB"],track); + this->SetTagValues("mood",aAllTags["TMOO"],track); + this->SetSlashSeparatedValues("org.artist",aAllTags["TOPE"],track); + this->SetTagValues("language",aAllTags["TLAN"],track); + this->SetTagValues("disc",aAllTags["TPOS"],track); + + ////////////////////////////////////////////////////////////////////////////// + // GENRES + if(!aAllTags["TCON"].isEmpty()){ + TagLib::ID3v2::FrameList genres = aAllTags["TCON"]; + for(TagLib::ID3v2::FrameList::ConstIterator genreFrame=genres.begin();genreFrame!=genres.end();++genreFrame){ + TagLib::String genreString = (*genreFrame)->toString(); + if( !genreString.isEmpty() ){ + // There is no need to worry about duplicates, since the Track will not save duplicates. + // TODO: This should also handle old ID3v1 genre numbers + int numberLength(0); + bool isNumber(true); + for(TagLib::String::ConstIterator charIt=genreString.begin();isNumber && charIt!=genreString.end();++charIt){ + isNumber = *charIt >= '0' && *charIt <= '9'; + ++numberLength; + } + + if(!isNumber && numberLength>0){ + // Genre starting with a number + if(genreString.substr(numberLength,1)==" "){ + // If the genre number is appended by a space, then this is a id3v1 genre number + int genreNumber = genreString.substr(0,numberLength).toInt(); + if(genreNumber>= 0 && genreNumber<= 255) + this->SetTagValue("genre",TagLib::ID3v1::genre(genreNumber),track); + + // Remove the prepending genre number + genreString = genreString.substr(numberLength+1); + } + } + + if(isNumber){ + // If the genre is only a number, look for id3v1 genres + int genreNumber = genreString.toInt(); + if(genreNumber>= 0 && genreNumber<= 255) + genreString = TagLib::ID3v1::genre(genreNumber); + } + + if( !genreString.isEmpty() ){ + this->SetTagValue("genre",genreString,track); + } + + } + } + } + + ////////////////////////////////////////////////////////////////////////////// + // ARTISTS + this->SetSlashSeparatedValues("artist",aAllTags["TPE1"],track); + this->SetSlashSeparatedValues("artist",aAllTags["TPE2"],track); + this->SetSlashSeparatedValues("conductor",aAllTags["TPE3"],track); + this->SetSlashSeparatedValues("interpreted",aAllTags["TPE4"],track); + + ////////////////////////////////////////////////////////////////////////////// + // Audio properties + this->SetAudioProperties(oAudioProperties,track); + + ////////////////////////////////////////////////////////////////////////////// + // Comments + TagLib::ID3v2::FrameList comments = aAllTags["COMM"]; + for(TagLib::ID3v2::FrameList::Iterator commentFrame=comments.begin();commentFrame!=comments.end();++commentFrame){ + TagLib::ID3v2::CommentsFrame *comment = dynamic_cast (*commentFrame); + + TagLib::String temp = comment->description(); + std::wstring description(temp.begin(),temp.end()); + + if(description.empty()){ + this->SetTagValue("comment",comment->toString(),track); + }else if(description==_T("MusicMatch_Mood")){ + this->SetTagValue("mood",comment->toString(),track); + }else if(description==_T("MusicMatch_Preference")){ + this->SetTagValue("textrating",comment->toString(),track); + } + } + + ////////////////////////////////////////////////////////////////////////////// + // Thumbnail + TagLib::ID3v2::FrameList pictures = aAllTags["APIC"]; + if(!pictures.isEmpty()){ + // Thumbnail exists + // Just get the front() picture + TagLib::ID3v2::AttachedPictureFrame *picture = static_cast(pictures.front()); + TagLib::ByteVector pictureData = picture->picture(); + DBINT size = pictureData.size(); + + if(size>32){ // Noticed that some id3tags have like a 4-8 byte size with no thumbnail + char *data = pictureData.data(); + track->SetThumbnail(data,size); + } + + } + + + return true; } +/* } // Try the "generic" way -/* TagLib::FileRef oFile(oMedia->sFileFullPath.c_str()); + TagLib::FileRef oFile(oMedia->sFileFullPath.c_str()); TagLib::Tag *oTag = oFile.tag(); TagLib::AudioProperties *oAudioProperties = oFile.audioProperties(); if(oTag && oAudioProperties){ @@ -232,6 +330,11 @@ void TagReaderTaglib::SetTagValue(const char* key,const wchar_t* string,musik::c track->SetValue(key,string); } +void TagReaderTaglib::SetTagValue(const char* key,const int tagInt,musik::core::Track *track){ + std::wstring temp = boost::str(boost::wformat(_T("%1%"))%tagInt); + track->SetValue(key,temp.c_str()); +} + void TagReaderTaglib::SetTagValues(const char* key,const TagLib::ID3v2::FrameList &frame,musik::core::Track *track){ using namespace musik::core; @@ -263,12 +366,25 @@ bool TagReaderTaglib::getStandardTags(musik::core::Track &track,TagLib::Tag *oTa return true; } */ +void TagReaderTaglib::SetSlashSeparatedValues(const char* key,TagLib::String &tagString,musik::core::Track *track){ + if( !tagString.isEmpty() ){ + std::wstring value(tagString.begin(),tagString.end()); + std::vector splitValues; + + boost::algorithm::split(splitValues,value,boost::algorithm::is_any_of(_T("/"))); + + for(std::vector::iterator theValue=splitValues.begin();theValue!=splitValues.end();++theValue){ + track->SetValue(key,theValue->c_str()); + } + } +} + void TagReaderTaglib::SetSlashSeparatedValues(const char* key,const TagLib::ID3v2::FrameList &frame,musik::core::Track *track){ using namespace musik::core; if(!frame.isEmpty()){ for(TagLib::ID3v2::FrameList::ConstIterator frameValue=frame.begin();frameValue!=frame.end();++frameValue){ TagLib::String tagString = (*frameValue)->toString(); - if( !tagString.isEmpty() ){ +/* if( !tagString.isEmpty() ){ std::wstring value(tagString.begin(),tagString.end()); std::vector splitValues; @@ -277,9 +393,28 @@ void TagReaderTaglib::SetSlashSeparatedValues(const char* key,const TagLib::ID3v for(std::vector::iterator theValue=splitValues.begin();theValue!=splitValues.end();++theValue){ track->SetValue(key,theValue->c_str()); } - } + }*/ + this->SetSlashSeparatedValues(key,tagString,track); } } } +void TagReaderTaglib::SetAudioProperties(TagLib::AudioProperties *audioProperties,musik::core::Track *track){ + if(audioProperties){ + std::wstring duration = boost::str(boost::wformat(_T("%1%"))%audioProperties->length()); + this->SetTagValue("duration",duration,track); + + int bitrate( audioProperties->bitrate() ); + if(bitrate){ + std::wstring temp( boost::str(boost::wformat(_T("%1%"))%bitrate ) ); + this->SetTagValue("bitrate",temp,track); + } + + int channels( audioProperties->channels() ); + if(channels){ + std::wstring temp( boost::str(boost::wformat(_T("%1%"))%channels ) ); + this->SetTagValue("channels",temp,track); + } + } +} diff --git a/src/contrib/taglib_plugin/TagReaderTaglib.h b/src/contrib/taglib_plugin/TagReaderTaglib.h index 4c23219f2..823534d8b 100644 --- a/src/contrib/taglib_plugin/TagReaderTaglib.h +++ b/src/contrib/taglib_plugin/TagReaderTaglib.h @@ -38,12 +38,15 @@ #include "stdafx.h" -#include -#include -#include -#include -#include -#include +#include +#include + +#include +#include +#include + +#include + #include @@ -54,15 +57,22 @@ class TagReaderTaglib : public musik::core::Plugin::IMetaDataReader { TagReaderTaglib(void); virtual ~TagReaderTaglib(void); bool ReadTag(musik::core::Track *track); - virtual bool CanReadTag(const wchar_t *extension); + virtual bool CanReadTag(const utfchar *extension); virtual void Destroy(); private: // bool getStandardTags(musik::core::Track &track,TagLib::Tag *oTag,TagLib::AudioProperties *oAudioProperties); void SetTagValue(const char* key,const wchar_t* string,musik::core::Track *track); void SetTagValue(const char* key,const TagLib::String tagString,musik::core::Track *track); + void SetTagValue(const char* key,const int tagInt,musik::core::Track *track); void SetTagValues(const char* key,const TagLib::ID3v2::FrameList &frame,musik::core::Track *track); + void SetAudioProperties(TagLib::AudioProperties *audioProperties,musik::core::Track *track); void SetSlashSeparatedValues(const char* key,const TagLib::ID3v2::FrameList &frame,musik::core::Track *track); + void SetSlashSeparatedValues(const char* key,TagLib::String &tagString,musik::core::Track *track); + + bool GetID3v2Tag(musik::core::Track *track); + bool GetGenericTag(musik::core::Track *track); + bool GetOGGTag(musik::core::Track *track); };