From e071b82e8af792e978db4178e42a9b83934dfe29 Mon Sep 17 00:00:00 2001 From: Dmitry Brazhenko <61410067+dmitry-brazhenko@users.noreply.github.com> Date: Fri, 28 Jul 2023 06:29:09 +0200 Subject: [PATCH] feat (web/server) 360 degrees Web panoramas [attempt 2] (#3412) * commit 1 (isPanorama: boolean) * working solution for projectiontypeenum * fix * format fix * fix * fix * fix * fix * enum projectiontype * working solution with exif * fix * reverted > * fix format * reverted auto-magic api.ts prettification * fix * reverted api.ts autogenerated * api ts regenerated * Update web/src/lib/components/assets/thumbnail/thumbnail.svelte Co-authored-by: Sergey Kondrikov * Update web/src/lib/components/asset-viewer/asset-viewer.svelte Co-authored-by: Sergey Kondrikov * exifProjectionType * Update server/src/microservices/processors/metadata-extraction.processor.ts Co-authored-by: Sergey Kondrikov * projectionType?: string = ProjectionType.NONE; * not null * projectionType!: ProjectionType; * opeapi generator fix * fixes * fix * fix * generate api * asset.exifInifo?.projectionType * Update server/src/domain/asset/response-dto/exif-response.dto.ts Co-authored-by: Jason Rasmussen * Update server/src/microservices/processors/metadata-extraction.processor.ts Co-authored-by: Jason Rasmussen * enum -> varchar;projectiontypeenum->projectiontype * asset-viewer fixed prettiffier * @Column({}) single line * enum | string * make api * enum | string * enum | str fix * fix * chore: use string instead of enum * chore: open api * fix: checks --------- Co-authored-by: Sergey Kondrikov Co-authored-by: Alex Tran Co-authored-by: Jason Rasmussen --- cli/src/api/open-api/api.ts | 6 + mobile/openapi/doc/ExifResponseDto.md | Bin 1274 -> 1323 bytes .../openapi/lib/model/exif_response_dto.dart | Bin 9917 -> 10334 bytes .../openapi/test/exif_response_dto_test.dart | Bin 2514 -> 2627 bytes server/immich-openapi-specs.json | 5 + server/package-lock.json | 75 +++++-------- server/package.json | 2 +- .../asset/response-dto/exif-response.dto.ts | 2 + server/src/infra/entities/exif.entity.ts | 3 + .../migrations/1690469489288-Panoramas.ts | 13 +++ .../metadata-extraction.processor.ts | 6 + server/test/fixtures.ts | 2 + web/package-lock.json | 103 ++++++++++++++++++ web/package.json | 1 + web/src/api/open-api/api.ts | 6 + .../asset-viewer/asset-viewer.svelte | 4 + .../asset-viewer/panorama-viewer.css | 20 ++++ .../asset-viewer/panorama-viewer.svelte | 40 +++++++ .../assets/thumbnail/thumbnail.svelte | 10 ++ web/src/lib/constants.ts | 11 ++ 20 files changed, 262 insertions(+), 47 deletions(-) create mode 100644 server/src/infra/migrations/1690469489288-Panoramas.ts create mode 100644 web/src/lib/components/asset-viewer/panorama-viewer.css create mode 100644 web/src/lib/components/asset-viewer/panorama-viewer.svelte diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index b5c3734a4a..74a8283d52 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -1304,6 +1304,12 @@ export interface ExifResponseDto { * @memberof ExifResponseDto */ 'description'?: string | null; + /** + * + * @type {string} + * @memberof ExifResponseDto + */ + 'projectionType'?: string | null; } /** * diff --git a/mobile/openapi/doc/ExifResponseDto.md b/mobile/openapi/doc/ExifResponseDto.md index dd4b3b4f96b4c33890a92db9a96b397aea312034..144ac4f49c34d2670e69b6a71dc1e0b507a32532 100644 GIT binary patch delta 32 ocmeyxxteQ3D2srWRzXpIR%&udW`15sWkKrX{fvr}Ls^yr0L4rU3IG5A delta 11 ScmZ3@^^0>uD9hwFmZbn3M+Afb diff --git a/mobile/openapi/lib/model/exif_response_dto.dart b/mobile/openapi/lib/model/exif_response_dto.dart index 8af6e8f09b835b446f9fdba62f6d79d5e5f6c8a9..330c254e58c991bd82b2b0fba2d1a9c151cc5190 100644 GIT binary patch delta 335 zcmdn%doN&v5VNR)LPDIC?=(fB9f{et6+=8x04ygV!Z diff --git a/mobile/openapi/test/exif_response_dto_test.dart b/mobile/openapi/test/exif_response_dto_test.dart index 9918892d34dcd156f04647229375e9fbea5e9909..6ab5d1edbd2f5b3d4f5c66f26dd649f6fe7f837e 100644 GIT binary patch delta 59 zcmca4d{|_|3C_v#tbBY0Mfq8&$t9Wjc_Ebrsgo1g6nRmECJQpDb8#ss)M{FDan*7G E0D>J8F8}}l delta 18 ZcmX>sa!Gi@2~I991%+BoYc8%@E&w}G1r7iJ diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index aed9b6c52a..d32eb5dfed 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -5553,6 +5553,11 @@ "type": "string", "nullable": true, "default": null + }, + "projectionType": { + "type": "string", + "nullable": true, + "default": null } } }, diff --git a/server/package-lock.json b/server/package-lock.json index 6666f83c9e..8fdd808640 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -28,7 +28,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "exiftool-vendored": "^19.0.0", + "exiftool-vendored": "^22.0.0", "exiftool-vendored.pl": "^12.54.0", "fluent-ffmpeg": "^2.1.2", "handlebars": "^4.7.7", @@ -4064,9 +4064,9 @@ } }, "node_modules/batch-cluster": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-11.0.0.tgz", - "integrity": "sha512-8iwqa+rKTaakOHkqdcXDT5L5117pa+FoP8/yAKpNdL44ZnC4V2NEA/sIg0ZO0O9NkpdjLk0A3efRFM5nVizqHw==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-12.1.0.tgz", + "integrity": "sha512-whGyJU4tr7kyg2USByu0/51mML5HsLAeNz5s03kMDYZNsQsGgDJgI47RdY3r7MciCjPkTaTD5O4eOVqOfEO7pg==", "engines": { "node": ">=14" } @@ -5976,25 +5976,25 @@ } }, "node_modules/exiftool-vendored": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-19.0.0.tgz", - "integrity": "sha512-Zes7TZrYWxts92mbF2Gs3drtWZucm4qsaeYaE6A+OOqmeD9UGaGisqIbyh9MilJrLi+ZHzWEJZtDj37QFf6xsA==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-22.0.0.tgz", + "integrity": "sha512-gBOQ4C2GLjxKPDPRuUbMOz91mG6IFA22L+Z/IQzFotFu20vc7YroqHALf/ophCbANA5sNSArbVDPijP7n/20Jg==", "dependencies": { "@photostructure/tz-lookup": "^7.0.0", - "@types/luxon": "^3.2.0", - "batch-cluster": "^11.0.0", + "@types/luxon": "^3.3.0", + "batch-cluster": "^12.1.0", "he": "^1.2.0", - "luxon": "^3.2.1" + "luxon": "^3.3.0" }, "optionalDependencies": { - "exiftool-vendored.exe": "12.54.0", - "exiftool-vendored.pl": "12.54.0" + "exiftool-vendored.exe": "12.62.0", + "exiftool-vendored.pl": "12.62.0" } }, "node_modules/exiftool-vendored.exe": { - "version": "12.54.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.54.0.tgz", - "integrity": "sha512-Dc4W6e0NtQfYuJIYK4piHfDJnd2jvA04e0aaq9R3Q1oO34KC5e+L1D2C7lFuZXqPQLYC1x3GYc/GVv5e+SkkrQ==", + "version": "12.62.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.62.0.tgz", + "integrity": "sha512-xNFkvmjwnMg6ivtmkc67w1qD23fIy06nRpMpGuBpTwTqAVatHV+vk7T75zyvLoXRRpd1rKID9XAVLGJCE/iiMQ==", "optional": true, "os": [ "win32" @@ -6008,15 +6008,6 @@ "!win32" ] }, - "node_modules/exiftool-vendored/node_modules/exiftool-vendored.pl": { - "version": "12.54.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.54.0.tgz", - "integrity": "sha512-RBBowsYcM6EvbWoBkg2dOqHpH3WIzN7bIzHc+o+LquqCTo3doZwECClD/6PNHVSMQsl2Z0fEf75sNq2msooMSg==", - "optional": true, - "os": [ - "!win32" - ] - }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -15584,9 +15575,9 @@ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, "batch-cluster": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-11.0.0.tgz", - "integrity": "sha512-8iwqa+rKTaakOHkqdcXDT5L5117pa+FoP8/yAKpNdL44ZnC4V2NEA/sIg0ZO0O9NkpdjLk0A3efRFM5nVizqHw==" + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-12.1.0.tgz", + "integrity": "sha512-whGyJU4tr7kyg2USByu0/51mML5HsLAeNz5s03kMDYZNsQsGgDJgI47RdY3r7MciCjPkTaTD5O4eOVqOfEO7pg==" }, "bcrypt": { "version": "5.1.0", @@ -17011,31 +17002,23 @@ } }, "exiftool-vendored": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-19.0.0.tgz", - "integrity": "sha512-Zes7TZrYWxts92mbF2Gs3drtWZucm4qsaeYaE6A+OOqmeD9UGaGisqIbyh9MilJrLi+ZHzWEJZtDj37QFf6xsA==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-22.0.0.tgz", + "integrity": "sha512-gBOQ4C2GLjxKPDPRuUbMOz91mG6IFA22L+Z/IQzFotFu20vc7YroqHALf/ophCbANA5sNSArbVDPijP7n/20Jg==", "requires": { "@photostructure/tz-lookup": "^7.0.0", - "@types/luxon": "^3.2.0", - "batch-cluster": "^11.0.0", - "exiftool-vendored.exe": "12.54.0", - "exiftool-vendored.pl": "12.54.0", + "@types/luxon": "^3.3.0", + "batch-cluster": "^12.1.0", + "exiftool-vendored.exe": "12.62.0", + "exiftool-vendored.pl": "12.62.0", "he": "^1.2.0", - "luxon": "^3.2.1" - }, - "dependencies": { - "exiftool-vendored.pl": { - "version": "12.54.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-12.54.0.tgz", - "integrity": "sha512-RBBowsYcM6EvbWoBkg2dOqHpH3WIzN7bIzHc+o+LquqCTo3doZwECClD/6PNHVSMQsl2Z0fEf75sNq2msooMSg==", - "optional": true - } + "luxon": "^3.3.0" } }, "exiftool-vendored.exe": { - "version": "12.54.0", - "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.54.0.tgz", - "integrity": "sha512-Dc4W6e0NtQfYuJIYK4piHfDJnd2jvA04e0aaq9R3Q1oO34KC5e+L1D2C7lFuZXqPQLYC1x3GYc/GVv5e+SkkrQ==", + "version": "12.62.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored.exe/-/exiftool-vendored.exe-12.62.0.tgz", + "integrity": "sha512-xNFkvmjwnMg6ivtmkc67w1qD23fIy06nRpMpGuBpTwTqAVatHV+vk7T75zyvLoXRRpd1rKID9XAVLGJCE/iiMQ==", "optional": true }, "exiftool-vendored.pl": { diff --git a/server/package.json b/server/package.json index c55a76c265..852c28602c 100644 --- a/server/package.json +++ b/server/package.json @@ -58,7 +58,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "exiftool-vendored": "^19.0.0", + "exiftool-vendored": "^22.0.0", "exiftool-vendored.pl": "^12.54.0", "fluent-ffmpeg": "^2.1.2", "handlebars": "^4.7.7", diff --git a/server/src/domain/asset/response-dto/exif-response.dto.ts b/server/src/domain/asset/response-dto/exif-response.dto.ts index 0f49f09b9a..8c9e5843c2 100644 --- a/server/src/domain/asset/response-dto/exif-response.dto.ts +++ b/server/src/domain/asset/response-dto/exif-response.dto.ts @@ -24,6 +24,7 @@ export class ExifResponseDto { state?: string | null = null; country?: string | null = null; description?: string | null = null; + projectionType?: string | null = null; } export function mapExif(entity: ExifEntity): ExifResponseDto { @@ -48,5 +49,6 @@ export function mapExif(entity: ExifEntity): ExifResponseDto { state: entity.state, country: entity.country, description: entity.description, + projectionType: entity.projectionType, }; } diff --git a/server/src/infra/entities/exif.entity.ts b/server/src/infra/entities/exif.entity.ts index d4abd6e6d7..799760a2cb 100644 --- a/server/src/infra/entities/exif.entity.ts +++ b/server/src/infra/entities/exif.entity.ts @@ -43,6 +43,9 @@ export class ExifEntity { @Column({ type: 'float', nullable: true }) longitude!: number | null; + @Column({ type: 'varchar', nullable: true }) + projectionType!: string | null; + @Column({ type: 'varchar', nullable: true }) city!: string | null; diff --git a/server/src/infra/migrations/1690469489288-Panoramas.ts b/server/src/infra/migrations/1690469489288-Panoramas.ts new file mode 100644 index 0000000000..ee0934b43a --- /dev/null +++ b/server/src/infra/migrations/1690469489288-Panoramas.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class Panoramas1690217088596 implements MigrationInterface { + name = 'Panoramas1690217088596'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "exif" ADD "projectionType" character varying`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "projectionType"`); + } +} diff --git a/server/src/microservices/processors/metadata-extraction.processor.ts b/server/src/microservices/processors/metadata-extraction.processor.ts index 51f1599983..a37ee6a23a 100644 --- a/server/src/microservices/processors/metadata-extraction.processor.ts +++ b/server/src/microservices/processors/metadata-extraction.processor.ts @@ -336,6 +336,12 @@ export class MetadataExtractionProcessor { await this.extractEmbeddedVideo(asset, offset, null, fileCreatedAt); } } + + const projectionType = getExifProperty('ProjectionType'); + if (projectionType) { + newExif.projectionType = String(projectionType).toUpperCase(); + } + newExif.livePhotoCID = getExifProperty('MediaGroupUUID'); if (newExif.livePhotoCID && !asset.livePhotoVideoId) { const motionAsset = await this.assetRepository.findLivePhotoMatch({ diff --git a/server/test/fixtures.ts b/server/test/fixtures.ts index b4abca792e..54da56961b 100644 --- a/server/test/fixtures.ts +++ b/server/test/fixtures.ts @@ -625,6 +625,7 @@ const assetInfo: ExifResponseDto = { state: 'state', country: 'country', description: 'description', + projectionType: null, }; const assetResponse: AssetResponseDto = { @@ -882,6 +883,7 @@ export const sharedLinkStub = { livePhotoVideoId: null, originalFileName: 'asset_1.jpeg', exifInfo: { + projectionType: null, livePhotoCID: null, assetId: 'id_1', description: 'description', diff --git a/web/package-lock.json b/web/package-lock.json index f870fb061f..9df735b093 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,6 +8,7 @@ "name": "immich-web", "version": "1.0.0", "dependencies": { + "@egjs/svelte-view360": "^4.0.0-beta.7", "@zoom-image/svelte": "^0.1.8", "axios": "^0.27.2", "buffer": "^6.0.3", @@ -1840,6 +1841,47 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@cfcs/core": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/@cfcs/core/-/core-0.0.24.tgz", + "integrity": "sha512-feB38qu+eDk0Pggh/yR7gjaNmvUYA2uCxHP3Pz2MLE4LZ/9jPdtu8bzCSI47yTEhWyZCF5Pk698hdz8IN2mTjA==", + "dependencies": { + "@egjs/component": "^3.0.4" + } + }, + "node_modules/@egjs/component": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@egjs/component/-/component-3.0.4.tgz", + "integrity": "sha512-sXA7bGbIeLF2OAw/vpka66c6QBBUPcA4UUhR4WGJfnp2XWdiI8QrnJGJMr/UxpE/xnevX9tN3jvNPlW8WkHl3g==" + }, + "node_modules/@egjs/imready": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@egjs/imready/-/imready-1.4.1.tgz", + "integrity": "sha512-JIOBs4lB7FYdsKi5uvz2j3SObX8eShtZjtqlOH41tm185aJOQZwiKBK8+V4MxzG4X6DqVhpdN8UcuVwBbElfsg==", + "dependencies": { + "@cfcs/core": "^0.0.24", + "@egjs/component": "^3.0.1" + } + }, + "node_modules/@egjs/svelte-view360": { + "version": "4.0.0-beta.7", + "resolved": "https://registry.npmjs.org/@egjs/svelte-view360/-/svelte-view360-4.0.0-beta.7.tgz", + "integrity": "sha512-qFNbLNME8H7QU2lg8SCKUTPoBXVdBcM5m8zmlDRE72esCTguDzUq2szXD7L1JWcb2lYPTFl3HVp/sZlcQ/1HpQ==", + "dependencies": { + "@egjs/view360": "4.0.0-beta.7" + } + }, + "node_modules/@egjs/view360": { + "version": "4.0.0-beta.7", + "resolved": "https://registry.npmjs.org/@egjs/view360/-/view360-4.0.0-beta.7.tgz", + "integrity": "sha512-prVTTxuQ1/k59NM7G0tm58k2vPHGoaExoFr5E7MoJaSGF56Otj4okQHAxxosXH87aQLN0feZMtBlsKz0b/7zEw==", + "dependencies": { + "@egjs/component": "^3.0.2", + "@egjs/imready": "^1.3.0", + "@types/webxr": "^0.5.1", + "gl-matrix": "^3.4.3" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", @@ -3821,6 +3863,11 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, + "node_modules/@types/webxr": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.2.tgz", + "integrity": "sha512-szL74BnIcok9m7QwYtVmQ+EdIKwbjPANudfuvDrAF8Cljg9MKUlIoc1w5tjj9PMpeSH3U1Xnx//czQybJ0EfSw==" + }, "node_modules/@types/yargs": { "version": "17.0.22", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", @@ -6290,6 +6337,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -13334,6 +13386,47 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@cfcs/core": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/@cfcs/core/-/core-0.0.24.tgz", + "integrity": "sha512-feB38qu+eDk0Pggh/yR7gjaNmvUYA2uCxHP3Pz2MLE4LZ/9jPdtu8bzCSI47yTEhWyZCF5Pk698hdz8IN2mTjA==", + "requires": { + "@egjs/component": "^3.0.4" + } + }, + "@egjs/component": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@egjs/component/-/component-3.0.4.tgz", + "integrity": "sha512-sXA7bGbIeLF2OAw/vpka66c6QBBUPcA4UUhR4WGJfnp2XWdiI8QrnJGJMr/UxpE/xnevX9tN3jvNPlW8WkHl3g==" + }, + "@egjs/imready": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@egjs/imready/-/imready-1.4.1.tgz", + "integrity": "sha512-JIOBs4lB7FYdsKi5uvz2j3SObX8eShtZjtqlOH41tm185aJOQZwiKBK8+V4MxzG4X6DqVhpdN8UcuVwBbElfsg==", + "requires": { + "@cfcs/core": "^0.0.24", + "@egjs/component": "^3.0.1" + } + }, + "@egjs/svelte-view360": { + "version": "4.0.0-beta.7", + "resolved": "https://registry.npmjs.org/@egjs/svelte-view360/-/svelte-view360-4.0.0-beta.7.tgz", + "integrity": "sha512-qFNbLNME8H7QU2lg8SCKUTPoBXVdBcM5m8zmlDRE72esCTguDzUq2szXD7L1JWcb2lYPTFl3HVp/sZlcQ/1HpQ==", + "requires": { + "@egjs/view360": "4.0.0-beta.7" + } + }, + "@egjs/view360": { + "version": "4.0.0-beta.7", + "resolved": "https://registry.npmjs.org/@egjs/view360/-/view360-4.0.0-beta.7.tgz", + "integrity": "sha512-prVTTxuQ1/k59NM7G0tm58k2vPHGoaExoFr5E7MoJaSGF56Otj4okQHAxxosXH87aQLN0feZMtBlsKz0b/7zEw==", + "requires": { + "@egjs/component": "^3.0.2", + "@egjs/imready": "^1.3.0", + "@types/webxr": "^0.5.1", + "gl-matrix": "^3.4.3" + } + }, "@esbuild/android-arm": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", @@ -14748,6 +14841,11 @@ "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", "dev": true }, + "@types/webxr": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.2.tgz", + "integrity": "sha512-szL74BnIcok9m7QwYtVmQ+EdIKwbjPANudfuvDrAF8Cljg9MKUlIoc1w5tjj9PMpeSH3U1Xnx//czQybJ0EfSw==" + }, "@types/yargs": { "version": "17.0.22", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", @@ -16510,6 +16608,11 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true }, + "gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, "glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", diff --git a/web/package.json b/web/package.json index e42f96adb5..9ed929774b 100644 --- a/web/package.json +++ b/web/package.json @@ -60,6 +60,7 @@ }, "type": "module", "dependencies": { + "@egjs/svelte-view360": "^4.0.0-beta.7", "@zoom-image/svelte": "^0.1.8", "axios": "^0.27.2", "buffer": "^6.0.3", diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 7de46248c8..be6fb67978 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -1304,6 +1304,12 @@ export interface ExifResponseDto { * @memberof ExifResponseDto */ 'description'?: string | null; + /** + * + * @type {string} + * @memberof ExifResponseDto + */ + 'projectionType'?: string | null; } /** * diff --git a/web/src/lib/components/asset-viewer/asset-viewer.svelte b/web/src/lib/components/asset-viewer/asset-viewer.svelte index aeaa0eb326..831b0a4a4f 100644 --- a/web/src/lib/components/asset-viewer/asset-viewer.svelte +++ b/web/src/lib/components/asset-viewer/asset-viewer.svelte @@ -12,6 +12,8 @@ import DetailPanel from './detail-panel.svelte'; import PhotoViewer from './photo-viewer.svelte'; import VideoViewer from './video-viewer.svelte'; + import PanoramaViewer from './panorama-viewer.svelte'; + import { ProjectionType } from '$lib/constants'; import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte'; import ProfileImageCropper from '../shared-components/profile-image-cropper.svelte'; @@ -293,6 +295,8 @@ on:close={closeViewer} on:onVideoEnded={() => (shouldPlayMotionPhoto = false)} /> + {:else if asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR} + {:else} {/if} diff --git a/web/src/lib/components/asset-viewer/panorama-viewer.css b/web/src/lib/components/asset-viewer/panorama-viewer.css new file mode 100644 index 0000000000..75898aa822 --- /dev/null +++ b/web/src/lib/components/asset-viewer/panorama-viewer.css @@ -0,0 +1,20 @@ +.view360-container { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + touch-action: pan-y; + overflow: hidden; +} + +.view360-canvas { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + -ms-user-select: none; + user-select: none; + -webkit-user-drag: none; +} diff --git a/web/src/lib/components/asset-viewer/panorama-viewer.svelte b/web/src/lib/components/asset-viewer/panorama-viewer.svelte new file mode 100644 index 0000000000..de195cfbdf --- /dev/null +++ b/web/src/lib/components/asset-viewer/panorama-viewer.svelte @@ -0,0 +1,40 @@ + + +
+ {#await loadAssetData()} + + {:then assetData} + {#if assetData} + + {:else} +

{errorMessage}

+ {/if} + {/await} +
diff --git a/web/src/lib/components/assets/thumbnail/thumbnail.svelte b/web/src/lib/components/assets/thumbnail/thumbnail.svelte index ee5500f759..5e37237266 100644 --- a/web/src/lib/components/assets/thumbnail/thumbnail.svelte +++ b/web/src/lib/components/assets/thumbnail/thumbnail.svelte @@ -1,4 +1,5 @@