shithub: nvi

ref: d81d2b35559fea6684cee44a5d4f494fc585e38d
dir: nvi/nvi.c

View raw version
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <json.h>
#include "nvi.h"

int cmd = Cdownload;
int debug = 0;

static int
cmpfmt(void *a_, void *b_)
{
	Format *a, *b;

	a = a_;
	b = b_;
	if(a->included != b->included)
		return a->included - b->included;

	return b->sz - a->sz;
}

static void
usage(void)
{
	fprint(2, "usage: %s [-i | [-a file_audio] [-v file_video] [-A id|quality] [-V id|quality]] url|id\n", argv0);
	threadexitsall("usage");
}

void
threadmain(int argc, char **argv)
{
	char *vid, *oa, *ov, *ida[8], *idv[8];
	Format *f, *fa, *fv, *ba, *bv;
	Info *(*fun)(char *), *info;
	int i, j, nida, nidv;
	int afd, vfd;

	fmtinstall('P', Pfmt);
	fmtinstall('Z', Zfmt);
	tmfmtinstall();

	fun = youtube;
	nida = 0;
	nidv = 0;
	oa = nil;
	ov = nil;
	ba = nil;
	bv = nil;
	ARGBEGIN{
	case 'd':
		debug++;
		break;
	case 'i':
		cmd = Cinfo;
		break;
	case 'a':
		oa = EARGF(usage());
		break;
	case 'v':
		ov = EARGF(usage());
		break;
	case 'A':
		if(nida >= nelem(ida))
			sysfatal("too many ids for audio");
		ida[nida++] = EARGF(usage());
		break;
	case 'V':
		if(nidv >= nelem(idv))
			sysfatal("too many ids for video");
		idv[nidv++] = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND

	if(argc != 1)
		usage();
	if(cmd != Cinfo && ov == nil && oa == nil){
		fprint(2, "at least one option (-i, -a, -v) has be specified\n");
		threadexitsall("usage");
	}
	vid = argv[0];
	if(strncmp(argv[0], "http", 4) == 0){
		if(strstr(argv[0], "youtu") != nil && (vid = strrpbrk(argv[0], "/=")) != nil)
			vid++;
		else
			fun = peertube;
	}

	if((info = fun(vid)) == nil)
		sysfatal("%r");

	qsort(info->fmt, info->nfmt, sizeof(Format), cmpfmt);
	if(cmd == Cinfo){
		print("ID\tQUALITY\tSIZE\tFORMAT\n");
		for(i = 0, f = info->fmt; i < info->nfmt; i++, f++)
			print("%d\t%s\t%Z\t%s\n", f->id, f->quality ? f->quality : "----", f->sz, f->type);
		print("\n");
		if(*info->author)
			print("author: %s\n", info->author);
		if(*info->title)
			print("title: %s\n", info->title);
		if(*info->description)
			print("description: %s\n", info->description);
		if(info->duration != 0)
			print("duration: %P\n", info->duration);
		if(tmnorm(&info->published) != 0)
			print("published: %τ\n", tmfmt(&info->published, "YYYY/MM/DD"));
	}else if(cmd == Cdownload){
		for(j = 0, fa = nil, f = info->fmt; j < info->nfmt && fa == nil; j++, f++){
			if((f->included == Iaudio) == 0)
				continue;
			if(ba == nil)
				ba = f;
			for(i = 0; i < nida && fa == nil; i++){
				if((alldigit(ida[i]) && atoi(ida[i]) == f->id) || (f->quality != nil && strcmp(f->quality, ida[i]) == 0)){
					fa = f;
					break;
				}
			}
		}
		for(j = 0, fv = nil, f = info->fmt; j < info->nfmt && fv == nil; j++, f++){
			if((f->included & Ivideo) == 0)
				continue;
			if(bv == nil)
				bv = f;
			for(i = 0; i < nidv && fv == nil; i++){
				if((alldigit(idv[i]) && atoi(idv[i]) == f->id) || strcmp(f->quality, idv[i]) == 0){
					fv = f;
					break;
				}
			}
		}
		if(fv == nil)
			fv = bv;
		/* no audio and video stream doesn't have it either */
		if(fa == nil && (fv == nil || (fv->included & Iaudio) == 0))
			fa = ba;

		i = 0;
		if(oa != nil){
			if((afd = open(oa, OWRITE|OTRUNC)) < 0 && (afd = create(oa, OWRITE|OTRUNC, 0644)) < 0)
				sysfatal("%r");
			if(fa != nil && ++i && hget(fa->url, afd) < 0)
				sysfatal("%r");
		}
		if(ov != nil){
			if((vfd = open(ov, OWRITE|OTRUNC)) < 0 && (vfd = create(ov, OWRITE|OTRUNC, 0644)) < 0)
				sysfatal("%r");
			if(fv != nil && ++i && hget(fv->url, vfd) < 0)
				sysfatal("%r");
		}
		if(i == 0)
			sysfatal("no streams found");

		while(i-- > 0)
			procwait();
	}

	threadexitsall(nil);
}