From b25ed5c0528dc69fbce2b7b15138b66c710fc763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lucas?= Date: Mon, 28 May 2012 07:05:05 +0200 Subject: [PATCH] Add v0.0.2 --- CHANGELOG | 6 + OPDS_renderer.php | 181 ++++++++++++++++++++++++++++ README | 10 +- author.php | 9 +- base.php | 287 +++++++++++++++++++-------------------------- book.php | 71 +++++++---- config_default.php | 9 ++ feed.php | 77 ++---------- fetch.php | 14 +-- serie.php | 9 +- 10 files changed, 403 insertions(+), 270 deletions(-) create mode 100644 OPDS_renderer.php diff --git a/CHANGELOG b/CHANGELOG index 0c416b3..0ab6524 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,2 +1,8 @@ +0.0.2 - 20120411 + * Add support for MOBI and PDF + * Major refactoring to prepare something nice for the future ;) + * Add a config item to make use of X-Sendfile instead of X-Accel-Redirect + if needed + 0.0.1 - 20120302 * First public release diff --git a/OPDS_renderer.php b/OPDS_renderer.php new file mode 100644 index 0000000..da082e5 --- /dev/null +++ b/OPDS_renderer.php @@ -0,0 +1,181 @@ + + */ + +require_once ("base.php"); + +class OPDSRenderer +{ + const PAGE_OPENSEARCH = "8"; + + private $xmlStream = NULL; + private $updated = NULL; + + private function getUpdatedTime () { + if (is_null ($this->updated)) { + $this->updated = time(); + } + return date (DATE_ATOM, $this->updated); + } + + private function getXmlStream () { + if (is_null ($this->xmlStream)) { + $this->xmlStream = new XMLWriter(); + $this->xmlStream->openMemory(); + $this->xmlStream->setIndent (true); + } + return $this->xmlStream; + } + + public function getOpenSearch () { + $xml = new XMLWriter (); + $xml->openMemory (); + $xml->setIndent (true); + $xml->startDocument('1.0','UTF-8'); + $xml->startElement ("OpenSearchDescription"); + $xml->startElement ("ShortName"); + $xml->text ("My catalog"); + $xml->endElement (); + $xml->startElement ("InputEncoding"); + $xml->text ("UTF-8"); + $xml->endElement (); + $xml->startElement ("OutputEncoding"); + $xml->text ("UTF-8"); + $xml->endElement (); + $xml->startElement ("Image"); + $xml->text ("favicon.ico"); + $xml->endElement (); + $xml->startElement ("Url"); + $xml->writeAttribute ("type", 'application/atom+xml'); + $xml->writeAttribute ("template", 'feed.php?page=' . self::PAGE_OPENSEARCH_QUERY . '&query={searchTerms}'); + $xml->endElement (); + $xml->endElement (); + $xml->endDocument(); + return $xml->outputMemory(true); + } + + private function startXmlDocument ($title) { + self::getXmlStream ()->startDocument('1.0','UTF-8'); + self::getXmlStream ()->startElement ("feed"); + self::getXmlStream ()->writeAttribute ("xmlns", "http://www.w3.org/2005/Atom"); + self::getXmlStream ()->writeAttribute ("xmlns:xhtml", "http://www.w3.org/1999/xhtml"); + self::getXmlStream ()->writeAttribute ("xmlns:opds", "http://opds-spec.org/2010/catalog"); + self::getXmlStream ()->writeAttribute ("xmlns:opensearch", "http://a9.com/-/spec/opensearch/1.1/"); + self::getXmlStream ()->writeAttribute ("xmlns:dcterms", "http://purl.org/dc/terms/"); + self::getXmlStream ()->startElement ("title"); + self::getXmlStream ()->text ($title); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("id"); + self::getXmlStream ()->text ($_SERVER['REQUEST_URI']); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("updated"); + self::getXmlStream ()->text (self::getUpdatedTime ()); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("icon"); + self::getXmlStream ()->text ("favicon.ico"); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("author"); + self::getXmlStream ()->startElement ("name"); + self::getXmlStream ()->text (utf8_encode ("Sébastien Lucas")); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("uri"); + self::getXmlStream ()->text ("http://blog.slucas.fr"); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("email"); + self::getXmlStream ()->text ("sebastien@slucas.fr"); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->endElement (); + $link = new LinkNavigation ("feed.php", "start", "Home"); + self::renderLink ($link); + $link = new LinkNavigation ($_SERVER['REQUEST_URI'], "self"); + self::renderLink ($link); + $link = new Link ("feed.php?page=" . self::PAGE_OPENSEARCH, "application/opensearchdescription+xml", "search", "Search here"); + self::renderLink ($link); + } + + private function endXmlDocument () { + self::getXmlStream ()->endElement (); + self::getXmlStream ()->endDocument (); + return self::getXmlStream ()->outputMemory(true); + } + + private function renderLink ($link) { + self::getXmlStream ()->startElement ("link"); + self::getXmlStream ()->writeAttribute ("href", $link->href); + self::getXmlStream ()->writeAttribute ("type", $link->type); + if (!is_null ($link->rel)) { + self::getXmlStream ()->writeAttribute ("rel", $link->rel); + } + if (!is_null ($link->title)) { + self::getXmlStream ()->writeAttribute ("title", $link->title); + } + self::getXmlStream ()->endElement (); + } + + + private function renderEntry ($entry) { + self::getXmlStream ()->startElement ("title"); + self::getXmlStream ()->text ($entry->title); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("updated"); + self::getXmlStream ()->text (self::getUpdatedTime ()); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("id"); + self::getXmlStream ()->text ($entry->id); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("content"); + self::getXmlStream ()->writeAttribute ("type", $entry->contentType); + if ($entry->contentType == "text") { + self::getXmlStream ()->text ($entry->content); + } else { + self::getXmlStream ()->writeRaw ($entry->content); + } + self::getXmlStream ()->endElement (); + foreach ($entry->linkArray as $link) { + self::renderLink ($link); + } + + if (get_class ($entry) != "EntryBook") { + return; + } + + foreach ($entry->book->getAuthors () as $author) { + self::getXmlStream ()->startElement ("author"); + self::getXmlStream ()->startElement ("name"); + self::getXmlStream ()->text ($author->name); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->startElement ("uri"); + self::getXmlStream ()->text ($author->getUri ()); + self::getXmlStream ()->endElement (); + self::getXmlStream ()->endElement (); + } + foreach ($entry->book->getTags () as $category) { + self::getXmlStream ()->startElement ("category"); + self::getXmlStream ()->writeAttribute ("term", $category); + self::getXmlStream ()->writeAttribute ("label", $category); + self::getXmlStream ()->endElement (); + } + if (!is_null ($entry->book->pubdate)) { + self::getXmlStream ()->startElement ("dcterms:issued"); + self::getXmlStream ()->text (date ("Y-m-d", $entry->book->pubdate)); + self::getXmlStream ()->endElement (); + } + + } + + public function render ($page) { + self::startXmlDocument ($page->title); + foreach ($page->entryArray as $entry) { + self::getXmlStream ()->startElement ("entry"); + self::renderEntry ($entry); + self::getXmlStream ()->endElement (); + } + return self::endXmlDocument (); + } +} + +?> \ No newline at end of file diff --git a/README b/README index bc0ebbd..7571ce6 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ http://opds-validator.appspot.com/ = Why ? = In my opinion Calibre is a marvelous tool but is too big and has too much -dependancies to be used for its content server. +dependencies to be used for its content server. That's the main reason why I coded this OPDS server. I needed a simple tool to be installed on a small server (Seagate Dockstar in my case). @@ -17,7 +17,7 @@ I initially thought of Calibre2OPDS but as it generate static file no search was possible. So COPS's main advantages are : - * No need for many dependancies. + * No need for many dependencies. * No need for a lot of CPU or RAM. * Not much code. * Search is available. @@ -77,8 +77,8 @@ application/epub+zip epub; = Known problems = * Only tested with Nginx. - * Contain Nginx specific code. - * Only works with EPUB (not MOBI or PDF). + * Contain Nginx specific code (that could be changed with a config item). + * Only works with EPUB, MOBI and PDF. * certainly many many more. = Disclaimer = @@ -88,6 +88,8 @@ with Apache or any other web server. On the OPDS client side I mainly tested with FBReader and Aldiko on Android. +It also seems to work with Stanza. + = Copyright & License = COPS - 2012 (c) Sébastien Lucas diff --git a/author.php b/author.php index 686a03b..e8aa0bb 100644 --- a/author.php +++ b/author.php @@ -31,9 +31,10 @@ class Author extends Base { public static function getCount() { $nAuthors = parent::getDb ()->query('select count(*) from authors')->fetchColumn(); - parent::addEntryClass ( new Entry ("Authors", self::ALL_AUTHORS_ID, + $entry = new Entry ("Authors", self::ALL_AUTHORS_ID, "Alphabetical index of the $nAuthors authors", "text", - array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_AUTHORS)))); + array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_AUTHORS))); + return $entry; } public static function getAllAuthors() { @@ -42,13 +43,15 @@ from authors, books_authors_link where author = authors.id group by authors.id, authors.name, authors.sort order by sort'); + $entryArray = array(); while ($post = $result->fetchObject ()) { $author = new Author ($post->id, $post->sort); - parent::addEntryClass ( new Entry ($post->sort, $author->getEntryId (), + array_push ($entryArray, new Entry ($post->sort, $author->getEntryId (), "$post->count books", "text", array ( new LinkNavigation ($author->getUri ())))); } + return $entryArray; } public static function getAuthorName ($authorId) { diff --git a/base.php b/base.php index 58e82ad..8c305bf 100644 --- a/base.php +++ b/base.php @@ -6,6 +6,12 @@ * @author Sébastien Lucas */ +function getURLParam ($name, $default = NULL) { + if (!empty ($_GET) && isset($_GET[$name])) { + return $_GET[$name]; + } + return $default; +} class Link { @@ -20,19 +26,6 @@ class Link $this->rel = $prel; $this->title = $ptitle; } - - public function render ($xml) { - $xml->startElement ("link"); - $xml->writeAttribute ("href", $this->href); - $xml->writeAttribute ("type", $this->type); - if (!is_null ($this->rel)) { - $xml->writeAttribute ("rel", $this->rel); - } - if (!is_null ($this->title)) { - $xml->writeAttribute ("title", $this->title); - } - $xml->endElement (); - } } class LinkNavigation extends Link @@ -72,36 +65,6 @@ class Entry $this->contentType = $pcontentType; $this->linkArray = $plinkArray; } - - public function renderContent ($xml) { - $xml->startElement ("title"); - $xml->text ($this->title); - $xml->endElement (); - $xml->startElement ("updated"); - $xml->text (self::getUpdatedTime ()); - $xml->endElement (); - $xml->startElement ("id"); - $xml->text ($this->id); - $xml->endElement (); - $xml->startElement ("content"); - $xml->writeAttribute ("type", $this->contentType); - if ($this->contentType == "text") { - $xml->text ($this->content); - } else { - $xml->writeRaw ($this->content); - } - $xml->endElement (); - foreach ($this->linkArray as $link) { - $link->render ($xml); - } - } - - public function render ($xml) { - $xml->startElement ("entry"); - self::renderContent ($xml); - $xml->endElement (); - } - } class EntryBook extends Entry @@ -113,41 +76,129 @@ class EntryBook extends Entry $this->book = $pbook; $this->localUpdated = $pbook->timestamp; } +} + +class Page +{ + public $title; + public $idPage; + public $idGet; + public $query; + public $entryArray = array(); - public function renderContent ($xml) { - parent::renderContent ($xml); - foreach ($this->book->getAuthors () as $author) { - $xml->startElement ("author"); - $xml->startElement ("name"); - $xml->text ($author->name); - $xml->endElement (); - $xml->startElement ("uri"); - $xml->text ($author->getUri ()); - $xml->endElement (); - $xml->endElement (); - } - foreach ($this->book->getTags () as $category) { - $xml->startElement ("category"); - $xml->writeAttribute ("term", $category); - $xml->writeAttribute ("label", $category); - $xml->endElement (); - } - if (!is_null ($this->book->pubdate)) { - $xml->startElement ("dcterms:issued"); - $xml->text (date ("Y-m-d", $this->book->pubdate)); - $xml->endElement (); + public static function getPage ($pageId, $id, $query) + { + switch ($pageId) { + case Base::PAGE_ALL_AUTHORS : + return new PageAllAuthors ($id, $query); + case Base::PAGE_AUTHOR_DETAIL : + return new PageAuthorDetail ($id, $query); + case Base::PAGE_ALL_SERIES : + return new PageAllSeries ($id, $query); + case Base::PAGE_ALL_BOOKS : + return new PageAllBooks ($id, $query); + case Base::PAGE_ALL_BOOKS_LETTER: + return new PageAllBooksLetter ($id, $query); + case Base::PAGE_ALL_RECENT_BOOKS : + return new PageRecentBooks ($id, $query); + case Base::PAGE_SERIE_DETAIL : + return new PageSerieDetail ($id, $query); + case Base::PAGE_OPENSEARCH_QUERY : + return new PageQueryResult ($id, $query); + break; + default: + return new Page ($id, $query); } } - /* Polymorphism is strange with PHP */ - public function render ($xml) { - $xml->startElement ("entry"); - self::renderContent ($xml); - $xml->endElement (); + public function __construct($pid, $pquery) { + $this->idGet = $pid; + $this->query = $pquery; + } + + public function InitializeContent () + { + global $config; + $this->title = $config['cops_title_default']; + array_push ($this->entryArray, Author::getCount()); + array_push ($this->entryArray, Serie::getCount()); + $this->entryArray = array_merge ($this->entryArray, Book::getCount()); } } +class PageAllAuthors extends Page +{ + public function InitializeContent () + { + $this->title = "All authors"; + $this->entryArray = Author::getAllAuthors(); + } +} + +class PageAuthorDetail extends Page +{ + public function InitializeContent () + { + $this->title = Author::getAuthorName ($this->idGet); + $this->entryArray = Book::getBooksByAuthor ($this->idGet); + } +} + +class PageAllSeries extends Page +{ + public function InitializeContent () + { + $this->title = "All series"; + $this->entryArray = Serie::getAllSeries(); + } +} + +class PageSerieDetail extends Page +{ + public function InitializeContent () + { + $this->title = "Series : " . Serie::getSerieById ($this->idGet)->name; + $this->entryArray = Book::getBooksBySeries ($this->idGet); + } +} + +class PageAllBooks extends Page +{ + public function InitializeContent () + { + $this->title = "All books by starting letter"; + $this->entryArray = Book::getAllBooks (); + } +} + +class PageAllBooksLetter extends Page +{ + public function InitializeContent () + { + $this->title = "All books starting by " . $this->idGet; + $this->entryArray = Book::getBooksByStartingLetter ($this->idGet); + } +} + +class PageRecentBooks extends Page +{ + public function InitializeContent () + { + $this->title = "Most recent books"; + $this->entryArray = Book::getAllRecentBooks (); + } +} + +class PageQueryResult extends Page +{ + public function InitializeContent () + { + $this->title = "Search result for query <" . $this->query . ">"; + $this->entryArray = Book::getBooksByQuery ($this->query); + } +} + abstract class Base { const PAGE_INDEX = "index"; @@ -164,15 +215,6 @@ abstract class Base const COMPATIBILITY_XML_ALDIKO = "aldiko"; private static $db = NULL; - private static $xmlStream = NULL; - private static $updated = NULL; - - public static function getUpdatedTime () { - if (is_null (self::$updated)) { - self::$updated = time(); - } - return date (DATE_ATOM, self::$updated); - } public static function getDb () { global $config; @@ -185,93 +227,6 @@ abstract class Base } } return self::$db; - } - - public static function getXmlStream () { - if (is_null (self::$xmlStream)) { - self::$xmlStream = new XMLWriter(); - self::$xmlStream->openMemory(); - self::$xmlStream->setIndent (true); - } - return self::$xmlStream; - } - - public static function getOpenSearch () { - $xml = new XMLWriter (); - $xml->openMemory (); - $xml->setIndent (true); - $xml->startDocument('1.0','UTF-8'); - $xml->startElement ("OpenSearchDescription"); - $xml->startElement ("ShortName"); - $xml->text ("My catalog"); - $xml->endElement (); - $xml->startElement ("InputEncoding"); - $xml->text ("UTF-8"); - $xml->endElement (); - $xml->startElement ("OutputEncoding"); - $xml->text ("UTF-8"); - $xml->endElement (); - $xml->startElement ("Image"); - $xml->text ("favicon.ico"); - $xml->endElement (); - $xml->startElement ("Url"); - $xml->writeAttribute ("type", 'application/atom+xml'); - $xml->writeAttribute ("template", 'feed.php?page=' . self::PAGE_OPENSEARCH_QUERY . '&query={searchTerms}'); - $xml->endElement (); - $xml->endElement (); - $xml->endDocument(); - return $xml->outputMemory(true); - } - - public static function startXmlDocument ($title) { - self::getXmlStream ()->startDocument('1.0','UTF-8'); - self::getXmlStream ()->startElement ("feed"); - self::getXmlStream ()->writeAttribute ("xmlns", "http://www.w3.org/2005/Atom"); - self::getXmlStream ()->writeAttribute ("xmlns:xhtml", "http://www.w3.org/1999/xhtml"); - self::getXmlStream ()->writeAttribute ("xmlns:opds", "http://opds-spec.org/2010/catalog"); - self::getXmlStream ()->writeAttribute ("xmlns:opensearch", "http://a9.com/-/spec/opensearch/1.1/"); - self::getXmlStream ()->writeAttribute ("xmlns:dcterms", "http://purl.org/dc/terms/"); - self::getXmlStream ()->startElement ("title"); - self::getXmlStream ()->text ($title); - self::getXmlStream ()->endElement (); - self::getXmlStream ()->startElement ("id"); - self::getXmlStream ()->text ($_SERVER['REQUEST_URI']); - self::getXmlStream ()->endElement (); - self::getXmlStream ()->startElement ("updated"); - self::getXmlStream ()->text (self::getUpdatedTime ()); - self::getXmlStream ()->endElement (); - self::getXmlStream ()->startElement ("icon"); - self::getXmlStream ()->text ("favicon.ico"); - self::getXmlStream ()->endElement (); - self::getXmlStream ()->startElement ("author"); - self::getXmlStream ()->startElement ("name"); - self::getXmlStream ()->text (utf8_encode ("Sébastien Lucas")); - self::getXmlStream ()->endElement (); - self::getXmlStream ()->startElement ("uri"); - self::getXmlStream ()->text ("http://blog.slucas.fr"); - self::getXmlStream ()->endElement (); - self::getXmlStream ()->startElement ("email"); - self::getXmlStream ()->text ("sebastien@slucas.fr"); - self::getXmlStream ()->endElement (); - self::getXmlStream ()->endElement (); - $link = new LinkNavigation ("feed.php", "start", "Home"); - $link->render (self::getXmlStream ()); - $link = new LinkNavigation ($_SERVER['REQUEST_URI'], "self"); - $link->render (self::getXmlStream ()); - $link = new Link ("feed.php?page=" . self::PAGE_OPENSEARCH, "application/opensearchdescription+xml", "search", "Search here"); - $link->render (self::getXmlStream ()); - $link = new LinkNavigation ("feed.php?page=7&id=9", "http://opds-spec.org/shelf", "Biblio"); - $link->render (self::getXmlStream ()); - } - - public static function addEntryClass ($entry) { - $entry->render (self::getXmlStream ()); - } - - public static function endXmlDocument () { - self::getXmlStream ()->endElement (); - self::getXmlStream ()->endDocument (); - return self::getXmlStream ()->outputMemory(true); } } ?> \ No newline at end of file diff --git a/book.php b/book.php index adb52d7..493ea42 100644 --- a/book.php +++ b/book.php @@ -7,6 +7,8 @@ */ require_once('base.php'); +require_once('serie.php'); +require_once('author.php'); class Book extends Base { const ALL_BOOKS_ID = "calibre:books"; @@ -23,6 +25,12 @@ class Book extends Base { public $authors = NULL; public $serie = NULL; public $tags = NULL; + public static $mimetypes = array( + 'epub' => 'application/epub+zip', + 'mobi' => 'application/x-mobipocket-ebook', + 'pdf' => 'application/pdf' + ); + public function __construct($pid, $ptitle, $ptimestamp, $ppubdate, $ppath, $pseriesIndex, $pcomment) { global $config; @@ -121,14 +129,17 @@ class Book extends Base { } array_push ($linkArray, new Link ("fetch.php?id=$this->id&width=50", "image/jpeg", "http://opds-spec.org/image/thumbnail")); } - if (preg_match ('/epub$/', $file)) { - if (preg_match ('/^\//', $config['calibre_directory'])) - { - array_push ($linkArray, new Link ("fetch.php?id=$this->id&type=epub", "application/epub+zip", "http://opds-spec.org/acquisition", "Download")); - } - else - { - array_push ($linkArray, new Link (rawurlencode ($this->path."/".$file), "application/epub+zip", "http://opds-spec.org/acquisition", "Download")); + foreach (self::$mimetypes as $ext => $mime) + { + if (preg_match ('/'. $ext .'$/', $file)) { + if (preg_match ('/^\//', $config['calibre_directory'])) + { + array_push ($linkArray, new Link ("fetch.php?id=$this->id&type=" . $ext, $mime, "http://opds-spec.org/acquisition", "Download")); + } + else + { + array_push ($linkArray, new Link (rawurlencode ($this->path."/".$file), $mime, "http://opds-spec.org/acquisition", "Download")); + } } } } @@ -148,21 +159,26 @@ class Book extends Base { public function getEntry () { - parent::addEntryClass (new EntryBook ($this->getTitle (), $this->getEntryId (), + return new EntryBook ($this->getTitle (), $this->getEntryId (), $this->getComment (), "text/html", - $this->getLinkArray (), $this)); + $this->getLinkArray (), $this); } public static function getCount() { + global $config; $nBooks = parent::getDb ()->query('select count(*) from books')->fetchColumn(); - parent::addEntryClass (new Entry ("Books", + $result = array(); + $entry = new Entry ("Books", self::ALL_BOOKS_ID, "Alphabetical index of the $nBooks books", "text", - array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_BOOKS)))); - parent::addEntryClass (new Entry ("Recents books", + array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_BOOKS))); + array_push ($result, $entry); + $entry = new Entry ("Recents books", self::ALL_RECENT_BOOKS_ID, - "Alphabetical index of the 50 most recent books", "text", - array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_RECENT_BOOKS)))); + "Alphabetical index of the " . $config['cops_recentbooks_limit'] . " most recent books", "text", + array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_RECENT_BOOKS))); + array_push ($result, $entry); + return $result; } public static function getBooksByAuthor($authorId) { @@ -171,12 +187,14 @@ from books_authors_link, books left outer join comments on comments.book = books where books_authors_link.book = books.id and author = ? order by pubdate'); + $entryArray = array(); $result->execute (array ($authorId)); while ($post = $result->fetchObject ()) { $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); - $book->getEntry (); + array_push ($entryArray, $book->getEntry ()); } + return $entryArray; } @@ -185,18 +203,21 @@ order by pubdate'); from books_series_link, books left outer join comments on comments.book = books.id where books_series_link.book = books.id and series = ? order by series_index'); + $entryArray = array(); $result->execute (array ($serieId)); while ($post = $result->fetchObject ()) { $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); - $book->getEntry (); + array_push ($entryArray, $book->getEntry ()); } + return $entryArray; } public static function getBookById($bookId) { $result = parent::getDb ()->prepare('select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index from books left outer join comments on book = books.id where books.id = ?'); + $entryArray = array(); $result->execute (array ($bookId)); while ($post = $result->fetchObject ()) { @@ -211,13 +232,15 @@ where books.id = ?'); from books left outer join comments on book = books.id where exists (select null from authors, books_authors_link where book = books.id and author = authors.id and authors.name like ?) or title like ?"); + $entryArray = array(); $queryLike = "%" . $query . "%"; $result->execute (array ($queryLike, $queryLike)); while ($post = $result->fetchObject ()) { $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); - $book->getEntry (); + array_push ($entryArray, $book->getEntry ()); } + return $entryArray; } public static function getAllBooks() { @@ -225,25 +248,29 @@ or title like ?"); from books group by substr (upper (sort), 1, 1) order by substr (upper (sort), 1, 1)"); + $entryArray = array(); while ($post = $result->fetchObject ()) { - parent::addEntryClass (new Entry ($post->title, "allbooks_" . $post->title, + array_push ($entryArray, new Entry ($post->title, "allbooks_" . $post->title, "$post->count books", "text", array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_BOOKS_LETTER."&id=".$post->title)))); } + return $entryArray; } public static function getBooksByStartingLetter($letter) { $result = parent::getDb ()->prepare('select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index from books left outer join comments on book = books.id where upper (books.sort) like ?'); + $entryArray = array(); $queryLike = $letter . "%"; $result->execute (array ($queryLike)); while ($post = $result->fetchObject ()) { $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); - $book->getEntry (); + array_push ($entryArray, $book->getEntry ()); } + return $entryArray; } @@ -252,11 +279,13 @@ where upper (books.sort) like ?'); $result = parent::getDb ()->query("select books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index from books left outer join comments on book = books.id order by timestamp desc limit " . $config['cops_recentbooks_limit']); + $entryArray = array(); while ($post = $result->fetchObject ()) { $book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); - $book->getEntry (); + array_push ($entryArray, $book->getEntry ()); } + return $entryArray; } } diff --git a/config_default.php b/config_default.php index 3fa9912..75e3359 100644 --- a/config_default.php +++ b/config_default.php @@ -18,6 +18,7 @@ /* * The internal directory set in nginx config file + * or the same directory as calibre_directory with X-Sendfile */ $config['calibre_internal_directory'] = '/Calibre/'; @@ -31,4 +32,12 @@ */ $config['cops_title_default'] = "Sebastien's COPS"; + + /* + * Wich header to use when downloading books outside the web directory + * Possible values are : + * X-Accel-Redirect : For Nginx + * X-Sendfile : For Lightttpd or Apache (with mod_xsendfile) + */ + $config['cops_x_accel_redirect'] = "X-Accel-Redirect"; ?> \ No newline at end of file diff --git a/feed.php b/feed.php index 5c832c7..68fd48b 100644 --- a/feed.php +++ b/feed.php @@ -12,75 +12,24 @@ require_once ("author.php"); require_once ("serie.php"); require_once ("book.php"); + require_once ("OPDS_renderer.php"); + header ("Content-Type:application/xml"); - $page = Base::PAGE_INDEX; - global $config; - if (!empty ($_GET) && isset($_GET["page"])) { - $page = $_GET["page"]; - } + $page = getURLParam ("page", Base::PAGE_INDEX); + $query = getURLParam ("query"); + $qid = getURLParam ("id"); + + $OPDSRender = new OPDSRenderer (); + switch ($page) { - case Base::PAGE_ALL_AUTHORS : - $title = "All authors"; - break; - case Base::PAGE_AUTHOR_DETAIL : - $title = Author::getAuthorName ($_GET["id"]); - break; - case Base::PAGE_ALL_SERIES : - $title = "All series"; - break; - case Base::PAGE_ALL_BOOKS : - $title = "All books by starting letter"; - break; - case Base::PAGE_ALL_BOOKS_LETTER: - $title = "All books starting by " . $_GET["id"]; - break; - case Base::PAGE_ALL_RECENT_BOOKS : - $title = "Most recent books"; - break; - case Base::PAGE_SERIE_DETAIL : - $title = "Series : " . Serie::getSerieById ($_GET["id"])->name; - break; case Base::PAGE_OPENSEARCH : - echo Base::getOpenSearch (); + echo $OPDSRender->getOpenSearch (); return; - case Base::PAGE_OPENSEARCH_QUERY : - $title = "Search result for query <" . $_GET["query"] . ">"; - break; default: - $title = $config['cops_title_default']; + $currentPage = Page::getPage ($page, $qid, $query); + $currentPage->InitializeContent (); + echo $OPDSRender->render ($currentPage); + return; break; } - Base::startXmlDocument ($title); - switch ($page) { - case Base::PAGE_ALL_AUTHORS : - Author::getAllAuthors(); - break; - case Base::PAGE_AUTHOR_DETAIL : - Book::getBooksByAuthor ($_GET["id"]); - break; - case Base::PAGE_ALL_SERIES : - Serie::getAllSeries(); - break; - case Base::PAGE_ALL_BOOKS : - Book::getAllBooks (); - break; - case Base::PAGE_ALL_BOOKS_LETTER: - Book::getBooksByStartingLetter ($_GET["id"]); - break; - case Base::PAGE_ALL_RECENT_BOOKS : - Book::getAllRecentBooks (); - break; - case Base::PAGE_SERIE_DETAIL : - Book::getBooksBySeries ($_GET["id"]); - break; - case Base::PAGE_OPENSEARCH_QUERY : - Book::getBooksByQuery ($_GET["query"]); - break; - default: - Author::getCount(); - Serie::getCount(); - Book::getCount(); - break; - } - echo Base::endXmlDocument (); ?> diff --git a/fetch.php b/fetch.php index 7238a13..fbd79ea 100644 --- a/fetch.php +++ b/fetch.php @@ -7,17 +7,13 @@ */ require_once ("config.php"); - require_once('book.php'); + require_once ("book.php"); global $config; $bookId = $_GET["id"]; $book = Book::getBookById($bookId); - $type = "jpg"; + $type = getURLParam ("type", "jpg"); - if (!empty ($_GET) && isset($_GET["type"])) { - $type = $_GET["type"]; - } - switch ($type) { case "jpg": @@ -48,11 +44,11 @@ return; } break; - case "epub": - header("Content-type: application/epub+zip"); + default: + header("Content-type: " . Book::$mimetypes[$type]); break; } $file = $book->getFilePath ($type, true); header('Content-Disposition: attachement; filename="' . basename ($file) . '"'); - header ("X-Accel-Redirect: " . $config['calibre_internal_directory'] . $file); + header ($config['cops_x_accel_redirect'] . ": " . $config['calibre_internal_directory'] . $file); ?> \ No newline at end of file diff --git a/serie.php b/serie.php index b628144..c269b3e 100644 --- a/serie.php +++ b/serie.php @@ -25,9 +25,10 @@ class Serie extends Base { public static function getCount() { $nSeries = parent::getDb ()->query('select count(*) from series')->fetchColumn(); - parent::addEntryClass (new Entry ("Series", self::ALL_SERIES_ID, + $entry = new Entry ("Series", self::ALL_SERIES_ID, "Alphabetical index of the $nSeries series", "text", - array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_SERIES)))); + array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_SERIES))); + return $entry; } public static function getSerieByBookId ($bookId) { @@ -56,12 +57,14 @@ from series, books_series_link where series.id = series group by series.id, series.name, series.sort order by series.sort'); + $entryArray = array(); while ($post = $result->fetchObject ()) { - parent::addEntryClass (new Entry ($post->sort, self::ALL_SERIES_ID.":".$post->id, + array_push ($entryArray, new Entry ($post->sort, self::ALL_SERIES_ID.":".$post->id, "$post->count books", "text", array ( new LinkNavigation ("feed.php?page=".parent::PAGE_SERIE_DETAIL."&id=$post->id")))); } + return $entryArray; } } ?> \ No newline at end of file