jsakshi commited on
Commit
3098acb
·
verified ·
1 Parent(s): 655c02c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +351 -0
app.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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()