Compare commits
No commits in common. "f246d2c1b5bd330cddd4040245af049d10855224" and "ce1dc19ff9d8e33fb6c4285152e2b9fa14223704" have entirely different histories.
f246d2c1b5
...
ce1dc19ff9
.eslintrc.cjspackage.json
.gitea/workflows
.l10nignore.nvmrcCHANGELOG.mdDockerfileMakefileappinfo
composer.jsoncomposer.locklib
AppInfo
Controller
Provider
src
stubs
stylelint.config.cjstemplates
tsconfig.jsonvite.config.ts
9
.eslintrc.cjs
Normal file
9
.eslintrc.cjs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: ['@nextcloud/eslint-config/vue3', 'plugin:prettier/recommended'],
|
||||||
|
rules: {
|
||||||
|
'jsdoc/require-jsdoc': 'off',
|
||||||
|
'vue/first-attribute-linebreak': 'off',
|
||||||
|
'sort-imports': 'error',
|
||||||
|
'vue/attributes-order': ['error', { alphabetical: true }],
|
||||||
|
},
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
name: opds_catalog
|
name: app_template
|
||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -26,6 +26,24 @@ jobs:
|
|||||||
- run: composer run cs:check
|
- run: composer run cs:check
|
||||||
- run: composer run phpstan
|
- run: composer run phpstan
|
||||||
|
|
||||||
|
nodejs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: skjnldsv/read-package-engines-version-actions@v3
|
||||||
|
id: versions
|
||||||
|
with:
|
||||||
|
fallbackNode: '^20'
|
||||||
|
fallbackNpm: '^10'
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||||
|
- run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run lint
|
||||||
|
- run: npm run stylelint
|
||||||
|
- run: npm run build
|
||||||
|
|
||||||
release:
|
release:
|
||||||
if: gitea.ref_type == 'tag'
|
if: gitea.ref_type == 'tag'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -36,6 +54,15 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: curl -sSLo /usr/local/bin/composer https://getcomposer.org/download/latest-stable/composer.phar
|
- run: curl -sSLo /usr/local/bin/composer https://getcomposer.org/download/latest-stable/composer.phar
|
||||||
- run: chmod +x /usr/local/bin/composer
|
- run: chmod +x /usr/local/bin/composer
|
||||||
|
- uses: skjnldsv/read-package-engines-version-actions@v3
|
||||||
|
id: versions
|
||||||
|
with:
|
||||||
|
fallbackNode: '^20'
|
||||||
|
fallbackNpm: '^10'
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ steps.versions.outputs.nodeVersion }}
|
||||||
|
- run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}'
|
||||||
- run: make dist
|
- run: make dist
|
||||||
- uses: akkuman/gitea-release-action@v1
|
- uses: akkuman/gitea-release-action@v1
|
||||||
with:
|
with:
|
||||||
|
14
.l10nignore
Normal file
14
.l10nignore
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
vendor/
|
||||||
|
vendor-bin/*/vendor/
|
||||||
|
|
||||||
|
.php-cs-fixer.cache
|
||||||
|
tests/.phpunit.cache
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
js/
|
||||||
|
css/
|
||||||
|
|
||||||
|
build/
|
1
.nvmrc
Normal file
1
.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
20
|
46
CHANGELOG.md
46
CHANGELOG.md
@ -1,46 +1,12 @@
|
|||||||
## 0.8.8 - 2018-01-31
|
# Changelog
|
||||||
### Changed
|
|
||||||
- some minor changes to the preference sections
|
|
||||||
|
|
||||||
## 0.8.5 - 0.8.7
|
All notable changes to this project will be documented in this file.
|
||||||
### Changed
|
|
||||||
- debugging NC and OC package signing collision
|
|
||||||
|
|
||||||
## 0.8.4 - 2018-01-19
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||||
### Changed
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
- signed package for publication in Owncloud marketplace
|
|
||||||
|
|
||||||
## 0.8.3 - 2018-01-18
|
## [Unreleased]
|
||||||
### Changed
|
|
||||||
- increased maximum version for OC and NC
|
|
||||||
|
|
||||||
## 0.8.2 - 2017-01-19
|
|
||||||
### Fixed
|
|
||||||
- Support login tokens ('app passwords', NC/OC) and 2FA (NC)
|
|
||||||
|
|
||||||
## 0.8.1 - 2017-01-14
|
|
||||||
### Changed
|
|
||||||
- more robust preview generator, fallback to mimetype icon when showPreview throws exception
|
|
||||||
|
|
||||||
## 0.8.0 - 2017-01-14
|
|
||||||
### New
|
|
||||||
- FictionBook 2 (.fb2) metadata parser
|
|
||||||
- FB2 preview provider
|
|
||||||
|
|
||||||
## 0.7.3 - 2017-01-09
|
|
||||||
### Fixed
|
|
||||||
- XML error after deleting an epub file from Library
|
|
||||||
- [#23](https://github.com/Yetangitu/owncloud-apps/issues/23)
|
|
||||||
|
|
||||||
## 0.7.2 - 2017-01-09
|
|
||||||
### Changed
|
|
||||||
- Modified info.xml, now with working screenshot url...
|
|
||||||
|
|
||||||
## 0.7.1 - 2017-01-09
|
|
||||||
### Added
|
### Added
|
||||||
- Modified info.xml, added screenshot
|
|
||||||
|
|
||||||
## 0.7.0 - 2017-01-09
|
- First release
|
||||||
### Changed
|
|
||||||
- New logo
|
|
||||||
- First release to be compatible with Nextcloud
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
FROM nextcloud:30
|
FROM nextcloud:30
|
||||||
|
|
||||||
ARG APP_NAME=opds_catalog
|
ARG APP_NAME=app_template
|
||||||
ENV NEXTCLOUD_UPDATE=1
|
ENV NEXTCLOUD_UPDATE=1
|
||||||
ENV NEXTCLOUD_ADMIN_USER=$APP_NAME
|
ENV NEXTCLOUD_ADMIN_USER=$APP_NAME
|
||||||
ENV NEXTCLOUD_ADMIN_PASSWORD=$APP_NAME
|
ENV NEXTCLOUD_ADMIN_PASSWORD=$APP_NAME
|
||||||
@ -8,7 +8,7 @@ ENV NEXTCLOUD_INIT_HTACCESS=1
|
|||||||
ENV SQLITE_DATABASE=$APP_NAME
|
ENV SQLITE_DATABASE=$APP_NAME
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y sqlite3 && \
|
apt-get install -y nodejs npm sqlite3 && \
|
||||||
rm -f /usr/local/etc/php/conf.d/opcache-recommended.ini && \
|
rm -f /usr/local/etc/php/conf.d/opcache-recommended.ini && \
|
||||||
/entrypoint.sh true
|
/entrypoint.sh true
|
||||||
|
|
||||||
|
36
Makefile
36
Makefile
@ -19,6 +19,9 @@ build:
|
|||||||
ifneq (,$(wildcard $(CURDIR)/composer.json))
|
ifneq (,$(wildcard $(CURDIR)/composer.json))
|
||||||
make composer
|
make composer
|
||||||
endif
|
endif
|
||||||
|
ifneq (,$(wildcard $(CURDIR)/package.json))
|
||||||
|
make npm
|
||||||
|
endif
|
||||||
|
|
||||||
# Installs and updates the composer dependencies. If composer is not installed
|
# Installs and updates the composer dependencies. If composer is not installed
|
||||||
# a copy is fetched from the web
|
# a copy is fetched from the web
|
||||||
@ -34,6 +37,12 @@ else
|
|||||||
composer install --prefer-dist
|
composer install --prefer-dist
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Installs npm dependencies
|
||||||
|
.PHONY: npm
|
||||||
|
npm:
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
|
||||||
# Removes the appstore build
|
# Removes the appstore build
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@ -44,6 +53,9 @@ clean:
|
|||||||
.PHONY: distclean
|
.PHONY: distclean
|
||||||
distclean: clean
|
distclean: clean
|
||||||
rm -rf vendor
|
rm -rf vendor
|
||||||
|
rm -rf node_modules
|
||||||
|
rm -rf js/vendor
|
||||||
|
rm -rf js/node_modules
|
||||||
|
|
||||||
# Builds the source and appstore package
|
# Builds the source and appstore package
|
||||||
.PHONY: dist
|
.PHONY: dist
|
||||||
@ -59,7 +71,10 @@ source:
|
|||||||
tar -C .. -cvzf $(source_package_name).tar.gz \
|
tar -C .. -cvzf $(source_package_name).tar.gz \
|
||||||
--exclude-vcs \
|
--exclude-vcs \
|
||||||
--exclude="$(app_name)/build" \
|
--exclude="$(app_name)/build" \
|
||||||
|
--exclude="$(app_name)/js/node_modules" \
|
||||||
|
--exclude="$(app_name)/node_modules" \
|
||||||
--exclude="$(app_name)/*.log" \
|
--exclude="$(app_name)/*.log" \
|
||||||
|
--exclude="$(app_name)/js/*.log" \
|
||||||
$(app_name)
|
$(app_name)
|
||||||
|
|
||||||
# Builds the source package for the app store, ignores php tests, js tests
|
# Builds the source package for the app store, ignores php tests, js tests
|
||||||
@ -68,12 +83,14 @@ source:
|
|||||||
appstore:
|
appstore:
|
||||||
rm -rf $(appstore_build_directory)
|
rm -rf $(appstore_build_directory)
|
||||||
mkdir -p $(appstore_build_directory)
|
mkdir -p $(appstore_build_directory)
|
||||||
composer install --prefer-dist --no-dev
|
|
||||||
tar -C .. -cvzf $(appstore_package_name).tar.gz \
|
tar -C .. -cvzf $(appstore_package_name).tar.gz \
|
||||||
$(app_name)/appinfo \
|
$(app_name)/appinfo \
|
||||||
|
$(app_name)/css \
|
||||||
$(app_name)/img \
|
$(app_name)/img \
|
||||||
|
$(app_name)/js \
|
||||||
|
$(app_name)/l10n \
|
||||||
$(app_name)/lib \
|
$(app_name)/lib \
|
||||||
$(app_name)/vendor \
|
$(app_name)/templates \
|
||||||
$(app_name)/CHANGELOG.md
|
$(app_name)/CHANGELOG.md
|
||||||
|
|
||||||
# Start a nextcloud server on Docker to kickstart developement
|
# Start a nextcloud server on Docker to kickstart developement
|
||||||
@ -83,3 +100,18 @@ dev: build
|
|||||||
docker rm $(app_name) || true
|
docker rm $(app_name) || true
|
||||||
docker build -t $(app_name) .
|
docker build -t $(app_name) .
|
||||||
docker run -itd --rm --name $(app_name) -v $(CURDIR):/var/www/html/apps/$(app_name) -p 80:80 $(app_name)
|
docker run -itd --rm --name $(app_name) -v $(CURDIR):/var/www/html/apps/$(app_name) -p 80:80 $(app_name)
|
||||||
|
npm run watch || docker stop $(app_name)
|
||||||
|
|
||||||
|
# Generate translations
|
||||||
|
.PHONY: l10n
|
||||||
|
l10n:
|
||||||
|
docker run --rm \
|
||||||
|
-v $(CURDIR):/app \
|
||||||
|
--entrypoint php \
|
||||||
|
nextcloudci/translations \
|
||||||
|
/translationtool.phar create-pot-files
|
||||||
|
docker run --rm \
|
||||||
|
-v $(CURDIR):/app \
|
||||||
|
--entrypoint php \
|
||||||
|
nextcloudci/translations \
|
||||||
|
/translationtool.phar convert-po-files
|
||||||
|
@ -1,18 +1,27 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||||
<id>opds_catalog</id>
|
<id>app_template</id>
|
||||||
<name>App Template</name>
|
<name>App Template</name>
|
||||||
<summary>An example summary</summary>
|
<summary>An example summary</summary>
|
||||||
<description>An example description</description>
|
<description>An example description</description>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
<licence>agpl</licence>
|
<licence>agpl</licence>
|
||||||
<author mail="example@example.com" homepage="https://example.com">Example</author>
|
<author mail="example@example.com" homepage="https://example.com">Example</author>
|
||||||
<namespace>OpdsCatalog</namespace>
|
<namespace>AppTemplate</namespace>
|
||||||
<category>customization</category>
|
<category>customization</category>
|
||||||
<bugs>https://example.com/bugs</bugs>
|
<bugs>https://example.com/bugs</bugs>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<php min-version="8.1"/>
|
<php min-version="8.1"/>
|
||||||
<nextcloud min-version="29" max-version="31"/>
|
<nextcloud min-version="29" max-version="31"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
<navigations>
|
||||||
|
<navigation>
|
||||||
|
<id>app_template</id>
|
||||||
|
<name>App Template</name>
|
||||||
|
<route>app_template.page.index</route>
|
||||||
|
<icon>app.svg</icon>
|
||||||
|
<type>link</type>
|
||||||
|
</navigation>
|
||||||
|
</navigations>
|
||||||
</info>
|
</info>
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "nextcloud/opds_catalog",
|
"name": "nextcloud/app_template",
|
||||||
"description": "An example description",
|
"description": "An example description",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "example",
|
||||||
|
"email": "example@example.com",
|
||||||
|
"homepage": "https://example.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"OCA\\OpdsCatalog\\": "lib/"
|
"OCA\\AppTemplate\\": "lib/",
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload-dev": {
|
|
||||||
"psr-4": {
|
|
||||||
"OCP\\": "vendor/nextcloud/ocp/OCP/"
|
"OCP\\": "vendor/nextcloud/ocp/OCP/"
|
||||||
},
|
}
|
||||||
"files": [
|
|
||||||
"stubs/OC_Image.php"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
|
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
|
||||||
@ -23,16 +23,14 @@
|
|||||||
"rector": "rector && composer cs:fix"
|
"rector": "rector && composer cs:fix"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.1",
|
"php": "^8.1"
|
||||||
"kiwilan/php-ebook": "^3.0.04",
|
|
||||||
"kiwilan/php-opds": "^2.1.0"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"nextcloud/ocp": "^30.0.6",
|
"nextcloud/ocp": "^30.0.6",
|
||||||
"roave/security-advisories": "dev-latest",
|
"roave/security-advisories": "dev-latest",
|
||||||
"nextcloud/coding-standard": "^1.3.2",
|
"nextcloud/coding-standard": "^1.3.2",
|
||||||
"nextcloud/rector": "^0.3.1",
|
"nextcloud/rector": "^0.3.1",
|
||||||
"phpstan/phpstan": "~1.12.19",
|
"phpstan/phpstan": "~1.12.18",
|
||||||
"rector/rector": "~1.2.10"
|
"rector/rector": "~1.2.10"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
2010
composer.lock
generated
2010
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,12 +2,8 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\OpdsCatalog\AppInfo;
|
namespace OCA\AppTemplate\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\App;
|
||||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
@ -15,18 +11,13 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
|||||||
|
|
||||||
class Application extends App implements IBootstrap
|
class Application extends App implements IBootstrap
|
||||||
{
|
{
|
||||||
public const APP_ID = 'opds_catalog';
|
public const APP_ID = 'app_template';
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct(self::APP_ID);
|
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 {}
|
public function boot(IBootContext $context): void {}
|
||||||
}
|
}
|
||||||
|
@ -2,120 +2,26 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace OCA\OpdsCatalog\Controller;
|
namespace OCA\AppTemplate\Controller;
|
||||||
|
|
||||||
use Kiwilan\Ebook\Ebook;
|
use OCA\AppTemplate\AppInfo\Application;
|
||||||
use Kiwilan\Opds\Entries\OpdsEntryBook;
|
|
||||||
use Kiwilan\Opds\Entries\OpdsEntryBookAuthor;
|
|
||||||
use Kiwilan\Opds\Entries\OpdsEntryNavigation;
|
|
||||||
use Kiwilan\Opds\Enums\OpdsVersionEnum;
|
|
||||||
use Kiwilan\Opds\Opds;
|
|
||||||
use Kiwilan\Opds\OpdsConfig;
|
|
||||||
use Kiwilan\Opds\OpdsResponse;
|
|
||||||
use OCA\OpdsCatalog\AppInfo\Application;
|
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
||||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||||
use OCP\AppFramework\Http\DataResponse;
|
use OCP\AppFramework\Http\Attribute\OpenAPI;
|
||||||
use OCP\AppFramework\Http\JSONResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
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;
|
|
||||||
|
|
||||||
class PageController extends Controller
|
class PageController extends Controller
|
||||||
{
|
{
|
||||||
public function __construct(
|
|
||||||
IRequest $request,
|
|
||||||
private readonly IRootFolder $rootFolder,
|
|
||||||
private readonly IUser $user,
|
|
||||||
private readonly IURLGenerator $urlGenerator
|
|
||||||
) {
|
|
||||||
parent::__construct(Application::APP_ID, $request);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[NoCSRFRequired]
|
#[NoCSRFRequired]
|
||||||
#[NoAdminRequired]
|
#[NoAdminRequired]
|
||||||
#[FrontpageRoute(verb: 'GET', url: '/index/{path}', requirements: ['path' => '.*'])]
|
#[OpenAPI(OpenAPI::SCOPE_IGNORE)]
|
||||||
public function index(string $path): Response {
|
#[FrontpageRoute(verb: 'GET', url: '/')]
|
||||||
$userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
|
public function index(): TemplateResponse {
|
||||||
$root = $userFolder->get($path);
|
return new TemplateResponse(
|
||||||
$feeds = [];
|
Application::APP_ID,
|
||||||
|
'index',
|
||||||
if ($root instanceof Folder) {
|
|
||||||
foreach ($root->getDirectoryListing() as $node) {
|
|
||||||
if ($node instanceof Folder) {
|
|
||||||
$feeds[] = new OpdsEntryNavigation(
|
|
||||||
(string) $node->getId(),
|
|
||||||
$node->getName(),
|
|
||||||
$this->urlGenerator->linkToRouteAbsolute(
|
|
||||||
'opds_catalog.page.index',
|
|
||||||
['path' => $userFolder->getRelativePath($node->getPath()) ?? $path.'/'.$node->getName()]
|
|
||||||
),
|
|
||||||
properties: [
|
|
||||||
'numberOfItems' => count($node->getDirectoryListing()),
|
|
||||||
],
|
|
||||||
updated: new \DateTime('@'.$node->getMTime())
|
|
||||||
);
|
|
||||||
} elseif ($node instanceof File) {
|
|
||||||
$ebook = Ebook::read($node->getPath());
|
|
||||||
if ($ebook instanceof Ebook) {
|
|
||||||
$authors = [];
|
|
||||||
foreach ($ebook->getAuthors() as $author) {
|
|
||||||
if (null !== $author->getName()) {
|
|
||||||
$authors[] = new OpdsEntryBookAuthor($author->getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$feeds[] = new OpdsEntryBook(
|
|
||||||
(string) $node->getId(),
|
|
||||||
$ebook->getTitle() ?? $ebook->getFilename(),
|
|
||||||
$this->urlGenerator->linkToRoute('opds_catalog.page.books', ['path' => $path.'/'.$node->getName()]),
|
|
||||||
summary: $ebook->getDescription(),
|
|
||||||
media: $this->urlGenerator->linkTo('', 'core/preview', ['fileId' => $node->getId(), 'x' => 1024, 'y' => 1024]),
|
|
||||||
updated: new \DateTime('@'.$node->getMTime()),
|
|
||||||
download: $this->urlGenerator->linkTo('', 'remote.php/dav/files/'.$this->user->getUID().'/'.$userFolder->getRelativePath($node->getPath())),
|
|
||||||
mediaThumbnail: $this->urlGenerator->linkTo('', 'core/preview', ['fileId' => $node->getId()]),
|
|
||||||
categories: $ebook->getTags(),
|
|
||||||
authors: $authors,
|
|
||||||
published: $ebook->getPublishDate(),
|
|
||||||
volume: $ebook->getVolume(),
|
|
||||||
serie: $ebook->getSeries(),
|
|
||||||
language: $ebook->getLanguage(),
|
|
||||||
identifier: $ebook->getIdentifiers()[0]->getValue(),
|
|
||||||
publisher: $ebook->getPublisher()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$config = new OpdsConfig(
|
|
||||||
name: $this->user->getDisplayName()."'s Library",
|
|
||||||
author: $this->user->getDisplayName(),
|
|
||||||
iconUrl: $this->urlGenerator->getAbsoluteURL('/avatar/'.$this->user->getUID().'/512'),
|
|
||||||
startUrl: $this->urlGenerator->linkToRouteAbsolute('opds_catalog.page.index')
|
|
||||||
);
|
);
|
||||||
|
|
||||||
$opds = Opds::make($config)->feeds($feeds)->get();
|
|
||||||
$response = $opds->getResponse();
|
|
||||||
if ($response instanceof OpdsResponse) {
|
|
||||||
if (OpdsVersionEnum::v1Dot2 === $opds->getVersion()) {
|
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
return new DataResponse(data: $response->getContents(), headers: $response->getHeaders());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OpdsVersionEnum::v2Dot0 === $opds->getVersion()) {
|
|
||||||
// @phpstan-ignore-next-line
|
|
||||||
return new JSONResponse(data: $response->getJson(), headers: $response->getHeaders());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new NotFoundResponse();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
<?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)/';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<?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])/';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
<?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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace OCA\OpdsCatalog\Provider;
|
|
||||||
|
|
||||||
class EpubPreviewProvider extends EbookPreviewProvider
|
|
||||||
{
|
|
||||||
public function getMimeType(): string {
|
|
||||||
return '/application\/epub\+zip/';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace OCA\OpdsCatalog\Provider;
|
|
||||||
|
|
||||||
class FictionBookPreviewProvider extends EbookPreviewProvider
|
|
||||||
{
|
|
||||||
public function getMimeType(): string {
|
|
||||||
return '/application\/x-fictionbook.*/';
|
|
||||||
}
|
|
||||||
}
|
|
40
package.json
Normal file
40
package.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "app_template",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.0.0",
|
||||||
|
"npm": "^10.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"dev": "vite --mode development build",
|
||||||
|
"watch": "vite --mode development build --watch",
|
||||||
|
"lint": "vue-tsc && eslint src",
|
||||||
|
"lint:fix": "vue-tsc && eslint src --fix",
|
||||||
|
"stylelint": "stylelint src/**/*.vue src/**/*.scss src/**/*.css",
|
||||||
|
"stylelint:fix": "stylelint src/**/*.vue src/**/*.scss src/**/*.css --fix"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"browserslist": [
|
||||||
|
"extends @nextcloud/browserslist-config"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/intl-segmenter": "^11.7.9",
|
||||||
|
"@nextcloud/vite-config": "^2.3.1",
|
||||||
|
"@nextcloud/vue": "9.0.0-alpha.6",
|
||||||
|
"vite": "^6.1.0",
|
||||||
|
"vue": "^3.5.13"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nextcloud/browserslist-config": "^3.0.1",
|
||||||
|
"@nextcloud/eslint-config": "^8.4.2",
|
||||||
|
"@nextcloud/prettier-config": "^1.1.0",
|
||||||
|
"@nextcloud/stylelint-config": "^3.0.1",
|
||||||
|
"@vue/tsconfig": "^0.7.0",
|
||||||
|
"eslint-config-prettier": "^10.0.1",
|
||||||
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
|
"typescript": "~5.5.4",
|
||||||
|
"vue-tsc": "^2.2.2"
|
||||||
|
},
|
||||||
|
"prettier": "@nextcloud/prettier-config"
|
||||||
|
}
|
26
src/App.vue
Normal file
26
src/App.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<NcAppContent>
|
||||||
|
<div id="app_template">
|
||||||
|
<h1>Hello world!</h1>
|
||||||
|
</div>
|
||||||
|
</NcAppContent>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { NcAppContent } from '@nextcloud/vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
NcAppContent,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
#app_template {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
6
src/main.ts
Normal file
6
src/main.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import '@formatjs/intl-segmenter/polyfill'
|
||||||
|
import App from './App.vue'
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
|
||||||
|
const View = createApp(App)
|
||||||
|
View.mount('#app_template')
|
@ -1,20 +0,0 @@
|
|||||||
<?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;
|
|
||||||
}
|
|
3
stylelint.config.cjs
Normal file
3
stylelint.config.cjs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: 'stylelint-config-recommended-vue',
|
||||||
|
}
|
13
templates/index.php
Normal file
13
templates/index.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use OCA\AppTemplate\AppInfo\Application;
|
||||||
|
use OCP\Util;
|
||||||
|
|
||||||
|
Util::addScript(Application::APP_ID, Application::APP_ID.'-main');
|
||||||
|
Util::addStyle(Application::APP_ID, Application::APP_ID.'-main');
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div id="app_template"></div>
|
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"extends": "@vue/tsconfig",
|
||||||
|
"include": ["./src/**/*.ts", "./src/**/*.vue", "**/*.ts"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"rootDir": ".",
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
}
|
||||||
|
}
|
20
vite.config.ts
Normal file
20
vite.config.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { join, resolve } from 'path'
|
||||||
|
import { createAppConfig } from '@nextcloud/vite-config'
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
|
const config = defineConfig({
|
||||||
|
build: {
|
||||||
|
sourcemap: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default createAppConfig(
|
||||||
|
{
|
||||||
|
main: resolve(join('src', 'main.ts')),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
createEmptyCSSEntryPoints: true,
|
||||||
|
thirdPartyLicense: false,
|
||||||
|
},
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user