Calibre OPDS (and HTML) PHP Server : web-based light alternative to Calibre content server / Calibre2OPDS to serve ebooks (epub, mobi, pdf, ...) http://blog.slucas.fr/en/oss/calibre-opds-php-server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

OPDS_renderer.php 13KB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
11 years ago
11 years ago
12 years ago
12 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <?php
  2. /**
  3. * COPS (Calibre OPDS PHP Server) class file
  4. *
  5. * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
  6. * @author Sbastien Lucas <sebastien@slucas.fr>
  7. */
  8. require_once ("base.php");
  9. class OPDSRenderer
  10. {
  11. private $xmlStream = NULL;
  12. private $updated = NULL;
  13. private function getUpdatedTime () {
  14. if (is_null ($this->updated)) {
  15. $this->updated = time();
  16. }
  17. return date (DATE_ATOM, $this->updated);
  18. }
  19. private function getXmlStream () {
  20. if (is_null ($this->xmlStream)) {
  21. $this->xmlStream = new XMLWriter();
  22. $this->xmlStream->openMemory();
  23. $this->xmlStream->setIndent (true);
  24. }
  25. return $this->xmlStream;
  26. }
  27. public function getOpenSearch () {
  28. global $config;
  29. $xml = new XMLWriter ();
  30. $xml->openMemory ();
  31. $xml->setIndent (true);
  32. $xml->startDocument('1.0','UTF-8');
  33. $xml->startElement ("OpenSearchDescription");
  34. $xml->writeAttribute ("xmlns", "http://a9.com/-/spec/opensearch/1.1/");
  35. $xml->startElement ("ShortName");
  36. $xml->text ("My catalog");
  37. $xml->endElement ();
  38. $xml->startElement ("Description");
  39. $xml->text ("Search for ebooks");
  40. $xml->endElement ();
  41. $xml->startElement ("InputEncoding");
  42. $xml->text ("UTF-8");
  43. $xml->endElement ();
  44. $xml->startElement ("OutputEncoding");
  45. $xml->text ("UTF-8");
  46. $xml->endElement ();
  47. $xml->startElement ("Image");
  48. $xml->writeAttribute ("type", "image/x-icon");
  49. $xml->writeAttribute ("width", "16");
  50. $xml->writeAttribute ("height", "16");
  51. $xml->text ($config['cops_icon']);
  52. $xml->endElement ();
  53. $xml->startElement ("Url");
  54. $xml->writeAttribute ("type", 'application/atom+xml');
  55. $urlparam = "?query={searchTerms}";
  56. if (!is_null (GetUrlParam (DB))) $urlparam = addURLParameter ($urlparam, DB, GetUrlParam (DB));
  57. $urlparam = str_replace ("%7B", "{", $urlparam);
  58. $urlparam = str_replace ("%7D", "}", $urlparam);
  59. $xml->writeAttribute ("template", $config['cops_full_url'] . 'feed.php' . $urlparam);
  60. $xml->endElement ();
  61. $xml->startElement ("Query");
  62. $xml->writeAttribute ("role", "example");
  63. $xml->writeAttribute ("searchTerms", "robot");
  64. $xml->endElement ();
  65. $xml->endElement ();
  66. $xml->endDocument();
  67. return $xml->outputMemory(true);
  68. }
  69. private function startXmlDocument ($page) {
  70. global $config;
  71. self::getXmlStream ()->startDocument('1.0','UTF-8');
  72. self::getXmlStream ()->startElement ("feed");
  73. self::getXmlStream ()->writeAttribute ("xmlns", "http://www.w3.org/2005/Atom");
  74. self::getXmlStream ()->writeAttribute ("xmlns:xhtml", "http://www.w3.org/1999/xhtml");
  75. self::getXmlStream ()->writeAttribute ("xmlns:opds", "http://opds-spec.org/2010/catalog");
  76. self::getXmlStream ()->writeAttribute ("xmlns:opensearch", "http://a9.com/-/spec/opensearch/1.1/");
  77. self::getXmlStream ()->writeAttribute ("xmlns:dcterms", "http://purl.org/dc/terms/");
  78. self::getXmlStream ()->startElement ("title");
  79. self::getXmlStream ()->text ($page->title);
  80. self::getXmlStream ()->endElement ();
  81. if ($page->subtitle != "")
  82. {
  83. self::getXmlStream ()->startElement ("subtitle");
  84. self::getXmlStream ()->text ($page->subtitle);
  85. self::getXmlStream ()->endElement ();
  86. }
  87. self::getXmlStream ()->startElement ("id");
  88. if ($page->idPage)
  89. {
  90. $idPage = $page->idPage;
  91. if (!is_null (GetUrlParam (DB))) $idPage = str_replace ("cops:", "cops:" . GetUrlParam (DB) . ":", $idPage);
  92. self::getXmlStream ()->text ($idPage);
  93. }
  94. else
  95. {
  96. self::getXmlStream ()->text ($_SERVER['REQUEST_URI']);
  97. }
  98. self::getXmlStream ()->endElement ();
  99. self::getXmlStream ()->startElement ("updated");
  100. self::getXmlStream ()->text (self::getUpdatedTime ());
  101. self::getXmlStream ()->endElement ();
  102. self::getXmlStream ()->startElement ("icon");
  103. self::getXmlStream ()->text ($page->favicon);
  104. self::getXmlStream ()->endElement ();
  105. self::getXmlStream ()->startElement ("author");
  106. self::getXmlStream ()->startElement ("name");
  107. self::getXmlStream ()->text ($page->authorName);
  108. self::getXmlStream ()->endElement ();
  109. self::getXmlStream ()->startElement ("uri");
  110. self::getXmlStream ()->text ($page->authorUri);
  111. self::getXmlStream ()->endElement ();
  112. self::getXmlStream ()->startElement ("email");
  113. self::getXmlStream ()->text ($page->authorEmail);
  114. self::getXmlStream ()->endElement ();
  115. self::getXmlStream ()->endElement ();
  116. $link = new LinkNavigation ("", "start", "Home");
  117. self::renderLink ($link);
  118. $link = new LinkNavigation ("?" . getQueryString (), "self");
  119. self::renderLink ($link);
  120. $urlparam = "?";
  121. if (!is_null (GetUrlParam (DB))) $urlparam = addURLParameter ($urlparam, DB, GetUrlParam (DB));
  122. if ($config['cops_generate_invalid_opds_stream'] == 0 || preg_match("/(MantanoReader|FBReader)/", $_SERVER['HTTP_USER_AGENT'])) {
  123. // Good and compliant way of handling search
  124. $urlparam = addURLParameter ($urlparam, "page", Base::PAGE_OPENSEARCH);
  125. $link = new Link ("feed.php" . $urlparam, "application/opensearchdescription+xml", "search", "Search here");
  126. }
  127. else
  128. {
  129. // Bad way, will be removed when OPDS client are fixed
  130. $urlparam = addURLParameter ($urlparam, "query", "{searchTerms}");
  131. $urlparam = str_replace ("%7B", "{", $urlparam);
  132. $urlparam = str_replace ("%7D", "}", $urlparam);
  133. $link = new Link ($config['cops_full_url'] . 'feed.php' . $urlparam, "application/atom+xml", "search", "Search here");
  134. }
  135. self::renderLink ($link);
  136. if ($page->containsBook () && !is_null ($config['cops_books_filter']) && count ($config['cops_books_filter']) > 0) {
  137. $Urlfilter = getURLParam ("tag", "");
  138. foreach ($config['cops_books_filter'] as $lib => $filter) {
  139. $link = new LinkFacet ("?" . addURLParameter (getQueryString (), "tag", $filter), $lib, localize ("tagword.title"), $filter == $Urlfilter);
  140. self::renderLink ($link);
  141. }
  142. }
  143. }
  144. private function endXmlDocument () {
  145. self::getXmlStream ()->endElement ();
  146. self::getXmlStream ()->endDocument ();
  147. return self::getXmlStream ()->outputMemory(true);
  148. }
  149. private function renderLink ($link) {
  150. self::getXmlStream ()->startElement ("link");
  151. self::getXmlStream ()->writeAttribute ("href", $link->href);
  152. self::getXmlStream ()->writeAttribute ("type", $link->type);
  153. if (!is_null ($link->rel)) {
  154. self::getXmlStream ()->writeAttribute ("rel", $link->rel);
  155. }
  156. if (!is_null ($link->title)) {
  157. self::getXmlStream ()->writeAttribute ("title", $link->title);
  158. }
  159. if (!is_null ($link->facetGroup)) {
  160. self::getXmlStream ()->writeAttribute ("opds:facetGroup", $link->facetGroup);
  161. }
  162. if ($link->activeFacet) {
  163. self::getXmlStream ()->writeAttribute ("opds:activeFacet", "true");
  164. }
  165. self::getXmlStream ()->endElement ();
  166. }
  167. private function getPublicationDate($book) {
  168. $dateYmd = substr($book->pubdate, 0, 10);
  169. $pubdate = \DateTime::createFromFormat('Y-m-d', $dateYmd);
  170. if ($pubdate === false ||
  171. $pubdate->format ("Y") == "0101" ||
  172. $pubdate->format ("Y") == "0100") {
  173. return "";
  174. }
  175. return $pubdate->format("Y-m-d");
  176. }
  177. private function renderEntry ($entry) {
  178. self::getXmlStream ()->startElement ("title");
  179. self::getXmlStream ()->text ($entry->title);
  180. self::getXmlStream ()->endElement ();
  181. self::getXmlStream ()->startElement ("updated");
  182. self::getXmlStream ()->text (self::getUpdatedTime ());
  183. self::getXmlStream ()->endElement ();
  184. self::getXmlStream ()->startElement ("id");
  185. self::getXmlStream ()->text ($entry->id);
  186. self::getXmlStream ()->endElement ();
  187. self::getXmlStream ()->startElement ("content");
  188. self::getXmlStream ()->writeAttribute ("type", $entry->contentType);
  189. if ($entry->contentType == "text") {
  190. self::getXmlStream ()->text ($entry->content);
  191. } else {
  192. self::getXmlStream ()->writeRaw ($entry->content);
  193. }
  194. self::getXmlStream ()->endElement ();
  195. foreach ($entry->linkArray as $link) {
  196. self::renderLink ($link);
  197. }
  198. if (get_class ($entry) != "EntryBook") {
  199. return;
  200. }
  201. foreach ($entry->book->getAuthors () as $author) {
  202. self::getXmlStream ()->startElement ("author");
  203. self::getXmlStream ()->startElement ("name");
  204. self::getXmlStream ()->text ($author->name);
  205. self::getXmlStream ()->endElement ();
  206. self::getXmlStream ()->startElement ("uri");
  207. self::getXmlStream ()->text ("feed.php" . $author->getUri ());
  208. self::getXmlStream ()->endElement ();
  209. self::getXmlStream ()->endElement ();
  210. }
  211. foreach ($entry->book->getTags () as $category) {
  212. self::getXmlStream ()->startElement ("category");
  213. self::getXmlStream ()->writeAttribute ("term", $category->name);
  214. self::getXmlStream ()->writeAttribute ("label", $category->name);
  215. self::getXmlStream ()->endElement ();
  216. }
  217. if ($entry->book->getPubDate () != "") {
  218. self::getXmlStream ()->startElement ("dcterms:issued");
  219. self::getXmlStream ()->text (self::getPublicationDate($entry->book));
  220. self::getXmlStream ()->endElement ();
  221. self::getXmlStream ()->startElement ("published");
  222. self::getXmlStream ()->text (self::getPublicationDate($entry->book) . "T08:08:08Z");
  223. self::getXmlStream ()->endElement ();
  224. }
  225. $lang = $entry->book->getLanguages ();
  226. if (!empty ($lang)) {
  227. self::getXmlStream ()->startElement ("dcterms:language");
  228. self::getXmlStream ()->text ($lang);
  229. self::getXmlStream ()->endElement ();
  230. }
  231. }
  232. public function render ($page) {
  233. global $config;
  234. self::startXmlDocument ($page);
  235. if ($page->isPaginated ())
  236. {
  237. self::getXmlStream ()->startElement ("opensearch:totalResults");
  238. self::getXmlStream ()->text ($page->totalNumber);
  239. self::getXmlStream ()->endElement ();
  240. self::getXmlStream ()->startElement ("opensearch:itemsPerPage");
  241. self::getXmlStream ()->text ($config['cops_max_item_per_page']);
  242. self::getXmlStream ()->endElement ();
  243. self::getXmlStream ()->startElement ("opensearch:startIndex");
  244. self::getXmlStream ()->text (($page->n - 1) * $config['cops_max_item_per_page'] + 1);
  245. self::getXmlStream ()->endElement ();
  246. $prevLink = $page->getPrevLink ();
  247. $nextLink = $page->getNextLink ();
  248. if (!is_null ($prevLink)) {
  249. self::renderLink ($prevLink);
  250. }
  251. if (!is_null ($nextLink)) {
  252. self::renderLink ($nextLink);
  253. }
  254. }
  255. foreach ($page->entryArray as $entry) {
  256. self::getXmlStream ()->startElement ("entry");
  257. self::renderEntry ($entry);
  258. self::getXmlStream ()->endElement ();
  259. }
  260. return self::endXmlDocument ();
  261. }
  262. }