mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
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:
parent
66fced40e7
commit
69d2fcb43e
91 changed files with 1932 additions and 2456 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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`,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue