From a5ec880eec276e0814d2ab3ed7f9bc0b804d0465 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 3 Jul 2026 15:59:08 +0200 Subject: [PATCH] =?UTF-8?q?fix(immich):=20v3=20API=20compatibility=20?= =?UTF-8?q?=E2=80=94=20use=20searchAssets=20for=20album=20assets,=20drop?= =?UTF-8?q?=20deviceAssetId=20(#5977)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/src/router/widgets/releases.ts | 9 +++-- .../src/immich/immich-integration.ts | 39 ++++++++++++++++--- .../request-handler/src/release-providers.ts | 2 +- .../src/immich/album-carousel/component.tsx | 1 - pnpm-lock.yaml | 37 +++++------------- pnpm-workspace.yaml | 3 +- 6 files changed, 52 insertions(+), 39 deletions(-) diff --git a/packages/api/src/router/widgets/releases.ts b/packages/api/src/router/widgets/releases.ts index 4ef99b846..08d9ac270 100644 --- a/packages/api/src/router/widgets/releases.ts +++ b/packages/api/src/router/widgets/releases.ts @@ -77,11 +77,12 @@ export const releasesRouter = createTRPCRouter({ return await Promise.all( input.repositories.map(async (repository) => { + const repositoryId = repository.id ?? repository.identifier; try { - const useToken = allowedRepoIds === null || allowedRepoIds.has(repository.id); + const useToken = allowedRepoIds === null || allowedRepoIds.has(repositoryId); const response = await releasesRequestHandler .handler({ - id: repository.id, + id: repositoryId, provider: repository.provider, identifier: repository.identifier, versionRegex: formatVersionFilterRegex(repository.versionFilter), @@ -93,14 +94,14 @@ export const releasesRouter = createTRPCRouter({ }); return { - id: repository.id, + id: repositoryId, provider: repository.provider, timestamp: response.timestamp, ...response.data, }; } catch (error) { return { - id: repository.id, + id: repositoryId, provider: repository.provider, timestamp: new Date(), success: false as const, diff --git a/packages/integrations/src/immich/immich-integration.ts b/packages/integrations/src/immich/immich-integration.ts index 9e13ff953..c01386256 100644 --- a/packages/integrations/src/immich/immich-integration.ts +++ b/packages/integrations/src/immich/immich-integration.ts @@ -1,4 +1,13 @@ -import { getAlbumInfo, getAllAlbums, getMyUser, getServerStatistics, init, searchUsers } from "@immich/sdk"; +import { + getAlbumInfo, + getAllAlbums, + getMyUser, + getServerStatistics, + init, + searchAssets, + searchUsers, +} from "@immich/sdk"; +import type { AssetResponseDto, MetadataSearchDto } from "@immich/sdk"; import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http"; import { createLogger } from "@homarr/core/infrastructure/logs"; @@ -23,7 +32,6 @@ export interface ImmichAlbum { export interface ImmichAsset { id: string; - deviceAssetId: string; originalPath: string; thumbhash: string | null; fileModifiedAt: string; @@ -56,12 +64,16 @@ export class ImmichIntegration extends Integration { public async getAlbumAsync(albumId: string): Promise { this.initClient(); - const album = await getAlbumInfo({ id: albumId }, this.getRequestOptions()); + const requestOptions = this.getRequestOptions(); + const [album, albumAssets] = await Promise.all([ + getAlbumInfo({ id: albumId }, requestOptions), + this.searchAlbumAssetsAsync(albumId), + ]); const imageProxy = new ImageProxy(); return { albumName: album.albumName, assets: await Promise.all( - album.assets.map(async (asset) => { + albumAssets.map(async (asset) => { const publicLink = await imageProxy.createImageAsync( this.url(`/api/assets/${asset.id}/original`).toString(), { @@ -71,7 +83,6 @@ export class ImmichIntegration extends Integration { return { id: asset.id, type: asset.type, - deviceAssetId: asset.deviceAssetId, thumbhash: asset.thumbhash, fileCreatedAt: asset.fileCreatedAt, fileModifiedAt: asset.fileModifiedAt, @@ -104,6 +115,24 @@ export class ImmichIntegration extends Integration { return { success: true }; } + private async searchAlbumAssetsAsync(albumId: string): Promise { + const requestOptions = this.getRequestOptions(); + const assets: AssetResponseDto[] = []; + let nextPage: string | null = null; + + do { + const metadataSearchDto: MetadataSearchDto = { albumIds: [albumId] }; + if (nextPage !== null) { + metadataSearchDto.page = Number(nextPage); + } + const searchResult = await searchAssets({ metadataSearchDto }, requestOptions); + assets.push(...searchResult.assets.items); + nextPage = searchResult.assets.nextPage; + } while (nextPage !== null); + + return assets; + } + private getRequestOptions(fetchAsync = fetchWithTrustedCertificatesAsync) { return { // Undici and node types are not the same diff --git a/packages/request-handler/src/release-providers.ts b/packages/request-handler/src/release-providers.ts index 8d918b761..925507336 100644 --- a/packages/request-handler/src/release-providers.ts +++ b/packages/request-handler/src/release-providers.ts @@ -9,7 +9,7 @@ import { createLogger } from "@homarr/core/infrastructure/logs"; const logger = createLogger({ module: "releaseProviders" }); const PROVIDER_TIMEOUT_MS = 10_000; -const fetchProvider = (url: URL | RequestInfo, options?: RequestInit & { timeout?: number }) => +const fetchProvider: typeof fetchWithTrustedCertificatesAsync = (url, options) => fetchWithTrustedCertificatesAsync(url, { timeout: PROVIDER_TIMEOUT_MS, ...options }); export interface ReleasesRepositoryRequest extends Record { diff --git a/packages/widgets/src/immich/album-carousel/component.tsx b/packages/widgets/src/immich/album-carousel/component.tsx index 9b18dfb89..d74144503 100644 --- a/packages/widgets/src/immich/album-carousel/component.tsx +++ b/packages/widgets/src/immich/album-carousel/component.tsx @@ -76,7 +76,6 @@ function shuffle(items: T[]) { interface CarouselProps { assets: { id: string; - deviceAssetId: string; originalPath: string; fileModifiedAt: string; publicLink: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93965d30c..b82f1a255 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,8 +49,8 @@ catalogs: specifier: ^2.6.0 version: 2.6.0 '@immich/sdk': - specifier: ^2.7.5 - version: 2.7.5 + specifier: ^3.0.0 + version: 3.0.0 '@jellyfin/sdk': specifier: ^0.13.0 version: 0.13.0 @@ -1929,7 +1929,7 @@ importers: version: link:../validation '@immich/sdk': specifier: 'catalog:' - version: 2.7.5 + version: 3.0.0 '@jellyfin/sdk': specifier: 'catalog:' version: 0.13.0(axios@1.15.0) @@ -4230,9 +4230,6 @@ packages: '@drizzle-team/brocli@0.12.0': resolution: {integrity: sha512-mlUE+rZ8CatQekLhnaiN91Iemdd+e2gFKooGlnRB3oPTL3VghLfX24dx7HrzMNeC1JrIB/0kpsfyty3f5HNfxQ==} - '@emnapi/core@1.11.0': - resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==} - '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} @@ -5174,8 +5171,8 @@ packages: cpu: [x64] os: [win32] - '@immich/sdk@2.7.5': - resolution: {integrity: sha512-gCTpFAWlFF+1Sx0jbqPVmwN+0JD+eZEHzhf3q+BbxuQF+ORpNqT0D453mOyonogvy17nJF1jGA2PtT6vOQiSnA==} + '@immich/sdk@3.0.0': + resolution: {integrity: sha512-HUT+L5LEIRsW65GtGj1hLqv9XQzIWzEyaAnAUsVVYHAc3hs+bD8p+KV9Km6Yw8Kg5eOqt+pDcp0peHYkgwnZJQ==} '@inquirer/ansi@1.0.2': resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} @@ -7626,9 +7623,6 @@ packages: cpu: [arm64] os: [win32] - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -17909,12 +17903,6 @@ snapshots: '@drizzle-team/brocli@0.12.0': {} - '@emnapi/core@1.11.0': - dependencies: - '@emnapi/wasi-threads': 1.2.2 - tslib: 2.8.1 - optional: true - '@emnapi/core@1.11.1': dependencies: '@emnapi/wasi-threads': 1.2.2 @@ -18538,7 +18526,7 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@immich/sdk@2.7.5': + '@immich/sdk@3.0.0': dependencies: '@oazapfts/runtime': 1.2.0 @@ -19201,9 +19189,9 @@ snapshots: '@napi-rs/wasm-runtime@1.0.7': dependencies: - '@emnapi/core': 1.11.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.2 + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 optional: true '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': @@ -21245,11 +21233,6 @@ snapshots: '@turbo/windows-arm64@2.10.0': optional: true - '@tybys/wasm-util@0.10.2': - dependencies: - tslib: 2.8.1 - optional: true - '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 @@ -26271,7 +26254,7 @@ snapshots: statuses: 2.0.2 strict-event-emitter: 0.5.1 tough-cookie: 6.0.1 - type-fest: 5.6.0 + type-fest: 5.7.0 until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 78f747b28..c6fcf0924 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -18,7 +18,7 @@ catalog: "@gitbeaker/rest": ^43.8.0 "@homarr/gridstack": ^1.12.0 "@homarr/node-unifi": ^2.6.0 - "@immich/sdk": ^2.7.5 + "@immich/sdk": ^3.0.0 "@jellyfin/sdk": ^0.13.0 "@kubernetes/client-node": ^1.4.0 "@modelcontextprotocol/sdk": 1.29.0 @@ -199,6 +199,7 @@ minimumReleaseAge: 4320 minimumReleaseAgeExclude: - next - "@next/*" + - "@immich/sdk@3.0.0" # Renovate security update: turbo@2.9.14 - turbo@2.9.14 # Renovate security update: form-data@4.0.6