summaryrefslogtreecommitdiffstats
path: root/fbreader/src/network/opds/OPDSBookItem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fbreader/src/network/opds/OPDSBookItem.cpp')
-rw-r--r--fbreader/src/network/opds/OPDSBookItem.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/fbreader/src/network/opds/OPDSBookItem.cpp b/fbreader/src/network/opds/OPDSBookItem.cpp
new file mode 100644
index 0000000..6899afa
--- /dev/null
+++ b/fbreader/src/network/opds/OPDSBookItem.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2004-2012 Geometer Plus <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <ZLNetworkManager.h>
+#include <ZLNetworkUtil.h>
+#include <ZLUnicodeUtil.h>
+#include <ZLStringUtil.h>
+
+#include "../NetworkLink.h"
+#include "OPDSXMLParser.h"
+
+#include "OPDSBookItem.h"
+#include "OPDSCatalogItem.h"
+
+#include "../tree/NetworkTreeFactory.h"
+
+OPDSBookItem::OPDSBookItem(const OPDSLink &link, OPDSEntry &entry, std::string baseUrl, unsigned int index) :
+ NetworkBookItem(
+ link,
+ entry.id()->uri(),
+ index,
+ entry.title(),
+ getAnnotation(entry),
+ entry.dcLanguage(),
+ getDate(entry),
+ getAuthors(entry),
+ getTags(entry),
+ entry.seriesTitle(),
+ entry.seriesIndex(),
+ getUrls(link, entry, baseUrl),
+ getReferences(link, entry, baseUrl)
+ ) {
+ myRelatedInfos = getRelatedUrls(link, entry, baseUrl);
+ myInformationIsFull = false;
+}
+
+bool OPDSBookItem::isFullyLoaded() const {
+ return myInformationIsFull || URLByType.find(URL_SINGLE_ENTRY) == URLByType.end();
+}
+
+class OPDSBookItemFullInfoLoader : public ZLNetworkRequest::Listener {
+public:
+ OPDSBookItemFullInfoLoader(OPDSBookItem &item, shared_ptr<ZLNetworkRequest> request, shared_ptr<ZLNetworkRequest::Listener> listener) :
+ myItem(item), myListener(listener) {
+ request->setListener(this);
+ ZLNetworkManager::Instance().performAsync(request);
+ }
+
+ void finished(const std::string &error) {
+ if (error.empty()) {
+ myItem.myInformationIsFull = true;
+ }
+ myListener->finished(error);
+ }
+private:
+ OPDSBookItem &myItem;
+ shared_ptr<ZLNetworkRequest::Listener> myListener;
+};
+
+void OPDSBookItem::loadFullInformation(shared_ptr<ZLNetworkRequest::Listener> listener) {
+ if (myInformationIsFull) {
+ listener->finished();
+ return;
+ }
+
+ if (URLByType.find(URL_SINGLE_ENTRY) == URLByType.end()) {
+ myInformationIsFull = true;
+ listener->finished();
+ return;
+ }
+
+ std::string url = URLByType[URL_SINGLE_ENTRY];
+ shared_ptr<ZLNetworkRequest> request = ZLNetworkManager::Instance().createXMLParserRequest(
+ url, new OPDSXMLParser(new FullEntryReader(*this, (const OPDSLink&)Link, url), true)
+ );
+
+ new OPDSBookItemFullInfoLoader(*this, request, listener);
+}
+
+std::vector<shared_ptr<NetworkItem> > OPDSBookItem::getRelatedCatalogsItems() const {
+ std::vector<shared_ptr<NetworkItem> > items;
+ for (std::size_t i = 0; i < myRelatedInfos.size(); ++i) {
+ shared_ptr<RelatedUrlInfo> urlInfo = myRelatedInfos.at(i);
+ if (!urlInfo->MimeType->weakEquals(*ZLMimeType::APPLICATION_ATOM_XML)) {
+ continue;
+ //TODO implement items for loading link in browser
+ }
+ UrlInfoCollection urlByType = URLByType;
+ urlByType[URL_CATALOG] = urlInfo->Url;
+ OPDSCatalogItem *item = new OPDSCatalogItem(static_cast<const OPDSLink&>(Link), urlInfo->Title, std::string(), urlByType);
+ items.push_back(item);
+ }
+ return items;
+}
+
+std::string OPDSBookItem::getAnnotation(OPDSEntry &entry) {
+ //TODO implement ATOMContent support (and return content)
+ return entry.summary();
+}
+
+std::string OPDSBookItem::getDate(OPDSEntry &entry) {
+ std::string date;
+ if (!entry.dcIssued().isNull()) {
+ date = entry.dcIssued()->getDateTime(true);
+ }
+ return date;
+}
+
+std::vector<NetworkBookItem::AuthorData> OPDSBookItem::getAuthors(OPDSEntry &entry) {
+ std::vector<NetworkBookItem::AuthorData> authors;
+ for (std::size_t i = 0; i < entry.authors().size(); ++i) {
+ ATOMAuthor &author = *(entry.authors()[i]);
+ NetworkBookItem::AuthorData authorData;
+ std::string name = author.name();
+ std::string lowerCased = ZLUnicodeUtil::toLower(name);
+ static const std::string authorPrefix = "author:";
+ std::size_t index = lowerCased.find(authorPrefix);
+ if (index != std::string::npos) {
+ name = name.substr(index + authorPrefix.size());
+ } else {
+ static const std::string authorsPrefix = "authors:";
+ index = lowerCased.find(authorsPrefix);
+ if (index != std::string::npos) {
+ name = name.substr(index + authorsPrefix.size());
+ }
+ }
+ index = name.find(',');
+ if (index != std::string::npos) {
+ std::string before = name.substr(0, index);
+ std::string after = name.substr(index + 1);
+ ZLUnicodeUtil::utf8Trim(before);
+ ZLUnicodeUtil::utf8Trim(after);
+ authorData.SortKey = before;
+ authorData.DisplayName = after + ' ' + before;
+ } else {
+ ZLUnicodeUtil::utf8Trim(name);
+ index = name.rfind(' ');
+ authorData.SortKey = name.substr(index + 1);
+ authorData.DisplayName = name;
+ }
+ authors.push_back(authorData);
+ }
+ return authors;
+}
+
+std::vector<std::string> OPDSBookItem::getTags(OPDSEntry &entry) {
+ std::vector<std::string> tags;
+ for (std::size_t i = 0; i < entry.categories().size(); ++i) {
+ ATOMCategory &category = *(entry.categories()[i]);
+ tags.push_back(category.label());
+ }
+ return tags;
+}
+
+NetworkItem::UrlInfoCollection OPDSBookItem::getUrls(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl) {
+ //TODO split urls and references in UrlInfoCollection, like it's implemented in FBReaderJ
+ NetworkItem::UrlInfoCollection urlMap;
+ for (std::size_t i = 0; i < entry.links().size(); ++i) {
+ ATOMLink &link = *(entry.links()[i]);
+ const std::string href = ZLNetworkUtil::url(baseUrl, link.href());
+ shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type());
+ const std::string &rel = networkLink.relation(link.rel(), link.type());
+ if (ZLStringUtil::stringStartsWith(rel, OPDSConstants::REL_IMAGE_PREFIX) || rel == OPDSConstants::REL_COVER) {
+ if (urlMap[NetworkItem::URL_COVER].empty() && ZLMimeType::isImage(type)) {
+ urlMap[NetworkItem::URL_COVER] = href;
+ }
+ } else if (rel == OPDSConstants::REL_THUMBNAIL || rel == OPDSConstants::REL_IMAGE_THUMBNAIL) {
+ if (ZLMimeType::isImage(type)) {
+ urlMap[NetworkItem::URL_COVER] = href;
+ }
+ } else if (type->weakEquals(*ZLMimeType::APPLICATION_ATOM_XML) &&
+ rel == ATOMConstants::REL_ALTERNATE &&
+ type->getParameter("type") == "entry") {
+ urlMap[NetworkItem::URL_SINGLE_ENTRY] = href;
+ }
+ }
+ return urlMap;
+}
+
+OPDSBookItem::RelatedUrlsList OPDSBookItem::getRelatedUrls(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl) {
+ OPDSBookItem::RelatedUrlsList relatedUrlList;
+ for (std::size_t i = 0; i < entry.links().size(); ++i) {
+ ATOMLink &link = *(entry.links()[i]);
+ const std::string href = ZLNetworkUtil::url(baseUrl, link.href());
+ shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type());
+ const std::string &rel = networkLink.relation(link.rel(), link.type());
+ if (rel == ATOMConstants::REL_RELATED) {
+ relatedUrlList.push_back(new RelatedUrlInfo(link.title(), type, href));
+ }
+ }
+ return relatedUrlList;
+}
+
+std::vector<shared_ptr<BookReference> > OPDSBookItem::getReferences(const OPDSLink &networkLink, OPDSEntry &entry, std::string baseUrl) {
+ //TODO split urls and references in UrlInfoCollection, like it's implemented in FBReaderJ
+ std::vector<shared_ptr<BookReference> > references;
+ for (std::size_t i = 0; i < entry.links().size(); ++i) {
+ ATOMLink &link = *(entry.links()[i]);
+ const std::string href = ZLNetworkUtil::url(baseUrl, link.href());
+ shared_ptr<ZLMimeType> type = ZLMimeType::get(link.type());
+ const std::string &rel = networkLink.relation(link.rel(), link.type());
+ const BookReference::Type referenceType = typeByRelation(rel);
+ if (referenceType == BookReference::BUY) {
+ std::string price = BuyBookReference::price(
+ link.userData(OPDSXMLParser::KEY_PRICE),
+ link.userData(OPDSXMLParser::KEY_CURRENCY)
+ );
+ if (price.empty()) {
+ price = BuyBookReference::price(
+ entry.userData(OPDSXMLParser::KEY_PRICE),
+ entry.userData(OPDSXMLParser::KEY_CURRENCY)
+ );
+ }
+ if (type == ZLMimeType::TEXT_HTML) {
+ references.push_back(new BuyBookReference(
+ href, BookReference::NONE, BookReference::BUY_IN_BROWSER, price
+ ));
+ } else {
+ BookReference::Format format = formatByZLMimeType(link.userData(OPDSXMLParser::KEY_FORMAT));
+ if (format != BookReference::NONE) {
+ references.push_back(new BuyBookReference(
+ href, format, BookReference::BUY, price
+ ));
+ }
+ }
+ } else if (referenceType != BookReference::UNKNOWN) {
+ BookReference::Format format = formatByZLMimeType(link.type());
+ if (format != BookReference::NONE) {
+ references.push_back(new BookReference(href, format, referenceType));
+ }
+ }
+ }
+ return references;
+}
+
+BookReference::Format OPDSBookItem::formatByZLMimeType(const std::string &mimeType) {
+ shared_ptr<ZLMimeType> type = ZLMimeType::get(mimeType);
+ if (type == ZLMimeType::APPLICATION_FB2_ZIP) {
+ return BookReference::FB2_ZIP;
+ } else if (type == ZLMimeType::APPLICATION_EPUB_ZIP) {
+ return BookReference::EPUB;
+ } else if (type == ZLMimeType::APPLICATION_MOBIPOCKET_EBOOK) {
+ return BookReference::MOBIPOCKET;
+ }
+ return BookReference::NONE;
+}
+
+BookReference::Type OPDSBookItem::typeByRelation(const std::string &rel) {
+ if (rel == OPDSConstants::REL_ACQUISITION || rel == OPDSConstants::REL_ACQUISITION_OPEN || rel.empty()) {
+ return BookReference::DOWNLOAD_FULL;
+ } else if (rel == OPDSConstants::REL_ACQUISITION_SAMPLE) {
+ return BookReference::DOWNLOAD_DEMO;
+ } else if (rel == OPDSConstants::REL_ACQUISITION_CONDITIONAL) {
+ return BookReference::DOWNLOAD_FULL_CONDITIONAL;
+ } else if (rel == OPDSConstants::REL_ACQUISITION_SAMPLE_OR_FULL) {
+ return BookReference::DOWNLOAD_FULL_OR_DEMO;
+ } else if (rel == OPDSConstants::REL_ACQUISITION_BUY) {
+ return BookReference::BUY;
+ } else {
+ return BookReference::UNKNOWN;
+ }
+}
+
+OPDSBookItem::FullEntryReader::FullEntryReader(OPDSBookItem &item, const OPDSLink &link, std::string url) :
+ myItem(item), myLink(link), myUrl(url) {
+}
+
+void OPDSBookItem::FullEntryReader::processFeedEntry(shared_ptr<OPDSEntry> entry) {
+ NetworkItem::UrlInfoCollection urlMap = OPDSBookItem::getUrls(myLink, *entry, myUrl);
+ std::vector<shared_ptr<BookReference> > references = OPDSBookItem::getReferences(myLink, *entry, myUrl);
+ for (NetworkItem::UrlInfoCollection::iterator it = urlMap.begin(); it != urlMap.end(); ++it) {
+ myItem.URLByType[(*it).first] = (*it).second;
+ }
+ myItem.updateReferences(references);
+ std::string summary = OPDSBookItem::getAnnotation(*entry);
+ if (!summary.empty()) {
+ myItem.Summary = summary;
+ }
+ myItem.myRelatedInfos = OPDSBookItem::getRelatedUrls(myLink, *entry, myUrl);
+}
+
+void OPDSBookItem::FullEntryReader::processFeedStart() {
+}
+
+void OPDSBookItem::FullEntryReader::processFeedMetadata(shared_ptr<OPDSFeedMetadata> /*feed*/) {
+}
+
+void OPDSBookItem::FullEntryReader::processFeedEnd() {
+}
+
+OPDSBookItem::RelatedUrlInfo::RelatedUrlInfo(const std::string &title, shared_ptr<ZLMimeType> mimeType, const std::string url) :
+ Title(title), MimeType(mimeType), Url(url) { }
+
+