diff --git a/core/interfaces/i_story_db.h b/core/interfaces/i_story_db.h new file mode 100644 index 0000000..7a77bd5 --- /dev/null +++ b/core/interfaces/i_story_db.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class IStoryDb +{ + +public: + struct Info { + int age; + std::string title; + std::string description; + std::string download; + std::string image_url; + std::string sound; + std::string uuid; + std::string url; + }; + + virtual ~IStoryDb() {} + + + virtual bool FindStory(const std::string &uuid, Info &info) = 0; + virtual void AddStory(IStoryDb::Info &info, int origin) = 0; +}; diff --git a/core/lib/sys_lib.cpp b/core/lib/sys_lib.cpp index fd5d678..60b9a97 100644 --- a/core/lib/sys_lib.cpp +++ b/core/lib/sys_lib.cpp @@ -43,7 +43,7 @@ std::string SysLib::GetFileName(const std::string &path) } else { - return ""; + return path; } } else diff --git a/core/src/story_project.h b/core/src/story_project.h index 1082dd8..5d601a8 100644 --- a/core/src/story_project.h +++ b/core/src/story_project.h @@ -15,7 +15,7 @@ #include "story_page.h" #include "story_options.h" -// FIXME : Structure très Lunii style, à utiliser pour la conversion peut-être ... +// FIXME : Structure très proche de la boiboite, à utiliser pour la conversion peut-être ... struct StoryNode { bool auto_jump; diff --git a/docs/intro-getting-started.md b/docs/intro-getting-started.md index c5cd0cc..61ce639 100644 --- a/docs/intro-getting-started.md +++ b/docs/intro-getting-started.md @@ -7,7 +7,7 @@ This documentation will guide you to make your own story teller. You can choose Before starting, here is a complete list of links where you'll find project information: - :octopus: [Github](https://github.com/arabine/open-story-teller): source code for the firmware, the player and the story editor, issue tracking -- Free stories community (in French) [Discord](https://monurl.ca/DiscordLuniiYT) +- Free stories community (in French) [Discord](https://monurl.ca/lunii.creations) - My Discord channel for this project: https://discord.gg/4TW4B3R4Ye ## Helping diff --git a/shared/downloader.cpp b/shared/downloader.cpp new file mode 100644 index 0000000..29b4686 --- /dev/null +++ b/shared/downloader.cpp @@ -0,0 +1,120 @@ + +#include "downloader.h" +#include "json.hpp" + +static size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp) +{ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} + +Downloader::Downloader() +{ + curl_global_init(CURL_GLOBAL_DEFAULT); + + // Try to download the store index file + m_curl = curl_easy_init(); + + m_downloadThread = std::thread( std::bind(&Downloader::DownloadThread, this) ); +} + +Downloader::~Downloader() +{ + if (m_curl) + { + m_downloadMutex.lock(); + m_cancel = true; + m_downloadMutex.unlock(); + } + + // Quit download thread + m_downloadQueue.push({"quit", ""}); + if (m_downloadThread.joinable()) + { + m_downloadThread.join(); + } + + curl_global_cleanup(); +} + +void Downloader::DownloadThread() +{ + for (;;) + { + auto cmd = m_downloadQueue.front(); + m_downloadQueue.pop(); + + if (cmd.order == "quit") + { + curl_easy_cleanup(m_curl); + + return; + } + else if (cmd.order == "dl") + { + // download_file(m_curl, cmd.url, cmd.filename, cmd.finished_callback); + } + } +} + +std::string Downloader::FetchToken() +{ + std::string url = "https://server-auth-prod.lunii.com/guest/create"; + std::string response = PerformGetRequest(url); + + // Parse the response JSON to extract the token + try { + auto jsonResponse = nlohmann::json::parse(response); + return jsonResponse["response"]["token"]["server"].get(); + } catch (const std::exception& ex) { + throw std::runtime_error("Failed to parse token from response: " + std::string(ex.what())); + } +} + +void Downloader::FetchDataAndSaveToFile(const std::string& token, const std::string& filePath) +{ + std::string url = "https://server-data-prod.lunii.com/v2/packs"; + + struct curl_slist* headers = nullptr; + headers = curl_slist_append(headers, ("X-AUTH-TOKEN: " + token).c_str()); + + std::string response = PerformGetRequest(url, headers); + + // Enregistrer dans un fichier + std::ofstream file(filePath); + if (!file.is_open()) { + throw std::runtime_error("Failed to open file for writing"); + } + file << response; + file.close(); + std::cout << "Data saved to " << filePath << std::endl; +} + +std::string Downloader::PerformGetRequest(const std::string& url, struct curl_slist* headers) +{ + + std::string response; + curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &response); + + if (headers) + { + curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers); + } + + CURLcode res = curl_easy_perform(m_curl); + if (res != CURLE_OK) + { + // FIXME: handle error + } + + if (headers) + { + curl_slist_free_all(headers); + } + + return response; +} + + diff --git a/shared/downloader.h b/shared/downloader.h new file mode 100644 index 0000000..fa54f86 --- /dev/null +++ b/shared/downloader.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "thread_safe_queue.h" + + +class Downloader +{ +public: + + struct Command { + std::string order; + std::string url; + std::string filename; + std::function finished_callback; + }; + + Downloader(); + ~Downloader(); + + std::string FetchToken(); + + void FetchDataAndSaveToFile(const std::string& token, const std::string& filePath); + +private: + CURL *m_curl; + std::thread m_downloadThread; + ThreadSafeQueue m_downloadQueue; + std::mutex m_downloadMutex; + bool m_cancel{false}; + + std::string PerformGetRequest(const std::string& url, struct curl_slist* headers = nullptr); + void DownloadThread(); +}; \ No newline at end of file diff --git a/shared/library_manager.cpp b/shared/library_manager.cpp index 54a21c2..e975889 100644 --- a/shared/library_manager.cpp +++ b/shared/library_manager.cpp @@ -193,3 +193,81 @@ std::string LibraryManager::GetVersion() { return std::to_string(VERSION_MAJOR) + '.' + std::to_string(VERSION_MINOR) + '.' + std::to_string(VERSION_PATCH); } + +void LibraryManager::AddStory(IStoryDb::Info &info, int origin) +{ + m_storyDb.AddStory(info, origin); +} + +void LibraryManager::ParseCommunityStore(const std::string &jsonFileName) +{ + try { + std::ifstream f(jsonFileName); + nlohmann::json j = nlohmann::json::parse(f); + + if (!j.contains("data")) { + throw std::runtime_error("Invalid JSON: 'data' key not found"); + } + + const auto& data = j["data"]; + + m_storyDb.ClearCommunity(); + for (const auto &obj : data) + { + IStoryDb::Info s; + + s.title = obj["title"].get(); + s.description = obj["description"].get(); + s.download = obj["download"].get(); + s.age = obj["age"].get(); + + m_storyDb.AddStory(s, StoryDb::cCommunityStore); + } + } + catch(std::exception &e) + { + std::cout << e.what() << std::endl; + } +} + +void LibraryManager::ParseCommercialStore(const std::string &jsonFileName) +{ + try + { + + std::ifstream f(jsonFileName); + nlohmann::json j = nlohmann::json::parse(f); + + if (!j.contains("response")) { + throw std::runtime_error("Invalid JSON: 'response' key not found"); + } + + const auto& response = j["response"]; + m_storyDb.ClearCommercial(); + + for (auto it = response.begin(); it != response.end(); ++it) + { + const auto& pack = it.value(); + + IStoryDb::Info story; + story.title = pack["title"].get(); + story.uuid = pack["uuid"].get(); + + if (pack.contains("localized_infos") && pack["localized_infos"].contains("fr_FR")) + { + const auto& localized = pack["localized_infos"]["fr_FR"]; + story.title = localized["title"].get(); + story.description = localized["description"].get(); + + if (localized.contains("image") && localized["image"].contains("image_url")) { + story.image_url = localized["image"]["image_url"].get(); + } + } + + m_storyDb.AddStory(story, StoryDb::cCommercialStore); + } + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << std::endl; + } +} + diff --git a/shared/library_manager.h b/shared/library_manager.h index 9e7a82a..b6e31ba 100644 --- a/shared/library_manager.h +++ b/shared/library_manager.h @@ -8,6 +8,8 @@ #include "story_project.h" #include "i_logger.h" +#include "story_db.h" + class LibraryManager { public: @@ -42,12 +44,26 @@ public: void SetStoreUrl(const std::string &store_url) { m_storeUrl = store_url; } std::string GetStoreUrl() const { return m_storeUrl; } + void AddStory(IStoryDb::Info &info, int origin); + + void ParseCommunityStore(const std::string &jsonFileName); + void ParseCommercialStore(const std::string &jsonFileName); + + auto CommunityDbView() const { + return m_storyDb.CommunityDbView(); + } + + auto CommercialDbView() const { + return m_storyDb.CommercialDbView(); + } + private: ILogger &m_log; std::string m_library_path; std::list> m_projectsList; std::string m_storeUrl; std::thread m_copyWorker; + StoryDb m_storyDb; }; #endif // LIBRARYMANAGER_H diff --git a/shared/story_db.cpp b/shared/story_db.cpp new file mode 100644 index 0000000..b2d5147 --- /dev/null +++ b/shared/story_db.cpp @@ -0,0 +1,46 @@ +#include "story_db.h" + +StoryDb::StoryDb() +{ +} + +StoryDb::~StoryDb() +{ +} + +bool StoryDb::FindStory(const std::string &uuid, Info &info) +{ + for (const auto &s : m_store) + { + if (s.uuid == uuid) + { + info = s; + return true; + } + } + + for (const auto &s : m_commercialStore) + { + if (s.uuid == uuid) + { + info = s; + return true; + } + } + + return false; +} + +void StoryDb::AddStory(IStoryDb::Info &info, int origin) +{ + if (origin == cCommercialStore) + { + m_commercialStore.push_back(info); + } + + if (origin == cCommunityStore) + { + m_store.push_back(info); + } + +} diff --git a/shared/story_db.h b/shared/story_db.h new file mode 100644 index 0000000..4a598f2 --- /dev/null +++ b/shared/story_db.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include "i_story_db.h" + +class StoryDb : public IStoryDb +{ +public: + static const int cCommercialStore = 0; + static const int cCommunityStore = 1; + + StoryDb(); + ~StoryDb(); + + bool FindStory(const std::string &uuid, Info &info) override; + void AddStory(IStoryDb::Info &info, int origin) override; + + void Clear() { + m_store.clear(); + m_commercialStore.clear(); + } + + void ClearCommercial() { + m_commercialStore.clear(); + } + + void ClearCommunity() { + m_store.clear(); + } + + auto CommercialDbView() const { + return std::ranges::views::all(m_commercialStore); + } + + auto CommunityDbView() const { + return std::ranges::views::all(m_store); + } + +private: + std::vector m_store; + std::vector m_commercialStore; +}; diff --git a/story-editor/CMakeLists.txt b/story-editor/CMakeLists.txt index 9d08437..ba6843e 100644 --- a/story-editor/CMakeLists.txt +++ b/story-editor/CMakeLists.txt @@ -182,51 +182,26 @@ set(SRCS src/main.cpp - src/window_base.h src/window_base.cpp - src/console_window.cpp - src/console_window.h - src/emulator_window.cpp - src/emulator_window.h - src/main_window.cpp - src/main_window.h - src/library_window.cpp - src/library_window.h - - src/platform_folders.cpp - src/platform_folders.h - src/node_editor/media_node_widget.h + src/node_editor/media_node_widget.cpp - src/node_editor/base_node_widget.h src/node_editor/base_node_widget.cpp - src/node_editor/node_editor_window.h src/node_editor/node_editor_window.cpp - src/node_editor/function_node_widget.h src/node_editor/function_node_widget.cpp src/resources_window.cpp - src/resources_window.h - src/properties_window.cpp - src/properties_window.h - src/cpu_window.cpp - src/cpu_window.h - - src/gui.h src/gui.cpp src/code_editor.cpp - src/code_editor.h - src/media_converter.cpp - src/media_converter.h src/miniz.c src/zip.cpp @@ -234,7 +209,6 @@ set(SRCS src/importers/pack_archive.cpp src/importers/ni_parser.c - libs/imgui-node-editor/imgui_node_editor.cpp libs/imgui-node-editor/imgui_canvas.cpp libs/imgui-node-editor/imgui_node_editor_api.cpp @@ -253,17 +227,11 @@ set(SRCS ../firmware/chip32/chip32_vm.c ../shared/audio_player.cpp - ../shared/audio_player.h - ../shared/tlv.h - ../shared/miniaudio.h ../shared/stb_vorbis.c - ../shared/uuid.h - ../shared/resource_manager.h ../shared/resource_manager.cpp - - ../shared/thread_safe_queue.h - ../shared/library_manager.h ../shared/library_manager.cpp + ../shared/downloader.cpp + ../shared/story_db.cpp # Core engine files diff --git a/story-editor/imgui.ini b/story-editor/imgui.ini index c8a1547..78b956e 100644 --- a/story-editor/imgui.ini +++ b/story-editor/imgui.ini @@ -9,20 +9,20 @@ Size=32,42 Collapsed=0 [Window][Library Manager] -Pos=466,26 -Size=814,262 +Pos=593,26 +Size=687,395 Collapsed=0 DockId=0x00000003,3 [Window][Console] -Pos=60,623 -Size=1220,97 +Pos=60,423 +Size=1220,297 Collapsed=0 DockId=0x00000006,0 [Window][Emulator] -Pos=466,26 -Size=814,262 +Pos=593,26 +Size=687,395 Collapsed=0 DockId=0x00000003,2 @@ -33,20 +33,20 @@ Collapsed=0 DockId=0x00000004,0 [Window][Resources] -Pos=466,26 -Size=814,262 +Pos=593,26 +Size=687,395 Collapsed=0 DockId=0x00000003,0 [Window][Properties] -Pos=466,26 -Size=814,262 +Pos=593,26 +Size=687,395 Collapsed=0 DockId=0x00000003,1 [Window][Node editor] Pos=60,26 -Size=404,595 +Size=531,395 Collapsed=0 DockId=0x00000002,0 @@ -61,12 +61,12 @@ Size=60,694 Collapsed=0 [Window][ProjectPropertiesPopup] -Pos=381,236 +Pos=381,290 Size=518,248 Collapsed=0 [Window][AboutPopup] -Pos=540,272 +Pos=436,235 Size=408,249 Collapsed=0 @@ -86,8 +86,8 @@ Size=951,564 Collapsed=0 [Window][CPU] -Pos=466,26 -Size=814,262 +Pos=593,26 +Size=687,395 Collapsed=0 DockId=0x00000003,4 @@ -106,6 +106,17 @@ Pos=60,26 Size=1220,694 Collapsed=0 +[Window][Code viewer] +Pos=593,26 +Size=687,395 +Collapsed=0 +DockId=0x00000003,5 + +[Window][Import story##ImportStoryDlgKey] +Pos=246,21 +Size=916,532 +Collapsed=0 + [Table][0x54B1A511,5] RefScale=20 Column 0 Width=197 Sort=0v @@ -187,12 +198,31 @@ RefScale=20 Column 0 Width=22 Sort=0v Column 1 Width=96 +[Table][0x37CE7681,4] +RefScale=20 +Column 0 Width=468 Sort=0v +Column 1 Width=273 +Column 2 Width=47 +Column 3 Width=93 + +[Table][0xACC2C3FC,4] +RefScale=20 +Column 0 Sort=0v + +[Table][0xE79886F3,4] +RefScale=20 +Column 0 Sort=0v + +[Table][0xD33987D9,4] +RefScale=20 +Column 0 Sort=0v + [Docking][Data] DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=60,26 Size=1220,694 Split=Y - DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=1220,595 Split=X - DockNode ID=0x00000002 Parent=0x00000005 SizeRef=574,694 CentralNode=1 Selected=0xBB79A587 - DockNode ID=0x00000001 Parent=0x00000005 SizeRef=814,694 Split=Y Selected=0x63869CAF - DockNode ID=0x00000003 Parent=0x00000001 SizeRef=543,294 Selected=0x4B07C626 + DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=1220,504 Split=X + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=531,694 CentralNode=1 Selected=0xBB79A587 + DockNode ID=0x00000001 Parent=0x00000005 SizeRef=687,694 Split=Y Selected=0x63869CAF + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=543,294 Selected=0x63869CAF DockNode ID=0x00000004 Parent=0x00000001 SizeRef=543,372 Selected=0x7563A968 - DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1220,97 Selected=0xEA83D666 + DockNode ID=0x00000006 Parent=0x08BD597D SizeRef=1220,297 Selected=0xEA83D666 diff --git a/story-editor/src/importers/pack_archive.cpp b/story-editor/src/importers/pack_archive.cpp index 65d607a..6347cbb 100644 --- a/story-editor/src/importers/pack_archive.cpp +++ b/story-editor/src/importers/pack_archive.cpp @@ -202,10 +202,19 @@ $MyArray DV8 10 ; array of 10 bytes */ - // NI file is not ciphered + // RI file is not ciphered uint8_t data[512]; uint32_t size = ni_get_ri_block(data); - WriteDataOnDisk(mPackName + "/ri", data, size); + // WriteDataOnDisk(mPackName + "/ri", data, size); + + StoryProject proj(m_log); + ResourceManager res(m_log); + + std::shared_ptr page = proj.CreatePage("main"); + + + proj.New(uuid, outputDir); + proj.SetName(j["title"].get()); nlohmann::json jsonImages; { @@ -224,7 +233,7 @@ $MyArray DV8 10 ; array of 10 bytes j["images"] = jsonImages; size = ni_get_si_block(data); - WriteDataOnDisk(mPackName + "/si", data, size); + // WriteDataOnDisk(mPackName + "/si", data, size); nlohmann::json jsonSounds; @@ -244,7 +253,7 @@ $MyArray DV8 10 ; array of 10 bytes j["sounds"] = jsonSounds; size = ni_get_li_block(data); - WriteDataOnDisk(mPackName + "/li", data, size); + // WriteDataOnDisk(mPackName + "/li", data, size); std::vector transitions; @@ -333,11 +342,7 @@ $MyArray DV8 10 ; array of 10 bytes j["name"] = ""; j["type"] = "lunii"; - std::ofstream o("pack.json"); - o << std::setw(4) << j << std::endl; // pretty print - o.close(); - chip32.close(); } bool PackArchive::LoadNiFile(const std::string &filePath) diff --git a/story-editor/src/library_window.cpp b/story-editor/src/library_window.cpp index d86b588..c1cee1c 100644 --- a/story-editor/src/library_window.cpp +++ b/story-editor/src/library_window.cpp @@ -59,7 +59,10 @@ LibraryWindow::LibraryWindow(IStoryManager &project, LibraryManager &library) m_storeUrl[0] = 0; - // std::strcpy(m_storeUrl, "https://gist.githubusercontent.com/DantSu/8920929530b58f2acbfcf1ed0e7746f9/raw/stories-contrib.json"); + // std::strcpy(m_storeUrl, "https://gist.githubusercontent.com/DantSu/3aea4c1fe15070bcf394a40b89aec33e/raw/stories.json"); + + // https://gist.githubusercontent.com/DantSu/3aea4c1fe15070bcf394a40b89aec33e/raw/stories.json + } LibraryWindow::~LibraryWindow() @@ -105,29 +108,49 @@ void LibraryWindow::Initialize() curl_easy_setopt(m_curl, CURLOPT_XFERINFOFUNCTION, static_cast(Callback::callback)); } - - std::strcpy(&m_storeUrl[0], m_libraryManager.GetStoreUrl().c_str()); - m_storeIndexFilename = ToLocalStoreFile(m_storeUrl); - - if (std::filesystem::is_regular_file(m_storeIndexFilename)) + auto communityStoreUrl = m_libraryManager.GetStoreUrl(); + if (!communityStoreUrl.empty()) { - // seems legit - ParseStoreDataCallback(true, m_storeIndexFilename); + std::strcpy(&m_storeUrl[0], m_libraryManager.GetStoreUrl().c_str()); + m_communityStoreFile = ToLocalStoreFile(m_storeUrl); + + if (std::filesystem::is_regular_file(m_communityStoreFile)) + { + std::cout << "Community store file already exists, skipping download" << std::endl; + ParseCommunityStoreDataCallback(true, m_communityStoreFile); + } + else + { + // Not exists, download it + m_downloadQueue.push({ + "dl", + m_storeUrl, + m_communityStoreFile, + std::bind(&LibraryWindow::ParseCommunityStoreDataCallback, this, std::placeholders::_1, std::placeholders::_2) + }); + + } } + + m_commercialStoreFile = ToLocalStoreFile("commercial_store_db.json"); + if (std::filesystem::is_regular_file(m_commercialStoreFile)) + { + std::cout << "Commercial store file already exists, skipping download" << std::endl; + ParseCommercialStoreDataCallback(true, m_commercialStoreFile); + } else { - // Not exists, download it m_downloadQueue.push({ - "dl", + "dl_commercial", m_storeUrl, - m_storeIndexFilename, - std::bind(&LibraryWindow::ParseStoreDataCallback, this, std::placeholders::_1, std::placeholders::_2) + m_communityStoreFile, + std::bind(&LibraryWindow::ParseCommercialStoreDataCallback, this, std::placeholders::_1, std::placeholders::_2) }); - } } + void LibraryWindow::DownloadThread() { for (;;) @@ -145,6 +168,22 @@ void LibraryWindow::DownloadThread() { download_file(m_curl, cmd.url, cmd.filename, cmd.finished_callback); } + else if (cmd.order == "dl_commercial") + { + try + { + std::cout << "Fetching token..." << std::endl; + std::string token = m_downloader.FetchToken(); + std::cout << "Token: " << token << std::endl; + + std::cout << "Fetching data and saving to file..." << std::endl; + m_downloader.FetchDataAndSaveToFile(token, m_commercialStoreFile); + + ParseCommercialStoreDataCallback(true, m_commercialStoreFile); + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << std::endl; + } + } } } @@ -313,28 +352,27 @@ void LibraryWindow::StoryFileDownloadedCallback(bool success, const std::string } -void LibraryWindow::ParseStoreDataCallback(bool success, const std::string &filename) +void LibraryWindow::ParseCommercialStoreDataCallback(bool success, const std::string &filename) { - try { - std::ifstream f(m_storeIndexFilename); - nlohmann::json j = nlohmann::json::parse(f); - - m_store.clear(); - for (const auto &obj : j) - { - StoryInf s; - - s.title = obj["title"].get(); - s.description = obj["description"].get(); - s.download = obj["download"].get(); - s.age = obj["age"].get(); - - m_store.push_back(s); - } - } - catch(std::exception &e) + if (success) { - std::cout << e.what() << std::endl; + m_libraryManager.ParseCommercialStore(m_commercialStoreFile); + } + else + { + std::cout << "Error while downloading commercial store index file" << std::endl; + } +} + +void LibraryWindow::ParseCommunityStoreDataCallback(bool success, const std::string &filename) +{ + if (success) + { + m_libraryManager.ParseCommunityStore(m_communityStoreFile); + } + else + { + std::cout << "Error while downloading community store index file" << std::endl; } } @@ -362,7 +400,7 @@ std::string LibraryWindow::ToLocalStoreFile(const std::string &url) auto filename = SysLib::GetFileName(url); filename = m_libraryManager.LibraryPath() + "/store/" + filename; - std::cout << "Store file: " << filename << std::endl; + std::cout << "Url: " << url << " will be downloaded in store file: " << filename << std::endl; return filename; } @@ -513,16 +551,22 @@ void LibraryWindow::Draw() // ============================================================================ if (ImGui::BeginTabItem("Remote Store##StoreTabBar", nullptr, ImGuiTabItemFlags_None)) { - ImGui::InputTextWithHint("##store_url", "Store URL", m_storeUrl, IM_ARRAYSIZE(m_storeUrl)); + bool changed = ImGui::InputTextWithHint("##store_url", "Store URL", m_storeUrl, IM_ARRAYSIZE(m_storeUrl)); + + if (changed) + { + m_libraryManager.SetStoreUrl(m_storeUrl); + } + ImGui::SameLine(); if (ImGui::Button("Load")) { - m_storeIndexFilename = ToLocalStoreFile(m_storeUrl); + m_communityStoreFile = ToLocalStoreFile(m_storeUrl); m_downloadQueue.push({ "dl", m_storeUrl, - m_storeIndexFilename, - std::bind(&LibraryWindow::ParseStoreDataCallback, this, std::placeholders::_1, std::placeholders::_2) + m_communityStoreFile, + std::bind(&LibraryWindow::ParseCommunityStoreDataCallback, this, std::placeholders::_1, std::placeholders::_2) }); } @@ -557,7 +601,7 @@ void LibraryWindow::Draw() ImGui::TableHeadersRow(); int id = 1; - for (const auto &obj : m_store) + for (const auto &obj : m_libraryManager.CommunityDbView()) { ImGui::TableNextColumn(); ImGui::Text("%s", obj.title.c_str()); diff --git a/story-editor/src/library_window.h b/story-editor/src/library_window.h index f21e0e8..54953ba 100644 --- a/story-editor/src/library_window.h +++ b/story-editor/src/library_window.h @@ -5,16 +5,10 @@ #include "library_manager.h" #include "i_story_manager.h" #include "thread_safe_queue.h" +#include "downloader.h" #include -struct StoryInf { - int age; - std::string title; - std::string description; - std::string download; -}; - struct DownloadCommand { std::string order; std::string url; @@ -80,6 +74,7 @@ private: IStoryManager &m_storyManager; LibraryManager &m_libraryManager; + Downloader m_downloader; CURL *m_curl; char m_storeUrl[1024]; std::thread m_downloadThread; @@ -90,18 +85,18 @@ private: std::mutex m_downloadBusyMutex; bool m_downloadBusy{false}; - - std::vector m_store; - std::string m_storeIndexFilename; + + std::string m_communityStoreFile; + std::string m_commercialStoreFile; std::string m_storeRawJson; - void ParseStoreDataCallback(bool success, const std::string &filename); + void ParseCommercialStoreDataCallback(bool success, const std::string &filename); + void ParseCommunityStoreDataCallback(bool success, const std::string &filename); void StoryFileDownloadedCallback(bool success, const std::string &filename); void DownloadThread(); int TransferCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); std::string ToLocalStoreFile(const std::string &url); - bool CheckIfSharepoint(const std::string &url, std::string &decoded_url); void SharePointJsonDownloadedCallback(bool success, const std::string &filename); }; diff --git a/story-editor/src/main_window.cpp b/story-editor/src/main_window.cpp index d5afe96..ff1a55d 100644 --- a/story-editor/src/main_window.cpp +++ b/story-editor/src/main_window.cpp @@ -1178,7 +1178,7 @@ void MainWindow::LoadParams() m_libraryManager.Initialize(library_path); } - nlohmann::json store_url = j.value("store_url", "https://gist.githubusercontent.com/DantSu/8920929530b58f2acbfcf1ed0e7746f9/raw/stories-contrib.json"); + nlohmann::json store_url = j.value("store_url", "https://gist.githubusercontent.com/DantSu/3aea4c1fe15070bcf394a40b89aec33e/raw/stories.json"); m_libraryManager.SetStoreUrl(store_url); } diff --git a/story-editor/tools/get_official_db.ts b/story-editor/tools/get_official_db.ts new file mode 100644 index 0000000..acff8fb --- /dev/null +++ b/story-editor/tools/get_official_db.ts @@ -0,0 +1,44 @@ +// A lancer avec Bun ! + +async function getCommercialStoreDb() { + console.log("Fetching db from commercial store"); + + try { + // Première requête pour obtenir le token + const authResponse = await fetch('https://server-auth-prod.lunii.com/guest/create'); + if (!authResponse.ok) { + throw new Error(`Failed to fetch auth token: ${authResponse.statusText}`); + } + + const authData = await authResponse.json(); + const token = authData?.response?.token?.server; + if (!token) { + throw new Error('Token not found in response'); + } + + // Deuxième requête pour récupérer les données + const dataResponse = await fetch('https://server-data-prod.lunii.com/v2/packs', { + headers: { + 'X-AUTH-TOKEN': token, + }, + }); + + if (!dataResponse.ok) { + throw new Error(`Failed to fetch packs: ${dataResponse.statusText}`); + } + + const jsonData = await dataResponse.json(); // Récupérer les données en JSON + + // Enregistrer les données dans un fichier local + const filePath = "./commercial_store_db.json"; + await Bun.write(filePath, JSON.stringify(jsonData, null, 2)); + console.log(`Data saved to ${filePath}`); + + return jsonData; + } catch (error) { + console.error(error); + throw error; + } +} + +await getCommercialStoreDb(); \ No newline at end of file