/* * cli-tube, just a simple youtube viewer * Copyright (C) 2021 vech * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include "main.h" void error(char *c) { fprintf(stderr, "%s: %s\n", c, strerror(errno)); exit(1); } list *list_create(char title[], char id[]) { list *x = (list *)malloc(sizeof(list)); sscanf(title, "%199[^\n]", x->title); sscanf(id, "%11s", x->id); x->next = NULL; return x; } void list_free(list *start) { list *i; list *next = NULL; for (i = start; i != NULL; i = next) { next = i->next; free(i); } } void list_append(list *list_first, char title[], char id[]) { list *i; for(i = list_first; i->next != NULL; i = i->next); i->next = list_create(title, id); } void list_print_item(list *x, int i) { list *p; if (i == -1){ for (p = x; p->next != NULL; p = p->next); } else { for(p = x; i != 0; p = p->next, i--){ if (p == NULL) { fprintf(stderr, "List index out of range"); exit(1); } } } printf("title: %s\n", p->title); printf("id: %s\n\n", p->id); } list *list_get_node(list *x, int index) { list *p; for (p = x; index != 0; p = p->next, index--) { if (p == NULL) { fprintf(stderr, "List index out of range"); exit(1); } } return p; } list *parse_stream(FILE *file) { int i; list *videos = list_create("", ""); char buffer_id[ID_LENGTH], buffer_title[TITLE_LENGTH]; do { i = fscanf(file, "\"videoRenderer\":{\"videoId\":\"%11s\"", buffer_id); if (i == 1){ do{ i = fscanf(file, "\"title\":{\"runs\":[{\"text\":%199[^}]\"}", buffer_title); if (i == 1){ list_append(videos, buffer_title, buffer_id); break; } } while (fscanf(file, "%*c") != EOF); } } while (fscanf(file, "%*c") != EOF); return videos; } char *argv_concat(char *concat_char, char **argv, int argc) { int i; size_t concat_length = 0; concat_char[0] = '\0'; for (i = 1; i < argc; i++){ concat_length += strlen(argv[i]); } concat_length += argc - 2; if (concat_length > TITLE_LENGTH - 1){ fprintf(stderr, "Input characters exceded: you can enter %i " "chars at most not %i\n", TITLE_LENGTH - 1, (int)concat_length); exit(1); } strcat(concat_char, argv[1]); for (i = 2; i < argc; i++) { strcat(concat_char, "+"); strcat(concat_char, argv[i]); } return concat_char; } void draw_options(list *videos, int video_index, int length) { char *title; int i, xmax, ymax; getmaxyx(stdscr, ymax, xmax); clear(); move(0, 0); i = (video_index/ymax) * ymax; for (i = (video_index/ymax) * ymax; i <= length && getcury(stdscr) < ymax - 1; i++) { if (i == video_index) attron(A_REVERSE); else attroff(A_REVERSE); if (!i) continue; printw("%2d. ", i); title = list_get_node(videos, i)->title; addnstr(title, xmax - 15); if (strlen(title) > xmax - 15) addstr("..."); addch('\n'); attroff(A_REVERSE); } refresh(); } int get_user_video(list *videos) { list *q; int length, key; int video_index = 1; for (q = videos->next, length = 0; q != NULL; q = q->next, length++); setlocale(LC_ALL, ""); initscr(); noecho(); curs_set(0); keypad(stdscr, TRUE); draw_options(videos, video_index, length); do { key = getch(); switch (key) { case KEY_UP: case 'k': video_index--; break; case KEY_DOWN: case 'j': video_index++; break; default: break; } if (video_index > length) video_index = 1; else if (video_index < 1) video_index = length; draw_options(videos, video_index, length); } while(key != '\n'); echo(); curs_set(1); endwin(); return video_index; } int main(int argc, char **argv) { char query[TITLE_LENGTH]; if (argc < 2) { fprintf(stderr, "You must provide at least 1 argument not %i\n", argc - 1); fprintf(stderr, "Usage: cli-tube ...\n"); exit(1); } argv_concat(query, argv, argc); char curl_query[TITLE_LENGTH + 43] = {"https://www.youtube.com/results?search_query="}; strcat(curl_query, query); int curl_main_pipe[2]; if (pipe(curl_main_pipe) == -1) { error("Can't create the pipe"); } pid_t curl_pid = fork(); if (curl_pid == -1) { error("Can't fork curl process"); } else if (!curl_pid) { close(curl_main_pipe[0]); dup2(curl_main_pipe[1], 1); if (execlp("curl", "curl", "--silent",curl_query, NULL) == -1) { error("Can't run curl command"); } } dup2(curl_main_pipe[0], 0); close(curl_main_pipe[1]); list *videos = parse_stream(stdin); int curl_exit_status; if (waitpid(curl_pid, &curl_exit_status, 0) == -1) { error("Can't wait curl process"); } if (WEXITSTATUS(curl_exit_status) < 0) { fprintf(stderr, "curl exit error, exit status: %i", WEXITSTATUS(curl_exit_status)); } close(curl_main_pipe[0]); if (!freopen("/dev/tty", "r", stdin)) { error("/dev/tty"); } list *q; int index_video; index_video = get_user_video(videos); q = list_get_node(videos, index_video); char url_watch[45]; sprintf(url_watch, "https://www.youtube.com/watch?v=%s", q->id); list_free(videos); if (execlp("mpv", "mpv", url_watch, NULL) == -1) { error("Can't run mpv"); } return 0; }