]> git.treefish.org Git - usetaglib.git/blob - usetaglib.cpp
8c04a55b71a765ff3c984b2aef9854e8b1a1ba89
[usetaglib.git] / usetaglib.cpp
1 /* Copyright (C) 2015 Alexander Schmidt <alex@treefish.org>
2  *
3  * This program is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation, either version 3 of the License, or
6  * (at your option) any later version.
7  * 
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  * 
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
15  */
16
17 #include <iostream>
18 #include <iomanip>
19 #include <iostream>
20 #include <taglib/fileref.h>
21 #include <taglib/tag.h>
22 #include <taglib/tpropertymap.h>
23 #include <taglib/tstringlist.h>
24 #include <getopt.h>
25 #include <vector>
26
27 using namespace std;
28
29 enum action {LIST, REPLACE, INSERT, ERASE, AUDIO};
30 typedef pair<action,string> actionpair;
31 typedef vector<actionpair> actionqueue;
32 typedef pair<string,string> tagpair;
33
34 TagLib::StringList argToStringList (const string &rawtagarg)
35 {
36   TagLib::StringList newlist;
37
38   size_t delpos = 0;
39   while (1) {
40     size_t nextdelpos = rawtagarg.find('=', delpos+1);
41     if (nextdelpos == -1)
42       break;
43     newlist.append(rawtagarg.substr(delpos,nextdelpos-delpos));
44     delpos = nextdelpos+1;
45   }
46   newlist.append(rawtagarg.substr(delpos,string::npos));  
47
48   return newlist;
49 }
50
51 tagpair splitToTagPair (const string &rawarg)
52 {
53   const size_t delpos = rawarg.find('=');
54   return make_pair(rawarg.substr(0, delpos), rawarg.substr(delpos+1, string::npos));
55 }
56
57 void action_eraseTag (TagLib::PropertyMap &propmap, const string &key)
58 {
59   propmap.erase(key);
60 }
61
62 void action_replaceTag (TagLib::PropertyMap &propmap, const tagpair &tagPair)
63 {
64   propmap.replace(tagPair.first, argToStringList(tagPair.second));
65 }
66
67 void action_insertTag (TagLib::PropertyMap &propmap, const tagpair &tagPair)
68 {
69   propmap.insert(tagPair.first, argToStringList(tagPair.second));
70 }
71
72 int action_printTags (const TagLib::FileRef f, TagLib::PropertyMap &propmap)
73 {
74   if(f.tag()) {
75     unsigned int longest = 0;
76     for(TagLib::PropertyMap::ConstIterator i = propmap.begin(); i != propmap.end(); ++i) {
77       if (i->first.size() > longest) {
78         longest = i->first.size();
79       }
80     }
81     cout << "-- TAG (properties) --" << endl;
82     for(TagLib::PropertyMap::ConstIterator i = propmap.begin(); i != propmap.end(); ++i) {
83       for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
84         cout << i->first << "=" << *j << endl;
85       }
86     }
87     return 0;
88   }
89   else
90     return 1;
91 }
92
93 int action_printAudio (const TagLib::FileRef f)
94 {
95   if(f.audioProperties()) {
96     TagLib::AudioProperties *properties = f.audioProperties();
97     int seconds = properties->length() % 60;
98     int minutes = (properties->length() - seconds) / 60;
99     cout << "-- AUDIO --" << endl;
100     cout << "BITRATE=" << properties->bitrate() << endl;
101     cout << "SAMPLERATE=" << properties->sampleRate() << endl;
102     cout << "CHANNELS=" << properties->channels() << endl;
103     cout << "LENGTH=" << minutes << ":" << setfill('0') << setw(2) << seconds << endl;
104     return 0;
105   }
106   else
107     return 1;
108 }
109
110 void printHelp ()
111 {  
112   cout <<
113     "Usage: usetaglib [ACTION]... [FILE]...\n"    
114     "Read and edit meta-data of audio formats supported by taglib.\n"
115     "Multiple ACTIONS and FILES may be given in arbitrary order.\n"
116     "\n"
117     "-h, --help      show help\n"
118     "-H, --longhelp  show long help\n"
119     "\n"
120     "ACTIONS\n"
121     "  If multiple actions are specified they are executed in given order.\n"
122     "\n"
123     "  -l, --list                    list all tags (implicit if no action given)\n"
124     "  -a, --audio                   show audio information\n"
125     "  -e, --erase=TAGNAME           erase tag TAGNAME\n"
126     "  -r, --replace=TAGNAME=TAGVAL  replace tag TAGNAME with value TAGVAL\n"
127     "  -i, --insert=TAGNAME=TAGVAL   insert tag TAGNAME with value TAGVAL\n";
128 }
129
130 void printExtraHelp ()
131 {
132   cout <<
133     "\n"
134     "TAGNAME\n"
135     "  TAGNAME is a media format independent id encoding the type of a tag.\n"
136     "  Note that also in --list output, format specific tag ids are translated\n"
137     "  to unified TAGNAMES.\n"
138     "\n"
139     "  Some \"well-known\" tags you might want to use are:\n"
140     "  TITLE ALBUM ARTIST ALBUMARTIST SUBTITLE TRACKNUMBER DISCNUMBER DATE\n"
141     "  ORIGINALDATE GENRE COMMENT TITLESORT ALBUMSORT ARTISTSORT\n"
142     "  ALBUMARTISTSORT COMPOSER LYRICIST CONDUCTOR REMIXER PERFORMER ISRC ASIN\n"
143     "  BPM COPYRIGHT ENCODEDBY MOOD COMMENT MEDIA LABEL CATALOGNUMBER BARCODE\n"
144     "\n"
145     "TAGVAL\n"
146     "  TAGVAL has to be either a single string or a list of strings separated\n"
147     "  by equal signs (=). If a list is given, multiple tags of type TAGNAME\n"
148     "  will be created and set to the respective values given in the list.\n"
149     "\n"
150     "EXAMPLES\n"
151     "  usetaglib file.ogg\n"
152     "  usetaglib -e ALBUM file.flac\n"
153     "  usetaglib -r \"ALBUM=New Album\" -i ARTIST=Horst=Hubert file.mp3\n"
154     "  usetaglib -r ARTIST=Horst -l file1.ogg file2.mp3\n"
155     "  usetaglib -i \"ALBUMARTIST=Horst und Hubert\" file.ogg\n"
156     "  usetaglib --insert=\"ALBUMARTIST=Horst und Hubert\" file.ogg\n";
157 }
158   
159 int main(int argc, char *argv[])
160 {
161   int c;
162   actionqueue requestedActions;
163   
164   while (1)
165     {
166       static struct option long_options[] =
167         {
168           {"help",      no_argument,       0, 'h'},
169           {"longhelp",  no_argument,       0, 'H'},
170           {"list",      no_argument,       0, 'l'},
171           {"audio",     no_argument,       0, 'a'},
172           {"insert",    required_argument, 0, 'i'},
173           {"erase",     required_argument, 0, 'e'},
174           {"replace",   required_argument, 0, 'r'},
175           {0, 0, 0, 0}
176         };
177
178       int option_index = 0;      
179       c = getopt_long (argc, argv, "hHlai:e:r:",
180                        long_options, &option_index);
181
182       if (c == -1)
183         break;
184
185       switch (c)
186         {
187         case 0:
188           if (long_options[option_index].flag != 0)
189             break;
190         case 'h':
191           printHelp();
192           return 0;
193           break;
194         case 'H':
195           printHelp();
196           printExtraHelp();
197           return 0;
198           break;
199         case 'l':
200           requestedActions.push_back( make_pair(LIST, "") );
201           break;
202         case 'a':
203           requestedActions.push_back( make_pair(AUDIO, "") );
204           break;
205         case 'i':
206           requestedActions.push_back( make_pair(INSERT, optarg) );
207           break;
208         case 'e':
209           requestedActions.push_back( make_pair(ERASE, optarg) );
210           break;
211         case 'r':
212           requestedActions.push_back( make_pair(REPLACE, optarg) );
213           break;
214         case '?':
215           break;
216         default:
217           abort ();
218         }
219     }
220
221   if ( optind == argc ) {
222     printHelp();
223     return 0;
224   }
225   
226   if (requestedActions.size() == 0)
227     requestedActions.push_back( make_pair(LIST, "") );
228         
229   for(int i = optind; i < argc; i++) {
230     cout << "******************** \"" << argv[i] << "\" ********************" << endl;
231     TagLib::FileRef f(argv[i]);
232
233     if(f.isNull())
234       continue;
235
236     TagLib::PropertyMap propmap = f.file()->properties();    
237     bool FCHANGED = false;
238     
239     for (actionqueue::iterator actit = requestedActions.begin(); actit != requestedActions.end(); ++actit) {
240       switch (actit->first) {
241       case LIST:
242         action_printTags(f, propmap);
243         break;
244       case AUDIO:
245         action_printAudio(f);
246         break;
247       case ERASE:
248         action_eraseTag(propmap, actit->second);
249         FCHANGED = true;
250         break;
251       case REPLACE:
252         action_replaceTag(propmap, splitToTagPair(actit->second));
253         FCHANGED = true;
254         break;
255       case INSERT:
256         action_insertTag(propmap, splitToTagPair(actit->second));
257         FCHANGED = true;
258         break;
259       }
260     }
261
262     if (FCHANGED) {
263       f.file()->setProperties(propmap);
264       f.file()->save();
265     }
266   }
267   
268   return 0;
269 }