ref: 748cb65df1bf26d73030a178d5390edd7f007934
dir: /youtube.c/
#include <u.h> #include <libc.h> #include <json.h> #include "nvi.h" static char *instlst = "https://api.invidious.io/instances.json?sort_by=health,type,users,signup"; static char *fmtnames[] = {"adaptiveFormats", "formatStreams", nil}; static int addfmt(Info *i, JSONEl *f) { Format *fmt; JSON *x; char *s; if((x = jsonbyname(f->val, "url")) == nil){ werrstr("no url"); return -1; } if((s = jsonstr(jsonbyname(f->val, "type"))) == nil){ werrstr("no type"); return -1; } i->nfmt++; if((i->fmt = realloc(i->fmt, i->nfmt * sizeof(*fmt))) == nil) sysfatal("memory"); fmt = &i->fmt[i->nfmt - 1]; memset(fmt, 0, sizeof(*fmt)); fmt->url = estrdup(jsonstr(x)); fmt->type = estrdup(s); if(strncmp(s, "audio/", 6) == 0){ fmt->included |= Iaudio; fmt->quality = estrdup("----"); }else if(strncmp(s, "video/", 6) == 0){ fmt->included |= Ivideo; fmt->quality = estrdup(jsonstr(jsonbyname(f->val, "qualityLabel"))); if((x = jsonbyname(f->val, "fps")) != nil) fmt->fps = x->n; if(strstr(s, ", ") != nil) /* I know, not the best way */ fmt->included |= Iaudio; } if((x = jsonbyname(f->val, "itag")) != nil) fmt->id = atoi(jsonstr(x)); if((x = jsonbyname(f->val, "clen")) != nil) fmt->sz = atoll(jsonstr(x)); if((x = jsonbyname(f->val, "bitrate")) != nil) fmt->bitrate = atoll(jsonstr(x)); return 0; } Info * youtube(char *vid) { JSON *j, *z, *x; JSONEl *e, *f; char *s, *u, **fmtname; Info *i; int fd; j = nil; if((fd = hget(instlst, -1)) >= 0){ if((s = readall(fd)) != nil){ j = jsonparse(s); free(s); } close(fd); } procwait(); if(j == nil){ werrstr("instances: %r"); return nil; } for(e = j->first, i = nil; e != nil && i == nil; e = e->next){ if(e->val->t != JSONArray || e->val->first == nil || e->val->first->next == nil) continue; f = e->val->first; if(f->val->t != JSONString) /* first is the url */ continue; if(f->next->val->t != JSONObject) /* second is the attributes */ continue; if((s = jsonstr(jsonbyname(f->next->val, "type"))) == nil) continue; if(strncmp(s, "http", 4) != 0) /* don't even try onion */ continue; u = smprint("%s://%s/api/v1/videos/%s", s, jsonstr(e->val->first->val), vid); s = nil; z = nil; werrstr(""); if((fd = hget(u, -1)) < 0 || (s = readall(fd)) == nil || (z = jsonparse(s)) == nil || z->t != JSONObject){ free(s); werrstr("%s: %r", u); }else{ free(s); if((i = calloc(1, sizeof(*i))) == nil) sysfatal("memory"); i->author = estrdup(jsonstr(jsonbyname(z, "author"))); i->title = estrdup(jsonstr(jsonbyname(z, "title"))); i->description = estrdup(jsonstr(jsonbyname(z, "description"))); i->duration = jsonbyname(z, "lengthSeconds")->n; i->published = jsonbyname(z, "published")->n; for(fmtname = fmtnames; *fmtname; fmtname++){ if((x = jsonbyname(z, *fmtname)) == nil){ if(debug) fprint(2, "%s: no streams\n", u); continue; } for(f = x->first; f != nil; f = f->next) addfmt(i, f); } if(i->nfmt < 1){ free(i->title); free(i->description); free(i); i = nil; } } close(fd); jsonfree(z); free(u); if(fd >= 0) procwait(); } jsonfree(j); return i; }