]> git.treefish.org Git - usetaglib.git/blob - usetaglib.cpp
e0d2fbfccf61068ba62f6a6594a6f4dad898d346
[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 << "Usage: usetaglib [ACTION]... [FILE]..." << endl;
113   cout << "Read and edit meta-data of audio formats supported by taglib." << endl;
114   cout << endl;
115   cout << "-h, --help   Show this help" << endl;
116   cout << endl;
117   cout << "ACTIONS" << endl;
118   cout << setfill(' ') << setw(37) << left << "  -l, --list"
119        << "list all tags (implicit if no action specified)"<< endl;
120   cout << setfill(' ') << setw(37) << left << "  -a, --listaudio"
121        << "show audio information"<< endl;
122   cout << setfill(' ') << setw(37) << left << "  -e, --erase=TAGNAME"
123        << "erase tag TAGNAME"<< endl;
124   cout << setfill(' ') << setw(37) << left << "  -r, --replace=TAGNAME=TAGVALSPEC"
125        << "replace tag TAGNAME with value TAGVALSPEC"<< endl;
126   cout << setfill(' ') << setw(37) << left << "  -i, --insert=TAGNAME=TAGVALSPEC"
127        << "insert value TAGVALSPEC for tag TAGNAME"<< endl;
128   cout << endl;
129   cout << "TAGVALSPEC" << endl;
130   cout << "  a list of values separated by '=': val1=val2=val3..." << endl;
131   cout << endl;
132   cout << "EXAMPLES" << endl;
133   cout << "  usetaglib file.ogg" << endl;
134   cout << "  usetaglib -e ALBUM file.flac" << endl;
135   cout << "  usetaglib -i ARTIST=Horst=Hubert file.mp3" << endl;
136   cout << "  usetaglib -r ARTIST=Horst file.ogg" << endl;
137   cout << "  usetaglib -i \"ALBUMARTIST=Horst und Hubert\" file.ogg" << endl;
138   cout << "  usetaglib --insert=\"ALBUMARTIST=Horst und Hubert\" file.ogg" << endl;
139 }
140   
141 int main(int argc, char *argv[])
142 {
143   int c;
144   actionqueue requestedActions;
145   
146   while (1)
147     {
148       static struct option long_options[] =
149         {
150           {"help",      no_argument,       0, 'h'},
151           {"list",      no_argument,       0, 'l'},
152           {"listaudio", no_argument,       0, 'a'},
153           {"insert",    required_argument, 0, 'i'},
154           {"erase",     required_argument, 0, 'e'},
155           {"replace",   required_argument, 0, 'r'},
156           {0, 0, 0, 0}
157         };
158
159       int option_index = 0;      
160       c = getopt_long (argc, argv, "hlai:e:r:",
161                        long_options, &option_index);
162
163       if (c == -1)
164         break;
165
166       switch (c)
167         {
168         case 0:
169           if (long_options[option_index].flag != 0)
170             break;
171
172         case 'h':
173           printHelp();
174           return 0;
175           break;
176           
177         case 'l':
178           requestedActions.push_back( make_pair(LIST, "") );
179           break;
180
181         case 'a':
182           requestedActions.push_back( make_pair(AUDIO, "") );
183           break;
184
185         case 'i':
186           requestedActions.push_back( make_pair(INSERT, optarg) );
187           break;
188
189         case 'e':
190           requestedActions.push_back( make_pair(ERASE, optarg) );
191           break;
192
193         case 'r':
194           requestedActions.push_back( make_pair(REPLACE, optarg) );
195           break;
196
197         case '?':
198           break;
199
200         default:
201           abort ();
202         }
203     }
204
205   if ( optind == argc ) {
206     printHelp();
207     return 0;
208   }
209   
210   if (requestedActions.size() == 0)
211     requestedActions.push_back( make_pair(LIST, "") );
212         
213   for(int i = optind; i < argc; i++) {
214     cout << "******************** \"" << argv[i] << "\" ********************" << endl;
215     TagLib::FileRef f(argv[i]);
216
217     if(f.isNull())
218       continue;
219
220     TagLib::PropertyMap propmap = f.file()->properties();    
221     bool FCHANGED = false;
222     
223     for (actionqueue::iterator actit = requestedActions.begin(); actit != requestedActions.end(); ++actit) {
224       switch (actit->first) {
225       case LIST:
226         action_printTags(f, propmap);
227         break;
228         
229       case AUDIO:
230         action_printAudio(f);
231         break;
232         
233       case ERASE:
234         action_eraseTag(propmap, actit->second);
235         FCHANGED = true;
236         break;
237         
238       case REPLACE:
239         action_replaceTag(propmap, splitToTagPair(actit->second));
240         FCHANGED = true;
241         break;
242
243       case INSERT:
244         action_insertTag(propmap, splitToTagPair(actit->second));
245         FCHANGED = true;
246         break;
247       }
248     }
249
250     if (FCHANGED) {
251       f.file()->setProperties(propmap);
252       f.file()->save();
253     }
254   }
255   
256   return 0;
257 }