import gradio as gr import easyocr from PIL import Image import numpy as np import google.generativeai as genai import time from gradio_client import Client, handle_file # <-- ADDED IMPORT import requests from urllib.parse import urlparse import io # --- Configuration --- # Gemini API key - It's better to use environment variables, but this works for now. api_key = "AIzaSyAKI92YawOKQ1-HRLmvaryMEWk_y4alJgA" # URL to your background image hosted on your Hugging Face Space. # IMPORTANT: Replace 'YOUR-HF-USERNAME/YOUR-SPACE-NAME' with your actual space details. # The file '1.jpg' must be in the root of your Space's repository. BACKGROUND_IMAGE_URL = "1.jpg" # Global reader - initialize once reader = None def initialize_reader(): """Initialize EasyOCR reader""" global reader if reader is None: print("Loading EasyOCR model...") reader = easyocr.Reader(['en'], gpu=False, verbose=False) print("EasyOCR model loaded successfully!") return reader def extract_text_from_quote(image): """Extract text from quote image using EasyOCR""" if image is None: return "Please upload an image first.", "Words: 0" try: reader = initialize_reader() img_array = np.array(image) results = reader.readtext(img_array, paragraph=True) if results: text_parts = [result[1].strip() for result in results if len(result) >= 2 and result[1].strip()] if text_parts: extracted_text = ' '.join(text_parts) word_count = f"Words: {len(extracted_text.split())}" return extracted_text, word_count return "No text detected in the image.", "Words: 0" except Exception as e: return f"Error processing image: {str(e)}", "Words: 0" def translate_extracted(text, lang): """Translate the extracted English text using Gemini API""" if not text or "No text" in text or "Error" in text or "Please upload" in text: return "No valid text to translate." try: print(f"DEBUG: API Key loaded (first 5 chars: {api_key[:5]}...). Starting translation to {lang}") genai.configure(api_key=api_key) for attempt in range(3): try: model = genai.GenerativeModel('gemini-2.0-flash') # Updated prompt for clarity and conciseness prompt = f""" You are a cool, chill translator with a fun and warm personality, inspired by Persian Twitter style. Your translations should be natural, slangy, and relatable. Use colloquial words and contractions. No emojis, keep it RTL-friendly. Be concise but preserve the emotional depth. Maintain correct grammar, even with slang. Avoid literal translations. Translate the following English quote into this style. English Quote: "{text}" Format your output for an image overlay: Break the Persian text into short, visually appealing lines. dont use emojies at all Output ONLY the translated Persian text. """ response = model.generate_content(prompt) translated = response.text.strip() print(f"DEBUG: Translation successful on attempt {attempt+1}: {translated[:50]}...") return translated except Exception as inner_e: print(f"DEBUG: Attempt {attempt+1} failed: {str(inner_e)}. Retrying in 2s...") time.sleep(2) raise Exception("All translation retries failed.") except Exception as e: error_msg = f"Error translating: {str(e)}. Check network access to Google API." print(f"DEBUG: Translation failed: {str(e)}") return error_msg # NEW FUNCTION TO CALL THE SECOND HF SPACE def overlay_text_on_image(translated_text): """ Sends translated text to the 'textoverimage1' Space and gets the resulting image. """ # Don't proceed if translation failed or is empty if not translated_text or "Error" in translated_text or "No valid text" in translated_text: print("DEBUG: Skipping image overlay due to invalid translated text.") return None # Return None to clear the image output try: print("DEBUG: Initializing client for 'kavehtaheri/textoverimage1'") client = Client("kavehtaheri/textoverimage1") print(f"DEBUG: Sending data to API endpoint '/overlay_text_on_image'") print(f"DEBUG: Persian Text: {translated_text}") print(f"DEBUG: Image URL: {BACKGROUND_IMAGE_URL}") # The handle_file function downloads the URL to a temporary file for upload result_image_path = client.predict( persian_text=translated_text, url="", # Pass an empty string for URL if upload is used upload=handle_file(BACKGROUND_IMAGE_URL), username="aramnevis", # Not needed based on the API, can be empty text_color="Black", # As specified in the screenshot api_name="/overlay_text_on_image" ) print(f"DEBUG: Received image path from API: {result_image_path}") return result_image_path except Exception as e: print(f"ERROR: Could not get image from 'textoverimage1' space. Error: {e}") # Return a placeholder or raise an error in Gradio UI # For now, we return None which will clear the output return None # UPDATED FUNCTION TO FETCH IMAGE FROM INSTAGRAM VIA ONE-API (BASED ON PROVIDED SAMPLE) def get_instagram_image(ig_url): """Fetch image from Instagram post using One-API.""" if not ig_url: return None try: one_api_key = "268976:66f4f58a2a905" shortcode = ig_url.split("/")[-2] url_one = "https://api.one-api.ir/instagram/v1/post/?shortcode=" + shortcode headers = { "accept": "application/json", "one-api-token": one_api_key, "Content-Type": "application/json" } response = requests.get(url_one, headers=headers) if response.status_code == 200: result = response.json() # Try main media URL first main_url = result.get("result", {}).get('media', [{}])[0].get("url") if main_url: try: img_response = requests.get(main_url) img_response.raise_for_status() img = Image.open(io.BytesIO(img_response.content)) return img # Return if successfully opened as image except Exception as e: print(f"DEBUG: Main URL not an image: {str(e)}. Trying cover...") # Fallback to cover URL cover_url = result.get("result", {}).get('media', [{}])[0].get("cover") if cover_url: img_response = requests.get(cover_url) img_response.raise_for_status() img = Image.open(io.BytesIO(img_response.content)) return img raise ValueError("No valid image URL found in API response.") else: raise ValueError(f"API error: {response.status_code}, {response.text}") except Exception as e: print(f"ERROR fetching Instagram image: {str(e)}") return None def clear_all(): """Clear all inputs and outputs""" return None, None, "Your extracted quote will appear here...", "Words: 0", "Translation will appear here...", None, True # Clear image, ig_url, and reset checkbox # --- Gradio Interface --- with gr.Blocks(title="Quote OCR & Overlay", theme=gr.themes.Soft()) as demo: gr.Markdown("# 📝 Quote Text Extractor & Image Generator") gr.Markdown("Upload an image to extract text, translate it, and overlay it onto a new background.") with gr.Row(): # --- INPUT COLUMN --- with gr.Column(scale=1): image_input = gr.Image(label="1. Upload Quote Image", type="pil", sources=["upload", "clipboard"]) ig_input = gr.Textbox(label="Or Instagram Link", placeholder="e.g., https://www.instagram.com/p/C-ODQjyy4N3/") with gr.Row(): clear_btn = gr.Button("Clear All", variant="secondary") extract_btn = gr.Button("Extract & Translate", variant="primary") target_lang = gr.Dropdown( label="Target Language", choices=["persian(farsi)"], # Locked to Persian as per the logic value="persian(farsi)", interactive=False # Not changeable since the prompt is hardcoded for Persian ) auto_translate = gr.Checkbox(label="Auto-Process After Upload", value=True) # --- OUTPUTS COLUMN --- with gr.Column(scale=2): text_output = gr.Textbox(label="2. Extracted English Text", placeholder="Extracted text appears here...", lines=4, show_copy_button=True) word_count = gr.Textbox(label="Word Count", interactive=False, max_lines=1) translated_output = gr.Textbox(label="3. Translated Persian Text", placeholder="Persian translation appears here...", lines=4, show_copy_button=True) gr.Markdown("---") # Separator final_image_output = gr.Image(label="4. Final Image with Text Overlay", type="filepath") # --- Event Handlers --- # Combined function for the main button and auto-processing def process_everything(image, ig_url, lang, auto_process_enabled): if not auto_process_enabled: # If auto-process is off, we still run extract but not the rest text, wc = extract_text_from_quote(image) return text, wc, "Translation will appear here...", None # Determine source image: prioritize uploaded image if provided, else IG link if image is not None: source_image = image elif ig_url: source_image = get_instagram_image(ig_url) if source_image is None: return "Error fetching image from Instagram link.", "Words: 0", "Translation failed: No image fetched.", None else: return "Please upload an image or provide an Instagram link.", "Words: 0", "Translation failed: No input provided.", None text, wc = extract_text_from_quote(source_image) # Proceed only if text was found if "No text" in text or "Error" in text: return text, wc, "Translation failed: No text extracted.", None translated = translate_extracted(text, lang) final_image = overlay_text_on_image(translated) return text, wc, translated, final_image # The main button triggers the full pipeline extract_btn.click( fn=process_everything, inputs=[image_input, ig_input, target_lang, gr.State(True)], # Pass True to force processing outputs=[text_output, word_count, translated_output, final_image_output] ) # Changing the image triggers the pipeline only if 'auto-translate' is checked image_input.change( fn=process_everything, inputs=[image_input, ig_input, target_lang, auto_translate], outputs=[text_output, word_count, translated_output, final_image_output] ) # Changing the IG link triggers the pipeline only if 'auto-translate' is checked ig_input.change( fn=process_everything, inputs=[image_input, ig_input, target_lang, auto_translate], outputs=[text_output, word_count, translated_output, final_image_output] ) # Clear button action clear_btn.click( fn=clear_all, outputs=[image_input, ig_input, text_output, word_count, translated_output, final_image_output, auto_translate] ) gr.Markdown("### 💡 How It Works:\n1. Upload a clear image containing English text OR provide an Instagram post link.\n2. The app automatically extracts the text using OCR.\n3. The text is translated to a casual, modern Persian.\n4. The Persian text is sent to a second app which overlays it on a background image.\n5. The final image is displayed.") if __name__ == "__main__": # Add requirements to your requirements.txt: # gradio # easyocr # pillow # numpy # google-generativeai # gradio_client # requests demo.launch()