diff src/anilist.cpp @ 2:23d0d9319a00

Update Also converted everything to LF from CRLF
author Paper <mrpapersonic@gmail.com>
date Sat, 12 Aug 2023 03:16:26 -0400
parents 1ae666fdf9e2
children 190ded9438c0
line wrap: on
line diff
--- a/src/anilist.cpp	Tue Aug 08 19:49:15 2023 -0400
+++ b/src/anilist.cpp	Sat Aug 12 03:16:26 2023 -0400
@@ -1,235 +1,234 @@
-#include "window.h"
-#include "json.h"
-#include <curl/curl.h>
-#include <chrono>
-#include <exception>
-#include <format>
-#include "anilist.h"
-#include "anime.h"
-#include "config.h"
-#include "string_utils.h"
-#define CLIENT_ID "13706"
-
-size_t AniList::CurlWriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) {
-    ((std::string*)userdata)->append((char*)contents, size * nmemb);
-    return size * nmemb;
-}
-
-std::string AniList::SendRequest(std::string data) {
-	struct curl_slist *list = NULL;
-	std::string userdata;
-	curl = curl_easy_init();
-	if (curl) {
-		list = curl_slist_append(list, "Accept: application/json");
-		list = curl_slist_append(list, "Content-Type: application/json");
-		std::string bearer = "Authorization: Bearer " + session.config.anilist.auth_token;
-		list = curl_slist_append(list, bearer.c_str());
-		curl_easy_setopt(curl, CURLOPT_URL, "https://graphql.anilist.co");
-		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
-		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
-		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata);
-		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlWriteCallback);
-		/* FIXME: This sucks. When using HTTPS, we should ALWAYS make sure that our peer
-		   is actually valid. I assume the best way to go about this would be to bundle a
-		   certificate file, and if it's not found we should *prompt the user* and ask them
-		   if it's okay to contact AniList WITHOUT verification. If so, we're golden, and this
-		   flag will be set. If not, we should abort mission.
-
-		   For this program, it's probably fine to just contact AniList without
-		   HTTPS verification. However it should still be in the list of things to do... */
-		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
-		res = curl_easy_perform(curl);
-		curl_slist_free_all(list);
-		if (res != CURLE_OK) {
-			QMessageBox box(QMessageBox::Icon::Critical, "", QString("curl_easy_perform(curl) failed!: ") + QString(curl_easy_strerror(res)));
-			box.exec();
-			curl_easy_cleanup(curl);
-			return "";
-		}
-		curl_easy_cleanup(curl);
-		return userdata;
-	}
-	return "";
-}
-
-int AniList::GetUserId(std::string name) {
-#define QUERY "query ($name: String) {\n" \
-			  "  User (name: $name) {\n" \
-			  "    id\n" \
-			  "  }\n" \
-			  "}\n"
-	nlohmann::json json = {
-		{"query", QUERY},
-		{"variables", {
-			{"name", name}
-		}}
-	};
-	auto ret = nlohmann::json::parse(SendRequest(json.dump()));
-	return ret["data"]["User"]["id"].get<int>();
-#undef QUERY
-}
-
-/* Maps to convert string forms to our internal enums */
-
-std::map<std::string, enum AnimeWatchingStatus> StringToAnimeWatchingMap = {
-	{"CURRENT",   CURRENT},
-	{"PLANNING",  PLANNING},
-	{"COMPLETED", COMPLETED},
-	{"DROPPED",   DROPPED},
-	{"PAUSED",    PAUSED},
-	{"REPEATING", REPEATING}
-};
-
-std::map<std::string, enum AnimeAiringStatus> StringToAnimeAiringMap = {
-	{"FINISHED",         FINISHED},
-	{"RELEASING",        RELEASING},
-	{"NOT_YET_RELEASED", NOT_YET_RELEASED},
-	{"CANCELLED",        CANCELLED},
-	{"HIATUS",           HIATUS}
-};
-
-std::map<std::string, enum AnimeSeason> StringToAnimeSeasonMap = {
-	{"WINTER", WINTER},
-	{"SPRING", SPRING},
-	{"SUMMER", SUMMER},
-	{"FALL",   FALL}
-};
-
-std::map<std::string, enum AnimeFormat> StringToAnimeFormatMap = {
-	{"TV",       TV},
-	{"TV_SHORT", TV_SHORT},
-	{"MOVIE",    MOVIE},
-	{"SPECIAL",  SPECIAL},
-	{"OVA",      OVA},
-	{"ONA",      ONA},
-	{"MUSIC",    MUSIC},
-	{"MANGA",    MANGA},
-	{"NOVEL",    NOVEL},
-	{"ONE_SHOT", ONE_SHOT}
-};
-
-int AniList::UpdateAnimeList(std::vector<AnimeList>* anime_lists, int id) {
-#define QUERY "query ($id: Int) {\n" \
-"  MediaListCollection (userId: $id, type: ANIME) {\n" \
-"    lists {\n" \
-"      name\n" \
-"      entries {\n" \
-"        score\n" \
-"        notes\n" \
-"        progress\n" \
-"        startedAt {\n" \
-"          year\n" \
-"          month\n" \
-"          day\n" \
-"        }\n" \
-"        completedAt {\n" \
-"          year\n" \
-"          month\n" \
-"          day\n" \
-"        }\n" \
-"        media {\n" \
-"          id\n" \
-"          title {\n" \
-"            userPreferred\n" \
-"          }\n" \
-"          format\n" \
-"          status\n" \
-"          averageScore\n" \
-"          season\n" \
-"          startDate {\n" \
-"            year\n" \
-"            month\n" \
-"            day\n" \
-"          }\n" \
-"          genres\n" \
-"          episodes\n" \
-"          duration\n" \
-"          synonyms\n" \
-"          description(asHtml: false)\n" \
-"        }\n" \
-"      }\n" \
-"    }\n" \
-"  }\n" \
-"}\n"
-	nlohmann::json json = {
-		{"query", QUERY},
-		{"variables", {
-			{"id", id}
-		}}
-	};
-	/* TODO: do a try catch here, catch any json errors and then call
-       Authorize() if needed */
-	auto res = nlohmann::json::parse(SendRequest(json.dump()));
-	/* TODO: make sure that we actually need the wstring converter and see
-	   if we can just get wide strings back from nlohmann::json */
-	for (const auto& list : res["data"]["MediaListCollection"]["lists"].items()) {
-		/* why are the .key() values strings?? */
-		int list_key = std::stoi(list.key());
-		AnimeList anime_list;
-		anime_list.name = StringUtils::Utf8ToWstr(list.value()["name"].get<std::string>());
-		for (const auto& entry : list.value()["entries"].items()) {
-			int entry_key = std::stoi(entry.key());
-			Anime anime;
-			anime.score = entry.value()["score"].get<int>();
-			anime.progress = entry.value()["progress"].get<int>();
-			if (entry.value()["status"].is_string())
-				anime.status = StringToAnimeWatchingMap[entry.value()["status"].get<std::string>()];
-			if (entry.value()["notes"].is_string())
-				anime.notes = StringUtils::Utf8ToWstr(entry.value()["notes"].get<std::string>());
-
-			if (ANILIST_DATE_IS_VALID(entry.value()["startedAt"]))
-				anime.started = ANILIST_DATE_TO_YMD(entry.value()["startedAt"]);
-			if (ANILIST_DATE_IS_VALID(entry.value()["completedAt"]))
-				anime.completed = ANILIST_DATE_TO_YMD(entry.value()["completedAt"]);
-
-			anime.title = StringUtils::Utf8ToWstr(entry.value()["media"]["title"]["userPreferred"].get<std::string>());
-			anime.id = entry.value()["media"]["id"].get<int>();
-			if (!entry.value()["media"]["episodes"].is_null())
-				anime.episodes = entry.value()["media"]["episodes"].get<int>();
-			else // hasn't aired yet
-				anime.episodes = 0;
-
-			if (!entry.value()["media"]["format"].is_null())
-				anime.type = StringToAnimeFormatMap[entry.value()["media"]["format"].get<std::string>()];
-
-			anime.airing = StringToAnimeAiringMap[entry.value()["media"]["status"].get<std::string>()];
-
-			if (ANILIST_DATE_IS_VALID(entry.value()["media"]["startDate"]))
-				anime.air_date = ANILIST_DATE_TO_YMD(entry.value()["media"]["startDate"]);
-
-			if (entry.value()["media"]["averageScore"].is_number())
-				anime.audience_score = entry.value()["media"]["averageScore"].get<int>();
-
-			if (entry.value()["media"]["season"].is_string())
-				anime.season = StringToAnimeSeasonMap[entry.value()["media"]["season"].get<std::string>()];
-
-			if (entry.value()["media"]["duration"].is_number())
-				anime.duration = entry.value()["media"]["duration"].get<int>();
-			else
-				anime.duration = 0;
-
-			if (entry.value()["media"]["genres"].is_array())
-				anime.genres = entry.value()["media"]["genres"].get<std::vector<std::string>>();
-			if (entry.value()["media"]["description"].is_string())
-				anime.synopsis = StringUtils::TextifySynopsis(StringUtils::Utf8ToWstr(entry.value()["media"]["description"].get<std::string>()));
-			anime_list.Add(anime);
-		}
-		anime_lists->push_back(anime_list);
-	}
-	return 1;
-}
-
-int AniList::Authorize() {
-	if (session.config.anilist.auth_token.empty()) {
-		/* Prompt for PIN */
-		QDesktopServices::openUrl(QUrl("https://anilist.co/api/v2/oauth/authorize?client_id=" CLIENT_ID "&response_type=token"));
-		bool ok;
-		QString token = QInputDialog::getText(0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal, "", &ok);
-		if (ok && !token.isEmpty()) {
-			session.config.anilist.auth_token = token.toStdString();
-		} else { // fail
-			return 0;
-		}
-	}
-	return 1;
-}
+#include "window.h"
+#include "json.h"
+#include <curl/curl.h>
+#include <chrono>
+#include <exception>
+#include <format>
+#include "anilist.h"
+#include "anime.h"
+#include "config.h"
+#include "string_utils.h"
+#define CLIENT_ID "13706"
+
+size_t AniList::CurlWriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) {
+    ((std::string*)userdata)->append((char*)contents, size * nmemb);
+    return size * nmemb;
+}
+
+std::string AniList::SendRequest(std::string data) {
+	struct curl_slist *list = NULL;
+	std::string userdata;
+	curl = curl_easy_init();
+	if (curl) {
+		list = curl_slist_append(list, "Accept: application/json");
+		list = curl_slist_append(list, "Content-Type: application/json");
+		std::string bearer = "Authorization: Bearer " + session.config.anilist.auth_token;
+		list = curl_slist_append(list, bearer.c_str());
+		curl_easy_setopt(curl, CURLOPT_URL, "https://graphql.anilist.co");
+		curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
+		curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
+		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata);
+		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlWriteCallback);
+		/* FIXME: This sucks. When using HTTPS, we should ALWAYS make sure that our peer
+		   is actually valid. I assume the best way to go about this would be to bundle a
+		   certificate file, and if it's not found we should *prompt the user* and ask them
+		   if it's okay to contact AniList WITHOUT verification. If so, we're golden, and this
+		   flag will be set. If not, we should abort mission.
+
+		   For this program, it's probably fine to just contact AniList without
+		   HTTPS verification. However it should still be in the list of things to do... */
+		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
+		res = curl_easy_perform(curl);
+		curl_slist_free_all(list);
+		if (res != CURLE_OK) {
+			QMessageBox box(QMessageBox::Icon::Critical, "", QString("curl_easy_perform(curl) failed!: ") + QString(curl_easy_strerror(res)));
+			box.exec();
+			curl_easy_cleanup(curl);
+			return "";
+		}
+		curl_easy_cleanup(curl);
+		return userdata;
+	}
+	return "";
+}
+
+int AniList::GetUserId(std::string name) {
+#define QUERY "query ($name: String) {\n" \
+			  "  User (name: $name) {\n" \
+			  "    id\n" \
+			  "  }\n" \
+			  "}\n"
+	nlohmann::json json = {
+		{"query", QUERY},
+		{"variables", {
+			{"name", name}
+		}}
+	};
+	auto ret = nlohmann::json::parse(SendRequest(json.dump()));
+	return ret["data"]["User"]["id"].get<int>();
+#undef QUERY
+}
+
+/* Maps to convert string forms to our internal enums */
+
+std::map<std::string, enum AnimeWatchingStatus> StringToAnimeWatchingMap = {
+	{"CURRENT",   CURRENT},
+	{"PLANNING",  PLANNING},
+	{"COMPLETED", COMPLETED},
+	{"DROPPED",   DROPPED},
+	{"PAUSED",    PAUSED},
+	{"REPEATING", REPEATING}
+};
+
+std::map<std::string, enum AnimeAiringStatus> StringToAnimeAiringMap = {
+	{"FINISHED",         FINISHED},
+	{"RELEASING",        RELEASING},
+	{"NOT_YET_RELEASED", NOT_YET_RELEASED},
+	{"CANCELLED",        CANCELLED},
+	{"HIATUS",           HIATUS}
+};
+
+std::map<std::string, enum AnimeSeason> StringToAnimeSeasonMap = {
+	{"WINTER", WINTER},
+	{"SPRING", SPRING},
+	{"SUMMER", SUMMER},
+	{"FALL",   FALL}
+};
+
+std::map<std::string, enum AnimeFormat> StringToAnimeFormatMap = {
+	{"TV",       TV},
+	{"TV_SHORT", TV_SHORT},
+	{"MOVIE",    MOVIE},
+	{"SPECIAL",  SPECIAL},
+	{"OVA",      OVA},
+	{"ONA",      ONA},
+	{"MUSIC",    MUSIC},
+	{"MANGA",    MANGA},
+	{"NOVEL",    NOVEL},
+	{"ONE_SHOT", ONE_SHOT}
+};
+
+int AniList::UpdateAnimeList(std::vector<AnimeList>* anime_lists, int id) {
+/* NOTE: these should be in the qrc file */
+#define QUERY "query ($id: Int) {\n" \
+"  MediaListCollection (userId: $id, type: ANIME) {\n" \
+"    lists {\n" \
+"      name\n" \
+"      entries {\n" \
+"        score\n" \
+"        notes\n" \
+"        progress\n" \
+"        startedAt {\n" \
+"          year\n" \
+"          month\n" \
+"          day\n" \
+"        }\n" \
+"        completedAt {\n" \
+"          year\n" \
+"          month\n" \
+"          day\n" \
+"        }\n" \
+"        updatedAt\n" \
+"        media {\n" \
+"          id\n" \
+"          title {\n" \
+"            romaji\n" \
+"            english\n" \
+"            native\n" \
+"          }\n" \
+"          format\n" \
+"          status\n" \
+"          averageScore\n" \
+"          season\n" \
+"          startDate {\n" \
+"            year\n" \
+"            month\n" \
+"            day\n" \
+"          }\n" \
+"          genres\n" \
+"          episodes\n" \
+"          duration\n" \
+"          synonyms\n" \
+"          description(asHtml: false)\n" \
+"        }\n" \
+"      }\n" \
+"    }\n" \
+"  }\n" \
+"}\n"
+	nlohmann::json json = {
+		{"query", QUERY},
+		{"variables", {
+			{"id", id}
+		}}
+	};
+	/* TODO: do a try catch here, catch any json errors and then call
+       Authorize() if needed */
+	auto res = nlohmann::json::parse(SendRequest(json.dump()));
+	/* TODO: make sure that we actually need the wstring converter and see
+	   if we can just get wide strings back from nlohmann::json */
+	for (const auto& list : res["data"]["MediaListCollection"]["lists"].items()) {
+		/* why are the .key() values strings?? */
+		int list_key = std::stoi(list.key());
+		AnimeList anime_list;
+		anime_list.name = StringUtils::Utf8ToWstr(JSON::GetString(list.value(), "name"));
+		for (const auto& entry : list.value()["entries"].items()) {
+			int entry_key = std::stoi(entry.key());
+			Anime anime;
+			anime.score = JSON::GetInt(entry.value(), "score");
+			anime.progress = JSON::GetInt(entry.value(), "progress");
+			anime.status = StringToAnimeWatchingMap[JSON::GetString(entry.value(), "status")];
+			anime.notes = StringUtils::Utf8ToWstr(JSON::GetString(entry.value(), "notes"));
+
+			anime.started.SetYear(JSON::GetInt(entry.value()["startedAt"], "year"));
+			anime.started.SetMonth(JSON::GetInt(entry.value()["startedAt"], "month"));
+			anime.started.SetDay(JSON::GetInt(entry.value()["startedAt"], "day"));
+
+			anime.completed.SetYear(JSON::GetInt(entry.value()["completedAt"], "year"));
+			anime.completed.SetMonth(JSON::GetInt(entry.value()["completedAt"], "month"));
+			anime.completed.SetDay(JSON::GetInt(entry.value()["completedAt"], "day"));
+
+			anime.updated = JSON::GetInt(entry.value(), "updatedAt");
+
+			anime.title.native  = StringUtils::Utf8ToWstr(JSON::GetString(entry.value()["media"]["title"], "native"));
+			anime.title.english = StringUtils::Utf8ToWstr(JSON::GetString(entry.value()["media"]["title"], "english"));
+			anime.title.romaji  = StringUtils::Utf8ToWstr(JSON::GetString(entry.value()["media"]["title"], "romaji"));
+
+			anime.id = JSON::GetInt(entry.value()["media"], "id");
+			anime.episodes = JSON::GetInt(entry.value()["media"], "episodes");
+			anime.type = StringToAnimeFormatMap[JSON::GetString(entry.value()["media"], "format")];
+
+			anime.airing = StringToAnimeAiringMap[JSON::GetString(entry.value()["media"], "status")];
+
+			anime.air_date.SetYear(JSON::GetInt(entry.value()["media"]["startDate"], "year"));
+			anime.air_date.SetMonth(JSON::GetInt(entry.value()["media"]["startDate"], "month"));
+			anime.air_date.SetDay(JSON::GetInt(entry.value()["media"]["startDate"], "day"));
+
+			anime.audience_score = JSON::GetInt(entry.value()["media"], "averageScore");
+			anime.season = StringToAnimeSeasonMap[JSON::GetString(entry.value()["media"], "season")];
+			anime.duration = JSON::GetInt(entry.value()["media"], "duration");
+			anime.synopsis = StringUtils::TextifySynopsis(StringUtils::Utf8ToWstr(JSON::GetString(entry.value()["media"], "duration")));
+
+			if (entry.value()["media"]["genres"].is_array())
+				anime.genres = entry.value()["media"]["genres"].get<std::vector<std::string>>();
+			anime_list.Add(anime);
+		}
+		anime_lists->push_back(anime_list);
+	}
+	return 1;
+#undef QUERY
+}
+
+int AniList::Authorize() {
+	if (session.config.anilist.auth_token.empty()) {
+		/* Prompt for PIN */
+		QDesktopServices::openUrl(QUrl("https://anilist.co/api/v2/oauth/authorize?client_id=" CLIENT_ID "&response_type=token"));
+		bool ok;
+		QString token = QInputDialog::getText(0, "Credentials needed!", "Please enter the code given to you after logging in to AniList:", QLineEdit::Normal, "", &ok);
+		if (ok && !token.isEmpty()) {
+			session.config.anilist.auth_token = token.toStdString();
+		} else { // fail
+			return 0;
+		}
+	}
+	return 1;
+}