jsakshi commited on
Commit
751285d
·
verified ·
1 Parent(s): 9fb4d89

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +122 -352
app.py CHANGED
@@ -1,354 +1,4 @@
1
- '''import gradio as gr
2
- import os
3
- import time
4
- import tempfile
5
- import requests
6
- from PIL import Image
7
- from io import BytesIO
8
- import markdown
9
- import re
10
- import json
11
- import random
12
- from transformers import pipeline
13
- from huggingface_hub import HfApi
14
- from linkedin_api import Linkedin
15
-
16
- # Initialize models
17
- try:
18
- text_generation = pipeline(
19
- "text-generation",
20
- model="mistralai/Mistral-7B-Instruct-v0.2",
21
- max_length=4096,
22
- temperature=0.7
23
- )
24
- image_generation = pipeline("text-to-image", model="runwayml/stable-diffusion-v1-5")
25
- except Exception as e:
26
- print(f"Error loading models: {e}")
27
- # Fallback to smaller models if needed
28
- try:
29
- text_generation = pipeline(
30
- "text-generation",
31
- model="TinyLlama/TinyLlama-1.1B-Chat-v1.0",
32
- max_length=2048
33
- )
34
- image_generation = None
35
- except:
36
- text_generation = None
37
- image_generation = None
38
-
39
- # Function to generate blog content
40
- def generate_blog_content(topic, tone="professional", length="medium"):
41
- if not text_generation:
42
- return "Error: Text generation model could not be loaded."
43
-
44
- length_words = {
45
- "short": "500-800",
46
- "medium": "1000-1500",
47
- "long": "2000-2500"
48
- }[length]
49
-
50
- tone_instructions = {
51
- "professional": "Use a formal, business-like tone with industry terminology.",
52
- "casual": "Write in a conversational, friendly tone as if talking to a peer.",
53
- "technical": "Include detailed technical information and analysis.",
54
- "storytelling": "Structure the content as a narrative with examples and stories."
55
- }[tone]
56
-
57
- prompt = f"""
58
- Write a complete blog post about {topic}.
59
-
60
- {tone_instructions}
61
-
62
- The blog post should be approximately {length_words} words and include:
63
- - An attention-grabbing headline
64
- - An engaging introduction
65
- - 3-5 well-structured sections with subheadings
66
- - Practical insights and takeaways
67
- - A conclusion
68
-
69
- Format the blog in markdown with proper headings, bullet points, and emphasis.
70
- """
71
-
72
- try:
73
- result = text_generation(prompt, max_length=4096)[0]['generated_text']
74
- # Clean up the output - extract just the blog post
75
- blog_content = result.split(prompt)[-1].strip()
76
- return blog_content
77
- except Exception as e:
78
- return f"Error generating blog content: {str(e)}"
79
-
80
- # Function to generate image
81
- def generate_featured_image(topic):
82
- if not image_generation:
83
- return None, "Image generation not available. Using default image."
84
-
85
- prompt = f"Professional illustration for blog about {topic}, digital art, high quality"
86
- try:
87
- image = image_generation(prompt)
88
- if isinstance(image, list):
89
- image = image[0] if image else None
90
-
91
- temp_img_path = f"temp_image_{random.randint(1000, 9999)}.png"
92
- if hasattr(image, 'save'):
93
- image.save(temp_img_path)
94
- else:
95
- # Handle different return types
96
- if isinstance(image, dict) and 'images' in image:
97
- image = Image.fromarray(image['images'][0])
98
- image.save(temp_img_path)
99
-
100
- return temp_img_path, "Image generated successfully"
101
- except Exception as e:
102
- return None, f"Error generating image: {str(e)}"
103
-
104
- # Function to post to LinkedIn
105
- def post_to_linkedin(content, image_path=None, linkedin_username=None, linkedin_password=None):
106
- if not linkedin_username or not linkedin_password:
107
- return "Error: LinkedIn credentials are required."
108
-
109
- try:
110
- # Extract title from markdown
111
- title_match = re.search(r'^#\s+(.+)$', content, re.MULTILINE)
112
- title = title_match.group(1) if title_match else "New Blog Post"
113
-
114
- # Convert markdown to plain text for LinkedIn
115
- # Remove markdown formatting for LinkedIn post
116
- plain_content = content
117
- plain_content = re.sub(r'^#+\s+', '', plain_content, flags=re.MULTILINE) # Remove headings
118
- plain_content = re.sub(r'\*\*(.*?)\*\*', r'\1', plain_content) # Remove bold
119
- plain_content = re.sub(r'\*(.*?)\*', r'\1', plain_content) # Remove italic
120
-
121
- # Shorten for LinkedIn
122
- if len(plain_content) > 1300: # LinkedIn character limit
123
- plain_content = plain_content[:1297] + "..."
124
-
125
- # Add a title and link to full blog if available
126
- post_text = f"{title}\n\n{plain_content}"
127
-
128
- # Initialize LinkedIn API
129
- api = Linkedin(linkedin_username, linkedin_password)
130
-
131
- # Post to LinkedIn
132
- if image_path and os.path.exists(image_path):
133
- # Upload image first
134
- media_id = api.upload_image(image_path)
135
- # Post with image
136
- post_response = api.create_post(post_text, media_ids=[media_id])
137
- else:
138
- # Text-only post
139
- post_response = api.create_post(post_text)
140
-
141
- # Clean up temporary image file
142
- if image_path and os.path.exists(image_path):
143
- try:
144
- os.remove(image_path)
145
- except:
146
- pass
147
-
148
- return f"Successfully posted to LinkedIn: {title}"
149
- except Exception as e:
150
- return f"Error posting to LinkedIn: {str(e)}"
151
-
152
- # Function to save as Hugging Face Space
153
- def save_as_blog(content, title, author, image_path=None, hf_token=None):
154
- if not hf_token:
155
- return "Error: Hugging Face token is required to save blog."
156
-
157
- try:
158
- # Process content
159
- html_content = markdown.markdown(content)
160
-
161
- # Create a simple HTML template
162
- blog_html = f"""
163
- <!DOCTYPE html>
164
- <html lang="en">
165
- <head>
166
- <meta charset="UTF-8">
167
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
168
- <title>{title}</title>
169
- <style>
170
- body {{ font-family: Arial, sans-serif; line-height: 1.6; max-width: 800px; margin: 0 auto; padding: 20px; }}
171
- h1, h2, h3 {{ color: #333; }}
172
- img {{ max-width: 100%; height: auto; border-radius: 8px; margin: 20px 0; }}
173
- .author {{ font-style: italic; color: #555; margin-bottom: 20px; }}
174
- .content {{ margin-top: 30px; }}
175
- </style>
176
- </head>
177
- <body>
178
- <h1>{title}</h1>
179
- <div class="author">By {author}</div>
180
- {f'<img src="featured_image.png" alt="{title}">' if image_path else ''}
181
- <div class="content">{html_content}</div>
182
- </body>
183
- </html>
184
- """
185
-
186
- # Create a safe repo name
187
- repo_name = f"blog-{re.sub(r'[^a-z0-9-]', '-', title.lower())}"
188
-
189
- # Initialize the Hugging Face API
190
- hf_api = HfApi(token=hf_token)
191
-
192
- # Create the Space if it doesn't exist
193
- try:
194
- hf_api.create_repo(
195
- repo_id=f"spaces/{repo_name}",
196
- repo_type="space",
197
- space_sdk="static"
198
- )
199
- except Exception as e:
200
- print(f"Space might already exist: {e}")
201
-
202
- # Upload the HTML file
203
- hf_api.upload_file(
204
- path_or_fileobj=blog_html.encode(),
205
- path_in_repo="index.html",
206
- repo_id=f"spaces/{repo_name}",
207
- repo_type="space"
208
- )
209
-
210
- # Upload image if available
211
- if image_path and os.path.exists(image_path):
212
- hf_api.upload_file(
213
- path_or_fileobj=image_path,
214
- path_in_repo="featured_image.png",
215
- repo_id=f"spaces/{repo_name}",
216
- repo_type="space"
217
- )
218
-
219
- return f"Blog published successfully: https://huggingface.co/spaces/{repo_name}"
220
- except Exception as e:
221
- return f"Error saving blog: {str(e)}"
222
-
223
- # Main app function
224
- def generate_blog(topic, tone, length, author_name, publish_option, linkedin_username=None, linkedin_password=None, hf_token=None):
225
- status_updates = []
226
- status_updates.append(f"Generating blog content for topic: {topic}")
227
-
228
- # Generate the blog content
229
- start_time = time.time()
230
- blog_content = generate_blog_content(topic, tone, length)
231
- generation_time = time.time() - start_time
232
- status_updates.append(f"Content generated in {generation_time:.2f} seconds")
233
-
234
- # Extract title from content
235
- title_match = re.search(r'^#\s+(.+)$', blog_content, re.MULTILINE)
236
- title = title_match.group(1) if title_match else topic
237
-
238
- # Generate image
239
- status_updates.append("Generating featured image...")
240
- image_path, image_message = generate_featured_image(topic)
241
- status_updates.append(image_message)
242
-
243
- # Handle publishing
244
- if publish_option == "linkedin" and linkedin_username and linkedin_password:
245
- status_updates.append("Posting to LinkedIn...")
246
- linkedin_result = post_to_linkedin(blog_content, image_path, linkedin_username, linkedin_password)
247
- status_updates.append(linkedin_result)
248
-
249
- if publish_option in ["huggingface", "both"] and hf_token:
250
- status_updates.append("Saving as Hugging Face blog...")
251
- hf_result = save_as_blog(blog_content, title, author_name, image_path, hf_token)
252
- status_updates.append(hf_result)
253
-
254
- # Clean up
255
- if image_path and os.path.exists(image_path):
256
- try:
257
- os.remove(image_path)
258
- except:
259
- pass
260
-
261
- return blog_content, title, "\n".join(status_updates)
262
-
263
- # Gradio interface
264
- with gr.Blocks(title="Blog Generator & Publisher") as app:
265
- gr.Markdown("# AI Blog Generator & LinkedIn Publisher")
266
- gr.Markdown("Generate professional blog content and publish directly to LinkedIn or save to Hugging Face Spaces.")
267
-
268
- with gr.Tab("Generate Blog"):
269
- with gr.Row():
270
- with gr.Column():
271
- topic_input = gr.Textbox(label="Blog Topic", placeholder="Enter the topic of your blog post")
272
- tone_input = gr.Dropdown(
273
- label="Writing Tone",
274
- choices=["professional", "casual", "technical", "storytelling"],
275
- value="professional"
276
- )
277
- length_input = gr.Dropdown(
278
- label="Content Length",
279
- choices=["short", "medium", "long"],
280
- value="medium"
281
- )
282
- author_input = gr.Textbox(label="Author Name", placeholder="Your name")
283
-
284
- with gr.Accordion("Publishing Options", open=False):
285
- publish_option = gr.Radio(
286
- label="Publish To",
287
- choices=["none", "linkedin", "huggingface", "both"],
288
- value="none"
289
- )
290
- with gr.Group():
291
- linkedin_username = gr.Textbox(label="LinkedIn Username", visible=False)
292
- linkedin_password = gr.Textbox(label="LinkedIn Password", type="password", visible=False)
293
-
294
- hf_token = gr.Textbox(label="Hugging Face Token", type="password", visible=False)
295
-
296
- def update_visibility(option):
297
- linkedin_visible = option in ["linkedin", "both"]
298
- hf_visible = option in ["huggingface", "both"]
299
- return {
300
- linkedin_username: gr.update(visible=linkedin_visible),
301
- linkedin_password: gr.update(visible=linkedin_visible),
302
- hf_token: gr.update(visible=hf_visible)
303
- }
304
-
305
- publish_option.change(update_visibility, inputs=[publish_option], outputs=[linkedin_username, linkedin_password, hf_token])
306
-
307
- generate_btn = gr.Button("Generate Blog", variant="primary")
308
-
309
- with gr.Column():
310
- title_output = gr.Textbox(label="Blog Title")
311
- blog_output = gr.Markdown(label="Blog Content")
312
- status_output = gr.Textbox(label="Status", lines=5)
313
-
314
- generate_btn.click(
315
- generate_blog,
316
- inputs=[topic_input, tone_input, length_input, author_input, publish_option, linkedin_username, linkedin_password, hf_token],
317
- outputs=[blog_output, title_output, status_output]
318
- )
319
-
320
- with gr.Tab("About"):
321
- gr.Markdown("""
322
- ## About This Tool
323
-
324
- This application uses AI to generate professional blog content that you can publish directly to LinkedIn or save as a Hugging Face Space.
325
-
326
- ### Features:
327
-
328
- - Generate blog posts on any topic
329
- - Choose from different writing tones and length options
330
- - Create featured images automatically
331
- - Publish directly to LinkedIn
332
- - Save as a Hugging Face Space blog
333
-
334
- ### How to Use:
335
-
336
- 1. Enter your blog topic
337
- 2. Select your preferred tone and length
338
- 3. Enter your author name
339
- 4. Choose publishing options (if desired)
340
- 5. Click "Generate Blog"
341
-
342
- ### Credits:
343
-
344
- This app was created using:
345
- - Hugging Face's Transformers library
346
- - Mistral and Stable Diffusion models
347
- - Gradio for the interface
348
- """)
349
-
350
- if __name__ == "__main__":
351
- app.launch()'''
352
  import gradio as gr
353
  import os
354
  import time
@@ -511,4 +161,124 @@ with gr.Blocks(title="AI Blog Generator", theme=gr.themes.Soft()) as app:
511
  )
512
 
513
  if __name__ == "__main__":
514
- app.launch(share=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  import gradio as gr
3
  import os
4
  import time
 
161
  )
162
 
163
  if __name__ == "__main__":
164
+ app.launch(share=False)'''
165
+
166
+
167
+ import gradio as gr
168
+ import os
169
+ import time
170
+ import tempfile
171
+ import zipfile
172
+ import requests
173
+ import re
174
+ from pathlib import Path
175
+ from PIL import Image
176
+ from io import BytesIO
177
+ from dotenv import load_dotenv
178
+
179
+ # Load environment variables
180
+ load_dotenv()
181
+
182
+ class MultiAIBlogger:
183
+ def __init__(self):
184
+ self.hf_token = os.getenv("HF_TOKEN")
185
+ self.headers = {"Authorization": f"Bearer {self.hf_token}"}
186
+
187
+ # Configure models
188
+ self.text_url = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2"
189
+ self.image_url = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0"
190
+
191
+ def generate_blog(self, topic):
192
+ # Generate text content
193
+ text_prompt = f"""Create a comprehensive blog post about {topic} including:
194
+ - SEO-optimized title
195
+ - 3-5 sections with subtitles
196
+ - 2-4 image placeholders [IMAGE:description]
197
+ - Bullet points and statistics
198
+ - Conclusion with call-to-action
199
+ Use markdown formatting"""
200
+
201
+ text_response = requests.post(
202
+ self.text_url,
203
+ headers=self.headers,
204
+ json={"inputs": text_prompt, "parameters": {"max_length": 2000}}
205
+ )
206
+
207
+ if text_response.status_code != 200:
208
+ return f"Text generation failed: {text_response.text}", None
209
+
210
+ raw_content = text_response.json()[0]['generated_text']
211
+
212
+ # Generate images
213
+ image_descriptions = re.findall(r'\[IMAGE:(.*?)\]', raw_content)
214
+ images = {}
215
+ temp_dir = tempfile.mkdtemp()
216
+
217
+ for idx, desc in enumerate(image_descriptions):
218
+ img_prompt = f"Blog image: {desc}, digital art, 8k resolution, trending on artstation"
219
+ img_response = requests.post(
220
+ self.image_url,
221
+ headers=self.headers,
222
+ json={"inputs": img_prompt}
223
+ )
224
+
225
+ if img_response.status_code == 200:
226
+ image = Image.open(BytesIO(img_response.content))
227
+ img_path = Path(temp_dir) / f"image_{idx+1}.png"
228
+ image.save(img_path)
229
+ images[desc] = str(img_path)
230
+ raw_content = raw_content.replace(f"[IMAGE:{desc}]", f"![{desc}](image_{idx+1}.png)")
231
+ time.sleep(1) # Rate limit protection
232
+
233
+ # Create zip package
234
+ zip_path = self.create_package(raw_content, temp_dir)
235
+ return raw_content, zip_path
236
+
237
+ def create_package(self, content, temp_dir):
238
+ # Save markdown
239
+ md_path = Path(temp_dir) / "blog.md"
240
+ with open(md_path, "w") as f:
241
+ f.write(content)
242
+
243
+ # Create zip file
244
+ zip_path = "blog_package.zip"
245
+ with zipfile.ZipFile(zip_path, "w") as zipf:
246
+ for file in Path(temp_dir).iterdir():
247
+ zipf.write(file, file.name)
248
+
249
+ return zip_path
250
+
251
+ # Initialize blogger
252
+ blogger = MultiAIBlogger()
253
+
254
+ # Gradio interface
255
+ with gr.Blocks(theme=gr.themes.Soft()) as app:
256
+ gr.Markdown("# 🚀 AI Blog Creator")
257
+
258
+ with gr.Row():
259
+ with gr.Column():
260
+ topic_input = gr.Textbox(label="Blog Topic", placeholder="Enter your topic...")
261
+ generate_btn = gr.Button("Generate Blog", variant="primary")
262
+
263
+ with gr.Column():
264
+ gr.Markdown("## Preview & Download")
265
+ content_preview = gr.Markdown(label="Blog Preview")
266
+ download_output = gr.File(label="Download Package")
267
+ status = gr.Textbox(label="Generation Status", interactive=False)
268
+
269
+ def process_blog(topic):
270
+ try:
271
+ content, package = blogger.generate_blog(topic)
272
+ return content, package, "Generation complete!"
273
+ except Exception as e:
274
+ return "", None, f"Error: {str(e)}"
275
+
276
+ generate_btn.click(
277
+ fn=process_blog,
278
+ inputs=topic_input,
279
+ outputs=[content_preview, download_output, status],
280
+ api_name="generate_blog"
281
+ )
282
+
283
+ if __name__ == "__main__":
284
+ app.launch(share=True)