Calibre Web Epub Downloading + Calibre Wireless Device Syncing (#219)
## Summary Adds support for browsing and downloading books from a Calibre-web server via OPDS. How it works 1. Configure server URL in Settings → Calibre Web URL (e.g., https://myserver.com:port I use Cloudflare tunnel to make my server accessible anywhere fwiw) 2. "Calibre Library" will now show on the the home screen 3. Browse the catalog - navigate through categories like "By Newest", "By Author", "By Series", etc. 4. Download books - select a book and press Confirm to download the EPUB to your device Navigation - Up/Down - Move through entries - Confirm - Open folder or download book - Back - Go to parent catalog, or exit to home if at root - Navigation entries show with > prefix, books show title and author - Button hints update dynamically ("Open" for folders, "Download" for books) Technical details - Fetches OPDS catalog from {server_url}/opds - Parses both navigation feeds (catalog links) and acquisition feeds (downloadable books) - Maintains navigation history stack for back navigation - Handles absolute paths in OPDS links correctly (e.g., /books/opds/navcatalog/...) - Downloads EPUBs directly to the SD card root Note The server URL should be typed to include https:// if the server requires it - HTTP→HTTPS redirects may cause SSL errors on ESP32. ## Additional Context * I also changed the home titles to use uppercase for each word and added a setting to change the size of the side margins --------- Co-authored-by: Dave Allie <dave@daveallie.com>
This commit is contained in:
99
lib/OpdsParser/OpdsParser.h
Normal file
99
lib/OpdsParser/OpdsParser.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#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 {
|
||||
public:
|
||||
OpdsParser() = default;
|
||||
~OpdsParser();
|
||||
|
||||
// Disable copy
|
||||
OpdsParser(const OpdsParser&) = delete;
|
||||
OpdsParser& operator=(const OpdsParser&) = delete;
|
||||
|
||||
/**
|
||||
* Parse an OPDS XML feed.
|
||||
* @param xmlData Pointer to the XML data
|
||||
* @param length Length of the XML data
|
||||
* @return true if parsing succeeded, false on error
|
||||
*/
|
||||
bool parse(const char* xmlData, size_t length);
|
||||
|
||||
/**
|
||||
* Get the parsed entries (both navigation and book entries).
|
||||
* @return Vector of OpdsEntry entries
|
||||
*/
|
||||
const std::vector<OpdsEntry>& getEntries() const { return 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;
|
||||
};
|
||||
Reference in New Issue
Block a user