]> git.treefish.org Git - usetaglib.git/blob - usetaglib.cpp
b175b2dba3019597440459d499a21ea4a85372ba
[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 (const TagLib::FileRef f, const string &key)
58 {
59   TagLib::PropertyMap propmap = f.file()->properties();
60   propmap.erase(key);
61   f.file()->setProperties(propmap);
62 }
63
64 void action_replaceTag (const TagLib::FileRef f, const tagpair &tagPair)
65 {
66   TagLib::PropertyMap propmap = f.file()->properties();
67   propmap.replace(tagPair.first, argToStringList(tagPair.second));
68   f.file()->setProperties(propmap);
69 }
70
71 void action_insertTag (const TagLib::FileRef f, const tagpair &tagPair)
72 {
73   TagLib::PropertyMap propmap = f.file()->properties();
74   propmap.insert(tagPair.first, argToStringList(tagPair.second));
75   f.file()->setProperties(propmap);
76 }
77
78 int action_printTags (const TagLib::FileRef f)
79 {
80   if(f.tag()) {
81     TagLib::PropertyMap tags = f.file()->properties();
82     unsigned int longest = 0;
83     for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
84       if (i->first.size() > longest) {
85         longest = i->first.size();
86       }
87     }
88     cout << "-- TAG (properties) --" << endl;
89     for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
90       for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
91         cout << i->first << "=" << *j << endl;
92       }
93     }
94     return 0;
95   }
96   else
97     return 1;
98 }
99
100 int action_printAudio (const TagLib::FileRef f)
101 {
102   if(f.audioProperties()) {
103     TagLib::AudioProperties *properties = f.audioProperties();
104     int seconds = properties->length() % 60;
105     int minutes = (properties->length() - seconds) / 60;
106     cout << "-- AUDIO --" << endl;
107     cout << "BITRATE=" << properties->bitrate() << endl;
108     cout << "SAMPLERATE=" << properties->sampleRate() << endl;
109     cout << "CHANNELS=" << properties->channels() << endl;
110     cout << "LENGTH=" << minutes << ":" << setfill('0') << setw(2) << seconds << endl;
111     return 0;
112   }
113   else
114     return 1;
115 }
116
117 void printHelp ()
118 {
119   cout << "Usage: usetaglib [ACTION]... [FILE]..." << endl;
120   cout << "List and edit tags on mediafiles in formats supported by libtag." << endl;
121   cout << endl;
122   cout << "-h, --help   Show this help" << endl;
123   cout << endl;
124   cout << "ACTIONS" << endl;
125   cout << setfill(' ') << setw(45) << left << "  -l, --list"
126        << "list all tags (implicit if no action specified)"<< endl;
127   cout << setfill(' ') << setw(45) << left << "  -e, --erase=TAGNAME"
128        << "erase tag with name TAGNAME"<< endl;
129   cout << setfill(' ') << setw(45) << left << "  -r, --replace=TAGNAME=TAGVAL[=TAGVAL...]"
130        << "replace tag TAGNAME with list of values TAGVAL"<< endl;
131   cout << setfill(' ') << setw(45) << left << "  -i, --insert=TAGNAME=TAGVAL[=TAGVAL...]"
132        << "insert list of values TAGVAL for tag TAGNAME"<< endl;
133 }
134   
135 int main(int argc, char *argv[])
136 {
137   int c;
138   actionqueue requestedActions;
139   
140   while (1)
141     {
142       static struct option long_options[] =
143         {
144           {"help",      no_argument,       0, 'h'},
145           {"list",      no_argument,       0, 'l'},
146           {"listaudio", no_argument,       0, 'a'},
147           {"insert",    required_argument, 0, 'i'},
148           {"erase",     required_argument, 0, 'e'},
149           {"replace",   required_argument, 0, 'r'},
150           {0, 0, 0, 0}
151         };
152
153       int option_index = 0;      
154       c = getopt_long (argc, argv, "hlai:e:r:",
155                        long_options, &option_index);
156
157       if (c == -1)
158         break;
159
160       switch (c)
161         {
162         case 0:
163           if (long_options[option_index].flag != 0)
164             break;
165
166         case 'h':
167           printHelp();
168           return 0;
169           break;
170           
171         case 'l':
172           requestedActions.push_back( make_pair(LIST, "") );
173           break;
174
175         case 'a':
176           requestedActions.push_back( make_pair(AUDIO, "") );
177           break;
178
179         case 'i':
180           requestedActions.push_back( make_pair(INSERT, optarg) );
181           break;
182
183         case 'e':
184           requestedActions.push_back( make_pair(ERASE, optarg) );
185           break;
186
187         case 'r':
188           requestedActions.push_back( make_pair(REPLACE, optarg) );
189           break;
190
191         case '?':
192           break;
193
194         default:
195           abort ();
196         }
197     }
198
199   if (requestedActions.size() == 0)
200     requestedActions.push_back( make_pair(LIST, "") );
201   
202   for(int i = optind; i < argc; i++) {
203     cout << "******************** \"" << argv[i] << "\" ********************" << endl;
204     TagLib::FileRef f(argv[i]);
205
206     if(f.isNull())
207       continue;
208     
209     for (actionqueue::iterator actit = requestedActions.begin(); actit != requestedActions.end(); ++actit) {
210       switch (actit->first) {
211       case LIST:
212         action_printTags(f);
213         break;
214         
215       case AUDIO:
216         action_printAudio(f);
217         break;
218         
219       case ERASE:
220         action_eraseTag(f, actit->second);
221         break;
222         
223       case REPLACE:
224         action_replaceTag(f, splitToTagPair(actit->second));
225         break;
226
227       case INSERT:
228         action_insertTag(f, splitToTagPair(actit->second));
229         break;
230       }
231     }
232     
233     f.file()->save();
234   }
235   
236   return 0;
237 }