root/Player.c

#include <ncurses.h>
#include "Player.h"
#include "CurlResult.h"

void Player_init(struct Player* this) {
    int x, y;
    getmaxyx(stdscr, y,x);
    this->window = newwin(PLAYER_H, x, y-PLAYER_H, 0);
    this->paused = true;
    this->progress.duration = 0;
    this->progress.current = 0;
}

void Player_destroyMembers(struct Player* this) {

}

bool Player_handleInput(struct Player* this, int event) {
    if (event == 's' || event == 'c') {
        Player_stop(this->baseUrl);
    } else if (event == '>') {
        Player_moveForwards(this->baseUrl);
    } else if (event == '<') {
        Player_moveBackwards(this->baseUrl);
    } else if (event == ' ') {
        Player_togglePause(this->baseUrl);
    } else if (event == KEY_RESIZE) {
        int screenX, screenY;
        getmaxyx(stdscr, screenY, screenX);
        mvwin(this->window, screenY-PLAYER_H, 0);
        wresize(this->window, PLAYER_H, screenX);
    } else {
        return false;
    }
    return true;
}

void Player_draw(struct Player* this) {
    /* refresh(); */
    /* int oldX, oldY; */
    /* getyx(shows.window, oldY, oldX); */
    /* refresh(); */

    int screenW = getmaxx(this->window);

    wmove(this->window, 0,0);
    wprintw(this->window, "■ (c)     ");
    wprintw(this->window, "<<(<) ");
    wprintw(this->window, this->paused ? "▶ " : "||");
    wprintw(this->window, "( ) ");
    wprintw(this->window, ">>(>)");


    /* draw progress bar */
    if (this->paused) {
        this->progress.lastUpdate = time(NULL);
    } else {
        time_t now = time(NULL);
        double diff = difftime(now, this->progress.lastUpdate);
        if (diff > 0) {
            this->progress.current += diff;
            this->progress.lastUpdate = now;
        }
    }

    int x = sizeof("■ (c)     " "<<(<) " "||" "( ) " ">>(>)")-1;
    int w = (screenW - x - 2 > 1) ? (screenW - x - 2) : 1;
    wprintw(this->window, " [");

    float percent = this->progress.duration != 0
        ? (float)this->progress.current / (float)this->progress.duration
        : 0.5f;

    for (int i=0; i < w; ++i) {
        float p = (float)i / (float)w;
        float nextP = (float)(i+1) / (float)w;
        if (p <= percent) {
            if (nextP <= percent) {
                wprintw(this->window, "=");
            } else {
                wprintw(this->window, "O");
            }
        } else {
            wprintw(this->window, "─");
        }
    }
    wprintw(this->window, "]");

    /* wprintw(this->window, "================="); */
    wrefresh(this->window);
}

void Player_simpleCommand(const char* baseUrl, const char* pathUrl) {
    char url[2048];
    sprintf(url, "%s%s", baseUrl, pathUrl);

    struct CurlResult userdata;
    CURL* handle = Curl_defaultHandle(url, &userdata);
    curl_easy_perform(handle);

    CurlResult_destroyMembers(&userdata);
    curl_easy_cleanup(handle);
}

void Player_stop(const char* baseUrl) {
    Player_simpleCommand(baseUrl, "/api/player/stop");
}
void Player_togglePause(const char* baseUrl) {
    Player_simpleCommand(baseUrl, "/api/player/togglePause");
}
void Player_moveBackwards(const char* baseUrl) {
    Player_simpleCommand(baseUrl, "/api/player/backwards");
}
void Player_moveForwards(const char* baseUrl) {
    Player_simpleCommand(baseUrl, "/api/player/forwards");
}

void restoreMetadata(struct Player* this, json_value* json) {
    if (!json || json->type != json_object) { return; }
    for (int j=0; j < json->u.object.length; ++j) {
        char* mItemName = json->u.object.values[j].name;
        json_value* mValue = json->u.object.values[j].value;
        if (0 == strcmp(mItemName, "duration") && mValue->type == json_integer) {
            this->progress.duration = mValue->u.integer;
        }
    }
}

void Player_onServerEvent(const char* event, const char* data, void* userdata) {
    struct Player* this = userdata;

    if (0 == strcmp(event, "playbackStarted")) {
        json_value* json = json_parse(data, strlen(data));
        if (json && json->type == json_object) {
            for (int i=0; i < json->u.object.length; ++i) {
                char* itemName = json->u.object.values[i].name;
                json_value* itemValue = json->u.object.values[i].value;
                if (0 == strcmp(itemName, "metaData") && itemValue->type == json_object) {
                    restoreMetadata(this, itemValue);
                } else if (0 == strcmp(itemName, "seconds") && itemValue->type == json_integer) {
                    this->progress.current = itemValue->u.integer;
                }
            }
        }
        this->paused = false;
        this->progress.lastUpdate = time(NULL);
    } else if (0 == strcmp(event, "playbackEnded")) {
        this->paused = true;
        this->progress.duration = 0;
        this->progress.current = 0;
        this->progress.lastUpdate = time(NULL);
    } else if (0 == strcmp(event, "paused")) {
        this->paused = true;
        this->progress.lastUpdate = time(NULL);
    } else if (0 == strcmp(event, "unpaused")) {
        this->paused = false;
        this->progress.lastUpdate = time(NULL);
    } else if (0 == strcmp(event, "jumped")) {
        /* {"seconds":3} */
        json_value* json = json_parse(data, strlen(data));
        if (json && json->type == json_object) {
            for (int i=0; i < json->u.object.length; ++i) {
                char* itemName = json->u.object.values[i].name;
                json_value* itemValue = json->u.object.values[i].value;
                if (0 == strcmp(itemName, "seconds") && itemValue->type == json_integer) {
                    this->progress.current = itemValue->u.integer;
                }
            }
        }
        this->progress.lastUpdate = time(NULL);
    }

    Player_draw(this);
}

void restorePauseStatus(struct Player* this, json_value* json) {
    if (!json || json->type != json_object) { return; }
    for (int j=0; j < json->u.object.length; ++j) {
        char* mItemName = json->u.object.values[j].name;
        json_value* mValue = json->u.object.values[j].value;
        if (0 == strcmp(mItemName, "status") && mValue->type == json_string) {
            this->paused = 0 == strncmp("paused", mValue->u.string.ptr, sizeof("paused")-1);
        }
    }
}
void restoreProgress(struct Player* this, json_value* json) {
    if (!json || json->type != json_object) { return; }
    for (int j=0; j < json->u.object.length; ++j) {
        char* mItemName = json->u.object.values[j].name;
        json_value* mValue = json->u.object.values[j].value;
        if (0 == strcmp(mItemName, "seconds") && mValue->type == json_integer) {
            this->progress.current = mValue->u.integer;
        }
    }
}

void fetchGeneric(struct Player* this, const char* pathUrl,
                  void (*restoreFn)(struct Player*,json_value*)) {
    char url[2048];
    sprintf(url, "%s%s", this->baseUrl, pathUrl);

    struct CurlResult userdata;
    CURL* handle = Curl_defaultHandle(url, &userdata);
    curl_easy_perform(handle);
    json_value* json = CurlResult_parse(&userdata);

    restoreFn(this, json);

    json_value_free(json);
    CurlResult_destroyMembers(&userdata);
    curl_easy_cleanup(handle);
}
void fetchMetadata(struct Player* this) {
    fetchGeneric(this, "/api/player/metaData", restoreMetadata);
}
void fetchPauseStatus(struct Player* this) {
    fetchGeneric(this, "/api/player/pauseStatus", restorePauseStatus);
}
void fetchProgress(struct Player* this) {
    fetchGeneric(this, "/api/player/progress", restoreProgress);
}

void Player_fetch(struct Player* this, const char* url) {
    this->baseUrl = url;
    fetchMetadata(this);
    fetchPauseStatus(this);
    fetchProgress(this);

    Player_draw(this);
}