Update app.py
Browse files
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 = "."
|
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):
|
105 |
-
|
|
|
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:
|
|
|
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):
|
151 |
-
|
|
|
|
|
|
|
|
|
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:
|
|
|
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']:
|
|
|
206 |
start_time = time.time()
|
207 |
text = clean_text_for_tts(text)
|
208 |
-
if not text:
|
|
|
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:
|
|
|
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:
|
|
|
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:
|
245 |
-
|
|
|
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:
|
|
|
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:
|
|
|
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:
|
|
|
333 |
papers = []
|
334 |
current = {}
|
335 |
for line in ref_text.split('\n'):
|
336 |
if line.count('|') == 2:
|
337 |
-
if 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']:
|
343 |
-
|
344 |
-
|
|
|
|
|
|
|
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):
|
|
|
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']:
|
|
|
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:
|
|
|
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
|
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:
|
|
|
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:
|
|
|
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 |
-
|
531 |
-
|
532 |
-
st.session_state.last_transcript
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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:
|
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'):
|
|
|
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:
|
|
|
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:
|
|
|
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())
|