#include "library_manager.h" #include "tlv.h" #include #include #include "json.hpp" #include "story_project.h" #include "uuid.h" LibraryManager::LibraryManager(ILogger &log) : m_log(log) { } LibraryManager::~LibraryManager() { if (m_copyWorker.joinable()) { m_copyWorker.join(); } } void LibraryManager::Initialize(const std::string &library_path) { m_library_path = library_path; CheckDirectories(); Scan(); } void LibraryManager::CheckDirectories() { std::filesystem::path dlDir = std::filesystem::path(m_library_path) / "store"; std::filesystem::create_directories(dlDir); } void LibraryManager::Scan() { std::filesystem::path directoryPath(m_library_path); if (std::filesystem::exists(directoryPath) && std::filesystem::is_directory(directoryPath)) { m_projectsList.clear(); for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) { if (std::filesystem::is_directory(entry.path())) { // Si c'est un sous-répertoire, récursivement scanner le contenu std::string uuid = entry.path().filename().generic_string(); if (Uuid::IsValid(uuid)) { std::cout << "Found story directory" << std::endl; // Look for a story.json file in this directory auto p = entry.path() / "project.json"; if (std::filesystem::exists(p)) { // okay, open it auto proj = std::make_shared(m_log); try { std::ifstream f(p); nlohmann::json j = nlohmann::json::parse(f); if (proj->ParseStoryInformation(j)) { // Valid project file, add it to the list proj->SetPaths(uuid, m_library_path); m_projectsList.push_back(proj); } } catch(nlohmann::json::exception &e) { std::cout << e.what() << std::endl; } } } } } } } std::shared_ptr LibraryManager::NewProject() { auto story = std::make_shared(m_log); std::string uuid = Uuid().String(); story->New(uuid, m_library_path); story->SetDisplayFormat(320, 240); story->SetImageFormat(Resource::IMG_FORMAT_QOIF); story->SetSoundFormat(Resource::SND_FORMAT_WAV); story->SetName("New project"); return story; } std::shared_ptr LibraryManager::GetStory(const std::string &uuid) { std::shared_ptr current; for (const auto &s : m_projectsList) { if (s->GetUuid() == uuid) { current = s; } } return current; } std::string LibraryManager::IndexFileName() const { auto p = std::filesystem::path(m_library_path) / "index.ost"; return p.string(); } void LibraryManager::Save() { Tlv tlv; tlv.add_object(2); // 2 éléments dans l'objet tlv.add_integer(1); // version du fichier (1 == V1) // On compte le nombre de projets sélecttionnés uint16_t stories = 0; for (auto &p : m_projectsList) { if (p->IsSelected()) { stories++; } } tlv.add_array(stories); // Nombre d'histoires for (auto &p : m_projectsList) { if (p->IsSelected()) { tlv.add_object(6); tlv.add_string(p->GetUuid()); tlv.add_string(p->GetTitleImage()); tlv.add_string(p->GetTitleSound()); tlv.add_string(p->GetName()); tlv.add_string(p->GetDescription()); tlv.add_integer(p->GetVersion()); } } tlv.Save(IndexFileName()); } void LibraryManager::CopyToDevice(const std::string &outputDir) { try { // Generate TLV file (index of all stories) Save(); // Copy TLV to the directory root std::filesystem::copy(IndexFileName(), outputDir, std::filesystem::copy_options::overwrite_existing); if (m_copyWorker.joinable()) { m_copyWorker.join(); } m_copyWorker = std::thread([&, outputDir]() { // myThread.detach(); std::cout << "Starting to copy elements" << std::endl; for (auto p : *this) { if (p->IsSelected()) { std::cout << "Copying " << p->GetName() << std::endl; p->CopyToDevice(outputDir); } } }); } catch (const std::filesystem::filesystem_error& e) { std::cerr << "Filesystem error: " << e.what() << std::endl; } catch(const std::exception& e) { std::cerr << e.what() << '\n'; } } bool LibraryManager::IsInitialized() const { return m_library_path.size() > 0; } 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(); std::cout << "Found " << response.size() << " commercial stories" << std::endl; 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; } }