diff options
author | Sigrid Solveig Haflínudóttir <sigrid@ftrv.se> | 2022-10-17 18:45:38 +0000 |
---|---|---|
committer | Sigrid Solveig Haflínudóttir <sigrid@ftrv.se> | 2022-10-17 18:45:38 +0000 |
commit | 3668c38e40b94bfa3fa634ab15f3b3b4746aa224 (patch) | |
tree | 64a0ee30848bc988027085727614e7ddb5d2fa17 /sys/src/cmd | |
parent | 5211d29ed470d2bfda78b03970be6dc6a6614447 (diff) |
zuke: update ICY title from the stream metadata while playing
Diffstat (limited to 'sys/src/cmd')
-rw-r--r-- | sys/src/cmd/audio/zuke/icy.c | 143 | ||||
-rw-r--r-- | sys/src/cmd/audio/zuke/icy.h | 2 | ||||
-rw-r--r-- | sys/src/cmd/audio/zuke/mkfile | 2 | ||||
-rw-r--r-- | sys/src/cmd/audio/zuke/mkplist.c | 8 | ||||
-rw-r--r-- | sys/src/cmd/audio/zuke/zuke.c | 60 |
5 files changed, 175 insertions, 40 deletions
diff --git a/sys/src/cmd/audio/zuke/icy.c b/sys/src/cmd/audio/zuke/icy.c index 80c51ea0e..95aa53d6b 100644 --- a/sys/src/cmd/audio/zuke/icy.c +++ b/sys/src/cmd/audio/zuke/icy.c @@ -1,14 +1,95 @@ #include <u.h> #include <libc.h> #include <bio.h> +#include <thread.h> #include "plist.h" #include "icy.h" +typedef struct Icyaux Icyaux; + +struct Icyaux +{ + Channel *newtitle; + int outfd; + int metaint; +}; + +static char * +truncate(char *s){ + Rune … = L'…'; + int n; + + /* titles can be obnoxiously long */ + if(strlen(s) > (n = 48-UTFmax)) + s[n + runetochar(s+n, &…)] = 0; + + return s; +} + +static long +Breadn(Biobufhdr *bp, void *addr, long nbytes) +{ + long n, r; + u8int *p; + + for(n = 0, p = addr; n < nbytes; n += r, p += r){ + if((r = Bread(bp, p, nbytes-n)) < 1) + break; + } + + return n; +} + +static void +icyproc(void *b_) +{ + char *p, *s, *e; + Biobuf *b, out; + int n, r, sz; + Icyaux *aux; + + threadsetname("icy/pull"); + b = b_; + aux = b->aux; + Binit(&out, aux->outfd, OWRITE); + sz = aux->metaint > 4096 ? aux->metaint : 4096; + p = malloc(sz); + for(;;){ + r = Breadn(b, p, aux->metaint > 0 ? aux->metaint : sz); + if(r < 1 || Bwrite(&out, p, r) != r) + break; + if(aux->metaint > 0){ + if((n = 16*Bgetc(b)) < 0) + break; + if(Breadn(b, p, n) != n) + break; + p[n] = 0; + if((s = strstr(p, "StreamTitle='")) != nil && (e = strstr(s+13, "';")) != nil && e != s+13){ + s += 13; + *e = 0; + s = strdup(truncate(s)); + if(sendp(aux->newtitle, s) != 1){ + free(s); + break; + } + } + } + } + free(p); + Bterm(b); + Bterm(&out); + chanclose(aux->newtitle); + + threadexits(nil); +} + int -icyfill(Meta *m) +icyget(Meta *m, int outfd, Channel **newtitle) { - char *s, *s0, *e, *p, *path, *d; - int f, n; + char *s, *e, *p, *path, *d; + int f, r, n; + Icyaux *aux; + Biobuf *b; path = strdup(m->path); s = strchr(path, ':')+3; @@ -25,25 +106,45 @@ icyfill(Meta *m) if(f < 0) return -1; fprint(f, "GET /%s HTTP/0.9\r\nIcy-MetaData: 1\r\n\r\n", e ? e : ""); - s0 = malloc(4096); - if((n = readn(f, s0, 4095)) > 0){ - s0[n] = 0; - for(s = s0; s = strchr(s, '\n');){ - s++; - if(strncmp(s, "icy-name:", 9) == 0 && (e = strchr(s, '\r')) != nil){ - *e = 0; - m->artist[0] = strdup(s+9); - m->numartist = 1; - s = e+1; - }else if(strncmp(s, "icy-url:", 8) == 0 && (e = strchr(s, '\r')) != nil){ - *e = 0; - m->title = strdup(s+8); - s = e+1; - } + b = Bfdopen(f, OREAD); + aux = mallocz(sizeof(*aux), 1); + aux->outfd = outfd; + aux->newtitle = chancreate(sizeof(char*), 2); + for(r = -1;;){ + if((s = Brdline(b, '\n')) == nil) + break; + if((n = Blinelen(b)) < 2) + break; + if(n == 2 && *s == '\r'){ /* eof */ + r = 0; + break; } + s[n-2] = 0; + if(strncmp(s, "icy-name:", 9) == 0){ + s = truncate(s+9); + if(newtitle != nil) + sendp(aux->newtitle, strdup(s)); + else if(m->title == nil) + m->title = strdup(s); + }else if(newtitle == nil && strncmp(s, "icy-url:", 8) == 0 && m->numartist == 0){ + s += 8; + m->artist[m->numartist++] = strdup(s); + }else if(strncmp(s, "icy-metaint:", 12) == 0){ + s += 12; + aux->metaint = atoi(s); + } + } + if(r < 0 || outfd < 0){ + Bterm(b); + b = nil; + free(aux); + } + if(b != nil){ + assert(aux->newtitle != nil); + b->aux = aux; + *newtitle = aux->newtitle; + proccreate(icyproc, b, mainstacksize); } - free(s0); - close(f); - return n > 0 ? 0 : -1; + return r; } diff --git a/sys/src/cmd/audio/zuke/icy.h b/sys/src/cmd/audio/zuke/icy.h index 326851cc3..4f754ad87 100644 --- a/sys/src/cmd/audio/zuke/icy.h +++ b/sys/src/cmd/audio/zuke/icy.h @@ -1 +1 @@ -int icyfill(Meta *m); +int icyget(Meta *m, int outfd, Channel **newtitle); diff --git a/sys/src/cmd/audio/zuke/mkfile b/sys/src/cmd/audio/zuke/mkfile index 4b70009b0..3d338c300 100644 --- a/sys/src/cmd/audio/zuke/mkfile +++ b/sys/src/cmd/audio/zuke/mkfile @@ -13,6 +13,6 @@ default:V: all $O.mkplist: icy.$O plist.$O mkplist.$O -$O.zuke: plist.$O zuke.$O +$O.zuke: icy.$O plist.$O zuke.$O </sys/src/cmd/mkmany diff --git a/sys/src/cmd/audio/zuke/mkplist.c b/sys/src/cmd/audio/zuke/mkplist.c index 9e56c5b68..169d52111 100644 --- a/sys/src/cmd/audio/zuke/mkplist.c +++ b/sys/src/cmd/audio/zuke/mkplist.c @@ -397,14 +397,16 @@ threadmain(int argc, char **argv) for(i = 0; i < argc; i++){ if(strncmp(argv[i], "http://", 7) == 0 || strncmp(argv[i], "https://", 8) == 0){ m = mallocz(sizeof(*m), 1); - m->title = argv[i]; m->path = argv[i]; m->filefmt = ""; - if(icyfill(m) != 0){ + if(icyget(m, -1, nil) != 0){ fprint(2, "%s: %r\n", argv[i]); free(m); - }else + }else{ + if(m->numartist == 0) + m->artist[m->numartist++] = argv[i]; sendp(cmeta, m); + } }else{ if(argv[i][0] == '/') dir = strdup(argv[i]); diff --git a/sys/src/cmd/audio/zuke/zuke.c b/sys/src/cmd/audio/zuke/zuke.c index db520e67b..ed3663d45 100644 --- a/sys/src/cmd/audio/zuke/zuke.c +++ b/sys/src/cmd/audio/zuke/zuke.c @@ -8,6 +8,7 @@ #include <plumb.h> #include <ctype.h> #include "plist.h" +#include "icy.h" #define MAX(a,b) ((a)>=(b)?(a):(b)) #define MIN(a,b) ((a)<=(b)?(a):(b)) @@ -61,6 +62,8 @@ struct Player Channel *ctl; Channel *ev; Channel *img; + Channel *icytitlec; + char *icytitle; double seek; double gain; int pcur; @@ -277,7 +280,7 @@ redraw_(int full) int x, i, j, scrollcenter, w; uvlong dur, msec; Rectangle sel, r; - char tmp[32]; + char tmp[32], *s; Point p, sp, p₀, p₁; Image *col; @@ -421,7 +424,11 @@ redraw_(int full) for(j = 0; cols[j] != 0; j++){ sel.max.x = p.x + colwidth[j]; replclipr(back, 0, sel); - string(back, p, col, sp, f, getcol(getmeta(i), cols[j])); + if(pcurplaying == i && playercurr->icytitle != nil && cols[j] == Ptitle) + s = playercurr->icytitle; + else + s = getcol(getmeta(i), cols[j]); + string(back, p, col, sp, f, s); p.x += colwidth[j] + 8; } replclipr(back, 0, back->r); @@ -456,6 +463,7 @@ redrawproc(void *) threadsetname("redraw"); while(recv(redrawc, &full) == 1){ +Again: redraw_(full); another = 0; full = 0; @@ -464,7 +472,7 @@ redrawproc(void *) another = 1; } if(another) - redraw_(nbfull); + goto Again; } threadexits(nil); @@ -669,12 +677,12 @@ gain(double g, char *buf, long n) static void playerthread(void *player_) { - char *buf, cmd[64], seekpos[12], *fmt; + char *buf, cmd[64], seekpos[12], *fmt, *path, *icytitle; Player *player; Ioproc *io; Image *thiscover; ulong c; - int p[2], fd, pid, noinit, trycoverload; + int p[2], q[2], fd, pid, noinit, trycoverload; long n, r; vlong boffset, boffsetlast; Meta *cur; @@ -691,7 +699,9 @@ playerthread(void *player_) restart: cur = getmeta(player->pcur); fmt = cur->filefmt; + path = cur->path; fd = -1; + q[0] = -1; if(*fmt){ if((fd = open(cur->path, OREAD)) < 0){ fprint(2, "%r\n"); @@ -702,14 +712,25 @@ restart: }else{ sendul(player->ev, Evready); chanclose(player->ev); + if(strncmp(cur->path, "http://", 7) == 0){ /* try icy */ + pipe(q); + if(icyget(cur, q[0], &player->icytitlec) == 0){ + fd = q[1]; + path = nil; + }else{ + close(q[0]); q[0] = -1; + close(q[1]); + } + } } pipe(p); if((pid = rfork(RFPROC|RFFDG|RFNOTEG|RFCENVG|RFNOWAIT)) == 0){ + close(q[0]); close(p[1]); if(fd < 0) fd = open("/dev/null", OREAD); - dup(fd, 0); close(fd); + dup(fd, 0); close(fd); /* fd == q[1] when it's Icy */ dup(p[0], 1); close(p[0]); if(!debug){ dup(fd = open("/dev/null", OWRITE), 2); @@ -720,7 +741,7 @@ restart: snprint(seekpos, sizeof(seekpos), "%g", (double)boffset/Bps); execl(cmd, cmd, boffset ? "-s" : nil, seekpos, nil); }else{ - execl("/bin/play", "play", "-o", "/fd/1", cur->path, nil); + execl("/bin/play", "play", "-o", "/fd/1", path, nil); } close(0); close(1); @@ -728,8 +749,8 @@ restart: } if(pid < 0) sysfatal("rfork: %r"); - if(fd >= 0) - close(fd); + /* fd is q[1] when it's Icy */ + close(fd); close(p[0]); c = 0; @@ -767,7 +788,11 @@ restart: } break; } - + if(player->icytitlec != nil && nbrecv(player->icytitlec, &icytitle) != 0){ + free(player->icytitle); + player->icytitle = icytitle; + redraw(1); + } thiscover = nil; if(player->img != nil && nbrecv(player->img, &thiscover) != 0){ freeimage(cover); @@ -828,18 +853,24 @@ stop: if(player->img != nil) freeimage(recvp(player->img)); freeplayer: - chanfree(player->ctl); - chanfree(player->ev); + close(q[0]); + close(p[1]); if(pid >= 0) postnote(PNGROUP, pid, "interrupt"); closeioproc(io); - if(p[1] >= 0) - close(p[1]); + if(player->icytitlec != nil){ + while((icytitle = recvp(player->icytitlec)) != nil) + free(icytitle); + chanfree(player->icytitlec); + } + chanfree(player->ctl); + chanfree(player->ev); if(player == playercurr) playercurr = nil; if(player == playernext) playernext = nil; free(buf); + free(player->icytitle); free(player); threadexits(nil); } @@ -1488,6 +1519,7 @@ playcur: case 'q': case Kdel: stop(playercurr); + stop(playernext); goto end; case 'i': case 'o': |