Spaces:
Sleeping
Sleeping
Nikita Makarov commited on
Commit ·
c04db7b
1
Parent(s): 84c7402
v2.3 we did it
Browse files- src/app.py +130 -92
src/app.py
CHANGED
|
@@ -230,6 +230,13 @@ def start_radio_stream():
|
|
| 230 |
return "🎵 Starting your personalized radio show...", None, None, None, ""
|
| 231 |
|
| 232 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
def start_and_play_first_segment():
|
| 234 |
"""
|
| 235 |
One-shot helper for the UI:
|
|
@@ -272,15 +279,15 @@ def start_and_play_first_segment():
|
|
| 272 |
}
|
| 273 |
print("📋 Using default preferences")
|
| 274 |
|
| 275 |
-
# Check if resuming from stopped state
|
| 276 |
-
if radio_state.get("is_stopped") and radio_state.get("planned_show"):
|
| 277 |
print("▶️ Resuming from stopped state...")
|
| 278 |
radio_state["is_playing"] = True
|
| 279 |
radio_state["is_stopped"] = False
|
| 280 |
radio_state["stop_flag"] = False
|
| 281 |
|
| 282 |
# Play next segment (continuing from where we stopped)
|
| 283 |
-
segment_info, host_audio, music_html, progress, now_playing, llm_script = play_next_segment()
|
| 284 |
|
| 285 |
return (
|
| 286 |
"▶️ Resuming radio...",
|
|
@@ -290,15 +297,30 @@ def start_and_play_first_segment():
|
|
| 290 |
progress,
|
| 291 |
now_playing,
|
| 292 |
llm_script,
|
|
|
|
| 293 |
)
|
| 294 |
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
|
| 303 |
import time
|
| 304 |
t0 = time.time()
|
|
@@ -326,7 +348,7 @@ def start_and_play_first_segment():
|
|
| 326 |
# Step 2 & 3: Generate and play first segment (LLM + TTS inside play_next_segment)
|
| 327 |
print(" [2/3] Generating first segment (LLM)...")
|
| 328 |
t1 = time.time()
|
| 329 |
-
segment_info, host_audio, music_html, progress, now_playing, llm_script = play_next_segment()
|
| 330 |
print(f" [2/3] Done in {time.time()-t1:.1f}s")
|
| 331 |
print(f" [3/3] Total time: {time.time()-t0:.1f}s")
|
| 332 |
print(f" Host audio file: {host_audio}")
|
|
@@ -339,6 +361,7 @@ def start_and_play_first_segment():
|
|
| 339 |
progress,
|
| 340 |
now_playing,
|
| 341 |
llm_script,
|
|
|
|
| 342 |
)
|
| 343 |
|
| 344 |
def play_next_segment():
|
|
@@ -356,11 +379,11 @@ def play_next_segment():
|
|
| 356 |
radio_state["is_playing"] = True
|
| 357 |
radio_state["stop_flag"] = False
|
| 358 |
else:
|
| 359 |
-
return "⏸️ No show planned. Click 'Generate & Play' to start.", None, None, None, "", ""
|
| 360 |
|
| 361 |
if radio_state["stop_flag"]:
|
| 362 |
radio_state["is_playing"] = False
|
| 363 |
-
return "⏸️ Radio paused", None, None, None, "", ""
|
| 364 |
|
| 365 |
# If there are remaining news TTS batches, play the next one without advancing to a new segment
|
| 366 |
if radio_state.get("current_news_batches"):
|
|
@@ -395,11 +418,11 @@ def play_next_segment():
|
|
| 395 |
radio_state["news_total_batches"] = 0
|
| 396 |
radio_state["news_batches_played"] = 0
|
| 397 |
|
| 398 |
-
return segment_info, host_audio_file, "", progress, get_now_playing(segment), display_script
|
| 399 |
|
| 400 |
if radio_state["current_segment_index"] >= len(radio_state["planned_show"]):
|
| 401 |
radio_state["is_playing"] = False
|
| 402 |
-
return "🎊 Show completed! Hope you enjoyed it.", None, None, None, "", ""
|
| 403 |
|
| 404 |
# Get current segment
|
| 405 |
segment_index = radio_state["current_segment_index"]
|
|
@@ -750,8 +773,38 @@ def play_next_segment():
|
|
| 750 |
|
| 751 |
threading.Thread(target=_prefetch_next, daemon=True).start()
|
| 752 |
|
| 753 |
-
#
|
| 754 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 755 |
|
| 756 |
def stop_radio():
|
| 757 |
"""Stop the radio stream - pauses at current segment without resetting"""
|
|
@@ -765,13 +818,14 @@ def stop_radio():
|
|
| 765 |
|
| 766 |
status_msg = f"⏹️ Stopped at segment {current_idx}/{total_segments}. Click 'Generate & Play' or 'Next Segment' to continue."
|
| 767 |
|
| 768 |
-
# Return status, clear audio, clear music player, progress, now playing
|
| 769 |
return (
|
| 770 |
status_msg,
|
| 771 |
None, # Clear audio
|
| 772 |
"", # Clear music player HTML
|
| 773 |
f"Stopped at {current_idx}/{total_segments}",
|
| 774 |
-
f"⏸️ Paused - Segment {current_idx}/{total_segments}"
|
|
|
|
| 775 |
)
|
| 776 |
|
| 777 |
|
|
@@ -959,6 +1013,7 @@ def handle_voice_request():
|
|
| 959 |
# Create music player HTML with volume fading (0→30% during host speech, then 30%→100%)
|
| 960 |
music_player_html = ""
|
| 961 |
player_id = f"voice_player_{int(time.time())}"
|
|
|
|
| 962 |
|
| 963 |
if track.get("source") == "youtube":
|
| 964 |
youtube_id = track.get("youtube_id", "")
|
|
@@ -966,53 +1021,48 @@ def handle_voice_request():
|
|
| 966 |
youtube_id = track["url"].split("v=")[-1].split("&")[0]
|
| 967 |
|
| 968 |
if youtube_id:
|
| 969 |
-
#
|
| 970 |
-
|
| 971 |
-
|
| 972 |
-
|
| 973 |
-
|
| 974 |
-
|
| 975 |
-
|
| 976 |
-
|
| 977 |
-
|
| 978 |
-
|
| 979 |
-
|
| 980 |
-
|
| 981 |
-
|
| 982 |
-
|
| 983 |
-
|
| 984 |
-
|
| 985 |
-
|
| 986 |
-
|
| 987 |
-
|
| 988 |
-
|
| 989 |
-
|
| 990 |
-
|
| 991 |
-
|
| 992 |
-
|
| 993 |
-
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
-
|
| 1001 |
-
|
| 1002 |
-
|
| 1003 |
-
|
| 1004 |
-
|
| 1005 |
-
|
| 1006 |
-
|
| 1007 |
-
|
| 1008 |
-
|
| 1009 |
-
|
| 1010 |
-
|
| 1011 |
-
track.get('title', 'Unknown'),
|
| 1012 |
-
track.get('artist', 'Unknown'),
|
| 1013 |
-
track['url']
|
| 1014 |
-
)
|
| 1015 |
-
print(f"✅ Voice request YouTube player created immediately: {track.get('title', 'Unknown')} (ID: {youtube_id})")
|
| 1016 |
else:
|
| 1017 |
music_player_html = f"""
|
| 1018 |
<div style="padding: 1rem; background: #f0f0f0; border-radius: 10px; margin: 1rem 0;">
|
|
@@ -1042,10 +1092,10 @@ def handle_voice_request():
|
|
| 1042 |
# The browser may block autoplay - user may need to click play
|
| 1043 |
|
| 1044 |
# Return with player
|
| 1045 |
-
return f"✅ {host_response}\n\n🎵 Playing: {track['title']} by {track['artist']}", audio_file, music_player_html
|
| 1046 |
|
| 1047 |
except Exception as e:
|
| 1048 |
-
return f"❌ Error processing voice request: {e}", None, ""
|
| 1049 |
|
| 1050 |
def get_now_playing(segment: Dict[str, Any]) -> str:
|
| 1051 |
"""Get now playing text"""
|
|
@@ -1561,26 +1611,14 @@ with gr.Blocks(css=custom_css, title="AI Radio 🎵", theme=gr.themes.Soft(), he
|
|
| 1561 |
audio_output = gr.Audio(label="🔊 Host Speech", autoplay=True, type="filepath", elem_id="host_audio")
|
| 1562 |
music_player = gr.HTML(label="🎵 Music/Podcast Player (streaming)")
|
| 1563 |
|
| 1564 |
-
#
|
| 1565 |
-
|
| 1566 |
-
|
| 1567 |
-
|
| 1568 |
-
|
| 1569 |
-
|
| 1570 |
-
|
| 1571 |
-
|
| 1572 |
-
# html = pending.get("html", "")
|
| 1573 |
-
# title = pending.get("title", "Unknown")
|
| 1574 |
-
# radio_state["pending_player"] = None # Clear it
|
| 1575 |
-
# print(f"▶️ Adding iframe for: {title}")
|
| 1576 |
-
# return html
|
| 1577 |
-
# return gr.update() # No change
|
| 1578 |
-
|
| 1579 |
-
# player_timer.tick(
|
| 1580 |
-
# fn=check_pending_player,
|
| 1581 |
-
# inputs=[],
|
| 1582 |
-
# outputs=[music_player]
|
| 1583 |
-
# )
|
| 1584 |
|
| 1585 |
status_text = gr.Textbox(label="Status", value="Ready", interactive=False, visible=False)
|
| 1586 |
|
|
@@ -1588,19 +1626,19 @@ with gr.Blocks(css=custom_css, title="AI Radio 🎵", theme=gr.themes.Soft(), he
|
|
| 1588 |
start_btn.click(
|
| 1589 |
fn=start_and_play_first_segment,
|
| 1590 |
inputs=[],
|
| 1591 |
-
outputs=[status_text, segment_info, audio_output, music_player, progress_text, now_playing, llm_script]
|
| 1592 |
)
|
| 1593 |
|
| 1594 |
next_btn.click(
|
| 1595 |
fn=play_next_segment,
|
| 1596 |
inputs=[],
|
| 1597 |
-
outputs=[segment_info, audio_output, music_player, progress_text, now_playing, llm_script]
|
| 1598 |
)
|
| 1599 |
|
| 1600 |
stop_btn.click(
|
| 1601 |
fn=stop_radio,
|
| 1602 |
inputs=[],
|
| 1603 |
-
outputs=[status_text, audio_output, music_player, progress_text, now_playing],
|
| 1604 |
js="() => { if(window.cancelNextSegment) window.cancelNextSegment(); }"
|
| 1605 |
)
|
| 1606 |
|
|
@@ -1608,7 +1646,7 @@ with gr.Blocks(css=custom_css, title="AI Radio 🎵", theme=gr.themes.Soft(), he
|
|
| 1608 |
voice_btn.click(
|
| 1609 |
fn=handle_voice_request,
|
| 1610 |
inputs=[],
|
| 1611 |
-
outputs=[voice_status, audio_output, music_player]
|
| 1612 |
)
|
| 1613 |
|
| 1614 |
# Like/Dislike buttons
|
|
|
|
| 230 |
return "🎵 Starting your personalized radio show...", None, None, None, ""
|
| 231 |
|
| 232 |
|
| 233 |
+
def show_pending_player():
|
| 234 |
+
"""Called by timer to finally show the media player"""
|
| 235 |
+
player_html = radio_state.get("pending_player_html", "")
|
| 236 |
+
print("⏰ Timer fired: Showing pending media player now")
|
| 237 |
+
# Return player HTML and disable timer
|
| 238 |
+
return player_html, gr.Timer(value=0, active=False)
|
| 239 |
+
|
| 240 |
def start_and_play_first_segment():
|
| 241 |
"""
|
| 242 |
One-shot helper for the UI:
|
|
|
|
| 279 |
}
|
| 280 |
print("📋 Using default preferences")
|
| 281 |
|
| 282 |
+
# Check if resuming from stopped state (only if show was manually stopped, not completed)
|
| 283 |
+
if radio_state.get("is_stopped") and radio_state.get("planned_show") and radio_state["current_segment_index"] < len(radio_state["planned_show"]):
|
| 284 |
print("▶️ Resuming from stopped state...")
|
| 285 |
radio_state["is_playing"] = True
|
| 286 |
radio_state["is_stopped"] = False
|
| 287 |
radio_state["stop_flag"] = False
|
| 288 |
|
| 289 |
# Play next segment (continuing from where we stopped)
|
| 290 |
+
segment_info, host_audio, music_html, progress, now_playing, llm_script, timer_config = play_next_segment()
|
| 291 |
|
| 292 |
return (
|
| 293 |
"▶️ Resuming radio...",
|
|
|
|
| 297 |
progress,
|
| 298 |
now_playing,
|
| 299 |
llm_script,
|
| 300 |
+
timer_config
|
| 301 |
)
|
| 302 |
|
| 303 |
+
# If show completed or no valid show to resume, start fresh
|
| 304 |
+
# Also check if is_playing is True but show is actually completed
|
| 305 |
+
if radio_state.get("is_playing") and radio_state.get("planned_show"):
|
| 306 |
+
# Check if show is actually completed
|
| 307 |
+
if radio_state["current_segment_index"] >= len(radio_state["planned_show"]):
|
| 308 |
+
# Show completed, reset and start fresh
|
| 309 |
+
print("🔄 Show completed, resetting for new show...")
|
| 310 |
+
reset_radio()
|
| 311 |
+
else:
|
| 312 |
+
# Show is actually playing
|
| 313 |
+
return (
|
| 314 |
+
"📻 Radio is already playing!",
|
| 315 |
+
"**Welcome!** Set your preferences and start the radio.",
|
| 316 |
+
None, None, "Ready to start", "📻 AI Radio",
|
| 317 |
+
"Model-generated script will appear here for each segment.",
|
| 318 |
+
gr.Timer(value=0, active=False)
|
| 319 |
+
)
|
| 320 |
+
elif radio_state.get("is_playing"):
|
| 321 |
+
# is_playing is True but no planned show - reset
|
| 322 |
+
print("🔄 is_playing=True but no planned_show, resetting...")
|
| 323 |
+
reset_radio()
|
| 324 |
|
| 325 |
import time
|
| 326 |
t0 = time.time()
|
|
|
|
| 348 |
# Step 2 & 3: Generate and play first segment (LLM + TTS inside play_next_segment)
|
| 349 |
print(" [2/3] Generating first segment (LLM)...")
|
| 350 |
t1 = time.time()
|
| 351 |
+
segment_info, host_audio, music_html, progress, now_playing, llm_script, timer_config = play_next_segment()
|
| 352 |
print(f" [2/3] Done in {time.time()-t1:.1f}s")
|
| 353 |
print(f" [3/3] Total time: {time.time()-t0:.1f}s")
|
| 354 |
print(f" Host audio file: {host_audio}")
|
|
|
|
| 361 |
progress,
|
| 362 |
now_playing,
|
| 363 |
llm_script,
|
| 364 |
+
timer_config
|
| 365 |
)
|
| 366 |
|
| 367 |
def play_next_segment():
|
|
|
|
| 379 |
radio_state["is_playing"] = True
|
| 380 |
radio_state["stop_flag"] = False
|
| 381 |
else:
|
| 382 |
+
return "⏸️ No show planned. Click 'Generate & Play' to start.", None, None, None, "", "", gr.Timer(value=0, active=False)
|
| 383 |
|
| 384 |
if radio_state["stop_flag"]:
|
| 385 |
radio_state["is_playing"] = False
|
| 386 |
+
return "⏸️ Radio paused", None, None, None, "", "", gr.Timer(value=0, active=False)
|
| 387 |
|
| 388 |
# If there are remaining news TTS batches, play the next one without advancing to a new segment
|
| 389 |
if radio_state.get("current_news_batches"):
|
|
|
|
| 418 |
radio_state["news_total_batches"] = 0
|
| 419 |
radio_state["news_batches_played"] = 0
|
| 420 |
|
| 421 |
+
return segment_info, host_audio_file, "", progress, get_now_playing(segment), display_script, gr.Timer(value=0, active=False)
|
| 422 |
|
| 423 |
if radio_state["current_segment_index"] >= len(radio_state["planned_show"]):
|
| 424 |
radio_state["is_playing"] = False
|
| 425 |
+
return "🎊 Show completed! Hope you enjoyed it.", None, None, None, "", "", gr.Timer(value=0, active=False)
|
| 426 |
|
| 427 |
# Get current segment
|
| 428 |
segment_index = radio_state["current_segment_index"]
|
|
|
|
| 773 |
|
| 774 |
threading.Thread(target=_prefetch_next, daemon=True).start()
|
| 775 |
|
| 776 |
+
# Calculate segment duration and set up delayed player if needed
|
| 777 |
+
timer_config = gr.Timer(value=0, active=False)
|
| 778 |
+
|
| 779 |
+
# Only delay player if we have both host audio and a music/podcast player
|
| 780 |
+
if host_audio_file and os.path.exists(host_audio_file) and music_player_html and ("<iframe" in music_player_html):
|
| 781 |
+
try:
|
| 782 |
+
audio = AudioSegment.from_file(host_audio_file)
|
| 783 |
+
host_duration_s = len(audio) / 1000.0
|
| 784 |
+
|
| 785 |
+
# Start player 3 seconds before host speech ends
|
| 786 |
+
delay_s = max(0.5, host_duration_s - 3.0)
|
| 787 |
+
|
| 788 |
+
print(f"⏱️ Host duration: {host_duration_s:.1f}s, Scheduling player in {delay_s:.1f}s")
|
| 789 |
+
|
| 790 |
+
# Store the actual player HTML
|
| 791 |
+
radio_state["pending_player_html"] = music_player_html
|
| 792 |
+
|
| 793 |
+
# Return placeholder/empty player and active timer
|
| 794 |
+
music_player_html = create_placeholder_html(
|
| 795 |
+
segment.get('track', {}).get('title', 'Content') if segment.get('type') == 'music' else segment.get('podcast', {}).get('title', 'Podcast'),
|
| 796 |
+
segment.get('track', {}).get('artist', '') if segment.get('type') == 'music' else segment.get('podcast', {}).get('host', ''),
|
| 797 |
+
delay_s,
|
| 798 |
+
segment.get('type', 'music')
|
| 799 |
+
)
|
| 800 |
+
timer_config = gr.Timer(value=delay_s, active=True)
|
| 801 |
+
|
| 802 |
+
except Exception as e:
|
| 803 |
+
print(f"⚠️ Error calculating audio duration for delay: {e}")
|
| 804 |
+
# Fallback: play immediately (leave music_player_html as is, timer inactive)
|
| 805 |
+
|
| 806 |
+
# Return: segment_info, host_audio, music_player_html, progress, now_playing, segmented LLM script for UI, timer
|
| 807 |
+
return segment_info, host_audio_file, music_player_html, progress, get_now_playing(segment), display_script, timer_config
|
| 808 |
|
| 809 |
def stop_radio():
|
| 810 |
"""Stop the radio stream - pauses at current segment without resetting"""
|
|
|
|
| 818 |
|
| 819 |
status_msg = f"⏹️ Stopped at segment {current_idx}/{total_segments}. Click 'Generate & Play' or 'Next Segment' to continue."
|
| 820 |
|
| 821 |
+
# Return status, clear audio, clear music player, progress, now playing, timer
|
| 822 |
return (
|
| 823 |
status_msg,
|
| 824 |
None, # Clear audio
|
| 825 |
"", # Clear music player HTML
|
| 826 |
f"Stopped at {current_idx}/{total_segments}",
|
| 827 |
+
f"⏸️ Paused - Segment {current_idx}/{total_segments}",
|
| 828 |
+
gr.Timer(value=0, active=False) # Stop timer
|
| 829 |
)
|
| 830 |
|
| 831 |
|
|
|
|
| 1013 |
# Create music player HTML with volume fading (0→30% during host speech, then 30%→100%)
|
| 1014 |
music_player_html = ""
|
| 1015 |
player_id = f"voice_player_{int(time.time())}"
|
| 1016 |
+
timer_config = gr.Timer(value=0, active=False)
|
| 1017 |
|
| 1018 |
if track.get("source") == "youtube":
|
| 1019 |
youtube_id = track.get("youtube_id", "")
|
|
|
|
| 1021 |
youtube_id = track["url"].split("v=")[-1].split("&")[0]
|
| 1022 |
|
| 1023 |
if youtube_id:
|
| 1024 |
+
# Delayed loading mechanism with gr.Timer
|
| 1025 |
+
try:
|
| 1026 |
+
# Get host audio duration to calculate delay
|
| 1027 |
+
delay_s = 0.5
|
| 1028 |
+
if audio_file and os.path.exists(audio_file):
|
| 1029 |
+
audio = AudioSegment.from_file(audio_file)
|
| 1030 |
+
duration_ms = len(audio)
|
| 1031 |
+
delay_s = max(0.5, (duration_ms / 1000.0) - 3.0) # Start 3 seconds before end
|
| 1032 |
+
print(f"⏱️ Voice request audio: {duration_ms}ms, YouTube delay: {delay_s:.1f}s")
|
| 1033 |
+
|
| 1034 |
+
# Create the actual player HTML
|
| 1035 |
+
actual_player_html = create_youtube_player_html(
|
| 1036 |
+
youtube_id,
|
| 1037 |
+
track.get('title', 'Unknown'),
|
| 1038 |
+
track.get('artist', 'Unknown'),
|
| 1039 |
+
track['url']
|
| 1040 |
+
)
|
| 1041 |
+
|
| 1042 |
+
# Store for timer callback
|
| 1043 |
+
radio_state["pending_player_html"] = actual_player_html
|
| 1044 |
+
|
| 1045 |
+
# Create placeholder
|
| 1046 |
+
music_player_html = create_placeholder_html(
|
| 1047 |
+
track.get('title', 'Unknown'),
|
| 1048 |
+
track.get('artist', 'Unknown'),
|
| 1049 |
+
delay_s,
|
| 1050 |
+
"music"
|
| 1051 |
+
)
|
| 1052 |
+
|
| 1053 |
+
# Activate timer
|
| 1054 |
+
timer_config = gr.Timer(value=delay_s, active=True)
|
| 1055 |
+
print(f"✅ Voice request: Player scheduled in {delay_s:.1f}s")
|
| 1056 |
+
|
| 1057 |
+
except Exception as e:
|
| 1058 |
+
print(f"⚠️ Error calculating delay for voice request: {e}")
|
| 1059 |
+
# Fallback: immediate play
|
| 1060 |
+
music_player_html = create_youtube_player_html(
|
| 1061 |
+
youtube_id,
|
| 1062 |
+
track.get('title', 'Unknown'),
|
| 1063 |
+
track.get('artist', 'Unknown'),
|
| 1064 |
+
track['url']
|
| 1065 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1066 |
else:
|
| 1067 |
music_player_html = f"""
|
| 1068 |
<div style="padding: 1rem; background: #f0f0f0; border-radius: 10px; margin: 1rem 0;">
|
|
|
|
| 1092 |
# The browser may block autoplay - user may need to click play
|
| 1093 |
|
| 1094 |
# Return with player
|
| 1095 |
+
return f"✅ {host_response}\n\n🎵 Playing: {track['title']} by {track['artist']}", audio_file, music_player_html, timer_config
|
| 1096 |
|
| 1097 |
except Exception as e:
|
| 1098 |
+
return f"❌ Error processing voice request: {e}", None, "", gr.Timer(value=0, active=False)
|
| 1099 |
|
| 1100 |
def get_now_playing(segment: Dict[str, Any]) -> str:
|
| 1101 |
"""Get now playing text"""
|
|
|
|
| 1611 |
audio_output = gr.Audio(label="🔊 Host Speech", autoplay=True, type="filepath", elem_id="host_audio")
|
| 1612 |
music_player = gr.HTML(label="🎵 Music/Podcast Player (streaming)")
|
| 1613 |
|
| 1614 |
+
# Timer for delayed player loading (start disabled)
|
| 1615 |
+
player_timer = gr.Timer(value=0, active=False)
|
| 1616 |
+
|
| 1617 |
+
player_timer.tick(
|
| 1618 |
+
fn=show_pending_player,
|
| 1619 |
+
inputs=[],
|
| 1620 |
+
outputs=[music_player, player_timer]
|
| 1621 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1622 |
|
| 1623 |
status_text = gr.Textbox(label="Status", value="Ready", interactive=False, visible=False)
|
| 1624 |
|
|
|
|
| 1626 |
start_btn.click(
|
| 1627 |
fn=start_and_play_first_segment,
|
| 1628 |
inputs=[],
|
| 1629 |
+
outputs=[status_text, segment_info, audio_output, music_player, progress_text, now_playing, llm_script, player_timer]
|
| 1630 |
)
|
| 1631 |
|
| 1632 |
next_btn.click(
|
| 1633 |
fn=play_next_segment,
|
| 1634 |
inputs=[],
|
| 1635 |
+
outputs=[segment_info, audio_output, music_player, progress_text, now_playing, llm_script, player_timer]
|
| 1636 |
)
|
| 1637 |
|
| 1638 |
stop_btn.click(
|
| 1639 |
fn=stop_radio,
|
| 1640 |
inputs=[],
|
| 1641 |
+
outputs=[status_text, audio_output, music_player, progress_text, now_playing, player_timer],
|
| 1642 |
js="() => { if(window.cancelNextSegment) window.cancelNextSegment(); }"
|
| 1643 |
)
|
| 1644 |
|
|
|
|
| 1646 |
voice_btn.click(
|
| 1647 |
fn=handle_voice_request,
|
| 1648 |
inputs=[],
|
| 1649 |
+
outputs=[voice_status, audio_output, music_player, player_timer]
|
| 1650 |
)
|
| 1651 |
|
| 1652 |
# Like/Dislike buttons
|