awacke1 commited on
Commit
483840d
ยท
verified ยท
1 Parent(s): 26476ff

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +80 -49
app.py CHANGED
@@ -77,7 +77,7 @@ for d in ["chat_logs", "vote_logs", "audio_logs", "history_logs", "audio_cache"]
77
 
78
  CHAT_DIR = "chat_logs"
79
  VOTE_DIR = "vote_logs"
80
- MEDIA_DIR = "." # Root directory
81
  AUDIO_CACHE_DIR = "audio_cache"
82
  AUDIO_DIR = "audio_logs"
83
  STATE_FILE = "user_state.txt"
@@ -101,11 +101,12 @@ def format_timestamp_prefix(username=""):
101
 
102
  # ๐Ÿ“ˆ Performance Timer
103
  class PerformanceTimer:
104
- def __init__(self, name): self.name, self.start = name, None
105
- def __enter__(self):
 
106
  self.start = time.time()
107
  return self
108
- def __exit__(self, *args):
109
  duration = time.time() - self.start
110
  st.session_state['operation_timings'][self.name] = duration
111
  st.session_state['performance_metrics'][self.name].append(duration)
@@ -128,7 +129,8 @@ def init_session_state():
128
  'quote_source': "famous"
129
  }
130
  for k, v in defaults.items():
131
- if k not in st.session_state: st.session_state[k] = v
 
132
 
133
  # ๐Ÿ–Œ๏ธ Marquee Helpers
134
  def update_marquee_settings_ui():
@@ -147,8 +149,12 @@ def display_marquee(text, settings, key_suffix=""):
147
  st.write("")
148
 
149
  # ๐Ÿ“ Text & File Helpers
150
- def clean_text_for_tts(text): return re.sub(r'[#*!\[\]]+', '', ' '.join(text.split()))[:200] or "No text"
151
- def clean_text_for_filename(text): return '_'.join(re.sub(r'[^\w\s-]', '', text.lower()).split())[:200]
 
 
 
 
152
  def get_high_info_terms(text, top_n=10):
153
  stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with'}
154
  words = re.findall(r'\b\w+(?:-\w+)*\b', text.lower())
@@ -163,7 +169,8 @@ def generate_filename(prompt, username, file_type="md"):
163
 
164
  def create_file(prompt, username, file_type="md"):
165
  filename = generate_filename(prompt, username, file_type)
166
- with open(filename, 'w', encoding='utf-8') as f: f.write(prompt)
 
167
  return filename
168
 
169
  def get_download_link(file, file_type="mp3"):
@@ -202,10 +209,12 @@ def concatenate_markdown_files():
202
  # ๐ŸŽถ Audio Processing
203
  async def async_edge_tts_generate(text, voice, username, rate=0, pitch=0, file_format="mp3"):
204
  cache_key = f"{text[:100]}_{voice}_{rate}_{pitch}_{file_format}"
205
- if cache_key in st.session_state['audio_cache']: return st.session_state['audio_cache'][cache_key], 0
 
206
  start_time = time.time()
207
  text = clean_text_for_tts(text)
208
- if not text: return None, 0
 
209
  filename = f"{format_timestamp_prefix(username)}-{hashlib.md5(text.encode()).hexdigest()[:8]}.{file_format}"
210
  communicate = edge_tts.Communicate(text, voice, rate=f"{rate:+d}%", pitch=f"{pitch:+d}Hz")
211
  await communicate.save(filename)
@@ -228,11 +237,13 @@ async def save_chat_entry(username, message, is_markdown=False):
228
  central = pytz.timezone('US/Central')
229
  timestamp = datetime.now(central).strftime("%Y-%m-%d %H:%M:%S")
230
  entry = f"[{timestamp}] {username}: {message}" if not is_markdown else f"[{timestamp}] {username}:\n```markdown\n{message}\n```"
231
- with open(CHAT_FILE, 'a') as f: f.write(f"{entry}\n")
 
232
  voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
233
  audio_file, _ = await async_edge_tts_generate(message, voice, username)
234
  if audio_file:
235
- with open(HISTORY_FILE, 'a') as f: f.write(f"[{timestamp}] {username}: Audio - {audio_file}\n")
 
236
  st.session_state['mp3_files'][os.path.basename(audio_file)] = audio_file
237
  await broadcast_message(f"{username}|{message}", "chat")
238
  st.session_state.last_chat_update = time.time()
@@ -241,8 +252,9 @@ async def save_chat_entry(username, message, is_markdown=False):
241
 
242
  async def load_chat():
243
  if not os.path.exists(CHAT_FILE):
244
- with open(CHAT_FILE, 'a') as f: f.write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub! ๐ŸŽค\n")
245
- with open(CHAT_FILE, 'r') as f:
 
246
  content = f.read().strip()
247
  lines = content.split('\n')
248
  numbered_content = "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines) if line.strip())
@@ -298,7 +310,8 @@ class AudioProcessor:
298
  self.metadata = json.load(open(f"{self.cache_dir}/metadata.json")) if os.path.exists(f"{self.cache_dir}/metadata.json") else {}
299
 
300
  def _save_metadata(self):
301
- with open(f"{self.cache_dir}/metadata.json", 'w') as f: json.dump(self.metadata, f)
 
302
 
303
  async def create_audio(self, text, voice='en-US-AriaNeural'):
304
  cache_key = hashlib.md5(f"{text}:{voice}".encode()).hexdigest()
@@ -306,7 +319,8 @@ class AudioProcessor:
306
  if cache_key in self.metadata and os.path.exists(cache_path):
307
  return cache_path
308
  text = clean_text_for_tts(text)
309
- if not text: return None
 
310
  communicate = edge_tts.Communicate(text, voice)
311
  await communicate.save(cache_path)
312
  self.metadata[cache_key] = {'timestamp': datetime.now().isoformat(), 'text_length': len(text), 'voice': voice}
@@ -317,7 +331,7 @@ def process_pdf(pdf_file, max_pages, voice, audio_processor):
317
  reader = PdfReader(pdf_file)
318
  total_pages = min(len(reader.pages), max_pages)
319
  texts, audios = [], {}
320
- async def process_page(i, text):
321
  audio_path = await audio_processor.create_audio(text, voice)
322
  if audio_path:
323
  audios[i] = audio_path
@@ -329,19 +343,24 @@ def process_pdf(pdf_file, max_pages, voice, audio_processor):
329
 
330
  # ๐Ÿ” ArXiv & AI Lookup
331
  def parse_arxiv_refs(ref_text):
332
- if not ref_text: return []
 
333
  papers = []
334
  current = {}
335
  for line in ref_text.split('\n'):
336
  if line.count('|') == 2:
337
- if current: papers.append(current)
 
338
  date, title, *_ = line.strip('* ').split('|')
339
  url = re.search(r'(https://arxiv.org/\S+)', line).group(1) if re.search(r'(https://arxiv.org/\S+)', line) else f"paper_{len(papers)}"
340
  current = {'date': date, 'title': title, 'url': url, 'authors': '', 'summary': '', 'full_audio': None, 'download_base64': ''}
341
  elif current:
342
- if not current['authors']: current['authors'] = line.strip('* ')
343
- else: current['summary'] += ' ' + line.strip() if current['summary'] else line.strip()
344
- if current: papers.append(current)
 
 
 
345
  return papers[:20]
346
 
347
  def generate_5min_feature_markdown(paper):
@@ -363,13 +382,15 @@ def generate_5min_feature_markdown(paper):
363
  ---
364
  """
365
 
366
- def create_detailed_paper_md(papers): return "# Detailed Summary\n" + "\n".join(generate_5min_feature_markdown(p) for p in papers)
 
367
 
368
  async def create_paper_audio_files(papers, query):
369
  for p in papers:
370
  audio_text = clean_text_for_tts(f"{p['title']} by {p['authors']}. {p['summary']}")
371
  p['full_audio'], _ = await async_edge_tts_generate(audio_text, st.session_state['tts_voice'], p['authors'])
372
- if p['full_audio']: p['download_base64'] = get_download_link(p['full_audio'])
 
373
 
374
  async def perform_ai_lookup(q, useArxiv=True, useArxivAudio=False):
375
  client = anthropic.Anthropic(api_key=anthropic_key)
@@ -388,7 +409,8 @@ async def perform_ai_lookup(q, useArxiv=True, useArxivAudio=False):
388
  md_file, audio_file = create_file(result, "System", "md"), (await async_edge_tts_generate(result, st.session_state['tts_voice'], "System"))[0]
389
  play_and_download_audio(audio_file)
390
  papers = parse_arxiv_refs(refs)
391
- if papers and useArxivAudio: await create_paper_audio_files(papers, q)
 
392
  return result, papers
393
  return result, []
394
 
@@ -435,7 +457,7 @@ async def save_pasted_image(image, username):
435
  return None
436
  timestamp = format_timestamp_prefix(username)
437
  filename = f"{timestamp}-{img_hash}.png"
438
- filepath = filename # Save at root
439
  image.save(filepath, "PNG")
440
  st.session_state.image_hashes.add(img_hash)
441
  return filepath
@@ -443,10 +465,12 @@ async def save_pasted_image(image, username):
443
  # ๐Ÿ“ฆ Zip Files
444
  def create_zip_of_files(md_files, mp3_files, png_files, mp4_files, query):
445
  all_files = md_files + mp3_files + png_files + mp4_files
446
- if not all_files: return None
 
447
  terms = get_high_info_terms(" ".join([open(f, 'r', encoding='utf-8').read() if f.endswith('.md') else os.path.splitext(os.path.basename(f))[0].replace('_', ' ') for f in all_files] + [query]), 5)
448
  zip_name = f"{format_timestamp_prefix()}_{'-'.join(terms)[:20]}.zip"
449
- with zipfile.ZipFile(zip_name, 'w') as z: [z.write(f) for f in all_files]
 
450
  return zip_name
451
 
452
  # ๐ŸŽฎ Main Interface
@@ -498,7 +522,7 @@ async def async_interface():
498
  st.session_state.timer_start = time.time()
499
  save_username(st.session_state.username)
500
  st.rerun()
501
-
502
  message = st.text_input(f"Message as {st.session_state.username}", key="message_input")
503
  paste_result = paste_image_button("๐Ÿ“‹ Paste Image or Text", key="paste_button_msg")
504
  if paste_result.image_data is not None:
@@ -524,22 +548,28 @@ async def async_interface():
524
 
525
  st.subheader("๐ŸŽค Speech-to-Chat")
526
  from mycomponent import speech_component
527
- transcript_data = speech_component(default_value=st.session_state.get('last_transcript', ''))
528
  if transcript_data and 'value' in transcript_data:
529
  transcript = transcript_data['value'].strip()
530
- st.write(f"๐ŸŽ™๏ธ You said: {transcript}")
531
- if transcript and transcript != st.session_state.last_transcript:
532
- st.session_state.last_transcript = transcript
533
- if st.session_state.autosend:
534
- await save_chat_entry(st.session_state.username, transcript, True)
535
- st.session_state.timer_start = time.time()
536
- save_username(st.session_state.username)
537
- st.rerun()
538
- elif st.button("Send to Chat"):
539
- await save_chat_entry(st.session_state.username, transcript, True)
540
- st.session_state.timer_start = time.time()
541
- save_username(st.session_state.username)
542
- st.rerun()
 
 
 
 
 
 
543
 
544
  # ๐Ÿ“ธ Media
545
  elif tab_main == "๐Ÿ“ธ Media":
@@ -550,12 +580,10 @@ async def async_interface():
550
  png_files = [f for f in all_files if f.endswith('.png')]
551
  mp4_files = [f for f in all_files if f.endswith('.mp4')]
552
 
553
- # Display concatenated Markdown
554
  st.subheader("All Submitted Text")
555
  all_md_content = concatenate_markdown_files()
556
  st.markdown(all_md_content)
557
 
558
- # Display Media by Type at End
559
  st.subheader("๐ŸŽต Audio (MP3)")
560
  for mp3 in mp3_files:
561
  with st.expander(os.path.basename(mp3)):
@@ -577,7 +605,7 @@ async def async_interface():
577
  uploaded_file = st.file_uploader("Upload Media", type=['png', 'mp4', 'mp3'])
578
  if uploaded_file:
579
  filename = f"{format_timestamp_prefix(st.session_state.username)}-{hashlib.md5(uploaded_file.getbuffer()).hexdigest()[:8]}.{uploaded_file.name.split('.')[-1]}"
580
- with open(filename, 'wb') as f: # Save at root
581
  f.write(uploaded_file.getbuffer())
582
  await save_chat_entry(st.session_state.username, f"Uploaded: {filename}")
583
  st.session_state.timer_start = time.time()
@@ -595,7 +623,8 @@ async def async_interface():
595
  with st.expander(f"{i}. ๐Ÿ“„ {p['title']}"):
596
  st.markdown(f"**{p['date']} | {p['title']}** โ€” [Link]({p['url']})")
597
  st.markdown(generate_5min_feature_markdown(p))
598
- if p.get('full_audio'): play_and_download_audio(p['full_audio'])
 
599
 
600
  # ๐Ÿ“š PDF to Audio
601
  elif tab_main == "๐Ÿ“š PDF to Audio":
@@ -608,7 +637,8 @@ async def async_interface():
608
  for i, text in enumerate(texts):
609
  with st.expander(f"Page {i+1}"):
610
  st.markdown(text)
611
- while i not in audios: time.sleep(0.1)
 
612
  if audios.get(i):
613
  st.audio(audios[i])
614
  st.markdown(get_download_link(audios[i], "mp3"), unsafe_allow_html=True)
@@ -662,7 +692,8 @@ async def async_interface():
662
  st.sidebar.write(f"{FILE_EMOJIS.get(f.split('.')[-1], '๐Ÿ“„')} {os.path.basename(f)}")
663
  if st.sidebar.button("โฌ‡๏ธ Zip All"):
664
  zip_name = create_zip_of_files(md_files, mp3_files, png_files, mp4_files, "latest_query")
665
- if zip_name: st.sidebar.markdown(get_download_link(zip_name, "zip"), unsafe_allow_html=True)
 
666
 
667
  def main():
668
  asyncio.run(async_interface())
 
77
 
78
  CHAT_DIR = "chat_logs"
79
  VOTE_DIR = "vote_logs"
80
+ MEDIA_DIR = "."
81
  AUDIO_CACHE_DIR = "audio_cache"
82
  AUDIO_DIR = "audio_logs"
83
  STATE_FILE = "user_state.txt"
 
101
 
102
  # ๐Ÿ“ˆ Performance Timer
103
  class PerformanceTimer:
104
+ def __init__(self, name):
105
+ self.name, self.start = name, None
106
+ def __enter__(self):
107
  self.start = time.time()
108
  return self
109
+ def __exit__(self, *args):
110
  duration = time.time() - self.start
111
  st.session_state['operation_timings'][self.name] = duration
112
  st.session_state['performance_metrics'][self.name].append(duration)
 
129
  'quote_source': "famous"
130
  }
131
  for k, v in defaults.items():
132
+ if k not in st.session_state:
133
+ st.session_state[k] = v
134
 
135
  # ๐Ÿ–Œ๏ธ Marquee Helpers
136
  def update_marquee_settings_ui():
 
149
  st.write("")
150
 
151
  # ๐Ÿ“ Text & File Helpers
152
+ def clean_text_for_tts(text):
153
+ return re.sub(r'[#*!\[\]]+', '', ' '.join(text.split()))[:200] or "No text"
154
+
155
+ def clean_text_for_filename(text):
156
+ return '_'.join(re.sub(r'[^\w\s-]', '', text.lower()).split())[:200]
157
+
158
  def get_high_info_terms(text, top_n=10):
159
  stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with'}
160
  words = re.findall(r'\b\w+(?:-\w+)*\b', text.lower())
 
169
 
170
  def create_file(prompt, username, file_type="md"):
171
  filename = generate_filename(prompt, username, file_type)
172
+ with open(filename, 'w', encoding='utf-8') as f:
173
+ f.write(prompt)
174
  return filename
175
 
176
  def get_download_link(file, file_type="mp3"):
 
209
  # ๐ŸŽถ Audio Processing
210
  async def async_edge_tts_generate(text, voice, username, rate=0, pitch=0, file_format="mp3"):
211
  cache_key = f"{text[:100]}_{voice}_{rate}_{pitch}_{file_format}"
212
+ if cache_key in st.session_state['audio_cache']:
213
+ return st.session_state['audio_cache'][cache_key], 0
214
  start_time = time.time()
215
  text = clean_text_for_tts(text)
216
+ if not text:
217
+ return None, 0
218
  filename = f"{format_timestamp_prefix(username)}-{hashlib.md5(text.encode()).hexdigest()[:8]}.{file_format}"
219
  communicate = edge_tts.Communicate(text, voice, rate=f"{rate:+d}%", pitch=f"{pitch:+d}Hz")
220
  await communicate.save(filename)
 
237
  central = pytz.timezone('US/Central')
238
  timestamp = datetime.now(central).strftime("%Y-%m-%d %H:%M:%S")
239
  entry = f"[{timestamp}] {username}: {message}" if not is_markdown else f"[{timestamp}] {username}:\n```markdown\n{message}\n```"
240
+ with open(CHAT_FILE, 'a') as f:
241
+ f.write(f"{entry}\n")
242
  voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
243
  audio_file, _ = await async_edge_tts_generate(message, voice, username)
244
  if audio_file:
245
+ with open(HISTORY_FILE, 'a') as f:
246
+ f.write(f"[{timestamp}] {username}: Audio - {audio_file}\n")
247
  st.session_state['mp3_files'][os.path.basename(audio_file)] = audio_file
248
  await broadcast_message(f"{username}|{message}", "chat")
249
  st.session_state.last_chat_update = time.time()
 
252
 
253
  async def load_chat():
254
  if not os.path.exists(CHAT_FILE):
255
+ with open(CHAT_FILE, 'a') as f:
256
+ f.write(f"# {START_ROOM} Chat\n\nWelcome to the cosmic hub! ๐ŸŽค\n")
257
+ with open(CHAT_FILE, 'r') as f:
258
  content = f.read().strip()
259
  lines = content.split('\n')
260
  numbered_content = "\n".join(f"{i+1}. {line}" for i, line in enumerate(lines) if line.strip())
 
310
  self.metadata = json.load(open(f"{self.cache_dir}/metadata.json")) if os.path.exists(f"{self.cache_dir}/metadata.json") else {}
311
 
312
  def _save_metadata(self):
313
+ with open(f"{self.cache_dir}/metadata.json", 'w') as f:
314
+ json.dump(self.metadata, f)
315
 
316
  async def create_audio(self, text, voice='en-US-AriaNeural'):
317
  cache_key = hashlib.md5(f"{text}:{voice}".encode()).hexdigest()
 
319
  if cache_key in self.metadata and os.path.exists(cache_path):
320
  return cache_path
321
  text = clean_text_for_tts(text)
322
+ if not text:
323
+ return None
324
  communicate = edge_tts.Communicate(text, voice)
325
  await communicate.save(cache_path)
326
  self.metadata[cache_key] = {'timestamp': datetime.now().isoformat(), 'text_length': len(text), 'voice': voice}
 
331
  reader = PdfReader(pdf_file)
332
  total_pages = min(len(reader.pages), max_pages)
333
  texts, audios = [], {}
334
+ async def process_page(i, text):
335
  audio_path = await audio_processor.create_audio(text, voice)
336
  if audio_path:
337
  audios[i] = audio_path
 
343
 
344
  # ๐Ÿ” ArXiv & AI Lookup
345
  def parse_arxiv_refs(ref_text):
346
+ if not ref_text:
347
+ return []
348
  papers = []
349
  current = {}
350
  for line in ref_text.split('\n'):
351
  if line.count('|') == 2:
352
+ if current:
353
+ papers.append(current)
354
  date, title, *_ = line.strip('* ').split('|')
355
  url = re.search(r'(https://arxiv.org/\S+)', line).group(1) if re.search(r'(https://arxiv.org/\S+)', line) else f"paper_{len(papers)}"
356
  current = {'date': date, 'title': title, 'url': url, 'authors': '', 'summary': '', 'full_audio': None, 'download_base64': ''}
357
  elif current:
358
+ if not current['authors']:
359
+ current['authors'] = line.strip('* ')
360
+ else:
361
+ current['summary'] += ' ' + line.strip() if current['summary'] else line.strip()
362
+ if current:
363
+ papers.append(current)
364
  return papers[:20]
365
 
366
  def generate_5min_feature_markdown(paper):
 
382
  ---
383
  """
384
 
385
+ def create_detailed_paper_md(papers):
386
+ return "# Detailed Summary\n" + "\n".join(generate_5min_feature_markdown(p) for p in papers)
387
 
388
  async def create_paper_audio_files(papers, query):
389
  for p in papers:
390
  audio_text = clean_text_for_tts(f"{p['title']} by {p['authors']}. {p['summary']}")
391
  p['full_audio'], _ = await async_edge_tts_generate(audio_text, st.session_state['tts_voice'], p['authors'])
392
+ if p['full_audio']:
393
+ p['download_base64'] = get_download_link(p['full_audio'])
394
 
395
  async def perform_ai_lookup(q, useArxiv=True, useArxivAudio=False):
396
  client = anthropic.Anthropic(api_key=anthropic_key)
 
409
  md_file, audio_file = create_file(result, "System", "md"), (await async_edge_tts_generate(result, st.session_state['tts_voice'], "System"))[0]
410
  play_and_download_audio(audio_file)
411
  papers = parse_arxiv_refs(refs)
412
+ if papers and useArxivAudio:
413
+ await create_paper_audio_files(papers, q)
414
  return result, papers
415
  return result, []
416
 
 
457
  return None
458
  timestamp = format_timestamp_prefix(username)
459
  filename = f"{timestamp}-{img_hash}.png"
460
+ filepath = filename
461
  image.save(filepath, "PNG")
462
  st.session_state.image_hashes.add(img_hash)
463
  return filepath
 
465
  # ๐Ÿ“ฆ Zip Files
466
  def create_zip_of_files(md_files, mp3_files, png_files, mp4_files, query):
467
  all_files = md_files + mp3_files + png_files + mp4_files
468
+ if not all_files:
469
+ return None
470
  terms = get_high_info_terms(" ".join([open(f, 'r', encoding='utf-8').read() if f.endswith('.md') else os.path.splitext(os.path.basename(f))[0].replace('_', ' ') for f in all_files] + [query]), 5)
471
  zip_name = f"{format_timestamp_prefix()}_{'-'.join(terms)[:20]}.zip"
472
+ with zipfile.ZipFile(zip_name, 'w') as z:
473
+ [z.write(f) for f in all_files]
474
  return zip_name
475
 
476
  # ๐ŸŽฎ Main Interface
 
522
  st.session_state.timer_start = time.time()
523
  save_username(st.session_state.username)
524
  st.rerun()
525
+
526
  message = st.text_input(f"Message as {st.session_state.username}", key="message_input")
527
  paste_result = paste_image_button("๐Ÿ“‹ Paste Image or Text", key="paste_button_msg")
528
  if paste_result.image_data is not None:
 
548
 
549
  st.subheader("๐ŸŽค Speech-to-Chat")
550
  from mycomponent import speech_component
551
+ transcript_data = speech_component(default_value=st.session_state.get('last_transcript', ''), key="speech_input")
552
  if transcript_data and 'value' in transcript_data:
553
  transcript = transcript_data['value'].strip()
554
+ if transcript:
555
+ st.write(f"๐ŸŽ™๏ธ You said: {transcript}")
556
+ if transcript != st.session_state.last_transcript:
557
+ st.session_state.last_transcript = transcript
558
+ if st.session_state.autosend:
559
+ await save_chat_entry(st.session_state.username, transcript, True)
560
+ st.session_state.timer_start = time.time()
561
+ save_username(st.session_state.username)
562
+ # Update chat display without full rerun to reduce flicker
563
+ with chat_container:
564
+ st.markdown(await load_chat())
565
+ else:
566
+ if st.button("Send to Chat", key="send_transcript"):
567
+ await save_chat_entry(st.session_state.username, transcript, True)
568
+ st.session_state.timer_start = time.time()
569
+ save_username(st.session_state.username)
570
+ st.rerun()
571
+ else:
572
+ st.write("๐ŸŽ™๏ธ Speak to transcribe your message...")
573
 
574
  # ๐Ÿ“ธ Media
575
  elif tab_main == "๐Ÿ“ธ Media":
 
580
  png_files = [f for f in all_files if f.endswith('.png')]
581
  mp4_files = [f for f in all_files if f.endswith('.mp4')]
582
 
 
583
  st.subheader("All Submitted Text")
584
  all_md_content = concatenate_markdown_files()
585
  st.markdown(all_md_content)
586
 
 
587
  st.subheader("๐ŸŽต Audio (MP3)")
588
  for mp3 in mp3_files:
589
  with st.expander(os.path.basename(mp3)):
 
605
  uploaded_file = st.file_uploader("Upload Media", type=['png', 'mp4', 'mp3'])
606
  if uploaded_file:
607
  filename = f"{format_timestamp_prefix(st.session_state.username)}-{hashlib.md5(uploaded_file.getbuffer()).hexdigest()[:8]}.{uploaded_file.name.split('.')[-1]}"
608
+ with open(filename, 'wb') as f:
609
  f.write(uploaded_file.getbuffer())
610
  await save_chat_entry(st.session_state.username, f"Uploaded: {filename}")
611
  st.session_state.timer_start = time.time()
 
623
  with st.expander(f"{i}. ๐Ÿ“„ {p['title']}"):
624
  st.markdown(f"**{p['date']} | {p['title']}** โ€” [Link]({p['url']})")
625
  st.markdown(generate_5min_feature_markdown(p))
626
+ if p.get('full_audio'):
627
+ play_and_download_audio(p['full_audio'])
628
 
629
  # ๐Ÿ“š PDF to Audio
630
  elif tab_main == "๐Ÿ“š PDF to Audio":
 
637
  for i, text in enumerate(texts):
638
  with st.expander(f"Page {i+1}"):
639
  st.markdown(text)
640
+ while i not in audios:
641
+ time.sleep(0.1)
642
  if audios.get(i):
643
  st.audio(audios[i])
644
  st.markdown(get_download_link(audios[i], "mp3"), unsafe_allow_html=True)
 
692
  st.sidebar.write(f"{FILE_EMOJIS.get(f.split('.')[-1], '๐Ÿ“„')} {os.path.basename(f)}")
693
  if st.sidebar.button("โฌ‡๏ธ Zip All"):
694
  zip_name = create_zip_of_files(md_files, mp3_files, png_files, mp4_files, "latest_query")
695
+ if zip_name:
696
+ st.sidebar.markdown(get_download_link(zip_name, "zip"), unsafe_allow_html=True)
697
 
698
  def main():
699
  asyncio.run(async_interface())