1
0
mirror of https://github.com/immich-app/immich.git synced 2025-01-12 15:32:36 +02:00

refactor(web): use ImmichApi to create urls (#2435)

This commit is contained in:
Michel Heusschen 2023-05-14 04:52:29 +02:00 committed by GitHub
parent 15fa8250cb
commit 4524aa0d06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 53 additions and 51 deletions

View File

@ -2,6 +2,7 @@ import {
AlbumApi, AlbumApi,
APIKeyApi, APIKeyApi,
AssetApi, AssetApi,
AssetApiFp,
AuthenticationApi, AuthenticationApi,
Configuration, Configuration,
ConfigurationParameters, ConfigurationParameters,
@ -11,11 +12,12 @@ import {
ServerInfoApi, ServerInfoApi,
ShareApi, ShareApi,
SystemConfigApi, SystemConfigApi,
ThumbnailFormat, UserApi,
UserApi UserApiFp
} from './open-api'; } from './open-api';
import { BASE_PATH } from './open-api/base'; import { BASE_PATH } from './open-api/base';
import { DUMMY_BASE_URL, toPathString } from './open-api/common'; import { DUMMY_BASE_URL, toPathString } from './open-api/common';
import type { ApiParams } from './types';
export class ImmichApi { export class ImmichApi {
public userApi: UserApi; public userApi: UserApi;
@ -75,15 +77,24 @@ export class ImmichApi {
this.config.basePath = baseUrl; this.config.basePath = baseUrl;
} }
public getAssetFileUrl(assetId: string, isThumb?: boolean, isWeb?: boolean, key?: string) { public getAssetFileUrl(
...[assetId, isThumb, isWeb, key]: ApiParams<typeof AssetApiFp, 'serveFile'>
) {
const path = `/asset/file/${assetId}`; const path = `/asset/file/${assetId}`;
return this.createUrl(path, { isThumb, isWeb, key }); return this.createUrl(path, { isThumb, isWeb, key });
} }
public getAssetThumbnailUrl(assetId: string, format?: ThumbnailFormat, key?: string) { public getAssetThumbnailUrl(
...[assetId, format, key]: ApiParams<typeof AssetApiFp, 'getAssetThumbnail'>
) {
const path = `/asset/thumbnail/${assetId}`; const path = `/asset/thumbnail/${assetId}`;
return this.createUrl(path, { format, key }); return this.createUrl(path, { format, key });
} }
public getProfileImageUrl(...[userId]: ApiParams<typeof UserApiFp, 'getProfileImage'>) {
const path = `/user/profile-image/${userId}`;
return this.createUrl(path);
}
} }
export const api = new ImmichApi({ basePath: '/api' }); export const api = new ImmichApi({ basePath: '/api' });

12
web/src/api/types.ts Normal file
View File

@ -0,0 +1,12 @@
import type { Configuration } from './open-api';
/* eslint-disable @typescript-eslint/no-explicit-any */
export type ApiFp = (configuration: Configuration) => Record<any, (...args: any) => any>;
export type OmitLast<T extends readonly unknown[]> = T extends readonly [...infer U, any?]
? U
: [...T];
export type ApiParams<F extends ApiFp, K extends keyof ReturnType<F>> = OmitLast<
Parameters<ReturnType<F>[K]>
>;

View File

@ -2,19 +2,6 @@ import { AxiosError, AxiosPromise } from 'axios';
import { api } from './api'; import { api } from './api';
import { UserResponseDto } from './open-api'; import { UserResponseDto } from './open-api';
const _basePath = '/api';
export function getFileUrl(assetId: string, isThumb?: boolean, isWeb?: boolean, key?: string) {
const urlObj = new URL(`${window.location.origin}${_basePath}/asset/file/${assetId}`);
if (isThumb !== undefined && isThumb !== null)
urlObj.searchParams.append('isThumb', `${isThumb}`);
if (isWeb !== undefined && isWeb !== null) urlObj.searchParams.append('isWeb', `${isWeb}`);
if (key !== undefined && key !== null) urlObj.searchParams.append('key', key);
return urlObj.href;
}
export type ApiError = AxiosError<{ message: string }>; export type ApiError = AxiosError<{ message: string }>;
export const oauth = { export const oauth = {

View File

@ -79,11 +79,6 @@ describe('AlbumCard component', () => {
const albumImgElement = sut.getByTestId('album-image'); const albumImgElement = sut.getByTestId('album-image');
const albumNameElement = sut.getByTestId('album-name'); const albumNameElement = sut.getByTestId('album-name');
const albumDetailsElement = sut.getByTestId('album-details'); const albumDetailsElement = sut.getByTestId('album-details');
// TODO: is this expected?
expect(albumImgElement).toHaveAttribute(
'src',
'/api/asset/thumbnail/thumbnailIdOne?format=WEBP'
);
expect(albumImgElement).toHaveAttribute('alt', album.id); expect(albumImgElement).toHaveAttribute('alt', album.id);
await waitFor(() => expect(albumImgElement).toHaveAttribute('src', thumbnailUrl)); await waitFor(() => expect(albumImgElement).toHaveAttribute('src', thumbnailUrl));

View File

@ -23,10 +23,9 @@
export let isSharingView = false; export let isSharingView = false;
export let user: UserResponseDto; export let user: UserResponseDto;
let imageData = `/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`; $: imageData = album.albumThumbnailAssetId
if (!album.albumThumbnailAssetId) { ? api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)
imageData = noThumbnailUrl; : noThumbnailUrl;
}
const dispatchClick = createEventDispatcher<OnClick>(); const dispatchClick = createEventDispatcher<OnClick>();
const dispatchShowContextMenu = createEventDispatcher<OnShowContextMenu>(); const dispatchShowContextMenu = createEventDispatcher<OnShowContextMenu>();

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { AlbumResponseDto, ThumbnailFormat } from '@api'; import { AlbumResponseDto, ThumbnailFormat, api } from '@api';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
const dispatcher = createEventDispatcher(); const dispatcher = createEventDispatcher();
@ -29,7 +29,8 @@
> >
<div class="h-12 w-12"> <div class="h-12 w-12">
<img <img
src={`/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=${ThumbnailFormat.Webp}`} src={album.albumThumbnailAssetId &&
api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Webp)}
alt={album.albumName} alt={album.albumName}
class={`object-cover h-full w-full transition-all z-0 rounded-xl duration-300 hover:shadow-lg`} class={`object-cover h-full w-full transition-all z-0 rounded-xl duration-300 hover:shadow-lg`}
data-testid="album-image" data-testid="album-image"

View File

@ -5,7 +5,7 @@
import CameraIris from 'svelte-material-icons/CameraIris.svelte'; import CameraIris from 'svelte-material-icons/CameraIris.svelte';
import MapMarkerOutline from 'svelte-material-icons/MapMarkerOutline.svelte'; import MapMarkerOutline from 'svelte-material-icons/MapMarkerOutline.svelte';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { AssetResponseDto, AlbumResponseDto, api } from '@api'; import { AssetResponseDto, AlbumResponseDto, api, ThumbnailFormat } from '@api';
import { asByteUnitString } from '../../utils/byte-units'; import { asByteUnitString } from '../../utils/byte-units';
import { locale } from '$lib/stores/preferences.store'; import { locale } from '$lib/stores/preferences.store';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
@ -241,7 +241,8 @@
<img <img
alt={album.albumName} alt={album.albumName}
class="w-[50px] h-[50px] object-cover rounded" class="w-[50px] h-[50px] object-cover rounded"
src={`/api/asset/thumbnail/${album.albumThumbnailAssetId}?format=JPEG`} src={album.albumThumbnailAssetId &&
api.getAssetThumbnailUrl(album.albumThumbnailAssetId, ThumbnailFormat.Jpeg)}
draggable="false" draggable="false"
/> />
</div> </div>

View File

@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import { api } from '@api';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import { getFileUrl } from '@api';
export let assetId: string; export let assetId: string;
export let publicSharedKey = ''; export let publicSharedKey: string | undefined = undefined;
let isVideoLoading = true; let isVideoLoading = true;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@ -31,7 +31,7 @@
on:canplay={handleCanPlay} on:canplay={handleCanPlay}
on:ended={() => dispatch('onVideoEnded')} on:ended={() => dispatch('onVideoEnded')}
> >
<source src={getFileUrl(assetId, false, true, publicSharedKey)} type="video/mp4" /> <source src={api.getAssetFileUrl(assetId, false, true, publicSharedKey)} type="video/mp4" />
<track kind="captions" /> <track kind="captions" />
</video> </video>

View File

@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import { UserResponseDto } from '@api'; import { UserResponseDto, api } from '@api';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { page } from '$app/stores';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import Cog from 'svelte-material-icons/Cog.svelte'; import Cog from 'svelte-material-icons/Cog.svelte';
import Logout from 'svelte-material-icons/Logout.svelte'; import Logout from 'svelte-material-icons/Logout.svelte';
@ -35,7 +34,7 @@
<img <img
transition:fade={{ duration: 100 }} transition:fade={{ duration: 100 }}
class:hidden={showProfilePictureFallback} class:hidden={showProfilePictureFallback}
src={`${$page.url.origin}/api/user/profile-image/${user.id}`} src={api.getProfileImageUrl(user.id)}
alt="profile-img" alt="profile-img"
class="inline rounded-full h-20 w-20 object-cover shadow-md border-2 border-immich-primary dark:border-immich-dark-primary" class="inline rounded-full h-20 w-20 object-cover shadow-md border-2 border-immich-primary dark:border-immich-dark-primary"
draggable="false" draggable="false"

View File

@ -126,7 +126,7 @@
{#if user.profileImagePath} {#if user.profileImagePath}
<img <img
class:hidden={showProfilePictureFallback} class:hidden={showProfilePictureFallback}
src={`${$page.url.origin}/api/user/profile-image/${user.id}`} src={api.getProfileImageUrl(user.id)}
alt="profile-img" alt="profile-img"
class="inline rounded-full h-12 w-12 object-cover shadow-md border-2 border-immich-primary hover:border-immich-dark-primary dark:hover:border-immich-primary dark:border-immich-dark-primary transition-all" class="inline rounded-full h-12 w-12 object-cover shadow-md border-2 border-immich-primary hover:border-immich-dark-primary dark:hover:border-immich-primary dark:border-immich-dark-primary transition-all"
draggable="false" draggable="false"

View File

@ -1,5 +1,11 @@
<script lang="ts"> <script lang="ts">
import { api, AssetResponseDto, SharedLinkResponseDto, SharedLinkType } from '@api'; import {
api,
AssetResponseDto,
SharedLinkResponseDto,
SharedLinkType,
ThumbnailFormat
} from '@api';
import LoadingSpinner from '../shared-components/loading-spinner.svelte'; import LoadingSpinner from '../shared-components/loading-spinner.svelte';
import OpenInNew from 'svelte-material-icons/OpenInNew.svelte'; import OpenInNew from 'svelte-material-icons/OpenInNew.svelte';
import Delete from 'svelte-material-icons/TrashCanOutline.svelte'; import Delete from 'svelte-material-icons/TrashCanOutline.svelte';
@ -69,7 +75,7 @@
{:then asset} {:then asset}
<img <img
id={asset.id} id={asset.id}
src={`/api/asset/thumbnail/${asset.id}?format=WEBP`} src={api.getAssetThumbnailUrl(asset.id, ThumbnailFormat.Webp)}
alt={asset.id} alt={asset.id}
class="object-cover w-[100px] h-[100px] rounded-lg" class="object-cover w-[100px] h-[100px] rounded-lg"
loading="lazy" loading="lazy"

View File

@ -1,18 +1,10 @@
import { api, AddAssetsResponseDto, AssetResponseDto, ThumbnailFormat } from '@api'; import { api, AddAssetsResponseDto, AssetResponseDto } from '@api';
import { import {
notificationController, notificationController,
NotificationType NotificationType
} from '$lib/components/shared-components/notification/notification'; } from '$lib/components/shared-components/notification/notification';
import { downloadAssets } from '$lib/stores/download'; import { downloadAssets } from '$lib/stores/download';
export const getThumbnailUrl = (assetId: string, format: ThumbnailFormat, key?: string) => {
let url = `/api/asset/thumbnail/${assetId}?format=${format}`;
if (key) {
url += `&key=${key}`;
}
return url;
};
export const addAssetsToAlbum = async ( export const addAssetsToAlbum = async (
albumId: string, albumId: string,
assetIds: Array<string>, assetIds: Array<string>,

View File

@ -1,6 +1,5 @@
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import { getThumbnailUrl } from '$lib/utils/asset-utils'; import { ThumbnailFormat, api as clientApi } from '@api';
import { ThumbnailFormat } from '@api';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
import featurePanelUrl from '$lib/assets/feature-panel.png'; import featurePanelUrl from '$lib/assets/feature-panel.png';
@ -19,7 +18,7 @@ export const load = (async ({ params, locals: { api } }) => {
title: sharedLink.album ? sharedLink.album.albumName : 'Public Share', title: sharedLink.album ? sharedLink.album.albumName : 'Public Share',
description: sharedLink.description || `${assetCount} shared photos & videos.`, description: sharedLink.description || `${assetCount} shared photos & videos.`,
imageUrl: assetId imageUrl: assetId
? getThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key) ? clientApi.getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp, sharedLink.key)
: featurePanelUrl : featurePanelUrl
} }
}; };