Spaces:
Running
Running
| import gradio as gr | |
| import os | |
| import base64 | |
| from dotenv import load_dotenv | |
| from image_generator_tool import GenerateImageTool | |
| from social_media_crew import SocialMediaCrew | |
| from utils_tools import GetImageUrlTool | |
| load_dotenv() | |
| def clean_env_vars(): | |
| os.environ.pop("OPENAI_API_KEY", None) | |
| os.environ.pop("NATURA_API_TOKEN", None) | |
| os.environ.pop("OPENAI_BASE_URL", None) | |
| os.environ.pop("OPENAI_MODEL_NAME", None) | |
| js_share_logic = """ | |
| async (image_url) => { | |
| if (!image_url) { | |
| console.error("Share button clicked, but no image URL found."); | |
| alert("No image to share. Please generate an image first."); | |
| return; | |
| } | |
| try { | |
| const response = await fetch(image_url); | |
| const blob = await response.blob(); | |
| const reader = new FileReader(); | |
| reader.readAsDataURL(blob); | |
| reader.onloadend = async function() { | |
| const base64data = reader.result; | |
| const parts = base64data.split(';base64,'); | |
| const contentType = parts[0].split(':')[1]; | |
| const raw = window.atob(parts[1]); | |
| const rawLength = raw.length; | |
| const uInt8Array = new Uint8Array(rawLength); | |
| for (let i = 0; i < rawLength; ++i) { | |
| uInt8Array[i] = raw.charCodeAt(i); | |
| } | |
| const newBlob = new Blob([uInt8Array], { type: contentType }); | |
| const fileName = `shared_image_${new Date().getTime()}.png`; | |
| const file = new File([newBlob], fileName, { type: contentType }); | |
| if (navigator.share && navigator.canShare({ files: [file] })) { | |
| await navigator.share({ | |
| files: [file], | |
| title: "Image", | |
| text: "Shared from app.", | |
| }); | |
| } else { | |
| alert("Sharing not supported on this browser."); | |
| } | |
| }; | |
| reader.onerror = function(error) { | |
| console.error("Error converting blob to data URL:", error); | |
| alert("An error occurred while preparing image for sharing."); | |
| }; | |
| } catch (error) { | |
| if (error.name === 'AbortError') { | |
| console.log("Share dialog cancelled by user."); | |
| } else { | |
| console.error("Error sharing:", error); | |
| alert("An error occurred while trying to share."); | |
| } | |
| } | |
| } | |
| """ | |
| # --- Gradio Interface --- | |
| def generate_ad(product_url: str, store_name: str, main_cupom: str, main_cupom_discount_percentage: float, cupom_1: str, original_price: float, discounted_price: float, openai_api_key: str, natura_api_token: str, openai_base_url: str, openai_model_name: str): | |
| yield gr.update(interactive=False, value="Generating..."), gr.Markdown(value="⏳ Generating ad... Please wait."), gr.Image(label="Product Image", show_label=True, visible=False, width=200, height=200) | |
| if not openai_api_key or not natura_api_token or not openai_model_name or not openai_base_url: | |
| yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value="Please configure your API keys in the settings section below.") | |
| return | |
| image_url = None | |
| if "natura" in product_url: | |
| image_url = GetImageUrlTool()._run(product_url) | |
| print(f"Extracted image URL: {image_url}") | |
| original_price = original_price if original_price is not None else 0 | |
| discounted_price = discounted_price if discounted_price is not None else 0 | |
| social_media_crew = SocialMediaCrew(openai_api_key, natura_api_token, openai_base_url, openai_model_name) | |
| result = social_media_crew.run_crew(product_url, store_name, main_cupom, main_cupom_discount_percentage, cupom_1, original_price, discounted_price) | |
| if result == "INVALID_URL": | |
| yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value="❌ The provided URL is invalid or the product page could not be found.") | |
| elif result == "MISSING_PRODUCT_INFO": | |
| yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value="⚠️ Could not extract all required product information from the URL. Please check the URL or try a different one.") | |
| else: | |
| yield gr.update(interactive=True, value="Generate Ad"), gr.Markdown(value=result.raw), gr.Image(value=image_url, visible=image_url is not None) | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 🚀 Social Media Ad Generator") | |
| gr.Markdown("Enter a product URL to generate a social media ad.") | |
| with gr.Tab("Generate Ad"): | |
| url_input = gr.Textbox(label="Product URL", placeholder="Enter product URL here...") | |
| store_name_input = gr.Textbox(label="Store Name (e.g., O Boticário)", placeholder="Enter store name...") | |
| main_cupom_input = gr.Textbox(label="Main Cupom (e.g., PRIMEIRACOMPRA)", value="MINHALOJATAON") | |
| main_cupom_discount_percentage_input = gr.Number(label="Main Cupom Discount Percentage (e.g., 20 for 20%)", value=15, minimum=0, maximum=100) | |
| cupom_1_input = gr.Textbox(label="Cupom 1 (e.g., AMIGO15)", placeholder="Enter first coupon code...") | |
| original_price_input = gr.Number(label="Original Price (Optional)", value=0, minimum=0) | |
| discounted_price_input = gr.Number(label="Discounted Price (Optional)", value=0, minimum=0) | |
| with gr.Row(): | |
| generate_ad_button = gr.Button("Generate Ad") | |
| clear_button = gr.Button("Clear") | |
| with gr.Row(): | |
| ad_output = gr.Markdown(label="Your Generated Ad", show_copy_button=True) | |
| ad_image_output = gr.Image(label="Product Image", show_label=False, visible=True, width=200, height=200) | |
| with gr.Tab("Fragrantica"): | |
| gr.Markdown("### 👃 Fragrantica Website Analyzer") | |
| fragrantica_url_input = gr.Textbox(label="Fragrantica Product URL", placeholder="Enter Fragrantica product URL here...") | |
| analyze_fragrantica_button = gr.Button("Analyze Fragrantica Product") | |
| fragrantica_output = gr.Markdown(label="Fragrantica Analysis Report") | |
| with gr.Tab("Images"): | |
| gr.Markdown("### 🖼️ Generate Promotional Image") | |
| with gr.Row(): | |
| with gr.Column(): | |
| image_product_url_input = gr.Textbox(label="Product Image URL", placeholder="Enter product image URL...") | |
| image_product_name_input = gr.Textbox(label="Product Name", placeholder="Enter product name...") | |
| image_original_price_input = gr.Number(label="Original Price", placeholder="Enter original price...") | |
| image_final_price_input = gr.Number(label="Final Price", placeholder="Enter final price...") | |
| image_coupon_code_input = gr.Textbox(label="Coupon Code", placeholder="Enter coupon code...") | |
| template_selection = gr.Radio( | |
| choices=[ | |
| "outros", | |
| "outros_sem_cupom", | |
| "natura", | |
| "avon", | |
| "lidi_promo", | |
| ], | |
| value="outros", | |
| label="Template Selection" | |
| ) | |
| gen_image_btn = gr.Button("Generate Image") | |
| with gr.Column(): | |
| image_output = gr.Image(label="Generated Image", height=500, type="filepath", interactive=False, show_share_button=True) | |
| base64_debug_output = gr.Textbox(label="Base64 Debug Output", visible=True, interactive=False) | |
| share_button = gr.Button("🚀 Share Image", interactive=False) | |
| with gr.Tab("Settings"): | |
| gr.Markdown("### ⚙️ API Key Settings") | |
| gr.Markdown("Enter your API keys below. These will be used for the current session.") | |
| openai_key_input = gr.Textbox(label="OPENAI_API_KEY", type="password", value=os.getenv("OPENAI_API_KEY", "")) | |
| natura_token_input = gr.Textbox(label="NATURA_API_TOKEN", type="password", value=os.getenv("NATURA_API_TOKEN", "")) | |
| openai_base_url_input = gr.Textbox(label="OPENAI_BASE_URL", value=os.getenv("OPENAI_BASE_URL", "https://llm-openai-ibombonato.openai.azure.com/openai/deployments/gpt-4.1-mini/chat/completions?api-version=2025-01-01-preview")) | |
| openai_model_name_input = gr.Textbox(label="OPENAI_MODEL_NAME", value=os.getenv("OPENAI_MODEL_NAME", "azure/gpt-4.1-mini")) | |
| clean_env_vars() | |
| # No save button needed as keys are passed directly | |
| gr.Markdown("API keys are used directly from these fields when you click 'Generate Ad'. They are not saved persistently.") | |
| def clear_fields(): | |
| return "", 0, 0 | |
| def analyze_fragrantica_url(url, openai_api_key, natura_api_token, openai_base_url, openai_model_name): | |
| yield "⏳ Analyzing Fragrantica product... Please wait." # Loading message | |
| if not openai_api_key or not openai_model_name or not openai_base_url: | |
| yield "Please configure your API keys in the settings section below." | |
| return | |
| from fragrantica_crew import FragranticaCrew | |
| fragrantica_crew = FragranticaCrew(openai_api_key, openai_base_url, openai_model_name) | |
| report = fragrantica_crew.kickoff(url=url) | |
| if report == "SCRAPING_FAILED": | |
| yield "❌ Scraping failed. The website could not be accessed or parsed. Please check the URL or try again later." | |
| return | |
| yield report.raw | |
| def generate_image(product_image_url, product_name, original_price, final_price, coupon_code, template_name): | |
| tool = GenerateImageTool() | |
| original_price_str = f"{original_price:.2f}".replace('.', ',') | |
| final_price_str = f"{final_price:.2f}".replace('.', ',') | |
| yield gr.update(interactive=False, value="Generating..."), None, gr.update(interactive=False) | |
| image_path = tool._run( | |
| product_image_url=product_image_url, | |
| product_name=product_name, | |
| original_price=original_price_str, | |
| final_price=final_price_str, | |
| coupon_code=coupon_code, | |
| template_name=template_name | |
| ) | |
| yield gr.update(interactive=True, value="Generate Image"), image_path, gr.update(interactive=True) | |
| def process_image_for_sharing(image_path): | |
| if image_path is None: | |
| return "" | |
| try: | |
| with open(image_path, "rb") as image_file: | |
| encoded_string = base64.b64encode(image_file.read()).decode("utf-8") | |
| data_url = f"data:image/png;base64,{encoded_string}" | |
| return data_url | |
| except Exception as e: | |
| print(f"Error processing image for sharing: {e}") | |
| return "" | |
| generate_ad_button.click(generate_ad, | |
| inputs=[url_input, store_name_input, main_cupom_input, main_cupom_discount_percentage_input, cupom_1_input, original_price_input, discounted_price_input, openai_key_input, natura_token_input, openai_base_url_input, openai_model_name_input], | |
| outputs=[generate_ad_button, ad_output, ad_image_output]) | |
| clear_button.click(clear_fields, | |
| inputs=[], | |
| outputs=[url_input, original_price_input, discounted_price_input]) | |
| analyze_fragrantica_button.click(analyze_fragrantica_url, | |
| inputs=[fragrantica_url_input, openai_key_input, natura_token_input, openai_base_url_input, openai_model_name_input], | |
| outputs=fragrantica_output) | |
| gen_image_btn.click(generate_image, | |
| inputs=[image_product_url_input, image_product_name_input, image_original_price_input, image_final_price_input, image_coupon_code_input, template_selection], | |
| outputs=[gen_image_btn, image_output, share_button]) | |
| share_button.click(fn=process_image_for_sharing, | |
| inputs=[image_output], | |
| outputs=[base64_debug_output]) | |
| base64_debug_output.change(fn=None, | |
| inputs=[base64_debug_output], | |
| js=js_share_logic) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |