youtube/feed: add simple HTML and gopher output format - frontends - front-ends for some sites (experiment) (DIR) Log (DIR) Files (DIR) Refs (DIR) README (DIR) LICENSE --- (DIR) commit d22d896072715e337381959c0cca366b45810cb0 (DIR) parent 4715c8cfb1799ef4dfe14dea87efc8e4d7c3a60e (HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org> 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(-) --- (DIR) 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\" />\n" + "</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", stdout); printf("400 %s\n", msg); + fputs("Supported extensions are: [atom|gph|html|json|tsv|txt]\n", stdout); } exit(0); } else { - fputs("usage: feed <channelid> [atom|json|tsv|txt]\n", stderr); + fputs("usage: feed <channelid> [atom|gph|html|json|tsv|txt]\n", stderr); + fputs("For example: feed UCrbvoMC0zUvPL8vjswhLOSw txt\n", stderr); 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(); (DIR) 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