fix(worker): run streamrip on one persistent event loop (fixes Qobuz download failures)

Each search/download called asyncio.run, creating and closing a fresh event loop.
streamrip's module-global download-concurrency Semaphore binds to the loop on first
use, so the 2nd+ download reused a Semaphore bound to a closed loop -> "Semaphore is
bound to a different event loop", tracks failed, album arrived short -> "incomplete
download" / needs_attention. Run all streamrip coroutines on one persistent loop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-13 23:04:25 +02:00
parent fdca692bb5
commit 53c0dcc256
2 changed files with 34 additions and 2 deletions
+17
View File
@@ -0,0 +1,17 @@
import asyncio
from lyra_worker.adapters._streamrip import _run
def test_run_reuses_one_persistent_loop_across_calls():
# The event-loop fix: unlike asyncio.run (which creates AND closes a fresh loop per call),
# _run runs every streamrip coroutine on ONE persistent loop — so streamrip's loop-bound
# global Semaphore stays valid across downloads instead of orphaning on a closed loop.
async def current_loop():
return asyncio.get_running_loop()
loop_a = _run(current_loop())
loop_b = _run(current_loop())
assert loop_a is loop_b # same loop reused, not a fresh one each call
assert not loop_a.is_closed() # still open (asyncio.run would have closed it)