|
'''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) |
|
|
|
|