Skip to content

Commit

Permalink
Merge pull request #21 from kiwilan/develop
Browse files Browse the repository at this point in the history
v1.0.0-alpha.4
  • Loading branch information
ewilan-riviere authored Aug 31, 2023
2 parents a618415 + 96837c1 commit 41aa037
Show file tree
Hide file tree
Showing 12 changed files with 268 additions and 111 deletions.
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ This package has been created to be used with [bookshelves-project/bookshelves](
- OPDS feeds examples (these projects don't use `kiwilan/php-opds`)
- [gallica.bnf.fr](https://gallica.bnf.fr/opds): Gallica (French National Library)
- [cops-demo.slucas.fr](https://cops-demo.slucas.fr/feed.php): COPS (OPDS PHP Server)
- [kiwilan/php-ebook](https://github.com/kiwilan/php-ebook): PHP package to handle eBook
- [koreader/koreader](https://github.com/koreader/koreader): eBook reader for Android, iOS, Kindle, Kobo, Linux, macOS, Windows, and more. If your eReader can't use OPDS feeds, you can install KOReader on it.
- [edrlab/thorium-reader](https://github.com/edrlab/thorium-reader): A cross platform desktop reading app, based on the Readium Desktop toolkit. You can use it to use OPDS feeds and read eBooks.

## Installation

Expand Down Expand Up @@ -258,12 +261,6 @@ $opds = Opds::make()
- [Basic usage](docs/basic-usage.md)
- [Advanced usage](docs/advanced-usage.md)

## More

- [kiwilan/php-ebook](https://github.com/kiwilan/php-ebook): PHP package to handle eBook
- [koreader/koreader](https://github.com/koreader/koreader): eBook reader for Android, iOS, Kindle, Kobo, Linux, macOS, Windows, and more. If your eReader can't use OPDS feeds, you can install KOReader on it.
- [edrlab/thorium-reader](https://github.com/edrlab/thorium-reader): A cross platform desktop reading app, based on the Readium Desktop toolkit. You can use it to use OPDS feeds and read eBooks.

## Testing

```bash
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "kiwilan/php-opds",
"description": "PHP package to create OPDS feed for eBooks.",
"version": "1.0.0-alpha.3",
"version": "1.0.0-alpha.4",
"keywords": [
"php",
"ebook",
Expand Down
40 changes: 27 additions & 13 deletions src/Engine/OpdsEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ abstract class OpdsEngine
{
protected function __construct(
protected Opds $opds,
protected array $xml = [],
protected array $content = [],
protected ?string $response = null,
) {
}
Expand Down Expand Up @@ -54,6 +54,20 @@ abstract public function addNavigationEntry(OpdsEntryNavigation $entry): array;
*/
abstract public function addBookEntry(OpdsEntryBook $entry): array;

public function setContent(array $content): self
{
$this->content = $content;

return $this;
}

public function setResponse(string $response): self
{
$this->response = $response;

return $this;
}

/**
* Get OPDS instance.
*/
Expand All @@ -63,15 +77,15 @@ public function getOpds(): Opds
}

/**
* Get XML array.
* Get content array.
*/
public function getXml(): array
public function getContent(): array
{
return $this->xml;
return $this->content;
}

/**
* Get XML response.
* Get response as XML or JSON.
*/
public function getResponse(): string
{
Expand Down Expand Up @@ -214,7 +228,7 @@ protected function handleJsonPagination(): array
/**
* Handle XML pagination.
*/
protected function handleXmlPagination(array &$xml, array &$feeds): void
protected function handleXmlPagination(array &$content, array &$feeds): void
{
$feeds = $this->opds->getFeeds();
$paginate = $this->opds->getConfig()->isUsePagination();
Expand Down Expand Up @@ -270,23 +284,23 @@ protected function handleXmlPagination(array &$xml, array &$feeds): void
]);

if ($queryStartRecord !== 0) {
$xml['__custom:link:4'] = $this->addXmlLink(href: $previousUrl, rel: 'previous', title: 'Previous page');
$content['__custom:link:4'] = $this->addXmlLink(href: $previousUrl, rel: 'previous', title: 'Previous page');
}

if ($queryStartRecord !== $last) {
$xml['__custom:link:5'] = $this->addXmlLink(href: $nextUrl, rel: 'next', title: 'Next page');
$content['__custom:link:5'] = $this->addXmlLink(href: $nextUrl, rel: 'next', title: 'Next page');
}

if ($queryStartRecord !== 0) {
$xml['__custom:link:6'] = $this->addXmlLink(href: $firstUrl, rel: 'first', title: 'First page');
$content['__custom:link:6'] = $this->addXmlLink(href: $firstUrl, rel: 'first', title: 'First page');
}

if ($queryStartRecord !== $last) {
$xml['__custom:link:7'] = $this->addXmlLink(href: $lastUrl, rel: 'last', title: 'Last page');
$content['__custom:link:7'] = $this->addXmlLink(href: $lastUrl, rel: 'last', title: 'Last page');
}

$xml['opensearch:totalResults'] = count($this->opds->getFeeds());
$xml['opensearch:itemsPerPage'] = $perPage;
$xml['opensearch:startIndex'] = $startRecord === 0 ? 1 : $start;
$content['opensearch:totalResults'] = count($this->opds->getFeeds());
$content['opensearch:itemsPerPage'] = $perPage;
$content['opensearch:startIndex'] = $startRecord === 0 ? 1 : $start;
}
}
12 changes: 6 additions & 6 deletions src/Engine/OpdsJsonEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static function make(Opds $opds): self

public function feed(): self
{
$this->xml = [
$this->content = [
'metadata' => [
'title' => $this->getFeedTitle(),
],
Expand All @@ -38,14 +38,14 @@ public function feed(): self

if ($this->opds->getConfig()->getStartUrl()) {
if (! $this->opds->getConfig()->isForceJson()) {
$this->xml['links'][] = $this->addJsonLink(
$this->content['links'][] = $this->addJsonLink(
rel: 'alternate',
href: $this->getVersionUrl(OpdsVersionEnum::v1Dot2),
title: 'OPDS 1.2',
type: 'application/atom+xml',
);
}
$this->xml['links'][] = $this->addJsonLink(
$this->content['links'][] = $this->addJsonLink(
rel: 'alternate',
href: $this->getVersionUrl(OpdsVersionEnum::v2Dot0),
title: 'OPDS 2.0',
Expand All @@ -55,15 +55,15 @@ public function feed(): self

foreach ($this->opds->getFeeds() as $feed) {
if ($feed instanceof OpdsEntryBook) {
$this->xml['publications'][] = $this->addEntry($feed);
$this->content['publications'][] = $this->addEntry($feed);

continue;
}

$this->xml['navigation'][] = $this->addEntry($feed);
$this->content['navigation'][] = $this->addEntry($feed);
}

$this->response = json_encode($this->xml);
$this->response = json_encode($this->content);

return $this;
}
Expand Down
67 changes: 13 additions & 54 deletions src/Engine/OpdsXmlEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,34 @@ public function feed(): self
$title = $this->getFeedTitle();
$updated = $this->opds->getConfig()->getUpdated();

$this->xml = [
$this->content = [
'id' => $id,
'title' => $title,
'updated' => $updated->format(DATE_ATOM),
];

if ($this->opds->getConfig()->getIconUrl()) {
$this->xml['icon'] = $this->opds->getConfig()->getIconUrl();
$this->content['icon'] = $this->opds->getConfig()->getIconUrl();
}

$this->xml['__custom:link:1'] = $this->addXmlLink(href: OpdsEngine::getCurrentUrl(), title: 'self', rel: 'self');
$this->content['__custom:link:1'] = $this->addXmlLink(href: OpdsEngine::getCurrentUrl(), title: 'self', rel: 'self');

if ($this->opds->getConfig()->getStartUrl()) {
$this->xml['__custom:link:2'] = $this->addXmlLink(href: $this->route($this->opds->getConfig()->getStartUrl()), title: 'Home', rel: 'start');
$this->content['__custom:link:2'] = $this->addXmlLink(href: $this->route($this->opds->getConfig()->getStartUrl()), title: 'Home', rel: 'start');
}

if ($this->opds->getConfig()->getSearchUrl()) {
$this->xml['__custom:link:3'] = $this->addXmlLink(href: $this->route($this->opds->getConfig()->getSearchUrl()), title: 'Search here', rel: 'search');
$this->content['__custom:link:3'] = $this->addXmlLink(href: $this->route($this->opds->getConfig()->getSearchUrl()), title: 'Search here', rel: 'search');
}

if ($this->opds->getConfig()->getStartUrl()) {
$this->xml['__custom:link:4'] = $this->addXmlLink(
$this->content['__custom:link:4'] = $this->addXmlLink(
href: $this->getVersionUrl(OpdsVersionEnum::v1Dot2),
title: 'OPDS 1.2',
rel: 'alternate',
type: 'application/atom+xml'
);
$this->xml['__custom:link:5'] = $this->addXmlLink(
$this->content['__custom:link:5'] = $this->addXmlLink(
href: $this->getVersionUrl(OpdsVersionEnum::v2Dot0),
title: 'OPDS 2.0',
rel: 'alternate',
Expand All @@ -65,18 +65,18 @@ public function feed(): self
}

if ($this->opds->getConfig()->getAuthor()) {
$this->xml['author'] = ['name' => $this->opds->getConfig()->getAuthor(), 'uri' => $this->opds->getConfig()->getAuthorUrl()];
$this->content['author'] = ['name' => $this->opds->getConfig()->getAuthor(), 'uri' => $this->opds->getConfig()->getAuthorUrl()];
}

$feeds = $this->opds->getFeeds();
$this->handleXmlPagination($this->xml, $feeds);
$this->handleXmlPagination($this->content, $feeds);

foreach ($feeds as $entry) {
$this->xml['entry'][] = $this->addEntry($entry);
$this->content['entry'][] = $this->addEntry($entry);
}

$this->response = ArrayToXml::convert(
array: $this->xml,
array: $this->content,
rootElement: [
'rootElementName' => 'feed',
'_attributes' => OpdsNamespaces::VERSION_1_2,
Expand All @@ -102,7 +102,7 @@ public function search(): self
return $this;
}

$this->xml = [
$this->content = [
'ShortName' => $this->addXmlNode($app),
'Description' => $this->addXmlNode("OPDS search engine {$app}"),
'InputEncoding' => $this->addXmlNode('UTF-8'),
Expand All @@ -123,7 +123,7 @@ public function search(): self
];

$this->response = ArrayToXml::convert(
array: $this->xml,
array: $this->content,
rootElement: [
'rootElementName' => 'OpenSearchDescription',
'_attributes' => OpdsNamespaces::VERSION_1_2_SEARCH,
Expand All @@ -135,47 +135,6 @@ public function search(): self
return $this;
}

public function entry(OpdsEntryNavigation $entry): array
{
$app = OpdsConfig::slug($this->opds->getConfig()->getName());
$feed = [
'title' => $entry->getTitle(),
'id' => "{$app}:{$entry->getId()}",
'__custom:link:1' => $this->addXmlLink(href: $this->route($entry->getRoute())),
];

if ($entry->getUpdated()) {
$feed['updated'] = $entry->getUpdated()->format(DATE_ATOM);
}

if ($entry->getSummary()) {
$feed['summary'] = $this->addXmlNode(
value: strip_tags($entry->getSummary()),
attributes: ['type' => 'text']
);
}

if ($entry->getContent()) {
$feed['content'] = $this->addXmlNode(
value: $entry->getContent(),
attributes: ['type' => 'text/html']
);
}

if ($entry->getMedia()) {
$type = 'unknown';
$ext = pathinfo($entry->getMedia(), PATHINFO_EXTENSION);

if (in_array($ext, ['png', 'jpeg', 'jpg', 'gif'])) {
$type = "image/{$ext}";
}

$feed['__custom:link:2'] = $this->addXmlLink(href: $entry->getMedia(), rel: 'http://opds-spec.org/image/thumbnail', type: $type);
}

return $feed;
}

public function addNavigationEntry(OpdsEntryNavigation $entry): array
{
$app = OpdsConfig::slug($this->opds->getConfig()->getName());
Expand Down
40 changes: 40 additions & 0 deletions tests/OpdsConfigTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

use Kiwilan\Opds\OpdsConfig;

it('can use setter', function () {
$config = new OpdsConfig();

$config->setName('Gallica');
$config->setAuthor('Hadrien Gardeur');
$config->setAuthorUrl('https://example.com');
$config->setIconUrl('https://example.com/favicon.ico');
$config->setStartUrl('https://example.com/opds');
$config->setSearchUrl('https://example.com/opds/search');
$config->setSearchQuery('query');
$config->setVersionQuery('v');
$config->setUpdated(new DateTime());
$config->usePagination();
$config->setMaxItemsPerPage(10);
$config->forceJson();

expect($config->getName())->toBe('Gallica');
expect($config->getAuthor())->toBe('Hadrien Gardeur');
expect($config->getAuthorUrl())->toBe('https://example.com');
expect($config->getIconUrl())->toBe('https://example.com/favicon.ico');
expect($config->getStartUrl())->toBe('https://example.com/opds');
expect($config->getSearchUrl())->toBe('https://example.com/opds/search');
expect($config->getSearchQuery())->toBe('query');
expect($config->getVersionQuery())->toBe('v');
expect($config->getUpdated())->toBeInstanceOf(DateTime::class);
expect($config->isUsePagination())->toBeTrue();
expect($config->getMaxItemsPerPage())->toBe(10);
expect($config->isForceJson())->toBeTrue();
});

it('can use slug', function () {
$empty = null;
$slug = OpdsConfig::slug($empty);

expect($slug)->toBeNull();
});
Loading

0 comments on commit 41aa037

Please sign in to comment.