When downloading a book via OPDS, the last acquisition link is chosen when there are multiple. This allows choosing which format to download when there are many. Having only one format will automatically download that.
121 lines
3.1 KiB
C++
121 lines
3.1 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
|
|
};
|
|
|
|
struct OpdsAcquisitionLink {
|
|
std::string href;
|
|
std::string mimeType;
|
|
std::string formatKey;
|
|
std::string fileExtension;
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
std::vector<OpdsAcquisitionLink> acquisitionLinks;
|
|
};
|
|
|
|
// 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
|
|
const std::string& getSearchTemplate() const { return searchTemplate; }
|
|
const std::string& getOsdUrl() const { return osdUrl; }
|
|
const std::string& getNextPageUrl() const { return nextPageUrl; }
|
|
const std::string& getPrevPageUrl() const { return prevPageUrl; }
|
|
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);
|
|
|
|
std::string searchTemplate;
|
|
std::string osdUrl;
|
|
std::string nextPageUrl;
|
|
std::string prevPageUrl;
|
|
// 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;
|
|
};
|