diff --git a/composer.json b/composer.json index 4d621a9..ce47578 100644 --- a/composer.json +++ b/composer.json @@ -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", diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index e7de913..b246e02 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -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 {} } diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php index 9dd1b80..0f2a630 100644 --- a/lib/Controller/PageController.php +++ b/lib/Controller/PageController.php @@ -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(), diff --git a/lib/Provider/AmazonPreviewProvider.php b/lib/Provider/AmazonPreviewProvider.php new file mode 100644 index 0000000..963c44d --- /dev/null +++ b/lib/Provider/AmazonPreviewProvider.php @@ -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)/'; + } +} diff --git a/lib/Provider/ComicBookPreviewProvider.php b/lib/Provider/ComicBookPreviewProvider.php new file mode 100644 index 0000000..115e39e --- /dev/null +++ b/lib/Provider/ComicBookPreviewProvider.php @@ -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])/'; + } +} diff --git a/lib/Provider/EbookPreviewProvider.php b/lib/Provider/EbookPreviewProvider.php new file mode 100644 index 0000000..61777d3 --- /dev/null +++ b/lib/Provider/EbookPreviewProvider.php @@ -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; + } +} diff --git a/lib/Provider/EpubPreviewProvider.php b/lib/Provider/EpubPreviewProvider.php new file mode 100644 index 0000000..c6e4bb6 --- /dev/null +++ b/lib/Provider/EpubPreviewProvider.php @@ -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/'; + } +} diff --git a/lib/Provider/FictionBookPreviewProvider.php b/lib/Provider/FictionBookPreviewProvider.php new file mode 100644 index 0000000..18c4566 --- /dev/null +++ b/lib/Provider/FictionBookPreviewProvider.php @@ -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.*/'; + } +} diff --git a/stubs/OC_Image.php b/stubs/OC_Image.php new file mode 100644 index 0000000..07b1811 --- /dev/null +++ b/stubs/OC_Image.php @@ -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; +}