fix: start cached songs even when played by voice (autoplay + room scope)
Rebuild & Deploy / rebuild (push) Successful in 50s
Rebuild & Deploy / rebuild (push) Successful in 50s
Hope often starts an already-downloaded song from a voice command, with no click on the player. Two things could leave it paused at 0: - The <audio> element sits outside the LiveKit room's audio-unlock flow, so autoplay policy rejected the poll-driven play(). Capture the first page gesture (pointerdown/keydown/touchstart) and play the current src inside it to whitelist the element for autoplay. - Player state is scoped per LiveKit room, but mediaScope() could return a stale shell room after a reconnect. Derive it from the connected room.name so the browser polls exactly the room the agent joined, and a "play X" request can never land in a scope the browser isn't watching. Add an in-container regression test (test_media_play_resolves_cached_ready_track) pinning the server-side resolve: an already-ready track plays without a re-download and serves a stable audio_url.
This commit is contained in:
1 parent
436a26a0fc
commit
df8aa64504
2 files changed
+100
No files matched your search
@@ -427,6 +427,59 @@ finally:
|
||||
assert "OK" in proc.stdout
|
||||
|
||||
|
||||
@needs_local_container
|
||||
def test_media_play_resolves_cached_ready_track(client):
|
||||
"""Playing an ALREADY-cached song must resolve to the on-disk ready track
|
||||
(no redownload) and hand the browser a playable stream URL, so a cached
|
||||
pick starts instantly rather than re-fetching from YouTube.
|
||||
|
||||
Regression: a cached track could be left as paused with currentTime 0 if
|
||||
the room the agent played into diverged from the room the browser polled,
|
||||
or if autoplay was blocked. This pins the server-side resolve + track
|
||||
dict so a ready file is always returned with a stable audio_url."""
|
||||
code = '''
|
||||
import sys
|
||||
sys.path.insert(0, "/opt/voice")
|
||||
import media_server as ms
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
probe = ms.FILES_DIR / "cachetrk.webm"
|
||||
probe.write_bytes(b"CACHEDBYTES" * 40)
|
||||
ms._library["tracks"]["cachetrk"] = {
|
||||
"id": "cachetrk", "kind": "youtube", "video_id": "cachetrk",
|
||||
"title": "Cached Song", "channel": "Artist", "duration": 200,
|
||||
"status": "ready", "ext": "webm", "error": None, "added_at": 1.0,
|
||||
}
|
||||
ms._start_download = lambda *a, **k: (_ for _ in ()).throw(
|
||||
AssertionError("must not re-download an already cached track")
|
||||
)
|
||||
try:
|
||||
c = TestClient(ms.app)
|
||||
# play by video_id (as Hope's play_song does) must reuse the ready track
|
||||
r = c.post("/media/api/play", json={
|
||||
"room": "cached-test-room", "video_id": "cachetrk",
|
||||
"title": "Cached Song", "channel": "Artist",
|
||||
}, timeout=10)
|
||||
assert r.status_code == 200, r.text
|
||||
body = r.json()
|
||||
assert body["desired"] == "playing", body
|
||||
track = body["track"]
|
||||
assert track["status"] == "ready", track
|
||||
assert track["audio_url"] == "/media/api/stream/cachetrk", track
|
||||
# the stream endpoint serves the cached bytes (browser plays it directly)
|
||||
data = c.get("/media/api/stream/cachetrk")
|
||||
assert data.status_code == 200
|
||||
assert data.content == b"CACHEDBYTES" * 40
|
||||
print("OK")
|
||||
finally:
|
||||
probe.unlink(missing_ok=True)
|
||||
ms._library["tracks"].pop("cachetrk", None)
|
||||
'''
|
||||
proc = docker_exec_python(code, timeout=60, check=False)
|
||||
assert proc.returncode == 0, proc.stderr[-800:]
|
||||
assert "OK" in proc.stdout
|
||||
|
||||
|
||||
@needs_local_container
|
||||
def test_mcp_media_list_library(mcp_client_factory):
|
||||
text, is_error = mcp_client_factory(
|
||||
|
||||
Reference in new issue
Block a user