jsakshi commited on
Commit
2ede80e
·
verified ·
1 Parent(s): 391bde9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +245 -343
app.py CHANGED
@@ -1,168 +1,3 @@
1
- '''
2
- import gradio as gr
3
- import os
4
- import time
5
- import tempfile
6
- import requests
7
- from PIL import Image
8
- from io import BytesIO
9
- import re
10
- from datetime import datetime
11
- from dotenv import load_dotenv
12
-
13
- # Load environment variables
14
- load_dotenv()
15
-
16
- # Hugging Face configuration
17
- HF_TOKEN = os.getenv("HF_TOKEN")
18
- TEXT_API_URL = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2"
19
- IMAGE_API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
20
- HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
21
-
22
- def generate_blog_content(topic, tone="professional", length="medium"):
23
- try:
24
- current_date = datetime.now().strftime("%B %d, %Y")
25
- reading_time = {"short": "5-8", "medium": "8-12", "long": "15-20"}[length]
26
-
27
- prompt = f"""<s>[INST] Write a {tone} blog post about {topic} with:
28
- - Title and subtitle
29
- - Introduction with statistics
30
- - 2 main sections with subsections
31
- - Conclusion
32
- - Markdown formatting
33
- - Published date: {current_date}
34
- - Reading time: {reading_time} minutes [/INST]</s>"""
35
-
36
- payload = {
37
- "inputs": prompt,
38
- "parameters": {
39
- "max_new_tokens": 1024,
40
- "temperature": 0.7,
41
- "return_full_text": False
42
- }
43
- }
44
-
45
- response = requests.post(TEXT_API_URL, headers=HEADERS, json=payload)
46
-
47
- if response.status_code == 503:
48
- estimate = response.json().get('estimated_time', 30)
49
- time.sleep(estimate)
50
- response = requests.post(TEXT_API_URL, headers=HEADERS, json=payload)
51
-
52
- response.raise_for_status()
53
- return response.json()[0]['generated_text']
54
-
55
- except Exception as e:
56
- return f"Error generating content: {str(e)}"
57
-
58
- def generate_featured_image(topic):
59
- try:
60
- prompt = f"Professional digital illustration for blog about {topic}, high quality"
61
- payload = {
62
- "inputs": prompt,
63
- "parameters": {
64
- "height": 512,
65
- "width": 768,
66
- "num_inference_steps": 25
67
- }
68
- }
69
-
70
- response = requests.post(IMAGE_API_URL, headers=HEADERS, json=payload)
71
- response.raise_for_status()
72
-
73
- image = Image.open(BytesIO(response.content))
74
- temp_img = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
75
- image.save(temp_img.name)
76
- return temp_img.name, None # Return path and no error
77
-
78
- except Exception as e:
79
- return None, f"Image error: {str(e)}"
80
-
81
- def create_download_file(content, title, author):
82
- try:
83
- temp_file = tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".md", encoding="utf-8")
84
- full_content = content + f"\n\n**About the Author** \n{author}"
85
- temp_file.write(full_content)
86
- temp_file.close()
87
-
88
- sanitized_title = re.sub(r'[^\w\-_ ]', '_', title)[:50]
89
- return temp_file.name # Return only the file path
90
-
91
- except Exception as e:
92
- return None
93
-
94
- def generate_blog(topic, tone, length, author_name, publish_option, linkedin_user=None, linkedin_pass=None, hf_token=None):
95
- status_updates = []
96
- blog_content = ""
97
- title = ""
98
- file_path = None
99
-
100
- try:
101
- # Generate content
102
- status_updates.append("🚀 Starting blog generation...")
103
- blog_content = generate_blog_content(topic, tone, length)
104
-
105
- if "Error" in blog_content:
106
- return blog_content, "", "\n".join(status_updates), None
107
-
108
- # Extract title
109
- title_match = re.search(r'^#\s+(.+)$', blog_content, re.MULTILINE)
110
- title = title_match.group(1).strip() if title_match else topic
111
-
112
- # Generate image
113
- status_updates.append("🖼️ Generating featured image...")
114
- image_path, image_error = generate_featured_image(topic)
115
- if image_error:
116
- status_updates.append(image_error)
117
- else:
118
- status_updates.append("✅ Image generated!")
119
-
120
- # Create downloadable file
121
- status_updates.append("📥 Preparing download...")
122
- file_path = create_download_file(blog_content, title, author_name)
123
- status_updates.append("✅ Download ready!")
124
-
125
- return blog_content, title, "\n".join(status_updates), file_path
126
-
127
- except Exception as e:
128
- status_updates.append(f"❌ Critical error: {str(e)}")
129
- return blog_content, title, "\n".join(status_updates), None
130
-
131
- # Gradio interface
132
- with gr.Blocks(title="AI Blog Generator", theme=gr.themes.Soft()) as app:
133
- gr.Markdown("# 📝 AI Blog Generator")
134
-
135
- with gr.Row():
136
- with gr.Column(scale=1):
137
- topic_input = gr.Textbox(label="Blog Topic")
138
- tone_input = gr.Dropdown(
139
- ["professional", "casual", "technical", "storytelling"],
140
- label="Writing Style",
141
- value="professional"
142
- )
143
- length_input = gr.Dropdown(
144
- ["short", "medium", "long"],
145
- label="Article Length",
146
- value="medium"
147
- )
148
- author_input = gr.Textbox(label="Author Name")
149
- generate_btn = gr.Button("Generate", variant="primary")
150
-
151
- with gr.Column(scale=2):
152
- title_output = gr.Textbox(label="Generated Title")
153
- blog_output = gr.Markdown()
154
- status_output = gr.Textbox(label="Status")
155
- download_output = gr.File(label="Download")
156
-
157
- generate_btn.click(
158
- generate_blog,
159
- inputs=[topic_input, tone_input, length_input, author_input, gr.Radio(["none"], visible=False)],
160
- outputs=[blog_output, title_output, status_output, download_output]
161
- )
162
-
163
- if __name__ == "__main__":
164
- app.launch(share=False)'''
165
-
166
  '''import gradio as gr
167
  import os
168
  import time
@@ -181,184 +16,6 @@ HF_TOKEN = os.getenv("HF_TOKEN")
181
  HF_USERNAME = "jsakshi"
182
  HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
183
 
184
- def generate_blog_content(topic):
185
- try:
186
- prompt = f"""Create a professional blog post about {topic} including:
187
- - Title and subtitle
188
- - 3 sections with subsections
189
- - 2 image placeholders [IMAGE1][IMAGE2]
190
- - Data points and examples
191
- - Conclusion
192
- Use markdown formatting"""
193
-
194
- response = requests.post(
195
- "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2",
196
- headers=HEADERS,
197
- json={"inputs": prompt, "parameters": {"max_length": 2000}}
198
- )
199
-
200
- if response.status_code != 200:
201
- return None, f"API Error: {response.text}"
202
-
203
- return response.json()[0]['generated_text'], None
204
-
205
- except Exception as e:
206
- return None, f"Error: {str(e)}"
207
-
208
- def create_hosted_blog(topic):
209
- try:
210
- # Generate blog content first
211
- content, error = generate_blog_content(topic)
212
- if error or not content:
213
- return f"Error generating content: {error}", ""
214
-
215
- # Create unique space
216
- space_id = f"blog-{uuid.uuid4().hex[:8]}"
217
- space_name = f"{HF_USERNAME}/{space_id}"
218
-
219
- # Initialize Hub API
220
- api = HfApi(token=HF_TOKEN)
221
- api.create_repo(
222
- repo_id=space_name,
223
- repo_type="space",
224
- space_sdk="static",
225
- private=False
226
- )
227
-
228
- # Generate and upload images
229
- image_paths = []
230
- for idx in range(2):
231
- response = requests.post(
232
- "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0",
233
- headers=HEADERS,
234
- json={"inputs": f"Professional illustration about {topic}, clean design"}
235
- )
236
- if response.status_code == 200:
237
- upload_file(
238
- path_or_fileobj=response.content,
239
- path_in_repo=f"image_{idx}.png",
240
- repo_id=space_name,
241
- repo_type="space"
242
- )
243
- image_paths.append(f"image_{idx}.png")
244
- time.sleep(1)
245
-
246
- # Replace image placeholders
247
- if content:
248
- updated_content = content.replace("[IMAGE1]", f'![Image 1]({image_paths[0]})').replace("[IMAGE2]", f'![Image 2]({image_paths[1]})')
249
- else:
250
- return "Error: No content generated", ""
251
-
252
- # Create HTML template
253
- html_content = f"""
254
- <!DOCTYPE html>
255
- <html>
256
- <head>
257
- <title>{topic}</title>
258
- <style>
259
- body {{
260
- max-width: 800px;
261
- margin: 0 auto;
262
- padding: 40px;
263
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
264
- line-height: 1.6;
265
- color: #333;
266
- }}
267
- .header {{
268
- text-align: center;
269
- padding: 40px 0;
270
- border-bottom: 1px solid #eee;
271
- margin-bottom: 40px;
272
- }}
273
- h1 {{
274
- color: #2c3e50;
275
- font-size: 2.5em;
276
- margin-bottom: 10px;
277
- }}
278
- img {{
279
- width: 100%;
280
- height: auto;
281
- margin: 25px 0;
282
- border-radius: 8px;
283
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
284
- }}
285
- .content {{
286
- background: white;
287
- padding: 30px;
288
- border-radius: 8px;
289
- }}
290
- </style>
291
- </head>
292
- <body>
293
- <div class="header">
294
- <h1>{topic}</h1>
295
- <p>Published on {datetime.now().strftime("%B %d, %Y")}</p>
296
- </div>
297
- <div class="content">
298
- {markdown.markdown(updated_content)}
299
- </div>
300
- </body>
301
- </html>
302
- """
303
-
304
- # Upload HTML file
305
- upload_file(
306
- path_or_fileobj=html_content.encode(),
307
- path_in_repo="index.html",
308
- repo_id=space_name,
309
- repo_type="space"
310
- )
311
-
312
- return f"https://huggingface.co/spaces/{space_name}", content
313
-
314
- except Exception as e:
315
- return f"Error: {str(e)}", ""
316
-
317
- # Gradio interface
318
- with gr.Blocks(theme=gr.themes.Soft()) as app:
319
- gr.Markdown("# 📄 Professional Blog Generator")
320
-
321
- with gr.Row():
322
- with gr.Column():
323
- topic_input = gr.Textbox(label="Enter Blog Topic",
324
- placeholder="e.g., Future of AI in Healthcare")
325
- generate_btn = gr.Button("Generate Blog", variant="primary")
326
-
327
- with gr.Column():
328
- gr.Markdown("### Generated Content")
329
- blog_output = gr.Markdown()
330
- gr.Markdown("### Blog URL")
331
- blog_link = gr.Markdown("Your blog link will appear here...")
332
- status = gr.Textbox(label="Status", interactive=False)
333
-
334
- generate_btn.click(
335
- fn=create_hosted_blog,
336
- inputs=topic_input,
337
- outputs=[blog_link, blog_output]
338
- )
339
-
340
- if __name__ == "__main__":
341
- app.launch(share=True)'''
342
-
343
-
344
- import gradio as gr
345
- import os
346
- import time
347
- import requests
348
- import re
349
- import uuid
350
- import markdown
351
- from datetime import datetime
352
- from dotenv import load_dotenv
353
- from huggingface_hub import HfApi, upload_file
354
-
355
- load_dotenv()
356
-
357
- # Configuration
358
- HF_TOKEN = os.getenv("HF_TOKEN")
359
- HF_USERNAME = "jsakshi"
360
- HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
361
-
362
  def generate_blog_content(topic):
363
  try:
364
  prompt = f"""Create a detailed, professional blog post about {topic} including:
@@ -807,5 +464,250 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
807
  outputs=[blog_link, blog_output]
808
  )
809
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
810
  if __name__ == "__main__":
811
  app.launch(share=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  '''import gradio as gr
2
  import os
3
  import time
 
16
  HF_USERNAME = "jsakshi"
17
  HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def generate_blog_content(topic):
20
  try:
21
  prompt = f"""Create a detailed, professional blog post about {topic} including:
 
464
  outputs=[blog_link, blog_output]
465
  )
466
 
467
+ if __name__ == "__main__":
468
+ app.launch(share=True)'''
469
+
470
+ import gradio as gr
471
+ import os
472
+ import time
473
+ import requests
474
+ import re
475
+ import uuid
476
+ import markdown
477
+ from datetime import datetime
478
+ from dotenv import load_dotenv
479
+ from huggingface_hub import HfApi, upload_file
480
+ import json
481
+ from functools import partial
482
+
483
+ load_dotenv()
484
+
485
+ # Configuration
486
+ HF_TOKEN = os.getenv("HF_TOKEN")
487
+ HF_USERNAME = "jsakshi"
488
+ HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"}
489
+
490
+ # Simulated user authentication (in practice, use a proper auth system)
491
+ AUTHORIZED_USERS = {"admin": "password123"} # username: password
492
+
493
+ # Store edit history for undo/redo
494
+ edit_history = []
495
+ current_history_index = -1
496
+
497
+ def generate_initial_content(topic):
498
+ # Same as original generate_blog_content but simplified for example
499
+ prompt = f"Create a blog post about {topic} with title, subtitle, introduction, 3 sections, and conclusion."
500
+ response = requests.post(
501
+ "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2",
502
+ headers=HEADERS,
503
+ json={"inputs": prompt, "parameters": {"max_length": 2000}}
504
+ )
505
+ if response.status_code == 200:
506
+ return response.json()[0]['generated_text']
507
+ return f"# {topic}\n## Subtitle\nIntro text\n## Section 1\nText\n## Section 2\nText\n## Section 3\nText\n## Conclusion\nText"
508
+
509
+ def create_or_update_space(content_data, space_name=None, images=[]):
510
+ api = HfApi(token=HF_TOKEN)
511
+
512
+ if not space_name:
513
+ space_id = f"blog-{uuid.uuid4().hex[:8]}"
514
+ space_name = f"{HF_USERNAME}/{space_id}"
515
+ api.create_repo(repo_id=space_name, repo_type="space", space_sdk="static", private=False)
516
+
517
+ # Generate HTML with contenteditable attributes
518
+ sections = re.split(r'(## .+)', content_data)
519
+ html_content = '<div class="editable-container">'
520
+ current_section = ""
521
+
522
+ for part in sections:
523
+ if part.strip():
524
+ if part.startswith('## '):
525
+ if current_section:
526
+ html_content += f'<div class="section-content" contenteditable="true">{markdown.markdown(current_section)}</div></div>'
527
+ html_content += f'<div class="section"><h2 class="editable-header" contenteditable="true">{part[3:]}</h2>'
528
+ current_section = ""
529
+ else:
530
+ current_section += part
531
+ if current_section:
532
+ html_content += f'<div class="section-content" contenteditable="true">{markdown.markdown(current_section)}</div></div>'
533
+ html_content += '</div>'
534
+
535
+ # Add images
536
+ for i, img in enumerate(images):
537
+ html_content += f'<div class="image-container" draggable="true" data-index="{i}"><img src="{img}" class="editable-image" /><button class="delete-image">Delete</button></div>'
538
+
539
+ complete_html = f"""<!DOCTYPE html>
540
+ <html>
541
+ <head>
542
+ <meta charset="UTF-8">
543
+ <title>Editable Blog</title>
544
+ <style>
545
+ .editable-container {{ max-width: 800px; margin: 20px auto; padding: 20px; }}
546
+ .editable-header {{ font-size: 1.5em; margin: 20px 0 10px; cursor: text; }}
547
+ .section-content {{ margin-bottom: 20px; cursor: text; }}
548
+ .image-container {{ position: relative; margin: 20px 0; }}
549
+ .editable-image {{ width: 100%; max-width: 500px; cursor: move; }}
550
+ .delete-image {{ position: absolute; top: 5px; right: 5px; }}
551
+ .editing-tools {{ position: fixed; top: 10px; left: 10px; background: white; padding: 10px; border: 1px solid #ccc; }}
552
+ [contenteditable]:focus {{ outline: 2px solid #2D68C4; }}
553
+ </style>
554
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
555
+ </head>
556
+ <body>
557
+ <div class="editing-tools" id="tools" style="display: none;">
558
+ <button onclick="document.execCommand('bold')">B</button>
559
+ <button onclick="document.execCommand('italic')">I</button>
560
+ <select onchange="document.execCommand('formatBlock', false, this.value)">
561
+ <option value="">Normal</option>
562
+ <option value="h1">H1</option>
563
+ <option value="h2">H2</option>
564
+ <option value="h3">H3</option>
565
+ </select>
566
+ <button onclick="saveChanges()">Save</button>
567
+ <button onclick="preview()">Preview</button>
568
+ <button onclick="undo()">Undo</button>
569
+ <button onclick="redo()">Redo</button>
570
+ </div>
571
+ {html_content}
572
+ <script>
573
+ let currentSpace = "{space_name}";
574
+ let images = {json.dumps(images)};
575
+
576
+ function saveChanges() {{
577
+ const content = document.querySelector('.editable-container').innerHTML;
578
+ fetch('/update', {{
579
+ method: 'POST',
580
+ headers: {{ 'Content-Type': 'application/json' }},
581
+ body: JSON.stringify({{ space: currentSpace, content: content, images: images }})
582
+ }}).then(() => alert('Saved!'));
583
+ addToHistory(content);
584
+ }}
585
+
586
+ function preview() {{
587
+ const content = document.querySelector('.editable-container').innerHTML;
588
+ const previewWindow = window.open('', '_blank');
589
+ previewWindow.document.write('<html><body>' + marked.parse(content) + '</body></html>');
590
+ }}
591
+
592
+ function addToHistory(content) {{
593
+ if (window.historyIndex < window.history.length - 1) {{
594
+ window.history.splice(window.historyIndex + 1);
595
+ }}
596
+ window.history.push(content);
597
+ window.historyIndex = window.history.length - 1;
598
+ localStorage.setItem('editHistory', JSON.stringify(window.history));
599
+ localStorage.setItem('historyIndex', window.historyIndex);
600
+ }}
601
+
602
+ window.history = JSON.parse(localStorage.getItem('editHistory') || '[]');
603
+ window.historyIndex = parseInt(localStorage.getItem('historyIndex') || '-1');
604
+
605
+ function undo() {{
606
+ if (window.historyIndex > 0) {{
607
+ window.historyIndex--;
608
+ document.querySelector('.editable-container').innerHTML = window.history[window.historyIndex];
609
+ localStorage.setItem('historyIndex', window.historyIndex);
610
+ }}
611
+ }}
612
+
613
+ function redo() {{
614
+ if (window.historyIndex < window.history.length - 1) {{
615
+ window.historyIndex++;
616
+ document.querySelector('.editable-container').innerHTML = window.history[window.historyIndex];
617
+ localStorage.setItem('historyIndex', window.historyIndex);
618
+ }}
619
+ }}
620
+
621
+ // Drag and drop
622
+ document.querySelectorAll('.image-container').forEach(container => {{
623
+ container.addEventListener('dragstart', e => {{
624
+ e.dataTransfer.setData('text/plain', container.dataset.index);
625
+ }});
626
+ container.addEventListener('dragover', e => e.preventDefault());
627
+ container.addEventListener('drop', e => {{
628
+ e.preventDefault();
629
+ const fromIndex = e.dataTransfer.getData('text/plain');
630
+ const toIndex = container.dataset.index;
631
+ if (fromIndex !== toIndex) {{
632
+ const temp = images[fromIndex];
633
+ images[fromIndex] = images[toIndex];
634
+ images[toIndex] = temp;
635
+ saveChanges();
636
+ location.reload();
637
+ }}
638
+ }});
639
+ }});
640
+
641
+ // Delete image
642
+ document.querySelectorAll('.delete-image').forEach(btn => {{
643
+ btn.addEventListener('click', () => {{
644
+ const index = btn.parentElement.dataset.index;
645
+ images.splice(index, 1);
646
+ saveChanges();
647
+ location.reload();
648
+ }});
649
+ }});
650
+
651
+ // Autosave every 30 seconds
652
+ setInterval(saveChanges, 30000);
653
+
654
+ // Show tools on edit
655
+ document.querySelectorAll('[contenteditable]').forEach(el => {{
656
+ el.addEventListener('focus', () => document.getElementById('tools').style.display = 'block');
657
+ }});
658
+ </script>
659
+ </body>
660
+ </html>"""
661
+
662
+ upload_file(
663
+ path_or_fileobj=complete_html.encode(),
664
+ path_in_repo="index.html",
665
+ repo_id=space_name,
666
+ repo_type="space"
667
+ )
668
+ return f"https://huggingface.co/spaces/{space_name}"
669
+
670
+ def authenticate(username, password):
671
+ return username in AUTHORIZED_USERS and AUTHORIZED_USERS[username] == password
672
+
673
+ def generate_and_edit(topic, username, password):
674
+ if not authenticate(username, password):
675
+ return "Authentication failed", "", ""
676
+
677
+ initial_content = generate_initial_content(topic)
678
+ space_url = create_or_update_space(initial_content)
679
+ return space_url, initial_content, "Blog generated successfully"
680
+
681
+ # Mock update endpoint (in practice, this would be a server-side API)
682
+ def update_content(space, content, images):
683
+ create_or_update_space(content, space, images)
684
+ return "Updated successfully"
685
+
686
+ # Gradio interface
687
+ with gr.Blocks(theme=gr.themes.Soft()) as app:
688
+ gr.Markdown("# 📝 Blog Editor")
689
+ gr.Markdown("Generate and edit professional blog posts with an intuitive interface")
690
+
691
+ with gr.Row():
692
+ with gr.Column():
693
+ username = gr.Textbox(label="Username", placeholder="admin")
694
+ password = gr.Textbox(label="Password", type="password")
695
+ topic_input = gr.Textbox(label="Blog Topic", placeholder="e.g., Future of AI")
696
+ generate_btn = gr.Button("Generate & Edit", variant="primary")
697
+
698
+ with gr.Column():
699
+ status = gr.Textbox(label="Status", interactive=False)
700
+ blog_link = gr.Markdown("Blog link will appear here...")
701
+ blog_preview = gr.Markdown(label="Preview", value="Content preview will appear here...")
702
+
703
+ # Update function (simulated)
704
+ update_btn = gr.Button("Update Content", visible=False)
705
+
706
+ generate_btn.click(
707
+ fn=generate_and_edit,
708
+ inputs=[topic_input, username, password],
709
+ outputs=[blog_link, blog_preview, status]
710
+ )
711
+
712
  if __name__ == "__main__":
713
  app.launch(share=True)