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