youtube/feed: add simple HTML and gopher output format - frontends - front-ends… | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit d22d896072715e337381959c0cca366b45810cb0 | |
parent 4715c8cfb1799ef4dfe14dea87efc8e4d7c3a60e | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Thu, 18 May 2023 15:28:32 +0200 | |
youtube/feed: add simple HTML and gopher output format | |
Diffstat: | |
M youtube/feed.c | 136 +++++++++++++++++++++++++++++… | |
M youtube/gopher.c | 2 +- | |
2 files changed, 136 insertions(+), 2 deletions(-) | |
--- | |
diff --git a/youtube/feed.c b/youtube/feed.c | |
@@ -86,6 +86,10 @@ static int parsetime(const char *, long long *); | |
static void atom_header(void); | |
static void atom_item(void); | |
static void atom_footer(void); | |
+static void gph_header(void); | |
+static void gph_footer(void); | |
+static void html_header(void); | |
+static void html_footer(void); | |
static void json_header(void); | |
static void json_item(void); | |
static void json_footer(void); | |
@@ -498,6 +502,122 @@ atom_item(void) | |
fputs("</entry>\n", stdout); | |
} | |
+ | |
+static void | |
+html_header(void) | |
+{ | |
+ fputs("<!DOCTYPE HTML>\n" | |
+ "<html>\n" | |
+ "<head>\n" | |
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\… | |
+ "</head>\n" | |
+ "<body><pre>\n", stdout); | |
+} | |
+ | |
+static void | |
+html_footer(void) | |
+{ | |
+ fputs("</pre></body>\n</html>\n", stdout); | |
+} | |
+ | |
+static void | |
+html_item(void) | |
+{ | |
+ struct item *v, *found = NULL; | |
+ size_t i; | |
+ | |
+ /* must have a video id */ | |
+ if (!ctx.fields[FeedFieldYoutubeId].str.len) | |
+ return; | |
+ | |
+ for (i = 0; i < search_res->nitems; i++) { | |
+ v = &(search_res->items[i]); | |
+ if (!strcmp(ctx.fields[FeedFieldYoutubeId].str.data, v->id)) | |
+ found = v; | |
+ } | |
+ /* Only print the video if it was found in the feed aswell. | |
+ This way it filters away shorts too. */ | |
+ if (!found) | |
+ return; | |
+ | |
+ /* just print the original timestamp, it should conform */ | |
+ xmlencode(ctx.fields[FeedFieldTime].str.data); | |
+ fputs(" ", stdout); | |
+ | |
+ if (ctx.fields[FeedFieldLink].str.len) { | |
+ fputs("<a href=\"", stdout); | |
+ xmlencode(ctx.fields[FeedFieldLink].str.data); | |
+ fputs("\">", stdout); | |
+ } | |
+ | |
+ xmlencode(ctx.fields[FeedFieldTitle].str.data); | |
+ | |
+ if (found->duration[0]) { | |
+ fputs(" [", stdout); | |
+ xmlencode(found->duration); | |
+ fputs("]", stdout); | |
+ } | |
+ if (ctx.fields[FeedFieldLink].str.len) { | |
+ fputs("</a>", stdout); | |
+ } | |
+ fputs("\n", stdout); | |
+} | |
+ | |
+static void | |
+gphencode(const char *s) | |
+{ | |
+ gophertext(stdout, s, strlen(s)); | |
+} | |
+ | |
+static void | |
+gph_header(void) | |
+{ | |
+} | |
+ | |
+static void | |
+gph_footer(void) | |
+{ | |
+ fputs(".\r\n", stdout); | |
+} | |
+ | |
+static void | |
+gph_item(void) | |
+{ | |
+ struct item *v, *found = NULL; | |
+ size_t i; | |
+ | |
+ /* must have a video id */ | |
+ if (!ctx.fields[FeedFieldYoutubeId].str.len) | |
+ return; | |
+ | |
+ for (i = 0; i < search_res->nitems; i++) { | |
+ v = &(search_res->items[i]); | |
+ if (!strcmp(ctx.fields[FeedFieldYoutubeId].str.data, v->id)) | |
+ found = v; | |
+ } | |
+ /* Only print the video if it was found in the feed aswell. | |
+ This way it filters away shorts too. */ | |
+ if (!found) | |
+ return; | |
+ | |
+ fputs("h", stdout); | |
+ /* just print the original timestamp, it should conform */ | |
+ gphencode(ctx.fields[FeedFieldTime].str.data); | |
+ fputs(" ", stdout); | |
+ gphencode(ctx.fields[FeedFieldTitle].str.data); | |
+ if (found->duration[0]) { | |
+ fputs(" [", stdout); | |
+ gphencode(found->duration); | |
+ fputs("]", stdout); | |
+ } | |
+ fputs("\t", stdout); | |
+ if (ctx.fields[FeedFieldLink].str.len) { | |
+ fputs("URL:", stdout); | |
+ gphencode(ctx.fields[FeedFieldLink].str.data); | |
+ } | |
+ printf("\t%s\t%s\r\n", server_name, server_port); | |
+} | |
+ | |
static void | |
json_header(void) | |
{ | |
@@ -931,10 +1051,12 @@ usage(void) | |
fputs("Status: 400 Bad Request\r\n", stdout); | |
fputs("Content-Type: text/plain; charset=utf-8\r\n\r\n… | |
printf("400 %s\n", msg); | |
+ fputs("Supported extensions are: [atom|gph|html|json|t… | |
} | |
exit(0); | |
} else { | |
- fputs("usage: feed <channelid> [atom|json|tsv|txt]\n", stderr); | |
+ fputs("usage: feed <channelid> [atom|gph|html|json|tsv|txt]\n"… | |
+ fputs("For example: feed UCrbvoMC0zUvPL8vjswhLOSw txt\n", stde… | |
exit(1); | |
} | |
} | |
@@ -987,6 +1109,10 @@ main(int argc, char *argv[]) | |
if (!strcmp(format, "atom") || !strcmp(format, "xml")) | |
printfields = atom_item; | |
+ else if (!strcmp(format, "gph")) | |
+ printfields = gph_item; | |
+ else if (!strcmp(format, "html")) | |
+ printfields = html_item; | |
else if (!strcmp(format, "json")) | |
printfields = json_item; | |
else if (!strcmp(format, "tsv") || !strcmp(format, "sfeed")) | |
@@ -1040,6 +1166,10 @@ main(int argc, char *argv[]) | |
if (!strcmp(format, "atom") || !strcmp(format, "xml")) | |
atom_header(); | |
+ else if (!strcmp(format, "gph")) | |
+ gph_header(); | |
+ else if (!strcmp(format, "html")) | |
+ html_header(); | |
else if (!strcmp(format, "json")) | |
json_header(); | |
@@ -1048,6 +1178,10 @@ main(int argc, char *argv[]) | |
if (!strcmp(format, "atom") || !strcmp(format, "xml")) | |
atom_footer(); | |
+ else if (!strcmp(format, "gph")) | |
+ gph_footer(); | |
+ else if (!strcmp(format, "html")) | |
+ html_footer(); | |
else if (!strcmp(format, "json")) | |
json_footer(); | |
diff --git a/youtube/gopher.c b/youtube/gopher.c | |
@@ -50,7 +50,7 @@ header(void) | |
void | |
footer(void) | |
{ | |
- printf(".\r\n"); | |
+ fputs(".\r\n", stdout); | |
} | |
int |