using System.Text; using System.Text.Json; using eprintServer.Enums; using eprintServer.Interfaces; using eprintServer.Requests; using System.Text.Json.Serialization; namespace eprintServer.Services { public class GeminiService : IGeminiService { private readonly HttpClient _httpClient; private readonly IConfiguration _configuration; private readonly ILogger _logger; public GeminiService( HttpClient httpClient, IConfiguration configuration, ILogger logger) // Add { _httpClient = httpClient; _configuration = configuration; _logger = logger; } public async Task GenerateImageAsync(GeminiImageRequest request) { var apiKey = "0bb245be-d2f2-4e5a-9701-e88b654f5f6e:e145463c60171124fd3cb84801b5567e"; var url = "https://fal.run/fal-ai/flux/schnell"; var falRequest = new { prompt = request.Prompt, image_size = request.AspectRatio switch { "16:9" => "landscape_16_9", "9:16" => "portrait_16_9", "4:3" => "landscape_4_3", "3:4" => "portrait_4_3", _ => "square" }, num_inference_steps = 4, num_images = 1, enable_safety_checker = false }; var httpRequest = new HttpRequestMessage(HttpMethod.Post, url); httpRequest.Headers.Add("Authorization", $"Key {apiKey}"); httpRequest.Content = new StringContent( JsonSerializer.Serialize(falRequest), Encoding.UTF8, "application/json"); _logger.LogInformation("Sending request to Fal.ai with prompt: {Prompt}", request.Prompt); var response = await _httpClient.SendAsync(httpRequest); var content = await response.Content.ReadAsStringAsync(); _logger.LogInformation("Fal.ai response status: {StatusCode}, Content: {Content}", response.StatusCode, content); if (!response.IsSuccessStatusCode) { _logger.LogError($"Fal.ai API error ({response.StatusCode}): {content}"); throw new HttpRequestException($"Fal.ai API error: {response.StatusCode}"); } var falResponse = JsonSerializer.Deserialize(content); // Log the response structure to debug _logger.LogInformation("Parsed Fal.ai response: {Response}", JsonSerializer.Serialize(falResponse)); if (falResponse?.Images != null && falResponse.Images.Length > 0) { var imageUrl = falResponse.Images[0].Url; _logger.LogInformation("Image URL received: {Url}", imageUrl); // Download the image and convert to base64 using var imageClient = new HttpClient(); var imageBytes = await imageClient.GetByteArrayAsync(imageUrl); var base64Image = Convert.ToBase64String(imageBytes); _logger.LogInformation("Successfully converted image to base64, size: {Size} bytes", imageBytes.Length); return new ImageGenerationResponse { Url = $"data:image/png;base64,{base64Image}", Width = falResponse.Images[0].Width, Height = falResponse.Images[0].Height }; } _logger.LogError("No images found in Fal.ai response"); throw new InvalidOperationException("No image data returned from Fal.ai"); } public class FalAiResponse { [JsonPropertyName("images")] public FalAiImage[] Images { get; set; } [JsonPropertyName("timings")] public object Timings { get; set; } [JsonPropertyName("seed")] public long Seed { get; set; } [JsonPropertyName("has_nsfw_concepts")] public bool[] HasNsfwConcepts { get; set; } [JsonPropertyName("prompt")] public string Prompt { get; set; } } public class FalAiImage { [JsonPropertyName("url")] public string Url { get; set; } [JsonPropertyName("width")] public int Width { get; set; } [JsonPropertyName("height")] public int Height { get; set; } [JsonPropertyName("content_type")] public string ContentType { get; set; } } public async Task EditImageAsync(GeminiEditRequest request) { var apiKey = "0bb245be-d2f2-4e5a-9701-e88b654f5f6e:e145463c60171124fd3cb84801b5567e"; var url = "https://fal.run/fal-ai/flux/schnell/image-to-image"; // Convert base64 to proper format if needed var imageData = request.ImageBase64; if (imageData.StartsWith("data:")) { imageData = imageData.Split(',')[1]; } var falRequest = new { prompt = request.Prompt, image_url = $"data:image/jpeg;base64,{imageData}", image_size = request.AspectRatio switch { "16:9" => "landscape_16_9", "9:16" => "portrait_16_9", "4:3" => "landscape_4_3", "3:4" => "portrait_4_3", _ => "square" }, num_inference_steps = 4, strength = 0.8, // How much to change the image (0.0 - 1.0) num_images = 1 }; var httpRequest = new HttpRequestMessage(HttpMethod.Post, url); httpRequest.Headers.Add("Authorization", $"Key {apiKey}"); httpRequest.Content = new StringContent( JsonSerializer.Serialize(falRequest), Encoding.UTF8, "application/json"); _logger.LogInformation("Sending image edit request to Fal.ai with prompt: {Prompt}", request.Prompt); var response = await _httpClient.SendAsync(httpRequest); var content = await response.Content.ReadAsStringAsync(); _logger.LogInformation("Fal.ai edit response status: {StatusCode}, Content: {Content}", response.StatusCode, content); if (!response.IsSuccessStatusCode) { _logger.LogError($"Fal.ai API error ({response.StatusCode}): {content}"); throw new HttpRequestException($"Fal.ai API error: {response.StatusCode}"); } var falResponse = JsonSerializer.Deserialize(content); if (falResponse?.Images != null && falResponse.Images.Length > 0) { var imageUrl = falResponse.Images[0].Url; _logger.LogInformation("Image URL received: {Url}", imageUrl); // Download the image and convert to base64 using var imageClient = new HttpClient(); var imageBytes = await imageClient.GetByteArrayAsync(imageUrl); var base64Image = Convert.ToBase64String(imageBytes); _logger.LogInformation("Successfully converted edited image to base64, size: {Size} bytes", imageBytes.Length); return new ImageGenerationResponse { Url = $"data:image/png;base64,{base64Image}", Width = falResponse.Images[0].Width, Height = falResponse.Images[0].Height }; } _logger.LogError("No images found in Fal.ai edit response"); throw new InvalidOperationException("No image data returned from Fal.ai"); } } }