feat: playlist shuffle, lazy resolution, TTS ducking, kept repair
Some checks failed
Some checks failed
Music: - #random URL fragment shuffles playlist tracks before enqueuing - Lazy playlist resolution: first 10 tracks resolve immediately, remaining are fetched in a background task - !kept repair re-downloads kept tracks with missing local files - !kept shows [MISSING] marker for tracks without local files - TTS ducking: music ducks when merlin speaks via voice peer, smooth restore after TTS finishes Performance (from profiling): - Connection pool: preload_content=True for SOCKS connection reuse - Pool tuning: 30 pools / 8 connections (up from 20/4) - _PooledResponse wrapper for stdlib-compatible read interface - Iterative _extract_videos (replace 51K-deep recursion with stack) - proxy=False for local SearXNG Voice + multi-bot: - Per-bot voice config lookup ([<username>.voice] in TOML) - Mute detection: skip duck silence when all users muted - Autoplay shuffle deck (no repeats until full cycle) - Seek clamp to track duration (prevent seek-past-end stall) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -38,6 +38,18 @@ _MAX_SAY_LEN = 500 # max characters for !say
|
||||
_WHISPER_URL = "http://192.168.129.9:8080/inference"
|
||||
_PIPER_URL = "http://192.168.129.9:5100/"
|
||||
|
||||
|
||||
def _find_voice_peer(bot):
|
||||
"""Find the voice-capable peer (the bot with 'voice' in only_plugins)."""
|
||||
bots = getattr(bot.registry, "_bots", {})
|
||||
for name, b in bots.items():
|
||||
if name == bot._username:
|
||||
continue
|
||||
if getattr(b, "_only_plugins", None) and "voice" in b._only_plugins:
|
||||
return b
|
||||
return None
|
||||
|
||||
|
||||
# -- Per-bot state -----------------------------------------------------------
|
||||
|
||||
|
||||
@@ -172,8 +184,10 @@ async def _flush_monitor(bot):
|
||||
remainder = text[len(trigger):].strip()
|
||||
if remainder:
|
||||
log.info("voice: trigger from %s: %s", name, remainder)
|
||||
bot._spawn(
|
||||
_tts_play(bot, remainder), name="voice-tts",
|
||||
# Route TTS through voice-capable peer if available
|
||||
speaker = _find_voice_peer(bot) or bot
|
||||
speaker._spawn(
|
||||
_tts_play(speaker, remainder), name="voice-tts",
|
||||
)
|
||||
continue
|
||||
|
||||
@@ -242,10 +256,13 @@ async def _tts_play(bot, text: str):
|
||||
if wav_path is None:
|
||||
return
|
||||
try:
|
||||
# Signal music plugin to duck while TTS is playing
|
||||
bot.registry._tts_active = True
|
||||
done = asyncio.Event()
|
||||
await bot.stream_audio(str(wav_path), volume=1.0, on_done=done)
|
||||
await done.wait()
|
||||
finally:
|
||||
bot.registry._tts_active = False
|
||||
Path(wav_path).unlink(missing_ok=True)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user