Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -18,6 +18,8 @@ from audio_recorder_streamlit import audio_recorder
|
|
| 18 |
import nest_asyncio
|
| 19 |
import re
|
| 20 |
from streamlit_paste_button import paste_image_button
|
|
|
|
|
|
|
| 21 |
|
| 22 |
# Patch for nested async - sneaky fix! πβ¨
|
| 23 |
nest_asyncio.apply()
|
|
@@ -140,7 +142,8 @@ if 'image_hashes' not in st.session_state:
|
|
| 140 |
|
| 141 |
# Timestamp wizardry - clock ticks with flair! β°π©
|
| 142 |
def format_timestamp_prefix(username):
|
| 143 |
-
|
|
|
|
| 144 |
return f"{now.strftime('%I-%M-%p-ct-%m-%d-%Y')}-by-{username}"
|
| 145 |
|
| 146 |
# Compute image hash from binary data
|
|
@@ -172,8 +175,9 @@ def log_action(username, action):
|
|
| 172 |
user_log = {k: v for k, v in user_log.items() if current_time - v < 10}
|
| 173 |
st.session_state.action_log[username] = user_log
|
| 174 |
if action not in user_log:
|
|
|
|
| 175 |
with open(HISTORY_FILE, 'a') as f:
|
| 176 |
-
f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
|
| 177 |
user_log[action] = current_time
|
| 178 |
|
| 179 |
# Clean text - strip the fancy stuff! π§Ήπ
|
|
@@ -185,13 +189,14 @@ def clean_text_for_tts(text):
|
|
| 185 |
# Chat saver - words locked tight! π¬π
|
| 186 |
async def save_chat_entry(username, message, is_markdown=False):
|
| 187 |
await asyncio.to_thread(log_action, username, "π¬π - Chat saver - words locked tight!")
|
| 188 |
-
|
|
|
|
| 189 |
if is_markdown:
|
| 190 |
entry = f"[{timestamp}] {username}:\n```markdown\n{message}\n```"
|
| 191 |
else:
|
| 192 |
entry = f"[{timestamp}] {username}: {message}"
|
| 193 |
await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"{entry}\n"))
|
| 194 |
-
voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
|
| 195 |
cleaned_message = clean_text_for_tts(message)
|
| 196 |
audio_file = await async_edge_tts_generate(cleaned_message, voice)
|
| 197 |
if audio_file:
|
|
@@ -199,6 +204,7 @@ async def save_chat_entry(username, message, is_markdown=False):
|
|
| 199 |
f.write(f"[{timestamp}] {username}: Audio generated - {audio_file}\n")
|
| 200 |
await broadcast_message(f"{username}|{message}", "chat")
|
| 201 |
st.session_state.last_chat_update = time.time()
|
|
|
|
| 202 |
|
| 203 |
# Chat loader - history unleashed! ππ
|
| 204 |
async def load_chat():
|
|
@@ -238,7 +244,8 @@ async def get_message_suggestions(chat_content, prefix):
|
|
| 238 |
# Vote saver - cheers recorded! ππ
|
| 239 |
async def save_vote(file, item, user_hash, username, comment=""):
|
| 240 |
await asyncio.to_thread(log_action, username, "ππ - Vote saver - cheers recorded!")
|
| 241 |
-
|
|
|
|
| 242 |
entry = f"[{timestamp}] {user_hash} voted for {item}"
|
| 243 |
await asyncio.to_thread(lambda: open(file, 'a').write(f"{entry}\n"))
|
| 244 |
await asyncio.to_thread(lambda: open(HISTORY_FILE, "a").write(f"- {timestamp} - User {user_hash} voted for {item}\n"))
|
|
@@ -289,7 +296,8 @@ async def async_edge_tts_generate(text, voice, rate=0, pitch=0, file_format="mp3
|
|
| 289 |
return filepath if os.path.exists(filepath) else None
|
| 290 |
except edge_tts.exceptions.NoAudioReceived:
|
| 291 |
with open(HISTORY_FILE, 'a') as f:
|
| 292 |
-
|
|
|
|
| 293 |
return None
|
| 294 |
|
| 295 |
# Audio player - tunes blast off! ππ
|
|
@@ -397,6 +405,40 @@ async def perform_ai_lookup(query, vocal_summary=True, extended_refs=False, titl
|
|
| 397 |
if audio_file:
|
| 398 |
st.audio(audio_file)
|
| 399 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 400 |
# ASR Component HTML
|
| 401 |
ASR_HTML = """
|
| 402 |
<html>
|
|
@@ -574,7 +616,7 @@ def main():
|
|
| 574 |
available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
|
| 575 |
st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
|
| 576 |
st.session_state.voice = FUN_USERNAMES[st.session_state.username]
|
| 577 |
-
st.markdown(f"**ποΈ Voice Selected**: {st.session_state.voice} π£οΈ")
|
| 578 |
|
| 579 |
st.title(f"π€π§ MMO {st.session_state.username}ππ¬")
|
| 580 |
st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! π")
|
|
@@ -671,6 +713,7 @@ def main():
|
|
| 671 |
if paste_result_quote.image_data is not None:
|
| 672 |
if isinstance(paste_result_quote.image_data, str):
|
| 673 |
st.session_state.message_text = paste_result_quote.image_data
|
|
|
|
| 674 |
else:
|
| 675 |
st.image(paste_result_quote.image_data, caption="Received Image for Quote")
|
| 676 |
filename = await save_pasted_image(paste_result_quote.image_data, st.session_state.username)
|
|
@@ -687,19 +730,21 @@ def main():
|
|
| 687 |
st.session_state.message_text = ''
|
| 688 |
st.rerun()
|
| 689 |
|
| 690 |
-
|
|
|
|
| 691 |
if new_username and new_username != st.session_state.username:
|
| 692 |
await save_chat_entry("System π", f"{st.session_state.username} changed name to {new_username}")
|
| 693 |
st.session_state.username = new_username
|
| 694 |
st.session_state.voice = FUN_USERNAMES[new_username]
|
| 695 |
-
st.markdown(f"**ποΈ Voice Changed**: {st.session_state.voice} π£οΈ")
|
| 696 |
st.rerun()
|
| 697 |
|
| 698 |
-
message = st.text_input(f"Message as {st.session_state.username}", key="message_input", value=st.session_state.message_text
|
| 699 |
paste_result_msg = paste_image_button("π Paste Image or Text with Message", key="paste_button_msg")
|
| 700 |
if paste_result_msg.image_data is not None:
|
| 701 |
if isinstance(paste_result_msg.image_data, str):
|
| 702 |
st.session_state.message_text = paste_result_msg.image_data
|
|
|
|
| 703 |
else:
|
| 704 |
st.image(paste_result_msg.image_data, caption="Received Image for Message")
|
| 705 |
filename = await save_pasted_image(paste_result_msg.image_data, st.session_state.username)
|
|
@@ -707,7 +752,9 @@ def main():
|
|
| 707 |
st.session_state.pasted_image_data = filename
|
| 708 |
if st.button("Send π", key="send_button") and (message.strip() or st.session_state.pasted_image_data):
|
| 709 |
if message.strip():
|
| 710 |
-
await save_chat_entry(st.session_state.username, message, is_markdown=True)
|
|
|
|
|
|
|
| 711 |
if st.session_state.pasted_image_data:
|
| 712 |
await save_chat_entry(st.session_state.username, f"Pasted image: {st.session_state.pasted_image_data}")
|
| 713 |
st.session_state.pasted_image_data = None
|
|
@@ -735,6 +782,20 @@ def main():
|
|
| 735 |
if file_path.endswith('.mp4'):
|
| 736 |
st.session_state.media_notifications.append(file_path)
|
| 737 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
st.subheader("Media Gallery π¨πΆπ₯")
|
| 739 |
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")
|
| 740 |
if media_files:
|
|
|
|
| 18 |
import nest_asyncio
|
| 19 |
import re
|
| 20 |
from streamlit_paste_button import paste_image_button
|
| 21 |
+
import pytz
|
| 22 |
+
import shutil
|
| 23 |
|
| 24 |
# Patch for nested async - sneaky fix! πβ¨
|
| 25 |
nest_asyncio.apply()
|
|
|
|
| 142 |
|
| 143 |
# Timestamp wizardry - clock ticks with flair! β°π©
|
| 144 |
def format_timestamp_prefix(username):
|
| 145 |
+
central = pytz.timezone('US/Central')
|
| 146 |
+
now = datetime.now(central)
|
| 147 |
return f"{now.strftime('%I-%M-%p-ct-%m-%d-%Y')}-by-{username}"
|
| 148 |
|
| 149 |
# Compute image hash from binary data
|
|
|
|
| 175 |
user_log = {k: v for k, v in user_log.items() if current_time - v < 10}
|
| 176 |
st.session_state.action_log[username] = user_log
|
| 177 |
if action not in user_log:
|
| 178 |
+
central = pytz.timezone('US/Central')
|
| 179 |
with open(HISTORY_FILE, 'a') as f:
|
| 180 |
+
f.write(f"[{datetime.now(central).strftime('%Y-%m-%d %H:%M:%S')}] {username}: {action}\n")
|
| 181 |
user_log[action] = current_time
|
| 182 |
|
| 183 |
# Clean text - strip the fancy stuff! π§Ήπ
|
|
|
|
| 189 |
# Chat saver - words locked tight! π¬π
|
| 190 |
async def save_chat_entry(username, message, is_markdown=False):
|
| 191 |
await asyncio.to_thread(log_action, username, "π¬π - Chat saver - words locked tight!")
|
| 192 |
+
central = pytz.timezone('US/Central')
|
| 193 |
+
timestamp = datetime.now(central).strftime("%Y-%m-%d %H:%M:%S")
|
| 194 |
if is_markdown:
|
| 195 |
entry = f"[{timestamp}] {username}:\n```markdown\n{message}\n```"
|
| 196 |
else:
|
| 197 |
entry = f"[{timestamp}] {username}: {message}"
|
| 198 |
await asyncio.to_thread(lambda: open(CHAT_FILE, 'a').write(f"{entry}\n"))
|
| 199 |
+
voice = FUN_USERNAMES.get(username, "en-US-AriaNeural")
|
| 200 |
cleaned_message = clean_text_for_tts(message)
|
| 201 |
audio_file = await async_edge_tts_generate(cleaned_message, voice)
|
| 202 |
if audio_file:
|
|
|
|
| 204 |
f.write(f"[{timestamp}] {username}: Audio generated - {audio_file}\n")
|
| 205 |
await broadcast_message(f"{username}|{message}", "chat")
|
| 206 |
st.session_state.last_chat_update = time.time()
|
| 207 |
+
return audio_file
|
| 208 |
|
| 209 |
# Chat loader - history unleashed! ππ
|
| 210 |
async def load_chat():
|
|
|
|
| 244 |
# Vote saver - cheers recorded! ππ
|
| 245 |
async def save_vote(file, item, user_hash, username, comment=""):
|
| 246 |
await asyncio.to_thread(log_action, username, "ππ - Vote saver - cheers recorded!")
|
| 247 |
+
central = pytz.timezone('US/Central')
|
| 248 |
+
timestamp = datetime.now(central).strftime("%Y-%m-%d %H:%M:%S")
|
| 249 |
entry = f"[{timestamp}] {user_hash} voted for {item}"
|
| 250 |
await asyncio.to_thread(lambda: open(file, 'a').write(f"{entry}\n"))
|
| 251 |
await asyncio.to_thread(lambda: open(HISTORY_FILE, "a").write(f"- {timestamp} - User {user_hash} voted for {item}\n"))
|
|
|
|
| 296 |
return filepath if os.path.exists(filepath) else None
|
| 297 |
except edge_tts.exceptions.NoAudioReceived:
|
| 298 |
with open(HISTORY_FILE, 'a') as f:
|
| 299 |
+
central = pytz.timezone('US/Central')
|
| 300 |
+
f.write(f"[{datetime.now(central).strftime('%Y-%m-%d %H:%M:%S')}] {username}: Audio failed - No audio received for '{text}'\n")
|
| 301 |
return None
|
| 302 |
|
| 303 |
# Audio player - tunes blast off! ππ
|
|
|
|
| 405 |
if audio_file:
|
| 406 |
st.audio(audio_file)
|
| 407 |
|
| 408 |
+
# Delete all user files function
|
| 409 |
+
def delete_user_files():
|
| 410 |
+
protected_files = {'app.py', 'requirements.txt', 'README.md'}
|
| 411 |
+
deleted_files = []
|
| 412 |
+
|
| 413 |
+
# Directories to clear
|
| 414 |
+
directories = [MEDIA_DIR, AUDIO_DIR, CHAT_DIR, VOTE_DIR, HISTORY_DIR]
|
| 415 |
+
|
| 416 |
+
for directory in directories:
|
| 417 |
+
if os.path.exists(directory):
|
| 418 |
+
for root, _, files in os.walk(directory):
|
| 419 |
+
for file in files:
|
| 420 |
+
file_path = os.path.join(root, file)
|
| 421 |
+
if os.path.basename(file_path) not in protected_files:
|
| 422 |
+
try:
|
| 423 |
+
os.remove(file_path)
|
| 424 |
+
deleted_files.append(file_path)
|
| 425 |
+
except Exception as e:
|
| 426 |
+
st.error(f"Failed to delete {file_path}: {e}")
|
| 427 |
+
# Remove empty directories
|
| 428 |
+
try:
|
| 429 |
+
shutil.rmtree(directory, ignore_errors=True)
|
| 430 |
+
os.makedirs(directory, exist_ok=True) # Recreate empty directory
|
| 431 |
+
except Exception as e:
|
| 432 |
+
st.error(f"Failed to remove directory {directory}: {e}")
|
| 433 |
+
|
| 434 |
+
# Clear session state caches
|
| 435 |
+
st.session_state.image_hashes.clear()
|
| 436 |
+
st.session_state.audio_cache.clear()
|
| 437 |
+
st.session_state.base64_cache.clear()
|
| 438 |
+
st.session_state.displayed_chat_lines.clear()
|
| 439 |
+
|
| 440 |
+
return deleted_files
|
| 441 |
+
|
| 442 |
# ASR Component HTML
|
| 443 |
ASR_HTML = """
|
| 444 |
<html>
|
|
|
|
| 616 |
available_names = [name for name in FUN_USERNAMES if not any(f"{name} has joined" in line for line in chat_content.split('\n'))]
|
| 617 |
st.session_state.username = random.choice(available_names) if available_names else random.choice(list(FUN_USERNAMES.keys()))
|
| 618 |
st.session_state.voice = FUN_USERNAMES[st.session_state.username]
|
| 619 |
+
st.markdown(f"**ποΈ Voice Selected**: {st.session_state.voice} π£οΈ for {st.session_state.username}")
|
| 620 |
|
| 621 |
st.title(f"π€π§ MMO {st.session_state.username}ππ¬")
|
| 622 |
st.markdown(f"Welcome to {START_ROOM} - chat, vote, upload, paste images, and enjoy quoting! π")
|
|
|
|
| 713 |
if paste_result_quote.image_data is not None:
|
| 714 |
if isinstance(paste_result_quote.image_data, str):
|
| 715 |
st.session_state.message_text = paste_result_quote.image_data
|
| 716 |
+
st.text_area("Add your response", key="quote_response", value=st.session_state.message_text)
|
| 717 |
else:
|
| 718 |
st.image(paste_result_quote.image_data, caption="Received Image for Quote")
|
| 719 |
filename = await save_pasted_image(paste_result_quote.image_data, st.session_state.username)
|
|
|
|
| 730 |
st.session_state.message_text = ''
|
| 731 |
st.rerun()
|
| 732 |
|
| 733 |
+
current_selection = st.session_state.username if st.session_state.username in FUN_USERNAMES else ""
|
| 734 |
+
new_username = st.selectbox("Change Name and Voice", [""] + list(FUN_USERNAMES.keys()), index=(list(FUN_USERNAMES.keys()).index(current_selection) + 1 if current_selection else 0), format_func=lambda x: f"{x} ({FUN_USERNAMES.get(x, 'No Voice')})" if x else "Select a name")
|
| 735 |
if new_username and new_username != st.session_state.username:
|
| 736 |
await save_chat_entry("System π", f"{st.session_state.username} changed name to {new_username}")
|
| 737 |
st.session_state.username = new_username
|
| 738 |
st.session_state.voice = FUN_USERNAMES[new_username]
|
| 739 |
+
st.markdown(f"**ποΈ Voice Changed**: {st.session_state.voice} π£οΈ for {st.session_state.username}")
|
| 740 |
st.rerun()
|
| 741 |
|
| 742 |
+
message = st.text_input(f"Message as {st.session_state.username} (Voice: {st.session_state.voice})", key="message_input", value=st.session_state.message_text)
|
| 743 |
paste_result_msg = paste_image_button("π Paste Image or Text with Message", key="paste_button_msg")
|
| 744 |
if paste_result_msg.image_data is not None:
|
| 745 |
if isinstance(paste_result_msg.image_data, str):
|
| 746 |
st.session_state.message_text = paste_result_msg.image_data
|
| 747 |
+
st.text_input(f"Message as {st.session_state.username} (Voice: {st.session_state.voice})", key="message_input_paste", value=st.session_state.message_text)
|
| 748 |
else:
|
| 749 |
st.image(paste_result_msg.image_data, caption="Received Image for Message")
|
| 750 |
filename = await save_pasted_image(paste_result_msg.image_data, st.session_state.username)
|
|
|
|
| 752 |
st.session_state.pasted_image_data = filename
|
| 753 |
if st.button("Send π", key="send_button") and (message.strip() or st.session_state.pasted_image_data):
|
| 754 |
if message.strip():
|
| 755 |
+
audio_file = await save_chat_entry(st.session_state.username, message, is_markdown=True)
|
| 756 |
+
if audio_file:
|
| 757 |
+
st.session_state.audio_cache[f"{message}_{FUN_USERNAMES[st.session_state.username]}"] = audio_file
|
| 758 |
if st.session_state.pasted_image_data:
|
| 759 |
await save_chat_entry(st.session_state.username, f"Pasted image: {st.session_state.pasted_image_data}")
|
| 760 |
st.session_state.pasted_image_data = None
|
|
|
|
| 782 |
if file_path.endswith('.mp4'):
|
| 783 |
st.session_state.media_notifications.append(file_path)
|
| 784 |
|
| 785 |
+
# Big Red Delete Button
|
| 786 |
+
st.subheader("π Danger Zone")
|
| 787 |
+
if st.button("Try Not To Delete It All On Your First Day", key="delete_all", help="Deletes all user-added files!", type="primary", use_container_width=True):
|
| 788 |
+
deleted_files = delete_user_files()
|
| 789 |
+
if deleted_files:
|
| 790 |
+
st.markdown("### ποΈ Deleted Files:\n" + "\n".join([f"- `{file}`" for file in deleted_files]))
|
| 791 |
+
else:
|
| 792 |
+
st.markdown("### ποΈ Nothing to Delete!")
|
| 793 |
+
st.session_state.image_hashes.clear()
|
| 794 |
+
st.session_state.audio_cache.clear()
|
| 795 |
+
st.session_state.base64_cache.clear()
|
| 796 |
+
st.session_state.displayed_chat_lines.clear()
|
| 797 |
+
st.rerun()
|
| 798 |
+
|
| 799 |
st.subheader("Media Gallery π¨πΆπ₯")
|
| 800 |
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")
|
| 801 |
if media_files:
|