Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- **frontend shell/sidebar foundation**: strengthened the Lucky sidebar into a clearer guild command center with a more prominent active guild block, explicit readiness state, stronger nav grouping, and fuller active-route treatment while keeping route structure unchanged.
- **autoplay diversity tuning**: autoplay recommendation scoring now penalizes same-source tracks more strongly, helping the queue favor varied sources without changing the existing hard caps that prevent refill starvation.

## [2.6.62] - 2026-04-04

Expand Down
140 changes: 140 additions & 0 deletions packages/bot/src/utils/music/queueManipulation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type QueueMock = Partial<GuildQueue> & {
addTrack: jest.Mock
tracks: { size: number; toArray: jest.Mock }
guild: { id: string }
history?: { tracks: { toArray: jest.Mock } }
}

function createQueueMock(overrides: Partial<QueueMock> = {}): QueueMock {
Expand Down Expand Up @@ -650,6 +651,145 @@ describe('queueManipulation.replenishQueue', () => {
).toContain('similar energy')
})

it('prefers a different-source candidate when scores are otherwise close', async () => {
const queue = createQueueMock({
tracks: {
size: 0,
toArray: jest.fn().mockReturnValue([]),
},
history: {
tracks: {
toArray: jest.fn().mockReturnValue([
{
title: 'History Song 1',
author: 'Same Artist',
url: 'https://example.com/history-1',
},
{
title: 'History Song 2',
author: 'Different Artist',
url: 'https://example.com/history-2',
},
]),
},
},
currentTrack: {
title: 'Alpha Beta Gamma Delta',
author: 'Current Artist',
url: 'https://example.com/current-diversity',
source: 'youtube',
} as unknown as Track,
player: {
search: jest.fn().mockResolvedValue({
tracks: [
{
title: 'Alpha Beta Gamma Delta Remix',
author: 'Same Artist',
url: 'https://example.com/same-source',
source: 'youtube',
},
{
title: 'Unrelated Fresh Cut',
author: 'Different Artist',
url: 'https://example.com/different-source',
source: 'spotify',
},
],
}),
},
})

await replenishQueue(queue as unknown as GuildQueue)

const firstAdded = queue.addTrack.mock.calls[0]?.[0] as Track

expect(firstAdded).toEqual(
expect.objectContaining({
url: 'https://example.com/different-source',
source: 'spotify',
metadata: expect.objectContaining({
recommendationReason:
expect.stringContaining('source variety'),
}),
}),
)
})

it('still selects three tracks when only one source is viable', async () => {
const queue = createQueueMock({
tracks: {
size: 5,
toArray: jest.fn().mockReturnValue([
{
title: 'Queued Song 1',
author: 'Queued Artist 1',
url: 'https://example.com/queued-1',
},
{
title: 'Queued Song 2',
author: 'Queued Artist 2',
url: 'https://example.com/queued-2',
},
{
title: 'Queued Song 3',
author: 'Queued Artist 3',
url: 'https://example.com/queued-3',
},
{
title: 'Queued Song 4',
author: 'Queued Artist 4',
url: 'https://example.com/queued-4',
},
{
title: 'Queued Song 5',
author: 'Queued Artist 5',
url: 'https://example.com/queued-5',
},
]),
},
currentTrack: {
title: 'Current Track',
author: 'Current Artist',
url: 'https://example.com/current-cap',
source: 'youtube',
} as unknown as Track,
player: {
search: jest.fn().mockResolvedValue({
tracks: [
{
title: 'Viable One',
author: 'Artist 1',
url: 'https://example.com/viable-1',
source: 'youtube',
},
{
title: 'Viable Two',
author: 'Artist 2',
url: 'https://example.com/viable-2',
source: 'youtube',
},
{
title: 'Viable Three',
author: 'Artist 3',
url: 'https://example.com/viable-3',
source: 'youtube',
},
{
title: 'Viable Four',
author: 'Artist 4',
url: 'https://example.com/viable-4',
source: 'youtube',
},
],
}),
},
})

await replenishQueue(queue as unknown as GuildQueue)

expect(queue.addTrack).toHaveBeenCalledTimes(3)
})

it('collects lastfm seed tracks and searches for recommendations', async () => {
getLastFmSeedTracksMock.mockResolvedValueOnce([
{ artist: 'Radiohead', title: 'Paranoid Android' },
Expand Down
2 changes: 1 addition & 1 deletion packages/bot/src/utils/music/queueManipulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ function calculateRecommendationScore(
score -= 0.25
}
if (candidate.source === currentTrack.source) {
score -= 0.15
score -= 0.25
} else if (candidate.source) {
reasons.push('source variety')
}
Expand Down