Skip to content

refactor(bot): split queueManipulation.ts into focused modules - #807

Merged
LucasSantana-Dev merged 8 commits into
mainfrom
refactor/queue-manipulation-split
May 5, 2026
Merged

LucasSantana-Dev merged 8 commits into
mainfrom
refactor/queue-manipulation-split

Conversation

@LucasSantana-Dev

@LucasSantana-Dev LucasSantana-Dev commented May 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Extracted 961-LOC hotspot into four focused modules, leaving a thin ~90-LOC barrel
  • trackNormalization.ts — normalizeTrackKey, normalizeText, genre family penalty utilities
  • queueEditOps.ts — clearQueue, shuffleQueue, smartShuffleQueue, removeTrackFromQueue, moveTrackInQueue, extractSpotifyTrackId, markAsAutoplayTrack, moveUserTrackToPriority, blendAutoplayTracks
  • candidateFallback.ts — collectBroadFallbackCandidates, collectGenreCandidates, enrichWithAudioFeatures, interleaveByArtist, CandidateContext
  • queueRescue.ts — rescueQueue, buildVcContributionWeights, getHistoryTracks, QueueRescueResult, RescueQueueOptions
  • queueManipulation.ts is now a pure re-export barrel plus getTrackAudioFeatures — all existing public exports preserved for backward compatibility

Test plan

  • tsc --noEmit — zero errors
  • Full Jest suite — 2865/2865 pass
  • CI green

Summary by CodeRabbit

New Features

  • Enhanced autoplay with genre-aware candidate selection and audio feature-based scoring for smarter recommendations
  • Smart queue shuffling that ensures fair distribution of music from different requesters
  • Automatic queue repair system to recover from broken or unresolved tracks
  • Improved recommendation engine factoring in mood, artist preferences, and listening history

Extract four focused modules from the 961-line hotspot:
- trackNormalization.ts: normalizeTrackKey, normalizeText, genre family utilities
- queueEditOps.ts: clearQueue, shuffleQueue, smartShuffleQueue, removeTrackFromQueue,
  moveTrackInQueue, extractSpotifyTrackId, markAsAutoplayTrack, moveUserTrackToPriority,
  blendAutoplayTracks
- candidateFallback.ts: collectBroadFallbackCandidates, collectGenreCandidates,
  enrichWithAudioFeatures, interleaveByArtist, CandidateContext
- queueRescue.ts: rescueQueue, buildVcContributionWeights, getHistoryTracks,
  QueueRescueResult, RescueQueueOptions

queueManipulation.ts becomes a thin barrel (~90 LOC) that re-exports everything
plus retains getTrackAudioFeatures. All existing exports preserved for backward
compatibility. 2865 tests pass, tsc --noEmit clean.
@vercel

vercel Bot commented May 5, 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 May 5, 2026 7:35pm

Request Review

@coderabbitai

coderabbitai Bot commented May 5, 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 15 minutes and 25 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ 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: aabf8264-eca1-4822-a47e-9c4a24b280b9

📥 Commits

Reviewing files that changed from the base of the PR and between cf2c762 and ad04343.

📒 Files selected for processing (8)
  • packages/bot/src/utils/music/candidateFallback.spec.ts
  • packages/bot/src/utils/music/candidateFallback.ts
  • packages/bot/src/utils/music/queueEditOps.spec.ts
  • packages/bot/src/utils/music/queueEditOps.ts
  • packages/bot/src/utils/music/queueRescue.spec.ts
  • packages/bot/src/utils/music/trackNormalization.spec.ts
  • packages/bot/src/utils/music/trackNormalization.ts
  • sonar-project.properties
📝 Walkthrough

Walkthrough

This PR refactors and expands the music autoplay and queue-management system by introducing four new modular utilities—track normalization, queue operations, queue rescue, and candidate fallback collection—while converting the main queueManipulation module into a barrel that re-exports these components. The changes implement Spotify/Last.fm-driven candidate collection, audio-feature enrichment, queue-repair with probing, and fairness-aware queue shuffling.

Changes

Music Autoplay & Queue System Reorganization

Layer / File(s) Summary
Data Shape & Classification
packages/bot/src/utils/music/trackNormalization.ts
Defines track normalization (title/author cleanup, composite keys) and genre-family classification with penalty scoring based on genre overlap.
Autoplay Candidate Infrastructure
packages/bot/src/utils/music/candidateFallback.ts
Implements Spotify-based broad fallback search, Last.fm genre-tag candidate collection, and candidate enrichment via Spotify audio features with energy/valence adjustments and genre-family penalties.
Queue Operations & State Management
packages/bot/src/utils/music/queueEditOps.ts
Provides queue manipulation (clear, shuffle, smart-shuffle with per-requester fairness scoring, track removal/movement) and autoplay metadata marking with non-configurable property handling.
Queue Health & Repair
packages/bot/src/utils/music/queueRescue.ts
Introduces queue rescue with optional resolvability probing via timeout, playability filtering, selective replenishment, and history-based contribution weight computation for VC members.
Module Reorganization
packages/bot/src/utils/music/queueManipulation.ts
Converts to a barrel module with explicit and wildcard re-exports of normalization, queue operations, candidate collection, and queue rescue functionality.

Sequence Diagrams

sequenceDiagram
    participant Client
    participant Queue as discord-player Queue
    participant Spotify
    participant LastFm as Last.fm
    participant Cache as Audio Feature Cache

    rect rgba(220, 100, 100, 0.5)
    Note over Client,Cache: Broad Fallback Candidate Collection
    Client->>Spotify: Search by current artist
    Spotify-->>Client: Results filtered by duration & dislike threshold
    Client->>Client: Compute recommendation score
    Client->>Queue: Upsert candidates with "artist fallback" reason
    end

    rect rgba(100, 150, 220, 0.5)
    Note over Client,Cache: Genre Candidate Collection
    Client->>LastFm: Fetch top tags for seed tracks
    LastFm-->>Client: Tag list (capped)
    Client->>Spotify: Search top tracks per genre tag
    Spotify-->>Client: Results
    Client->>Queue: Add genre-derived candidates with boost
    end

    rect rgba(100, 220, 100, 0.5)
    Note over Client,Cache: Audio Feature Enrichment
    Client->>Spotify: Request access token
    Spotify-->>Client: Token
    Client->>Spotify: Batch fetch audio features by Spotify track ID
    Spotify-->>Cache: Store energy, valence, popularity
    Client->>Client: Adjust candidate scores by feature deltas & genre family penalty
    Client->>Queue: Update candidates sorted by descending score
    end
Loading
sequenceDiagram
    participant Client
    participant Queue as discord-player Queue
    participant SearchEngine
    participant Replenish

    rect rgba(180, 140, 220, 0.5)
    Note over Client,Replenish: Queue Rescue & Repair
    Client->>Queue: Filter tracks with url, title, author
    alt Optional Probing
    Client->>SearchEngine: Probe resolvability with timeout
    SearchEngine-->>Client: Success or timeout
    end
    Client->>Queue: Clear queue
    Client->>Queue: Re-add kept tracks
    alt Below Refill Threshold & Current Track Exists
    Client->>Replenish: Call replenishQueue(queue)
    Replenish->>Queue: Add candidates until full
    end
    Client-->>Client: Return removed/kept/added counts
    end
Loading
sequenceDiagram
    participant User
    participant Queue as discord-player Queue
    participant Autoplay as Autoplay System

    rect rgba(220, 180, 100, 0.5)
    Note over User,Autoplay: Autoplay Track Priority Management
    User->>Queue: Append track to queue
    Autoplay->>Queue: Mark track with metadata (isAutoplay, reason)
    alt moveUserTrackToPriority Invoked
    Autoplay->>Queue: Find user track by URL
    Autoplay->>Queue: Determine first autoplay position
    Autoplay->>Queue: Remove user track & re-insert at priority
    end
    Autoplay->>Queue: Query queue state
    alt blendAutoplayTracks Invoked
    Autoplay->>Queue: Filter autoplay tracks by blendRatio
    Autoplay->>Queue: Remove excess autoplay tracks
    Autoplay->>Queue: Replenish queue with new candidates
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • LucasSantana-Dev/Lucky#569: Adds normalizeTrackKey usage and title-level deduplication in candidate selection helpers that depend on the trackNormalization module introduced in this PR.
  • LucasSantana-Dev/Lucky#578: Modifies autoplay candidate scoring and collection to thread weighted like/dislike feedback and session-mood signals through the same candidate collection pipeline.
  • LucasSantana-Dev/Lucky#639: Introduces genre-family mapping and penalty logic (calculateGenreFamilyPenalty, getGenreFamilies) and enhances enrichWithAudioFeatures for genre-based score adjustments.

Suggested labels

bot, size/xl

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactoring work: splitting a large queueManipulation.ts module into four focused modules (trackNormalization, queueEditOps, candidateFallback, queueRescue).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 refactor/queue-manipulation-split

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.

coderabbitai[bot]
coderabbitai Bot previously requested changes May 5, 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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/bot/src/utils/music/candidateFallback.ts`:
- Around line 151-162: The genre fallback loop currently lets exceptions from
getTagTopTracks and searchLastFmQuery abort the whole pass; wrap the call to
getTagTopTracks(tag, ...) in a try/catch and, on error, log/debug the failure
and continue to the next tag, and likewise wrap each searchLastFmQuery(...) call
in its own try/catch so a failed search skips that seed and continues adding
other candidates via addGenreTrackCandidate(track, tag, ctx); ensure the catches
do not rethrow and preserve the existing flow that checks ctx.candidates size.
- Around line 94-142: The loop over fallbackQueries currently returns early when
any query yields candidates (the `if (candidates.size > 0) return`), which
prevents later queries like the "artist popular" fallback from running and
underfills the buffer; remove that early return and instead let the loop
continue collecting candidates from all fallback queries (or replace the return
with a proper cap check such as stopping only when candidates.size reaches the
intended buffer/capacity like SEARCH_RESULTS_LIMIT or your configured fallback
buffer size), so update the code around the `for (const query of
fallbackQueries)` loop and the `candidates` collection logic to accumulate
across queries rather than exiting on the first non-empty result.

In `@packages/bot/src/utils/music/queueEditOps.ts`:
- Around line 214-250: The code finds the queued item by URL (tracks and
trackIndex) but calls queue.node.remove(track) and later reinserts the caller's
track; replace uses of the caller's track with the actual queued item
(tracks[trackIndex]) for removal and when re-adding/ inserting (use the
matchedQueueItem / tracks[trackIndex] for remove, queue.addTrack and
queue.insertTrack) so instance identity matches the queue's stored Track; ensure
remaining/newFirstAutoplayIndex logic continues to operate on
queue.tracks.toArray() as shown.

In `@packages/bot/src/utils/music/trackNormalization.ts`:
- Around line 20-24: normalizeText() currently strips all non-ASCII chars which
collapses accented and non-Latin names into the same key; update normalizeText
(used by normalizeTrackKey) to preserve Unicode letters/numbers and normalize
Unicode form: first call value?.normalize('NFKC') (or 'NFC'), then use
toLowerCase(), and replace runs of characters that are not Unicode letters or
numbers using a Unicode-aware regex like /[^\p{L}\p{N}]+/gu to remove only
punctuation/whitespace while keeping accented and non-Latin scripts; keep the
final .trim() so dedupe/like/dislike and audio-feature caching work correctly.
🪄 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: 2c244ad4-110d-400f-84b6-054392c14a47

📥 Commits

Reviewing files that changed from the base of the PR and between 5809ca5 and cf2c762.

📒 Files selected for processing (5)
  • packages/bot/src/utils/music/candidateFallback.ts
  • packages/bot/src/utils/music/queueEditOps.ts
  • packages/bot/src/utils/music/queueManipulation.ts
  • packages/bot/src/utils/music/queueRescue.ts
  • packages/bot/src/utils/music/trackNormalization.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: Quality Gates
  • GitHub Check: SonarCloud Scan

Comment thread packages/bot/src/utils/music/candidateFallback.ts
Comment thread packages/bot/src/utils/music/candidateFallback.ts
Comment thread packages/bot/src/utils/music/queueEditOps.ts
Comment thread packages/bot/src/utils/music/trackNormalization.ts
Covers trackNormalization, queueEditOps, queueRescue, candidateFallback
(84 new tests) to satisfy SonarCloud new-code coverage gate on PR #807.
…dates tests and enrichWithAudioFeatures branch coverage
…ks and continue paths

Adds 5 new tests to reach full coverage on the uncovered lines:
- line 115: continue in collectBroadFallbackCandidates when shouldIncludeCandidate=false
- line 176: catch(() => null) when getValidAccessToken throws
- line 201: catch(() => []) when getArtistGenres throws for currentArtistName
- line 224: catch(() => []) when getArtistGenres throws per-track
…y, unicode normalization

- candidateFallback: remove early-return that prevented artist-popular fallback
  from running; wrap getTagTopTracks/searchLastFmQuery in per-seed try/catch so
  a single failed seed skips rather than aborting the whole genre pass
- queueEditOps: use tracks[trackIndex] (queue-resident instance) instead of the
  caller's track reference for remove/addTrack/insertTrack in moveUserTrackToPriority
- trackNormalization: normalizeText now calls .normalize('NFKC') and uses
  Unicode-aware /[^\p{L}\p{N}]+/gu so accented and non-Latin scripts are
  preserved instead of collapsed; update spec accordingly
@sonarqubecloud

sonarqubecloud Bot commented May 5, 2026

Copy link
Copy Markdown

@LucasSantana-Dev
LucasSantana-Dev merged commit d188863 into main May 5, 2026
12 checks passed
LucasSantana-Dev added a commit that referenced this pull request May 13, 2026
* refactor(bot): split queueManipulation.ts (961 LOC) into focused modules

Extract four focused modules from the 961-line hotspot:
- trackNormalization.ts: normalizeTrackKey, normalizeText, genre family utilities
- queueEditOps.ts: clearQueue, shuffleQueue, smartShuffleQueue, removeTrackFromQueue,
  moveTrackInQueue, extractSpotifyTrackId, markAsAutoplayTrack, moveUserTrackToPriority,
  blendAutoplayTracks
- candidateFallback.ts: collectBroadFallbackCandidates, collectGenreCandidates,
  enrichWithAudioFeatures, interleaveByArtist, CandidateContext
- queueRescue.ts: rescueQueue, buildVcContributionWeights, getHistoryTracks,
  QueueRescueResult, RescueQueueOptions

queueManipulation.ts becomes a thin barrel (~90 LOC) that re-exports everything
plus retains getTrackAudioFeatures. All existing exports preserved for backward
compatibility. 2865 tests pass, tsc --noEmit clean.

* test(bot): add specs for queueManipulation refactor modules

Covers trackNormalization, queueEditOps, queueRescue, candidateFallback
(84 new tests) to satisfy SonarCloud new-code coverage gate on PR #807.

* test(bot): improve candidateFallback coverage — add collectGenreCandidates tests and enrichWithAudioFeatures branch coverage

* test(bot): cover remaining candidateFallback branches — catch callbacks and continue paths

Adds 5 new tests to reach full coverage on the uncovered lines:
- line 115: continue in collectBroadFallbackCandidates when shouldIncludeCandidate=false
- line 176: catch(() => null) when getValidAccessToken throws
- line 201: catch(() => []) when getArtistGenres throws for currentArtistName
- line 224: catch(() => []) when getArtistGenres throws per-track

* test(bot): fix branch coverage gaps in queueEditOps and queueRescue specs

* chore: exclude split queueManipulation files from CPD analysis

* fix: address CodeRabbit review — genre error isolation, queue identity, unicode normalization

- candidateFallback: remove early-return that prevented artist-popular fallback
  from running; wrap getTagTopTracks/searchLastFmQuery in per-seed try/catch so
  a single failed seed skips rather than aborting the whole genre pass
- queueEditOps: use tracks[trackIndex] (queue-resident instance) instead of the
  caller's track reference for remove/addTrack/insertTrack in moveUserTrackToPriority
- trackNormalization: normalizeText now calls .normalize('NFKC') and uses
  Unicode-aware /[^\p{L}\p{N}]+/gu so accented and non-Latin scripts are
  preserved instead of collapsed; update spec accordingly
@LucasSantana-Dev
LucasSantana-Dev deleted the refactor/queue-manipulation-split branch May 14, 2026 02:02

This branch was successfully deployed

1 active deployment
Preview — ad04343d Deployed May 5, 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