foxygit / ytmdl Log in
commits tags

/c/src/main.c · 2.4 KB

raw
#include <locale.h>
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#include "app.h"
#include "ui.h"

static void build_default_output_dir(char *out, size_t cap) {
    const char *home = getenv("HOME");
    if (!home) home = "/tmp";
    snprintf(out, cap, "%s/Music/ytmdl", home);
}

static void print_usage(const char *prog) {
    fprintf(stderr,
            "Usage: %s [-o output_dir] [-f audio_format]\n"
            "  -o  Directory to save downloaded audio files (default: ~/Music/ytmdl)\n"
            "  -f  Audio format: mp3, m4a, flac, opus, wav, vorbis (default: mp3)\n",
            prog);
}

int main(int argc, char **argv) {
    char output_dir[OUTPUT_DIR_CAP];
    build_default_output_dir(output_dir, sizeof(output_dir));
    char audio_format[AUDIO_FORMAT_CAP] = "mp3";

    int opt;
    while ((opt = getopt(argc, argv, "o:f:h")) != -1) {
        switch (opt) {
            case 'o':
                snprintf(output_dir, sizeof(output_dir), "%s", optarg);
                break;
            case 'f':
                snprintf(audio_format, sizeof(audio_format), "%s", optarg);
                break;
            case 'h':
            default:
                print_usage(argv[0]);
                return opt == 'h' ? 0 : 1;
        }
    }

    setlocale(LC_ALL, "");

    /* Heap-allocated: Queue's fixed-size item array makes App several MB,
     * too large to safely put on the stack. */
    App *app = malloc(sizeof(App));
    if (!app) {
        fprintf(stderr, "Out of memory\n");
        return 1;
    }
    app_init(app, output_dir, audio_format);
    app_start_workers(app);

    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    curs_set(1);
    timeout(150); /* ms; also drives periodic redraws for background progress */

    /* Disable software flow control (IXON/IXOFF) so Ctrl+Q/Ctrl+S reach the
     * app as normal keys instead of being swallowed by the tty driver. */
    struct termios term;
    if (tcgetattr(STDIN_FILENO, &term) == 0) {
        term.c_iflag &= ~(IXON | IXOFF);
        tcsetattr(STDIN_FILENO, TCSANOW, &term);
    }

    UiState ui;
    ui_init(&ui);

    while (!ui.quit_requested) {
        ui_draw(app, &ui);
        int ch = getch();
        if (ch != ERR) {
            ui_handle_key(app, &ui, ch);
        }
    }

    ui_destroy(&ui);
    endwin();
    app_shutdown(app);
    free(app);
    return 0;
}