ref: 765295db16e33455dbc7ae7a2d764219e76bc581
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;
}