| | '''import gradio as gr |
| | import os |
| | import time |
| | import requests |
| | import re |
| | import uuid |
| | import markdown |
| | from datetime import datetime |
| | from dotenv import load_dotenv |
| | from huggingface_hub import HfApi, upload_file |
| | |
| | load_dotenv() |
| | |
| | # Configuration |
| | HF_TOKEN = os.getenv("HF_TOKEN") |
| | HF_USERNAME = "jsakshi" |
| | HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"} |
| | |
| | def generate_blog_content(topic): |
| | try: |
| | prompt = f"""Create a detailed, professional blog post about {topic} including: |
| | - A compelling title and subtitle |
| | - An introduction |
| | - 3 main sections with descriptive headings |
| | - Key points and data examples in each section |
| | - A conclusion |
| | Use an informative, professional tone.""" |
| | |
| | response = requests.post( |
| | "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2", |
| | headers=HEADERS, |
| | json={"inputs": prompt, "parameters": {"max_length": 2000}} |
| | ) |
| | |
| | if response.status_code != 200: |
| | return None, f"API Error: {response.text}" |
| | |
| | blog_text = response.json()[0]['generated_text'] |
| | |
| | # Extract title (assuming first line contains the title) |
| | lines = blog_text.split('\n') |
| | title = topic |
| | subtitle = "A comprehensive analysis and exploration" |
| | |
| | for line in lines[:5]: |
| | if line.strip() and not line.startswith('#'): |
| | title = line.strip() |
| | break |
| | |
| | # Look for a possible subtitle |
| | for line in lines[1:10]: |
| | if line.strip() and line != title and not line.startswith('#'): |
| | subtitle = line.strip() |
| | break |
| | |
| | return { |
| | "title": title, |
| | "subtitle": subtitle, |
| | "content": blog_text |
| | }, None |
| | |
| | except Exception as e: |
| | return None, f"Error: {str(e)}" |
| | |
| | def create_hosted_blog(topic): |
| | try: |
| | # Generate blog content first |
| | content_data, error = generate_blog_content(topic) |
| | if error or not content_data: |
| | return f"Error generating content: {error}", "" |
| | |
| | # Create unique space |
| | space_id = f"blog-{uuid.uuid4().hex[:8]}" |
| | space_name = f"{HF_USERNAME}/{space_id}" |
| | |
| | # Initialize Hub API |
| | api = HfApi(token=HF_TOKEN) |
| | api.create_repo( |
| | repo_id=space_name, |
| | repo_type="space", |
| | space_sdk="static", |
| | private=False |
| | ) |
| | |
| | # Generate and upload images |
| | image_paths = [] |
| | image_prompts = [ |
| | f"Professional illustration about {topic}, integrating real-world images with clean design and minimalist style. " |
| | f"Include conceptual diagrams, flowcharts, or graphs alongside real-world elements to enhance understanding. " |
| | f"Use subtle colors, modern typography, and a well-structured layout for clarity and engagement.", |
| | |
| | f"Data visualization or concept diagram related to {topic}, combining infographic elements with real-world scenarios. " |
| | f"Ensure a balance of artistic design and informative content, making it suitable for presentations or reports. " |
| | f"Include 3D renders or photorealistic overlays to improve visualization." |
| | ] |
| | |
| | for idx, img_prompt in enumerate(image_prompts): |
| | response = requests.post( |
| | "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0", |
| | headers=HEADERS, |
| | json={"inputs": img_prompt} |
| | ) |
| | if response.status_code == 200: |
| | img_filename = f"image_{idx}.png" |
| | upload_file( |
| | path_or_fileobj=response.content, |
| | path_in_repo=img_filename, |
| | repo_id=space_name, |
| | repo_type="space" |
| | ) |
| | image_paths.append(img_filename) |
| | time.sleep(2) # Add delay to prevent rate limiting |
| | |
| | # Format the current date |
| | current_date = datetime.now().strftime("%B %d, %Y") |
| | |
| | # Create HTML using the modern template from the example |
| | title = content_data.get("title", topic) |
| | subtitle = content_data.get("subtitle", "A comprehensive analysis") |
| | content = content_data.get("content", "") |
| | |
| | # Process the content to get sections for TOC |
| | sections = [] |
| | section_pattern = re.compile(r'^##?\s+(.+)$', re.MULTILINE) |
| | section_matches = section_pattern.findall(content) |
| | |
| | for i, section in enumerate(section_matches[:6]): |
| | section_id = section.lower().replace(' ', '-').replace(':', '') |
| | sections.append({ |
| | "title": section, |
| | "id": section_id |
| | }) |
| | |
| | # Convert markdown content to HTML with proper section IDs |
| | html_content = content |
| | for section in sections: |
| | pattern = f"## {section['title']}" |
| | replacement = f"<h2 id=\"{section['id']}\">{section['title']}</h2>" |
| | html_content = html_content.replace(pattern, replacement) |
| | |
| | html_content = markdown.markdown(html_content) |
| | |
| | # Create complete HTML |
| | complete_html = f"""<!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>{title}</title> |
| | <style> |
| | :root {{ |
| | --primary-color: #2D68C4; |
| | --secondary-color: #f8f9fa; |
| | --text-color: #333; |
| | --light-gray: #e9ecef; |
| | --dark-gray: #495057; |
| | }} |
| | |
| | * {{ |
| | margin: 0; |
| | padding: 0; |
| | box-sizing: border-box; |
| | }} |
| | |
| | body {{ |
| | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | line-height: 1.6; |
| | color: var(--text-color); |
| | background-color: #fff; |
| | }} |
| | |
| | header {{ |
| | background: linear-gradient(135deg, var(--primary-color), #1d4ed8); |
| | color: white; |
| | padding: 2rem 0; |
| | text-align: center; |
| | }} |
| | |
| | .container {{ |
| | max-width: 1200px; |
| | margin: 0 auto; |
| | padding: 0 2rem; |
| | }} |
| | |
| | .blog-header {{ |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | }} |
| | |
| | .blog-title {{ |
| | font-size: 2.5rem; |
| | margin-bottom: 1rem; |
| | }} |
| | |
| | .blog-subtitle {{ |
| | font-size: 1.2rem; |
| | opacity: 0.9; |
| | }} |
| | |
| | .blog-meta {{ |
| | display: flex; |
| | margin-top: 1rem; |
| | font-size: 0.9rem; |
| | }} |
| | |
| | .blog-meta div {{ |
| | margin-right: 1.5rem; |
| | display: flex; |
| | align-items: center; |
| | }} |
| | |
| | .blog-content {{ |
| | display: grid; |
| | grid-template-columns: 1fr; |
| | gap: 2rem; |
| | margin: 3rem 0; |
| | }} |
| | |
| | @media (min-width: 768px) {{ |
| | .blog-content {{ |
| | grid-template-columns: 7fr 3fr; |
| | }} |
| | }} |
| | |
| | .main-content {{ |
| | background-color: white; |
| | border-radius: 8px; |
| | box-shadow: 0 4px 6px rgba(0,0,0,0.05); |
| | padding: 2rem; |
| | }} |
| | |
| | .sidebar {{ |
| | position: sticky; |
| | top: 2rem; |
| | height: fit-content; |
| | }} |
| | |
| | .sidebar-section {{ |
| | background-color: white; |
| | border-radius: 8px; |
| | box-shadow: 0 4px 6px rgba(0,0,0,0.05); |
| | padding: 1.5rem; |
| | margin-bottom: 2rem; |
| | }} |
| | |
| | .sidebar-title {{ |
| | font-size: 1.2rem; |
| | margin-bottom: 1rem; |
| | padding-bottom: 0.5rem; |
| | border-bottom: 2px solid var(--light-gray); |
| | }} |
| | |
| | .toc-list {{ |
| | list-style: none; |
| | }} |
| | |
| | .toc-list li {{ |
| | margin-bottom: 0.5rem; |
| | }} |
| | |
| | .toc-list a {{ |
| | color: var(--primary-color); |
| | text-decoration: none; |
| | }} |
| | |
| | .toc-list a:hover {{ |
| | text-decoration: underline; |
| | }} |
| | |
| | h1, h2, h3, h4 {{ |
| | margin: 1.5rem 0 1rem 0; |
| | line-height: 1.3; |
| | }} |
| | |
| | h1 {{ |
| | font-size: 2rem; |
| | }} |
| | |
| | h2 {{ |
| | font-size: 1.75rem; |
| | border-bottom: 2px solid var(--light-gray); |
| | padding-bottom: 0.5rem; |
| | }} |
| | |
| | h3 {{ |
| | font-size: 1.4rem; |
| | }} |
| | |
| | p {{ |
| | margin-bottom: 1.5rem; |
| | }} |
| | |
| | .blog-image {{ |
| | width: 100%; |
| | height: auto; |
| | border-radius: 8px; |
| | margin: 1.5rem 0; |
| | }} |
| | |
| | .highlight-box {{ |
| | background-color: var(--secondary-color); |
| | border-left: 4px solid var(--primary-color); |
| | padding: 1.5rem; |
| | margin: 1.5rem 0; |
| | border-radius: 0 8px 8px 0; |
| | }} |
| | |
| | .highlight-box h4 {{ |
| | margin-top: 0; |
| | color: var(--primary-color); |
| | }} |
| | |
| | footer {{ |
| | background-color: var(--dark-gray); |
| | color: white; |
| | padding: 2rem 0; |
| | margin-top: 3rem; |
| | }} |
| | |
| | .footer-content {{ |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | text-align: center; |
| | }} |
| | |
| | @media (min-width: 768px) {{ |
| | .footer-content {{ |
| | flex-direction: row; |
| | justify-content: space-between; |
| | text-align: left; |
| | }} |
| | }} |
| | |
| | .footer-links {{ |
| | list-style: none; |
| | display: flex; |
| | margin-top: 1rem; |
| | }} |
| | |
| | .footer-links li {{ |
| | margin-right: 1rem; |
| | }} |
| | |
| | .footer-links a {{ |
| | color: white; |
| | text-decoration: none; |
| | }} |
| | |
| | .footer-links a:hover {{ |
| | text-decoration: underline; |
| | }} |
| | </style> |
| | </head> |
| | <body> |
| | <header> |
| | <div class="container"> |
| | <div class="blog-header"> |
| | <h1 class="blog-title">{title}</h1> |
| | <div class="blog-meta"> |
| | <div>Published: {current_date}</div> |
| | <div>Reading time: 8 minutes</div> |
| | </div> |
| | </div> |
| | </div> |
| | </header> |
| | |
| | <div class="container"> |
| | <div class="blog-content"> |
| | <article class="main-content"> |
| | {f'<img src="{image_paths[0]}" alt="{topic} illustration" class="blog-image" />' if image_paths else ''} |
| | |
| | {html_content} |
| | |
| | {f'<img src="{image_paths[1]}" alt="{topic} visualization" class="blog-image" />' if len(image_paths) > 1 else ''} |
| | |
| | <div class="highlight-box"> |
| | <h4>Key Takeaways</h4> |
| | <p>This article explores the essential aspects of {topic}, providing insights into current trends, challenges, and future opportunities in this field.</p> |
| | </div> |
| | </article> |
| | |
| | <aside class="sidebar"> |
| | <div class="sidebar-section"> |
| | <h3 class="sidebar-title">Table of Contents</h3> |
| | <ul class="toc-list"> |
| | {''.join([f'<li><a href="#{section["id"]}">{section["title"]}</a></li>' for section in sections])} |
| | </ul> |
| | </div> |
| | |
| | <div class="sidebar-section"> |
| | <h3 class="sidebar-title">About the Author</h3> |
| | <p>This article was created by Sakshi Jadhav that combines research, writing, and design capabilities to produce comprehensive, informative content on cutting-edge topics.</p> |
| | </div> |
| | |
| | <div class="sidebar-section"> |
| | <h3 class="sidebar-title">Related Topics</h3> |
| | <ul class="toc-list"> |
| | <li><a href="#">Latest Developments in {topic}</a></li> |
| | <li><a href="#">Industry Perspectives on {topic}</a></li> |
| | <li><a href="#">Research Advancements in {topic}</a></li> |
| | <li><a href="#">Case Studies: {topic} in Action</a></li> |
| | </ul> |
| | </div> |
| | </aside> |
| | </div> |
| | </div> |
| | |
| | <footer> |
| | <div class="container"> |
| | <div class="footer-content"> |
| | <div> |
| | <p>© {datetime.now().year} Professional Blog Hub</p> |
| | <p>Created with AI Blog Generator</p> |
| | </div> |
| | <ul class="footer-links"> |
| | <li><a href="#">Home</a></li> |
| | <li><a href="#">About</a></li> |
| | <li><a href="#">Topics</a></li> |
| | <li><a href="#">Contact</a></li> |
| | </ul> |
| | </div> |
| | </div> |
| | </footer> |
| | </body> |
| | </html> |
| | """ |
| | |
| | # Upload HTML file |
| | upload_file( |
| | path_or_fileobj=complete_html.encode(), |
| | path_in_repo="index.html", |
| | repo_id=space_name, |
| | repo_type="space" |
| | ) |
| | |
| | return f"https://huggingface.co/spaces/{space_name}", content_data.get("content", "") |
| | |
| | except Exception as e: |
| | return f"Error: {str(e)}", "" |
| | |
| | # Gradio interface |
| | with gr.Blocks(theme=gr.themes.Soft()) as app: |
| | gr.Markdown("# 📄 Professional Blog Generator") |
| | gr.Markdown("Create well-structured, professional blog posts with just a topic") |
| | |
| | with gr.Row(): |
| | with gr.Column(): |
| | topic_input = gr.Textbox(label="Enter Blog Topic", |
| | placeholder="e.g., Future of AI in Healthcare") |
| | generate_btn = gr.Button("Generate Blog", variant="primary") |
| | status = gr.Textbox(label="Status", interactive=False) |
| | |
| | with gr.Column(): |
| | gr.Markdown("### Blog URL") |
| | blog_link = gr.Markdown("Your blog link will appear here...") |
| | gr.Markdown("### Preview") |
| | blog_output = gr.Markdown() |
| | |
| | generate_btn.click( |
| | fn=create_hosted_blog, |
| | inputs=topic_input, |
| | outputs=[blog_link, blog_output] |
| | ) |
| | |
| | if __name__ == "__main__": |
| | app.launch(share=True)''' |
| |
|
| |
|
| |
|
| | |
| | import gradio as gr |
| | import os |
| | import time |
| | import requests |
| | import re |
| | import uuid |
| | import markdown |
| | from datetime import datetime |
| | from dotenv import load_dotenv |
| | from huggingface_hub import HfApi, upload_file |
| | import json |
| | from functools import partial |
| | import logging |
| |
|
| | |
| | logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") |
| | logger = logging.getLogger(__name__) |
| |
|
| | load_dotenv() |
| |
|
| | |
| | HF_TOKEN = os.getenv("HF_TOKEN") |
| | if not HF_TOKEN: |
| | logger.error("HF_TOKEN not found in .env file") |
| | HF_USERNAME = "jsakshi" |
| | HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"} |
| |
|
| | |
| | AUTHORIZED_USERS = {"admin": "password123"} |
| |
|
| | |
| | edit_history = [] |
| | current_history_index = -1 |
| |
|
| | def generate_initial_content(topic): |
| | """Generate initial blog content using Hugging Face API.""" |
| | logger.info(f"Generating content for topic: {topic}") |
| | try: |
| | prompt = f"""Create a detailed blog post about {topic} including: |
| | - A compelling title and subtitle |
| | - An introduction |
| | - 3 main sections with descriptive headings |
| | - A conclusion |
| | Use an informative tone.""" |
| | response = requests.post( |
| | "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2", |
| | headers=HEADERS, |
| | json={"inputs": prompt, "parameters": {"max_length": 2000}} |
| | ) |
| | if response.status_code != 200: |
| | logger.error(f"API request failed: {response.status_code} - {response.text}") |
| | return f"Error: API request failed with status {response.status_code}" |
| | return response.json()[0]['generated_text'] |
| | except Exception as e: |
| | logger.error(f"Error generating content: {str(e)}") |
| | return f"Error generating content: {str(e)}" |
| |
|
| | def generate_image(prompt): |
| | """Generate an image using Stable Diffusion API.""" |
| | try: |
| | response = requests.post( |
| | "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0", |
| | headers=HEADERS, |
| | json={"inputs": prompt} |
| | ) |
| | if response.status_code == 200: |
| | return response.content |
| | logger.error(f"Image generation failed: {response.status_code} - {response.text}") |
| | return None |
| | except Exception as e: |
| | logger.error(f"Error generating image: {str(e)}") |
| | return None |
| |
|
| | def create_or_update_space(content_data, space_name=None, images=[]): |
| | """Create or update a Hugging Face Space with editable content.""" |
| | try: |
| | api = HfApi(token=HF_TOKEN) |
| | if not space_name: |
| | space_id = f"blog-{uuid.uuid4().hex[:8]}" |
| | space_name = f"{HF_USERNAME}/{space_id}" |
| | api.create_repo(repo_id=space_name, repo_type="space", space_sdk="static", private=False) |
| | logger.info(f"Created new space: {space_name}") |
| |
|
| | |
| | sections = re.split(r'(## .+)', content_data) |
| | html_content = '<div class="editable-container">' |
| | current_section = "" |
| | for part in sections: |
| | if part.strip(): |
| | if part.startswith('## '): |
| | if current_section: |
| | html_content += f'<div class="section-content" contenteditable="true">{markdown.markdown(current_section)}</div></div>' |
| | html_content += f'<div class="section"><h2 class="editable-header" contenteditable="true">{part[3:]}</h2>' |
| | current_section = "" |
| | else: |
| | current_section += part |
| | if current_section: |
| | html_content += f'<div class="section-content" contenteditable="true">{markdown.markdown(current_section)}</div></div>' |
| | html_content += '</div>' |
| |
|
| | |
| | image_html = "" |
| | for i, img_path in enumerate(images): |
| | image_html += f'<div class="image-container" draggable="true" data-index="{i}"><img src="{img_path}" class="editable-image" alt="Blog image" /><button class="delete-image">Delete</button></div>' |
| |
|
| | |
| | complete_html = f"""<!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <title>Editable Blog</title> |
| | <style> |
| | .editable-container {{ max-width: 800px; margin: 20px auto; padding: 20px; }} |
| | .editable-header {{ font-size: 1.5em; margin: 20px 0 10px; cursor: text; }} |
| | .section-content {{ margin-bottom: 20px; cursor: text; }} |
| | .image-container {{ position: relative; margin: 20px 0; }} |
| | .editable-image {{ width: 100%; max-width: 500px; cursor: move; }} |
| | .delete-image {{ position: absolute; top: 5px; right: 5px; }} |
| | .editing-tools {{ position: fixed; top: 10px; left: 10px; background: white; padding: 10px; border: 1px solid #ccc; z-index: 1000; }} |
| | [contenteditable]:focus {{ outline: 2px solid #2D68C4; }} |
| | body {{ font-family: Arial, sans-serif; line-height: 1.6; }} |
| | </style> |
| | <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
| | </head> |
| | <body> |
| | <div class="editing-tools" id="tools" style="display: none;"> |
| | <button onclick="document.execCommand('bold')">B</button> |
| | <button onclick="document.execCommand('italic')">I</button> |
| | <select onchange="document.execCommand('formatBlock', false, this.value)"> |
| | <option value="">Normal</option> |
| | <option value="h1">H1</option> |
| | <option value="h2">H2</option> |
| | <option value="h3">H3</option> |
| | </select> |
| | <button onclick="saveChanges()">Save</button> |
| | <button onclick="preview()">Preview</button> |
| | <button onclick="undo()">Undo</button> |
| | <button onclick="redo()">Redo</button> |
| | </div> |
| | {html_content} |
| | {image_html} |
| | <script> |
| | let currentSpace = "{space_name}"; |
| | let images = {json.dumps(images)}; |
| | |
| | function saveChanges() {{ |
| | const content = document.querySelector('.editable-container').innerHTML; |
| | fetch('/update', {{ |
| | method: 'POST', |
| | headers: {{ 'Content-Type': 'application/json' }}, |
| | body: JSON.stringify({{ space: currentSpace, content: content, images: images }}) |
| | }}).then(() => alert('Saved!')); |
| | addToHistory(content); |
| | }} |
| | |
| | function preview() {{ |
| | const content = document.querySelector('.editable-container').innerHTML; |
| | const previewWindow = window.open('', '_blank'); |
| | previewWindow.document.write('<html><body>' + marked.parse(content) + '</body></html>'); |
| | }} |
| | |
| | function addToHistory(content) {{ |
| | if (window.historyIndex < window.history.length - 1) {{ |
| | window.history.splice(window.historyIndex + 1); |
| | }} |
| | window.history.push(content); |
| | window.historyIndex = window.history.length - 1; |
| | localStorage.setItem('editHistory', JSON.stringify(window.history)); |
| | localStorage.setItem('historyIndex', window.historyIndex); |
| | }} |
| | |
| | window.history = JSON.parse(localStorage.getItem('editHistory') || '[]'); |
| | window.historyIndex = parseInt(localStorage.getItem('historyIndex') || '-1'); |
| | |
| | function undo() {{ |
| | if (window.historyIndex > 0) {{ |
| | window.historyIndex--; |
| | document.querySelector('.editable-container').innerHTML = window.history[window.historyIndex]; |
| | localStorage.setItem('historyIndex', window.historyIndex); |
| | }} |
| | }} |
| | |
| | function redo() {{ |
| | if (window.historyIndex < window.history.length - 1) {{ |
| | window.historyIndex++; |
| | document.querySelector('.editable-container').innerHTML = window.history[window.historyIndex]; |
| | localStorage.setItem('historyIndex', window.historyIndex); |
| | }} |
| | }} |
| | |
| | // Drag and drop images |
| | document.querySelectorAll('.image-container').forEach(container => {{ |
| | container.addEventListener('dragstart', e => {{ |
| | e.dataTransfer.setData('text/plain', container.dataset.index); |
| | }}); |
| | container.addEventListener('dragover', e => e.preventDefault()); |
| | container.addEventListener('drop', e => {{ |
| | e.preventDefault(); |
| | const fromIndex = e.dataTransfer.getData('text/plain'); |
| | const toIndex = container.dataset.index; |
| | if (fromIndex !== toIndex) {{ |
| | const temp = images[fromIndex]; |
| | images[fromIndex] = images[toIndex]; |
| | images[toIndex] = temp; |
| | saveChanges(); |
| | location.reload(); |
| | }} |
| | }}); |
| | }}); |
| | |
| | // Delete image |
| | document.querySelectorAll('.delete-image').forEach(btn => {{ |
| | btn.addEventListener('click', () => {{ |
| | const index = btn.parentElement.dataset.index; |
| | images.splice(index, 1); |
| | saveChanges(); |
| | location.reload(); |
| | }}); |
| | }}); |
| | |
| | // Autosave every 30 seconds |
| | setInterval(saveChanges, 30000); |
| | |
| | // Show tools on edit |
| | document.querySelectorAll('[contenteditable]').forEach(el => {{ |
| | el.addEventListener('focus', () => document.getElementById('tools').style.display = 'block'); |
| | }}); |
| | </script> |
| | </body> |
| | </html>""" |
| |
|
| | |
| | upload_file( |
| | path_or_fileobj=complete_html.encode(), |
| | path_in_repo="index.html", |
| | repo_id=space_name, |
| | repo_type="space" |
| | ) |
| |
|
| | |
| | for i, img in enumerate(images): |
| | if isinstance(img, bytes): |
| | upload_file( |
| | path_or_fileobj=img, |
| | path_in_repo=f"image_{i}.png", |
| | repo_id=space_name, |
| | repo_type="space" |
| | ) |
| | images[i] = f"image_{i}.png" |
| |
|
| | logger.info(f"Updated space: {space_name}") |
| | return f"https://huggingface.co/spaces/{space_name}" |
| | except Exception as e: |
| | logger.error(f"Error creating/updating space: {str(e)}") |
| | return None |
| |
|
| | def authenticate(username, password): |
| | """Authenticate user against hardcoded credentials.""" |
| | if not username or not password: |
| | logger.warning("Empty username or password provided") |
| | return False |
| | is_valid = username in AUTHORIZED_USERS and AUTHORIZED_USERS[username] == password |
| | logger.info(f"Authentication attempt for {username}: {'Success' if is_valid else 'Failed'}") |
| | return is_valid |
| |
|
| | def generate_and_edit(topic, username, password): |
| | """Generate blog and create editable space.""" |
| | logger.info(f"Starting generate_and_edit for topic: {topic}") |
| | |
| | |
| | if not authenticate(username, password): |
| | return "Authentication failed: Incorrect username or password", "", "Error" |
| | |
| | |
| | if not HF_TOKEN: |
| | return "Authentication failed: HF_TOKEN not set in .env", "", "Error" |
| |
|
| | |
| | initial_content = generate_initial_content(topic) |
| | if "Error" in initial_content: |
| | return "Failed to generate content", initial_content, "Error" |
| |
|
| | |
| | image_prompts = [ |
| | f"Professional illustration about {topic}, clean design, minimalist style.", |
| | f"Data visualization related to {topic}, infographic style." |
| | ] |
| | images = [] |
| | for prompt in image_prompts: |
| | img_data = generate_image(prompt) |
| | if img_data: |
| | images.append(img_data) |
| | time.sleep(2) |
| |
|
| | |
| | space_url = create_or_update_space(initial_content, images=images) |
| | if not space_url: |
| | return "Failed to create space", initial_content, "Error" |
| |
|
| | return space_url, initial_content, "Blog generated successfully" |
| |
|
| | |
| | with gr.Blocks(theme=gr.themes.Soft()) as app: |
| | gr.Markdown("# 📝 Blog Editor") |
| | gr.Markdown("Generate and edit professional blog posts with an intuitive interface") |
| | |
| | with gr.Row(): |
| | with gr.Column(): |
| | username = gr.Textbox(label="Username", placeholder="admin") |
| | password = gr.Textbox(label="Password", type="password", placeholder="password123") |
| | topic_input = gr.Textbox(label="Blog Topic", placeholder="e.g., Future of AI") |
| | generate_btn = gr.Button("Generate & Edit", variant="primary") |
| | |
| | with gr.Column(): |
| | status = gr.Textbox(label="Status", interactive=False) |
| | blog_link = gr.Markdown("Blog link will appear here...") |
| | blog_preview = gr.Markdown(label="Preview", value="Content preview will appear here...") |
| | |
| | generate_btn.click( |
| | fn=generate_and_edit, |
| | inputs=[topic_input, username, password], |
| | outputs=[blog_link, blog_preview, status] |
| | ) |
| |
|
| | if __name__ == "__main__": |
| | app.launch(share=True, debug=True) |
| | |
| |
|