root/EpisodeList.c

#include "StringReplace.h"
#include "EpisodeList.h"
#include "CurlResult.h"
#include "EpisodeListToJson.h"
#include "urlcode.h"
#include <string.h>
#include <stdio.h>
#include <curl/curl.h>
#include <ncurses.h>
#include "utils.h"

void EpisodeList_init(struct EpisodeList* this) {
    this->name = NULL;
    this->episodes = List_create();
    this->rewatchMarker = NOT_REWATCHING;
}
void EpisodeList_destroyMembers(struct EpisodeList* this) {
    safe_free(this->name);
    List_destroyWithContent(this->episodes, Episode_destroy);
}
DEFAULT_CREATE_DESTROY(EpisodeList)

struct EpisodeList* EpisodeList_fetch(const char* baseurl, const char* tvShow) {
    char url[2048];
    char* escapedTvShow = url_encode((char*)tvShow);
    sprintf(url, "%s/api/library/tvshow/%s/details", baseurl, escapedTvShow);

    CURL* handle = curl_easy_init();
    struct CurlResult userdata;
    CurlResult_init(&userdata);
    curl_easy_setopt(handle, CURLOPT_URL, url);
    curl_easy_setopt(handle, CURLOPT_TIMEOUT, 15);
    curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, &userdata);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, CurlResult_write);
    curl_easy_perform(handle);
    json_value* json = CurlResult_parse(&userdata);
    struct EpisodeList* this = EpisodeList_restore(json);

    if (json) {
        json_value_free(json);
    }
    CurlResult_destroyMembers(&userdata);
    curl_easy_cleanup(handle);
    free(escapedTvShow);
    return this;
}

struct EpisodeList* EpisodeList_restore(json_value* json) {
    if (!json || json->type != json_object) {
        return NULL;
    }

    struct EpisodeList* this = EpisodeList_create();
    for (int i=0; i < json->u.object.length; ++i) {
        json_value* value = json->u.object.values[i].value;
        if (0 == strcmp(json->u.object.values[i].name, "rewatchMarker")) {
            this->rewatchMarker =
                value->type == json_double ? value->u.dbl :
                value->type == json_integer ? value->u.integer :
                NOT_REWATCHING;
        } else if (0 == strcmp(json->u.object.values[i].name, "episodes") &&
                   value->type == json_array) {
            json_value* array = value;
            for (int j=0; j < array->u.array.length; ++j) {
                struct Episode* ep = Episode_restore(array->u.array.values[j]);
                if (ep) {
                    List_pushBack(this->episodes, ep);
                }
            }
        }
    }
    List_qSort(this->episodes, Episode_compare);
    return this;
}

List* EpisodeList_contstructPlaylist(struct EpisodeList* this) {
    List* list = List_create();
    // unwatched normal episodes
    for (ListNode* it = this->episodes->first; it; it = it->next) {
        struct Episode* ep = it->data;
        if (!ep->watched && ep->number > EPISODE_SPECIAL) {
            List_pushBack(list, ep->path);
        }
    }
    if (list->first) { return list; }

    // rewatching
    for (ListNode* it = this->episodes->first; it; it = it->next) {
        struct Episode* ep = it->data;
        if (this->rewatchMarker != NOT_REWATCHING &&
            ep->number > this->rewatchMarker) {
            List_pushBack(list, ep->path);
        }
    }
    if (list->first) { return list; }

    // unwatched extras
    for (ListNode* it = this->episodes->first; it; it = it->next) {
        struct Episode* ep = it->data;
        if (!ep->watched) {
            List_pushBack(list, ep->path);
        }
    }
    if (list->first) { return list; }

    // already watched
    for (ListNode* it = this->episodes->first; it; it = it->next) {
        struct Episode* ep = it->data;
        if (ep->watched) {
            List_pushBack(list, ep->path);
        }
    }

    if (!list->first) {
        List_destroy(list);
        return NULL;
    }
    return list;
}

void EpisodeList_play(struct EpisodeList* this, const char* baseUrl, List* playList) {
    if (!playList) {
        return;
    }

    char* jsonResponse = EpisodeList_toJsonString(playList);
    /* size_t jsonResponse_lenght = strlen(jsonResponse); */

    char url[2048];
    sprintf(url, "%s/api/player/setPlaylist", baseUrl);
    CURL* handle = curl_easy_init();
    struct CurlResult userdata;
    CurlResult_init(&userdata);
    curl_easy_setopt(handle, CURLOPT_URL, url);
    curl_easy_setopt(handle, CURLOPT_TIMEOUT, 15);
    curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1);
    curl_easy_setopt(handle, CURLOPT_WRITEDATA, &userdata);
    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, CurlResult_write);

    curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "POST");
    /* curl_easy_setopt(handle, CURLOPT_HTTPPOST, true); */
    curl_easy_setopt(handle, CURLOPT_POSTFIELDS, jsonResponse);
    /* curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, jsonResponse_lenght); */

    curl_easy_perform(handle);
    /* json_value* json = CurlResult_parse(&userdata); */
    printw(userdata.buffer.buffer);
    CurlResult_destroyMembers(&userdata);
    curl_easy_cleanup(handle);

    List_destroy(playList);
}

void Episode_init(struct Episode* this) {
    this->path = NULL;
    this->number = EPISODE_INVALID;
    this->numberStr = NULL;
    this->releaseGroup = NULL;
    this->episodeName = NULL;
    this->watched = false;
}

void Episode_destroyMembers(struct Episode* this) {
    if (this->path) {
        free(this->path);
    }
    if (this->numberStr) {
        free(this->numberStr);
    }
}
DEFAULT_CREATE_DESTROY(Episode)

/* void Episode_draw(struct Episode* this, WINDOW* window) { */
/*     wprintw() */
/* } */

struct Episode* Episode_restore(json_value* json) {
    if (json->type != json_object) {
        return NULL;
    }

    struct Episode* this = Episode_create();
    for (int i=0; i < json->u.object.length; ++i) {
        const char* key = json->u.object.values[i].name;
        json_value* value = json->u.object.values[i].value;
        if (0 == strcmp(key, "path") && value->type == json_string) {
            this->path = copy_string(value->u.string.ptr, value->u.string.length);
        } else if (0 == strcmp(key, "releaseGroup") && value->type == json_string) {
            this->releaseGroup = copy_string(value->u.string.ptr, value->u.string.length);
        } else if (0 == strcmp(key, "episodeName") && value->type == json_string) {
            this->episodeName = copy_string(value->u.string.ptr, value->u.string.length);
        } else if (0 == strcmp(key, "episodeNumber") && value->type == json_string) {
            this->numberStr = copy_string(value->u.string.ptr, value->u.string.length);
        } else if (0 == strcmp(key, "numericEpisodeNumber")) {
            this->number =
                value->type == json_double ? value->u.dbl :
                value->type == json_integer ? value->u.integer :
                EPISODE_INVALID;
        } else if (0 == strcmp(key, "watched") && value->type == json_boolean) {
            this->watched = value->u.boolean;
        }
    }
    return this;
}

int Episode_compare(void* av, void* bv) {
    struct Episode* a = av;
    struct Episode* b = bv;

    if (a->number < 0 && b->number >= 0) {
        return 1;
    }
    if (b->number < 0 && a->number >= 0) {
        return -1;
    }
    if (a->number != b->number) {
        if (a->number < -1 && b->number >= -1) {
            return 1;
        }
        if (b->number < -1 && a->number >= -1) {
            return -1;
        }
        return
            a->number < b->number ? -1 :
            a->number > b->number ?  1 :
            0;
    }
    int strNumCmp = strcmp(a->numberStr, b->numberStr);
    int strNum =
        strNumCmp < 0 ? -1 :
        strNumCmp > 0 ?  1 :
        0;
    if (strNum != 0) {
        return strNum;
    }

    int epNameCmp = strcmp(a->episodeName, b->episodeName);
    return
        epNameCmp < 0 ? -1 :
        epNameCmp > 0 ?  1 :
        0;
}