root/EpisodePage.c

#include "EpisodePage.h"
#include <stdlib.h>
#include "TvShows.h"
#include "Colors.h"
#include "utils.h"
#include "CurlResult.h"
#include "urlcode.h"


void EpisodePage_init(struct EpisodePage* this) {
    this->name = NULL;
    this->startPage = NULL;
    this->baseUrl = NULL;
    this->episodes = NULL;
    this->selectedLi = NULL;

    int y, x;
    getmaxyx(stdscr, y, x);
    this->window = newwin(y-PLAYER_H, x, 0, 0);
}
void EpisodePage_destroyMembers(struct EpisodePage* this) {
    safe_free(this->name);
    safe_free(this->episodes);
}

bool EpisodePage_handleInput(struct EpisodePage* this, int c) {
    if (this != *this->activePage && c != KEY_RESIZE) {
        return false;
    }

    if (c == 'n'  || c == 'j' || c == KEY_DOWN) {
        EpisodePage_selectDelta(this, +1);
    } else if (c == 'p' || c == 'k' || c == KEY_UP) {
        EpisodePage_selectDelta(this, -1);
    } else if (c == PAGE_UP) {
        EpisodePage_selectDelta(this, -10);
    } else if (c == PAGE_DOWN || c == '<') {
        EpisodePage_selectDelta(this, +10);
    } else if (c == KEY_HOME  || c == '>') {
        EpisodePage_selectFirstOrLast(this, true);
    } else if (c == KEY_END) {
        EpisodePage_selectFirstOrLast(this, false);
    } else if (c == 'b' || c == 'h' || c == KEY_LEFT) {
        *this->activePage = this->startPage;
        wclear(this->startPage->window);
        TvShows_printAll(this->startPage, NULL);
        /* TODO select chapters */
    } else if (c == 'f' || c == 'l' || c == KEY_RIGHT) {
        /* TODO select chapters */
    } else if (c == '/') {
        // TODO search
    } else if (c == KEY_ENTER || c == 'a') {
        EpisodePage_playSelected(this);
    } else if (c == 'u') {
        if (this->episodes || this->name) {
            EpisodePage_fetch(this, (this->name ? this->name : this->episodes->name));
        }
    } else if (c == '+' || c == '-' || c == 't') {
        EpisodePage_toggleWatchedLi(this);
    } else if (c == KEY_RESIZE) {
        int screenX, screenY;
        getmaxyx(stdscr, screenY, screenX);
        wresize(this->window, screenY-PLAYER_H, screenX);
        EpisodePage_draw(this);
        return false;
    } else {
        return false;
    }

    EpisodePage_draw(this);
    return true;
}

void EpisodePage_draw(struct EpisodePage* this) {
    if (this != *this->activePage) {
        return;
    }
    wclear(this->window);
    wmove(this->window, 0, 0);
    wprintw(this->window, "Episode list");


    if (!this->episodes) {
        return;
    }

    int y = 1;
    int screenY = getmaxy(stdscr);
    for (ListNode* it = this->episodes->episodes->first; it; it=it->next) {
        if (y >= screenY - PLAYER_H) {
            break;
        }
        struct Episode* ep = it->data;
        EpisodePage_drawEpisode(this, ep, y);
        ++y;
    }
    wrefresh(this->window);
}

void EpisodePage_drawEpisode(struct EpisodePage* this, struct Episode* ep, int y) {
    char buffer[2048];
    char watchedChar = ep->watched ? '+' : '-';
    const char* epNameSeparator = strlen(ep->episodeName) > 0 ? " - " : " ";
    bool isRewatch = this->episodes->rewatchMarker != -1 &&
        (this->episodes->rewatchMarker+1) == ep->number;
    bool isSelected = this->selectedLi && ep == this->selectedLi->data;
    if (isRewatch) {
        Colors_wset(this->window, Colors_Rewatch);
    }
    if (isSelected) {
        Colors_wset(this->window, Colors_Selected);
    }
    sprintf(buffer, "%c %s %s%s%s %s           -> chapters",
            watchedChar, ep->numberStr, (this->name ? this->name : ""),
            epNameSeparator, ep->episodeName, ep->releaseGroup);
    wmove(this->window, y, 0);
    wprintw(this->window, buffer);
    if (isRewatch) {
        Colors_wset(this->window, Colors_Default);
    }
    if (isSelected) {
        Colors_wset(this->window, Colors_Default);
    }
}

void EpisodePage_selectFirstOrLast(struct EpisodePage* this, bool direction) {
    this->selectedLi = direction
        ? this->episodes->episodes->first
        : this->episodes->episodes->last;
}

void EpisodePage_selectDelta(struct EpisodePage* this, int delta) {
    if (!this->episodes) {
        return;
    }

    if (!this->selectedLi) {
        this->selectedLi = this->episodes->episodes->first;
        return;
    }
    while (delta > 0 && this->selectedLi->next) {
        this->selectedLi = this->selectedLi->next;
        --delta;
    }
    while (delta < 0 && this->selectedLi->previous) {
        this->selectedLi = this->selectedLi->previous;
        ++delta;
    }

}
void EpisodePage_playSelected(struct EpisodePage* this) {
    if (this->selectedLi) {
        List* playlist = List_create();
        bool passedCurrent = false;
        for (ListNode* it = this->episodes->episodes->first; it; it=it->next) {
            struct Episode* ep = it->data;
            if (it == this->selectedLi) {
                passedCurrent = true;
            }
            if (passedCurrent) {
                List_pushBack(playlist, ep->path);
            }
        }
        EpisodeList_play(this->episodes, this->baseUrl, playlist);
    } else {
        List* playlist = EpisodeList_contstructPlaylist(this->episodes);
        EpisodeList_play(this->episodes, this->baseUrl, playlist);
    }
}
void EpisodePage_fetch(struct EpisodePage* this, const char* name) {
    struct EpisodeList* oldList = this->episodes;
    this->episodes = EpisodeList_fetch(this->baseUrl, name);

    if (oldList && this->episodes) {
        /* try to keep selection */
        if (this->selectedLi && this->selectedLi->data) {
            /* const char* path = ((struct Episode*)this->selectedLi->data)->path; */
            float number = ((struct Episode*)this->selectedLi->data)->number;
            ListNode* newLi = NULL;
            for (ListNode* it = this->episodes->episodes->first; it; it=it->next) {
                struct Episode* ep = it->data;
                if (ep && ep->number == number /* 0 == strcmp(path, ep->path) */) {
                    newLi = it;
                    break;
                }
            }
            this->selectedLi = newLi;
        }
        EpisodeList_destroy(oldList);
    }
    EpisodePage_draw(this);
}

void EpisodePage_activate(struct EpisodePage* this) {
    char url[2048];
    char* encoded = url_encode(this->name);
    sprintf(url, "%s/api/assurePage/TvShowPage?%s", this->baseUrl, encoded);

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

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

void EpisodePage_toggleWatched(const char* baseUrl, const char* path) {
    struct CurlResult userdata;
    CurlResult_init(&userdata);
    char url[1024];
    char* encoded = url_encode(path);
    sprintf(url, "%s/api/library/toggleWatched?%s", baseUrl, encoded);
    CURL* handle = Curl_defaultHandle(url, &userdata);
    curl_easy_perform(handle);

    curl_easy_cleanup(handle);
    free(encoded);
}

void EpisodePage_toggleWatchedLi(struct EpisodePage* this) {
    if (!this->selectedLi) {
        return;
    }
    struct Episode* ep = this->selectedLi->data;
    EpisodePage_toggleWatched(this->baseUrl, ep->path);
    ep->watched = !ep->watched;
}

DEFAULT_CREATE_DESTROY(EpisodePage)

void EpisodePage_onServerEvent(const char* event, const char* data, void* userdata) {
    struct EpisodePage* this = userdata;
    if (!this->name) {
        return;
    }

    if (0 == strcmp(event, "playbackEnded")) {
        EpisodePage_fetch(this, this->name);
    }
}