diff --git a/lib/OpdsParser/OpdsParser.cpp b/lib/OpdsParser/OpdsParser.cpp index 84feef74..45d92eaa 100644 --- a/lib/OpdsParser/OpdsParser.cpp +++ b/lib/OpdsParser/OpdsParser.cpp @@ -70,6 +70,7 @@ bool OpdsParser::error() const { return errorOccured; } void OpdsParser::clear() { entries.clear(); searchTemplate.clear(); + osdUrl.clear(); nextPageUrl.clear(); prevPageUrl.clear(); currentEntry = OpdsEntry{}; @@ -105,6 +106,8 @@ void XMLCALL OpdsParser::startElement(void* userData, const XML_Char* name, cons std::string sHref(href); if (sHref.find("{searchTerms}") != std::string::npos) { self->searchTemplate = sHref; + } else if (type && strcmp(type, "application/opensearchdescription+xml") == 0) { + self->osdUrl = sHref; } } else if (rel && strcmp(rel, "next") == 0 && !self->inEntry) { self->nextPageUrl = href; diff --git a/lib/OpdsParser/OpdsParser.h b/lib/OpdsParser/OpdsParser.h index 9c93b89a..5f287495 100644 --- a/lib/OpdsParser/OpdsParser.h +++ b/lib/OpdsParser/OpdsParser.h @@ -50,6 +50,7 @@ class OpdsParser final : public Print { // 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; @@ -89,6 +90,7 @@ class OpdsParser final : public Print { 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 diff --git a/src/activities/browser/OpdsBookBrowserActivity.cpp b/src/activities/browser/OpdsBookBrowserActivity.cpp index 2b9f1202..bee5a10a 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.cpp +++ b/src/activities/browser/OpdsBookBrowserActivity.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -245,6 +246,9 @@ void OpdsBookBrowserActivity::fetchFeed(const std::string& path) { } searchTemplate = parser.getSearchTemplate(); + if (searchTemplate.empty() && !parser.getOsdUrl().empty()) { + fetchOsdTemplate(UrlUtils::buildUrl(url, parser.getOsdUrl())); + } const auto& nextUrl = parser.getNextPageUrl(); const auto& prevUrl = parser.getPrevPageUrl(); entries = std::move(parser).getEntries(); @@ -336,6 +340,42 @@ void OpdsBookBrowserActivity::downloadBook(const OpdsEntry& book) { } } +void OpdsBookBrowserActivity::fetchOsdTemplate(const std::string& osdUrl) { + std::string content; + if (!HttpDownloader::fetchUrl(osdUrl, content)) { + LOG_ERR("OPDS", "Failed to fetch OSD: %s", osdUrl.c_str()); + return; + } + + struct OsdState { + std::string templateUrl; + static void XMLCALL onStart(void* ud, const XML_Char* name, const XML_Char** atts) { + if (strcmp(name, "Url") != 0 && strstr(name, ":Url") == nullptr) return; + auto* state = static_cast(ud); + for (int i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "template") == 0 && strstr(atts[i + 1], "{searchTerms}") != nullptr) { + state->templateUrl = atts[i + 1]; + break; + } + } + } + } osdState; + + XML_Parser p = XML_ParserCreate(nullptr); + if (!p) { + LOG_ERR("OPDS", "OSD parser alloc failed"); + return; + } + XML_SetUserData(p, &osdState); + XML_SetElementHandler(p, OsdState::onStart, nullptr); + XML_Parse(p, content.c_str(), static_cast(content.size()), XML_TRUE); + XML_ParserFree(p); + + if (!osdState.templateUrl.empty()) { + searchTemplate = UrlUtils::buildUrl(osdUrl, osdState.templateUrl); + } +} + void OpdsBookBrowserActivity::launchSearch() { consumeConfirm = true; state = BrowserState::SEARCH_INPUT; diff --git a/src/activities/browser/OpdsBookBrowserActivity.h b/src/activities/browser/OpdsBookBrowserActivity.h index 8b55343a..b1fc3d01 100644 --- a/src/activities/browser/OpdsBookBrowserActivity.h +++ b/src/activities/browser/OpdsBookBrowserActivity.h @@ -46,6 +46,7 @@ class OpdsBookBrowserActivity final : public Activity { void navigateToEntry(const OpdsEntry& entry); void navigateBack(); void downloadBook(const OpdsEntry& book); + void fetchOsdTemplate(const std::string& osdUrl); void launchSearch(); void performSearch(const std::string& query); bool preventAutoSleep() override { return true; }