mirror of
https://github.com/immich-app/immich.git
synced 2026-01-22 09:28:56 -08:00
Compare commits
1 Commits
docs-2026
...
push-zzmpz
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ee13c1e3f |
3
mobile/openapi/lib/model/asset_media_size.dart
generated
3
mobile/openapi/lib/model/asset_media_size.dart
generated
@@ -23,12 +23,14 @@ class AssetMediaSize {
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const original = AssetMediaSize._(r'original');
|
||||
static const fullsize = AssetMediaSize._(r'fullsize');
|
||||
static const preview = AssetMediaSize._(r'preview');
|
||||
static const thumbnail = AssetMediaSize._(r'thumbnail');
|
||||
|
||||
/// List of all possible values in this [enum][AssetMediaSize].
|
||||
static const values = <AssetMediaSize>[
|
||||
original,
|
||||
fullsize,
|
||||
preview,
|
||||
thumbnail,
|
||||
@@ -70,6 +72,7 @@ class AssetMediaSizeTypeTransformer {
|
||||
AssetMediaSize? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'original': return AssetMediaSize.original;
|
||||
case r'fullsize': return AssetMediaSize.fullsize;
|
||||
case r'preview': return AssetMediaSize.preview;
|
||||
case r'thumbnail': return AssetMediaSize.thumbnail;
|
||||
|
||||
@@ -16299,6 +16299,7 @@
|
||||
},
|
||||
"AssetMediaSize": {
|
||||
"enum": [
|
||||
"original",
|
||||
"fullsize",
|
||||
"preview",
|
||||
"thumbnail"
|
||||
|
||||
@@ -5658,6 +5658,7 @@ export enum MirrorAxis {
|
||||
Vertical = "vertical"
|
||||
}
|
||||
export enum AssetMediaSize {
|
||||
Original = "original",
|
||||
Fullsize = "fullsize",
|
||||
Preview = "preview",
|
||||
Thumbnail = "thumbnail"
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AssetVisibility } from 'src/enum';
|
||||
import { Optional, ValidateBoolean, ValidateDate, ValidateEnum, ValidateUUID } from 'src/validation';
|
||||
|
||||
export enum AssetMediaSize {
|
||||
Original = 'original',
|
||||
/**
|
||||
* An full-sized image extracted/converted from non-web-friendly formats like RAW/HIF.
|
||||
* or otherwise the original image itself.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { albumFactory } from '@test-data/factories/album-factory';
|
||||
import { render } from '@testing-library/svelte';
|
||||
|
||||
@@ -7,7 +7,7 @@ vi.mock('$lib/utils');
|
||||
|
||||
describe('AlbumCover component', () => {
|
||||
it('renders an image when the album has a thumbnail', () => {
|
||||
vi.mocked(getAssetThumbnailUrl).mockReturnValue('/asdf');
|
||||
vi.mocked(getAssetMediaUrl).mockReturnValue('/asdf');
|
||||
const component = render(AlbumCover, {
|
||||
album: albumFactory.build({
|
||||
albumName: 'someName',
|
||||
@@ -21,7 +21,7 @@ describe('AlbumCover component', () => {
|
||||
expect(img.getAttribute('loading')).toBe('lazy');
|
||||
expect(img.className).toBe('size-full rounded-xl object-cover aspect-square text');
|
||||
expect(img.getAttribute('src')).toBe('/asdf');
|
||||
expect(getAssetThumbnailUrl).toHaveBeenCalledWith({ id: '123' });
|
||||
expect(getAssetMediaUrl).toHaveBeenCalledWith({ id: '123' });
|
||||
});
|
||||
|
||||
it('renders an image when the album has no thumbnail', () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { type AlbumResponseDto } from '@immich/sdk';
|
||||
import NoCover from '$lib/components/sharedlinks-page/covers/no-cover.svelte';
|
||||
import AssetCover from '$lib/components/sharedlinks-page/covers/asset-cover.svelte';
|
||||
import NoCover from '$lib/components/sharedlinks-page/covers/no-cover.svelte';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { type AlbumResponseDto } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
interface Props {
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
let alt = $derived(album.albumName || $t('unnamed_album'));
|
||||
let thumbnailUrl = $derived(
|
||||
album.albumThumbnailAssetId ? getAssetThumbnailUrl({ id: album.albumThumbnailAssetId }) : null,
|
||||
album.albumThumbnailAssetId ? getAssetMediaUrl({ id: album.albumThumbnailAssetId }) : null,
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { assetViewerManager } from '$lib/managers/asset-viewer-manager.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { getAssetType } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { isTenMinutesApart } from '$lib/utils/timesince';
|
||||
@@ -142,7 +142,7 @@
|
||||
<a class="aspect-square w-19 h-19" href={Route.viewAlbumAsset({ albumId, assetId: reaction.assetId })}>
|
||||
<img
|
||||
class="rounded-lg w-19 h-19 object-cover"
|
||||
src={getAssetThumbnailUrl(reaction.assetId)}
|
||||
src={getAssetMediaUrl({ id: reaction.assetId })}
|
||||
alt="Profile picture of {reaction.user.name}, who commented on this asset"
|
||||
/>
|
||||
</a>
|
||||
@@ -195,7 +195,7 @@
|
||||
>
|
||||
<img
|
||||
class="rounded-lg w-19 h-19 object-cover"
|
||||
src={getAssetThumbnailUrl(reaction.assetId)}
|
||||
src={getAssetMediaUrl({ id: reaction.assetId })}
|
||||
alt="Profile picture of {reaction.user.name}, who liked this asset"
|
||||
/>
|
||||
</a>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { SCROLL_PROPERTIES } from '$lib/components/shared-components/album-selection/album-selection-utils';
|
||||
import { mobileDevice } from '$lib/stores/mobile-device.svelte';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { normalizeSearchString } from '$lib/utils/string-utils.js';
|
||||
import { type AlbumResponseDto } from '@immich/sdk';
|
||||
import { Icon } from '@immich/ui';
|
||||
@@ -134,7 +134,7 @@
|
||||
<span class="h-16 w-16 shrink-0 rounded-xl bg-slate-300">
|
||||
{#if album.albumThumbnailAssetId}
|
||||
<img
|
||||
src={getAssetThumbnailUrl(album.albumThumbnailAssetId)}
|
||||
src={getAssetMediaUrl({ id: album.albumThumbnailAssetId })}
|
||||
alt={album.albumName}
|
||||
class={['h-full w-full rounded-xl object-cover transition-all duration-300 hover:shadow-lg']}
|
||||
data-testid="album-image"
|
||||
|
||||
@@ -12,20 +12,19 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { editManager, EditToolType } from '$lib/managers/edit/edit-manager.svelte';
|
||||
import { eventManager } from '$lib/managers/event-manager.svelte';
|
||||
import { preloadManager } from '$lib/managers/PreloadManager.svelte';
|
||||
import { imageManager } from '$lib/managers/ImageManager.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { ocrManager } from '$lib/stores/ocr.svelte';
|
||||
import { alwaysLoadOriginalVideo } from '$lib/stores/preferences.store';
|
||||
import { SlideshowNavigation, SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
|
||||
import { user } from '$lib/stores/user.store';
|
||||
import { getAssetUrl, getSharedLink, handlePromiseError } from '$lib/utils';
|
||||
import { getSharedLink, handlePromiseError } from '$lib/utils';
|
||||
import type { OnUndoDelete } from '$lib/utils/actions';
|
||||
import { navigateToAsset } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { InvocationTracker } from '$lib/utils/invocationTracker';
|
||||
import { SlideshowHistory } from '$lib/utils/slideshow-history';
|
||||
import { preloadImageUrl } from '$lib/utils/sw-messaging';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import {
|
||||
AssetTypeEnum,
|
||||
@@ -133,9 +132,7 @@
|
||||
}
|
||||
|
||||
untrack(() => {
|
||||
if (stack && stack?.assets.length > 1) {
|
||||
preloadImageUrl(getAssetUrl({ asset: stack.assets[1] }));
|
||||
}
|
||||
imageManager.preload(stack?.assets[1]);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -220,7 +217,7 @@
|
||||
}
|
||||
|
||||
e?.stopPropagation();
|
||||
preloadManager.cancel(asset);
|
||||
imageManager.cancel(asset);
|
||||
if (tracker.isActive()) {
|
||||
return;
|
||||
}
|
||||
@@ -380,8 +377,8 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
asset;
|
||||
untrack(() => handlePromiseError(refresh()));
|
||||
preloadManager.preload(cursor.nextAsset);
|
||||
preloadManager.preload(cursor.previousAsset);
|
||||
imageManager.preload(cursor.nextAsset);
|
||||
imageManager.preload(cursor.previousAsset);
|
||||
});
|
||||
|
||||
const onAssetReplace = async ({ oldAssetId, newAssetId }: { oldAssetId: string; newAssetId: string }) => {
|
||||
@@ -502,7 +499,7 @@
|
||||
/>
|
||||
{:else if viewerKind === 'StackVideoViewer'}
|
||||
<VideoViewer
|
||||
assetId={previewStackedAsset!.id}
|
||||
asset={previewStackedAsset!}
|
||||
cacheKey={previewStackedAsset!.thumbhash}
|
||||
projectionType={previewStackedAsset!.exifInfo?.projectionType}
|
||||
loopVideo={true}
|
||||
@@ -515,6 +512,7 @@
|
||||
/>
|
||||
{:else if viewerKind === 'LiveVideoViewer'}
|
||||
<VideoViewer
|
||||
{asset}
|
||||
assetId={asset.livePhotoVideoId!}
|
||||
cacheKey={asset.thumbhash}
|
||||
projectionType={asset.exifInfo?.projectionType}
|
||||
@@ -540,7 +538,7 @@
|
||||
/>
|
||||
{:else if viewerKind === 'VideoViewer'}
|
||||
<VideoViewer
|
||||
assetId={asset.id}
|
||||
{asset}
|
||||
cacheKey={asset.thumbhash}
|
||||
projectionType={asset.exifInfo?.projectionType}
|
||||
loopVideo={$slideshowState !== SlideshowState.PlaySlideshow}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
import { boundingBoxesArray } from '$lib/stores/people.store';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { preferences, user } from '$lib/stores/user.store';
|
||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { delay, getDimensions } from '$lib/utils/asset-utils';
|
||||
import { getByteUnitString } from '$lib/utils/byte-units';
|
||||
import { fromISODateTime, fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
@@ -515,7 +515,7 @@
|
||||
alt={album.albumName}
|
||||
class="h-12.5 w-12.5 rounded object-cover"
|
||||
src={album.albumThumbnailAssetId &&
|
||||
getAssetThumbnailUrl({ id: album.albumThumbnailAssetId, size: AssetMediaSize.Preview })}
|
||||
getAssetMediaUrl({ id: album.albumThumbnailAssetId, size: AssetMediaSize.Preview })}
|
||||
draggable="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { transformManager } from '$lib/managers/edit/transform-manager.svelte';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { AssetMediaSize, type AssetResponseDto } from '@immich/sdk';
|
||||
@@ -14,7 +14,7 @@
|
||||
let canvasContainer = $state<HTMLElement | null>(null);
|
||||
|
||||
let imageSrc = $derived(
|
||||
getAssetThumbnailUrl({ id: asset.id, cacheKey: asset.thumbhash, edited: false, size: AssetMediaSize.Preview }),
|
||||
getAssetMediaUrl({ id: asset.id, cacheKey: asset.thumbhash, edited: false, size: AssetMediaSize.Preview }),
|
||||
);
|
||||
|
||||
let imageTransform = $derived.by(() => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import { getAssetOriginalUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { isWebCompatibleImage } from '$lib/utils/asset-utils';
|
||||
import { getAssetUrl } from '$lib/utils';
|
||||
import { AssetMediaSize, viewAsset, type AssetResponseDto } from '@immich/sdk';
|
||||
import { LoadingSpinner } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
@@ -24,13 +23,7 @@
|
||||
{#await Promise.all([loadAssetData(asset.id), import('./photo-sphere-viewer-adapter.svelte')])}
|
||||
<LoadingSpinner />
|
||||
{:then [data, { default: PhotoSphereViewer }]}
|
||||
<PhotoSphereViewer
|
||||
bind:zoomToggle
|
||||
panorama={data}
|
||||
originalPanorama={isWebCompatibleImage(asset)
|
||||
? getAssetOriginalUrl(asset.id)
|
||||
: getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Fullsize, cacheKey: asset.thumbhash })}
|
||||
/>
|
||||
<PhotoSphereViewer bind:zoomToggle panorama={data} originalPanorama={getAssetUrl({ asset, forceOriginal: true })} />
|
||||
{:catch}
|
||||
{$t('errors.failed_to_load_asset')}
|
||||
{/await}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
|
||||
import { assetViewerFadeDuration } from '$lib/constants';
|
||||
import { castManager } from '$lib/managers/cast-manager.svelte';
|
||||
import { preloadManager } from '$lib/managers/PreloadManager.svelte';
|
||||
import { imageManager } from '$lib/managers/ImageManager.svelte';
|
||||
import { photoViewerImgElement } from '$lib/stores/assets-store.svelte';
|
||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||
import { ocrManager } from '$lib/stores/ocr.svelte';
|
||||
@@ -164,7 +164,7 @@
|
||||
imageError = imageLoaded = true;
|
||||
};
|
||||
|
||||
onDestroy(() => preloadManager.cancelPreloadUrl(imageLoaderUrl));
|
||||
onDestroy(() => imageManager.cancelPreloadUrl(imageLoaderUrl));
|
||||
|
||||
let imageLoaderUrl = $derived(
|
||||
getAssetUrl({ asset, sharedLink, forceOriginal: originalImageLoaded || $photoZoomState.currentZoom > 1 }),
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
videoViewerMuted,
|
||||
videoViewerVolume,
|
||||
} from '$lib/stores/preferences.store';
|
||||
import { getAssetOriginalUrl, getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl, getAssetPlaybackUrl } from '$lib/utils';
|
||||
import { AssetMediaSize } from '@immich/sdk';
|
||||
import { LoadingSpinner } from '@immich/ui';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
@@ -44,7 +44,9 @@
|
||||
let videoPlayer: HTMLVideoElement | undefined = $state();
|
||||
let isLoading = $state(true);
|
||||
let assetFileUrl = $derived(
|
||||
playOriginalVideo ? getAssetOriginalUrl({ id: assetId, cacheKey }) : getAssetPlaybackUrl({ id: assetId, cacheKey }),
|
||||
playOriginalVideo
|
||||
? getAssetMediaUrl({ id: assetId, size: AssetMediaSize.Original, cacheKey })
|
||||
: getAssetPlaybackUrl({ id: assetId, cacheKey }),
|
||||
);
|
||||
let isScrubbing = $state(false);
|
||||
let showVideo = $state(false);
|
||||
@@ -127,7 +129,7 @@
|
||||
{#if castManager.isCasting}
|
||||
<div class="place-content-center h-full place-items-center">
|
||||
<VideoRemoteViewer
|
||||
poster={getAssetThumbnailUrl({ id: assetId, size: AssetMediaSize.Preview, cacheKey })}
|
||||
poster={getAssetMediaUrl({ id: assetId, size: AssetMediaSize.Preview, cacheKey })}
|
||||
{onVideoStarted}
|
||||
{onVideoEnded}
|
||||
{assetFileUrl}
|
||||
@@ -154,7 +156,7 @@
|
||||
onclose={() => onClose()}
|
||||
muted={$videoViewerMuted}
|
||||
bind:volume={$videoViewerVolume}
|
||||
poster={getAssetThumbnailUrl({ id: assetId, size: AssetMediaSize.Preview, cacheKey })}
|
||||
poster={getAssetMediaUrl({ id: assetId, size: AssetMediaSize.Preview, cacheKey })}
|
||||
src={assetFileUrl}
|
||||
>
|
||||
</video>
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { getAssetOriginalUrl, getAssetPlaybackUrl } from '$lib/utils';
|
||||
import { getAssetPlaybackUrl, getAssetUrl } from '$lib/utils';
|
||||
import type { AssetResponseDto } from '@immich/sdk';
|
||||
import { LoadingSpinner } from '@immich/ui';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
interface Props {
|
||||
assetId: string;
|
||||
asset: AssetResponseDto;
|
||||
}
|
||||
|
||||
const { assetId }: Props = $props();
|
||||
const { asset }: Props = $props();
|
||||
|
||||
const modules = Promise.all([
|
||||
import('./photo-sphere-viewer-adapter.svelte').then((module) => module.default),
|
||||
@@ -23,8 +24,8 @@
|
||||
<LoadingSpinner />
|
||||
{:then [PhotoSphereViewer, adapter, videoPlugin]}
|
||||
<PhotoSphereViewer
|
||||
panorama={{ source: getAssetPlaybackUrl(assetId) }}
|
||||
originalPanorama={{ source: getAssetOriginalUrl(assetId) }}
|
||||
panorama={{ source: getAssetPlaybackUrl({ id: asset.id }) }}
|
||||
originalPanorama={{ source: getAssetUrl({ asset, forceOriginal: true })! }}
|
||||
plugins={[videoPlugin]}
|
||||
{adapter}
|
||||
navbar
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
import VideoNativeViewer from '$lib/components/asset-viewer/video-native-viewer.svelte';
|
||||
import VideoPanoramaViewer from '$lib/components/asset-viewer/video-panorama-viewer.svelte';
|
||||
import { ProjectionType } from '$lib/constants';
|
||||
import type { AssetResponseDto } from '@immich/sdk';
|
||||
|
||||
interface Props {
|
||||
assetId: string;
|
||||
asset: AssetResponseDto;
|
||||
assetId?: string;
|
||||
projectionType: string | null | undefined;
|
||||
cacheKey: string | null;
|
||||
loopVideo: boolean;
|
||||
@@ -17,6 +19,7 @@
|
||||
}
|
||||
|
||||
let {
|
||||
asset,
|
||||
assetId,
|
||||
projectionType,
|
||||
cacheKey,
|
||||
@@ -28,15 +31,17 @@
|
||||
onVideoEnded,
|
||||
onVideoStarted,
|
||||
}: Props = $props();
|
||||
|
||||
const effectiveAssetId = $derived(assetId ?? asset.id);
|
||||
</script>
|
||||
|
||||
{#if projectionType === ProjectionType.EQUIRECTANGULAR}
|
||||
<VideoPanoramaViewer {assetId} />
|
||||
<VideoPanoramaViewer {asset} />
|
||||
{:else}
|
||||
<VideoNativeViewer
|
||||
{loopVideo}
|
||||
{cacheKey}
|
||||
{assetId}
|
||||
assetId={effectiveAssetId}
|
||||
{playOriginalVideo}
|
||||
{onPreviousAsset}
|
||||
{onNextAsset}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import BrokenAsset from '$lib/components/assets/broken-asset.svelte';
|
||||
import { preloadManager } from '$lib/managers/PreloadManager.svelte';
|
||||
import { imageManager } from '$lib/managers/ImageManager.svelte';
|
||||
import { Icon } from '@immich/ui';
|
||||
import { mdiEyeOffOutline } from '@mdi/js';
|
||||
import type { ActionReturn } from 'svelte/action';
|
||||
@@ -60,7 +60,7 @@
|
||||
onComplete?.(false);
|
||||
}
|
||||
return {
|
||||
destroy: () => preloadManager.cancelPreloadUrl(url),
|
||||
destroy: () => imageManager.cancelPreloadUrl(url),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { ProjectionType } from '$lib/constants';
|
||||
import { locale, playVideoThumbnailOnHover } from '$lib/stores/preferences.store';
|
||||
import { getAssetOriginalUrl, getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl, getAssetPlaybackUrl } from '$lib/utils';
|
||||
import { timeToSeconds } from '$lib/utils/date-time';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { AssetMediaSize, AssetVisibility, type UserResponseDto } from '@immich/sdk';
|
||||
@@ -335,7 +335,7 @@
|
||||
<ImageThumbnail
|
||||
class={imageClass}
|
||||
{brokenAssetClass}
|
||||
url={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, cacheKey: asset.thumbhash })}
|
||||
url={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Thumbnail, cacheKey: asset.thumbhash })}
|
||||
altText={$getAltText(asset)}
|
||||
widthStyle="{width}px"
|
||||
heightStyle="{height}px"
|
||||
@@ -371,7 +371,7 @@
|
||||
<ImageThumbnail
|
||||
class={imageClass}
|
||||
{brokenAssetClass}
|
||||
url={getAssetOriginalUrl({ id: asset.id, cacheKey: asset.thumbhash })}
|
||||
url={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Original, cacheKey: asset.thumbhash })}
|
||||
altText={$getAltText(asset)}
|
||||
widthStyle="{width}px"
|
||||
heightStyle="{height}px"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { assetViewerFadeDuration } from '$lib/constants';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { AssetMediaSize } from '@immich/sdk';
|
||||
import { LoadingSpinner } from '@immich/ui';
|
||||
@@ -35,7 +35,7 @@
|
||||
};
|
||||
});
|
||||
|
||||
const imageLoaderUrl = $derived(getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Preview }));
|
||||
const imageLoaderUrl = $derived(getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview }));
|
||||
</script>
|
||||
|
||||
{#if !imageLoaded}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { assetViewerFadeDuration } from '$lib/constants';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { autoPlayVideo } from '$lib/stores/preferences.store';
|
||||
import { getAssetPlaybackUrl, getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl, getAssetPlaybackUrl } from '$lib/utils';
|
||||
import { AssetMediaSize } from '@immich/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
@@ -32,7 +32,7 @@
|
||||
playsinline
|
||||
class="h-full w-full rounded-2xl object-contain transition-all"
|
||||
src={getAssetPlaybackUrl({ id: asset.id })}
|
||||
poster={getAssetThumbnailUrl({ id: asset.id, size: AssetMediaSize.Preview })}
|
||||
poster={getAssetMediaUrl({ id: asset.id, size: AssetMediaSize.Preview })}
|
||||
draggable="false"
|
||||
muted={videoViewerMuted}
|
||||
volume={videoViewerVolume}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
import { memoryStore, type MemoryAsset } from '$lib/stores/memory.store.svelte';
|
||||
import { locale, videoViewerMuted, videoViewerVolume } from '$lib/stores/preferences.store';
|
||||
import { preferences } from '$lib/stores/user.store';
|
||||
import { getAssetThumbnailUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
|
||||
import { getAssetMediaUrl, handlePromiseError, memoryLaneTitle } from '$lib/utils';
|
||||
import { cancelMultiselect } from '$lib/utils/asset-utils';
|
||||
import { fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import { AssetMediaSize, AssetTypeEnum, getAssetInfo } from '@immich/sdk';
|
||||
@@ -449,7 +449,7 @@
|
||||
{#if current.previousMemory && current.previousMemory.assets.length > 0}
|
||||
<img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
src={getAssetThumbnailUrl({ id: current.previousMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
||||
src={getAssetMediaUrl({ id: current.previousMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
||||
alt={$t('previous_memory')}
|
||||
draggable="false"
|
||||
/>
|
||||
@@ -598,7 +598,7 @@
|
||||
{#if current.nextMemory && current.nextMemory.assets.length > 0}
|
||||
<img
|
||||
class="h-full w-full rounded-2xl object-cover"
|
||||
src={getAssetThumbnailUrl({ id: current.nextMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
||||
src={getAssetMediaUrl({ id: current.nextMemory.assets[0].id, size: AssetMediaSize.Preview })}
|
||||
alt={$t('next_memory')}
|
||||
draggable="false"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Route } from '$lib/route';
|
||||
import { placesViewSettings } from '$lib/stores/preferences.store';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { type PlacesGroup, isPlacesGroupCollapsed, togglePlacesGroupCollapsing } from '$lib/utils/places-utils';
|
||||
import { AssetMediaSize, type AssetResponseDto } from '@immich/sdk';
|
||||
import { Icon } from '@immich/ui';
|
||||
@@ -45,7 +45,7 @@
|
||||
class="flex w-[calc((100vw-(72px+5rem))/2)] max-w-39 justify-center overflow-hidden rounded-xl brightness-75 filter"
|
||||
>
|
||||
<img
|
||||
src={getAssetThumbnailUrl({ id: item.id, size: AssetMediaSize.Thumbnail })}
|
||||
src={getAssetMediaUrl({ id: item.id, size: AssetMediaSize.Thumbnail })}
|
||||
alt={city}
|
||||
class="object-cover w-39 h-39"
|
||||
loading="lazy"
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { themeManager } from '$lib/managers/theme-manager.svelte';
|
||||
import MapSettingsModal from '$lib/modals/MapSettingsModal.svelte';
|
||||
import { mapSettings } from '$lib/stores/preferences.store';
|
||||
import { getAssetThumbnailUrl, handlePromiseError } from '$lib/utils';
|
||||
import { getAssetMediaUrl, handlePromiseError } from '$lib/utils';
|
||||
import { getMapMarkers, type MapMarkerResponseDto } from '@immich/sdk';
|
||||
import { Icon, modalManager } from '@immich/ui';
|
||||
import { mdiCog, mdiMap, mdiMapMarker } from '@mdi/js';
|
||||
@@ -388,7 +388,7 @@
|
||||
<Icon icon={mdiMapMarker} size="50px" class="text-primary -translate-y-[50%]" />
|
||||
{:else}
|
||||
<img
|
||||
src={getAssetThumbnailUrl(feature.properties?.id)}
|
||||
src={getAssetMediaUrl({ id: feature.properties?.id })}
|
||||
class="rounded-full w-15 h-15 border-2 border-immich-primary shadow-lg hover:border-immich-dark-primary transition-all duration-200 hover:scale-150 object-cover bg-immich-primary"
|
||||
alt={feature.properties?.city && feature.properties.country
|
||||
? $t('map_marker_for_images', {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Route } from '$lib/route';
|
||||
import { userInteraction } from '$lib/stores/user.svelte';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { getAllAlbums, type AlbumResponseDto } from '@immich/sdk';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -34,7 +34,7 @@
|
||||
<div
|
||||
class="h-6 w-6 bg-cover rounded bg-gray-200 dark:bg-gray-600"
|
||||
style={album.albumThumbnailAssetId
|
||||
? `background-image:url('${getAssetThumbnailUrl({ id: album.albumThumbnailAssetId })}')`
|
||||
? `background-image:url('${getAssetMediaUrl({ id: album.albumThumbnailAssetId })}')`
|
||||
: ''}
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import ShareCover from '$lib/components/sharedlinks-page/covers/share-cover.svelte';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { albumFactory } from '@test-data/factories/album-factory';
|
||||
import { assetFactory } from '@test-data/factories/asset-factory';
|
||||
import { sharedLinkFactory } from '@test-data/factories/shared-link-factory';
|
||||
@@ -21,7 +21,7 @@ describe('ShareCover component', () => {
|
||||
});
|
||||
|
||||
it('renders an image when the shared link is an individual share', () => {
|
||||
vi.mocked(getAssetThumbnailUrl).mockReturnValue('/asdf');
|
||||
vi.mocked(getAssetMediaUrl).mockReturnValue('/asdf');
|
||||
const component = render(ShareCover, {
|
||||
sharedLink: sharedLinkFactory.build({ assets: [assetFactory.build({ id: 'someId' })] }),
|
||||
preload: false,
|
||||
@@ -32,7 +32,7 @@ describe('ShareCover component', () => {
|
||||
expect(img.getAttribute('loading')).toBe('lazy');
|
||||
expect(img.className).toBe('size-full rounded-xl object-cover aspect-square text');
|
||||
expect(img.getAttribute('src')).toBe('/asdf');
|
||||
expect(getAssetThumbnailUrl).toHaveBeenCalledWith('someId');
|
||||
expect(getAssetMediaUrl).toHaveBeenCalledWith({ id: 'someId' });
|
||||
});
|
||||
|
||||
it('renders an image when the shared link has no album or assets', () => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import AlbumCover from '$lib/components/album-page/album-cover.svelte';
|
||||
import AssetCover from '$lib/components/sharedlinks-page/covers/asset-cover.svelte';
|
||||
import NoCover from '$lib/components/sharedlinks-page/covers/no-cover.svelte';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import type { SharedLinkResponseDto } from '@immich/sdk';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
alt={$t('individual_share')}
|
||||
class={className}
|
||||
{preload}
|
||||
src={getAssetThumbnailUrl(sharedLink.assets[0].id)}
|
||||
src={getAssetMediaUrl({ id: sharedLink.assets[0].id })}
|
||||
/>
|
||||
{:else}
|
||||
<NoCover alt={$t('unnamed_share')} class={className} {preload} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { getAssetResolution, getFileSize } from '$lib/utils/asset-utils';
|
||||
import { getAltText } from '$lib/utils/thumbnail-util';
|
||||
import { fromISODateTime, fromISODateTimeUTC, toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
@@ -112,7 +112,7 @@
|
||||
>
|
||||
<!-- THUMBNAIL-->
|
||||
<img
|
||||
src={getAssetThumbnailUrl(asset.id)}
|
||||
src={getAssetMediaUrl({ id: asset.id })}
|
||||
alt={$getAltText(toTimelineAsset(asset))}
|
||||
title={assetData}
|
||||
class="h-60 object-cover w-full rounded-t-md"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import type { AlbumResponseDto, PersonResponseDto } from '@immich/sdk';
|
||||
import { Card, CardBody, IconButton, Text } from '@immich/ui';
|
||||
import { mdiClose } from '@mdi/js';
|
||||
@@ -20,7 +20,7 @@
|
||||
{#if isAlbum && 'albumThumbnailAssetId' in item}
|
||||
{#if item.albumThumbnailAssetId}
|
||||
<img
|
||||
src={getAssetThumbnailUrl(item.albumThumbnailAssetId)}
|
||||
src={getAssetMediaUrl({ id: item.albumThumbnailAssetId })}
|
||||
alt={item.albumName}
|
||||
class="h-12 w-12 rounded-lg object-cover"
|
||||
/>
|
||||
|
||||
48
web/src/lib/managers/ImageManager.svelte.ts
Normal file
48
web/src/lib/managers/ImageManager.svelte.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { cancelImageUrl } from '$lib/utils/sw-messaging';
|
||||
import { AssetMediaSize, type AssetResponseDto } from '@immich/sdk';
|
||||
|
||||
const AllAssetMediaSize = {
|
||||
...AssetMediaSize,
|
||||
All: 'all',
|
||||
} as const;
|
||||
|
||||
type AllAssetMediaSize = (typeof AllAssetMediaSize)[keyof typeof AllAssetMediaSize];
|
||||
|
||||
class ImageManager {
|
||||
preload(asset: AssetResponseDto | undefined, size: AssetMediaSize = AssetMediaSize.Preview) {
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = getAssetMediaUrl({ id: asset.id, size, cacheKey: asset.thumbhash });
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
cancel(asset: AssetResponseDto | undefined, size: AllAssetMediaSize = AssetMediaSize.Preview) {
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sizes = size === 'all' ? Object.values(AssetMediaSize) : [size];
|
||||
for (const size of sizes) {
|
||||
const url = getAssetMediaUrl({ id: asset.id, size, cacheKey: asset.thumbhash });
|
||||
if (url) {
|
||||
cancelImageUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cancelPreloadUrl(url: string | undefined) {
|
||||
if (url) {
|
||||
cancelImageUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const imageManager = new ImageManager();
|
||||
@@ -1,38 +0,0 @@
|
||||
import { getAssetUrl } from '$lib/utils';
|
||||
import { cancelImageUrl, preloadImageUrl } from '$lib/utils/sw-messaging';
|
||||
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
||||
|
||||
class PreloadManager {
|
||||
preload(asset: AssetResponseDto | undefined) {
|
||||
if (globalThis.isSecureContext) {
|
||||
preloadImageUrl(getAssetUrl({ asset }));
|
||||
return;
|
||||
}
|
||||
if (!asset || asset.type !== AssetTypeEnum.Image) {
|
||||
return;
|
||||
}
|
||||
const img = new Image();
|
||||
const url = getAssetUrl({ asset });
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
img.src = url;
|
||||
}
|
||||
|
||||
cancel(asset: AssetResponseDto | undefined) {
|
||||
if (!globalThis.isSecureContext || !asset) {
|
||||
return;
|
||||
}
|
||||
const url = getAssetUrl({ asset });
|
||||
cancelImageUrl(url);
|
||||
}
|
||||
|
||||
cancelPreloadUrl(url: string | undefined) {
|
||||
if (!globalThis.isSecureContext) {
|
||||
return;
|
||||
}
|
||||
cancelImageUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
export const preloadManager = new PreloadManager();
|
||||
@@ -1,5 +1,5 @@
|
||||
import { editManager, type EditActions, type EditToolManager } from '$lib/managers/edit/edit-manager.svelte';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { getDimensions } from '$lib/utils/asset-utils';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import {
|
||||
@@ -185,7 +185,7 @@ class TransformManager implements EditToolManager {
|
||||
|
||||
this.imgElement = new Image();
|
||||
|
||||
const imageURL = getAssetThumbnailUrl({
|
||||
const imageURL = getAssetMediaUrl({
|
||||
id: asset.id,
|
||||
cacheKey: asset.thumbhash,
|
||||
edited: false,
|
||||
|
||||
@@ -193,7 +193,7 @@ const createUrl = (path: string, parameters?: Record<string, unknown>) => {
|
||||
return getBaseUrl() + url.pathname + url.search + url.hash;
|
||||
};
|
||||
|
||||
type AssetUrlOptions = { id: string; cacheKey?: string | null; edited?: boolean };
|
||||
type AssetUrlOptions = { id: string; cacheKey?: string | null; edited?: boolean; size?: AssetMediaSize };
|
||||
|
||||
export const getAssetUrl = ({
|
||||
asset,
|
||||
@@ -210,12 +210,10 @@ export const getAssetUrl = ({
|
||||
const id = asset.id;
|
||||
const cacheKey = asset.thumbhash;
|
||||
if (sharedLink && (!sharedLink.allowDownload || !sharedLink.showMetadata)) {
|
||||
return getAssetThumbnailUrl({ id, size: AssetMediaSize.Preview, cacheKey });
|
||||
return getAssetMediaUrl({ id, size: AssetMediaSize.Preview, cacheKey });
|
||||
}
|
||||
const targetSize = targetImageSize(asset, forceOriginal);
|
||||
return targetSize === 'original'
|
||||
? getAssetOriginalUrl({ id, cacheKey })
|
||||
: getAssetThumbnailUrl({ id, size: targetSize, cacheKey });
|
||||
const size = targetImageSize(asset, forceOriginal);
|
||||
return getAssetMediaUrl({ id, size, cacheKey });
|
||||
};
|
||||
|
||||
const forceUseOriginal = (asset: AssetResponseDto) => {
|
||||
@@ -224,33 +222,21 @@ const forceUseOriginal = (asset: AssetResponseDto) => {
|
||||
|
||||
export const targetImageSize = (asset: AssetResponseDto, forceOriginal: boolean) => {
|
||||
if (forceOriginal || get(alwaysLoadOriginalFile) || forceUseOriginal(asset)) {
|
||||
return isWebCompatibleImage(asset) ? 'original' : AssetMediaSize.Fullsize;
|
||||
return isWebCompatibleImage(asset) ? AssetMediaSize.Original : AssetMediaSize.Fullsize;
|
||||
}
|
||||
return AssetMediaSize.Preview;
|
||||
};
|
||||
|
||||
export const getAssetOriginalUrl = (options: string | AssetUrlOptions) => {
|
||||
if (typeof options === 'string') {
|
||||
options = { id: options };
|
||||
}
|
||||
const { id, cacheKey, edited = true } = options;
|
||||
return createUrl(getAssetOriginalPath(id), { ...authManager.params, c: cacheKey, edited });
|
||||
export const getAssetMediaUrl = (options: AssetUrlOptions) => {
|
||||
const { id, size, cacheKey: c, edited = true } = options;
|
||||
const isOriginal = size === AssetMediaSize.Original;
|
||||
const path = isOriginal ? getAssetOriginalPath(id) : getAssetThumbnailPath(id);
|
||||
return createUrl(path, { ...authManager.params, size: isOriginal ? undefined : size, c, edited });
|
||||
};
|
||||
|
||||
export const getAssetThumbnailUrl = (options: string | (AssetUrlOptions & { size?: AssetMediaSize })) => {
|
||||
if (typeof options === 'string') {
|
||||
options = { id: options };
|
||||
}
|
||||
const { id, size, cacheKey, edited = true } = options;
|
||||
return createUrl(getAssetThumbnailPath(id), { ...authManager.params, size, c: cacheKey, edited });
|
||||
};
|
||||
|
||||
export const getAssetPlaybackUrl = (options: string | AssetUrlOptions) => {
|
||||
if (typeof options === 'string') {
|
||||
options = { id: options };
|
||||
}
|
||||
const { id, cacheKey } = options;
|
||||
return createUrl(getAssetPlaybackPath(id), { ...authManager.params, c: cacheKey });
|
||||
export const getAssetPlaybackUrl = (options: AssetUrlOptions) => {
|
||||
const { id, cacheKey: c } = options;
|
||||
return createUrl(getAssetPlaybackPath(id), { ...authManager.params, c });
|
||||
};
|
||||
|
||||
export const getProfileImageUrl = (user: UserResponseDto) =>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Faces } from '$lib/stores/people.store';
|
||||
import { getAssetThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl } from '$lib/utils';
|
||||
import { AssetTypeEnum, type AssetFaceResponseDto } from '@immich/sdk';
|
||||
import type { ZoomImageWheelState } from '@zoom-image/core';
|
||||
|
||||
@@ -82,7 +82,7 @@ export const zoomImageToBase64 = async (
|
||||
if (assetType === AssetTypeEnum.Image) {
|
||||
image = photoViewer;
|
||||
} else if (assetType === AssetTypeEnum.Video) {
|
||||
const data = getAssetThumbnailUrl(assetId);
|
||||
const data = getAssetMediaUrl({ id: assetId });
|
||||
const img: HTMLImageElement = new Image();
|
||||
img.src = data;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getAssetThumbnailUrl, setSharedLink } from '$lib/utils';
|
||||
import { getAssetMediaUrl, setSharedLink } from '$lib/utils';
|
||||
import { authenticate } from '$lib/utils/auth';
|
||||
import { getFormatter } from '$lib/utils/i18n';
|
||||
import { getAssetInfoFromParam } from '$lib/utils/navigation';
|
||||
@@ -36,7 +36,7 @@ export const loadSharedLink = async ({
|
||||
setSharedLink(sharedLink);
|
||||
const assetCount = sharedLink.assets.length;
|
||||
const assetId = sharedLink.album?.albumThumbnailAssetId || sharedLink.assets[0]?.id;
|
||||
const assetPath = assetId ? getAssetThumbnailUrl(assetId) : '/feature-panel.png';
|
||||
const assetPath = assetId ? getAssetMediaUrl({ id: assetId }) : '/feature-panel.png';
|
||||
|
||||
return {
|
||||
...common,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import EmptyPlaceholder from '$lib/components/shared-components/empty-placeholder.svelte';
|
||||
import SingleGridRow from '$lib/components/shared-components/single-grid-row.svelte';
|
||||
import { Route } from '$lib/route';
|
||||
import { getAssetThumbnailUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { getAssetMediaUrl, getPeopleThumbnailUrl } from '$lib/utils';
|
||||
import { AssetMediaSize, type SearchExploreResponseDto } from '@immich/sdk';
|
||||
import { Icon } from '@immich/ui';
|
||||
import { mdiHeart } from '@mdi/js';
|
||||
@@ -90,7 +90,7 @@
|
||||
<a class="relative" href={Route.search({ city: item.value })} draggable="false">
|
||||
<div class="flex justify-center overflow-hidden rounded-xl brightness-75 filter">
|
||||
<img
|
||||
src={getAssetThumbnailUrl({ id: item.data.id, size: AssetMediaSize.Thumbnail })}
|
||||
src={getAssetMediaUrl({ id: item.data.id, size: AssetMediaSize.Thumbnail })}
|
||||
alt={item.value}
|
||||
class="object-cover aspect-square w-full"
|
||||
/>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
import { isFaceEditMode } from '$lib/stores/face-edit.svelte';
|
||||
import { memoryStore } from '$lib/stores/memory.store.svelte';
|
||||
import { preferences, user } from '$lib/stores/user.store';
|
||||
import { getAssetThumbnailUrl, memoryLaneTitle } from '$lib/utils';
|
||||
import { getAssetMediaUrl, memoryLaneTitle } from '$lib/utils';
|
||||
import {
|
||||
updateStackedAssetInTimeline,
|
||||
updateUnstackedAssetInTimeline,
|
||||
@@ -98,7 +98,7 @@
|
||||
title: $memoryLaneTitle(memory),
|
||||
href: Route.memories({ id: memory.assets[0].id }),
|
||||
alt: $t('memory_lane_title', { values: { title: $getAltText(toTimelineAsset(memory.assets[0])) } }),
|
||||
src: getAssetThumbnailUrl(memory.assets[0].id),
|
||||
src: getAssetMediaUrl({ id: memory.assets[0].id }),
|
||||
})),
|
||||
);
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user