Bernardo Magri 1 year ago
parent
commit
629b255c08
6 changed files with 367 additions and 0 deletions
  1. 226 0
      src/book.cpp
  2. 43 0
      src/book.h
  3. 26 0
      src/main.cpp
  4. 37 0
      src/mainWindow.cpp
  5. 16 0
      src/mainWindow.h
  6. 19 0
      src/meson.build

+ 226 - 0
src/book.cpp

@@ -0,0 +1,226 @@
+#include "book.h"
+
+
+
+// void Book::extractEPUBMetadata() {
+//     try {
+//         xmlpp::DomParser parser;
+//         parser.parse_file(file_path);
+//         if (parser) {
+//             auto root = parser.get_document()->get_root_node();
+//             auto metadataNode = root->get_first_child("metadata");
+
+//             if (metadataNode) {
+//                 auto titleNode = metadataNode->get_first_child("dc:title");
+//                 auto authorNode = metadataNode->get_first_child("dc:creator");
+
+//                 if (titleNode) {
+//                     title = titleNode->get_child_text()->get_content();
+//                 }
+//                 if (authorNode) {
+//                     authors = authorNode->get_child_text()->get_content();
+//                 }
+//             }
+
+//             // Extract cover from EPUB (usually a reference to the image in the manifest)
+//             auto manifestNode = root->get_first_child("manifest");
+//             if (manifestNode) {
+//                 for (auto child : manifestNode->get_children()) {
+//                     auto element = dynamic_cast<xmlpp::Element*>(child);
+//                     if (element && element->get_attribute_value("id") == "cover-image") {
+//                         std::string href = element->get_attribute_value("href");
+//                         cover_image = Gdk::Pixbuf::create_from_file(href);
+//                         break;
+//                     }
+//                 }
+//             }
+//         }
+//     } catch (const std::exception& ex) {
+//         std::cerr << "Error extracting EPUB metadata: " << ex.what() << std::endl;
+//     }
+// }
+
+void Book::extractPDFMetadata() {
+    PopplerDocument* document = poppler_document_new_from_file(("file://" + file_path).c_str(), nullptr, nullptr);
+    if (document) {
+        title = poppler_document_get_title(document);
+        authors = poppler_document_get_author(document);
+
+        cover_image = render_pdf_page_to_pixbuf(file_path, 0);
+
+        g_object_unref(document);
+    }
+}
+
+Glib::RefPtr<Gdk::Pixbuf> Book::render_pdf_page_to_pixbuf(const std::string& pdf_file, int page_num) {
+    PopplerDocument* document = poppler_document_new_from_file(("file://" + pdf_file).c_str(), nullptr, nullptr);
+    if (!document) {
+        std::cerr << "Error loading PDF file." << std::endl;
+        return Glib::RefPtr<Gdk::Pixbuf>();
+    }
+
+    PopplerPage* page = poppler_document_get_page(document, page_num);
+    if (!page) {
+        std::cerr << "Error loading PDF page." << std::endl;
+        g_object_unref(document);
+        return Glib::RefPtr<Gdk::Pixbuf>();
+    }
+
+    int width = 150;
+    int height = 200;
+    Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create(Cairo::FORMAT_RGB24, width, height);
+    Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surface);
+
+    poppler_page_render(page, cr->cobj());
+
+    Glib::RefPtr<Gdk::Pixbuf> pixbuf = create_pixbuf_from_cairo_surface(surface);
+
+    g_object_unref(page);
+    g_object_unref(document);
+
+    return pixbuf;
+}
+
+void Book::loadMetadata() {
+    if (file_path.find(".pdf") != std::string::npos) {
+        extractPDFMetadata();
+    } else if (file_path.find(".epub") != std::string::npos) {
+        extractEPUBMetadata();
+    } else {
+        std::cerr << "Unsupported file format: " << file_path << std::endl;
+    }
+}
+
+void Book::loadCover() {
+    // This can be handled inside `extractPDFMetadata` and `extractEPUBMetadata`
+}
+
+// Initialize the SQLite database and create the necessary table
+void Book::initDatabase() {
+    sqlite3* db;
+    int rc = sqlite3_open("bookcollection.db", &db);
+    if (rc != SQLITE_OK) {
+        std::cerr << "Cannot open database: " << sqlite3_errmsg(db) << std::endl;
+        return;
+    }
+
+    const char* createTableSQL = 
+        "CREATE TABLE IF NOT EXISTS books ("
+        "id INTEGER PRIMARY KEY, "
+        "title TEXT, "
+        "authors TEXT, "
+        "isbn TEXT, "
+        "file_path TEXT, "
+        "cover BLOB);";
+
+    char* errorMessage;
+    rc = sqlite3_exec(db, createTableSQL, 0, 0, &errorMessage);
+    if (rc != SQLITE_OK) {
+        std::cerr << "Failed to create table: " << errorMessage << std::endl;
+        sqlite3_free(errorMessage);
+    }
+
+    sqlite3_close(db);
+}
+
+// Save the book's metadata and cover image to the SQLite database
+void Book::saveToDatabase() {
+    sqlite3* db;
+    sqlite3_open("bookcollection.db", &db);
+
+    const char* insertSQL = "INSERT INTO books (title, authors, isbn, file_path, cover) VALUES (?, ?, ?, ?, ?);";
+    sqlite3_stmt* stmt;
+    sqlite3_prepare_v2(db, insertSQL, -1, &stmt, nullptr);
+
+    sqlite3_bind_text(stmt, 1, title.c_str(), -1, SQLITE_STATIC);
+    sqlite3_bind_text(stmt, 2, authors.c_str(), -1, SQLITE_STATIC);
+    sqlite3_bind_text(stmt, 3, isbn.c_str(), -1, SQLITE_STATIC);
+    sqlite3_bind_text(stmt, 4, file_path.c_str(), -1, SQLITE_STATIC);
+
+    // Load cover image as binary data (BLOB)
+    if (cover_image) {
+        Glib::RefPtr<Gdk::PixbufLoader> loader = Gdk::PixbufLoader::create();
+        cover_image->save(loader->get_pixbuf(), "jpeg");
+        std::string coverData(loader->get_data(), loader->get_size());
+        sqlite3_bind_blob(stmt, 5, coverData.c_str(), coverData.size(), SQLITE_STATIC);
+    } else {
+        sqlite3_bind_null(stmt, 5);
+    }
+
+    if (sqlite3_step(stmt) != SQLITE_DONE) {
+        std::cerr << "Failed to insert data into the database: " << sqlite3_errmsg(db) << std::endl;
+    }
+
+    sqlite3_finalize(stmt);
+    sqlite3_close(db);
+}
+
+// Load all books from the SQLite database
+std::vector<std::shared_ptr<Book>> Book::loadFromDatabase() {
+    std::vector<std::shared_ptr<Book>> books;
+
+    sqlite3* db;
+    sqlite3_open("bookcollection.db", &db);
+
+    const char* selectSQL = "SELECT title, authors, isbn, file_path, cover FROM books;";
+    sqlite3_stmt* stmt;
+    sqlite3_prepare_v2(db, selectSQL, -1, &stmt, nullptr);
+
+    while (sqlite3_step(stmt) == SQLITE_ROW) {
+        auto book = std::make_shared<Book>((const char*)sqlite3_column_text(stmt, 3));
+        book->title = (const char*)sqlite3_column_text(stmt, 0);
+        book->authors = (const char*)sqlite3_column_text(stmt, 1);
+        book->isbn = (const char*)sqlite3_column_text(stmt, 2);
+
+        // Load cover from BLOB
+        const void* coverBlob = sqlite3_column_blob(stmt, 4);
+        int coverSize = sqlite3_column_bytes(stmt, 4);
+        if (coverBlob && coverSize > 0) {
+            std::string coverData((const char*)coverBlob, coverSize);
+            Glib::RefPtr<Gdk::PixbufLoader> loader = Gdk::PixbufLoader::create();
+            loader->write((const guint8*)coverData.c_str(), coverSize);
+            loader->close();
+            book->cover_image = loader->get_pixbuf();
+        }
+
+        books.push_back(book);
+    }
+
+    sqlite3_finalize(stmt);
+    sqlite3_close(db);
+    return books;
+}
+
+// Synchronize the book to the cloud using a cloud API (e.g., Google Drive)
+void Book::syncToCloud() {
+    CURL* curl;
+    CURLcode res;
+    curl_global_init(CURL_GLOBAL_DEFAULT);
+    curl = curl_easy_init();
+    if (curl) {
+        // Replace with your cloud storage URL
+        const std::string url = "https://cloud-storage-url.com/upload";
+        
+        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+        curl_easy_setopt(curl, CURLOPT_POST, 1L);
+
+        // Set up form data
+        curl_mime* form = curl_mime_init(curl);
+        curl_mimepart* field = curl_mime_addpart(form);
+        curl_mime_name(field, "file");
+        curl_mime_filedata(field, file_path.c_str());
+        curl_easy_setopt(curl, CURLOPT_MIMEPOST, form);
+
+        // Perform the upload
+        res = curl_easy_perform(curl);
+        if (res != CURLE_OK) {
+            std::cerr << "Cloud sync failed: " << curl_easy_strerror(res) << std::endl;
+        } else {
+            std::cout << "Book successfully uploaded to the cloud!" << std::endl;
+        }
+
+        curl_mime_free(form);
+        curl_easy_cleanup(curl);
+    }
+    curl_global_cleanup();
+}

+ 43 - 0
src/book.h

@@ -0,0 +1,43 @@
+#include <gtkmm.h>
+#include <sqlite3.h>
+#include <curl/curl.h>
+#include <memory>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include <libxml++/libxml++.h>
+#include <libxml++/parsers/domparser.h>
+#include <poppler/glib/poppler.h>
+
+class Book {
+public:
+    Book(const std::string& filePath);
+    static void initDatabase();
+    void saveToDatabase();
+    static std::vector<std::shared_ptr<Book>> loadFromDatabase();
+    void syncToCloud();
+
+    std::string getTitle() const { return title; }
+    std::string getAuthors() const { return authors; }
+    std::string getFilePath() const { return file_path; }
+    Glib::RefPtr<Gdk::Pixbuf> getCoverImage() const { return cover_image; }
+    double getProgress() const { return progress; }
+
+    void setProgress(double progressValue);
+    void organizeFile();
+
+private:
+    std::string title;
+    std::string authors;
+    std::string isbn;
+    std::string file_path;
+    std::string image_path;
+    double progress;
+
+    Glib::RefPtr<Gdk::Pixbuf> cover_image;
+    Glib::RefPtr<Gdk::Pixbuf> render_pdf_page_to_pixbuf(const std::string& pdf_file, int page_num)
+
+    void extractEPUBMetadata();
+    void extractPDFMetadata();
+};

+ 26 - 0
src/main.cpp

@@ -0,0 +1,26 @@
+#include <gtkmm.h>
+#include "mainWindow.h"
+
+class BookManagerApp : public Gtk::Application {
+protected:
+    BookManagerApp() : Gtk::Application("com.example.BookManager") {}
+
+    Gtk::Window* create_window() {
+        auto window = new MainWindow();
+        return window;
+    }
+
+    void on_activate() override {
+        Gtk::Application::on_activate();
+
+        auto window = create_window();
+        add_window(*window);
+
+        window->show();
+    }
+};
+
+int main(int argc, char** argv) {
+    auto app = BookManagerApp::create();
+    return app->run(argc, argv);
+}

+ 37 - 0
src/mainWindow.cpp

@@ -0,0 +1,37 @@
+#include "mainWindow.h"
+
+MainWindow::MainWindow() : add_button("Add Book") {
+    set_title("Book Collection Manager");
+    set_default_size(800, 600);
+
+    add_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_add_button_clicked));
+
+    main_box.set_orientation(Gtk::Orientation::VERTICAL);
+    main_box.append(add_button);
+    main_box.append(flowbox);
+
+    // Initialize SQLite database and load books
+    Book::initDatabase();
+    auto books = Book::loadFromDatabase();
+    for (auto& book : books) {
+        addBook(book);
+    }
+
+    set_child(main_box);
+}
+
+void MainWindow::on_add_button_clicked() {
+    auto book = std::make_shared<Book>("path_to_book_file.epub");
+    //book->loadMetadata();
+    book->saveToDatabase();
+    book->syncToCloud();
+    addBook(book);
+}
+
+void MainWindow::addBook(std::shared_ptr<Book> book) {
+    auto cover_image = book->getCoverImage();
+    if (cover_image) {
+        Gtk::Image* image = Gtk::make_managed<Gtk::Image>(cover_image);
+        flowbox.append(*image);  // Add to the flowbox
+    }
+}

+ 16 - 0
src/mainWindow.h

@@ -0,0 +1,16 @@
+#include<glibmm-2.68/glibmm.h>
+#include<gtkmm-4.0/gtkmm.h>
+#include "book.h"
+
+class MainWindow : public Gtk::ApplicationWindow {
+public:
+    MainWindow();
+    void addBook(std::shared_ptr<Book> book);
+
+private:
+    Gtk::FlowBox flowbox;
+    Gtk::Button add_button;
+    Gtk::Box main_box;
+
+    void on_add_button_clicked();
+};

+ 19 - 0
src/meson.build

@@ -0,0 +1,19 @@
+project('ebooksmith', 'cpp',
+  version : '0.1',
+  default_options : ['warning_level=3'])
+
+#gnome = import('gnome')
+
+#res = gnome.compile_resources(
+#    'resources', '../resources/gresource.xml',
+#    source_dir: '../resources',
+#    c_name: 'gresources'
+#)
+
+dep = [dependency('gtkmm-4.0'),
+       dependency('sqlite3'),
+       dependency('libcurl'),
+       dependency('libxml++-2.6'),
+       dependency('poppler-glib')]
+src = ['book.h', 'mainWindow.cpp', 'main.cpp', 'book.cpp', 'mainWindow.h']
+exe = executable('ebooksmith', src, dependencies : dep, install : true)