X-Git-Url: http://git.treefish.org/~alex/usetaglib.git/blobdiff_plain/5e2119a394e8163a0ac1105fae0e4ec8727eb2c3..d2fdf4b137a60be9d899663f5f7d176d0659a47b:/usetaglib.cpp diff --git a/usetaglib.cpp b/usetaglib.cpp index a616a2e..d8d51ed 100644 --- a/usetaglib.cpp +++ b/usetaglib.cpp @@ -1,17 +1,18 @@ -/* - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ +/* Copyright (C) 2015 Alexander Schmidt + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include #include @@ -22,70 +23,84 @@ #include #include #include +#include using namespace std; enum action {LIST, REPLACE, INSERT, ERASE, AUDIO}; typedef pair actionpair; typedef vector actionqueue; -typedef pair tagpair; +typedef pair keyandvalues; -TagLib::StringList argToStringList (const string &rawtagarg) +keyandvalues toKeyAndValues (const string &rawstring) { - TagLib::StringList newlist; - - size_t delpos = 0; - while (1) { - size_t nextdelpos = rawtagarg.find('=', delpos+1); - if (nextdelpos == -1) - break; - newlist.append(rawtagarg.substr(delpos,nextdelpos-delpos)); - delpos = nextdelpos+1; + stringstream tmpss; + TagLib::String key; + TagLib::StringList values; + int isplit=0; + + for (int ipos=0; ipos < rawstring.length(); ipos++) { + if ( rawstring[ipos] == '\\' ) { + switch (rawstring[ipos+1]) { + case '\\': + tmpss << '\\'; + break; + case '=': + tmpss << '='; + break; + default: + tmpss << '\\' << rawstring[ipos+1]; + break; + } + ipos++; + } + else + if ( rawstring[ipos] == '=' ) { + if ( isplit == 0 ) + key = tmpss.str(); + else + values.append(tmpss.str()); + isplit++; + tmpss.str(""); + } + else + tmpss << rawstring[ipos]; } - newlist.append(rawtagarg.substr(delpos,string::npos)); - return newlist; -} - -tagpair splitToTagPair (const string &rawarg) -{ - const size_t delpos = rawarg.find('='); - return make_pair(rawarg.substr(0, delpos), rawarg.substr(delpos+1, string::npos)); + if (isplit==0) + key = tmpss.str(); + else + values.append(tmpss.str()); + + return make_pair(key, values); } -void action_eraseTag (const TagLib::FileRef f, const string &key) +void action_eraseTag (TagLib::PropertyMap &propmap, const string &key) { - TagLib::PropertyMap propmap = f.file()->properties(); propmap.erase(key); - f.file()->setProperties(propmap); } -void action_replaceTag (const TagLib::FileRef f, const tagpair &tagPair) +void action_replaceTag (TagLib::PropertyMap &propmap, const keyandvalues &keyAndValues) { - TagLib::PropertyMap propmap = f.file()->properties(); - propmap.replace(tagPair.first, argToStringList(tagPair.second)); - f.file()->setProperties(propmap); + propmap.replace(keyAndValues.first, keyAndValues.second); } -void action_insertTag (const TagLib::FileRef f, const tagpair &tagPair) +void action_insertTag (TagLib::PropertyMap &propmap, const keyandvalues &keyAndValues) { - TagLib::PropertyMap propmap = f.file()->properties(); - propmap.insert(tagPair.first, argToStringList(tagPair.second)); - f.file()->setProperties(propmap); + propmap.insert(keyAndValues.first, keyAndValues.second); } -int action_printTags (const TagLib::FileRef f) +int action_printTags (const TagLib::FileRef f, TagLib::PropertyMap &propmap) { if(f.tag()) { - TagLib::PropertyMap tags = f.file()->properties(); unsigned int longest = 0; - for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) { + for(TagLib::PropertyMap::ConstIterator i = propmap.begin(); i != propmap.end(); ++i) { if (i->first.size() > longest) { longest = i->first.size(); } } - cout << "-- TAG (properties) --" << endl; - for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) { + cout << " \\_____/ TAGS \\_____ _ _ _" << endl; + for(TagLib::PropertyMap::ConstIterator i = propmap.begin(); i != propmap.end(); ++i) { for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) { cout << i->first << "=" << *j << endl; } @@ -102,7 +117,7 @@ int action_printAudio (const TagLib::FileRef f) TagLib::AudioProperties *properties = f.audioProperties(); int seconds = properties->length() % 60; int minutes = (properties->length() - seconds) / 60; - cout << "-- AUDIO --" << endl; + cout << " \\_____/ AUDIO PROPERTIES \\_____ _ _ _" << endl; cout << "BITRATE=" << properties->bitrate() << endl; cout << "SAMPLERATE=" << properties->sampleRate() << endl; cout << "CHANNELS=" << properties->channels() << endl; @@ -114,21 +129,55 @@ int action_printAudio (const TagLib::FileRef f) } void printHelp () +{ + cout << + "Usage: usetaglib [ACTION]... [FILE]...\n" + "Read and edit meta-data of audio formats supported by taglib.\n" + "Multiple ACTIONS and FILES may be given in arbitrary order.\n" + "\n" + "-h, --help show help\n" + "-H, --longhelp show long help\n" + "\n" + "ACTIONS\n" + " If multiple actions are specified they are executed in given order.\n" + "\n" + " -l, --list list all tags (implicit if no action given)\n" + " -a, --audio show audio properties\n" + " -e, --erase=TAGNAME erase tag TAGNAME\n" + " -r, --replace=TAGNAME=TAGVAL replace tag TAGNAME with value TAGVAL\n" + " -i, --insert=TAGNAME=TAGVAL insert tag TAGNAME with value TAGVAL\n"; +} + +void printExtraHelp () { - cout << "Usage: usetaglib [ACTION]... [FILE]..." << endl; - cout << "List and edit tags on mediafiles in formats supported by libtag." << endl; - cout << endl; - cout << "-h, --help Show this help" << endl; - cout << endl; - cout << "ACTIONS" << endl; - cout << setfill(' ') << setw(45) << left << " -l, --list" - << "list all tags (implicit if no action specified)"<< endl; - cout << setfill(' ') << setw(45) << left << " -e, --erase=TAGNAME" - << "erase tag with name TAGNAME"<< endl; - cout << setfill(' ') << setw(45) << left << " -r, --replace=TAGNAME=TAGVAL[=TAGVAL...]" - << "replace tag TAGNAME with list of values TAGVAL"<< endl; - cout << setfill(' ') << setw(45) << left << " -i, --insert=TAGNAME=TAGVAL[=TAGVAL...]" - << "insert list of values TAGVAL for tag TAGNAME"<< endl; + cout << + "\n" + "TAGNAME\n" + " TAGNAME is a media format independent id encoding the type of a tag.\n" + " Note that also in --list output, format specific tag ids are translated\n" + " to unified TAGNAMES.\n" + "\n" + " Some \"well-known\" tags you might want to use are:\n" + " TITLE ALBUM ARTIST ALBUMARTIST SUBTITLE TRACKNUMBER DISCNUMBER DATE\n" + " ORIGINALDATE GENRE COMMENT TITLESORT ALBUMSORT ARTISTSORT\n" + " ALBUMARTISTSORT COMPOSER LYRICIST CONDUCTOR REMIXER PERFORMER ISRC ASIN\n" + " BPM COPYRIGHT ENCODEDBY MOOD COMMENT MEDIA LABEL CATALOGNUMBER BARCODE\n" + "\n" + "TAGVAL\n" + " TAGVAL has to be either a single string or a list of strings separated\n" + " by equal signs (=). If a list is given, multiple tags of type TAGNAME\n" + " will be created and set to the respective values given by the list.\n" + " Note that equal signs have to be escaped with a leading backslash (\\=),\n" + " if they shall not be interpreted as list separators.\n" + "\n" + "EXAMPLES\n" + " usetaglib file.ogg\n" + " usetaglib -e ALBUM file.flac\n" + " usetaglib -r \"ALBUM=New Album\" -i ARTIST=Horst=Hubert file.mp3\n" + " usetaglib -r ARTIST=Horst -l file1.ogg file2.mp3\n" + " usetaglib -i \"ALBUMARTIST=Horst und Hubert\" file.ogg\n" + " usetaglib --insert=\"ALBUMARTIST=Horst und Hubert\" file.ogg\n" + " usetaglib --replace='ARTIST=This Band \\= Great' file.ogg\n"; } int main(int argc, char *argv[]) @@ -141,8 +190,9 @@ int main(int argc, char *argv[]) static struct option long_options[] = { {"help", no_argument, 0, 'h'}, + {"longhelp", no_argument, 0, 'H'}, {"list", no_argument, 0, 'l'}, - {"listaudio", no_argument, 0, 'a'}, + {"audio", no_argument, 0, 'a'}, {"insert", required_argument, 0, 'i'}, {"erase", required_argument, 0, 'e'}, {"replace", required_argument, 0, 'r'}, @@ -150,7 +200,7 @@ int main(int argc, char *argv[]) }; int option_index = 0; - c = getopt_long (argc, argv, "hlai:e:r:", + c = getopt_long (argc, argv, "hHlai:e:r:", long_options, &option_index); if (c == -1) @@ -161,75 +211,86 @@ int main(int argc, char *argv[]) case 0: if (long_options[option_index].flag != 0) break; - case 'h': printHelp(); return 0; break; - + case 'H': + printHelp(); + printExtraHelp(); + return 0; + break; case 'l': requestedActions.push_back( make_pair(LIST, "") ); break; - case 'a': requestedActions.push_back( make_pair(AUDIO, "") ); break; - case 'i': requestedActions.push_back( make_pair(INSERT, optarg) ); break; - case 'e': requestedActions.push_back( make_pair(ERASE, optarg) ); break; - case 'r': requestedActions.push_back( make_pair(REPLACE, optarg) ); break; - case '?': break; - default: abort (); } } + if ( optind == argc ) { + printHelp(); + return 0; + } + if (requestedActions.size() == 0) requestedActions.push_back( make_pair(LIST, "") ); - + for(int i = optind; i < argc; i++) { - cout << "******************** \"" << argv[i] << "\" ********************" << endl; + cout << " _________________________________________ _ _ _" << endl; + cout << "/" << endl; + cout << " " << argv[i] << endl; + cout << "\\_________________________________________ _ _ _" << endl; + TagLib::FileRef f(argv[i]); if(f.isNull()) continue; + + TagLib::PropertyMap propmap = f.file()->properties(); + bool FCHANGED = false; for (actionqueue::iterator actit = requestedActions.begin(); actit != requestedActions.end(); ++actit) { switch (actit->first) { case LIST: - action_printTags(f); + action_printTags(f, propmap); break; - case AUDIO: action_printAudio(f); break; - case ERASE: - action_eraseTag(f, actit->second); + action_eraseTag(propmap, actit->second); + FCHANGED = true; break; - case REPLACE: - action_replaceTag(f, splitToTagPair(actit->second)); + action_replaceTag(propmap, toKeyAndValues(actit->second)); + FCHANGED = true; break; - case INSERT: - action_insertTag(f, splitToTagPair(actit->second)); + action_insertTag(propmap, toKeyAndValues(actit->second)); + FCHANGED = true; break; } } - - f.file()->save(); + + if (FCHANGED) { + f.file()->setProperties(propmap); + f.file()->save(); + } } return 0;