This commit is contained in:
arys
2026-03-01 15:11:27 +05:00
parent 832b93f151
commit 97d368f46b
2 changed files with 31 additions and 23 deletions
@@ -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<String, Object> 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<String, Object> 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) {
@@ -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<Map<String, Object>>() {})
.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;