mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-04 06:37:38 +03:00
## Summary - Rewrite OpdsParser to stream parsing instead of full content - Fix OOM due to big http xml response Closes #385 --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**NO**_
105 lines
2.5 KiB
C++
105 lines
2.5 KiB
C++
#pragma once
|
|
#include <Print.h>
|
|
#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
|
|
* }
|
|
* }
|
|
* }
|
|
*/
|
|
class OpdsParser final : public Print {
|
|
public:
|
|
OpdsParser();
|
|
~OpdsParser();
|
|
|
|
// Disable copy
|
|
OpdsParser(const OpdsParser&) = delete;
|
|
OpdsParser& operator=(const OpdsParser&) = delete;
|
|
|
|
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(); }
|
|
|
|
/**
|
|
* Get the parsed entries (both navigation and book entries).
|
|
* @return Vector of OpdsEntry entries
|
|
*/
|
|
const std::vector<OpdsEntry>& getEntries() const& { return entries; }
|
|
std::vector<OpdsEntry> getEntries() && { return std::move(entries); }
|
|
|
|
/**
|
|
* 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;
|
|
|
|
bool errorOccured = false;
|
|
};
|