diff --git a/apps/api-service/package.json b/apps/api-service/package.json index cd31a7f..0be1e82 100644 --- a/apps/api-service/package.json +++ b/apps/api-service/package.json @@ -48,6 +48,7 @@ "express-rate-limit": "^7.4.1", "express-validator": "^7.2.0", "helmet": "^8.0.0", + "image-size": "^2.0.2", "mime": "3.0.0", "minio": "^8.0.6", "multer": "^2.0.2", diff --git a/apps/api-service/src/services/ImageGenService.ts b/apps/api-service/src/services/ImageGenService.ts index 7c2f3aa..827d8e6 100644 --- a/apps/api-service/src/services/ImageGenService.ts +++ b/apps/api-service/src/services/ImageGenService.ts @@ -1,6 +1,7 @@ import { GoogleGenAI } from '@google/genai'; // eslint-disable-next-line @typescript-eslint/no-var-requires const mime = require('mime') as any; +import sizeOf from 'image-size'; import { ImageGenerationOptions, ImageGenerationResult, @@ -81,6 +82,7 @@ export class ImageGenService { size: uploadResult.size, model: this.primaryModel, geminiParams, + generatedImageData: generatedData, ...(generatedData.description && { description: generatedData.description, }), @@ -232,10 +234,25 @@ export class ImageGenService { const fileExtension = mime.getExtension(imageData.mimeType) || 'png'; + // Extract image dimensions from buffer + let width = 1024; // Default fallback + let height = 1024; // Default fallback + try { + const dimensions = sizeOf(imageData.buffer); + if (dimensions.width && dimensions.height) { + width = dimensions.width; + height = dimensions.height; + } + } catch (error) { + console.warn('Failed to extract image dimensions, using defaults:', error); + } + const generatedData: GeneratedImageData = { buffer: imageData.buffer, mimeType: imageData.mimeType, fileExtension, + width, + height, ...(generatedDescription && { description: generatedDescription }), }; diff --git a/apps/api-service/src/services/core/GenerationService.ts b/apps/api-service/src/services/core/GenerationService.ts index 1f970df..f172a48 100644 --- a/apps/api-service/src/services/core/GenerationService.ts +++ b/apps/api-service/src/services/core/GenerationService.ts @@ -182,6 +182,8 @@ export class GenerationService { source: 'generated', alias: params.alias || null, meta: params.meta || {}, + width: genResult.generatedImageData?.width ?? null, + height: genResult.generatedImageData?.height ?? null, }); // Eager flow creation if flowAlias is provided (Section 4.2) diff --git a/apps/api-service/src/types/api.ts b/apps/api-service/src/types/api.ts index 3f48b0c..ca08a54 100644 --- a/apps/api-service/src/types/api.ts +++ b/apps/api-service/src/types/api.ts @@ -109,6 +109,8 @@ export interface GeneratedImageData { mimeType: string; fileExtension: string; description?: string; + width: number; + height: number; } // Logging types diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c68a371..86b1387 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,6 +117,9 @@ importers: helmet: specifier: ^8.0.0 version: 8.1.0 + image-size: + specifier: ^2.0.2 + version: 2.0.2 mime: specifier: 3.0.0 version: 3.0.0 @@ -3348,6 +3351,11 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + image-size@2.0.2: + resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} + engines: {node: '>=16.x'} + hasBin: true + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -7772,7 +7780,7 @@ snapshots: eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -7806,7 +7814,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -7821,7 +7829,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.44.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -8442,6 +8450,8 @@ snapshots: ignore@7.0.5: {} + image-size@2.0.2: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 diff --git a/tests/api/01-generation-basic.rest b/tests/api/01-generation-basic.rest index 6a78aa5..3aa35ba 100644 --- a/tests/api/01-generation-basic.rest +++ b/tests/api/01-generation-basic.rest @@ -25,7 +25,6 @@ X-API-Key: {{apiKey}} { "prompt": "шикарная моторная яхта движется по живописному озеру, люди сидят в спасательных жилетах и держат в руках бутылки с пивом, густой хвойный лес на берегу. фотореалистичная фотография", - "autoEnhance": true, "aspectRatio": "16:9" }