(WIP) new Download manager, download of commercial DB
Some checks failed
build-story-editor / build_linux (push) Has been cancelled
build-story-editor / build_win32 (push) Has been cancelled
Deploy / deploy (push) Has been cancelled

This commit is contained in:
anthony@rabine.fr 2025-01-03 13:21:47 +01:00
parent 3d9b60cb32
commit fe920f4b15
17 changed files with 577 additions and 118 deletions

View file

@ -0,0 +1,25 @@
#pragma once
#include <string>
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;
};

View file

@ -43,7 +43,7 @@ std::string SysLib::GetFileName(const std::string &path)
}
else
{
return "";
return path;
}
}
else

View file

@ -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;

View file

@ -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

120
shared/downloader.cpp Normal file
View file

@ -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<std::string>();
} 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;
}

43
shared/downloader.h Normal file
View file

@ -0,0 +1,43 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <stdexcept>
#include <thread>
#include <functional>
#include <curl/curl.h>
#include "thread_safe_queue.h"
class Downloader
{
public:
struct Command {
std::string order;
std::string url;
std::string filename;
std::function<void(bool, const std::string &filename)> 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<Downloader::Command> m_downloadQueue;
std::mutex m_downloadMutex;
bool m_cancel{false};
std::string PerformGetRequest(const std::string& url, struct curl_slist* headers = nullptr);
void DownloadThread();
};

View file

@ -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<std::string>();
s.description = obj["description"].get<std::string>();
s.download = obj["download"].get<std::string>();
s.age = obj["age"].get<int>();
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<std::string>();
story.uuid = pack["uuid"].get<std::string>();
if (pack.contains("localized_infos") && pack["localized_infos"].contains("fr_FR"))
{
const auto& localized = pack["localized_infos"]["fr_FR"];
story.title = localized["title"].get<std::string>();
story.description = localized["description"].get<std::string>();
if (localized.contains("image") && localized["image"].contains("image_url")) {
story.image_url = localized["image"]["image_url"].get<std::string>();
}
}
m_storyDb.AddStory(story, StoryDb::cCommercialStore);
}
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << std::endl;
}
}

View file

@ -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<std::shared_ptr<StoryProject>> m_projectsList;
std::string m_storeUrl;
std::thread m_copyWorker;
StoryDb m_storyDb;
};
#endif // LIBRARYMANAGER_H

46
shared/story_db.cpp Normal file
View file

@ -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);
}
}

45
shared/story_db.h Normal file
View file

@ -0,0 +1,45 @@
#pragma once
#include <string>
#include <vector>
#include <ranges>
#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<IStoryDb::Info> m_store;
std::vector<IStoryDb::Info> m_commercialStore;
};

View file

@ -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

View file

@ -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

View file

@ -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<StoryPage> page = proj.CreatePage("main");
proj.New(uuid, outputDir);
proj.SetName(j["title"].get<std::string>());
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<uint32_t> 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)

View file

@ -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<xfer_callback_t>(Callback<int(void *, curl_off_t, curl_off_t, curl_off_t, curl_off_t)>::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<std::string>();
s.description = obj["description"].get<std::string>();
s.download = obj["download"].get<std::string>();
s.age = obj["age"].get<int>();
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());

View file

@ -5,16 +5,10 @@
#include "library_manager.h"
#include "i_story_manager.h"
#include "thread_safe_queue.h"
#include "downloader.h"
#include <curl/curl.h>
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<StoryInf> 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);
};

View file

@ -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);
}

View file

@ -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();