Browse Source

Add v0.0.2

master
Sébastien Lucas 12 years ago
parent
commit
b25ed5c052
10 changed files with 404 additions and 271 deletions
  1. +6
    -0
      CHANGELOG
  2. +181
    -0
      OPDS_renderer.php
  3. +6
    -4
      README
  4. +6
    -3
      author.php
  5. +122
    -167
      base.php
  6. +50
    -21
      book.php
  7. +9
    -0
      config_default.php
  8. +13
    -64
      feed.php
  9. +5
    -9
      fetch.php
  10. +6
    -3
      serie.php

+ 6
- 0
CHANGELOG 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
* First public release

+ 181
- 0
OPDS_renderer.php 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 ();
}
}
?>

+ 6
- 4
README View File

@@ -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 <sebastien@slucas.fr>


+ 6
- 3
author.php View File

@@ -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) {


+ 122
- 167
base.php View File

@@ -6,6 +6,12 @@
* @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
{
@@ -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,39 +76,127 @@ class EntryBook extends Entry
$this->book = $pbook;
$this->localUpdated = $pbook->timestamp;
}
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 ();
}

class Page
{
public $title;
public $idPage;
public $idGet;
public $query;
public $entryArray = array();
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
@@ -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);
}
}
?>

+ 50
- 21
book.php View File

@@ -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;
}

}

+ 9
- 0
config_default.php View File

@@ -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";
?>

+ 13
- 64
feed.php View File

@@ -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'];
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();
$currentPage = Page::getPage ($page, $qid, $query);
$currentPage->InitializeContent ();
echo $OPDSRender->render ($currentPage);
return;
break;
}
echo Base::endXmlDocument ();
?>

+ 5
- 9
fetch.php View File

@@ -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);
?>

+ 6
- 3
serie.php View File

@@ -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;
}
}
?>

Loading…
Cancel
Save