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;
};