youtube: output improvements - frontends - front-ends for some sites (experiment) (DIR) Log (DIR) Files (DIR) Refs (DIR) README (DIR) LICENSE --- (DIR) commit 4eef4fb9b890ae71f554e996a3a7a542302e68f4 (DIR) parent 11f745425e13385e5a69cf3f8cdceaa3027dad64 (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org> Date: Fri, 24 Feb 2023 22:39:39 +0100 youtube: output improvements - pre-parse numbers to long long. - show duration as a string %H:%M:%S. - show filesize in bytes and MB. - etc... Diffstat: M util.c | 17 +++++++++++++++++ M util.h | 1 + M youtube/cli.c | 110 +++++++++++++++++-------------- M youtube/youtube.c | 41 ++++++++++++++++++++----------- M youtube/youtube.h | 31 ++++++++++++++++--------------- 5 files changed, 122 insertions(+), 78 deletions(-) --- (DIR) diff --git a/util.c b/util.c @@ -204,3 +204,20 @@ gophertext(FILE *fp, const char *s, size_t len) } } } + +/* seconds to duration string: "%H:%M:%S" or "%H:%M:%S" */ +int +durationstr(long secs, char *buf, size_t bufsiz) +{ + int h, m, s, r; + + h = secs / 3600; + m = secs / 60; + s = secs; + if (h <= 0) + r = snprintf(buf, bufsiz, "%02d:%02d", m % 60, s % 60); + else + r = snprintf(buf, bufsiz, "%d:%02d:%02d", h, m % 60, s % 60); + + return r; +} (DIR) diff --git a/util.h b/util.h @@ -9,6 +9,7 @@ size_t strlcat(char *, const char *, size_t); size_t strlcpy(char *, const char *, size_t); int decodeparam(char *buf, size_t bufsiz, const char *s); +int durationstr(long secs, char *buf, size_t bufsiz); int friendlytime(time_t now, time_t t); char *getparam(const char *query, const char *s); void gophertext(FILE *fp, const char *s, size_t len); (DIR) diff --git a/youtube/cli.c b/youtube/cli.c @@ -157,12 +157,11 @@ int render_video(struct video_response *r) { struct video_format *f; - long l; + char buf[256]; int i; OUT("URL: "); - OUTESCAPE(r->id); - OUT(", https://www.youtube.com/embed/"); + OUT("https://www.youtube.com/embed/"); OUTESCAPE(r->id); OUT("\n"); @@ -170,21 +169,28 @@ render_video(struct video_response *r) OUTESCAPE(r->title); OUT("\n"); - OUT("Views: "); - OUTESCAPE(r->viewcount); - OUT("\n"); + if (r->lengthseconds > 0) { + OUT("Length: "); + if (durationstr(r->lengthseconds, buf, sizeof(buf)) < sizeof(buf)) + OUTESCAPE(buf); + OUT("\n"); + } - OUT("Length: "); - OUTESCAPE(r->lengthseconds); + OUT("Views: "); + printf("%ld", r->viewcount); OUT("\n"); - OUT("Published: "); - OUTESCAPE(r->publishdate); - OUT("\n"); + if (r->publishdate[0]) { + OUT("Published: "); + OUTESCAPE(r->publishdate); + OUT("\n"); + } - OUT("Uploaded: "); - OUTESCAPE(r->uploaddate); - OUT("\n"); + if (r->uploaddate[0]) { + OUT("Uploaded: "); + OUTESCAPE(r->uploaddate); + OUT("\n"); + } if (r->author[0]) { OUT("Channel: "); @@ -208,9 +214,9 @@ render_video(struct video_response *r) OUT("\n\nFormats:\n\n"); /* links expiration */ - if (r->expiresinseconds[0]) { + if (r->expiresinseconds > 0) { OUT("Expires in "); - OUTESCAPE(r->expiresinseconds); + printf("%ld", r->expiresinseconds); OUT(" seconds\n"); } @@ -218,68 +224,76 @@ render_video(struct video_response *r) f = &(r->formats[i]); #if 0 - l = strtol(f->width, NULL, 10); - if (l < 1280) - continue; - l = strtol(f->height, NULL, 10); - if (l < 720) + if (f->width < 1280 || f->height < 720) continue; #endif #if 0 - OUT("\titag: "); + OUT("itag: "); OUTESCAPE(f->itag); OUT("\n"); - OUT("\tLast modified: "); + OUT("Last modified: "); OUTESCAPE(f->lastmodified); OUT("\n"); - OUT("\tContent-Length: "); - OUTESCAPE(f->contentlength); - OUT("\n"); + #endif - OUT("\tURL: "); + OUT("URL: "); OUTESCAPE(f->url); OUT("\n"); - OUT("\tMime-type: "); - OUTESCAPE(f->mimetype); - OUT("\n"); - - OUT("\tBitrate: "); - OUTESCAPE(f->bitrate); - OUT("\n"); + if (f->mimetype[0]) { + OUT("Mime-type: "); + OUTESCAPE(f->mimetype); + OUT("\n"); + } - OUT("\tQuality: "); - if (f->qualitylabel[0]) + if (f->qualitylabel[0]) { + OUT("Quality: "); OUTESCAPE(f->qualitylabel); - else if (f->quality[0]) + } else if (f->quality[0]) { + OUT("Quality: "); OUTESCAPE(f->quality); + } - if (f->width[0]) { + if (f->width > 0) { OUT(", "); - OUTESCAPE(f->width); + printf("%ld", f->width); OUT("x"); - OUTESCAPE(f->height); + printf("%ld", f->height); OUT(""); } - if (f->fps[0]) { + if (f->fps > 0) { OUT(", "); - OUTESCAPE(f->fps); + printf("%ld", f->fps); OUT(" FPS"); } OUT("\n"); - if (f->audiochannels[0]) { - OUT("\tAudio channels: "); - OUTESCAPE(f->audiochannels); + if (f->bitrate > 0) { + OUT("Bitrate: "); + printf("%ld", f->bitrate); + if (f->averagebitrate > 0) + printf(", average: %ld", f->averagebitrate); OUT("\n"); } - if (f->audiosamplerate[0]) { - OUT("\tAudio sample rate: "); - OUTESCAPE(f->audiosamplerate); + + if (f->contentlength > 0) { + OUT("Size: "); + printf("%lld bytes (%.2f MB)\n", f->contentlength, f->contentlength / 1024.0 / 1024.0); + } + + if (f->audiochannels > 0 || f->audiosamplerate) { + OUT("Audio: "); + if (f->audiochannels > 0) + printf("%ld channels", f->audiochannels); + if (f->audiosamplerate > 0) { + if (f->audiochannels > 0) + OUT(", "); + printf("%ld sample rate", f->audiosamplerate); + } OUT("\n"); } (DIR) diff --git a/youtube/youtube.c b/youtube/youtube.c @@ -15,6 +15,17 @@ #include "util.h" #include "youtube.h" +static long long +getnum(const char *s) +{ + long long l; + + l = strtoll(s, 0, 10); + if (l < 0) + l = 0; + return l; +} + static char * youtube_request(const char *path) { @@ -286,7 +297,6 @@ processnode_video(struct json_node *nodes, size_t depth, const char *value, { struct video_response *r = (struct video_response *)pp; struct video_format *f; - static struct item *item; if (depth > 1) { if (nodes[0].type == JSON_TYPE_OBJECT && @@ -296,7 +306,7 @@ processnode_video(struct json_node *nodes, size_t depth, const char *value, if (depth == 2 && nodes[2].type == JSON_TYPE_STRING && !strcmp(nodes[2].name, "expiresInSeconds")) { - strlcpy(r->expiresinseconds, value, sizeof(r->expiresinseconds)); + r->expiresinseconds = getnum(value); } if (depth >= 3 && @@ -306,9 +316,8 @@ processnode_video(struct json_node *nodes, size_t depth, const char *value, if (r->nformats > MAX_FORMATS) return; /* ignore: don't add too many formats */ - if (depth == 4 && nodes[3].type == JSON_TYPE_OBJECT) { + if (depth == 4 && nodes[3].type == JSON_TYPE_OBJECT) r->nformats++; - } if (r->nformats == 0) return; @@ -321,9 +330,9 @@ processnode_video(struct json_node *nodes, size_t depth, const char *value, nodes[4].type == JSON_TYPE_NUMBER || nodes[4].type == JSON_TYPE_BOOL)) { if (!strcmp(nodes[4].name, "width")) { - strlcpy(f->width, value, sizeof(f->width)); + f->width = getnum(value); } else if (!strcmp(nodes[4].name, "height")) { - strlcpy(f->height, value, sizeof(f->height)); + f->height = getnum(value); } else if (!strcmp(nodes[4].name, "url")) { strlcpy(f->url, value, sizeof(f->url)); } else if (!strcmp(nodes[4].name, "qualityLabel")) { @@ -331,21 +340,23 @@ processnode_video(struct json_node *nodes, size_t depth, const char *value, } else if (!strcmp(nodes[4].name, "quality")) { strlcpy(f->quality, value, sizeof(f->quality)); } else if (!strcmp(nodes[4].name, "fps")) { - strlcpy(f->fps, value, sizeof(f->fps)); + f->fps = getnum(value); } else if (!strcmp(nodes[4].name, "bitrate")) { - strlcpy(f->bitrate, value, sizeof(f->bitrate)); + f->bitrate = getnum(value); + } else if (!strcmp(nodes[4].name, "averageBitrate")) { + f->averagebitrate = getnum(value); } else if (!strcmp(nodes[4].name, "mimeType")) { strlcpy(f->mimetype, value, sizeof(f->mimetype)); } else if (!strcmp(nodes[4].name, "itag")) { - strlcpy(f->itag, value, sizeof(f->itag)); + f->itag = getnum(value); } else if (!strcmp(nodes[4].name, "contentLength")) { - strlcpy(f->contentlength, value, sizeof(f->contentlength)); + f->contentlength = getnum(value); } else if (!strcmp(nodes[4].name, "lastModified")) { - strlcpy(f->lastmodified, value, sizeof(f->lastmodified)); + f->lastmodified = getnum(value); } else if (!strcmp(nodes[4].name, "audioChannels")) { - strlcpy(f->audiochannels, value, sizeof(f->audiochannels)); + f->audiochannels = getnum(value); } else if (!strcmp(nodes[4].name, "audioSampleRate")) { - strlcpy(f->audiosamplerate, value, sizeof(f->audiosamplerate)); + f->audiosamplerate = getnum(value); } } } @@ -375,11 +386,11 @@ processnode_video(struct json_node *nodes, size_t depth, const char *value, } else if (!strcmp(nodes[2].name, "videoId")) { strlcpy(r->id, value, sizeof(r->id)); } else if (!strcmp(nodes[2].name, "lengthSeconds")) { - strlcpy(r->lengthseconds, value, sizeof(r->lengthseconds)); + r->lengthseconds = getnum(value); } else if (!strcmp(nodes[2].name, "author")) { strlcpy(r->author, value, sizeof(r->author)); } else if (!strcmp(nodes[2].name, "viewCount")) { - strlcpy(r->viewcount, value, sizeof(r->viewcount)); + r->viewcount = getnum(value); } else if (!strcmp(nodes[2].name, "channelId")) { strlcpy(r->channelid, value, sizeof(r->channelid)); } else if (!strcmp(nodes[2].name, "shortDescription")) { (DIR) diff --git a/youtube/youtube.h b/youtube/youtube.h @@ -5,9 +5,9 @@ struct item { char channeltitle[1024]; char channelid[256]; char userid[256]; - char publishedat[32]; - char viewcount[32]; - char duration[32]; + char publishedat[32]; /* "human-friendly" string */ + char viewcount[32]; /* view count string, formatted */ + char duration[32]; /* duration string */ #ifdef neinneinnein char shortdescription[4096]; @@ -21,19 +21,20 @@ struct search_response { }; struct video_format { - char itag[32]; /* video id */ + long itag; char url[2048]; char mimetype[256]; /* mime-type and video codecs, etc */ - char bitrate[256]; - char width[32]; /* pixel width */ - char height[32]; /* pixel width */ - char fps[16]; /* frames-per-second */ + long bitrate; + long averagebitrate; + long width; /* pixel width */ + long height; /* pixel width */ + long fps; /* frames-per-second */ char qualitylabel[64]; char quality[64]; - char contentlength[64]; /* content length in bytes */ - char lastmodified[64]; - char audiosamplerate[32]; - char audiochannels[16]; + long long contentlength; /* content length in bytes */ + long lastmodified; /* timestamp */ + long audiosamplerate; + long audiochannels; }; #define MAX_FORMATS 50 @@ -44,14 +45,14 @@ struct video_response { char channelid[256]; char publishdate[32]; /* YYYY-mm-dd */ char uploaddate[32]; /* YYYY-mm-dd */ - char viewcount[32]; - char lengthseconds[32]; + long viewcount; + long lengthseconds; char shortdescription[4096 * 4]; int isfound; /* expiration for URLs in video formats */ - char expiresinseconds[32]; + long expiresinseconds; struct video_format formats[MAX_FORMATS + 1]; int nformats; };