File size: 12,250 Bytes
3c7d0f6
 
df0eb35
5b4a293
94a7d52
457afbf
ae4bc61
3cdec01
94a7d52
3c7d0f6
e4f36f5
 
 
 
 
 
df0eb35
ddc7a67
 
 
df0eb35
 
 
 
 
ddc7a67
df0eb35
ddc7a67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df0eb35
 
 
 
 
 
 
 
 
 
 
3c7d0f6
94a7d52
ae4bc61
94a7d52
e4f36f5
94a7d52
 
 
ae4bc61
 
 
 
 
94a7d52
 
3c7d0f6
e4f36f5
94a7d52
3c7d0f6
 
94a7d52
3c7d0f6
94a7d52
3c7d0f6
ae4bc61
3c7d0f6
 
 
 
 
 
 
94a7d52
3c7d0f6
9944cf3
94a7d52
3c7d0f6
94a7d52
 
 
df0eb35
94a7d52
ae4bc61
 
 
3c7d0f6
5b4a293
 
 
 
 
 
df0eb35
 
 
 
 
 
 
 
 
02d4d4d
c5d76a1
 
 
 
 
 
30d4e20
c5d76a1
02d4d4d
 
df0eb35
 
6bc7ff7
ddc7a67
df0eb35
 
3c7d0f6
 
170eb66
e4f36f5
 
9362af6
 
170eb66
e4f36f5
170eb66
 
 
94a7d52
 
 
5b4a293
df0eb35
5b4a293
df0eb35
 
5b4a293
 
 
 
df0eb35
 
 
 
ddc7a67
df0eb35
02d4d4d
df0eb35
 
 
 
ddc7a67
df0eb35
 
 
 
 
 
02d4d4d
 
df0eb35
 
ddc7a67
 
 
 
 
 
 
 
 
 
 
 
 
5b4a293
ae4bc61
 
 
 
 
 
 
 
 
ddc7a67
02d4d4d
ddc7a67
ae4bc61
 
 
 
 
 
5b4a293
 
96e9004
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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)