⚡ Bolt: Make Deezer get_downloadable calls async#24
Conversation
Wraps blocking synchronous calls from the deezer-python API inside asyncio.to_thread in streamrip/client/deezer.py's get_downloadable. Co-authored-by: davidjuarezdev <230496599+davidjuarezdev@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited) Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR improves Deezer download resolution concurrency by preventing synchronous deezer-py network calls from blocking the asyncio event loop inside DeezerClient.get_downloadable.
Changes:
- Wrapped
client.gw.get_trackandclient.get_track_urlcalls inawait asyncio.to_thread(...)withinDeezerClient.get_downloadable. - Minor formatting alignment (double quotes) in
streamrip/db.pyand whitespace cleanup instreamrip/client/deezer.py. - Added a journal entry documenting the async I/O learning in
.jules/bolt.md.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
streamrip/client/deezer.py |
Moves blocking Deezer API calls off the event loop to enable real concurrency during downloads. |
streamrip/db.py |
Formatting-only tweak to align with the project’s double-quote style. |
.jules/bolt.md |
Adds an internal note capturing the rationale and takeaway for the change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
streamrip/client/deezer.py (1)
151-151: Add explicit timeouts to network calls that can hang indefinitely.The underlying
deezer-pylibrary (v1.3.6) uses therequestslibrary for HTTP calls, which has no default timeout (timeout=None). This meansgw.get_trackandget_track_urlcan stall indefinitely if the connection hangs. Wrap these calls withasyncio.wait_for()to prevent hanging.⏱️ Suggested timeout wrapper
- track_info = await asyncio.to_thread(self.client.gw.get_track, item_id) + track_info = await asyncio.wait_for( + asyncio.to_thread(self.client.gw.get_track, item_id), + timeout=30, + ) ... - url = await asyncio.to_thread(self.client.get_track_url, token, format_str) + url = await asyncio.wait_for( + asyncio.to_thread(self.client.get_track_url, token, format_str), + timeout=30, + )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@streamrip/client/deezer.py` at line 151, Wrap blocking Deezer network calls in asyncio.wait_for to enforce a bounded timeout: replace uses of asyncio.to_thread(self.client.gw.get_track, item_id) with awaiting asyncio.wait_for(asyncio.to_thread(self.client.gw.get_track, item_id), timeout=YOUR_TIMEOUT) and similarly wrap any call to get_track_url (e.g., asyncio.to_thread(self.client.get_track_url, ...)) with asyncio.wait_for(..., timeout=YOUR_TIMEOUT). Use a configurable constant (e.g., NETWORK_TIMEOUT or deezer_timeout) instead of a magic number, raise or handle asyncio.TimeoutError where appropriate, and update the methods in deezer.py that call self.client.gw.get_track and get_track_url.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@streamrip/client/deezer.py`:
- Line 151: Wrap blocking Deezer network calls in asyncio.wait_for to enforce a
bounded timeout: replace uses of asyncio.to_thread(self.client.gw.get_track,
item_id) with awaiting
asyncio.wait_for(asyncio.to_thread(self.client.gw.get_track, item_id),
timeout=YOUR_TIMEOUT) and similarly wrap any call to get_track_url (e.g.,
asyncio.to_thread(self.client.get_track_url, ...)) with asyncio.wait_for(...,
timeout=YOUR_TIMEOUT). Use a configurable constant (e.g., NETWORK_TIMEOUT or
deezer_timeout) instead of a magic number, raise or handle asyncio.TimeoutError
where appropriate, and update the methods in deezer.py that call
self.client.gw.get_track and get_track_url.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: adce9905-b000-4c31-b696-856389c82fec
📒 Files selected for processing (3)
.jules/bolt.mdstreamrip/client/deezer.pystreamrip/db.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (1)
streamrip/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
streamrip/**/*.py: Use Black-compatible code formatting with double quotes and spaces via ruff format
Lint Python code with ruff using rules: E4, E7, E9, F, I, ASYNC, N, RUF, ERA001
Use async/await for asynchronous operations instead of blocking I/O
Implement Windows compatibility by using WindowsSelectorEventLoopPolicy on Windows and the pick library instead of simple-term-menu
Files:
streamrip/db.pystreamrip/client/deezer.py
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: davidjuarezdev/streamrip_RipDL PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T03:25:38.225Z
Learning: Applies to streamrip/**/*.py : Use async/await for asynchronous operations instead of blocking I/O
📚 Learning: 2026-03-18T03:25:38.225Z
Learnt from: CR
Repo: davidjuarezdev/streamrip_RipDL PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T03:25:38.225Z
Learning: Applies to streamrip/**/*.py : Use async/await for asynchronous operations instead of blocking I/O
Applied to files:
.jules/bolt.mdstreamrip/client/deezer.py
🔇 Additional comments (3)
streamrip/db.py (1)
85-85: Formatting-only update is clean and behavior-preserving.Using
"conn"here is consistent and does not alter connection lifecycle behavior.As per coding guidelines:
streamrip/**/*.py: Use Black-compatible code formatting with double quotes and spaces via ruff format.Also applies to: 171-171
streamrip/client/deezer.py (1)
151-151: Good fix: synchronous Deezer calls are now offloaded from the event loop.This directly addresses blocking in
get_downloadableand matches the intended async behavior.Based on learnings:
Applies to streamrip/**/*.py : Use async/await for asynchronous operations instead of blocking I/O.Also applies to: 190-190
.jules/bolt.md (1)
5-7: Great journal entry—clear root cause and concrete remediation.This accurately documents the event-loop blocking issue and the exact
to_threadfix points for future maintainers.
💡 What: Wrapped synchronous
deezer-pythoncalls (client.gw.get_trackandclient.get_track_url) inawait asyncio.to_thread(...)withinDeezerClient.get_downloadable.🎯 Why:
get_downloadablewas executing synchronous network I/O calls directly on theasyncioevent loop. This blocks the main thread and severely degrades performance when resolving and downloading multiple tracks concurrently (e.g., from an album or playlist).📊 Impact: Expected to significantly reduce total download time for Deezer albums/playlists by allowing proper concurrent processing of metadata fetches without blocking the event loop.
🔬 Measurement: Can be verified by downloading a large Deezer playlist and observing a reduction in overall wall-clock time and smoother concurrent progress updates compared to before. Also added a journal entry in
.jules/bolt.mdrecording this learning.PR created automatically by Jules for task 11883376257823855673 started by @davidjuarezdev