kiwilan/php-opds

创建电子书 OPDS 馈送的 PHP 包。

资助包维护!
kiwilan

2.1.0 2024-06-26 06:41 UTC

README

Banner with woman with eReader picture in background and PHP OPDS title

php version downloads license tests codecov

PHP 包用于创建 OPDS 馈送 (Open Publication Distribution System) 以供电子书使用。

所有旧版本:0.9,1.0 和 1.1 均回退到 OPDS 1.2。

需求

  • php v8.1 最低

关于

OPDS 类似于 RSS 馈送,但针对电子书进行了适配,它是一个在图书馆、书店、出版社和读者之间共享电子书的标准。由 Hadrien GardeurLeonard Richardson 开发。

此包是为了与 bookshelves-project/bookshelves 一起使用而创建的,这是一个开源的电子书 Web 应用程序。

注意

开放出版分发系统 (OPDS) 目录格式是基于 Atom 和 HTTP 的电子出版物聚合、分发、发现和获取的订阅格式。OPDS 目录使用现有的或新兴的开放标准和惯例,并优先考虑简单性。

开放出版分发系统规范由一个非正式的合作伙伴群体准备,该群体结合了互联网档案馆、O'Reilly 媒体、Feedbooks、OLPC 等。

来自 Wikipedia

有关 OPDS 和电子书的一些资源

  • opds.io: OPDS 官方网站
  • OPDS 馈送示例
  • kiwilan/php-ebook: 处理电子书的 PHP 包
  • koreader/koreader: Android、iOS、Kindle、Kobo、Linux、macOS、Windows 和更多设备上的电子书阅读器。如果你的电子书阅读器不能使用 OPDS 馈送,你可以在上面安装 KOReader
  • edrlab/thorium-reader: 基于 Readium Desktop 工具包的跨平台桌面阅读应用程序。你可以使用它来使用 OPDS 馈送并阅读电子书

功能

  • ⚛️ 生成 OPDS XML 和 JSON 馈送(导航馈送和获取馈送)
  • 👌 支持 OPDS 1.2 和 2.0
  • 🔖 带有分页选项
  • 🔍 包含搜索页面,但不是搜索引擎
  • 🌐 以 XML 或 JSON 格式处理浏览器响应的选项

路线图

  • OPDS 1.2:支持高级获取馈送
  • OPDS 2.0:支持 FacetsGroups、高级 belongsTo
  • 添加来自 anansi-projectOPDS 页面流扩展

安装

您可以通过Composer安装此软件包。

composer require kiwilan/php-opds

使用方法

您必须使用 Opds::make() 方法来创建OPDS实例,唯一参数是 config 用于设置OPDS配置,完全可选。默认响应为带有OPDS 1.2版本的XML,您可以使用 OpdsConfig::class 方法的 forceJson() 方法强制JSON响应,仅使用OPDS 2.0。使用 get() 方法,您可以使用 OpdsEngineOpdsResponse 获取完整的 Opds 实例。

use Kiwilan\Opds\Opds;
use Kiwilan\Opds\OpdsConfig;

$opds = Opds::make(new OpdsConfig()) // OpdsConfig::class, optional
  ->title('My feed')
  ->feeds([...]) // OpdsEntryNavigation[]|OpdsEntryBook[]|OpdsEntryNavigation|OpdsEntryBook
  ->get()
;

Opds::class 中有不同信息。

关于OPDS实例的一些信息

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->get()
;

$opds->getConfig(); // OpdsConfig - Configuration used to create OPDS feed set into `make()` method
$opds->getUrl(); // string|null - Current URL, generated automatically but can be overrided with `url()` method
$opds->getTitle(); // string - Title of OPDS feed set with `title()` method
$opds->getVersion(); // OpdsVersionEnum - OPDS version used, determined by query parameter `v` or `OpdsConfig::class` method `forceJson()`
$opds->getQueryVersion(); // OpdsVersionEnum|null - Name of query parameter used to set OPDS version, default is `v`
$opds->getUrlParts(); // array - URL parts, determined from `url`
$opds->getQuery(); // array - Query parameters, determined from `url`
$opds->getFeeds(); // array - Feeds set with `feeds()` method
$opds->checkIfSearch(); // bool, default is false, set to true if `isSearch()` method is used

以及关于引擎和响应

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->get()
;

$opds->getEngine(); // OpdsEngine|null - Engine used to create OPDS feed, determined by OPDS version, can be `OpdsXmlEngine::class` or `OpdsJsonEngine::class`
$opds->getOutput(); // OpdsOutputEnum|null - Output of response, useful for debug
$opds->getPaginator(); // OpdsPaginator|OpdsPaginate|null - Paginator used to paginate feeds, if you use `paginate()` method
$opds->getResponse(); // OpdsResponse|null - Response of OPDS feed, will use `OpdsEngine` to create a response

OPDS版本

您可以使用查询参数 version 动态设置它。您可以将此查询更改为 OpdsConfig::class

  • 版本 1.2 可以使用 ?v=1.2 设置
  • 版本 2.0 可以使用 ?v=2.0 设置

警告

如果您使用 OpdsConfig::class 方法的 forceJson() 将查询参数 v 设置为 1.2,则查询参数将被忽略。

OPDS引擎

根据OPDS版本,引擎将您的源转换为OPDS。

  • OPDS 1.2 将使用 OpdsXmlEngine::class
  • OPDS 2.0 将使用 OpdsJsonEngine::class

您可以从 Opds::classgetEngine() 方法获取使用的引擎。属性 contents 包含源数组,OpdsEngine 允许使用 __toString() 方法转换为XML或JSON,输出取决于OPDS版本。

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->get()
;

$engine = $opds->getEngine(); // OpdsEngine
$contents = $engine->getContents(); // array
$output = $engine->__toString(); // string

OPDS响应

要构建OPDS源,您必须使用 get() 方法。它将返回一个包含 OpdsEngineOpdsResponse 和分页器的 Opds 实例。

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->get() // `Opds` to fill `OpdsEngine`, `OpdsResponse` and paginator
;

要获取响应,您可以使用 Opds::classgetResponse() 方法。

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->get()
;

$response = $opds->getResponse(); // OpdsResponse

$response->getStatus(); // int - Status code of response
$response->isJson(); // bool - If response is JSON
$response->isXml(); // bool - If response is XML
$response->getHeaders(); // array - Headers of response
$response->getContents(); // string - Contents of response

发送响应

注意

此方法完全可选,您可以直接将响应发送到浏览器。

您可以从 OpdsResponse 直接发送响应到浏览器以获取状态码、头信息和内容,或使用 OpdsOpdsResponse 中可用的 send() 方法。

  • 您可以从 OpdsOpdsResponse 使用 send() 发送响应到浏览器(完全相同)
  • send() 方法之前不需要调用 get() 方法,send() 将自动调用 get()
use Kiwilan\Opds\Opds;

Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->send(); // XML or JSON response
;

如果您想获取 OpdsResponse 实例,可以在 send() 方法之前调用 get() 方法。

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->get()
;

// do something with `OpdsResponse` instance

$opds->send(); // XML or JSON response

获取响应

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->get();

$response = $opds->getResponse(); // OpdsResponse
$response->send(); // XML or JSON response

注意

您可以使用 send() 方法的 exit 参数在发送响应后停止脚本。

OPDS配置

可以使用 OpdsConfig::class 设置OPDS配置。

<?php

use Kiwilan\Opds\OpdsConfig;

$config = new OpdsConfig(
  name: 'My OPDS Catalog', // Name of OPDS feed
  author: 'John Doe', // Author name
  authorUrl: 'https://example.com', // Author URL
  iconUrl: 'https://example.com/icon.png', // Icon URL
  startUrl: 'https://example.com/opds', // Start URL, will be included in top navigation
  searchUrl: 'https://example.com/opds/search', // Search URL, will be included in top navigation
  versionQuery: 'v', // query parameter for version
  paginationQuery: 'page', // query parameter for pagination
  updated: new DateTime(), // Last update of OPDS feed
  maxItemsPerPage: 16, // Max items per page, default is 16
  forceJson: false, // To force JSON response as OPDS 2.0, default is false
);

注意

您可以使用setter方法覆盖 OpdsConfig

OPDS分页

您可以使用 Opdspaginate() 方法使用分页,它将根据 OpdsConfig::classmaxItemsPerPage 属性生成分页。

  • 如果您没有设置任何参数,它将生成分页
  • 如果您设置了 OpdsPaginate 对象,它将根据它生成分页
use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->title('My feed')
  ->feeds([...])
  ->paginate() // will generate pagination
  ->get();

$opds->getPaginator(); // OpdsPaginator

您可以使用 OpdsPaginate::class 来处理手动分页

use Kiwilan\Opds\Opds;

$opds = Opds::make(getConfig())
  ->title('My feed')
  ->url('http://localhost:8080/opds?u=2')
  ->feeds([...])
  ->paginate(new OpdsPaginate(
    currentPage: $page,
    totalItems: $total,
    firstUrl: 'http://localhost:8080/opds?f=1',
    lastUrl: 'http://localhost:8080/opds?l=42',
    previousUrl: 'http://localhost:8080/opds?p=1',
    nextUrl: 'http://localhost:8080/opds?n=3',
  )) // will generate pagination based on `OpdsPaginate` object
  ->get();

$opds->getPaginator(); // OpdsPaginate

OPDS条目

导航

您可以使用 OpdsEntryNavigation::class 创建导航条目

use Kiwilan\Opds\Entries\OpdsEntryNavigation;

$entry = new OpdsEntryNavigation(
  id: 'authors',
  title: 'Authors',
  route: 'http://mylibrary.com/opds/authors',
  summary: 'Authors, 1 available',
  media: 'https://user-images.githubusercontent.com/48261459/201463225-0a5a084e-df15-4b11-b1d2-40fafd3555cf.svg',
  updated: new DateTime(),
  properties: [
    'numberOfItems' => 1,
  ], // to include extra properties (like numberOfItems for facets)
  relation: 'current', // to specify the relation to use (instead of `current`)
);

提示

您可以使用setter方法覆盖 OpdsEntryNavigation

您可以使用 feeds() 方法将此条目添加到OPDS源中

use Kiwilan\Opds\Opds;

$opds = Opds::make()
  ->feeds([$entry])
  ->get();

书籍

您可以使用 OpdsEntryBook::class 创建书籍条目

警告

某些属性只能在OPDS 2.0中使用,请参阅 OPDS 2.0规范

use Kiwilan\Opds\Entries\OpdsEntryBook;
use Kiwilan\Opds\Entries\OpdsEntryBookAuthor;

$entry = new OpdsEntryBook(
  id: 'the-clan-of-the-cave-bear-epub-en',
  title: 'The Clan of the Cave Bear',
  route: 'http://mylibrary.com/opds/books/the-clan-of-the-cave-bear-epub-en',
  summary: 'The Clan of the Cave Bear is an epic work of prehistoric fiction by Jean M. Auel.',
  content: 'The Clan of the Cave Bear is an epic work of prehistoric fiction by Jean M. Auel about prehistoric times. It is the first book in the Earth\'s Children book series which speculates on the possibilities of interactions between Neanderthal and modern Cro-Magnon humans.',
  media: 'https://user-images.githubusercontent.com/48261459/201463225-0a5a084e-df15-4b11-b1d2-40fafd3555cf.svg',
  updated: new DateTime(),
  download: 'http://mylibrary.com/api/download/books/the-clan-of-the-cave-bear-epub-en',
  mediaThumbnail: 'https://user-images.githubusercontent.com/48261459/201463225-0a5a084e-df15-4b11-b1d2-40fafd3555cf.svg',
  categories: ['category'],
  authors: [
    new OpdsEntryBookAuthor(
      name: 'Jean M. Auel',
      uri: 'http://mylibrary.com/opds/authors/jean-m-auel',
    ),
  ],
  published: new DateTime(),
  volume: 1,
  serie: 'Earth\'s Children',
  language: 'English',
  identifier: 'urn:isbn:9780553381672', // to specify the actual identifier to use (instead of `urn:isbn:...`)
  translator: 'translator',
  publisher: 'publisher',
);

提示

您可以使用setter方法覆盖 OpdsEntryBook

您可以使用 feeds() 方法将此条目添加到OPDS源中

$opds = Opds::make()
  ->feeds([$entry])
  ->get();

搜索

此软件包不实现任何搜索引擎,您可以使用自己的搜索引擎并使用 Opds::class 创建OPDS源。

用于搜索的查询参数在规范中静态定义:

  • q 参数由 OPDS 1.2 使用
  • query 参数由 OPDS 2.0 使用

提示

我推荐使用 Meilisearch 作为搜索引擎,它是一个功能强大且易于使用的搜索引擎。

这里有一个示例

use Kiwilan\Opds\Opds;
use Kiwilan\Opds\Entries\OpdsEntryBook;

$query = // get query from URL, `q` or `query` param
$feeds = [];

if ($query) {
    $results = []; // use your search engine here

    foreach ($results as $result) {
      $feeds[] = new OpdsEntryBook(
        title: $result->title,
        // ...
      );
    }
}

$opds = Opds::make()
  ->title("Search for {$query}")
  ->isSearch()
  ->feeds($feeds)
  ->get();

更多用法

测试

composer test

变更日志

有关最近更改的更多信息,请参阅 变更日志

鸣谢

许可

MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件