import requests import base64 import json import os from io import BytesIO from typing import Optional, Dict # Pillow is required for image format conversion and normalization. # Please install it using: pip install Pillow try: from PIL import Image except ImportError: print("Pillow library not found. Please install it using: pip install Pillow") exit() # --- Configuration --- IMGBB_API_KEY = os.getenv("IMGBB_API_KEY") def upload_to_imgbb(image_path: str, file_name: str) -> Optional[str]: """ Uploads the image at image_path to ImgBB. Returns the public URL or None on failure. """ if not IMGBB_API_KEY: print("Warning: IMGBB_API_KEY not set, skipping upload") return None print(f"Uploading {file_name} to ImgBB...") try: with open(image_path, 'rb') as f: files = {"image": (file_name, f.read())} resp = requests.post( "https://api.imgbb.com/1/upload", params={"key": IMGBB_API_KEY}, files=files, timeout=20 ) resp.raise_for_status() data = resp.json().get("data", {}) url = data.get("url") if url: print(f"Successfully uploaded to ImgBB: {url}") return url else: print(f"Error: ImgBB API response missing 'url'. Response: {resp.json()}") return None except requests.exceptions.RequestException as e: print(f"Error: ImgBB upload failed: {e}") return None def _download_image_from_url(image_url: str, save_path: str) -> bool: """ Downloads an image from a URL and saves it to a local path. Args: image_url: The URL of the image to download. save_path: The local path to save the downloaded image. Returns: True if the download was successful, False otherwise. """ print(f"Downloading generated image from: {image_url}") try: image_response = requests.get(image_url, stream=True, timeout=30) # Check if the download request was successful. if image_response.status_code == 200: content_type = image_response.headers.get('Content-Type', '') if 'image' in content_type: with open(save_path, 'wb') as f: for chunk in image_response.iter_content(1024): f.write(chunk) print(f"Image successfully saved to {save_path}") return True else: print(f"Error: Content at URL is not an image. Content-Type: {content_type}") return False else: print(f"Error: Failed to download image. Status code: {image_response.status_code}") return False except requests.exceptions.RequestException as e: print(f"An error occurred during image download: {e}") return False def generate_image( prompt_text: str, image_path: str, download_path: Optional[str] = None ) -> Optional[Dict]: """ Sends a request to the image generation API, optionally downloads the result, and uploads it to ImgBB. Args: prompt_text: The instructional text for image modification. image_path: The file path to the input image (any common format). download_path: If provided, the path to save the generated image. Returns: A dictionary of the JSON response from the API, including the ImgBB URL, or None on error. """ url = "https://kontext-chat.replicate.dev/generate-image" try: # --- Image Normalization Step --- with Image.open(image_path) as img: if img.mode != 'RGB': img = img.convert('RGB') with BytesIO() as output_buffer: img.save( output_buffer, format="JPEG", quality=95, subsampling=0, progressive=False ) image_bytes = output_buffer.getvalue() except FileNotFoundError: print(f"Error: Image file not found at {image_path}") return None except Exception as e: print(f"Error processing image file. Ensure it's a valid image. Details: {e}") return None encoded_string = base64.b64encode(image_bytes).decode('utf-8') input_image_data_uri = f"data:image/jpeg;base64,{encoded_string}" payload = { "prompt": prompt_text, "input_image": input_image_data_uri } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0", "Accept": "*/*", "Content-Type": "application/json", "Referer": "https://kontext-chat.replicate.dev/", "Origin": "https://kontext-chat.replicate.dev", } try: response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=45) response.raise_for_status() api_response_data = response.json() imgbb_url = None # Initialize imgbb_url # --- Download & Upload Logic --- if download_path and isinstance(api_response_data, dict): image_url = api_response_data.get("imageUrl") if image_url: if _download_image_from_url(image_url, download_path): # If download is successful, upload the saved file to ImgBB file_name = os.path.basename(download_path) imgbb_url = upload_to_imgbb(download_path, file_name) else: print("Warning: 'imageUrl' not found in API response, cannot download or upload image.") # Add the ImgBB URL to the response dictionary if it was generated if isinstance(api_response_data, dict): api_response_data['imgbb_url'] = imgbb_url return api_response_data except requests.exceptions.RequestException as e: print(f"API request failed: {e}") return None