Skip to content

fix(bot): Spotify-first provider order + Queue Error recovery - #581

Merged
LucasSantana-Dev merged 4 commits into
mainfrom
fix/spotify-first-provider
Apr 13, 2026
Merged

LucasSantana-Dev merged 4 commits into
mainfrom
fix/spotify-first-provider

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented Apr 13, 2026 •

Copy link
Copy Markdown
Owner

Problem

  1. YouTube default provider — text searches always resolved to YouTube even though Spotify produces cleaner metadata with far fewer duplicate entries. The handleSearchQuery path hardcoded handleYouTubeSearch.

  2. Queue Error not recovering — when createResilientStream exhausted all fallbacks it threw Bridge exhausted: no stream for "...". The isStreamExtractionError check in errorHandlers.ts didn't match that message, so no YouTube retry was attempted and Discord showed the raw "Queue Error" embed.

Fixes

playerFactory.ts — extractor registration order

Before: loadMulti(DefaultExtractors) → SoundCloud, Spotify, Apple Music, Vimeo, Attachments → YouTube (async, last)

After: Spotify → YouTube → SoundCloud → Apple Music → Vimeo → Attachments

  • SpotifyExtractor registered first with SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET so spsearch: queries resolve
  • YouTube stays second with the resilient yt-dlp bridge
  • Other extractors registered in sequence after YouTube

processor.ts + youtubeHandler.ts — Spotify-first search

Text query flow (/play some song name):

  1. Try QueryType.SPOTIFY_SEARCH — silently returns success: false on no results
  2. Fall back to YouTube search if Spotify finds nothing

errorHandlers.ts — Queue Error fix

Added error.message.includes('Bridge exhausted') to isStreamExtractionError so bridge exhaustion triggers the same YouTube retry + channel notification flow as other stream failures.

Summary by CodeRabbit

  • New Features

    • Music search now attempts Spotify lookups before falling back to YouTube, providing faster results for Spotify-indexed tracks.
  • Bug Fixes

    • Improved stream extraction error detection to handle additional failure patterns, enabling better recovery and fallback behavior.
  • Tests

    • Added comprehensive test coverage for Spotify and YouTube search handlers, plus music source initialization.

Extractor registration order: Spotify → YouTube → SoundCloud → others.
SpotifyExtractor gets SPOTIFY_CLIENT_ID/SECRET credentials so spsearch
queries work. Text searches now try Spotify before YouTube falling back.

Also fixes Queue Error not triggering recovery: adds 'Bridge exhausted'
to the isStreamExtractionError check so the YouTube retry kicks in
instead of showing a raw discord-player Queue Error embed.
@vercel

vercel Bot commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lucky Ready Ready Preview, Comment Apr 13, 2026 1:33am

@coderabbitai

coderabbitai Bot commented Apr 13, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@LucasSantana-Dev has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 49 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 17 minutes and 49 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ec5ff16a-2845-4625-b519-9402ff49b911

📥 Commits

Reviewing files that changed from the base of the PR and between 5ba1f63 and 376e54a.

📒 Files selected for processing (2)
  • packages/bot/src/handlers/player/playerFactory.ts
  • packages/bot/tests/handlers/player/playerFactory.test.ts
📝 Walkthrough

Walkthrough

This PR implements Spotify-first search for the play command by adding a handleSpotifySearch function to query Spotify before falling back to YouTube, refactors extractor registration in the player factory to be async and sequential with dedicated Spotify registration, and extends stream extraction error detection to include "Bridge exhausted" errors.

Changes

Cohort / File(s) Summary
Play Command Spotify-First Search
packages/bot/src/functions/music/commands/play/processor.ts, packages/bot/src/functions/music/commands/play/youtubeHandler.ts
Added handleSpotifySearch function that queries Spotify using QueryType.SPOTIFY_SEARCH and gracefully returns { success: false } on failure. Integrated into PlayCommandProcessor.handleSearchQuery to attempt Spotify search before falling back to YouTube search.
Extractor Registration Refactoring
packages/bot/src/handlers/player/playerFactory.ts
Refactored registerExtractors from synchronous loadMulti to async sequential registration via registerExtractorsInOrder. Added dedicated Spotify extractor registration with fallback warning, then registers SoundCloud, Apple Music, Vimeo, and Attachments extractors individually with per-extractor error handling.
Stream Extraction Error Detection
packages/bot/src/handlers/player/errorHandlers.ts
Extended isStreamExtractionError pattern matching to include "Bridge exhausted" errors, expanding stream extraction failure detection alongside existing patterns.
Play Command & Search Tests
packages/bot/tests/functions/music/commands/play/processor.test.ts, packages/bot/tests/functions/music/commands/play/youtubeHandler.test.ts
Added test suites covering Spotify search success/failure cases, fallback to YouTube, and handleSpotifySearch function behavior with mocked Discord Player and logging utilities.
Extractor Registration Tests
packages/bot/tests/handlers/player/playerFactory.test.ts
Updated and extended tests for new extractor registration flow, verifying Spotify registration as first extractor, warning logs on registration failures, and YouTube extractor identification via createStream option.

Sequence Diagrams

sequenceDiagram
    actor User
    participant PlayCommandProcessor
    participant handleSpotifySearch
    participant Player as Discord Player
    participant SpotifyAPI
    participant handleYouTubeSearch

    User->>PlayCommandProcessor: play query
    PlayCommandProcessor->>handleSpotifySearch: search(query)
    handleSpotifySearch->>Player: search(query, {searchEngine: SPOTIFY_SEARCH})
    Player->>SpotifyAPI: fetch tracks
    SpotifyAPI-->>Player: no results
    Player-->>handleSpotifySearch: empty SearchResult
    handleSpotifySearch-->>PlayCommandProcessor: {success: false}
    PlayCommandProcessor->>handleYouTubeSearch: search(query)
    handleYouTubeSearch->>Player: search(query, {searchEngine: YOUTUBE_SEARCH})
    Player->>SpotifyAPI: fetch tracks
    SpotifyAPI-->>Player: tracks found
    Player-->>handleYouTubeSearch: SearchResult
    handleYouTubeSearch-->>PlayCommandProcessor: {success: true, tracks}
    PlayCommandProcessor-->>User: queue tracks
Loading
sequenceDiagram
    participant Bot as Bot Initialization
    participant registerExtractorsInOrder
    participant registerSpotifyExtractor
    participant registerRemainingExtractors
    participant Player as Discord Player<br/>Extractors

    Bot->>registerExtractorsInOrder: async start
    registerExtractorsInOrder->>registerSpotifyExtractor: register Spotify
    registerSpotifyExtractor->>Player: register(SpotifyExtractor)
    alt Spotify registration succeeds
        Player-->>registerSpotifyExtractor: registered
        registerSpotifyExtractor-->>registerExtractorsInOrder: complete
    else Spotify registration fails
        Player--xregisterSpotifyExtractor: error
        registerSpotifyExtractor-->>registerExtractorsInOrder: warn & continue
    end
    registerExtractorsInOrder->>registerRemainingExtractors: register others
    registerRemainingExtractors->>Player: register(SoundCloud)
    registerRemainingExtractors->>Player: register(AppleMusic)
    registerRemainingExtractors->>Player: register(Vimeo)
    registerRemainingExtractors->>Player: register(Attachments)
    Player-->>registerRemainingExtractors: all registered
    registerRemainingExtractors-->>registerExtractorsInOrder: complete
    registerExtractorsInOrder-->>Bot: ready
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • PR #550 — Adds "Bridge exhausted" error throwing in stream creation, which pairs with this PR's stream extraction error detection expansion.
  • PR #493 — Implements Spotify-first search behavior in play/autoplay commands with QueryType.SPOTIFY_SEARCH priority, directly aligned with this PR's handleSpotifySearch integration.
  • PR #427 — Refactors playerFactory extractor registration to async sequential initialization, matching the same architectural shift in this PR's registerExtractorsInOrder implementation.
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two main changes: Spotify-first provider order and queue error recovery for bridge exhaustion.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/spotify-first-provider

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- handleSpotifySearch: success, no results, error paths
- PlayCommandProcessor.handleSearchQuery: spotify-win and yt-fallback
- playerFactory: spotify extractor error path, remaining extractor error path
coderabbitai[bot]
coderabbitai Bot previously requested changes Apr 13, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bot/src/handlers/player/playerFactory.ts`:
- Around line 44-48: The player is being exposed before extractor registration
finishes because registerExtractors calls registerExtractorsInOrder() without
awaiting; change the flow so createPlayer (or the function that returns the
Player) awaits extractor bootstrap instead of fire-and-forget: update
registerExtractors to return the Promise from registerExtractorsInOrder (remove
the .catch swallow), and in createPlayer (or the factory that returns the
Player) await registerExtractors(player) and propagate errors so the player is
only returned after registerExtractorsInOrder completes successfully; keep error
logging but do not allow a partially-initialized extractor registry to be
visible to command handlers.
- Around line 68-74: registerSpotifyExtractor and registerRemainingExtractors
currently ignore the return value of player.extractors.register (which can be
null on failure); change both functions to capture the result of await
player.extractors.register(...) into a variable, check for null, and only call
infoLog('Registered ...') when the result is non-null; if null, log an error (or
warning) indicating registration failed and include the extractor name (e.g.,
SpotifyExtractor or relevant extractor identifiers) so behavior matches the
null-check logic already used in loadYoutubeExtractor.

In `@packages/bot/tests/handlers/player/playerFactory.test.ts`:
- Around line 160-181: The test's current check uses typeof ExtractorClass ===
'function', which makes hasSpotify true for any extractor; update the loop and
assertions to explicitly detect the Spotify extractor by its name rather than
any function: change the hasSpotify check inside the loop that inspects
player.extractors.register.mock.calls to only consider the ExtractorClass name
=== 'MockSpotifyExtractor' (remove the typeof branch), wait until that specific
registration appears, then assert that the first call to
player.extractors.register (firstCall) is defined and that firstCall[0].name ===
'MockSpotifyExtractor' to guarantee Spotify was registered first.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a1b9ae3a-9630-4883-a579-4a4b689f3e2d

📥 Commits

Reviewing files that changed from the base of the PR and between 0277b3a and 5ba1f63.

📒 Files selected for processing (7)
  • packages/bot/src/functions/music/commands/play/processor.ts
  • packages/bot/src/functions/music/commands/play/youtubeHandler.ts
  • packages/bot/src/handlers/player/errorHandlers.ts
  • packages/bot/src/handlers/player/playerFactory.ts
  • packages/bot/tests/functions/music/commands/play/processor.test.ts
  • packages/bot/tests/functions/music/commands/play/youtubeHandler.test.ts
  • packages/bot/tests/handlers/player/playerFactory.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: SonarCloud Scan
  • GitHub Check: Quality Gates
🔇 Additional comments (1)
packages/bot/src/handlers/player/errorHandlers.ts (1)

321-326: Good fix for bridge-exhaustion recovery path.

Adding error.message.includes('Bridge exhausted') correctly classifies that failure and reuses the existing recovery flow instead of exposing a raw Queue Error.

Comment thread packages/bot/src/handlers/player/playerFactory.ts
Comment thread packages/bot/src/handlers/player/playerFactory.ts
Comment thread packages/bot/tests/handlers/player/playerFactory.test.ts
- registerSpotifyExtractor and registerRemainingExtractors now warn
  when player.extractors.register() returns null (mirrors existing
  null-check in loadYoutubeExtractor)
- fix overly-broad SpotifyExtractor test detection (was matching any
  function, now checks MockSpotifyExtractor name)
- add null-return path tests for both functions
@github-actions github-actions Bot added size/xl and removed size/l labels Apr 13, 2026
@LucasSantana-Dev
LucasSantana-Dev dismissed coderabbitai[bot]’s stale review April 13, 2026 01:32

Fixed: null-check added to extractor registration functions; test updated to check MockSpotifyExtractor name specifically.

@sonarqubecloud

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit c8bbdbe into main Apr 13, 2026
12 checks passed
@LucasSantana-Dev
LucasSantana-Dev deleted the fix/spotify-first-provider branch April 13, 2026 01:37
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
* fix(bot): Spotify-first provider order + Queue Error recovery

Extractor registration order: Spotify → YouTube → SoundCloud → others.
SpotifyExtractor gets SPOTIFY_CLIENT_ID/SECRET credentials so spsearch
queries work. Text searches now try Spotify before YouTube falling back.

Also fixes Queue Error not triggering recovery: adds 'Bridge exhausted'
to the isStreamExtractionError check so the YouTube retry kicks in
instead of showing a raw discord-player Queue Error embed.

* test(bot): add coverage for spotify-first provider changes

- handleSpotifySearch: success, no results, error paths
- PlayCommandProcessor.handleSearchQuery: spotify-win and yt-fallback
- playerFactory: spotify extractor error path, remaining extractor error path

* fix(bot): check null return from extractor registration

- registerSpotifyExtractor and registerRemainingExtractors now warn
  when player.extractors.register() returns null (mirrors existing
  null-check in loadYoutubeExtractor)
- fix overly-broad SpotifyExtractor test detection (was matching any
  function, now checks MockSpotifyExtractor name)
- add null-return path tests for both functions

This branch was successfully deployed

1 active deployment
Preview — 376e54a4 Deployed Apr 13, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant