refactor: asset media endpoints (#9831)

* refactor: asset media endpoints

* refactor: mobile upload livePhoto as separate request

* refactor: change mobile backup flow to use new asset upload endpoints

* chore: format and analyze dart code

* feat: mark motion as hidden when linked

* feat: upload video portion of live photo before image portion

* fix: incorrect assetApi calls in mobile code

* fix: download asset

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Zack Pollard <zackpollard@ymail.com>
This commit is contained in:
Jason Rasmussen 2024-05-31 13:44:04 -04:00 committed by GitHub
parent 66fced40e7
commit 69d2fcb43e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
91 changed files with 1932 additions and 2456 deletions

View file

@ -9,7 +9,6 @@
import { isTenMinutesApart } from '$lib/utils/timesince';
import {
ReactionType,
ThumbnailFormat,
createActivity,
deleteActivity,
getActivities,
@ -182,7 +181,7 @@
<a class="aspect-square w-[75px] h-[75px]" href="{AppRoute.ALBUMS}/{albumId}/photos/{reaction.assetId}">
<img
class="rounded-lg w-[75px] h-[75px] object-cover"
src={getAssetThumbnailUrl(reaction.assetId, ThumbnailFormat.Webp)}
src={getAssetThumbnailUrl(reaction.assetId)}
alt="Profile picture of {reaction.user.name}, who commented on this asset"
/>
</a>
@ -235,7 +234,7 @@
>
<img
class="rounded-lg w-[75px] h-[75px] object-cover"
src={getAssetThumbnailUrl(reaction.assetId, ThumbnailFormat.Webp)}
src={getAssetThumbnailUrl(reaction.assetId)}
alt="Profile picture of {reaction.user.name}, who liked this asset"
/>
</a>

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { getAssetThumbnailUrl } from '$lib/utils';
import { ThumbnailFormat, type AlbumResponseDto } from '@immich/sdk';
import { type AlbumResponseDto } from '@immich/sdk';
import { createEventDispatcher } from 'svelte';
import { normalizeSearchString } from '$lib/utils/string-utils.js';
import AlbumListItemDetails from './album-list-item-details.svelte';
@ -35,7 +35,7 @@
<span class="h-12 w-12 shrink-0 rounded-xl bg-slate-300">
{#if album.albumThumbnailAssetId}
<img
src={getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)}
src={getAssetThumbnailUrl(album.albumThumbnailAssetId)}
alt={album.albumName}
class="z-0 h-full w-full rounded-xl object-cover transition-all duration-300 hover:shadow-lg"
data-testid="album-image"

View file

@ -11,7 +11,7 @@
import { getAssetThumbnailUrl, getPeopleThumbnailUrl, handlePromiseError, isSharedLink } from '$lib/utils';
import { delay, isFlipped } from '$lib/utils/asset-utils';
import {
ThumbnailFormat,
AssetMediaSize,
getAssetInfo,
updateAsset,
type AlbumResponseDto,
@ -474,7 +474,7 @@
alt={album.albumName}
class="h-[50px] w-[50px] rounded object-cover"
src={album.albumThumbnailAssetId &&
getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Jpeg)}
getAssetThumbnailUrl({ id: album.albumThumbnailAssetId, size: AssetMediaSize.Preview })}
draggable="false"
/>
</div>

View file

@ -1,9 +1,9 @@
<script lang="ts">
import { serveFile, type AssetResponseDto, AssetTypeEnum } from '@immich/sdk';
import { getAssetOriginalUrl, getKey } from '$lib/utils';
import { AssetMediaSize, AssetTypeEnum, viewAsset, type AssetResponseDto } from '@immich/sdk';
import type { AdapterConstructor, PluginConstructor } from '@photo-sphere-viewer/core';
import { fade } from 'svelte/transition';
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import { getAssetFileUrl, getKey } from '$lib/utils';
import type { AdapterConstructor, PluginConstructor } from '@photo-sphere-viewer/core';
export let asset: Pick<AssetResponseDto, 'id' | 'type'>;
const photoSphereConfigs =
@ -20,9 +20,9 @@
const loadAssetData = async () => {
if (asset.type === AssetTypeEnum.Video) {
return { source: getAssetFileUrl(asset.id, false, false) };
return { source: getAssetOriginalUrl(asset.id) };
}
const data = await serveFile({ id: asset.id, isWeb: false, isThumb: false, key: getKey() });
const data = await viewAsset({ id: asset.id, size: AssetMediaSize.Preview, key: getKey() });
const url = URL.createObjectURL(data);
return url;
};

View file

@ -44,7 +44,7 @@ describe('PhotoViewer component', () => {
expect(downloadRequestMock).toBeCalledWith(
expect.objectContaining({
url: `/api/asset/file/${asset.id}?isThumb=false&isWeb=true&c=${asset.checksum}`,
url: `/api/assets/${asset.id}/thumbnail?size=preview&c=${asset.checksum}`,
}),
);
await waitFor(() => expect(screen.getByRole('img')).toBeInTheDocument());
@ -61,7 +61,7 @@ describe('PhotoViewer component', () => {
await waitFor(() => expect(screen.getByRole('img')).toHaveAttribute('src', 'url-two'));
expect(downloadRequestMock).toBeCalledWith(
expect.objectContaining({
url: `/api/asset/file/${asset.id}?isThumb=false&isWeb=false&c=${asset.checksum}`,
url: `/api/assets/${asset.id}/original?c=${asset.checksum}`,
}),
);
});
@ -76,7 +76,7 @@ describe('PhotoViewer component', () => {
await waitFor(() => expect(screen.getByRole('img')).toHaveAttribute('src', 'url-two'));
expect(downloadRequestMock).toBeCalledWith(
expect.objectContaining({
url: `/api/asset/file/${asset.id}?isThumb=false&isWeb=true&c=new-checksum`,
url: `/api/assets/${asset.id}/thumbnail?size=preview&c=new-checksum`,
}),
);
});

View file

@ -3,11 +3,11 @@
import { boundingBoxesArray } from '$lib/stores/people.store';
import { alwaysLoadOriginalFile } from '$lib/stores/preferences.store';
import { photoZoomState } from '$lib/stores/zoom-image.store';
import { downloadRequest, getAssetFileUrl, handlePromiseError } from '$lib/utils';
import { downloadRequest, getAssetOriginalUrl, getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
import { getBoundingBox } from '$lib/utils/people-utils';
import { shortcuts } from '$lib/actions/shortcut';
import { type AssetResponseDto, AssetTypeEnum } from '@immich/sdk';
import { type AssetResponseDto, AssetTypeEnum, AssetMediaSize } from '@immich/sdk';
import { useZoomImageWheel } from '@zoom-image/svelte';
import { onDestroy, onMount } from 'svelte';
import { fade } from 'svelte/transition';
@ -62,7 +62,9 @@
// TODO: Use sdk once it supports signals
const res = await downloadRequest({
url: getAssetFileUrl(asset.id, !loadOriginal, false, checksum),
url: loadOriginal
? getAssetOriginalUrl({ id: asset.id, checksum })
: getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Preview, checksum }),
signal: abortController.signal,
});
@ -76,7 +78,9 @@
for (const preloadAsset of preloadAssets) {
if (preloadAsset.type === AssetTypeEnum.Image) {
await downloadRequest({
url: getAssetFileUrl(preloadAsset.id, !loadOriginal, false),
url: loadOriginal
? getAssetOriginalUrl(preloadAsset.id)
: getAssetThumbnailUrl({ id: preloadAsset.id, size: AssetMediaSize.Preview }),
signal: abortController.signal,
});
}

View file

@ -1,11 +1,11 @@
<script lang="ts">
import { loopVideo as loopVideoPreference, videoViewerVolume, videoViewerMuted } from '$lib/stores/preferences.store';
import { getAssetFileUrl, getAssetThumbnailUrl } from '$lib/utils';
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
import { loopVideo as loopVideoPreference, videoViewerMuted, videoViewerVolume } from '$lib/stores/preferences.store';
import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { ThumbnailFormat } from '@immich/sdk';
import { AssetMediaSize } from '@immich/sdk';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import LoadingSpinner from '../shared-components/loading-spinner.svelte';
export let assetId: string;
export let loopVideo: boolean;
@ -16,7 +16,7 @@
let assetFileUrl: string;
$: {
const next = getAssetFileUrl(assetId, false, true, checksum);
const next = getAssetPlaybackUrl({ id: assetId, checksum });
if (assetFileUrl !== next) {
assetFileUrl = next;
element && element.load();
@ -54,7 +54,7 @@
on:ended={() => dispatch('onVideoEnded')}
bind:muted={$videoViewerMuted}
bind:volume={$videoViewerVolume}
poster={getAssetThumbnailUrl(assetId, ThumbnailFormat.Jpeg, checksum)}
poster={getAssetThumbnailUrl({ id: assetId, size: AssetMediaSize.Preview, checksum })}
>
<source src={assetFileUrl} type="video/mp4" />
<track kind="captions" />