Can now import anime from Anilist by ID in Custom list
This commit is contained in:
parent
605e4c763a
commit
b0e32b6717
8 changed files with 156 additions and 37 deletions
0
src/httplib.h → external/httplib.h
vendored
0
src/httplib.h → external/httplib.h
vendored
37
src/AddAnimeWindow.slint
Normal file
37
src/AddAnimeWindow.slint
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { State } from "./state.slint";
|
||||
import { VText, VTextInput , VButton, ToggleButton, VActionButton, Svg, Palette } from "@selenite";
|
||||
import { ComboBox } from "std-widgets.slint";
|
||||
|
||||
export component AddAnimeWindow inherits Window {
|
||||
title: "Lali - Add List";
|
||||
|
||||
background: Palette.background;
|
||||
default-font-size: 16px;
|
||||
padding: 0px;
|
||||
|
||||
width: 200px;
|
||||
|
||||
private property <bool> show-add-list-form: false;
|
||||
in-out property <string> anilist-anime-id;
|
||||
|
||||
callback import-anilist-anime(string);
|
||||
|
||||
VerticalLayout {
|
||||
alignment: start;
|
||||
padding: 16px;
|
||||
spacing: 16px;
|
||||
source := ComboBox {
|
||||
model: ["Anilist", "Custom"];
|
||||
}
|
||||
|
||||
if source.current-value == "Anilist" : VTextInput {
|
||||
label: "Anilist anime ID";
|
||||
text <=> anilist-anime-id;
|
||||
}
|
||||
|
||||
VButton {
|
||||
text: "Import";
|
||||
clicked => { import-anilist-anime(anilist-anime-id) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,6 +62,46 @@ public:
|
|||
return std::get<rei::json::JsonObject>(rei::json::parse(res->body));
|
||||
}
|
||||
|
||||
static std::optional<rei::json::JsonObject> fetchAnimeById(int id) {
|
||||
httplib::Client cli("https://graphql.anilist.co");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
rei::json::JsonObject body{};
|
||||
rei::json::JsonObject variables{};
|
||||
const std::string graphqlQuery = "query Query($mediaId: Int) {\\n Media(id: $mediaId) {\\n id\\n title {\\n romaji\\n }\\n description\\n episodes\\n genres\\n status\\n startDate {\\n year\\n month\\n day\\n }\\n endDate {\\n year\\n month\\n day\\n }\\n coverImage {\\n extraLarge\\n }\\n idMal\\n isAdult\\n siteUrl\\n bannerImage\\n }\\n}";
|
||||
|
||||
variables.set("mediaId", id);
|
||||
body.set("query", graphqlQuery).addObject("variables", variables);
|
||||
auto res = cli.Post("/", rei::json::toString(body), "application/json");
|
||||
|
||||
if (res.error() != httplib::Error::Success) {
|
||||
std::cerr << res.error() << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!res->has_header("Content-Type")) {
|
||||
std::cerr << "Missing 'Content-Type'" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const std::string contentType = res->get_header_value("Content-Type");
|
||||
if (!contentType.starts_with("application/json")) {
|
||||
std::cerr << "Wrong content type" << " (" << contentType << ")" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (res.error() != httplib::Error::Success) {
|
||||
std::cerr << res.error() << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (res->status != 200) {
|
||||
std::println("Status: {}\nBody: {}", res->status, res->body);
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::get<rei::json::JsonObject>(rei::json::parse(res->body));
|
||||
}
|
||||
|
||||
static std::optional<std::string> fetchImage(const std::string& path) {
|
||||
httplib::Client cli("https://s4.anilist.co");
|
||||
cli.set_follow_location(true);
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include "rei-json/Field.h"
|
||||
#include "rei-json/Object.h"
|
||||
#include "ui.h"
|
||||
#include "rei-json/Array.h"
|
||||
#include "rei-json/json.h"
|
||||
|
@ -106,6 +108,7 @@ public:
|
|||
|
||||
animesSlint = std::make_shared<slint::VectorModel<ui::Anime>>();
|
||||
ui->global<ui::State>().set_current_list({
|
||||
.index = 0,
|
||||
.name = "ok",
|
||||
.animes = animesSlint
|
||||
});
|
||||
|
@ -137,31 +140,25 @@ public:
|
|||
addLocalList(std::string(params.name));
|
||||
});
|
||||
|
||||
ui->global<ui::State>().on_sync_list([&](slint::SharedString name) {
|
||||
//std::println("{}",res->body);
|
||||
try {
|
||||
//addAnilistList("defaut", name, "Completed");
|
||||
|
||||
ui->global<ui::State>().on_open_add_anime_window([&]() {
|
||||
addAnimeWindow->show();
|
||||
});
|
||||
|
||||
/*std::println("0000");*/
|
||||
/*selectList(saveJson.getArray("lists").getObject(0).getArray("animes"), animesSlint);*/
|
||||
/*std::println("1111");*/
|
||||
/*imgsToLoad.clear();*/
|
||||
/*std::println("2222");*/
|
||||
/*saveJson.getArray("lists").getObject(0).getArray("animes").forEach([&] (rei::json::FieldValue& element, int index) {*/
|
||||
/*std::println("IN");*/
|
||||
/*imgsToLoad.push_back(element.asObject().getObject("coverImage").getString("extraLarge"));*/
|
||||
/*std::println("OUT");*/
|
||||
/*});*/
|
||||
/*std::println("3333");*/
|
||||
/*readLinks.unlock();*/
|
||||
|
||||
} catch (std::exception& e) {
|
||||
std::println(std::cerr, "Error: {}", e.what());
|
||||
} catch (char const* e) {
|
||||
std::println(std::cerr, "Error str: {}", e);
|
||||
}
|
||||
addAnimeWindow->on_import_anilist_anime([&] (slint::SharedString animeIdStr) {
|
||||
int animeId = stoi(std::string(animeIdStr));
|
||||
int currentListIndex = ui->global<ui::State>().get_current_list().index;
|
||||
addAnimeWindow->hide();
|
||||
addAnimeWindow->set_anilist_anime_id("");
|
||||
addAnilistAnime(currentListIndex, animeId);
|
||||
});
|
||||
|
||||
ui->global<ui::State>().on_sync_list([&]() {
|
||||
int currentListIndex = ui->global<ui::State>().get_current_list().index;
|
||||
auto& listJson = json.getArray("lists").getObject(currentListIndex);
|
||||
const std::string listName = listJson.getString("name");
|
||||
const std::string anilistUserName = listJson.getString("anilistUserName");
|
||||
const std::string anilistListName = listJson.getString("anilistListName");
|
||||
addAnilistList(listName, anilistUserName, anilistListName);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -201,6 +198,8 @@ public:
|
|||
}
|
||||
|
||||
ui->global<ui::State>().set_current_list({
|
||||
.index = listIndex,
|
||||
.source = list.getString("source") == "anilist" ? ui::ListSource::Anilist : ui::ListSource::Local,
|
||||
.name = slint::SharedString(list.getString("name")),
|
||||
.animes = animesSlint
|
||||
});
|
||||
|
@ -215,9 +214,6 @@ public:
|
|||
}
|
||||
auto anilistJson = anilistJsonResult.value();
|
||||
|
||||
rei::json::JsonObject saveJson{};
|
||||
saveJson.set("version", 1);
|
||||
|
||||
rei::json::JsonObject listJson{};
|
||||
anilistJson.getObject("data").getObject("MediaListCollection").getArray("lists")
|
||||
.forEach([&](rei::json::FieldValue& field, int index) {
|
||||
|
@ -238,19 +234,27 @@ public:
|
|||
|
||||
listJson.addArray("animes", animesJson);
|
||||
});
|
||||
json.getArray("lists").pushObject(listJson);
|
||||
listsSlint->push_back(ui::List{
|
||||
.name = slint::SharedString(listName),
|
||||
.selected = false
|
||||
std::optional<int> existingListIndex;
|
||||
json.getArray("lists").forEach([&](rei::json::FieldValue& value, int index) {
|
||||
if (value.asObject().getString("name") == listName) {
|
||||
existingListIndex = index;
|
||||
}
|
||||
});
|
||||
if (existingListIndex.has_value()) {
|
||||
json.getArray("lists").getObject(existingListIndex.value()) = listJson;
|
||||
selectList(existingListIndex.value());
|
||||
} else {
|
||||
json.getArray("lists").pushObject(listJson);
|
||||
listsSlint->push_back(ui::List{
|
||||
.name = slint::SharedString(listName),
|
||||
.selected = false
|
||||
});
|
||||
}
|
||||
save();
|
||||
}
|
||||
|
||||
void addLocalList(const std::string& listName) {
|
||||
|
||||
rei::json::JsonObject saveJson{};
|
||||
saveJson.set("version", 1);
|
||||
|
||||
rei::json::JsonObject listJson{};
|
||||
listJson.set("name", listName);
|
||||
listJson.set("source", "local");
|
||||
|
@ -265,6 +269,20 @@ public:
|
|||
save();
|
||||
}
|
||||
|
||||
void addAnilistAnime(int currentListIndex, int animeId) {
|
||||
auto res = Anilist::fetchAnimeById(animeId);
|
||||
if (!res.has_value()) {
|
||||
std::println(std::cerr, "Cannot fetch anime id {}", animeId);
|
||||
return;
|
||||
}
|
||||
std::println("{}", rei::json::toString(res.value()));
|
||||
|
||||
rei::json::JsonArray& listJson = json.getArray("lists").getObject(currentListIndex).getArray("animes");
|
||||
listJson.pushObject(res.value().getObject("data").getObject("Media"));
|
||||
selectList(currentListIndex);
|
||||
save();
|
||||
}
|
||||
|
||||
slint::Image getAnimeImage(int animeId, std::string imageUrl) {
|
||||
//std::println("Get image for anime {}", animeId);
|
||||
std::string imagePath = "./imgs/" + std::to_string(animeId);
|
||||
|
@ -322,6 +340,7 @@ private:
|
|||
|
||||
rei::json::JsonObject json;
|
||||
slint::ComponentHandle<ui::AppWindow> ui;
|
||||
slint::ComponentHandle<ui::AddAnimeWindow> addAnimeWindow = ui::AddAnimeWindow::create();
|
||||
std::shared_ptr<slint::VectorModel<ui::Anime>> animesSlint;
|
||||
std::shared_ptr<slint::VectorModel<ui::List>> listsSlint;
|
||||
std::mutex animeListLock;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { State } from "./state.slint";
|
||||
import { State, ListSource } from "./state.slint";
|
||||
import { VText, VTextInput , VButton, ToggleButton, VActionButton, Svg, Palette } from "@selenite";
|
||||
import { ComboBox } from "std-widgets.slint";
|
||||
|
||||
|
@ -117,6 +117,19 @@ export component AppWindow inherits Window {
|
|||
text: "\{State.current-list.animes.length} animes";
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
alignment: start;
|
||||
if State.current-list.source == ListSource.Local : VButton {
|
||||
icon-svg: Svg.plus;
|
||||
text: "Add";
|
||||
clicked => { State.open-add-anime-window() }
|
||||
}
|
||||
if State.current-list.source == ListSource.Anilist : VButton {
|
||||
text: "Sync";
|
||||
clicked => { State.sync-list() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
animes-grid-background := Rectangle {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <print>
|
||||
#include <print>
|
||||
|
||||
struct Config {
|
||||
std::string timerEndingScript = "";
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
export enum ListSource {
|
||||
Local,
|
||||
Anilist
|
||||
}
|
||||
|
||||
export struct Anime {
|
||||
id: int,
|
||||
title: string,
|
||||
|
@ -12,6 +17,8 @@ export struct List {
|
|||
}
|
||||
|
||||
export struct CurrentList {
|
||||
index: int,
|
||||
source: ListSource,
|
||||
name: string,
|
||||
animes: [Anime]
|
||||
}
|
||||
|
@ -32,9 +39,11 @@ export global State {
|
|||
in-out property <CurrentList> current-list;
|
||||
|
||||
callback select-list(int);
|
||||
callback sync-list(string);
|
||||
callback sync-list();
|
||||
callback add-anilist-list(AddAnilistListParams);
|
||||
callback add-local-list(AddLocalListParams);
|
||||
|
||||
callback open-add-anime-window();
|
||||
|
||||
callback config-changed();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { AppWindow } from "./AppWindow.slint";
|
||||
import { AddAnimeWindow } from "./AddAnimeWindow.slint";
|
||||
import { State } from "./state.slint";
|
||||
|
||||
export { State, AppWindow }
|
||||
export { State, AppWindow, AddAnimeWindow }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue