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:
|