1 #if defined HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4
5 #include <cstdio>
6 #include <iostream>
7 #include <cstring>
8 #include <id3/tag.h>
9 #include <getopt.h>
10 #include <id3/misc_support.h>
11 #ifdef WIN32
12 #include <io.h>
13 #define snprintf _snprintf
14 #endif
15
16 #include <sys/types.h>
17 #include <sys/stat.h>
18
19 #include "genre.h"
20
21 #define MAXNOFRAMES 1000
22
23 #define TMPSIZE 255
24
25 /* Write both tags by default */
26 flags_t UpdFlags = ID3TT_ALL;
27
28
PrintUsage(char * sName)29 void PrintUsage(char *sName)
30 {
31 std::cout << "Usage: " << sName << " [OPTION]... [FILE]..." << std::endl;
32 std::cout << "Adds/Modifies/Removes/Views id3v2 tags, modifies/converts/lists id3v1 tags" << std::endl;
33 std::cout << std::endl;
34 std::cout << " -h, --help Display this help and exit" << std::endl;
35 std::cout << " -f, --list-frames Display all possible frames for id3v2" << std::endl;
36 std::cout << " -L, --list-genres Lists all id3v1 genres" << std::endl;
37 std::cout << " -v, --version Display version information and exit" << std::endl;
38 std::cout << " -l, --list Lists the tag(s) on the file(s)" << std::endl;
39 std::cout << " -d, --delete-v2 Deletes id3v2 tags" << std::endl;
40 std::cout << " -s, --delete-v1 Deletes id3v1 tags" << std::endl;
41 std::cout << " -D, --delete-all Deletes both id3v1 and id3v2 tags" << std::endl;
42 std::cout << " -C, --convert Converts id3v1 tag to id3v2" << std::endl;
43 std::cout << " -1, --id3v1-only Writes only id3v1 tag" << std::endl;
44 std::cout << " -2, --id3v2-only Writes only id3v2 tag" << std::endl;
45 std::cout << " -r, --remove-frame \"FRAMEID\" Removes the specified id3v2 frame" << std::endl;
46 std::cout << " -a, --artist \"ARTIST\" Set the artist information" << std::endl;
47 std::cout << " -A, --album \"ALBUM\" Set the album title information" << std::endl;
48 std::cout << " -t, --song \"SONG\" Set the song title information" << std::endl;
49 std::cout << " -c, --comment \"DESCRIPTION\":\"COMMENT\":\"LANGUAGE\" "<< std::endl
50 << " Set the comment information (both" << std::endl
51 << " description and language optional)" << std::endl;
52 std::cout << " -g, --genre num Set the genre number" << std::endl;
53 std::cout << " -y, --year num Set the year" << std::endl;
54 std::cout << " -T, --track num/num Set the track number/(optional) total tracks" << std::endl;
55 std::cout << std::endl;
56 std::cout << "You can set the value for any id3v2 frame by using '--' and then frame id" << std::endl;
57 std::cout << "For example: " << std::endl;
58 std::cout << " id3v2 --TIT3 \"Monkey!\" file.mp3" << std::endl;
59 std::cout << "would set the \"Subtitle/Description\" frame to \"Monkey!\". " << std::endl;
60 std::cout << std::endl;
61 }
62
63
PrintVersion(char * sName)64 void PrintVersion(char *sName)
65 {
66 std::cout << sName << " " << VERSION << std::endl;
67 std::cout << "Uses " << ID3LIB_FULL_NAME << std::endl << std::endl;
68
69 std::cout << "This program adds/modifies/removes/views id3v2 tags, " << std::endl
70 << "and can convert from id3v1 tags" << std::endl;
71 }
72
73
74 extern void ListTag(int argc, char *argv[], int optind);
75 extern void PrintFrameHelp(char *sName);
76 extern void PrintGenreList();
77
78 extern void DeleteTag(int argc, char *argv[], int optind, int whichTags);
79 extern void ConvertTag(int argc, char *argv[], int optind);
80 extern void DeleteFrame(int argc, char *argv[], int optind, char * frame);
81
82 #ifdef SORT_RUNTIME
83 extern void InitGenres();
84 #endif // SORT_RUNTIME
85
main(int argc,char * argv[])86 int main( int argc, char *argv[])
87 {
88 int iOpt;
89 int argCounter = 0;
90 int ii;
91 char tmp[TMPSIZE];
92 FILE * fp;
93
94 struct frameInfo {
95 enum ID3_FrameID id;
96 char *data;
97 } frameList[MAXNOFRAMES];
98
99 int frameCounter = 0;
100
101 while (true)
102 {
103 int option_index = 0;
104 int iLongOpt = 0;
105 int optFrameID = ID3FID_NOFRAME;
106 static struct option long_options[] =
107 {
108 // help and info
109 { "help", no_argument, &iLongOpt, 'h' },
110 { "list-frames",
111 no_argument, &iLongOpt, 'f' },
112 { "list-genres",
113 no_argument, &iLongOpt, 'L' },
114 { "version", no_argument, &iLongOpt, 'v' },
115
116 // list / remove / convert
117 { "list", no_argument, &iLongOpt, 'l' },
118 { "delete-v2", no_argument, &iLongOpt, 'd' },
119 { "delete-v1",
120 no_argument, &iLongOpt, 's' },
121 { "delete-all",
122 no_argument, &iLongOpt, 'D' },
123 { "convert", no_argument, &iLongOpt, 'C' },
124 { "id3v1-only", no_argument, &iLongOpt, '1' },
125 { "id3v2-only", no_argument, &iLongOpt, '2' },
126 { "remove-frame", required_argument, &iLongOpt, 'r' },
127
128 // infomation to tag
129 { "artist", required_argument, &iLongOpt, 'a' },
130 { "album", required_argument, &iLongOpt, 'A' },
131 { "song", required_argument, &iLongOpt, 't' },
132 { "comment", required_argument, &iLongOpt, 'c' },
133 { "genre", required_argument, &iLongOpt, 'g' },
134 { "year", required_argument, &iLongOpt, 'y' },
135 { "track", required_argument, &iLongOpt, 'T' },
136 { "AENC", required_argument, &optFrameID, ID3FID_AUDIOCRYPTO },
137 { "APIC", required_argument, &optFrameID, ID3FID_PICTURE },
138 { "COMM", required_argument, &optFrameID, ID3FID_COMMENT },
139 /* COMR too complex */
140 { "ENCR", required_argument, &optFrameID, ID3FID_CRYPTOREG },
141 { "EQUA", required_argument, &optFrameID, ID3FID_EQUALIZATION },
142 { "ETCO", required_argument, &optFrameID, ID3FID_EVENTTIMING },
143 { "GEOB", required_argument, &optFrameID, ID3FID_GENERALOBJECT },
144 { "GRID", required_argument, &optFrameID, ID3FID_GROUPINGREG },
145 { "IPLS", required_argument, &optFrameID, ID3FID_INVOLVEDPEOPLE },
146 { "LINK", required_argument, &optFrameID, ID3FID_LINKEDINFO },
147 { "MCDI", required_argument, &optFrameID, ID3FID_CDID },
148 { "MLLT", required_argument, &optFrameID, ID3FID_MPEGLOOKUP },
149 { "OWNE", required_argument, &optFrameID, ID3FID_OWNERSHIP },
150 { "PRIV", required_argument, &optFrameID, ID3FID_PRIVATE },
151 { "PCNT", required_argument, &optFrameID, ID3FID_PLAYCOUNTER },
152 { "POPM", required_argument, &optFrameID, ID3FID_POPULARIMETER },
153 { "POSS", required_argument, &optFrameID, ID3FID_POSITIONSYNC },
154 { "RBUF", required_argument, &optFrameID, ID3FID_BUFFERSIZE },
155 { "RVAD", required_argument, &optFrameID, ID3FID_VOLUMEADJ },
156 { "RVRB", required_argument, &optFrameID, ID3FID_REVERB },
157 { "SYLT", required_argument, &optFrameID, ID3FID_SYNCEDLYRICS },
158 { "SYTC", required_argument, &optFrameID, ID3FID_SYNCEDTEMPO },
159 { "TALB", required_argument, &optFrameID, ID3FID_ALBUM },
160 { "TBPM", required_argument, &optFrameID, ID3FID_BPM },
161 { "TCOM", required_argument, &optFrameID, ID3FID_COMPOSER },
162 { "TCON", required_argument, &optFrameID, ID3FID_CONTENTTYPE },
163 { "TCOP", required_argument, &optFrameID, ID3FID_COPYRIGHT },
164 { "TDAT", required_argument, &optFrameID, ID3FID_DATE },
165 { "TDLY", required_argument, &optFrameID, ID3FID_PLAYLISTDELAY },
166 { "TENC", required_argument, &optFrameID, ID3FID_ENCODEDBY },
167 { "TEXT", required_argument, &optFrameID, ID3FID_LYRICIST },
168 { "TFLT", required_argument, &optFrameID, ID3FID_FILETYPE },
169 { "TIME", required_argument, &optFrameID, ID3FID_TIME },
170 { "TIT1", required_argument, &optFrameID, ID3FID_CONTENTGROUP },
171 { "TIT2", required_argument, &optFrameID, ID3FID_TITLE },
172 { "TIT3", required_argument, &optFrameID, ID3FID_SUBTITLE },
173 { "TKEY", required_argument, &optFrameID, ID3FID_INITIALKEY },
174 { "TLAN", required_argument, &optFrameID, ID3FID_LANGUAGE },
175 { "TLEN", required_argument, &optFrameID, ID3FID_SONGLEN },
176 { "TMED", required_argument, &optFrameID, ID3FID_MEDIATYPE },
177 { "TOAL", required_argument, &optFrameID, ID3FID_ORIGALBUM },
178 { "TOFN", required_argument, &optFrameID, ID3FID_ORIGFILENAME },
179 { "TOLY", required_argument, &optFrameID, ID3FID_ORIGLYRICIST },
180 { "TOPE", required_argument, &optFrameID, ID3FID_ORIGARTIST },
181 { "TORY", required_argument, &optFrameID, ID3FID_ORIGYEAR },
182 { "TOWN", required_argument, &optFrameID, ID3FID_FILEOWNER },
183 { "TPE1", required_argument, &optFrameID, ID3FID_LEADARTIST },
184 { "TPE2", required_argument, &optFrameID, ID3FID_BAND },
185 { "TPE3", required_argument, &optFrameID, ID3FID_CONDUCTOR },
186 { "TPE4", required_argument, &optFrameID, ID3FID_MIXARTIST },
187 { "TPOS", required_argument, &optFrameID, ID3FID_PARTINSET },
188 { "TPUB", required_argument, &optFrameID, ID3FID_PUBLISHER },
189 { "TRCK", required_argument, &optFrameID, ID3FID_TRACKNUM },
190 { "TRDA", required_argument, &optFrameID, ID3FID_RECORDINGDATES },
191 { "TRSN", required_argument, &optFrameID, ID3FID_NETRADIOSTATION },
192 { "TRSO", required_argument, &optFrameID, ID3FID_NETRADIOOWNER },
193 { "TSIZ", required_argument, &optFrameID, ID3FID_SIZE },
194 { "TSRC", required_argument, &optFrameID, ID3FID_ISRC },
195 { "TSSE", required_argument, &optFrameID, ID3FID_ENCODERSETTINGS },
196 { "TXXX", required_argument, &optFrameID, ID3FID_USERTEXT },
197 { "TYER", required_argument, &optFrameID, ID3FID_YEAR },
198 { "UFID", required_argument, &optFrameID, ID3FID_UNIQUEFILEID },
199 { "USER", required_argument, &optFrameID, ID3FID_TERMSOFUSE },
200 { "USLT", required_argument, &optFrameID, ID3FID_UNSYNCEDLYRICS },
201 { "WCOM", required_argument, &optFrameID, ID3FID_WWWCOMMERCIALINFO },
202 { "WCOP", required_argument, &optFrameID, ID3FID_WWWCOPYRIGHT },
203 { "WOAF", required_argument, &optFrameID, ID3FID_WWWAUDIOFILE },
204 { "WOAR", required_argument, &optFrameID, ID3FID_WWWARTIST },
205 { "WOAS", required_argument, &optFrameID, ID3FID_WWWAUDIOSOURCE },
206 { "WORS", required_argument, &optFrameID, ID3FID_WWWRADIOPAGE },
207 { "WPAY", required_argument, &optFrameID, ID3FID_WWWPAYMENT },
208 { "WPUB", required_argument, &optFrameID, ID3FID_WWWPUBLISHER },
209 { "WXXX", required_argument, &optFrameID, ID3FID_WWWUSER },
210 { 0, 0, 0, 0 }
211 };
212 iOpt = getopt_long (argc, argv, "12hfLvldsDCr:a:A:t:c:g:y:T:",
213 long_options, &option_index);
214
215 if (iOpt == -1 && argCounter == 0)
216 {
217 PrintUsage(argv[0]);
218 exit(0);
219 }
220 else if (iOpt == -1)
221 break;
222 argCounter++;
223
224 if (iOpt == 0) iOpt = iLongOpt;
225 // if (iOpt == 0) iOpt = optFrameID;
226
227 #ifdef SORT_RUNTIME
228 InitGenres();
229 #endif // SORT_RUNTIME
230
231 switch (iOpt)
232 {
233 case 0:
234 frameList[frameCounter].id = (enum ID3_FrameID)optFrameID;
235 frameList[frameCounter].data = optarg;
236 frameCounter++;
237 break;
238 case '?':
239 case 'h': PrintUsage(argv[0]); exit (0);
240 case 'r': DeleteFrame(argc, argv, optind, optarg); exit (0);
241 case 'f': PrintFrameHelp(argv[0]);exit (0);
242 case 'L': PrintGenreList(); exit (0);
243 case 'v': PrintVersion(argv[0]); exit (0);
244
245 // listing / remove / convert -- see list.cpp and convert.cpp
246 case 'l': ListTag(argc, argv, optind);
247 exit (0);
248 case 'd': DeleteTag(argc, argv, optind, 2);
249 exit (0);
250 case 's': DeleteTag(argc, argv, optind, 1);
251 exit (0);
252 case 'D': DeleteTag(argc, argv, optind, 0);
253 exit (0);
254 case 'C': ConvertTag(argc, argv, optind);
255 exit (0);
256 case '1':
257 UpdFlags = ID3TT_ID3V1;
258 break;
259 case '2':
260 UpdFlags = ID3TT_ID3V2;
261 break;
262 // Tagging stuff
263 case 'a':
264 frameList[frameCounter].id = ID3FID_LEADARTIST;
265 frameList[frameCounter].data = optarg;
266 frameCounter++;
267 break;
268 case 'A':
269 frameList[frameCounter].id = ID3FID_ALBUM;
270 frameList[frameCounter].data = optarg;
271 frameCounter++;
272 break;
273 case 't':
274 frameList[frameCounter].id = ID3FID_TITLE;
275 frameList[frameCounter].data = optarg;
276 frameCounter++;
277 break;
278 case 'c':
279 frameList[frameCounter].id = ID3FID_COMMENT;
280 frameList[frameCounter].data = optarg;
281 frameCounter++;
282 break;
283 case 'g':
284 {
285 int genre_id = 255;
286 char *genre_str;
287 sscanf(optarg, "%d", &genre_id);
288 if (genre_id == 255)
289 genre_id = GetNumFromGenre(optarg);
290 if (genre_id == 255)
291 genre_str = optarg;
292 else {
293 sprintf(tmp, "(%d)", genre_id);
294 genre_str = tmp;
295 }
296 frameList[frameCounter].id = ID3FID_CONTENTTYPE;
297 frameList[frameCounter].data = genre_str;
298 frameCounter++;
299 }
300 break;
301 case 'y':
302 frameList[frameCounter].id = ID3FID_YEAR;
303 frameList[frameCounter].data = optarg;
304 frameCounter++;
305 break;
306 case 'T':
307 frameList[frameCounter].id = ID3FID_TRACKNUM;
308 frameList[frameCounter].data = optarg;
309 frameCounter++;
310 break;
311 // other tags
312
313 default:
314 std::cerr << "This isn't supposed to happen" << std::endl;
315 exit(1);
316 }
317 }
318
319 // loop thru the files
320 if (optind == argc)
321 {
322 std::cerr << "No file to work on." << std::endl;
323 exit(1);
324 }
325
326 for (int nIndex = optind; nIndex < argc; nIndex++)
327 {
328 ID3_Tag myTag;
329 struct stat filestat;
330
331 // std::cout << "Tagging " << argv[nIndex] << ": ";
332
333 // fix me - not checking to see if we can link to it
334
335
336 if (stat(argv[nIndex], &filestat))
337 {
338 std::cerr << "Couldn't stat file '" << argv[nIndex] << "'\n";
339 break;
340 }
341
342 /* cludgy to check if we have the proper perms */
343 fp = fopen(argv[nIndex], "r+");
344 if (fp == NULL) { /* file didn't open */
345 fprintf(stderr, "fopen: %s: ", argv[nIndex]);
346 perror("id3v2");
347 continue;
348 }
349 fclose(fp);
350
351 size_t ret;
352 ret = myTag.Link(argv[nIndex], UpdFlags);
353
354 // loop thru the frames we need to add/modify
355 for (ii = 0; ii < frameCounter; ii++)
356 {
357 ID3_Frame *myFrame;
358 myFrame = new ID3_Frame;
359 if (NULL == myFrame)
360 {
361 std::cout << "\nOut of memory\n" << std::endl;
362 exit(1);
363 }
364
365 myFrame->SetID(frameList[ii].id);
366
367 ID3_Frame *pFrame;
368 pFrame = myTag.Find(frameList[ii].id);
369
370 switch (frameList[ii].id)
371 {
372 // strings
373 case ID3FID_ALBUM:
374 case ID3FID_BPM:
375 case ID3FID_COMPOSER:
376 case ID3FID_CONTENTTYPE:
377 case ID3FID_COPYRIGHT:
378 case ID3FID_DATE:
379 case ID3FID_PLAYLISTDELAY:
380 case ID3FID_ENCODEDBY:
381 case ID3FID_LYRICIST:
382 case ID3FID_FILETYPE:
383 case ID3FID_TIME:
384 case ID3FID_CONTENTGROUP:
385 case ID3FID_TITLE:
386 case ID3FID_SUBTITLE:
387 case ID3FID_INITIALKEY:
388 case ID3FID_LANGUAGE:
389 case ID3FID_SONGLEN:
390 case ID3FID_MEDIATYPE:
391 case ID3FID_ORIGALBUM:
392 case ID3FID_ORIGFILENAME:
393 case ID3FID_ORIGLYRICIST:
394 case ID3FID_ORIGARTIST:
395 case ID3FID_ORIGYEAR:
396 case ID3FID_FILEOWNER:
397 case ID3FID_LEADARTIST:
398 case ID3FID_BAND:
399 case ID3FID_CONDUCTOR:
400 case ID3FID_MIXARTIST:
401 case ID3FID_PARTINSET:
402 case ID3FID_PUBLISHER:
403 case ID3FID_RECORDINGDATES:
404 case ID3FID_NETRADIOSTATION:
405 case ID3FID_NETRADIOOWNER:
406 case ID3FID_SIZE:
407 case ID3FID_ISRC:
408 case ID3FID_ENCODERSETTINGS:
409 case ID3FID_YEAR:
410 {
411 if (pFrame != NULL)
412 {
413 ID3_Frame * todel = myTag.RemoveFrame(pFrame);
414 delete todel;
415 }
416 if (strlen(frameList[ii].data) > 0) {
417 myFrame->Field(ID3FN_TEXT) = frameList[ii].data;
418 myTag.AttachFrame(myFrame);
419 }
420 break;
421 }
422 case ID3FID_TERMSOFUSE:
423 {
424 if (pFrame != NULL)
425 {
426 ID3_Frame * todel = myTag.RemoveFrame(pFrame);
427 delete todel;
428 }
429 if (strlen(frameList[ii].data) > 0) {
430 myFrame->Field(ID3FN_TEXTENC) = ID3TE_ASCII;
431 char *text;
432 text = strchr(frameList[ii].data, ':');
433 if (text == NULL)
434 {
435 myFrame->Field(ID3FN_TEXT) = frameList[ii].data;
436 } else {
437 *text = '\0';
438 text++;
439 myFrame->Field(ID3FN_LANGUAGE) = frameList[ii].data;
440 myFrame->Field(ID3FN_TEXT) = text;
441 }
442 char * test = ID3_GetString(myFrame, ID3FN_TEXT);
443 if (strlen(test) > 0) {
444 delete[] test;
445 myTag.AttachFrame(myFrame);
446 }
447 break;
448 }
449 }
450 case ID3FID_TRACKNUM:
451 {
452 // this ought to check if there is a total track number and
453 // combine it with the given track number, but it doesn't.
454 char *currentTrackNum = NULL;
455 char *newTrackNum = NULL;
456
457 if (pFrame != NULL)
458 {
459 currentTrackNum = ID3_GetString(pFrame, ID3FN_TEXT);
460 if (*currentTrackNum == '/')
461 {
462 newTrackNum = (char *)malloc(strlen(currentTrackNum)
463 + strlen(frameList[ii].data));
464 strcpy(newTrackNum, frameList[ii].data);
465 strcat(newTrackNum, currentTrackNum);
466 }
467 else
468 {
469 ID3_Frame * todel = myTag.RemoveFrame(pFrame);
470 delete todel;
471 }
472 }
473
474 myFrame->Field(ID3FN_TEXT) = frameList[ii].data;
475 myTag.AttachFrame(myFrame);
476
477 free(newTrackNum);
478 break;
479 }
480 case ID3FID_USERTEXT:
481 {
482 if (pFrame != NULL)
483 {
484 ID3_Frame * todel = myTag.RemoveFrame(pFrame);
485 delete todel;
486 }
487
488 // split the string at the ':' remember if no : then leave
489 // descrip empty
490 char *text;
491 text = strchr(frameList[ii].data, ':');
492 if (text == NULL)
493 {
494 myFrame->Field(ID3FN_TEXT) = frameList[ii].data;
495 } else {
496 *text = '\0';
497 text++;
498 myFrame->Field(ID3FN_DESCRIPTION) = frameList[ii].data;
499 myFrame->Field(ID3FN_TEXT) = text;
500 }
501 if (strlen(ID3_GetString(myFrame, ID3FN_TEXT)) > 0) {
502 myTag.AttachFrame(myFrame);
503 }
504
505 break;
506 }
507 case ID3FID_COMMENT:
508 case ID3FID_UNSYNCEDLYRICS:
509 {
510 // split the string at the ':' remember if no : then leave
511 // descrip/lang empty
512 char *text;
513 text = strchr(frameList[ii].data, ':');
514 if (text == NULL)
515 {
516 myFrame->Field(ID3FN_TEXT) = frameList[ii].data;
517 } else {
518 *text = '\0';
519 text++;
520 char *lang;
521 lang = strchr(text, ':');
522 if (lang == NULL)
523 {
524 myFrame->Field(ID3FN_DESCRIPTION) = frameList[ii].data;
525 myFrame->Field(ID3FN_TEXT) = text;
526 } else {
527 *lang = '\0';
528 lang++;
529 myFrame->Field(ID3FN_DESCRIPTION) = frameList[ii].data;
530 myFrame->Field(ID3FN_TEXT) = text;
531 myFrame->Field(ID3FN_LANGUAGE) = lang;
532 }
533 }
534 /* debug
535 std::cout << ID3_GetString(myFrame, ID3FN_DESCRIPTION) << std::endl
536 << ID3_GetString(myFrame, ID3FN_TEXT) << std::endl
537 << ID3_GetString(myFrame, ID3FN_LANGUAGE) << std::endl;
538 */
539
540 // now try and find a comment/lyrics with the same descript
541 // and lang as what we have
542 ID3_Frame *pFirstFrame = NULL;
543 do {
544 // if pFrame is NULL, either there were no comments/lyrics
545 // to begin with, or we removed them all in the process
546 if (pFrame == NULL) break;
547
548 if (pFirstFrame == NULL)
549 {
550 pFirstFrame = pFrame;
551 }
552
553 char *tmp_desc = ID3_GetString(pFrame, ID3FN_DESCRIPTION);
554 char *tmp_my_desc = ID3_GetString(myFrame, ID3FN_DESCRIPTION);
555 char *tmp_lang = ID3_GetString(pFrame, ID3FN_LANGUAGE);
556 char *tmp_my_lang = ID3_GetString(myFrame, ID3FN_LANGUAGE);
557 if ((strcmp(tmp_desc, tmp_my_desc) == 0) &&
558 (strcmp(tmp_lang, tmp_my_lang) == 0))
559 {
560 ID3_Frame * todel = myTag.RemoveFrame(pFrame);
561 delete todel;
562 if (pFrame == pFirstFrame)
563 {
564 pFirstFrame = NULL;
565 }
566 }
567 delete [] tmp_desc;
568 delete [] tmp_my_desc;
569 delete [] tmp_lang;
570 delete [] tmp_my_lang;
571
572 // get the next frame until it wraps around
573 } while ((pFrame = myTag.Find(frameList[ii].id)) != pFirstFrame);
574
575 char * temp = ID3_GetString(myFrame, ID3FN_TEXT);
576 if (strlen(temp) > 0) {
577 delete[] temp;
578 myTag.AttachFrame(myFrame);
579 }
580
581 break;
582 }
583 case ID3FID_WWWAUDIOFILE:
584 case ID3FID_WWWARTIST:
585 case ID3FID_WWWAUDIOSOURCE:
586 case ID3FID_WWWCOMMERCIALINFO:
587 case ID3FID_WWWCOPYRIGHT:
588 case ID3FID_WWWPUBLISHER:
589 case ID3FID_WWWPAYMENT:
590 case ID3FID_WWWRADIOPAGE:
591 {
592 if (pFrame != NULL)
593 {
594 char *sURL = ID3_GetString(pFrame, ID3FN_URL);
595 if (strcmp(frameList[ii].data, sURL) == 0) {
596 ID3_Frame * todel = myTag.RemoveFrame(pFrame);
597 delete todel;
598 }
599 }
600
601 if (strlen(frameList[ii].data) > 0) {
602 myFrame->Field(ID3FN_URL) = frameList[ii].data;
603 myTag.AttachFrame(myFrame);
604 }
605
606 break;
607
608 }
609 case ID3FID_WWWUSER:
610 {
611 char
612 *sURL = ID3_GetString(myFrame, ID3FN_URL),
613 *sDesc = ID3_GetString(myFrame, ID3FN_DESCRIPTION);
614 std::cout << "(" << sDesc << "): " << sURL << std::endl;
615 delete [] sURL;
616 delete [] sDesc;
617 break;
618 }
619 case ID3FID_INVOLVEDPEOPLE:
620 {
621 // This isn't the right way to do it---will only get first person
622 size_t nItems = myFrame->Field(ID3FN_TEXT).GetNumTextItems();
623 for (size_t nIndex = 1; nIndex <= nItems; nIndex++)
624 {
625 char *sPeople = ID3_GetString(myFrame, ID3FN_TEXT, nIndex);
626 std::cout << sPeople;
627 delete [] sPeople;
628 if (nIndex < nItems)
629 {
630 std::cout << ", ";
631 }
632 }
633 std::cout << std::endl;
634 break;
635 }
636 case ID3FID_PICTURE:
637 {
638 char
639 *sMimeType = ID3_GetString(myFrame, ID3FN_MIMETYPE),
640 *sDesc = ID3_GetString(myFrame, ID3FN_DESCRIPTION),
641 *sFormat = ID3_GetString(myFrame, ID3FN_IMAGEFORMAT);
642 size_t
643 nPicType = myFrame->Field(ID3FN_PICTURETYPE).Get(),
644 nDataSize = myFrame->Field(ID3FN_DATA).Size();
645 std::cout << "(" << sDesc << ")[" << sFormat << ", "
646 << nPicType << "]: " << sMimeType << ", " << nDataSize
647 << " bytes" << std::endl;
648 delete [] sMimeType;
649 delete [] sDesc;
650 delete [] sFormat;
651 break;
652 }
653 case ID3FID_GENERALOBJECT:
654 {
655 char
656 *sMimeType = ID3_GetString(myFrame, ID3FN_TEXT),
657 *sDesc = ID3_GetString(myFrame, ID3FN_DESCRIPTION),
658 *sFileName = ID3_GetString(myFrame, ID3FN_FILENAME);
659 size_t
660 nDataSize = myFrame->Field(ID3FN_DATA).Size();
661 std::cout << "(" << sDesc << ")["
662 << sFileName << "]: " << sMimeType << ", " << nDataSize
663 << " bytes" << std::endl;
664 delete [] sMimeType;
665 delete [] sDesc;
666 delete [] sFileName;
667 break;
668 }
669 case ID3FID_UNIQUEFILEID:
670 {
671 if (pFrame != NULL)
672 {
673 char *sOwner = ID3_GetString(pFrame, ID3FN_TEXT);
674 size_t nDataSize = pFrame->Field(ID3FN_DATA).Size();
675 std::cout << sOwner << ", " << nDataSize
676 << " bytes" << std::endl;
677 delete [] sOwner;
678 }
679 break;
680 }
681 case ID3FID_PLAYCOUNTER:
682 {
683 if (pFrame != NULL)
684 {
685 size_t nCounter = pFrame->Field(ID3FN_COUNTER).Get();
686 std::cout << nCounter << std::endl;
687 }
688 break;
689 }
690 case ID3FID_POPULARIMETER:
691 {
692 if (pFrame != NULL)
693 {
694 char *sEmail = ID3_GetString(pFrame, ID3FN_EMAIL);
695 size_t
696 nCounter = pFrame->Field(ID3FN_COUNTER).Get(),
697 nRating = pFrame->Field(ID3FN_RATING).Get();
698 std::cout << sEmail << ", counter="
699 << nCounter << " rating=" << nRating;
700 delete [] sEmail;
701 }
702 break;
703 }
704 case ID3FID_CRYPTOREG:
705 case ID3FID_GROUPINGREG:
706 {
707 char *sOwner = ID3_GetString(myFrame, ID3FN_OWNER);
708 size_t
709 nSymbol = myFrame->Field(ID3FN_ID).Get(),
710 nDataSize = myFrame->Field(ID3FN_DATA).Size();
711 std::cout << "(" << nSymbol << "): " << sOwner
712 << ", " << nDataSize << " bytes";
713 break;
714 }
715 case ID3FID_AUDIOCRYPTO:
716 case ID3FID_EQUALIZATION:
717 case ID3FID_EVENTTIMING:
718 case ID3FID_CDID:
719 case ID3FID_MPEGLOOKUP:
720 case ID3FID_OWNERSHIP:
721 case ID3FID_PRIVATE:
722 case ID3FID_POSITIONSYNC:
723 case ID3FID_BUFFERSIZE:
724 case ID3FID_VOLUMEADJ:
725 case ID3FID_REVERB:
726 case ID3FID_SYNCEDLYRICS:
727 case ID3FID_SYNCEDTEMPO:
728 case ID3FID_METACRYPTO:
729 {
730 std::cout << " (unimplemented)" << std::endl;
731 break;
732 }
733 default:
734 {
735 std::cout << " frame" << std::endl;
736 break;
737 }
738 }
739 } // steping thru frames
740
741 myTag.Update(UpdFlags);
742
743 /* update file with old mode */
744 if (chmod(argv[nIndex], filestat.st_mode))
745 {
746 std::cerr << "Couldn't reset permissions on '" << argv[nIndex] << "'\n";
747 }
748 }
749
750 return 0;
751 }
752