GundeRichardson commited on
Commit
d77a765
Β·
verified Β·
1 Parent(s): 68a6cb0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +232 -139
app.py CHANGED
@@ -5,18 +5,17 @@ import os
5
  from youtube_transcript_api import YouTubeTranscriptApi
6
  import time
7
  import re
8
- from concurrent.futures import ThreadPoolExecutor, as_completed
 
9
  from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
10
  from google.api_core.exceptions import ResourceExhausted
11
  # Load environment variables from a .env file
12
  load_dotenv()
13
 
14
- # Configure the Google Generative AI client with the API key from environment variables
15
  genai.configure(api_key='AIzaSyBMb20Nck_BV_4297NnxlEju73UL5vsvYY')
16
 
17
-
18
  @retry(
19
- stop=stop_after_attempt(5),
20
  wait=wait_exponential(multiplier=1, min=4, max=60),
21
  retry=retry_if_exception_type(ResourceExhausted),
22
  reraise=True
@@ -31,59 +30,94 @@ def generate_content_with_retry(model, prompt):
31
 
32
  # Define the base prompt template
33
  base_prompt_template = '''
34
- You are an AI assistant specializing in transforming long-form content, such as YouTube video transcripts or user-provided text, into a single, cohesive, and engaging blog post. Your task is to create a comprehensive blog post that captures the essence of the entire input while enriching it with additional information, insights, and a conversational touch.
35
-
36
- Guidelines for the Blog Post:
37
-
38
- 1. Structure:
39
- - Title: Create an engaging title for the blog post.
40
- - Meta Description: Write a compelling 150-160 character meta description for SEO.
41
- - Introduction: Briefly introduce the topic and hook the reader.
42
- - Main Body: Divide into relevant sections with subheadings. Ensure smooth transitions between sections.
43
- - Conclusion: Summarize key points and provide a call-to-action.
44
-
45
- 2. Content Enhancement:
46
- - Synthesize information from all parts of the input to create a coherent narrative.
47
- - Provide additional explanations, examples, or related information to enrich the content.
48
- - Include interesting anecdotes or expert opinions to add depth and credibility.
49
-
50
- 3. Engagement:
51
- - Use a {tone} tone consistently throughout the post.
52
- - Include relevant descriptions of potential visuals or infographics.
53
- - Structure the post for easy readability using subheadings, bullet points, and short paragraphs.
54
-
55
- 4. SEO Optimization:
56
- - Naturally incorporate these keywords: {keywords}
57
- - Use variations and related terms to avoid keyword stuffing.
58
- - Implement proper heading structure (H1 for title, H2 for main sections, H3 for subsections).
59
-
60
- 5. Length and Style:
61
- - Aim for a total of approximately {word_count} words for the entire blog post.
62
- - Use varied sentence structures and paragraph lengths for better flow.
63
- - Incorporate rhetorical devices like analogies, metaphors, or storytelling elements where appropriate.
64
-
65
- 6. Cohesion:
66
- - Ensure that all parts of the blog post connect logically and flow smoothly.
67
- - Use transitional phrases to link different sections and ideas.
68
- - Maintain consistent themes and arguments throughout the post.
69
-
70
- 7. Formatting:
71
- - Use appropriate HTML tags for headings (h1, h2, h3), lists (ul, ol), and emphasis (strong, em).
72
- - Suggest places to break up text with [IMAGE PLACEHOLDER] or [VIDEO EMBED PLACEHOLDER] tags.
73
- - Include a table of contents for longer articles.
74
-
75
- 8. Additional Elements:
76
- - Create a "Key Takeaways" or "TL;DR" section for quick reference.
77
- - Suggest pull quotes or highlight boxes for important information.
78
- - If applicable, include a section addressing common questions or misconceptions about the topic.
79
-
80
- Important: Create only ONE cohesive blog post that covers all the main points from the entire input. Ensure that the final output is a single, well-structured article, not multiple separate posts.
81
-
82
- Please create a single, detailed, and engaging blog post based on the following input:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
  {input_text}
85
 
86
- Remember to maintain a {tone} tone throughout the post and aim for a total of {word_count} words for the entire article.
87
  '''
88
 
89
  # Expanded tone options
@@ -118,94 +152,135 @@ cache_lock = threading.Lock()
118
 
119
  # Optimized transcript fetching with caching
120
  @st.cache_data
121
- @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=4, max=10))
122
- def get_transcript(youtube_video_url, max_retries=3, delay=2):
123
- video_id = extract_video_id(youtube_video_url)
124
- if not video_id:
125
- raise ValueError("Invalid YouTube URL")
126
-
127
- # Check cache first
128
- if video_id in cache:
129
- return cache[video_id]
130
 
131
- for attempt in range(max_retries):
132
- try:
133
- transcript = YouTubeTranscriptApi.get_transcript(video_id)
134
- text = " ".join([entry["text"] for entry in transcript])
 
 
 
135
 
136
- # Cache the result
137
- with cache_lock:
138
- cache[video_id] = text
 
 
 
 
 
 
 
139
 
140
- return text
141
- except Exception as e:
142
- if attempt == max_retries - 1:
143
- raise e
144
- time.sleep(delay * (attempt + 1)) # Exponential backoff
145
-
146
- raise Exception("Failed to retrieve transcript after multiple attempts")
147
-
148
- # Function to chunk long text
149
- def chunk_text(text, chunk_size=4000, overlap=500):
150
- # Only chunk if text is longer than chunk_size
151
- if len(text) <= chunk_size:
152
- return [text]
153
-
154
- chunks = []
155
- start = 0
156
- while start < len(text):
157
- end = start + chunk_size
158
 
159
- # Find the nearest sentence end
160
- if end < len(text):
161
- end = text.rfind('.', start, end) + 1
162
- if end <= start:
163
- end = start + chunk_size
164
-
165
- chunk = text[start:end].strip()
166
- if chunk:
167
- chunks.append(chunk)
168
- start = end - overlap
169
 
170
- return chunks
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
- # Function to generate blog post using Gemini AI model with retries
173
  def generate_blog_post(input_text, tone, keywords, length):
 
174
  word_count = LENGTH_OPTIONS[length]
175
- chunks = chunk_text(input_text)
176
  model = genai.GenerativeModel("gemini-1.5-flash")
177
-
178
- all_content = []
179
- for i, chunk in enumerate(chunks):
180
- chunk_prompt = f"""
181
- Analyze the following part of content and extract key points, main ideas, and important details:
 
182
 
183
- {chunk}
 
 
184
 
185
- Provide a concise summary of this part, highlighting the most important information.
 
 
 
 
 
 
 
186
  """
187
- try:
188
- response = generate_content_with_retry(model, chunk_prompt)
189
- all_content.append(response.text)
190
- except Exception as e:
191
- st.error(f"Error processing chunk {i+1}: {str(e)}")
192
- return None
193
-
194
- final_prompt = base_prompt_template.format(
195
- tone=tone,
196
- keywords=', '.join(keywords),
197
- word_count=word_count,
198
- input_text='\n'.join(all_content)
199
- )
200
-
201
- try:
202
- final_response = generate_content_with_retry(model, final_prompt)
203
- return final_response.text
204
  except Exception as e:
205
- st.error(f"Error generating final blog post: {str(e)}")
206
  return None
207
 
208
-
209
  # Streamlit UI with progress tracking
210
  def main():
211
  st.set_page_config(page_title="BlogBrain Genius AI", layout="wide")
@@ -215,10 +290,12 @@ def main():
215
  st.session_state.blog_post = None
216
  if 'processing' not in st.session_state:
217
  st.session_state.processing = False
 
 
218
 
219
  st.title("✍️ BlogBrain Genius AI: Video to Blog Alchemist")
220
 
221
- # Input method selection with proper state management
222
  input_method = st.radio("Choose input method:", ("YouTube Video", "Custom Text"))
223
 
224
  input_text = ""
@@ -242,21 +319,30 @@ def main():
242
  keywords = st.text_input("Enter keywords (comma-separated):")
243
  length = st.selectbox("Select length:", list(LENGTH_OPTIONS.keys()))
244
 
 
245
  if st.button("Generate Blog Post") and input_text:
246
  st.session_state.processing = True
 
247
  try:
248
  with st.spinner("Generating a single, comprehensive blog post..."):
 
 
 
 
249
  blog_post = generate_blog_post(
250
  input_text,
251
  tone,
252
  keywords.split(",") if keywords else [],
253
  length
254
  )
255
- if blog_post:
256
- st.session_state.blog_post = blog_post
257
- st.success("Blog post generated successfully!")
258
- else:
259
- st.error("Failed to generate the blog post. Please try again later.")
 
 
 
260
  except Exception as e:
261
  st.error(f"An unexpected error occurred: {str(e)}")
262
  finally:
@@ -265,12 +351,19 @@ def main():
265
  # Display results
266
  if st.session_state.blog_post:
267
  st.markdown(st.session_state.blog_post)
268
- st.download_button(
269
- "Download Blog Post",
270
- st.session_state.blog_post,
271
- "blog_post.md",
272
- "text/markdown"
273
- )
 
 
 
 
 
 
 
274
 
275
  if __name__ == "__main__":
276
  main()
 
5
  from youtube_transcript_api import YouTubeTranscriptApi
6
  import time
7
  import re
8
+ import tiktoken
9
+ import time
10
  from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
11
  from google.api_core.exceptions import ResourceExhausted
12
  # Load environment variables from a .env file
13
  load_dotenv()
14
 
 
15
  genai.configure(api_key='AIzaSyBMb20Nck_BV_4297NnxlEju73UL5vsvYY')
16
 
 
17
  @retry(
18
+ stop=stop_after_attempt(3),
19
  wait=wait_exponential(multiplier=1, min=4, max=60),
20
  retry=retry_if_exception_type(ResourceExhausted),
21
  reraise=True
 
30
 
31
  # Define the base prompt template
32
  base_prompt_template = '''
33
+ You are an expert content writer and storyteller with years of experience in creating viral blog posts. Your task is to transform the input content into a captivating, human-like blog post that feels personally written and connects deeply with readers.
34
+
35
+ Guidelines for Creating an Authentic, Engaging Blog Post:
36
+
37
+ 1. Voice & Personality:
38
+ - Write as if you're having a coffee chat with the reader
39
+ - Include personal observations and insights
40
+ - Use "I", "we", and "you" to create connection
41
+ - Add thoughtful rhetorical questions to engage readers
42
+ - Include relevant personal anecdotes or examples
43
+
44
+ 2. Enhanced Structure:
45
+ - Hook: Start with a powerful personal story or provocative question
46
+ - Introduction: Create an emotional connection with the reader's pain points
47
+ - Story Arc: Maintain narrative tension throughout
48
+ - Strategic Cliffhangers: Keep readers engaged between sections
49
+ - Memorable Conclusion: End with inspiration or call-to-action
50
+
51
+ 3. Human Touch Elements:
52
+ - Add occasional conversational asides (e.g., "Now, here's the interesting part...")
53
+ - Include relatable real-world examples
54
+ - Share practical tips from personal experience
55
+ - Address potential reader objections naturally
56
+ - Use humor and wit where appropriate
57
+
58
+ 4. Engagement Boosters:
59
+ - Create "Aha!" moments
60
+ - Include surprising statistics or counterintuitive insights
61
+ - Add social proof through expert quotes or case studies
62
+ - Use power words and emotional triggers
63
+ - Create shareable, quotable moments
64
+
65
+ 5. Modern Content Optimization:
66
+ - Write scannable content with varied paragraph lengths
67
+ - Use bucket brigades to maintain flow
68
+ - Include tweet-worthy quotes
69
+ - Add content upgrades or bonus tips
70
+ - Suggest related resources
71
+
72
+ 6. Visual Flow:
73
+ - Use descriptive scene-setting
74
+ - Include sensory details
75
+ - Suggest relevant image placements
76
+ - Break up text with varied formatting
77
+ - Create visual hierarchy with subheadings
78
+
79
+ 7. Viral Elements:
80
+ - Include controversial or debate-worthy points
81
+ - Add "share-worthy" statistics or facts
82
+ - Create memorable metaphors
83
+ - Include practical takeaways
84
+ - End with discussion-provoking questions
85
+
86
+ 8. Reader Experience:
87
+ - Address common objections preemptively
88
+ - Include FAQs in conversational style
89
+ - Add expert tips and insider secrets
90
+ - Provide actionable next steps
91
+ - Create FOMO (Fear of Missing Out) elements
92
+
93
+ 9. Content Enhancement:
94
+ - Add relevant industry trends
95
+ - Include success stories or case studies
96
+ - Provide practical implementation steps
97
+ - Share common mistakes to avoid
98
+ - Offer exclusive insights
99
+
100
+ 10. SEO & Readability:
101
+ - Natural keyword integration: {keywords}
102
+ - Use power words and emotional triggers
103
+ - Create skimmable sections
104
+ - Include meta description and title suggestions
105
+ - Optimize for featured snippets
106
+
107
+ Tone Guidelines:
108
+ - Maintain a {tone} voice throughout
109
+ - Balance expertise with accessibility
110
+ - Use conversational language
111
+ - Show personality and authenticity
112
+ - Be empathetic and understanding
113
+
114
+ Length: Aim for {word_count} words while maintaining quality and engagement
115
+
116
+ Please transform the following input into a captivating, human-like blog post that readers won't be able to resist sharing:
117
 
118
  {input_text}
119
 
120
+ Remember: Write as if you're the world's most engaging storyteller sharing invaluable insights with a friend. Make every word count and every paragraph impossible to skip.
121
  '''
122
 
123
  # Expanded tone options
 
152
 
153
  # Optimized transcript fetching with caching
154
  @st.cache_data
155
+ def get_transcript(youtube_video_url):
156
+ """Get transcript from YouTube video with verification dropdown"""
157
+ try:
158
+ video_id = youtube_video_url.split("=")[1]
159
+ transcript_text = YouTubeTranscriptApi.get_transcript(video_id, languages=['en-IN', 'en', 'hi'])
160
+ transcript = " ".join([entry["text"] for entry in transcript_text])
 
 
 
161
 
162
+ # Store transcript in session state for verification
163
+ st.session_state.current_transcript = transcript
164
+
165
+ # Add expandable section to verify transcript
166
+ with st.expander("πŸ” View Raw Transcript", expanded=False):
167
+ st.markdown("### Raw Transcript")
168
+ st.markdown("*Verify the transcript before generating the blog post:*")
169
 
170
+ # Display transcript with scroll
171
+ st.markdown(
172
+ f"""
173
+ <div style="max-height: 300px; overflow-y: scroll; padding: 10px;
174
+ border: 1px solid #ccc; border-radius: 5px; background-color: #f5f5f5;">
175
+ {transcript}
176
+ </div>
177
+ """,
178
+ unsafe_allow_html=True
179
+ )
180
 
181
+ # Add word count info
182
+ word_count = len(transcript.split())
183
+ st.info(f"πŸ“ Word Count: {word_count} words")
184
+
185
+ # Add transcript quality warning if needed
186
+ if word_count < 100:
187
+ st.warning("⚠️ The transcript seems quite short. This might affect the quality of the generated blog post.")
 
 
 
 
 
 
 
 
 
 
 
188
 
189
+ return transcript
 
 
 
 
 
 
 
 
 
190
 
191
+ except Exception as e:
192
+ st.error(f"Error retrieving transcript: {e}")
193
+ return None
194
+
195
+ class ResponseManager:
196
+ def __init__(self, model):
197
+ self.model = model
198
+ self.MAX_TOKENS = 8192 # Adjust based on the model's limit
199
+ self.BATCH_SIZE = 1000 # Adjust as needed
200
+
201
+ def count_tokens(self, text: str) -> int:
202
+ try:
203
+ encoding = tiktoken.encoding_for_model("gemini-1.5-flash") # Use the correct model name
204
+ return len(encoding.encode(text))
205
+ except Exception:
206
+ return len(text.split()) * 1.3 # Fallback approximation
207
+
208
+ def generate_response(self, prompt, temperature, placeholder):
209
+ full_response = ""
210
+ continuation_prompt = "\nPlease continue from where you left off..."
211
+ current_prompt = prompt
212
+
213
+ try:
214
+ while True:
215
+ remaining_tokens = self.MAX_TOKENS - self.count_tokens(full_response)
216
+ tokens_to_generate = min(self.BATCH_SIZE, remaining_tokens)
217
+
218
+ response = self.model.generate_content(
219
+ current_prompt,
220
+ generation_config=genai.types.GenerationConfig(
221
+ temperature=temperature,
222
+ max_output_tokens=tokens_to_generate,
223
+ ),
224
+ stream=True
225
+ )
226
+
227
+ batch_response = ""
228
+ for chunk in response:
229
+ if chunk.text:
230
+ batch_response += chunk.text
231
+ full_response += chunk.text
232
+ placeholder.markdown(full_response + "β–Œ")
233
+ time.sleep(0.01)
234
+
235
+ if batch_response.strip().endswith((".", "!", "?", "\n")) or \
236
+ len(batch_response.strip()) < tokens_to_generate * 0.9:
237
+ break
238
+
239
+ current_prompt = full_response + continuation_prompt
240
+
241
+ return full_response
242
+
243
+ except Exception as e:
244
+ st.error(f"An error occurred: {str(e)}")
245
+ return f"Error generating response: {str(e)}"
246
 
 
247
  def generate_blog_post(input_text, tone, keywords, length):
248
+ """Generate a complete blog post using ResponseManager"""
249
  word_count = LENGTH_OPTIONS[length]
 
250
  model = genai.GenerativeModel("gemini-1.5-flash")
251
+ response_manager = ResponseManager(model)
252
+
253
+ try:
254
+ # Show processing status
255
+ status_container = st.empty()
256
+ status_container.info("πŸ”„ Starting blog post generation...")
257
 
258
+ # Prepare the prompt
259
+ prompt = f"""
260
+ {base_prompt_template}
261
 
262
+ Use the following input to create a single, cohesive blog post:
263
+ {input_text}
264
+
265
+ Ensure the blog post:
266
+ - Has a consistent {tone} tone
267
+ - Incorporates these keywords: {', '.join(keywords)}
268
+ - Is approximately {word_count} words long
269
+ - Flows smoothly and reads as a single, coherent piece
270
  """
271
+
272
+ # Generate content with ResponseManager
273
+ status_container.info("🎯 Generating blog post...")
274
+ blog_post = response_manager.generate_response(prompt, temperature=0.7, placeholder=status_container)
275
+
276
+ status_container.success("✨ Blog post generated successfully!")
277
+
278
+ return blog_post
279
+
 
 
 
 
 
 
 
 
280
  except Exception as e:
281
+ st.error(f"❌ Error generating blog post: {str(e)}")
282
  return None
283
 
 
284
  # Streamlit UI with progress tracking
285
  def main():
286
  st.set_page_config(page_title="BlogBrain Genius AI", layout="wide")
 
290
  st.session_state.blog_post = None
291
  if 'processing' not in st.session_state:
292
  st.session_state.processing = False
293
+ if 'cancel_generation' not in st.session_state:
294
+ st.session_state.cancel_generation = False
295
 
296
  st.title("✍️ BlogBrain Genius AI: Video to Blog Alchemist")
297
 
298
+ # Input method selection
299
  input_method = st.radio("Choose input method:", ("YouTube Video", "Custom Text"))
300
 
301
  input_text = ""
 
319
  keywords = st.text_input("Enter keywords (comma-separated):")
320
  length = st.selectbox("Select length:", list(LENGTH_OPTIONS.keys()))
321
 
322
+ # Generate button
323
  if st.button("Generate Blog Post") and input_text:
324
  st.session_state.processing = True
325
+ st.session_state.cancel_generation = False
326
  try:
327
  with st.spinner("Generating a single, comprehensive blog post..."):
328
+ # Add a cancel button
329
+ if st.button("Cancel Generation"):
330
+ st.session_state.cancel_generation = True
331
+
332
  blog_post = generate_blog_post(
333
  input_text,
334
  tone,
335
  keywords.split(",") if keywords else [],
336
  length
337
  )
338
+
339
+ if blog_post and not st.session_state.cancel_generation:
340
+ st.session_state.blog_post = blog_post
341
+ st.success("Blog post generated successfully!")
342
+ elif st.session_state.cancel_generation:
343
+ st.warning("Blog post generation was cancelled.")
344
+ else:
345
+ st.error("Failed to generate the blog post. Please try again later.")
346
  except Exception as e:
347
  st.error(f"An unexpected error occurred: {str(e)}")
348
  finally:
 
351
  # Display results
352
  if st.session_state.blog_post:
353
  st.markdown(st.session_state.blog_post)
354
+ col1, col2 = st.columns(2)
355
+ with col1:
356
+ if st.download_button(
357
+ "Download Blog Post",
358
+ st.session_state.blog_post,
359
+ "blog_post.md",
360
+ "text/markdown"
361
+ ):
362
+ st.success("Blog post downloaded successfully!")
363
+ with col2:
364
+ if st.button("Reset"):
365
+ st.session_state.blog_post = None
366
+ st.experimental_rerun()
367
 
368
  if __name__ == "__main__":
369
  main()