]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/aux/cddb.c
aux/cddb: Provide -e option to print commands to rip audio with tags.
[plan9front.git] / sys / src / cmd / aux / cddb.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <ctype.h>
5
6 char *server = "gnudb.org";
7
8 int debug;
9 #define DPRINT if(debug)fprint
10 int tflag;
11 int Tflag;
12 char *eflag;
13
14 typedef struct Track Track;
15 struct Track {
16         int n;
17         char *title;
18         char *artist;
19 };
20
21 enum {
22         MTRACK = 64,
23 };
24
25 typedef struct Toc Toc;
26 struct Toc {
27         ulong diskid;
28         int ntrack;
29         char *title;
30         char *year;
31         char *artist;
32         Track track[MTRACK];
33 };
34
35 void*
36 emalloc(uint n)
37 {
38         void *p;
39
40         p = malloc(n);
41         if(p == nil)
42                 sysfatal("can't malloc: %r");
43         memset(p, 0, n);
44         return p;
45 }
46
47 char*
48 estrdup(char *s)
49 {
50         char *t;
51
52         t = emalloc(strlen(s)+1);
53         strcpy(t, s);
54         return t;
55 }
56
57 static void
58 dumpcddb(Toc *t)
59 {
60         int i, n, s;
61
62         print("title\t%s\n", t->title);
63         if(t->year[0] != 0)
64                 print("year\t%s\n", t->year);
65         if(t->artist[0] != 0)
66                 print("artist\t%s\n", t->artist);
67         for(i=0; i<t->ntrack; i++){
68                 print("%d\t%s", i+1, t->track[i].title);
69                 if(tflag){
70                         n = t->track[i+1].n;
71                         if(i == t->ntrack-1)
72                                 n *= 75;
73                         s = (n - t->track[i].n)/75;
74                         print("\t%d:%2.2d", s/60, s%60); 
75                 }
76                 if(t->track[i].artist[0] != 0)
77                         print("\t%s", t->track[i].artist);
78                 print("\n");
79         }
80         if(Tflag){
81                 s = t->track[i].n;
82                 print("Total time: %d:%2.2d\n", s/60, s%60);
83         }
84 }
85
86 static void
87 dumpencode(Toc *t)
88 {
89         int i;
90
91         quotefmtinstall();
92         for(i=0; i < t->ntrack; i++){
93                 print("</mnt/cd/a%03d audio/flacenc ", i);
94                 print("-T 'title='^%q -T 'trackno=%d' ", t->track[i].title, i+1);
95                 if(t->year[0] != 0)
96                         print("-T 'year='^%q ", t->year);
97                 if(t->track[i].artist[0] != 0 || t->artist[0] != 0)
98                         print("-T 'artist='^%q ", t->track[i].artist[0] != 0 ? t->track[i].artist : t->artist);
99                 print(">%q/a%03d.flac\n", eflag, i);
100         }
101 }
102
103 static char*
104 split(char *s)
105 {
106         char *p;
107
108         if((p = strchr(s, '/')) == nil)
109                 return nil;
110         p[-1] = 0;
111         return p+2;
112 }
113
114 static int
115 cddbfilltoc(Toc *t)
116 {
117         int fd;
118         int i;
119         char *p, *q, *a;
120         Biobuf bin;
121         char *f[10];
122         int nf;
123         char *id, *categ;
124
125         fd = dial(netmkaddr(server, "tcp", "8880"), 0, 0, 0);
126         if(fd < 0) {
127                 fprint(2, "%s: %s: cannot dial: %r\n", argv0, server);
128                 return -1;
129         }
130         Binit(&bin, fd, OREAD);
131
132         if((p=Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2) {
133         died:
134                 close(fd);
135                 Bterm(&bin);
136                 fprint(2, "%s: error talking to cddb server %s\n",
137                         argv0, server);
138                 if(p) {
139                         p[Blinelen(&bin)-1] = 0;
140                         fprint(2, "%s: server says: %s\n", argv0, p);
141                 }
142                 return -1;
143         }
144
145         fprint(fd, "cddb hello gre plan9 9cd 1.0\r\n");
146         if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
147                 goto died;
148
149         /*
150          *      Protocol level 6 is the same as level 5 except that
151          *      the character set is now UTF-8 instead of ISO-8859-1. 
152          */
153         fprint(fd, "proto 6\r\n");
154         DPRINT(2, "proto 6\r\n");
155         if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
156                 goto died;
157         p[Blinelen(&bin)-1] = 0;
158         DPRINT(2, "cddb: %s\n", p);
159
160         fprint(fd, "cddb query %8.8lux %d", t->diskid, t->ntrack);
161         DPRINT(2, "cddb query %8.8lux %d", t->diskid, t->ntrack);
162         for(i=0; i<t->ntrack; i++) {
163                 fprint(fd, " %d", t->track[i].n);
164                 DPRINT(2, " %d", t->track[i].n);
165         }
166         fprint(fd, " %d\r\n", t->track[t->ntrack].n);
167         DPRINT(2, " %d\r\n", t->track[t->ntrack].n);
168
169         if((p = Brdline(&bin, '\n')) == nil || atoi(p)/100 != 2)
170                 goto died;
171         p[Blinelen(&bin)-1] = 0;
172         DPRINT(2, "cddb: %s\n", p);
173         nf = tokenize(p, f, nelem(f));
174         if(nf < 1)
175                 goto died;
176
177         switch(atoi(f[0])) {
178         case 200:       /* exact match */
179                 if(nf < 3)
180                         goto died;
181                 categ = f[1];
182                 id = f[2];
183                 break;
184         case 210:       /* exact matches */
185         case 211:       /* close matches */
186                 if((p = Brdline(&bin, '\n')) == nil)
187                         goto died;
188                 if(p[0] == '.') /* no close matches? */
189                         goto died;
190                 p[Blinelen(&bin)-1] = '\0';
191
192                 /* accept first match */
193                 nf = tokenize(p, f, nelem(f));
194                 if(nf < 2)
195                         goto died;
196                 categ = f[0];
197                 id = f[1];
198
199                 /* snarf rest of buffer */
200                 while(p[0] != '.') {
201                         if((p = Brdline(&bin, '\n')) == nil)
202                                 goto died;
203                         p[Blinelen(&bin)-1] = '\0';
204                         DPRINT(2, "cddb: %s\n", p);
205                 }
206                 break;
207         case 202: /* no match */
208         default:
209                 goto died;
210         }
211
212         t->title = "";
213         t->artist = "";
214         t->year = "";
215         for(i=0; i<t->ntrack; i++) {
216                 t->track[i].title = "";
217                 t->track[i].artist = "";
218         }
219
220         /* fetch results for this cd */
221         fprint(fd, "cddb read %s %s\r\n", categ, id);
222         do {
223                 if((p = Brdline(&bin, '\n')) == nil)
224                         goto died;
225                 q = p+Blinelen(&bin)-1;
226                 while(isspace(*q))
227                         *q-- = 0;
228 DPRINT(2, "cddb %s\n", p);
229                 if(strncmp(p, "DTITLE=", 7) == 0) {
230                         p += 7;
231                         a = split(p);
232                         if(a != nil) {
233                                 t->artist = estrdup(p);
234                                 p = a;
235                         }
236                         t->title = estrdup(p);
237                 }
238                 else if(strncmp(p, "DYEAR=", 6) == 0) {
239                         t->year = estrdup(p+6);
240                 }
241                 else if(strncmp(p, "TTITLE", 6) == 0 && isdigit(p[6])) {
242                         i = atoi(p+6);
243                         if(i < t->ntrack) {
244                                 p += 6;
245                                 while(isdigit(*p))
246                                         p++;
247                                 if(*p == '=')
248                                         p++;
249                                 a = split(p);
250                                 if(a != nil) {
251                                         t->track[i].artist = estrdup(p);
252                                         p = a;
253                                 }
254                                 t->track[i].title = estrdup(p); 
255                         }
256                 } 
257         } while(*p != '.');
258
259         fprint(fd, "quit\r\n");
260         close(fd);
261         Bterm(&bin);
262
263         return 0;
264 }
265
266 void
267 usage(void)
268 {
269         fprint(2, "usage: aux/cddb [-DTt] [-s server] query diskid n ...\n");
270         exits("usage");
271 }
272
273 void
274 main(int argc, char **argv)
275 {
276         int i;
277         Toc toc;
278
279         ARGBEGIN{
280         case 'D':
281                 debug = 1;
282                 break;
283         case 's':
284                 server = EARGF(usage());
285                 break;
286         case 'e':
287                 eflag = EARGF(usage());
288                 break;
289         case 'T':
290                 Tflag = 1;
291                 /*FALLTHROUGH*/
292         case 't':
293                 tflag = 1;
294                 break;
295         }ARGEND
296
297         if(argc < 3 || strcmp(argv[0], "query") != 0)
298                 usage();
299
300         toc.diskid = strtoul(argv[1], 0, 16);
301         toc.ntrack = atoi(argv[2]);
302         if(argc != 3+toc.ntrack+1)
303                 sysfatal("argument count does not match given ntrack");
304
305         for(i=0; i<=toc.ntrack; i++)
306                 toc.track[i].n = atoi(argv[3+i]);
307
308         if(cddbfilltoc(&toc) < 0)
309                 exits("whoops");
310
311         if(eflag != nil)
312                 dumpencode(&toc);
313         else
314                 dumpcddb(&toc);
315         exits(nil);
316 }