feat: add preview providers

This commit is contained in:
Michel Roux 2025-02-22 16:31:48 +00:00
parent f41df55ff2
commit a7da64b4de
9 changed files with 152 additions and 11 deletions

@ -2,13 +2,6 @@
"name": "nextcloud/opds_catalog",
"description": "An example description",
"license": "AGPL-3.0-or-later",
"authors": [
{
"name": "example",
"email": "example@example.com",
"homepage": "https://example.com"
}
],
"autoload": {
"psr-4": {
"OCA\\OpdsCatalog\\": "lib/"
@ -17,7 +10,10 @@
"autoload-dev": {
"psr-4": {
"OCP\\": "vendor/nextcloud/ocp/OCP/"
}
},
"files": [
"stubs/OC_Image.php"
]
},
"scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",

@ -4,6 +4,10 @@ declare(strict_types=1);
namespace OCA\OpdsCatalog\AppInfo;
use OCA\OpdsCatalog\Provider\AmazonPreviewProvider;
use OCA\OpdsCatalog\Provider\ComicBookPreviewProvider;
use OCA\OpdsCatalog\Provider\EpubPreviewProvider;
use OCA\OpdsCatalog\Provider\FictionBookPreviewProvider;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
@ -17,7 +21,12 @@ class Application extends App implements IBootstrap
parent::__construct(self::APP_ID);
}
public function register(IRegistrationContext $context): void {}
public function register(IRegistrationContext $context): void {
$context->registerPreviewProvider(AmazonPreviewProvider::class, '/application\/(vnd.amazon.(ebook|mobi8-ebook)|x-(mobi8|mobipocket)-ebook)/');
$context->registerPreviewProvider(ComicBookPreviewProvider::class, '/application\/(vnd.comicbook[+-](rar|zip)|x-cb[7rtz])/');
$context->registerPreviewProvider(EpubPreviewProvider::class, '/application\/epub\+zip/');
$context->registerPreviewProvider(FictionBookPreviewProvider::class, '/application\/x-fictionbook.*/');
}
public function boot(IBootContext $context): void {}
}

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace OCA\OpdsCatalog\Controller;
use Kiwilan\Opds\Entries\OpdsEntryNavigation;
use Kiwilan\Opds\Enums\OpdsVersionEnum;
use Kiwilan\Opds\Opds;
use Kiwilan\Opds\OpdsConfig;
@ -17,6 +18,9 @@ use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\Http\Response;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUser;
@ -25,6 +29,7 @@ class PageController extends Controller
{
public function __construct(
IRequest $request,
private readonly IRootFolder $rootFolder,
private readonly IUser $user,
private readonly IURLGenerator $urlGenerator
) {
@ -33,8 +38,29 @@ class PageController extends Controller
#[NoCSRFRequired]
#[NoAdminRequired]
#[FrontpageRoute(verb: 'GET', url: '/')]
public function index(): Response {
#[FrontpageRoute(verb: 'GET', url: '/{path}', requirements: ['path' => '.+'])]
public function index(string $path): Response {
$userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
$root = $userFolder->get($path);
$feeds = [];
if ($root instanceof Folder) {
foreach ($root->getDirectoryListing() as $node) {
if ($node instanceof Folder) {
$feeds[] = new OpdsEntryNavigation(
(string) $node->getId(),
$node->getName(),
$userFolder->getRelativePath($node->getPath()) ?? $path.'/'.$node->getName(),
properties: [
'numberOfItems' => count($node->getDirectoryListing()),
],
updated: new \DateTime('@'.$node->getMTime())
);
} elseif ($node instanceof File) {
}
}
}
$config = new OpdsConfig(
name: $this->user->getDisplayName()."'s Library",
author: $this->user->getDisplayName(),

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace OCA\OpdsCatalog\Provider;
class AmazonPreviewProvider extends EbookPreviewProvider
{
public function getMimeType(): string {
return '/application\/(vnd.amazon.(ebook|mobi8-ebook)|x-(mobi8|mobipocket)-ebook)/';
}
}

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace OCA\OpdsCatalog\Provider;
class ComicBookPreviewProvider extends EbookPreviewProvider
{
public function getMimeType(): string {
return '/application\/(vnd.comicbook[+-](rar|zip)|x-cb[7rtz])/';
}
}

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace OCA\OpdsCatalog\Provider;
use Kiwilan\Ebook\Ebook;
use Kiwilan\Ebook\EbookCover;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\IImage;
use OCP\Image;
use OCP\Preview\IProviderV2;
abstract class EbookPreviewProvider implements IProviderV2
{
public function isAvailable(FileInfo $file): bool {
return Ebook::isValid($file->getPath());
}
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
$ebook = Ebook::read($file->getPath());
if ($ebook instanceof Ebook) {
$cover = $ebook->getCover();
if ($cover instanceof EbookCover) {
$path = $cover->getPath();
if (null !== $path) {
$image = new Image();
$image->loadFromFile($path);
$image->scaleDownToFit($maxX, $maxY);
return $image;
}
}
}
return null;
}
}

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace OCA\OpdsCatalog\Provider;
class EpubPreviewProvider extends EbookPreviewProvider
{
public function getMimeType(): string {
return '/application\/epub\+zip/';
}
}

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace OCA\OpdsCatalog\Provider;
class FictionBookPreviewProvider extends EbookPreviewProvider
{
public function getMimeType(): string {
return '/application\/x-fictionbook.*/';
}
}

20
stubs/OC_Image.php Normal file

@ -0,0 +1,20 @@
<?php
use OCP\IImage;
interface OC_Image extends IImage
{
/**
* Loads an image from a local file.
*
* @param bool|string $imagePath the path to a local file
*
* @return bool|GdImage An image resource or false on error
*/
public function loadFromFile($imagePath = false);
/**
* Shrinks larger images to fit within specified boundaries while preserving ratio.
*/
public function scaleDownToFit(int $maxWidth, int $maxHeight): bool;
}