noumanjavaid commited on
Commit
8aab118
·
verified ·
1 Parent(s): f69a160

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +597 -0
app.py ADDED
@@ -0,0 +1,597 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from groq import Groq
3
+ import json
4
+ import os
5
+ import time
6
+ import numpy as np
7
+ import tempfile
8
+ from io import BytesIO, StringIO
9
+ from md2pdf.core import md2pdf
10
+ from dotenv import load_dotenv
11
+ from datetime import datetime
12
+ import threading
13
+ from download import download_video_audio, delete_download
14
+
15
+ # Override the max file size (40MB in bytes)
16
+ MAX_FILE_SIZE = 41943040 # 40MB in bytes
17
+ FILE_TOO_LARGE_MESSAGE = "File too large. Maximum size is 40MB."
18
+
19
+ # Load environment variables
20
+ load_dotenv()
21
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY", None)
22
+ audio_file_path = None
23
+
24
+ # Initialize session states
25
+ if 'api_key' not in st.session_state:
26
+ st.session_state.api_key = GROQ_API_KEY
27
+
28
+ if 'recording' not in st.session_state:
29
+ st.session_state.recording = False
30
+
31
+ if 'audio_data' not in st.session_state:
32
+ st.session_state.audio_data = None
33
+
34
+ if 'transcript' not in st.session_state:
35
+ st.session_state.transcript = ""
36
+
37
+ if 'groq' not in st.session_state:
38
+ if st.session_state.api_key:
39
+ st.session_state.groq = Groq(api_key=st.session_state.api_key)
40
+
41
+ # Set page configuration
42
+ st.set_page_config(
43
+ page_title="ScribeWizard 🧙‍♂️",
44
+ page_icon="🧙‍♂️",
45
+ layout="wide",
46
+ initial_sidebar_state="expanded"
47
+ )
48
+
49
+ # Fixed model selections
50
+ LLM_MODEL = "deepseek-r1-distill-llama-70b"
51
+ TRANSCRIPTION_MODEL = "distil-whisper-large-v3-en"
52
+
53
+ class GenerationStatistics:
54
+ def __init__(self, input_time=0, output_time=0, input_tokens=0, output_tokens=0, total_time=0, model_name=LLM_MODEL):
55
+ self.input_time = input_time
56
+ self.output_time = output_time
57
+ self.input_tokens = input_tokens
58
+ self.output_tokens = output_tokens
59
+ self.total_time = total_time # Sum of queue, prompt (input), and completion (output) times
60
+ self.model_name = model_name
61
+
62
+ def get_input_speed(self):
63
+ """ Tokens per second calculation for input """
64
+ if self.input_time != 0:
65
+ return self.input_tokens / self.input_time
66
+ else:
67
+ return 0
68
+
69
+ def get_output_speed(self):
70
+ """ Tokens per second calculation for output """
71
+ if self.output_time != 0:
72
+ return self.output_tokens / self.output_time
73
+ else:
74
+ return 0
75
+
76
+ def add(self, other):
77
+ """ Add statistics from another GenerationStatistics object to this one. """
78
+ if not isinstance(other, GenerationStatistics):
79
+ raise TypeError("Can only add GenerationStatistics objects")
80
+ self.input_time += other.input_time
81
+ self.output_time += other.output_time
82
+ self.input_tokens += other.input_tokens
83
+ self.output_tokens += other.output_tokens
84
+ self.total_time += other.total_time
85
+
86
+ def __str__(self):
87
+ return (f"\n## {self.get_output_speed():.2f} T/s ⚡\nRound trip time: {self.total_time:.2f}s Model: {self.model_name}\n\n"
88
+ f"| Metric | Input | Output | Total |\n"
89
+ f"|-----------------|----------------|-----------------|----------------|\n"
90
+ f"| Speed (T/s) | {self.get_input_speed():.2f} | {self.get_output_speed():.2f} | {(self.input_tokens + self.output_tokens) / self.total_time if self.total_time != 0 else 0:.2f} |\n"
91
+ f"| Tokens | {self.input_tokens} | {self.output_tokens} | {self.input_tokens + self.output_tokens} |\n"
92
+ f"| Inference Time (s) | {self.input_time:.2f} | {self.output_time:.2f} | {self.total_time:.2f} |")
93
+
94
+ class NoteSection:
95
+ def __init__(self, structure, transcript):
96
+ self.structure = structure
97
+ self.contents = {title: "" for title in self.flatten_structure(structure)}
98
+ self.placeholders = {title: st.empty() for title in self.flatten_structure(structure)}
99
+
100
+ with st.expander("Raw Transcript", expanded=False):
101
+ st.markdown(transcript)
102
+
103
+ def flatten_structure(self, structure):
104
+ sections = []
105
+ for title, content in structure.items():
106
+ sections.append(title)
107
+ if isinstance(content, dict):
108
+ sections.extend(self.flatten_structure(content))
109
+ return sections
110
+
111
+ def update_content(self, title, new_content):
112
+ try:
113
+ self.contents[title] += new_content
114
+ self.display_content(title)
115
+ except TypeError as e:
116
+ st.error(f"Error updating content: {e}")
117
+
118
+ def display_content(self, title):
119
+ if self.contents[title].strip():
120
+ self.placeholders[title].markdown(f"## {title}\n{self.contents[title]}")
121
+
122
+ def return_existing_contents(self, level=1) -> str:
123
+ existing_content = ""
124
+ for title, content in self.structure.items():
125
+ if self.contents[title].strip():
126
+ existing_content += f"{'#' * level} {title}\n{self.contents[title]}\n\n"
127
+ if isinstance(content, dict):
128
+ existing_content += self.get_markdown_content(content, level + 1)
129
+ return existing_content
130
+
131
+ def display_structure(self, structure=None, level=1):
132
+ if structure is None:
133
+ structure = self.structure
134
+ for title, content in structure.items():
135
+ if self.contents[title].strip():
136
+ st.markdown(f"{'#' * level} {title}")
137
+ self.placeholders[title].markdown(self.contents[title])
138
+ if isinstance(content, dict):
139
+ self.display_structure(content, level + 1)
140
+
141
+ def display_toc(self, structure, columns, level=1, col_index=0):
142
+ for title, content in structure.items():
143
+ with columns[col_index % len(columns)]:
144
+ st.markdown(f"{' ' * (level-1) * 2}- {title}")
145
+ col_index += 1
146
+ if isinstance(content, dict):
147
+ col_index = self.display_toc(content, columns, level + 1, col_index)
148
+ return col_index
149
+
150
+ def get_markdown_content(self, structure=None, level=1):
151
+ """ Returns the markdown styled pure string with the contents. """
152
+ if structure is None:
153
+ structure = self.structure
154
+ markdown_content = ""
155
+ for title, content in structure.items():
156
+ if self.contents[title].strip():
157
+ markdown_content += f"{'#' * level} {title}\n{self.contents[title]}\n\n"
158
+ if isinstance(content, dict):
159
+ markdown_content += self.get_markdown_content(content, level + 1)
160
+ return markdown_content
161
+
162
+ # Audio recorder functionality
163
+ class AudioRecorder:
164
+ def __init__(self, sample_rate=44100):
165
+ self.sample_rate = sample_rate
166
+ self.recording = False
167
+ self.audio_data = []
168
+ self.thread = None
169
+
170
+ def start_recording(self):
171
+ self.recording = True
172
+ self.audio_data = []
173
+ self.thread = threading.Thread(target=self._record_audio)
174
+ self.thread.start()
175
+
176
+ def _record_audio(self):
177
+ import sounddevice as sd
178
+ with sd.InputStream(callback=self._audio_callback, channels=1, samplerate=self.sample_rate):
179
+ while self.recording:
180
+ time.sleep(0.1)
181
+
182
+ def _audio_callback(self, indata, frames, time, status):
183
+ if status:
184
+ print(f"Status: {status}")
185
+ self.audio_data.append(indata.copy())
186
+
187
+ def stop_recording(self):
188
+ self.recording = False
189
+ if self.thread:
190
+ self.thread.join()
191
+
192
+ if not self.audio_data:
193
+ return None
194
+
195
+ # Concatenate all audio chunks
196
+ import numpy as np
197
+ import soundfile as sf
198
+ audio = np.concatenate(self.audio_data, axis=0)
199
+
200
+ # Save to a temporary file
201
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".m4a")
202
+ sf.write(temp_file.name, audio, self.sample_rate)
203
+
204
+ return temp_file.name
205
+
206
+ def transcribe_audio_with_groq(audio_file_path):
207
+ """Transcribe audio file using Groq's transcription API"""
208
+ if not st.session_state.api_key:
209
+ st.error("Please provide a valid Groq API key in the sidebar.")
210
+ return ""
211
+
212
+ client = Groq(api_key=st.session_state.api_key)
213
+
214
+ try:
215
+ with open(audio_file_path, "rb") as file:
216
+ transcription = client.audio.transcriptions.create(
217
+ file=(audio_file_path, file.read()),
218
+ model=TRANSCRIPTION_MODEL,
219
+ response_format="verbose_json"
220
+ )
221
+ return transcription.text
222
+ except Exception as e:
223
+ st.error(f"Error transcribing audio with Groq: {e}")
224
+ return ""
225
+
226
+ def process_transcript(transcript):
227
+ """Process transcript with Groq's DeepSeek model for highly structured notes"""
228
+ if not st.session_state.api_key:
229
+ st.error("Please provide a valid Groq API key in the sidebar.")
230
+ return None
231
+
232
+ client = Groq(api_key=st.session_state.api_key)
233
+
234
+ # Enhanced structure for better organization
235
+ structure = {
236
+ "Executive Summary": "",
237
+ "Key Insights": "",
238
+ "Action Items": "",
239
+ "Questions & Considerations": "",
240
+ "Detailed Analysis": {
241
+ "Context & Background": "",
242
+ "Main Discussion Points": "",
243
+ "Supporting Evidence": "",
244
+ "Conclusions & Recommendations": ""
245
+ }
246
+ }
247
+
248
+ prompt = f"""
249
+ You are an expert note organizer with exceptional skills in creating structured, clear, and comprehensive notes.
250
+ Please analyze the following transcript and transform it into highly organized notes:
251
+
252
+ ```
253
+ {transcript}
254
+ ```
255
+
256
+ Create a well-structured document with the following sections:
257
+
258
+ # Executive Summary
259
+ - Provide a concise 3-5 sentence overview of the main topic and key takeaways
260
+ - Use clear, direct language
261
+
262
+ # Key Insights
263
+ - Extract 5-7 critical insights as bullet points
264
+ - Each insight should be bolded and followed by 1-2 supporting sentences
265
+ - Organize these insights in order of importance
266
+
267
+ # Action Items
268
+ - Create a table with these columns: Action | Owner/Responsible Party | Timeline | Priority
269
+ - List all tasks, assignments, or follow-up items mentioned
270
+ - If information is not explicitly stated, indicate with "Not specified"
271
+
272
+ # Questions & Considerations
273
+ - List all questions raised during the discussion
274
+ - Include concerns or areas needing further exploration
275
+ - For each question, provide brief context explaining why it matters
276
+
277
+ # Detailed Analysis
278
+
279
+ ## Context & Background
280
+ - Summarize relevant background information
281
+ - Explain the context in which the discussion took place
282
+ - Include references to prior work or decisions if mentioned
283
+
284
+ ## Main Discussion Points
285
+ - Create subsections for each major topic discussed
286
+ - Use appropriate formatting (bullet points, numbered lists) to organize information
287
+ - Include direct quotes when particularly significant, marked with ">"
288
+
289
+ ## Supporting Evidence
290
+ - Create a table summarizing any data, evidence, or examples mentioned
291
+ - Include source information when available
292
+
293
+ ## Conclusions & Recommendations
294
+ - Summarize the conclusions reached
295
+ - List any recommendations or next steps discussed
296
+ - Note any decisions that were made
297
+
298
+ Make extensive use of markdown formatting:
299
+ - Use tables for structured information
300
+ - Use bold for emphasis on important points
301
+ - Use bullet points and numbered lists for clarity
302
+ - Use headings and subheadings to organize content
303
+ - Include blockquotes for direct citations
304
+
305
+ Your notes should be comprehensive but concise, focusing on extracting the maximum value from the transcript.
306
+ """
307
+
308
+ try:
309
+ stats = GenerationStatistics(model_name=LLM_MODEL)
310
+ start_time = time.time()
311
+
312
+ response = client.chat.completions.create(
313
+ messages=[{"role": "user", "content": prompt}],
314
+ model=LLM_MODEL,
315
+ temperature=0.3, # Lower temperature for more structured output
316
+ max_tokens=4096,
317
+ top_p=0.95,
318
+ stream=True
319
+ )
320
+
321
+ input_time = time.time() - start_time
322
+ stats.input_time = input_time
323
+
324
+ note_section = NoteSection(structure, transcript)
325
+ current_section = None
326
+ current_subsection = None
327
+ notes_content = ""
328
+
329
+ section_markers = {
330
+ "# Executive Summary": "Executive Summary",
331
+ "## Executive Summary": "Executive Summary",
332
+ "# Key Insights": "Key Insights",
333
+ "## Key Insights": "Key Insights",
334
+ "# Action Items": "Action Items",
335
+ "## Action Items": "Action Items",
336
+ "# Questions & Considerations": "Questions & Considerations",
337
+ "## Questions & Considerations": "Questions & Considerations",
338
+ "# Detailed Analysis": "Detailed Analysis",
339
+ "## Detailed Analysis": "Detailed Analysis",
340
+ "## Context & Background": "Context & Background",
341
+ "### Context & Background": "Context & Background",
342
+ "## Main Discussion Points": "Main Discussion Points",
343
+ "### Main Discussion Points": "Main Discussion Points",
344
+ "## Supporting Evidence": "Supporting Evidence",
345
+ "### Supporting Evidence": "Supporting Evidence",
346
+ "## Conclusions & Recommendations": "Conclusions & Recommendations",
347
+ "### Conclusions & Recommendations": "Conclusions & Recommendations"
348
+ }
349
+
350
+ for chunk in response:
351
+ if hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content is not None:
352
+ content = chunk.choices[0].delta.content
353
+ notes_content += content
354
+
355
+ # Check for section markers in the accumulated content
356
+ for marker, section in section_markers.items():
357
+ if marker in notes_content:
358
+ if section in ["Context & Background", "Main Discussion Points",
359
+ "Supporting Evidence", "Conclusions & Recommendations"]:
360
+ current_section = "Detailed Analysis"
361
+ current_subsection = section
362
+ else:
363
+ current_section = section
364
+ current_subsection = None
365
+
366
+ # Update the appropriate section
367
+ if current_section and current_section != "Detailed Analysis":
368
+ note_section.update_content(current_section, content)
369
+ elif current_section == "Detailed Analysis" and current_subsection:
370
+ note_section.update_content(current_subsection, content)
371
+
372
+ output_time = time.time() - start_time - input_time
373
+ stats.output_time = output_time
374
+ stats.total_time = time.time() - start_time
375
+
376
+ # Display statistics in expandable section
377
+ with st.expander("Generation Statistics", expanded=False):
378
+ st.markdown(str(stats))
379
+
380
+ return note_section
381
+
382
+ except Exception as e:
383
+ st.error(f"Error processing transcript: {e}")
384
+ return None
385
+
386
+ def export_notes(notes, format="markdown"):
387
+ """Export notes in the specified format"""
388
+ if format == "markdown":
389
+ markdown_content = notes.get_markdown_content()
390
+ # Create a download button for the markdown file
391
+ st.download_button(
392
+ label="Download Markdown",
393
+ data=markdown_content,
394
+ file_name=f"notes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md",
395
+ mime="text/markdown"
396
+ )
397
+ elif format == "pdf":
398
+ markdown_content = notes.get_markdown_content()
399
+ pdf_file = BytesIO()
400
+ md2pdf(pdf_file, markdown_content)
401
+ pdf_file.seek(0)
402
+
403
+ # Create a download button for the PDF file
404
+ st.download_button(
405
+ label="Download PDF",
406
+ data=pdf_file,
407
+ file_name=f"notes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf",
408
+ mime="application/pdf"
409
+ )
410
+
411
+ def main():
412
+ st.title("🧙‍♂️ ScribeWizard")
413
+ st.markdown("Transform speech into highly structured notes with AI magic")
414
+
415
+ # Sidebar for configuration
416
+ with st.sidebar:
417
+ st.header("Configuration")
418
+ api_key = st.text_input("Groq API Key", value=st.session_state.api_key or "", type="password")
419
+
420
+ if api_key:
421
+ st.session_state.api_key = api_key
422
+ if 'groq' not in st.session_state or st.session_state.groq is None:
423
+ st.session_state.groq = Groq(api_key=api_key)
424
+
425
+ st.markdown("---")
426
+ st.info("Using DeepSeek-R1-Distill-Llama-70B model for note generation and Distil Whisper for transcription")
427
+
428
+ # Input methods tabs
429
+ input_method = st.radio("Choose input method:", ["Live Recording", "Upload Audio", "YouTube URL", "Text Input"])
430
+
431
+ audio_recorder = AudioRecorder()
432
+
433
+ if input_method == "Live Recording":
434
+ col1, col2 = st.columns(2)
435
+
436
+ with col1:
437
+ if not st.session_state.recording:
438
+ if st.button("Start Recording 🎤", key="start_rec"):
439
+ st.session_state.recording = True
440
+ audio_recorder.start_recording()
441
+ st.rerun()
442
+ else:
443
+ if st.button("Stop Recording ⏹️", key="stop_rec"):
444
+ audio_file = audio_recorder.stop_recording()
445
+ st.session_state.recording = False
446
+
447
+ if audio_file:
448
+ st.session_state.audio_data = audio_file
449
+ st.success("Recording saved!")
450
+
451
+ # Auto-transcribe using Groq
452
+ with st.spinner("Transcribing audio with Groq..."):
453
+ transcript = transcribe_audio_with_groq(audio_file)
454
+ if transcript:
455
+ st.session_state.transcript = transcript
456
+ st.success("Transcription complete!")
457
+ st.rerun()
458
+
459
+ with col2:
460
+ if st.session_state.recording:
461
+ st.markdown("#### 🔴 Recording in progress...")
462
+
463
+ # Animated recording indicator
464
+ progress_bar = st.progress(0)
465
+ for i in range(100):
466
+ time.sleep(0.05)
467
+ progress_bar.progress((i + 1) % 101)
468
+
469
+ # Break if recording stopped
470
+ if not st.session_state.recording:
471
+ break
472
+ st.rerun()
473
+
474
+ if st.session_state.audio_data:
475
+ st.audio(st.session_state.audio_data)
476
+
477
+ if st.session_state.transcript:
478
+ if st.button("Generate Structured Notes", key="generate_live"):
479
+ with st.spinner("Creating highly structured notes..."):
480
+ notes = process_transcript(st.session_state.transcript)
481
+
482
+ if notes:
483
+ st.success("Notes generated successfully!")
484
+
485
+ # Export options
486
+ col1, col2 = st.columns(2)
487
+ with col1:
488
+ if st.button("Export as Markdown", key="md_live"):
489
+ export_notes(notes, "markdown")
490
+ with col2:
491
+ if st.button("Export as PDF", key="pdf_live"):
492
+ export_notes(notes, "pdf")
493
+
494
+ elif input_method == "Upload Audio":
495
+ uploaded_file = st.file_uploader("Upload an audio file (max 40MB)", type=["mp3", "wav", "m4a", "ogg"])
496
+
497
+ if uploaded_file:
498
+ file_size = uploaded_file.size
499
+ if file_size > MAX_FILE_SIZE:
500
+ st.error(f"File size ({file_size/1048576:.2f}MB) exceeds the maximum allowed size of 40MB.")
501
+ else:
502
+ # Save the uploaded file temporarily
503
+ with tempfile.NamedTemporaryFile(delete=False, suffix="." + uploaded_file.name.split(".")[-1]) as tmp_file:
504
+ tmp_file.write(uploaded_file.getvalue())
505
+ audio_file_path = tmp_file.name
506
+
507
+ st.audio(uploaded_file)
508
+
509
+ if st.button("Transcribe and Generate Notes", key="transcribe_upload"):
510
+ with st.spinner("Transcribing audio with Groq..."):
511
+ transcript = transcribe_audio_with_groq(audio_file_path)
512
+
513
+ if transcript:
514
+ st.session_state.transcript = transcript
515
+
516
+ with st.spinner("Creating highly structured notes..."):
517
+ notes = process_transcript(transcript)
518
+
519
+ if notes:
520
+ st.success("Notes generated successfully!")
521
+
522
+ # Export options
523
+ col1, col2 = st.columns(2)
524
+ with col1:
525
+ if st.button("Export as Markdown", key="md_upload"):
526
+ export_notes(notes, "markdown")
527
+ with col2:
528
+ if st.button("Export as PDF", key="pdf_upload"):
529
+ export_notes(notes, "pdf")
530
+
531
+ elif input_method == "YouTube URL":
532
+ youtube_url = st.text_input("Enter YouTube URL:")
533
+
534
+ if youtube_url:
535
+ if st.button("Process YouTube Content", key="process_yt"):
536
+ with st.spinner("Downloading YouTube content..."):
537
+ try:
538
+ audio_path = download_video_audio(youtube_url)
539
+
540
+ if audio_path:
541
+ st.success("Video downloaded successfully!")
542
+ st.audio(audio_path)
543
+
544
+ with st.spinner("Transcribing audio with Groq..."):
545
+ transcript = transcribe_audio_with_groq(audio_path)
546
+
547
+ if transcript:
548
+ st.session_state.transcript = transcript
549
+
550
+ with st.spinner("Creating highly structured notes..."):
551
+ notes = process_transcript(transcript)
552
+
553
+ if notes:
554
+ st.success("Notes generated successfully!")
555
+
556
+ # Export options
557
+ col1, col2 = st.columns(2)
558
+ with col1:
559
+ if st.button("Export as Markdown", key="md_yt"):
560
+ export_notes(notes, "markdown")
561
+ with col2:
562
+ if st.button("Export as PDF", key="pdf_yt"):
563
+ export_notes(notes, "pdf")
564
+
565
+ # Clean up downloaded files
566
+ delete_download(audio_path)
567
+
568
+ except Exception as e:
569
+ if "exceeds maximum allowed size" in str(e):
570
+ st.error(f"{FILE_TOO_LARGE_MESSAGE} Try a shorter video.")
571
+ else:
572
+ st.error(f"Error processing YouTube video: {e}")
573
+
574
+ else: # Text Input
575
+ transcript = st.text_area("Enter transcript text:", height=300)
576
+
577
+ if transcript:
578
+ st.session_state.transcript = transcript
579
+
580
+ if st.button("Generate Structured Notes", key="process_text"):
581
+ with st.spinner("Creating highly structured notes..."):
582
+ notes = process_transcript(transcript)
583
+
584
+ if notes:
585
+ st.success("Notes generated successfully!")
586
+
587
+ # Export options
588
+ col1, col2 = st.columns(2)
589
+ with col1:
590
+ if st.button("Export as Markdown", key="md_text"):
591
+ export_notes(notes, "markdown")
592
+ with col2:
593
+ if st.button("Export as PDF", key="pdf_text"):
594
+ export_notes(notes, "pdf")
595
+
596
+ if __name__ == "__main__":
597
+ main()