diff --git a/appinfo/routes.php b/appinfo/routes.php index 42b18552..c3e86bf7 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -52,6 +52,7 @@ return [ ['name' => 'Faces#faces', 'url' => '/api/faces', 'verb' => 'GET'], ['name' => 'Faces#preview', 'url' => '/api/faces/preview/{id}', 'verb' => 'GET'], + ['name' => 'Image#preview', 'url' => '/api/image/preview/{id}', 'verb' => 'GET'], ['name' => 'Image#info', 'url' => '/api/image/info/{id}', 'verb' => 'GET'], ['name' => 'Image#setExif', 'url' => '/api/image/set-exif/{id}', 'verb' => 'PATCH'], ['name' => 'Image#jpeg', 'url' => '/api/image/jpeg/{id}', 'verb' => 'GET'], diff --git a/e2e/folders.spec.ts b/e2e/folders.spec.ts index dd33a760..b292eab7 100644 --- a/e2e/folders.spec.ts +++ b/e2e/folders.spec.ts @@ -12,6 +12,6 @@ test.describe("Open", () => { test("Open folder", async ({ page }) => { await page.locator("text=Local").click(); - await page.waitForSelector('img[src*="core/preview"]'); + await page.waitForSelector('img[src*="api/image/preview"]'); }); }); diff --git a/e2e/login.ts b/e2e/login.ts index 2278626d..5f2b3d91 100644 --- a/e2e/login.ts +++ b/e2e/login.ts @@ -13,6 +13,6 @@ export function login(route: string) { await expect(page).toHaveURL( "http://localhost:8080/index.php/apps/memories" + route ); - await page.waitForSelector('img[src*="core/preview"]'); + await page.waitForSelector('img[src*="api/image/preview"]'); }; } diff --git a/e2e/timeline.spec.ts b/e2e/timeline.spec.ts index 06430ae7..4c1beafa 100644 --- a/e2e/timeline.spec.ts +++ b/e2e/timeline.spec.ts @@ -6,7 +6,7 @@ test.beforeEach(login("/")); test.describe("Open", () => { test("Look for Images", async ({ page }) => { expect( - await page.locator('img[src*="core/preview"]').count(), + await page.locator('img[src*="api/image/preview"]').count(), "Number of previews" ).toBeGreaterThan(4); await page.waitForTimeout(1000); @@ -50,7 +50,7 @@ test.describe("Open", () => { // refresh page await page.reload(); await page.waitForTimeout(4000); // cache - await page.waitForSelector('img[src*="core/preview"]'); + await page.waitForSelector('img[src*="api/image/preview"]'); expect(await page.locator(`img[src="${src1}"]`).count()).toBe(0); expect(await page.locator(`img[src="${src2}"]`).count()).toBe(0); }); diff --git a/lib/Controller/ImageController.php b/lib/Controller/ImageController.php index 5c79a6a8..354b674a 100644 --- a/lib/Controller/ImageController.php +++ b/lib/Controller/ImageController.php @@ -26,10 +26,55 @@ namespace OCA\Memories\Controller; use OCA\Memories\AppInfo\Application; use OCA\Memories\Exif; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\JSONResponse; class ImageController extends ApiBase { + /** + * @NoAdminRequired + * + * @NoCSRFRequired + * + * @PublicPage + * + * Get preview of image + */ + public function preview( + int $id, + int $x = 32, + int $y = 32, + bool $a = false, + string $mode = 'fill' + ) { + if (-1 === $id || 0 === $x || 0 === $y) { + return new JSONResponse([ + 'message' => 'Invalid parameters', + ], Http::STATUS_BAD_REQUEST); + } + + $file = $this->getUserFile($id); + if (!$file) { + return new JSONResponse([ + 'message' => 'File not found', + ], Http::STATUS_NOT_FOUND); + } + + try { + $f = $this->previewManager->getPreview($file, $x, $y, !$a, $mode); + $response = new FileDisplayResponse($f, Http::STATUS_OK, [ + 'Content-Type' => $f->getMimeType(), + ]); + $response->cacheFor(3600 * 24, false, true); + + return $response; + } catch (\OCP\Files\NotFoundException $e) { + return new JSONResponse([], Http::STATUS_NOT_FOUND); + } catch (\InvalidArgumentException $e) { + return new JSONResponse([], Http::STATUS_BAD_REQUEST); + } + } + /** * @NoAdminRequired * diff --git a/src/components/frame/Tag.vue b/src/components/frame/Tag.vue index 4307f07d..bcd3cd19 100644 --- a/src/components/frame/Tag.vue +++ b/src/components/frame/Tag.vue @@ -38,7 +38,7 @@ import { Component, Prop, Watch, Mixins, Emit } from "vue-property-decorator"; import { IAlbum, IPhoto, ITag } from "../../types"; import { generateUrl } from "@nextcloud/router"; -import { getPhotosPreviewUrl, getPreviewUrl } from "../../services/FileUtils"; +import { getPreviewUrl } from "../../services/FileUtils"; import { getCurrentUser } from "@nextcloud/auth"; import NcCounterBubble from "@nextcloud/vue/dist/Components/NcCounterBubble"; @@ -89,10 +89,6 @@ export default class Tag extends Mixins(GlobalMixin) { ); } - if (this.isAlbum) { - return getPhotosPreviewUrl(photo, true, 256); - } - return getPreviewUrl(photo, true, 256); } diff --git a/src/components/modal/AlbumPicker.vue b/src/components/modal/AlbumPicker.vue index 20fd1fd5..18768d8c 100644 --- a/src/components/modal/AlbumPicker.vue +++ b/src/components/modal/AlbumPicker.vue @@ -70,7 +70,7 @@ import NcLoadingIcon from "@nextcloud/vue/dist/Components/NcLoadingIcon"; const NcListItem = () => import("@nextcloud/vue/dist/Components/NcListItem"); import { generateUrl } from "@nextcloud/router"; -import { getPhotosPreviewUrl } from "../../services/FileUtils"; +import { getPreviewUrl } from "../../services/FileUtils"; import { IAlbum, IPhoto } from "../../types"; import axios from "@nextcloud/axios"; @@ -85,7 +85,7 @@ import axios from "@nextcloud/axios"; }, filters: { toCoverUrl(fileId: string) { - return getPhotosPreviewUrl( + return getPreviewUrl( { fileid: Number(fileId), } as IPhoto, diff --git a/src/services/FileUtils.ts b/src/services/FileUtils.ts index 0a15f6bd..e1a3f882 100644 --- a/src/services/FileUtils.ts +++ b/src/services/FileUtils.ts @@ -143,35 +143,23 @@ const getPreviewUrl = function ( const [x, y] = typeof size === "number" ? [size, size] : size; const a = square ? "0" : "1"; - // Public preview - if (vuerouter.currentRoute.name === "folder-share") { - const token = vuerouter.currentRoute.params.token; - return generateUrl( - `/apps/files_sharing/publicpreview/${token}?file=${photo.filename}&fileId=${photo.fileid}&x=${x}&y=${y}&a=${a}` - ); - } + // Get base URL + let url = generateUrl( + `/apps/memories/api/image/preview/${photo.fileid}?c=${photo.etag}&x=${x}&y=${y}&a=${a}` + ); // Albums from Photos if (vuerouter.currentRoute.name === "albums") { - return getPhotosPreviewUrl(photo, square, size); + url += `&album=1`; } - return generateUrl( - `/core/preview?fileId=${photo.fileid}&c=${photo.etag}&x=${x}&y=${y}&forceIcon=0&a=${a}` - ); -}; + // Public preview + if (vuerouter.currentRoute.name === "folder-share") { + const token = vuerouter.currentRoute.params.token; + url += `&folder_share=${token}`; + } -/** Get the preview URL from the photos app */ -const getPhotosPreviewUrl = function ( - photo: IPhoto | IFileInfo, - square: boolean, - size: number | [number, number] -): string { - const [x, y] = typeof size === "number" ? [size, size] : size; - const a = square ? "0" : "1"; - return generateUrl( - `/apps/photos/api/v1/preview/${photo.fileid}?c=${photo.etag}&x=${x}&y=${y}&forceIcon=0&a=${a}` - ); + return url; }; export { @@ -180,5 +168,4 @@ export { sortCompare, genFileInfo, getPreviewUrl, - getPhotosPreviewUrl, }; diff --git a/webpack.js b/webpack.js index 68f45c7c..44c22d52 100644 --- a/webpack.js +++ b/webpack.js @@ -64,12 +64,8 @@ if (!isDev) { urlPattern: /^.*\/apps\/files\/ajax\/download.php?.*/, handler: 'NetworkOnly', }, { - // Preview file request from core - urlPattern: /^.*\/core\/preview\?fileId=.*/, - ...imageCacheOpts(7), - }, { - // Albums from Photos - urlPattern: /^.*\/apps\/photos\/api\/v1\/preview\/.*/, + // Preview file request + urlPattern: /^.*\/apps\/memories\/api\/image\/preview\/.*/, ...imageCacheOpts(7), }, { // Live photo videos