Add v0.0.2

This commit is contained in:
Sébastien Lucas 2012-05-28 07:05:05 +02:00
parent d023b869a1
commit b25ed5c052
10 changed files with 403 additions and 270 deletions

View file

@ -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 0.0.1 - 20120302
* First public release * First public release

181
OPDS_renderer.php Normal file
View file

@ -0,0 +1,181 @@
<?php
/**
* COPS (Calibre OPDS PHP Server) class file
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Sébastien Lucas <sebastien@slucas.fr>
*/
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 ();
}
}
?>

10
README
View file

@ -8,7 +8,7 @@ http://opds-validator.appspot.com/
= Why ? = = Why ? =
In my opinion Calibre is a marvelous tool but is too big and has too much 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 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). 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. search was possible.
So COPS's main advantages are : 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. * No need for a lot of CPU or RAM.
* Not much code. * Not much code.
* Search is available. * Search is available.
@ -77,8 +77,8 @@ application/epub+zip epub;
= Known problems = = Known problems =
* Only tested with Nginx. * Only tested with Nginx.
* Contain Nginx specific code. * Contain Nginx specific code (that could be changed with a config item).
* Only works with EPUB (not MOBI or PDF). * Only works with EPUB, MOBI and PDF.
* certainly many many more. * certainly many many more.
= Disclaimer = = 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. On the OPDS client side I mainly tested with FBReader and Aldiko on Android.
It also seems to work with Stanza.
= Copyright & License = = Copyright & License =
COPS - 2012 (c) Sébastien Lucas <sebastien@slucas.fr> COPS - 2012 (c) Sébastien Lucas <sebastien@slucas.fr>

View file

@ -31,9 +31,10 @@ class Author extends Base {
public static function getCount() { public static function getCount() {
$nAuthors = parent::getDb ()->query('select count(*) from authors')->fetchColumn(); $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", "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() { public static function getAllAuthors() {
@ -42,13 +43,15 @@ from authors, books_authors_link
where author = authors.id where author = authors.id
group by authors.id, authors.name, authors.sort group by authors.id, authors.name, authors.sort
order by sort'); order by sort');
$entryArray = array();
while ($post = $result->fetchObject ()) while ($post = $result->fetchObject ())
{ {
$author = new Author ($post->id, $post->sort); $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", "$post->count books", "text",
array ( new LinkNavigation ($author->getUri ())))); array ( new LinkNavigation ($author->getUri ()))));
} }
return $entryArray;
} }
public static function getAuthorName ($authorId) { public static function getAuthorName ($authorId) {

287
base.php
View file

@ -6,6 +6,12 @@
* @author Sébastien Lucas <sebastien@slucas.fr> * @author Sébastien Lucas <sebastien@slucas.fr>
*/ */
function getURLParam ($name, $default = NULL) {
if (!empty ($_GET) && isset($_GET[$name])) {
return $_GET[$name];
}
return $default;
}
class Link class Link
{ {
@ -20,19 +26,6 @@ class Link
$this->rel = $prel; $this->rel = $prel;
$this->title = $ptitle; $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 class LinkNavigation extends Link
@ -72,36 +65,6 @@ class Entry
$this->contentType = $pcontentType; $this->contentType = $pcontentType;
$this->linkArray = $plinkArray; $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 class EntryBook extends Entry
@ -113,39 +76,127 @@ class EntryBook extends Entry
$this->book = $pbook; $this->book = $pbook;
$this->localUpdated = $pbook->timestamp; $this->localUpdated = $pbook->timestamp;
} }
}
public function renderContent ($xml) { class Page
parent::renderContent ($xml); {
foreach ($this->book->getAuthors () as $author) { public $title;
$xml->startElement ("author"); public $idPage;
$xml->startElement ("name"); public $idGet;
$xml->text ($author->name); public $query;
$xml->endElement (); public $entryArray = array();
$xml->startElement ("uri");
$xml->text ($author->getUri ()); public static function getPage ($pageId, $id, $query)
$xml->endElement (); {
$xml->endElement (); switch ($pageId) {
} case Base::PAGE_ALL_AUTHORS :
foreach ($this->book->getTags () as $category) { return new PageAllAuthors ($id, $query);
$xml->startElement ("category"); case Base::PAGE_AUTHOR_DETAIL :
$xml->writeAttribute ("term", $category); return new PageAuthorDetail ($id, $query);
$xml->writeAttribute ("label", $category); case Base::PAGE_ALL_SERIES :
$xml->endElement (); return new PageAllSeries ($id, $query);
} case Base::PAGE_ALL_BOOKS :
if (!is_null ($this->book->pubdate)) { return new PageAllBooks ($id, $query);
$xml->startElement ("dcterms:issued"); case Base::PAGE_ALL_BOOKS_LETTER:
$xml->text (date ("Y-m-d", $this->book->pubdate)); return new PageAllBooksLetter ($id, $query);
$xml->endElement (); 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 __construct($pid, $pquery) {
public function render ($xml) { $this->idGet = $pid;
$xml->startElement ("entry"); $this->query = $pquery;
self::renderContent ($xml);
$xml->endElement ();
} }
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 abstract class Base
@ -164,15 +215,6 @@ abstract class Base
const COMPATIBILITY_XML_ALDIKO = "aldiko"; const COMPATIBILITY_XML_ALDIKO = "aldiko";
private static $db = NULL; 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 () { public static function getDb () {
global $config; global $config;
@ -186,92 +228,5 @@ abstract class Base
} }
return self::$db; 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);
}
} }
?> ?>

View file

@ -7,6 +7,8 @@
*/ */
require_once('base.php'); require_once('base.php');
require_once('serie.php');
require_once('author.php');
class Book extends Base { class Book extends Base {
const ALL_BOOKS_ID = "calibre:books"; const ALL_BOOKS_ID = "calibre:books";
@ -23,6 +25,12 @@ class Book extends Base {
public $authors = NULL; public $authors = NULL;
public $serie = NULL; public $serie = NULL;
public $tags = 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) { public function __construct($pid, $ptitle, $ptimestamp, $ppubdate, $ppath, $pseriesIndex, $pcomment) {
global $config; 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")); 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)) { foreach (self::$mimetypes as $ext => $mime)
if (preg_match ('/^\//', $config['calibre_directory'])) {
{ if (preg_match ('/'. $ext .'$/', $file)) {
array_push ($linkArray, new Link ("fetch.php?id=$this->id&type=epub", "application/epub+zip", "http://opds-spec.org/acquisition", "Download")); if (preg_match ('/^\//', $config['calibre_directory']))
} {
else array_push ($linkArray, new Link ("fetch.php?id=$this->id&type=" . $ext, $mime, "http://opds-spec.org/acquisition", "Download"));
{ }
array_push ($linkArray, new Link (rawurlencode ($this->path."/".$file), "application/epub+zip", "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 () { public function getEntry () {
parent::addEntryClass (new EntryBook ($this->getTitle (), $this->getEntryId (), return new EntryBook ($this->getTitle (), $this->getEntryId (),
$this->getComment (), "text/html", $this->getComment (), "text/html",
$this->getLinkArray (), $this)); $this->getLinkArray (), $this);
} }
public static function getCount() { public static function getCount() {
global $config;
$nBooks = parent::getDb ()->query('select count(*) from books')->fetchColumn(); $nBooks = parent::getDb ()->query('select count(*) from books')->fetchColumn();
parent::addEntryClass (new Entry ("Books", $result = array();
$entry = new Entry ("Books",
self::ALL_BOOKS_ID, self::ALL_BOOKS_ID,
"Alphabetical index of the $nBooks books", "text", "Alphabetical index of the $nBooks books", "text",
array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_BOOKS)))); array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_BOOKS)));
parent::addEntryClass (new Entry ("Recents books", array_push ($result, $entry);
$entry = new Entry ("Recents books",
self::ALL_RECENT_BOOKS_ID, self::ALL_RECENT_BOOKS_ID,
"Alphabetical index of the 50 most recent books", "text", "Alphabetical index of the " . $config['cops_recentbooks_limit'] . " most recent books", "text",
array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_RECENT_BOOKS)))); array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_RECENT_BOOKS)));
array_push ($result, $entry);
return $result;
} }
public static function getBooksByAuthor($authorId) { 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 where books_authors_link.book = books.id
and author = ? and author = ?
order by pubdate'); order by pubdate');
$entryArray = array();
$result->execute (array ($authorId)); $result->execute (array ($authorId));
while ($post = $result->fetchObject ()) while ($post = $result->fetchObject ())
{ {
$book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); $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 from books_series_link, books left outer join comments on comments.book = books.id
where books_series_link.book = books.id and series = ? where books_series_link.book = books.id and series = ?
order by series_index'); order by series_index');
$entryArray = array();
$result->execute (array ($serieId)); $result->execute (array ($serieId));
while ($post = $result->fetchObject ()) while ($post = $result->fetchObject ())
{ {
$book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); $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) { 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 $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 from books left outer join comments on book = books.id
where books.id = ?'); where books.id = ?');
$entryArray = array();
$result->execute (array ($bookId)); $result->execute (array ($bookId));
while ($post = $result->fetchObject ()) while ($post = $result->fetchObject ())
{ {
@ -211,13 +232,15 @@ where books.id = ?');
from books left outer join comments on book = 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 ?) where exists (select null from authors, books_authors_link where book = books.id and author = authors.id and authors.name like ?)
or title like ?"); or title like ?");
$entryArray = array();
$queryLike = "%" . $query . "%"; $queryLike = "%" . $query . "%";
$result->execute (array ($queryLike, $queryLike)); $result->execute (array ($queryLike, $queryLike));
while ($post = $result->fetchObject ()) while ($post = $result->fetchObject ())
{ {
$book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); $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() { public static function getAllBooks() {
@ -225,25 +248,29 @@ or title like ?");
from books from books
group by substr (upper (sort), 1, 1) group by substr (upper (sort), 1, 1)
order by substr (upper (sort), 1, 1)"); order by substr (upper (sort), 1, 1)");
$entryArray = array();
while ($post = $result->fetchObject ()) 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", "$post->count books", "text",
array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_BOOKS_LETTER."&id=".$post->title)))); array ( new LinkNavigation ("feed.php?page=".parent::PAGE_ALL_BOOKS_LETTER."&id=".$post->title))));
} }
return $entryArray;
} }
public static function getBooksByStartingLetter($letter) { 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 $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 from books left outer join comments on book = books.id
where upper (books.sort) like ?'); where upper (books.sort) like ?');
$entryArray = array();
$queryLike = $letter . "%"; $queryLike = $letter . "%";
$result->execute (array ($queryLike)); $result->execute (array ($queryLike));
while ($post = $result->fetchObject ()) while ($post = $result->fetchObject ())
{ {
$book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); $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 $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 from books left outer join comments on book = books.id
order by timestamp desc limit " . $config['cops_recentbooks_limit']); order by timestamp desc limit " . $config['cops_recentbooks_limit']);
$entryArray = array();
while ($post = $result->fetchObject ()) while ($post = $result->fetchObject ())
{ {
$book = new Book ($post->id, $post->title, $post->timestamp, $post->pubdate, $post->path, $post->series_index, $post->comment); $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;
} }
} }

View file

@ -18,6 +18,7 @@
/* /*
* The internal directory set in nginx config file * The internal directory set in nginx config file
* or the same directory as calibre_directory with X-Sendfile
*/ */
$config['calibre_internal_directory'] = '/Calibre/'; $config['calibre_internal_directory'] = '/Calibre/';
@ -31,4 +32,12 @@
*/ */
$config['cops_title_default'] = "Sebastien's COPS"; $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";
?> ?>

View file

@ -12,75 +12,24 @@
require_once ("author.php"); require_once ("author.php");
require_once ("serie.php"); require_once ("serie.php");
require_once ("book.php"); require_once ("book.php");
require_once ("OPDS_renderer.php");
header ("Content-Type:application/xml"); header ("Content-Type:application/xml");
$page = Base::PAGE_INDEX; $page = getURLParam ("page", Base::PAGE_INDEX);
global $config; $query = getURLParam ("query");
if (!empty ($_GET) && isset($_GET["page"])) { $qid = getURLParam ("id");
$page = $_GET["page"];
} $OPDSRender = new OPDSRenderer ();
switch ($page) { 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 : case Base::PAGE_OPENSEARCH :
echo Base::getOpenSearch (); echo $OPDSRender->getOpenSearch ();
return; return;
case Base::PAGE_OPENSEARCH_QUERY :
$title = "Search result for query <" . $_GET["query"] . ">";
break;
default: default:
$title = $config['cops_title_default']; $currentPage = Page::getPage ($page, $qid, $query);
$currentPage->InitializeContent ();
echo $OPDSRender->render ($currentPage);
return;
break; 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 ();
?> ?>

View file

@ -7,16 +7,12 @@
*/ */
require_once ("config.php"); require_once ("config.php");
require_once('book.php'); require_once ("book.php");
global $config; global $config;
$bookId = $_GET["id"]; $bookId = $_GET["id"];
$book = Book::getBookById($bookId); $book = Book::getBookById($bookId);
$type = "jpg"; $type = getURLParam ("type", "jpg");
if (!empty ($_GET) && isset($_GET["type"])) {
$type = $_GET["type"];
}
switch ($type) switch ($type)
{ {
@ -48,11 +44,11 @@
return; return;
} }
break; break;
case "epub": default:
header("Content-type: application/epub+zip"); header("Content-type: " . Book::$mimetypes[$type]);
break; break;
} }
$file = $book->getFilePath ($type, true); $file = $book->getFilePath ($type, true);
header('Content-Disposition: attachement; filename="' . basename ($file) . '"'); 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);
?> ?>

View file

@ -25,9 +25,10 @@ class Serie extends Base {
public static function getCount() { public static function getCount() {
$nSeries = parent::getDb ()->query('select count(*) from series')->fetchColumn(); $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", "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) { public static function getSerieByBookId ($bookId) {
@ -56,12 +57,14 @@ from series, books_series_link
where series.id = series where series.id = series
group by series.id, series.name, series.sort group by series.id, series.name, series.sort
order by series.sort'); order by series.sort');
$entryArray = array();
while ($post = $result->fetchObject ()) 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", "$post->count books", "text",
array ( new LinkNavigation ("feed.php?page=".parent::PAGE_SERIE_DETAIL."&id=$post->id")))); array ( new LinkNavigation ("feed.php?page=".parent::PAGE_SERIE_DETAIL."&id=$post->id"))));
} }
return $entryArray;
} }
} }
?> ?>