2026-01-07 03:58:37 -05:00
|
|
|
#pragma once
|
2026-01-21 17:43:51 +03:00
|
|
|
#include <Print.h>
|
2026-01-07 03:58:37 -05:00
|
|
|
#include <expat.h>
|
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Type of OPDS entry.
|
|
|
|
|
*/
|
|
|
|
|
enum class OpdsEntryType {
|
|
|
|
|
NAVIGATION, // Link to another catalog
|
|
|
|
|
BOOK // Downloadable book
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Represents an entry from an OPDS feed (either a navigation link or a book).
|
|
|
|
|
*/
|
|
|
|
|
struct OpdsEntry {
|
|
|
|
|
OpdsEntryType type = OpdsEntryType::NAVIGATION;
|
|
|
|
|
std::string title;
|
|
|
|
|
std::string author; // Only for books
|
|
|
|
|
std::string href; // Navigation URL or epub download URL
|
|
|
|
|
std::string id;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Legacy alias for backward compatibility
|
|
|
|
|
using OpdsBook = OpdsEntry;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parser for OPDS (Open Publication Distribution System) Atom feeds.
|
|
|
|
|
* Uses the Expat XML parser to parse OPDS catalog entries.
|
|
|
|
|
*
|
|
|
|
|
* Usage:
|
|
|
|
|
* OpdsParser parser;
|
|
|
|
|
* if (parser.parse(xmlData, xmlLength)) {
|
|
|
|
|
* for (const auto& entry : parser.getEntries()) {
|
|
|
|
|
* if (entry.type == OpdsEntryType::BOOK) {
|
|
|
|
|
* // Downloadable book
|
|
|
|
|
* } else {
|
|
|
|
|
* // Navigation link to another catalog
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*/
|
2026-01-21 17:43:51 +03:00
|
|
|
class OpdsParser final : public Print {
|
2026-01-07 03:58:37 -05:00
|
|
|
public:
|
2026-01-21 17:43:51 +03:00
|
|
|
OpdsParser();
|
2026-01-07 03:58:37 -05:00
|
|
|
~OpdsParser();
|
|
|
|
|
|
|
|
|
|
// Disable copy
|
|
|
|
|
OpdsParser(const OpdsParser&) = delete;
|
|
|
|
|
OpdsParser& operator=(const OpdsParser&) = delete;
|
|
|
|
|
|
2026-01-21 17:43:51 +03:00
|
|
|
size_t write(uint8_t) override;
|
|
|
|
|
size_t write(const uint8_t*, size_t) override;
|
|
|
|
|
|
|
|
|
|
void flush() override;
|
|
|
|
|
|
|
|
|
|
bool error() const;
|
|
|
|
|
|
|
|
|
|
operator bool() { return !error(); }
|
2026-01-07 03:58:37 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the parsed entries (both navigation and book entries).
|
|
|
|
|
* @return Vector of OpdsEntry entries
|
|
|
|
|
*/
|
2026-01-21 17:43:51 +03:00
|
|
|
const std::vector<OpdsEntry>& getEntries() const& { return entries; }
|
|
|
|
|
std::vector<OpdsEntry> getEntries() && { return std::move(entries); }
|
2026-01-07 03:58:37 -05:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get only book entries (legacy compatibility).
|
|
|
|
|
* @return Vector of book entries
|
|
|
|
|
*/
|
|
|
|
|
std::vector<OpdsEntry> getBooks() const;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Clear all parsed entries.
|
|
|
|
|
*/
|
|
|
|
|
void clear();
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// Expat callbacks
|
|
|
|
|
static void XMLCALL startElement(void* userData, const XML_Char* name, const XML_Char** atts);
|
|
|
|
|
static void XMLCALL endElement(void* userData, const XML_Char* name);
|
|
|
|
|
static void XMLCALL characterData(void* userData, const XML_Char* s, int len);
|
|
|
|
|
|
|
|
|
|
// Helper to find attribute value
|
|
|
|
|
static const char* findAttribute(const XML_Char** atts, const char* name);
|
|
|
|
|
|
|
|
|
|
XML_Parser parser = nullptr;
|
|
|
|
|
std::vector<OpdsEntry> entries;
|
|
|
|
|
OpdsEntry currentEntry;
|
|
|
|
|
std::string currentText;
|
|
|
|
|
|
|
|
|
|
// Parser state
|
|
|
|
|
bool inEntry = false;
|
|
|
|
|
bool inTitle = false;
|
|
|
|
|
bool inAuthor = false;
|
|
|
|
|
bool inAuthorName = false;
|
|
|
|
|
bool inId = false;
|
2026-01-21 17:43:51 +03:00
|
|
|
|
|
|
|
|
bool errorOccured = false;
|
2026-01-07 03:58:37 -05:00
|
|
|
};
|