Skip to content

Commit

Permalink
Implement recommendation seed: allow recommendations based on any lis…
Browse files Browse the repository at this point in the history
…t of album file names
  • Loading branch information
shedrachokonofua committed Jul 29, 2024
1 parent 67eea30 commit f886b33
Show file tree
Hide file tree
Showing 18 changed files with 466 additions and 327 deletions.
3 changes: 3 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ tasks:
cmds:
- task: "web:proto"
- task: "extension:proto"
- task: "discord-connector:proto"
- task: "mandolin:proto"
- task: "graph:proto"

"build":
cmds:
Expand Down
20 changes: 12 additions & 8 deletions connector/discord/src/lute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export interface RecommendationSettings {

export const recommendAlbums = async (settings: RecommendationSettings) => {
const request = new core.RecommendAlbumsRequest({
profileId: settings.profileId,
seed: new core.AlbumRecommendationSeed({
profileId: settings.profileId,
}),
recommendationSettings: new core.AlbumRecommendationSettings({
count: settings.limit,
includePrimaryGenres: settings.filters.includePrimaryGenres,
Expand Down Expand Up @@ -81,7 +83,7 @@ export interface Pagination {
const toPagination = (pagination?: Pagination) => {
return new core.SearchPagination({
offset: pagination?.offset,
limit: pagination?.limit || 10
limit: pagination?.limit || 10,
});
};
export interface AlbumSearchParams {
Expand Down Expand Up @@ -170,10 +172,12 @@ const toArtistSearchQuery = (query?: ArtistQuery) => {
excludeSecondaryGenres: query?.excludeSecondaryGenres,
includeCreditRoles: query?.includeCreditRoles,
excludeCreditRoles: query?.excludeCreditRoles,
activeYearsRange: query?.activeYearsRange ? new core.YearRange({
start: query?.activeYearsRange?.[0],
end: query?.activeYearsRange?.[1],
}) : undefined,
activeYearsRange: query?.activeYearsRange
? new core.YearRange({
start: query?.activeYearsRange?.[0],
end: query?.activeYearsRange?.[1],
})
: undefined,
minAlbumCount: query?.minAlbumCount,
});
};
Expand All @@ -184,12 +188,12 @@ export interface ArtistSearchParams {
}

export const searchArtists = async (params: ArtistSearchParams) => {
console.log(params)
console.log(params);
const request = new core.SearchArtistsRequest({
query: toArtistSearchQuery(params.query),
pagination: toPagination(params.pagination),
});
const results = await lute.artists.SearchArtists(request);
console.log(results)
console.log(results);
return results.artists.map((artist) => artist.toObject());
};
7 changes: 6 additions & 1 deletion connector/mandolin/lib/mandolin/lute/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ defmodule Mandolin.Lute.Client do
Lute.RecommendationService.Stub.recommend_albums(
Channel.channel(),
%Lute.RecommendAlbumsRequest{
profile_id: profile_id,
seed: %Lute.AlbumRecommendationSeed{
value: {
:profile_id,
profile_id
}
},
recommendation_settings: filters,
assessment_settings: settings
}
Expand Down
194 changes: 194 additions & 0 deletions core/src/albums/album_collection_summary.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
use super::album_read_model::AlbumReadModel;
use crate::{
files::file_metadata::file_name::FileName,
helpers::{
item_with_factor::{desc_sort_by_factor, ItemWithFactor},
math::median,
},
};
use chrono::Datelike;
use rayon::prelude::*;
use std::{collections::HashMap, iter::repeat};

pub struct AlbumCollectionSummary {
pub average_rating: f32,
pub median_year: u32,
pub artists: Vec<ItemWithFactor>,
pub primary_genres: Vec<ItemWithFactor>,
pub secondary_genres: Vec<ItemWithFactor>,
pub descriptors: Vec<ItemWithFactor>,
pub years: Vec<ItemWithFactor>,
pub decades: Vec<ItemWithFactor>,
pub credit_tags: Vec<ItemWithFactor>,
}

impl AlbumCollectionSummary {
pub fn new(
albums: Vec<AlbumReadModel>,
factor_map: HashMap<FileName, u32>,
) -> AlbumCollectionSummary {
let album_read_models_map = albums
.into_par_iter()
.map(|album_read_model| (album_read_model.file_name.clone(), album_read_model))
.collect::<HashMap<FileName, AlbumReadModel>>();
let mut artists_map: HashMap<String, u32> = HashMap::new();
let mut primary_genres_map: HashMap<String, u32> = HashMap::new();
let mut secondary_genres_map: HashMap<String, u32> = HashMap::new();
let mut descriptors_map: HashMap<String, u32> = HashMap::new();
let mut years_map: HashMap<u32, u32> = HashMap::new();
let mut decades_map: HashMap<u32, u32> = HashMap::new();
let mut credit_tags_map: HashMap<String, u32> = HashMap::new();
let mut ratings: Vec<f32> = Vec::new();

for (file_name, factor) in &factor_map {
let album = album_read_models_map.get(file_name);
if album.is_none() {
continue;
}
let album = album.unwrap();

for artist in &album.artists {
artists_map
.entry(artist.name.clone())
.and_modify(|c| *c += factor)
.or_insert(*factor);
}

for genre in &album.primary_genres {
primary_genres_map
.entry(genre.clone())
.and_modify(|c| *c += factor)
.or_insert(*factor);
}

for genre in &album.secondary_genres {
secondary_genres_map
.entry(genre.clone())
.and_modify(|c| *c += factor)
.or_insert(*factor);
}

for descriptor in &album.descriptors {
descriptors_map
.entry(descriptor.clone())
.and_modify(|c| *c += factor)
.or_insert(*factor);
}

for tag in &album.credit_tags() {
credit_tags_map
.entry(tag.clone())
.and_modify(|c| *c += factor)
.or_insert(*factor);
}

if let Some(release_date) = album.release_date {
let year = release_date.year() as u32;
years_map
.entry(year)
.and_modify(|c| *c += factor)
.or_insert(*factor);
let decade = year - (year % 10);
decades_map
.entry(decade)
.and_modify(|c| *c += factor)
.or_insert(*factor);
}

ratings.append(
&mut repeat(album.rating)
.take(*factor as usize)
.collect::<Vec<_>>(),
);
}

let average_rating = ratings.iter().sum::<f32>() / ratings.len() as f32;

let mut artists = artists_map
.iter()
.map(|(item, factor)| ItemWithFactor {
item: item.clone(),
factor: *factor,
})
.collect::<Vec<ItemWithFactor>>();
desc_sort_by_factor(&mut artists);

let mut years = years_map
.iter()
.map(|(item, factor)| ItemWithFactor {
item: item.to_string(),
factor: *factor,
})
.collect::<Vec<ItemWithFactor>>();
desc_sort_by_factor(&mut years);

let median_year = median(
years
.iter()
.flat_map(|item| {
repeat(item.item.parse::<f32>().unwrap())
.take(item.factor as usize)
.collect::<Vec<_>>()
})
.collect::<Vec<_>>(),
)
.round() as u32;

let mut primary_genres = primary_genres_map
.iter()
.map(|(item, factor)| ItemWithFactor {
item: item.clone(),
factor: *factor,
})
.collect::<Vec<ItemWithFactor>>();
desc_sort_by_factor(&mut primary_genres);

let mut secondary_genres = secondary_genres_map
.iter()
.map(|(item, factor)| ItemWithFactor {
item: item.clone(),
factor: *factor,
})
.collect::<Vec<ItemWithFactor>>();
desc_sort_by_factor(&mut secondary_genres);

let mut descriptors = descriptors_map
.iter()
.map(|(item, factor)| ItemWithFactor {
item: item.clone(),
factor: *factor,
})
.collect::<Vec<ItemWithFactor>>();
desc_sort_by_factor(&mut descriptors);

let mut credit_tags = credit_tags_map
.iter()
.map(|(item, factor)| ItemWithFactor {
item: item.clone(),
factor: *factor,
})
.collect::<Vec<ItemWithFactor>>();
desc_sort_by_factor(&mut credit_tags);

let mut decades = decades_map
.iter()
.map(|(item, factor)| ItemWithFactor {
item: item.to_string(),
factor: *factor,
})
.collect::<Vec<ItemWithFactor>>();
desc_sort_by_factor(&mut decades);

AlbumCollectionSummary {
average_rating,
median_year,
artists,
primary_genres,
secondary_genres,
descriptors,
credit_tags,
years,
decades,
}
}
}
1 change: 1 addition & 0 deletions core/src/albums/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod album_collection_summary;
pub mod album_event_subscribers;
pub mod album_interactor;
pub mod album_read_model;
Expand Down
7 changes: 5 additions & 2 deletions core/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
albums::{
album_interactor::AlbumInteractor, album_repository::AlbumRepository,
album_search_index::AlbumSearchIndex, es_album_search_index::EsAlbumSearchIndex,
album_search_index::AlbumSearchIndex, redis_album_search_index::RedisAlbumSearchIndex,
},
artists::artist_interactor::ArtistInteractor,
crawler::crawler::Crawler,
Expand Down Expand Up @@ -83,7 +83,10 @@ impl ApplicationContext {
Arc::clone(&settings),
Arc::clone(&kv),
));
let album_search_index = Arc::new(EsAlbumSearchIndex::new(Arc::clone(&elasticsearch_client)));
let album_search_index = Arc::new(RedisAlbumSearchIndex::new(
Arc::clone(&redis_connection_pool),
Arc::clone(&embedding_provider_interactor),
));
let spotify_client = Arc::new(SpotifyClient::new(
&settings.spotify.clone(),
Arc::clone(&kv),
Expand Down
Loading

0 comments on commit f886b33

Please sign in to comment.