diff --git a/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java b/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java index 214ac8e..d8c7555 100644 --- a/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java +++ b/src/main/java/kz/konturai/parser/service/GeminiVideoGenerationService.java @@ -2,6 +2,8 @@ package kz.konturai.parser.service; import com.google.auth.oauth2.GoogleCredentials; import io.netty.resolver.DefaultAddressResolverGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.io.ClassPathResource; @@ -22,6 +24,7 @@ import java.util.*; @Service public class GeminiVideoGenerationService { + private static final Logger logger = LoggerFactory.getLogger(GeminiVideoGenerationService.class); private static final String VERTEX_API_TEMPLATE = "https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:predict"; private static final String CREDENTIALS_FILE_PATH = "keys/google-key.json"; @@ -39,10 +42,10 @@ public class GeminiVideoGenerationService { public GeminiVideoGenerationService() { HttpClient httpClient = HttpClient.create() .resolver(DefaultAddressResolverGroup.INSTANCE) - .responseTimeout(Duration.ofMillis(300000)); + .responseTimeout(Duration.ofMillis(600000)); ExchangeStrategies strategies = ExchangeStrategies.builder() - .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(100 * 1024 * 1024)) + .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(150 * 1024 * 1024)) .build(); this.webClient = WebClient.builder() @@ -52,13 +55,15 @@ public class GeminiVideoGenerationService { } public byte[] generateVideo(String prompt) { - if (projectId == null || projectId.isEmpty()) { + if (projectId == null || projectId.trim().isEmpty()) { + logger.error("VEO: Project ID is missing!"); return null; } try { String accessToken = getAccessTokenFromResources(); if (accessToken == null) { + logger.error("VEO: Failed to load Google Credentials!"); return null; } @@ -75,6 +80,8 @@ public class GeminiVideoGenerationService { parameters.put("aspectRatio", "9:16"); requestBody.put("parameters", parameters); + logger.info("VEO: Requesting video generation. Prompt: {}", prompt); + Map response = webClient.post() .uri(URI.create(endpointUrl)) .header("Authorization", "Bearer " + accessToken) @@ -85,11 +92,21 @@ public class GeminiVideoGenerationService { .retryWhen(reactor.util.retry.Retry.backoff(3, Duration.ofSeconds(15)) .filter(t -> t instanceof WebClientResponseException && ((WebClientResponseException) t).getStatusCode().value() == 429)) - .block(Duration.ofMillis(300000)); + .block(Duration.ofMillis(600000)); - return extractVideoFromResponse(response); + byte[] videoBytes = extractVideoFromResponse(response); + if (videoBytes != null) { + logger.info("VEO: Successfully generated video! Size: {} bytes", videoBytes.length); + } else { + logger.error("VEO: Failed to extract video bytes from Google response."); + } + return videoBytes; + } catch (WebClientResponseException e) { + logger.error("VEO API Error! Status: {}, Body: {}", e.getStatusCode(), e.getResponseBodyAsString()); + return null; } catch (Exception e) { + logger.error("VEO API Fatal Error: {}", e.getMessage()); return null; } } @@ -118,7 +135,7 @@ public class GeminiVideoGenerationService { Map firstPrediction = predictions.get(0); String base64Video = (String) firstPrediction.get("bytesBase64Encoded"); if (base64Video != null) { - return Base64.getDecoder().decode(base64Video); + return java.util.Base64.getDecoder().decode(base64Video); } } } catch (Exception e) { diff --git a/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java b/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java index bee5a6b..7f551c6 100644 --- a/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java +++ b/src/main/java/kz/konturai/parser/service/NanoBananaImageGenerationService.java @@ -43,22 +43,13 @@ public class NanoBananaImageGenerationService implements ImageGenerationService @Value("${google.gemini.image.model:imagen-3.0-generate-001}") private String model; - @Value("${google.gemini.timeoutMs:120000}") - private long timeoutMs; - - @Value("${google.gemini.retry.maxAttempts:5}") - private int maxRetryAttempts; - - @Value("${google.gemini.rateLimit.delayMs:15000}") - private long rateLimitDelayMs; - public NanoBananaImageGenerationService() { HttpClient httpClient = HttpClient.create() .resolver(DefaultAddressResolverGroup.INSTANCE) - .responseTimeout(Duration.ofMillis(120000)); + .responseTimeout(Duration.ofMillis(180000)); ExchangeStrategies strategies = ExchangeStrategies.builder() - .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(20 * 1024 * 1024)) + .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(30 * 1024 * 1024)) .build(); this.webClient = WebClient.builder() @@ -92,8 +83,8 @@ public class NanoBananaImageGenerationService implements ImageGenerationService semaphore.acquire(); long timeSinceLast = System.currentTimeMillis() - lastRequestTimestamp; - if (timeSinceLast < rateLimitDelayMs) { - Thread.sleep(rateLimitDelayMs - timeSinceLast); + if (timeSinceLast < 15000) { + Thread.sleep(15000 - timeSinceLast); } String accessToken = getAccessTokenFromResources(); @@ -137,11 +128,11 @@ public class NanoBananaImageGenerationService implements ImageGenerationService .bodyValue(requestBody) .retrieve() .bodyToMono(new ParameterizedTypeReference>() {}) - .retryWhen(reactor.util.retry.Retry.backoff(maxRetryAttempts, Duration.ofSeconds(10)) + .retryWhen(reactor.util.retry.Retry.backoff(10, Duration.ofSeconds(10)) .maxBackoff(Duration.ofSeconds(60)) .filter(t -> t instanceof WebClientResponseException && ((WebClientResponseException) t).getStatusCode().value() == 429)) - .block(Duration.ofMillis(timeoutMs)); + .block(Duration.ofMillis(180000)); } catch (WebClientResponseException e) { if (e.getStatusCode().value() == 400) { throw e; @@ -189,7 +180,7 @@ public class NanoBananaImageGenerationService implements ImageGenerationService refImagesList.add(referenceImageParams); instance.put("referenceImages", refImagesList); - instance.put("prompt", prompt + ". Naturally integrate the provided reference subject into the scene."); + instance.put("prompt", prompt + ". CRITICAL: Preserve the text, letters, and typography of the provided logo EXACTLY as they appear. Do not invent new text, do not distort letters. Integrate naturally."); } instances.add(instance); @@ -200,7 +191,7 @@ public class NanoBananaImageGenerationService implements ImageGenerationService parameters.put("aspectRatio", "1:1"); parameters.put("safetyFilterLevel", "block_some"); parameters.put("personGeneration", "allow_adult"); - parameters.put("negativePrompt", "text, typography, watermark, signature, blurry, distorted, bad anatomy"); + parameters.put("negativePrompt", "gibberish text, alien language, wrong spelling, distorted letters, bad anatomy, nsfw, watermark"); body.put("parameters", parameters); return body;