diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 7fecc8a647..8eb172c1fb 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -4214,6 +4214,13 @@ "name": "size", "required": false, "in": "query", + "x-immich-history": [ + { + "version": "v3", + "state": "Updated", + "description": "Specifying 'original' is deprecated. Use the original endpoint directly instead" + } + ], "schema": { "$ref": "#/components/schemas/AssetMediaSize" } diff --git a/server/src/controllers/asset-media.controller.spec.ts b/server/src/controllers/asset-media.controller.spec.ts index 0bfb423898..71206c3855 100644 --- a/server/src/controllers/asset-media.controller.spec.ts +++ b/server/src/controllers/asset-media.controller.spec.ts @@ -166,11 +166,16 @@ describe(AssetMediaController.name, () => { }); // TODO figure out how to deal with `sendFile` - describe.skip('GET /assets/:id/thumbnail', () => { - it('should be an authenticated route', async () => { + describe('GET /assets/:id/thumbnail', () => { + it.skip('should be an authenticated route', async () => { await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/thumbnail`); expect(ctx.authenticate).toHaveBeenCalled(); }); + + it('should redirect if size=original is requested', async () => { + const { status } = await request(ctx.getHttpServer()).get(`/assets/${factory.uuid()}/thumbnail?size=original`); + expect(status).toBe(302); + }); }); }); }); diff --git a/server/src/controllers/asset-media.controller.ts b/server/src/controllers/asset-media.controller.ts index c1ef8c853d..dbb906fd02 100644 --- a/server/src/controllers/asset-media.controller.ts +++ b/server/src/controllers/asset-media.controller.ts @@ -126,6 +126,16 @@ export class AssetMediaController { @Res() res: Response, @Next() next: NextFunction, ) { + if (dto.size === AssetMediaSize.Original) { + this.logger.deprecate( + 'Calling the thumbnail endpoint with size=original is deprecated. Use the :id/original endpoint instead', + ); + const [_, reqSearch] = req.url.split('?'); + const redirSearchParams = new URLSearchParams(reqSearch); + redirSearchParams.delete('size'); + return res.redirect('original' + '?' + redirSearchParams.toString()); + } + const viewThumbnailRes = await this.service.viewThumbnail(auth, id, dto); if (viewThumbnailRes instanceof ImmichFileResponse) { diff --git a/server/src/dtos/asset-media.dto.ts b/server/src/dtos/asset-media.dto.ts index bb1d3826bb..7178d4184e 100644 --- a/server/src/dtos/asset-media.dto.ts +++ b/server/src/dtos/asset-media.dto.ts @@ -1,4 +1,5 @@ import { createZodDto } from 'nestjs-zod'; +import { HistoryBuilder } from 'src/decorators'; import { AssetMetadataUpsertItemSchema } from 'src/dtos/asset.dto'; import { AssetVisibilitySchema } from 'src/enum'; import { isoDatetimeToDate, JsonParsed, stringToBool } from 'src/validation'; @@ -19,7 +20,11 @@ const AssetMediaSizeSchema = z.enum(AssetMediaSize).describe('Asset media size') const AssetMediaOptionsSchema = z .object({ - size: AssetMediaSizeSchema.optional(), + size: AssetMediaSizeSchema.optional().meta( + new HistoryBuilder() + .updated('v3', "Specifying 'original' is deprecated. Use the original endpoint directly instead") + .getExtensions(), + ), edited: stringToBool.default(false).optional().describe('Return edited asset if available'), }) .meta({ id: 'AssetMediaOptionsDto' });