1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdarg.h>
5 #if defined(_WIN32)
6 # include <io.h>
7 # define F_OK 0
8 #else
9 # include <unistd.h>
10 #endif
11 #include "id3v2.h"
12
13 #define NAME "id3-images"
14
15 /*
16
17 minimalistic program to extract cover art situated in ID3v2 tags
18
19 copyright (c) 2015 Marc R. Schoolderman <squell@alumina.nl>
20
21 use, modification, copying and distribution of this software is permitted
22 under the conditions described in the file 'COPYING'.
23
24 */
25
26 static const char* picture_types[] = {
27 "other",
28 "icon",
29 "other_icon",
30 "front_cover",
31 "back_cover",
32 "leaflet",
33 "media",
34 "lead_artist",
35 "artist",
36 "conductor",
37 "band",
38 "composer",
39 "lyricist",
40 "location",
41 "recording",
42 "performance",
43 "screencap",
44 "red_herring",
45 "illustration",
46 "logotype",
47 "studio_logotype"
48 };
49
Help(const char * name)50 static void Help(const char *name)
51 {
52 const char *base = strchr(name, '/');
53 printf(
54 NAME " 0.2\n"
55 "Extract embedded art from ID3v2 tags to current directory\n"
56 "usage: %s filename.mp3\n"
57 "\nReport bugs to <squell@alumina.nl>.\n",
58 base? base+1 : name
59 );
60 exit(1);
61 }
62
eprintf(const char * msg,...)63 static void eprintf(const char* msg, ...)
64 {
65 va_list args;
66 va_start(args, msg);
67 fprintf (stderr, "%s: ", NAME);
68 vfprintf (stderr, msg, args);
69 va_end(args);
70 }
71
mime_ext(const char * fname,const char * mime_type)72 const char* mime_ext(const char *fname, const char* mime_type)
73 {
74 static const char* exts[] = {
75 "image/jpeg", ".jpg",
76 "image/png", ".png",
77 0
78 };
79 size_t i;
80 for(i=0; exts[i]; i+=2) {
81 if(strcmp(mime_type, exts[i]) == 0)
82 return exts[i+1];
83 }
84 eprintf("%s: unknown mime type: %s\n", fname, mime_type);
85 return ".unknown";
86 }
87
write_blob(const char * basename,const char * ext,const void * blob,size_t size)88 void write_blob(const char *basename, const char* ext, const void* blob, size_t size)
89 {
90 static char image_fn[512];
91 FILE *f;
92
93 strncpy(image_fn, basename, 510);
94 strncat(image_fn, ext, 510);
95
96 if( access(image_fn, F_OK) == 0 ) {
97 eprintf("`%s' already exists, not overwriting\n", image_fn);
98 return;
99 }
100
101 f = fopen(image_fn, "wb");
102 if(fwrite(blob, 1, size, f) != size | fclose(f) != 0) {
103 eprintf("`%s' could not be written\n", image_fn);
104 perror(0);
105 } else {
106 printf("%s\n", image_fn);
107 }
108 }
109
membrk0(const char * buf,size_t size,int wide)110 const char *membrk0(const char *buf, size_t size, int wide)
111 {
112 const char* const end = buf + size - wide;
113 const int step = 1+wide;
114 for( ; buf < end; buf += step) {
115 if(!buf[0] && !buf[wide])
116 return buf;
117 }
118 return 0;
119 }
120
main(int argc,char * argv[])121 int main(int argc, char *argv[])
122 {
123 if(argc <= 1 || argc > 2) Help(argv[0]);
124
125 if(*++argv) {
126 void *tag = ID3_readf(*argv, 0);
127 ID3FRAME f;
128 int counter = 0;
129
130 if(!tag) return 0;
131
132 if(ID3_start(f,tag) >= 2) {
133 while(ID3_frame(f)) {
134 if(strcmp(f->ID, "APIC") == 0) {
135
136 /* see ID3v2.3 4.15 -- 'Attached Picture' for reference */
137
138 char wide = f->data[0] == 1 || f->data[0] == 2;
139 const char *mime_type = f->data+1;
140 const char *type = memchr(mime_type, 0, f->size-(2+wide));
141 const char *descr, *blob;
142
143 if(!type || (type[1]&0xFFu) > sizeof picture_types/sizeof *picture_types) {
144 eprintf("%s has an incorrect ID3v2 tag!\n", *argv);
145 continue;
146 } else {
147 ++type; /* skip terminator */
148 }
149
150 descr = type+1;
151 blob = membrk0(descr, f->size-(descr-f->data), wide);
152 if(!blob) {
153 eprintf("%s has an incorrect ID3v2 tag!\n", *argv);
154 continue;
155 } else {
156 blob += 1+wide; /* skip terminator */
157 }
158
159 write_blob( picture_types[*type],
160 mime_ext(*argv, mime_type),
161 blob,
162 f->size - (blob - f->data) );
163 counter++;
164 }
165 }
166 }
167
168 if(counter == 0)
169 eprintf("%s contains no embedded images\n", *argv);
170 ID3_free(tag);
171 }
172 return 0;
173 }
174
175