refactor: zoom support
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
import { authManager } from '$lib/managers/auth-manager.svelte';
|
||||
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
|
||||
import { CancellableTask } from '$lib/utils/cancellable-task';
|
||||
import type { AssetGridRouteSearchParams } from '$lib/utils/navigation';
|
||||
import { toTimelineAsset } from '$lib/utils/timeline-util';
|
||||
import {
|
||||
getAllAlbums,
|
||||
getAssetInfo,
|
||||
getAssetOriginalPath,
|
||||
getAssetPlaybackPath,
|
||||
getAssetThumbnailPath,
|
||||
getBaseUrl,
|
||||
type AlbumResponseDto,
|
||||
type AssetResponseDto,
|
||||
} from '@immich/sdk';
|
||||
import { type ZoomImageWheelState } from '@zoom-image/core';
|
||||
import { isEqual } from 'lodash-es';
|
||||
|
||||
export enum AssetMediaSize {
|
||||
Original = 'original',
|
||||
Fullsize = 'fullsize',
|
||||
Preview = 'preview',
|
||||
Thumbnail = 'thumbnail',
|
||||
Playback = 'playback',
|
||||
}
|
||||
|
||||
export type AssetManagerOptions = {
|
||||
assetId?: string;
|
||||
preloadAssetIds?: string[];
|
||||
loadAlbums?: boolean;
|
||||
size?: AssetMediaSize;
|
||||
};
|
||||
|
||||
export class AssetManager {
|
||||
isInitialized = $state(false);
|
||||
isLoaded = $state(false);
|
||||
loadError = $state(false);
|
||||
asset: AssetResponseDto | undefined = $state();
|
||||
preloadAssets: TimelineAsset[] = $state([]);
|
||||
albums: AlbumResponseDto[] = $state([]);
|
||||
|
||||
cacheKey: string | null = $derived(this.asset?.thumbhash ?? null);
|
||||
|
||||
url: string | undefined = $derived.by(() => {
|
||||
if (this.asset) {
|
||||
return this.#getAssetUrl(toTimelineAsset(this.asset!));
|
||||
}
|
||||
});
|
||||
|
||||
showAssetViewer: boolean = $state(false);
|
||||
gridScrollTarget: AssetGridRouteSearchParams | undefined = $state();
|
||||
zoomImageState: ZoomImageWheelState | undefined = $state();
|
||||
|
||||
initTask = new CancellableTask(
|
||||
() => (this.isInitialized = true),
|
||||
() => {
|
||||
this.asset = undefined;
|
||||
this.preloadAssets = [];
|
||||
this.albums = [];
|
||||
this.isInitialized = false;
|
||||
},
|
||||
() => void 0,
|
||||
);
|
||||
|
||||
static #INIT_OPTIONS = {};
|
||||
#options: AssetManagerOptions = AssetManager.#INIT_OPTIONS;
|
||||
|
||||
constructor() {}
|
||||
|
||||
async #initializeAsset() {
|
||||
if (this.#options.assetId) {
|
||||
const assetResponse = await getAssetInfo({ id: this.#options.assetId, key: authManager.key });
|
||||
if (!assetResponse) {
|
||||
return;
|
||||
}
|
||||
this.asset = assetResponse;
|
||||
} else {
|
||||
throw new Error('The assetId in required in options.');
|
||||
}
|
||||
|
||||
// TODO: Preload assets.
|
||||
|
||||
if (this.#options.loadAlbums ?? true) {
|
||||
const albumsResponse = await getAllAlbums({ assetId: this.#options.assetId });
|
||||
if (!albumsResponse) {
|
||||
return;
|
||||
}
|
||||
this.albums = albumsResponse;
|
||||
}
|
||||
}
|
||||
|
||||
async updateOptions(options: AssetManagerOptions) {
|
||||
if (this.#options !== AssetManager.#INIT_OPTIONS && isEqual(this.#options, options)) {
|
||||
return;
|
||||
}
|
||||
await this.initTask.reset();
|
||||
await this.#init(options);
|
||||
}
|
||||
|
||||
#checkOptions() {
|
||||
this.#options.size = AssetMediaSize.Original;
|
||||
|
||||
if (!this.asset || !this.zoomImageState) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.asset.originalMimeType === 'image/gif' || this.zoomImageState.currentZoom > 1) {
|
||||
// TODO: use original image forcely and according to the setting.
|
||||
}
|
||||
}
|
||||
|
||||
async #init(options: AssetManagerOptions) {
|
||||
this.isInitialized = false;
|
||||
this.asset = undefined;
|
||||
this.preloadAssets = [];
|
||||
this.albums = [];
|
||||
await this.initTask.execute(async () => {
|
||||
this.#options = options;
|
||||
await this.#initializeAsset();
|
||||
this.#checkOptions();
|
||||
}, true);
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.isInitialized = false;
|
||||
}
|
||||
|
||||
async refreshAlbums() {}
|
||||
|
||||
async refreshAsset() {}
|
||||
|
||||
#preload() {
|
||||
for (const preloadAsset of this.preloadAssets) {
|
||||
if (preloadAsset.isImage) {
|
||||
let img = new Image();
|
||||
const preloadUrl = this.#getAssetUrl(preloadAsset);
|
||||
if (preloadUrl) {
|
||||
img.src = preloadUrl;
|
||||
} else {
|
||||
throw new Error('AssetManager is not initialized.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#getAssetUrl(asset: TimelineAsset) {
|
||||
if (!this.asset) {
|
||||
return;
|
||||
}
|
||||
|
||||
let path = undefined;
|
||||
const searchParameters = new URLSearchParams();
|
||||
if (authManager.key) {
|
||||
searchParameters.set('key', authManager.key);
|
||||
}
|
||||
if (this.cacheKey) {
|
||||
searchParameters.set('c', this.cacheKey);
|
||||
}
|
||||
|
||||
switch (this.#options.size) {
|
||||
case AssetMediaSize.Original: {
|
||||
path = getAssetOriginalPath(this.asset.id);
|
||||
break;
|
||||
}
|
||||
case AssetMediaSize.Fullsize:
|
||||
case AssetMediaSize.Thumbnail:
|
||||
case AssetMediaSize.Preview: {
|
||||
path = getAssetThumbnailPath(this.asset.id);
|
||||
break;
|
||||
}
|
||||
case AssetMediaSize.Playback: {
|
||||
path = getAssetPlaybackPath(this.asset.id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO: default AssetMediaSize
|
||||
}
|
||||
|
||||
return getBaseUrl() + path + '?' + searchParameters.toString();
|
||||
}
|
||||
|
||||
get isOriginalImage() {
|
||||
return this.#options.size === AssetMediaSize.Original || this.#options.size === AssetMediaSize.Fullsize;
|
||||
}
|
||||
}
|
||||
|
||||
// const getAssetUrl = (id: string, targetSize: AssetMediaSize | 'original', cacheKey: string | null) => {
|
||||
// if (sharedLink && (!sharedLink.allowDownload || !sharedLink.showMetadata)) {
|
||||
// return getAssetThumbnailUrl({ id, size: AssetMediaSize.Preview, cacheKey });
|
||||
// }
|
||||
|
||||
// return targetSize === 'original'
|
||||
// ? getAssetOriginalUrl({ id, cacheKey })
|
||||
// : getAssetThumbnailUrl({ id, size: targetSize, cacheKey });
|
||||
// };
|
||||
|
||||
// $effect(() => {
|
||||
// if ($alwaysLoadOriginalFile || forceUseOriginal || originalImageLoaded) {
|
||||
// assetManager.updateOptions({
|
||||
// size: isWebCompatibleImage(asset) ? AssetMediaSize.Original : AssetMediaSize.Fullsize,
|
||||
// });
|
||||
// }
|
||||
// assetManager.updateOptions({ size: AssetMediaSize.Preview });
|
||||
// });
|
||||
Reference in New Issue
Block a user