awacke1 commited on
Commit
564fd41
Β·
verified Β·
1 Parent(s): 1bd5fe7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -192
app.py CHANGED
@@ -17,8 +17,6 @@ import streamlit.components.v1 as components
17
  import edge_tts
18
  from audio_recorder_streamlit import audio_recorder
19
  import nest_asyncio
20
- import re
21
- from streamlit_paste_button import paste_image_button # Using this for image pasting
22
 
23
  # Patch for nested async - sneaky fix! 🐍✨
24
  nest_asyncio.apply()
@@ -35,7 +33,7 @@ st.set_page_config(
35
  initial_sidebar_state="auto"
36
  )
37
 
38
- # Funky usernames - who’s who in the zoo with unique voices! πŸŽ­πŸΎπŸŽ™οΈ
39
  FUN_USERNAMES = {
40
  "CosmicJester 🌌": "en-US-AriaNeural",
41
  "PixelPanda 🐼": "en-US-JennyNeural",
@@ -46,17 +44,17 @@ FUN_USERNAMES = {
46
  "ByteBuster πŸ’Ύ": "en-GB-RyanNeural",
47
  "GalacticGopher 🌍": "en-AU-WilliamNeural",
48
  "RocketRaccoon πŸš€": "en-CA-LiamNeural",
49
- "EchoElf 🧝": "en-US-AnaNeural",
50
- "PhantomFox 🦊": "en-US-BrandonNeural",
51
- "WittyWizard πŸ§™": "en-GB-ThomasNeural",
52
- "LunarLlama πŸŒ™": "en-AU-FreyaNeural",
53
- "SolarSloth β˜€οΈ": "en-CA-LindaNeural",
54
- "AstroAlpaca πŸ¦™": "en-US-ChristopherNeural",
55
- "CyberCoyote 🐺": "en-GB-ElliotNeural",
56
- "MysticMoose 🦌": "en-AU-JamesNeural",
57
- "GlitchGnome 🧚": "en-CA-EthanNeural",
58
- "VortexViper 🐍": "en-US-AmberNeural",
59
- "ChronoChimp πŸ’": "en-GB-LibbyNeural"
60
  }
61
 
62
  # Folders galore - organizing chaos! πŸ“‚πŸŒ€
@@ -65,12 +63,10 @@ VOTE_DIR = "vote_logs"
65
  STATE_FILE = "user_state.txt"
66
  AUDIO_DIR = "audio_logs"
67
  HISTORY_DIR = "history_logs"
68
- MEDIA_DIR = "media_files"
69
  os.makedirs(CHAT_DIR, exist_ok=True)
70
  os.makedirs(VOTE_DIR, exist_ok=True)
71
  os.makedirs(AUDIO_DIR, exist_ok=True)
72
  os.makedirs(HISTORY_DIR, exist_ok=True)
73
- os.makedirs(MEDIA_DIR, exist_ok=True)
74
 
75
  CHAT_FILE = os.path.join(CHAT_DIR, "global_chat.md")
76
  QUOTE_VOTES_FILE = os.path.join(VOTE_DIR, "quote_votes.md")
@@ -110,26 +106,6 @@ if 'server_task' not in st.session_state:
110
  st.session_state.server_task = None
111
  if 'active_connections' not in st.session_state:
112
  st.session_state.active_connections = {}
113
- if 'media_notifications' not in st.session_state:
114
- st.session_state.media_notifications = []
115
- if 'last_chat_update' not in st.session_state:
116
- st.session_state.last_chat_update = 0
117
- if 'displayed_chat_lines' not in st.session_state:
118
- st.session_state.displayed_chat_lines = []
119
- if 'old_val' not in st.session_state:
120
- st.session_state.old_val = ""
121
- if 'last_query' not in st.session_state:
122
- st.session_state.last_query = ""
123
- if 'message_text' not in st.session_state:
124
- st.session_state.message_text = ""
125
- if 'audio_cache' not in st.session_state:
126
- st.session_state.audio_cache = {}
127
- if 'pasted_image_data' not in st.session_state:
128
- st.session_state.pasted_image_data = None
129
- if 'quote_line' not in st.session_state:
130
- st.session_state.quote_line = None
131
- if 'refresh_rate' not in st.session_state:
132
- st.session_state.refresh_rate = 5 # Default refresh rate
133
 
134
  # Timestamp wizardry - clock ticks with flair! ⏰🎩
135
  def format_timestamp_prefix():
@@ -158,12 +134,6 @@ def log_action(username, action):
158
  f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
159
  user_log[action] = current_time
160
 
161
- # Clean text - strip the fancy stuff! πŸ§ΉπŸ“
162
- def clean_text_for_tts(text):
163
- cleaned = re.sub(r'[#*!\[\]]+', '', text)
164
- cleaned = ' '.join(cleaned.split())
165
- return cleaned[:200] if cleaned else "No text to speak"
166
-
167
  # Chat saver - words locked tight! πŸ’¬πŸ”’
168
  async def save_chat_entry(username, message):
169
  await asyncio.to_thread(log_action, username, "πŸ’¬πŸ”’ - Chat saver - words locked tight!")
@@ -171,13 +141,10 @@ async def save_chat_entry(username, message):
171
  entry = f"[{timestamp}] {username}: {message}"
172
  await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"{entry}\n"))
173
  voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
174
- cleaned_message = clean_text_for_tts(message)
175
- audio_file = await async_edge_tts_generate(cleaned_message, voice)
176
  if audio_file:
177
  with open(HISTORY_FILE, 'a') as f:
178
  f.write(f"[{timestamp}] {username}: Audio generated - {audio_file}\n")
179
- await broadcast_message(f"{username}|{message}", "chat")
180
- st.session_state.last_chat_update = time.time()
181
 
182
  # Chat loader - history unleashed! πŸ“œπŸš€
183
  async def load_chat():
@@ -204,7 +171,7 @@ async def get_user_list(chat_content):
204
  async def has_joined_before(client_id, chat_content):
205
  username = st.session_state.get('username', 'System 🌟')
206
  await asyncio.to_thread(log_action, username, "πŸšͺπŸ” - Join checker - been here before?")
207
- return any(f"Client-{client_id}" in line for line in chat_content.split('\n'))
208
 
209
  # Suggestion maker - old quips resurface! πŸ’‘πŸ“
210
  async def get_message_suggestions(chat_content, prefix):
@@ -234,7 +201,7 @@ async def load_votes(file):
234
  await asyncio.to_thread(lambda: open(file, 'w').write("# Vote Tally\n\nNo votes yet - get clicking! πŸ–±οΈ\n"))
235
  with open(file, 'r') as f:
236
  content = await asyncio.to_thread(f.read)
237
- lines = content.strip().split('\n')[2:]
238
  votes = {}
239
  user_votes = set()
240
  for line in lines:
@@ -262,13 +229,8 @@ async def async_edge_tts_generate(text, voice, rate=0, pitch=0, file_format="mp3
262
  timestamp = format_timestamp_prefix()
263
  filename = os.path.join(AUDIO_DIR, f"audio_{timestamp}_{random.randint(1000, 9999)}.mp3")
264
  communicate = edge_tts.Communicate(text, voice, rate=f"{rate:+d}%", pitch=f"{pitch:+d}Hz")
265
- try:
266
- await communicate.save(filename)
267
- return filename if os.path.exists(filename) else None
268
- except edge_tts.exceptions.NoAudioReceived:
269
- with open(HISTORY_FILE, 'a') as f:
270
- f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: Audio failed - No audio received for '{text}'\n")
271
- return None
272
 
273
  # Audio player - tunes blast off! πŸ”ŠπŸš€
274
  def play_and_download_audio(file_path):
@@ -279,24 +241,26 @@ def play_and_download_audio(file_path):
279
  dl_link = f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(file_path)}">🎡 Download {os.path.basename(file_path)}</a>'
280
  st.markdown(dl_link, unsafe_allow_html=True)
281
 
282
- # Image saver - pics preserved with naming! πŸ“ΈπŸ’Ύ
283
- async def save_pasted_image(image, username):
 
284
  await asyncio.to_thread(log_action, username, "πŸ“ΈπŸ’Ύ - Image saver - pics preserved!")
285
  timestamp = format_timestamp_prefix()
286
- filename = f"paste_{timestamp}_{username}.png"
287
- filepath = os.path.join(MEDIA_DIR, filename)
288
- # Directly save the PIL Image object without base64 conversion
289
- await asyncio.to_thread(image.save, filepath, "PNG")
290
- return filepath
291
-
292
- # Video renderer - movies roll with autoplay! πŸŽ₯🎬
 
 
 
293
  async def get_video_html(video_path, width="100%"):
294
  username = st.session_state.get('username', 'System 🌟')
295
  await asyncio.to_thread(log_action, username, "πŸŽ₯🎬 - Video renderer - movies roll!")
296
- with open(video_path, 'rb') as f:
297
- video_data = await asyncio.to_thread(f.read)
298
- video_url = f"data:video/mp4;base64,{base64.b64encode(video_data).decode()}"
299
- return f'<video width="{width}" controls autoplay><source src="{video_url}" type="video/mp4">Your browser does not support the video tag.</video>'
300
 
301
  # Audio renderer - sounds soar! 🎢✈️
302
  async def get_audio_html(audio_path, width="100%"):
@@ -322,6 +286,7 @@ async def websocket_handler(websocket, path):
322
  if len(parts) == 2:
323
  username, content = parts
324
  await save_chat_entry(username, content)
 
325
  except websockets.ConnectionClosed:
326
  pass
327
  finally:
@@ -356,23 +321,12 @@ async def process_voice_input(audio_bytes):
356
  username = st.session_state.get('username', 'System 🌟')
357
  await asyncio.to_thread(log_action, username, "πŸŽ€πŸ“ - Voice processor - speech to text!")
358
  if audio_bytes:
359
- text = "Voice input simulation"
360
  await save_chat_entry(username, text)
361
 
362
- # Dummy AI lookup function (replace with actual implementation)
363
- async def perform_ai_lookup(query, vocal_summary=True, extended_refs=False, titles_summary=True, full_audio=False, useArxiv=True, useArxivAudio=False):
364
- username = st.session_state.get('username', 'System 🌟')
365
- result = f"AI Lookup Result for '{query}' (Arxiv: {useArxiv}, Audio: {useArxivAudio})"
366
- await save_chat_entry(username, result)
367
- if useArxivAudio:
368
- audio_file = await async_edge_tts_generate(result, FUN_USERNAMES.get(username, "en-US-AriaNeural"))
369
- if audio_file:
370
- st.audio(audio_file)
371
-
372
- # Main execution - let’s roll! πŸŽ²πŸš€
373
- def main():
374
- NODE_NAME, port = get_node_name()
375
-
376
  loop = asyncio.new_event_loop()
377
  asyncio.set_event_loop(loop)
378
 
@@ -382,151 +336,187 @@ def main():
382
  available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
383
  st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
384
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  st.title(f"πŸ€–πŸ§ MMO {st.session_state.username}πŸ“πŸ”¬")
386
  st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! πŸŽ‰")
387
 
 
388
  if not st.session_state.server_task:
389
  st.session_state.server_task = loop.create_task(run_websocket_server())
390
 
 
391
  audio_bytes = audio_recorder()
392
  if audio_bytes:
393
  await process_voice_input(audio_bytes)
394
  st.rerun()
395
 
396
- # Load and display chat
397
  st.subheader(f"{START_ROOM} Chat πŸ’¬")
398
  chat_content = await load_chat()
399
  chat_lines = chat_content.split('\n')
400
  chat_votes = await load_votes(QUOTE_VOTES_FILE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
 
402
- current_time = time.time()
403
- if current_time - st.session_state.last_chat_update > 1 or not st.session_state.displayed_chat_lines:
404
- new_lines = [line for line in chat_lines if line.strip() and ': ' in line and line not in st.session_state.displayed_chat_lines and not line.startswith('#')]
405
- st.session_state.displayed_chat_lines.extend(new_lines)
406
- st.session_state.last_chat_update = current_time
407
-
408
- for i, line in enumerate(st.session_state.displayed_chat_lines):
409
- col1, col2, col3, col4 = st.columns([3, 1, 1, 2])
410
- with col1:
411
- st.markdown(line)
412
- if "Pasted image:" in line or "Uploaded media:" in line:
413
- file_path = line.split(': ')[-1].strip()
414
- if os.path.exists(file_path):
415
- if file_path.endswith(('.png', '.jpg')):
416
- st.image(file_path, use_container_width=True)
417
- elif file_path.endswith('.mp4'):
418
- st.markdown(await get_video_html(file_path), unsafe_allow_html=True)
419
- elif file_path.endswith('.mp3'):
420
- st.markdown(await get_audio_html(file_path), unsafe_allow_html=True)
421
- with col2:
422
- vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
423
- if st.button(f"πŸ‘ {vote_count}", key=f"chat_vote_{i}"):
424
- comment = st.session_state.message_text
425
- await save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, await generate_user_hash(), st.session_state.username, comment)
426
- st.session_state.message_text = ''
427
- st.rerun()
428
- with col3:
429
- if st.button("πŸ“’ Quote", key=f"quote_{i}"):
430
- st.session_state.quote_line = line
431
- st.rerun()
432
- with col4:
433
- username = line.split(': ')[1].split(' ')[0]
434
- cache_key = f"{line}_{FUN_USERNAMES.get(username, 'en-US-AriaNeural')}"
435
- if cache_key not in st.session_state.audio_cache:
436
- cleaned_text = clean_text_for_tts(line.split(': ', 1)[1])
437
- audio_file = await async_edge_tts_generate(cleaned_text, FUN_USERNAMES.get(username, "en-US-AriaNeural"))
438
- st.session_state.audio_cache[cache_key] = audio_file
439
- audio_file = st.session_state.audio_cache.get(cache_key)
440
- if audio_file:
441
- play_and_download_audio(audio_file)
442
-
443
- if st.session_state.quote_line:
444
  st.markdown(f"### Quoting: {st.session_state.quote_line}")
445
  quote_response = st.text_area("Add your response", key="quote_response")
446
- # Add paste button within quote response
447
- paste_result_quote = paste_image_button("πŸ“‹ Paste Image with Quote", key="paste_button_quote")
448
- if paste_result_quote.image_data is not None:
449
- st.image(paste_result_quote.image_data, caption="Received Image for Quote")
450
- filename = await save_pasted_image(paste_result_quote.image_data, st.session_state.username)
451
- if filename:
452
- st.session_state.pasted_image_data = filename # Store for use in quote submission
453
  if st.button("Send Quote πŸš€", key="send_quote"):
454
- markdown_response = f"### Quote Response\n- **Original**: {st.session_state.quote_line}\n- **{st.session_state.username} Replies**: {quote_response}"
455
- if st.session_state.pasted_image_data:
456
- markdown_response += f"\n- **Image**: ![Pasted Image]({st.session_state.pasted_image_data})"
457
- await save_chat_entry(st.session_state.username, f"Pasted image: {st.session_state.pasted_image_data}")
458
- st.session_state.pasted_image_data = None
459
- await save_chat_entry(st.session_state.username, markdown_response)
460
- st.session_state.quote_line = None
 
 
 
 
461
  st.session_state.message_text = ''
462
  st.rerun()
463
 
 
464
  new_username = st.selectbox("Change Name", [""] + list(FUN_USERNAMES.keys()), index=0)
465
  if new_username and new_username != st.session_state.username:
466
- await save_chat_entry("System 🌟", f"{st.session_state.username} changed name to {new_username}")
467
  st.session_state.username = new_username
468
  st.rerun()
469
 
 
470
  message = st.text_input(f"Message as {st.session_state.username}", key="message_input", value=st.session_state.message_text, on_change=lambda: st.session_state.update(message_text=st.session_state.message_input))
471
- # Add paste button next to message input
472
- paste_result_msg = paste_image_button("πŸ“‹ Paste Image with Message", key="paste_button_msg")
473
- if paste_result_msg.image_data is not None:
474
- st.image(paste_result_msg.image_data, caption="Received Image for Message")
475
- filename = await save_pasted_image(paste_result_msg.image_data, st.session_state.username)
476
- if filename:
477
- st.session_state.pasted_image_data = filename # Store for use in message submission
478
- if st.button("Send πŸš€", key="send_button") and (message.strip() or st.session_state.pasted_image_data):
479
- if message.strip():
480
- await save_chat_entry(st.session_state.username, message)
481
  if st.session_state.pasted_image_data:
482
- await save_chat_entry(st.session_state.username, f"Pasted image: {st.session_state.pasted_image_data}")
483
- st.session_state.pasted_image_data = None
 
 
484
  st.session_state.message_text = ''
485
  st.rerun()
486
 
487
- # Main action tabs and model use choices
488
- tab_main = st.radio("Action:", ["🎀 Voice", "πŸ“Έ Media", "πŸ” ArXiv", "πŸ“ Editor"], horizontal=True)
489
- useArxiv = st.checkbox("Search Arxiv for Research Paper Answers", value=True)
490
- useArxivAudio = st.checkbox("Generate Audio File for Research Paper Answers", value=False)
491
-
492
- # Enhanced Media Gallery with Image, Audio, Video
493
- st.subheader("Upload Media 🎨🎢πŸŽ₯")
494
- uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp4', 'mp3'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  if uploaded_file:
496
- timestamp = format_timestamp_prefix()
497
- username = st.session_state.username
498
- ext = uploaded_file.name.split('.')[-1]
499
- filename = f"{timestamp}_{username}.{ext}"
500
- file_path = os.path.join(MEDIA_DIR, filename)
501
  await asyncio.to_thread(lambda: open(file_path, 'wb').write(uploaded_file.getbuffer()))
502
- st.success(f"Uploaded {filename}")
503
- await save_chat_entry(username, f"Uploaded media: {file_path}")
504
- if file_path.endswith('.mp4'):
505
- st.session_state.media_notifications.append(file_path)
506
 
507
- st.subheader("Media Gallery 🎨🎢πŸŽ₯")
508
- media_files = glob.glob(f"{MEDIA_DIR}/*.png") + glob.glob(f"{MEDIA_DIR}/*.jpg") + glob.glob(f"{MEDIA_DIR}/*.mp4") + glob.glob(f"{MEDIA_DIR}/*.mp3")
509
  if media_files:
510
- media_votes = await load_votes(MEDIA_VOTES_FILE)
511
- st.write("### All Media Uploads")
512
- for media_file in sorted(media_files, key=os.path.getmtime, reverse=True): # Sort by modification time, newest first
513
- filename = os.path.basename(media_file)
514
  vote_count = media_votes.get(media_file, 0)
515
-
516
- col1, col2 = st.columns([3, 1])
517
- with col1:
518
- st.markdown(f"**{filename}**")
519
- if media_file.endswith(('.png', '.jpg')):
520
- st.image(media_file, use_container_width=True)
521
- elif media_file.endswith('.mp4'):
522
- st.markdown(await get_video_html(media_file), unsafe_allow_html=True)
523
- elif media_file.endswith('.mp3'):
524
- st.markdown(await get_audio_html(media_file), unsafe_allow_html=True)
525
- with col2:
526
- if st.button(f"πŸ‘ {vote_count}", key=f"media_vote_{media_file}"):
527
- await save_vote(MEDIA_VOTES_FILE, media_file, await generate_user_hash(), st.session_state.username)
528
- st.rerun()
529
-
 
 
 
 
 
 
 
 
 
 
 
530
  st.subheader("Refresh ⏳")
531
  refresh_rate = st.slider("Refresh Rate", 1, 300, st.session_state.refresh_rate)
532
  st.session_state.refresh_rate = refresh_rate
@@ -535,15 +525,22 @@ def main():
535
  font_name, font_func = random.choice(UNICODE_FONTS)
536
  countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
537
  timer_placeholder.markdown(f"<p class='timer'>⏳ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
538
- time.sleep(1) # Use synchronous sleep
539
  st.rerun()
540
 
 
541
  st.sidebar.subheader("Chat History πŸ“œ")
542
  with open(HISTORY_FILE, 'r') as f:
543
  history_content = f.read()
544
  st.sidebar.markdown(history_content)
545
 
 
546
  loop.run_until_complete(async_interface())
547
 
 
 
 
 
 
548
  if __name__ == "__main__":
549
  main()
 
17
  import edge_tts
18
  from audio_recorder_streamlit import audio_recorder
19
  import nest_asyncio
 
 
20
 
21
  # Patch for nested async - sneaky fix! 🐍✨
22
  nest_asyncio.apply()
 
33
  initial_sidebar_state="auto"
34
  )
35
 
36
+ # Funky usernames - who’s who in the zoo! 🎭🐾
37
  FUN_USERNAMES = {
38
  "CosmicJester 🌌": "en-US-AriaNeural",
39
  "PixelPanda 🐼": "en-US-JennyNeural",
 
44
  "ByteBuster πŸ’Ύ": "en-GB-RyanNeural",
45
  "GalacticGopher 🌍": "en-AU-WilliamNeural",
46
  "RocketRaccoon πŸš€": "en-CA-LiamNeural",
47
+ "EchoElf 🧝": "en-US-AriaNeural",
48
+ "PhantomFox 🦊": "en-US-JennyNeural",
49
+ "WittyWizard πŸ§™": "en-GB-SoniaNeural",
50
+ "LunarLlama πŸŒ™": "en-AU-NatashaNeural",
51
+ "SolarSloth β˜€οΈ": "en-CA-ClaraNeural",
52
+ "AstroAlpaca πŸ¦™": "en-US-GuyNeural",
53
+ "CyberCoyote 🐺": "en-GB-RyanNeural",
54
+ "MysticMoose 🦌": "en-AU-WilliamNeural",
55
+ "GlitchGnome 🧚": "en-CA-LiamNeural",
56
+ "VortexViper 🐍": "en-US-AriaNeural",
57
+ "ChronoChimp πŸ’": "en-US-JennyNeural"
58
  }
59
 
60
  # Folders galore - organizing chaos! πŸ“‚πŸŒ€
 
63
  STATE_FILE = "user_state.txt"
64
  AUDIO_DIR = "audio_logs"
65
  HISTORY_DIR = "history_logs"
 
66
  os.makedirs(CHAT_DIR, exist_ok=True)
67
  os.makedirs(VOTE_DIR, exist_ok=True)
68
  os.makedirs(AUDIO_DIR, exist_ok=True)
69
  os.makedirs(HISTORY_DIR, exist_ok=True)
 
70
 
71
  CHAT_FILE = os.path.join(CHAT_DIR, "global_chat.md")
72
  QUOTE_VOTES_FILE = os.path.join(VOTE_DIR, "quote_votes.md")
 
106
  st.session_state.server_task = None
107
  if 'active_connections' not in st.session_state:
108
  st.session_state.active_connections = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
  # Timestamp wizardry - clock ticks with flair! ⏰🎩
111
  def format_timestamp_prefix():
 
134
  f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
135
  user_log[action] = current_time
136
 
 
 
 
 
 
 
137
  # Chat saver - words locked tight! πŸ’¬πŸ”’
138
  async def save_chat_entry(username, message):
139
  await asyncio.to_thread(log_action, username, "πŸ’¬πŸ”’ - Chat saver - words locked tight!")
 
141
  entry = f"[{timestamp}] {username}: {message}"
142
  await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"{entry}\n"))
143
  voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
144
+ audio_file = await async_edge_tts_generate(message, voice)
 
145
  if audio_file:
146
  with open(HISTORY_FILE, 'a') as f:
147
  f.write(f"[{timestamp}] {username}: Audio generated - {audio_file}\n")
 
 
148
 
149
  # Chat loader - history unleashed! πŸ“œπŸš€
150
  async def load_chat():
 
171
  async def has_joined_before(client_id, chat_content):
172
  username = st.session_state.get('username', 'System 🌟')
173
  await asyncio.to_thread(log_action, username, "πŸšͺπŸ” - Join checker - been here before?")
174
+ return any(f"Client-{client_id} has joined" in line for line in chat_content.split('\n'))
175
 
176
  # Suggestion maker - old quips resurface! πŸ’‘πŸ“
177
  async def get_message_suggestions(chat_content, prefix):
 
201
  await asyncio.to_thread(lambda: open(file, 'w').write("# Vote Tally\n\nNo votes yet - get clicking! πŸ–±οΈ\n"))
202
  with open(file, 'r') as f:
203
  content = await asyncio.to_thread(f.read)
204
+ lines = content.strip().split('\n')[2:] # Process content after reading
205
  votes = {}
206
  user_votes = set()
207
  for line in lines:
 
229
  timestamp = format_timestamp_prefix()
230
  filename = os.path.join(AUDIO_DIR, f"audio_{timestamp}_{random.randint(1000, 9999)}.mp3")
231
  communicate = edge_tts.Communicate(text, voice, rate=f"{rate:+d}%", pitch=f"{pitch:+d}Hz")
232
+ await communicate.save(filename)
233
+ return filename if os.path.exists(filename) else None
 
 
 
 
 
234
 
235
  # Audio player - tunes blast off! πŸ”ŠπŸš€
236
  def play_and_download_audio(file_path):
 
241
  dl_link = f'<a href="data:audio/mpeg;base64,{b64}" download="{os.path.basename(file_path)}">🎡 Download {os.path.basename(file_path)}</a>'
242
  st.markdown(dl_link, unsafe_allow_html=True)
243
 
244
+ # Image saver - pics preserved! πŸ“ΈπŸ’Ύ
245
+ async def save_pasted_image(image_data):
246
+ username = st.session_state.get('username', 'System 🌟')
247
  await asyncio.to_thread(log_action, username, "πŸ“ΈπŸ’Ύ - Image saver - pics preserved!")
248
  timestamp = format_timestamp_prefix()
249
+ filename = f"paste_{timestamp}.png"
250
+ filepath = os.path.join('./', filename)
251
+ if ',' in image_data:
252
+ image_data = image_data.split(',')[1]
253
+ img_bytes = base64.b64decode(image_data)
254
+ img = Image.open(io.BytesIO(img_bytes))
255
+ await asyncio.to_thread(img.save, filepath, "PNG")
256
+ return filename
257
+
258
+ # Video renderer - movies roll! πŸŽ₯🎬
259
  async def get_video_html(video_path, width="100%"):
260
  username = st.session_state.get('username', 'System 🌟')
261
  await asyncio.to_thread(log_action, username, "πŸŽ₯🎬 - Video renderer - movies roll!")
262
+ video_url = f"data:video/mp4;base64,{base64.b64encode(await asyncio.to_thread(open, video_path, 'rb').read()).decode()}"
263
+ return f'<video width="{width}" controls autoplay muted loop><source src="{video_url}" type="video/mp4">Your browser does not support the video tag.</video>'
 
 
264
 
265
  # Audio renderer - sounds soar! 🎢✈️
266
  async def get_audio_html(audio_path, width="100%"):
 
286
  if len(parts) == 2:
287
  username, content = parts
288
  await save_chat_entry(username, content)
289
+ await broadcast_message(f"{username}|{content}", room_id)
290
  except websockets.ConnectionClosed:
291
  pass
292
  finally:
 
321
  username = st.session_state.get('username', 'System 🌟')
322
  await asyncio.to_thread(log_action, username, "πŸŽ€πŸ“ - Voice processor - speech to text!")
323
  if audio_bytes:
324
+ text = "Voice input simulation" # Replace with actual speech-to-text logic
325
  await save_chat_entry(username, text)
326
 
327
+ # Interface builder - UI takes shape! πŸŽ¨πŸ–ŒοΈ
328
+ def create_streamlit_interface():
329
+ # Run async tasks within an event loop
 
 
 
 
 
 
 
 
 
 
 
330
  loop = asyncio.new_event_loop()
331
  asyncio.set_event_loop(loop)
332
 
 
336
  available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
337
  st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
338
 
339
+ if 'refresh_rate' not in st.session_state:
340
+ st.session_state.refresh_rate = 5
341
+ if 'timer_start' not in st.session_state:
342
+ st.session_state.timer_start = time.time()
343
+ if 'quote_line' not in st.session_state:
344
+ st.session_state.quote_line = None
345
+ if 'pasted_image_data' not in st.session_state:
346
+ st.session_state.pasted_image_data = None
347
+ if 'message_text' not in st.session_state:
348
+ st.session_state.message_text = ""
349
+ if 'audio_cache' not in st.session_state:
350
+ st.session_state.audio_cache = {}
351
+ if 'chat_history' not in st.session_state:
352
+ st.session_state.chat_history = []
353
+
354
+ st.markdown("""
355
+ <style>
356
+ .chat-box {font-family: monospace; background: #1e1e1e; color: #d4d4d4; padding: 10px; border-radius: 5px; height: 300px; overflow-y: auto;}
357
+ .timer {font-size: 24px; color: #ffcc00; text-align: center; animation: pulse 1s infinite;}
358
+ @keyframes pulse {0% {transform: scale(1);} 50% {transform: scale(1.1);} 100% {transform: scale(1);}}
359
+ #paste-target {border: 2px dashed #ccc; padding: 20px; text-align: center; cursor: pointer;}
360
+ </style>
361
+ """, unsafe_allow_html=True)
362
+
363
  st.title(f"πŸ€–πŸ§ MMO {st.session_state.username}πŸ“πŸ”¬")
364
  st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! πŸŽ‰")
365
 
366
+ # Start websocket server if not running
367
  if not st.session_state.server_task:
368
  st.session_state.server_task = loop.create_task(run_websocket_server())
369
 
370
+ # Voice Input
371
  audio_bytes = audio_recorder()
372
  if audio_bytes:
373
  await process_voice_input(audio_bytes)
374
  st.rerun()
375
 
376
+ # Chat Section
377
  st.subheader(f"{START_ROOM} Chat πŸ’¬")
378
  chat_content = await load_chat()
379
  chat_lines = chat_content.split('\n')
380
  chat_votes = await load_votes(QUOTE_VOTES_FILE)
381
+ for i, line in enumerate(chat_lines):
382
+ if line.strip() and ': ' in line:
383
+ col1, col2, col3 = st.columns([4, 1, 1])
384
+ with col1:
385
+ st.markdown(line)
386
+ username = line.split(': ')[1].split(' ')[0]
387
+ audio_file = None
388
+ cache_key = f"{line}_{FUN_USERNAMES.get(username, 'en-US-AriaNeural')}"
389
+ if cache_key in st.session_state.audio_cache:
390
+ audio_file = st.session_state.audio_cache[cache_key]
391
+ else:
392
+ audio_file = await async_edge_tts_generate(line.split(': ', 1)[1], FUN_USERNAMES.get(username, "en-US-AriaNeural"))
393
+ st.session_state.audio_cache[cache_key] = audio_file
394
+ if audio_file:
395
+ play_and_download_audio(audio_file)
396
+ with col2:
397
+ vote_count = chat_votes.get(line.split('. ')[1] if '. ' in line else line, 0)
398
+ if st.button(f"πŸ‘ {vote_count}", key=f"chat_vote_{i}"):
399
+ comment = st.session_state.message_text
400
+ await save_vote(QUOTE_VOTES_FILE, line.split('. ')[1] if '. ' in line else line, await generate_user_hash(), st.session_state.username, comment)
401
+ if st.session_state.pasted_image_data:
402
+ filename = await save_pasted_image(st.session_state.pasted_image_data)
403
+ if filename:
404
+ await save_chat_entry(st.session_state.username, f"Pasted image: {filename}")
405
+ st.session_state.pasted_image_data = None
406
+ st.session_state.message_text = ''
407
+ st.rerun()
408
+ with col3:
409
+ if st.button("πŸ“’ Quote", key=f"quote_{i}"):
410
+ st.session_state.quote_line = line
411
+ st.rerun()
412
 
413
+ # Quoting Section
414
+ if 'quote_line' in st.session_state:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  st.markdown(f"### Quoting: {st.session_state.quote_line}")
416
  quote_response = st.text_area("Add your response", key="quote_response")
 
 
 
 
 
 
 
417
  if st.button("Send Quote πŸš€", key="send_quote"):
418
+ async def process_quote():
419
+ await asyncio.to_thread(log_action, st.session_state.username, "πŸ“’πŸ’¬ - Quote processor - echoes resound!")
420
+ markdown_response = f"### Quote Response\n- **Original**: {st.session_state.quote_line}\n- **{st.session_state.username} Replies**: {quote_response}"
421
+ if st.session_state.pasted_image_data:
422
+ filename = await save_pasted_image(st.session_state.pasted_image_data)
423
+ if filename:
424
+ markdown_response += f"\n- **Image**: ![Pasted Image]({filename})"
425
+ st.session_state.pasted_image_data = None
426
+ await save_chat_entry(st.session_state.username, markdown_response)
427
+ loop.run_until_complete(process_quote())
428
+ del st.session_state.quote_line
429
  st.session_state.message_text = ''
430
  st.rerun()
431
 
432
+ # Username changer
433
  new_username = st.selectbox("Change Name", [""] + list(FUN_USERNAMES.keys()), index=0)
434
  if new_username and new_username != st.session_state.username:
435
+ loop.run_until_complete(save_chat_entry("System 🌟", f"{st.session_state.username} changed name to {new_username}"))
436
  st.session_state.username = new_username
437
  st.rerun()
438
 
439
+ # Message Input
440
  message = st.text_input(f"Message as {st.session_state.username}", key="message_input", value=st.session_state.message_text, on_change=lambda: st.session_state.update(message_text=st.session_state.message_input))
441
+ if st.button("Send πŸš€", key="send_button") and message.strip():
442
+ loop.run_until_complete(save_chat_entry(st.session_state.username, message))
 
 
 
 
 
 
 
 
443
  if st.session_state.pasted_image_data:
444
+ filename = loop.run_until_complete(save_pasted_image(st.session_state.pasted_image_data))
445
+ if filename:
446
+ loop.run_until_complete(save_chat_entry(st.session_state.username, f"Pasted image: {filename}"))
447
+ st.session_state.pasted_image_data = None
448
  st.session_state.message_text = ''
449
  st.rerun()
450
 
451
+ # Paste Target
452
+ components.html(
453
+ """
454
+ <div id="paste-target">Paste an image here (Ctrl+V)</div>
455
+ <script>
456
+ const pasteTarget = document.getElementById('paste-target');
457
+ pasteTarget.addEventListener('paste', (event) => {
458
+ const items = (event.clipboardData || window.clipboardData).items;
459
+ for (let i = 0; i < items.length; i++) {
460
+ if (items[i].type.indexOf('image') !== -1) {
461
+ const blob = items[i].getAsFile();
462
+ const reader = new FileReader();
463
+ reader.onload = (e) => {
464
+ window.parent.postMessage({
465
+ type: 'streamlit:setComponentValue',
466
+ value: e.target.result
467
+ }, '*');
468
+ pasteTarget.innerHTML = '<p>Image pasted! Processing...</p>';
469
+ };
470
+ reader.readAsDataURL(blob);
471
+ }
472
+ }
473
+ event.preventDefault();
474
+ });
475
+ </script>
476
+ """,
477
+ height=100
478
+ )
479
+
480
+ # Media Section
481
+ st.subheader("Media Gallery 🎨🎢πŸŽ₯")
482
+ uploaded_file = st.file_uploader("Upload Media", type=['png', 'jpg', 'mp3', 'mp4'])
483
  if uploaded_file:
484
+ file_path = os.path.join('./', uploaded_file.name)
 
 
 
 
485
  await asyncio.to_thread(lambda: open(file_path, 'wb').write(uploaded_file.getbuffer()))
486
+ st.success(f"Uploaded {uploaded_file.name}")
 
 
 
487
 
488
+ media_files = glob.glob("./*.png") + glob.glob("./*.jpg") + glob.glob("./*.mp3") + glob.glob("./*.mp4")
 
489
  if media_files:
490
+ cols = st.columns(3)
491
+ media_votes = loop.run_until_complete(load_votes(MEDIA_VOTES_FILE))
492
+ for idx, media_file in enumerate(media_files):
 
493
  vote_count = media_votes.get(media_file, 0)
494
+ if vote_count > 0:
495
+ with cols[idx % 3]:
496
+ if media_file.endswith(('.png', '.jpg')):
497
+ st.image(media_file, use_container_width=True)
498
+ elif media_file.endswith('.mp3'):
499
+ st.markdown(loop.run_until_complete(get_audio_html(media_file)), unsafe_allow_html=True)
500
+ elif media_file.endswith('.mp4'):
501
+ st.markdown(loop.run_until_complete(get_video_html(media_file)), unsafe_allow_html=True)
502
+ col1, col2 = st.columns(2)
503
+ with col1:
504
+ if st.button(f"πŸ‘ {vote_count}", key=f"media_vote_{idx}"):
505
+ comment = st.session_state.message_text
506
+ loop.run_until_complete(save_vote(MEDIA_VOTES_FILE, media_file, await generate_user_hash(), st.session_state.username, comment))
507
+ if st.session_state.pasted_image_data:
508
+ filename = loop.run_until_complete(save_pasted_image(st.session_state.pasted_image_data))
509
+ if filename:
510
+ loop.run_until_complete(save_chat_entry(st.session_state.username, f"Pasted image: {filename}"))
511
+ st.session_state.pasted_image_data = None
512
+ st.session_state.message_text = ''
513
+ st.rerun()
514
+ with col2:
515
+ if st.button("πŸ—‘οΈ", key=f"media_delete_{idx}"):
516
+ await asyncio.to_thread(os.remove, media_file)
517
+ st.rerun()
518
+
519
+ # Refresh Timer
520
  st.subheader("Refresh ⏳")
521
  refresh_rate = st.slider("Refresh Rate", 1, 300, st.session_state.refresh_rate)
522
  st.session_state.refresh_rate = refresh_rate
 
525
  font_name, font_func = random.choice(UNICODE_FONTS)
526
  countdown_str = "".join(UNICODE_DIGITS[int(d)] for d in str(i)) if i < 10 else font_func(str(i))
527
  timer_placeholder.markdown(f"<p class='timer'>⏳ {font_func('Refresh in:')} {countdown_str}</p>", unsafe_allow_html=True)
528
+ loop.run_until_complete(asyncio.sleep(1))
529
  st.rerun()
530
 
531
+ # Sidebar History
532
  st.sidebar.subheader("Chat History πŸ“œ")
533
  with open(HISTORY_FILE, 'r') as f:
534
  history_content = f.read()
535
  st.sidebar.markdown(history_content)
536
 
537
+ # Run the async interface
538
  loop.run_until_complete(async_interface())
539
 
540
+ # Main execution - let’s roll! πŸŽ²πŸš€
541
+ def main():
542
+ NODE_NAME, port = get_node_name()
543
+ create_streamlit_interface()
544
+
545
  if __name__ == "__main__":
546
  main()