cli.c - frontends - front-ends for some sites (experiment)
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       cli.c (7952B)
       ---
            1 #include <sys/socket.h>
            2 #include <sys/types.h>
            3 
            4 #include <ctype.h>
            5 #include <errno.h>
            6 #include <netdb.h>
            7 #include <stdarg.h>
            8 #include <stdio.h>
            9 #include <stdlib.h>
           10 #include <string.h>
           11 #include <unistd.h>
           12 
           13 #include "https.h"
           14 #include "util.h"
           15 #include "youtube.h"
           16 
           17 #define OUT(s) fputs((s), stdout)
           18 #define OUTESCAPE(s) printescape((s))
           19 
           20 /* print: ignore control-characters */
           21 void
           22 printescape(const char *s)
           23 {
           24         for (; *s; ++s)
           25                 if (!iscntrl((unsigned char)*s))
           26                         fputc(*s, stdout);
           27 }
           28 
           29 void
           30 printescape_multiline(const char *s, const char *indent)
           31 {
           32         int i = 0;
           33 
           34         for (; *s; ++s) {
           35                 if (!i)
           36                         fputs(indent, stdout);
           37 
           38                 if (*s == '\n') {
           39                         i = 0;
           40                         fputc(*s, stdout);
           41                 } else if (!iscntrl((unsigned char)*s)) {
           42                         fputc(*s, stdout);
           43                         i = 1;
           44                 }
           45         }
           46 }
           47 
           48 int
           49 render_search_tsv(struct search_response *r)
           50 {
           51         struct item *v;
           52         size_t i;
           53 
           54         for (i = 0; i < r->nitems; i++) {
           55                 v = &(r->items[i]);
           56 
           57                 OUTESCAPE(v->id);
           58                 OUT("\t");
           59                 if (v->id[0]) {
           60                         OUT("https://www.youtube.com/embed/");
           61                         OUTESCAPE(v->id);
           62                 }
           63                 OUT("\t");
           64                 OUTESCAPE(v->title);
           65                 OUT("\t");
           66                 OUTESCAPE(v->publishedat);
           67                 OUT("\t");
           68                 OUTESCAPE(v->viewcount);
           69                 OUT("\t");
           70                 OUTESCAPE(v->duration);
           71                 OUT("\t");
           72                 switch (v->linktype) {
           73                 case Channel:  OUT("channel"); break;
           74                 case Movie:    OUT("movie"); break;
           75                 case Playlist: OUT("playlist"); break;
           76                 default:       break;
           77                 }
           78                 OUT("\t");
           79                 OUTESCAPE(v->channelid);
           80                 OUT("\t");
           81                 OUTESCAPE(v->channeltitle);
           82                 OUT("\t");
           83                 OUTESCAPE(v->userid);
           84 /*                OUT("\t");
           85                 OUTESCAPE(v->shortdescription); */ /* TODO: escape newlines etc */
           86                 OUT("\n");
           87         }
           88 
           89         return 0;
           90 }
           91 
           92 int
           93 render_search(struct search_response *r)
           94 {
           95         struct item *v;
           96         size_t i;
           97 
           98         for (i = 0; i < r->nitems; i++) {
           99                 v = &(r->items[i]);
          100 
          101                 switch (v->linktype) {
          102                 case Channel:
          103                         OUT("Channel:   ");
          104                         OUTESCAPE(v->channeltitle);
          105                         break;
          106                 case Movie:
          107                         OUT("Movie:     ");
          108                         OUTESCAPE(v->title);
          109                         break;
          110                 case Playlist:
          111                         OUT("Playlist:  ");
          112                         OUTESCAPE(v->title);
          113                         break;
          114                 default:
          115                         OUT("           ");
          116                         OUTESCAPE(v->title);
          117                         break;
          118                 }
          119                 if (v->duration[0]) {
          120                         OUT(" [");
          121                         OUTESCAPE(v->duration);
          122                         OUT("]");
          123                 }
          124                 OUT("\n");
          125 
          126                 if (v->id[0]) {
          127                         OUT("URL:       https://www.youtube.com/embed/");
          128                         OUTESCAPE(v->id);
          129                         OUT("\n");
          130                 }
          131 
          132                 if (v->channelid[0] || v->userid[0]) {
          133                         OUT("Channel:   ");
          134                         OUTESCAPE(v->channeltitle);
          135                         OUT(": https://www.youtube.com/feeds/videos.xml?");
          136                         if (v->channelid[0]) {
          137                                 OUT("channel_id=");
          138                                 OUTESCAPE(v->channelid);
          139                         } else if (v->userid[0]) {
          140                                 OUT("user=");
          141                                 OUTESCAPE(v->userid);
          142                         }
          143                         OUT("\n");
          144                 }
          145                 if (v->publishedat[0]) {
          146                         OUT("Published: ");
          147                         OUTESCAPE(v->publishedat);
          148                         OUT("\n");
          149                 }
          150                 if (v->viewcount[0]) {
          151                         OUT("Views:     ");
          152                         printnumsep(v->viewcount);
          153                         OUT("\n");
          154                 }
          155                 OUT("\n");
          156         }
          157 
          158         return 0;
          159 }
          160 
          161 int
          162 render_video(struct video_response *r)
          163 {
          164         struct video_format *f;
          165         char buf[256];
          166         int i;
          167 
          168         OUT("URL:       ");
          169         OUT("https://www.youtube.com/embed/");
          170         OUTESCAPE(r->id);
          171         OUT("\n");
          172 
          173         OUT("Title:     ");
          174         OUTESCAPE(r->title);
          175         OUT("\n");
          176 
          177         if (r->lengthseconds > 0) {
          178                 OUT("Length:    ");
          179                 if (durationstr(r->lengthseconds, buf, sizeof(buf)) < sizeof(buf))
          180                         OUTESCAPE(buf);
          181                 OUT("\n");
          182         }
          183 
          184         OUT("Views:     ");
          185         snprintf(buf, sizeof(buf), "%ld", r->viewcount);
          186         printnumsep(buf);
          187         OUT("\n");
          188 
          189         if (r->publishdate[0]) {
          190                 OUT("Published: ");
          191                 OUTESCAPE(r->publishdate);
          192                 OUT("\n");
          193         }
          194 
          195         if (r->uploaddate[0]) {
          196                 OUT("Uploaded:  ");
          197                 OUTESCAPE(r->uploaddate);
          198                 OUT("\n");
          199         }
          200 
          201         if (r->author[0]) {
          202                 OUT("Channel:   ");
          203                 OUTESCAPE(r->author);
          204                 if (r->channelid[0]) {
          205                         OUT(": https://www.youtube.com/feeds/videos.xml?channel_id=");
          206                         OUTESCAPE(r->channelid);
          207                 }
          208                 OUT("\n");
          209         }
          210 
          211         if (r->shortdescription[0]) {
          212                 OUT("Description:\n\n");
          213                 printescape_multiline(r->shortdescription, "");
          214                 OUT("\n");
          215         }
          216 
          217         if (r->nformats == 0)
          218                 return 0;
          219 
          220         OUT("\n\nFormats:\n\n");
          221 
          222         /* links expiration */
          223         if (r->expiresinseconds > 0) {
          224                 OUT("Expires in ");
          225                 printf("%ld", r->expiresinseconds);
          226                 OUT(" seconds\n");
          227         }
          228 
          229         for (i = 0; i < r->nformats; i++) {
          230                 f = &(r->formats[i]);
          231 
          232 #if 0
          233                 if (f->width < 1280 || f->height < 720)
          234                         continue;
          235 #endif
          236 
          237 #if 0
          238                 OUT("Last modified: ");
          239                 OUTESCAPE(f->lastmodified);
          240                 OUT("\n");
          241 #endif
          242 
          243                 if (f->url[0]) {
          244                         OUT("URL:     ");
          245                         OUTESCAPE(f->url);
          246                         OUT("\n");
          247                 }
          248 
          249                 /* encrypted stream */
          250                 if (f->signaturecipher[0]) {
          251                         OUT("Signature: ");
          252                         OUTESCAPE(f->signaturecipher);
          253                         OUT("\n");
          254                 }
          255 
          256                 if (f->mimetype[0]) {
          257                         OUT("Mime:    ");
          258                         OUTESCAPE(f->mimetype);
          259                         OUT("\n");
          260                 }
          261 
          262                 if (f->itag > 0) {
          263                         OUT("itag:    ");
          264                         printf("%ld\n", f->itag);
          265                 }
          266 
          267                 if (f->qualitylabel[0]) {
          268                         OUT("Quality: ");
          269                         OUTESCAPE(f->qualitylabel);
          270                 } else if (f->quality[0]) {
          271                         OUT("Quality: ");
          272                         OUTESCAPE(f->quality);
          273                 }
          274 
          275                 if (f->width > 0) {
          276                         OUT(", ");
          277                         printf("%ld", f->width);
          278                         OUT("x");
          279                         printf("%ld", f->height);
          280                         OUT("");
          281                 }
          282                 if (f->fps > 0) {
          283                         OUT(", ");
          284                         printf("%ld", f->fps);
          285                         OUT(" FPS");
          286                 }
          287                 OUT("\n");
          288 
          289                 if (f->bitrate > 0) {
          290                         OUT("Bitrate: ");
          291                         printf("%ld", f->bitrate);
          292                         if (f->averagebitrate > 0)
          293                                 printf(", average: %ld", f->averagebitrate);
          294                         OUT("\n");
          295                 }
          296 
          297                 if (f->contentlength > 0) {
          298                         OUT("Size:    ");
          299                         printf("%lld bytes (%.2f MB)\n", f->contentlength, f->contentlength / 1024.0 / 1024.0);
          300                 }
          301 
          302                 if (f->audiochannels > 0 || f->audiosamplerate) {
          303                         OUT("Audio:   ");
          304                         if (f->audiochannels > 0)
          305                                 printf("%ld channels", f->audiochannels);
          306                         if (f->audiosamplerate > 0) {
          307                                 if (f->audiochannels > 0)
          308                                         OUT(", ");
          309                                 printf("%ld sample rate", f->audiosamplerate);
          310                         }
          311                         OUT("\n");
          312                 }
          313 
          314                 OUT("\n");
          315         }
          316 
          317         return 0;
          318 }
          319 
          320 static void
          321 usage(const char *argv0)
          322 {
          323         fprintf(stderr, "usage: %s [-t] <keyword> | <-c channelid> | <-u user> | <-i videoid> | [-o relevance|views|rating]\n", argv0);
          324         exit(1);
          325 }
          326 
          327 int
          328 main(int argc, char *argv[])
          329 {
          330         struct search_response *r = NULL;
          331         struct video_response *vr = NULL;
          332         char search[1024];
          333         const char *keywords = NULL, *channelid = NULL, *user = NULL, *videoid = NULL;
          334         const char *order = "relevance";
          335         int i, usetsv = 0;
          336 
          337         if (pledge("stdio dns inet rpath unveil", NULL) == -1) {
          338                 fprintf(stderr, "pledge: %s\n", strerror(errno));
          339                 exit(1);
          340         }
          341 
          342         for (i = 1; i < argc; i++) {
          343                 if (argv[i][0] == '-') {
          344                         switch (argv[i][1]) {
          345                         case 'c':
          346                                 if (i + 1 >= argc)
          347                                         usage(argv[0]);
          348                                 channelid = argv[i + 1];
          349                                 i++;
          350                                 break;
          351                         case 'i':
          352                                 if (i + 1 >= argc)
          353                                         usage(argv[0]);
          354                                 videoid = argv[i + 1];
          355                                 i++;
          356                                 break;
          357                         case 'o':
          358                                 if (i + 1 >= argc)
          359                                         usage(argv[0]);
          360                                 order = argv[i + 1];
          361                                 i++;
          362                                 break;
          363                         case 'u':
          364                                 if (i + 1 >= argc)
          365                                         usage(argv[0]);
          366                                 user = argv[i + 1];
          367                                 i++;
          368                                 break;
          369                         case 't':
          370                                 usetsv = 1;
          371                                 break;
          372                         default:
          373                                 usage(argv[0]);
          374                         }
          375                         continue;
          376                 }
          377                 keywords = argv[i];
          378         }
          379 
          380         if (unveil(TLS_CA_CERT_FILE, "r") == -1) {
          381                 fprintf(stderr, "unveil: %s\n", strerror(errno));
          382                 exit(1);
          383         }
          384         if (unveil(NULL, NULL) == -1) {
          385                 fprintf(stderr, "unveil: %s\n", strerror(errno));
          386                 exit(1);
          387         }
          388 
          389         if (argc < 2 || !argv[1][0])
          390                 usage(argv[0]);
          391 
          392         /* check order options */
          393         if (strcmp(order, "relevance") &&
          394             strcmp(order, "views") &&
          395             strcmp(order, "rating"))
          396                 usage(argv[0]);
          397 
          398         if (channelid) {
          399                 r = youtube_channel_videos(channelid);
          400         } else if (user) {
          401                 r = youtube_user_videos(user);
          402         } else if (videoid) {
          403                 vr = youtube_video(videoid);
          404                 if (!vr || vr->isfound == 0) {
          405                         OUT("No video found\n");
          406                         exit(1);
          407                 }
          408                 render_video(vr);
          409                 return 0;
          410         } else if (keywords) {
          411                 if (!uriencode(keywords, search, sizeof(search)))
          412                         usage(argv[0]);
          413                 r = youtube_search(search, "", order);
          414         }
          415         if (!r || r->nitems == 0) {
          416                 OUT("No videos found\n");
          417                 exit(1);
          418         }
          419 
          420         if (pledge("stdio", NULL) == -1) {
          421                 fprintf(stderr, "pledge: %s\n", strerror(errno));
          422                 exit(1);
          423         }
          424 
          425         if (usetsv)
          426                 render_search_tsv(r);
          427         else
          428                 render_search(r);
          429 
          430         return 0;
          431 }