Skip to content

Commit ac0ec3c

Browse files
committed
feat: increase song pool increase threshold and add a cutoff
1 parent 48af53e commit ac0ec3c

2 files changed

Lines changed: 31 additions & 27 deletions

File tree

app/lib/generateSeedPlaylist.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export async function generateSeedPlaylist(
3939
// 1. Process seeds to ensure they are in DB & have embeddings
4040
const seedSpotifyIds = seeds.map((s) => s.id);
4141

42-
const processedSeeds = await Promise.all(
42+
await Promise.all(
4343
seeds.map((seed) =>
4444
processSong({
4545
id: seed.id,
@@ -49,13 +49,10 @@ export async function generateSeedPlaylist(
4949
}),
5050
),
5151
);
52-
53-
console.log('Processed seeds:', processedSeeds);
5452

5553
// 2. Fetch seed embeddings from DB
5654
const rawEmbeddings = await getSongEmbeddings(seedSpotifyIds);
5755

58-
console.log('Raw embeddings:', rawEmbeddings.slice(0, 5));
5956
// PgVector from Prisma raw queries usually returns strings like "[0.1, 0.2, ...]"
6057
const seedEmbeddings: number[][] = rawEmbeddings
6158
.map((row: any) => {
@@ -102,7 +99,7 @@ export async function generateSeedPlaylist(
10299
const albums = await getEveryAlbum(finalList);
103100

104101
console.log('Getting tracks for albums...');
105-
const aiTracks = (await getAllTracks(albums as string[], 3, true)) as any[]; // Need more tracks to filter down
102+
const aiTracks = (await getAllTracks(albums as string[], 5, true)) as any[]; // Need more tracks to filter down
106103

107104
console.log('AI Tracks:', aiTracks);
108105

@@ -118,9 +115,12 @@ export async function generateSeedPlaylist(
118115
const limit = pLimit(15);
119116

120117
// 0.6 is a good starting point, but you can tune this
121-
const THRESHOLD = 0.6
118+
const THRESHOLD = 0.75
122119

123120
// we want to avoid low quality recommendations so we check each song against all seeds(selected songs) put that into an array and sort by the number of seeds that match the treshold then return the top 100
121+
// we also want to avoid songs that are too far in similarity to the seeds so we set a cutoff of 0.2 below the threshold to give extra grace
122+
const CUTOFF = THRESHOLD - 0.2
123+
124124
const scoredTracks = await Promise.all(
125125
aiTracks.map(track => limit(async () => {
126126
try {
@@ -137,9 +137,14 @@ const scoredTracks = await Promise.all(
137137
calculateCosineSimilarity(emb, seedEmb)
138138
)
139139

140+
const maxScore = Math.max(...scores)
141+
142+
// cut off songs that don't even come close
143+
if (maxScore < CUTOFF) return null
144+
140145
const thresholdHits = scores.filter(s => s >= THRESHOLD).length
141146

142-
return { uri: track.uri, thresholdHits, maxScore: Math.max(...scores) }
147+
return { uri: track.uri, thresholdHits, maxScore }
143148

144149
} catch (e) {
145150
return null
@@ -178,4 +183,6 @@ fallbackTracksUris = scoredTracks
178183
console.error('Error generating seed playlist:', error);
179184
return { tracks: [], error: error?.message || 'Unknown error' };
180185
}
181-
}
186+
}
187+
188+
// TODO: use the same algorithm for the songs coming from the db - they should be scored and sorted as well

app/lib/utils.ts

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,23 @@ export const decrypt = (encryptedText: string): string => {
2020

2121
return originalText;
2222
};
23-
export const getEveryAlbum = async (artists: string[]) => {
24-
console.log('Fetching albums for artists:', artists);
25-
const artistAlbums = artists.map((item) =>
26-
getArtistsAlbums(item, artists.length),
27-
);
28-
const albumArray = await Promise.all(artistAlbums);
29-
console.log(
30-
'Album results (might contain errors):',
31-
albumArray.map((a) => {
32-
if (Array.isArray(a)) return `Count: ${a.length}`;
33-
console.log('Non-array album result encountered:', a);
34-
return 'ERROR/EMPTY';
35-
}),
36-
);
37-
const albums = [...new Set(albumArray.flat())];
38-
const stringAlbums = albums.filter((item) => typeof item === 'string');
39-
console.log('Final unique album IDs:', stringAlbums.length);
4023

41-
return stringAlbums;
42-
};
24+
/**
25+
* Fetches all albums for a list of artists, shuffles them, and returns unique album IDs.
26+
* @param artists - Array of artist names
27+
* @returns Array of unique album IDs
28+
*/
29+
export const getEveryAlbum = async (artists: string[]) => {
30+
const shuffled = [...artists].sort(() => Math.random() - 0.5)
31+
const artistAlbums = shuffled.map((item) =>
32+
getArtistsAlbums(item, shuffled.length),
33+
)
34+
const albumArray = await Promise.all(artistAlbums)
35+
const albums = [...new Set(albumArray.flat())].sort(() => Math.random() - 0.5)
36+
const stringAlbums = albums.filter((item) => typeof item === 'string')
37+
38+
return stringAlbums
39+
}
4340

4441
export const getAllTracks = async (
4542
albums: string[],

0 commit comments

Comments
 (0)