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 <sergey.kondrikov@gmail.com>

* Update web/src/lib/components/asset-viewer/asset-viewer.svelte

Co-authored-by: Sergey Kondrikov <sergey.kondrikov@gmail.com>

* exifProjectionType

* Update server/src/microservices/processors/metadata-extraction.processor.ts

Co-authored-by: Sergey Kondrikov <sergey.kondrikov@gmail.com>

* 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 <jrasm91@gmail.com>

* Update server/src/microservices/processors/metadata-extraction.processor.ts

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>

* 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 <sergey.kondrikov@gmail.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
Dmitry Brazhenko 2023-07-28 06:29:09 +02:00 committed by GitHub
parent 13b2b2fc4e
commit e071b82e8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 282 additions and 50 deletions

View file

@ -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}
<PanoramaViewer {publicSharedKey} {asset} />
{:else}
<PhotoViewer {publicSharedKey} {asset} on:close={closeViewer} />
{/if}

View file

@ -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;
}

View file

@ -0,0 +1,40 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import { api, AssetResponseDto } from '@api';
import View360, { EquirectProjection } from '@egjs/svelte-view360';
import './panorama-viewer.css';
export let asset: AssetResponseDto;
export let publicSharedKey = '';
let dataUrl = '';
let errorMessage = '';
const loadAssetData = async () => {
try {
const { data } = await api.assetApi.serveFile(
{ id: asset.id, isThumb: false, isWeb: false, key: publicSharedKey },
{ responseType: 'blob' },
);
if (data instanceof Blob) {
dataUrl = URL.createObjectURL(data);
return dataUrl;
} else {
throw new Error('Invalid data format');
}
} catch (error) {
errorMessage = 'Failed to load asset';
return '';
}
};
</script>
<div transition:fade={{ duration: 150 }} class="flex h-full select-none place-content-center place-items-center">
{#await loadAssetData()}
<LoadingSpinner />
{:then assetData}
{#if assetData}
<View360 autoResize={true} initialZoom={0.5} projection={new EquirectProjection({ src: assetData })} />
{:else}
<p>{errorMessage}</p>
{/if}
{/await}
</div>

View file

@ -1,4 +1,5 @@
<script lang="ts">
import { ProjectionType } from '$lib/constants';
import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte';
import { timeToSeconds } from '$lib/utils/time-to-seconds';
import { api, AssetResponseDto, AssetTypeEnum, ThumbnailFormat } from '@api';
@ -12,6 +13,7 @@
import { fade } from 'svelte/transition';
import ImageThumbnail from './image-thumbnail.svelte';
import VideoThumbnail from './video-thumbnail.svelte';
import Rotate360Icon from 'svelte-material-icons/Rotate360.svelte';
const dispatch = createEventDispatcher();
@ -124,6 +126,14 @@
</div>
{/if}
{#if asset.type === AssetTypeEnum.Image && asset.exifInfo?.projectionType === ProjectionType.EQUIRECTANGULAR}
<div class="absolute right-0 top-0 z-20 flex place-items-center gap-1 text-xs font-medium text-white">
<span class="pr-2 pt-2">
<Rotate360Icon size="24" />
</span>
</div>
{/if}
{#if asset.resized}
<ImageThumbnail
url={api.getAssetThumbnailUrl(asset.id, format, publicSharedKey)}