A Wrapped / Replay like for teal.fm and rocksky.app (currently on hiatus)
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

first attempt at musicbrainz lookup

Mia 59a87bd8 654c93dc

+124 -1
+11 -1
src/mbz/init_fts.sql
··· 1 1 PRAGMA create_fts_index('mbz.artist_credit', 'id', 'name'); 2 2 PRAGMA create_fts_index('mbz.recording', 'id', 'name'); 3 - PRAGMA create_fts_index('mbz.release', 'id', 'name', 'comment'); 3 + PRAGMA create_fts_index('mbz.release', 'id', 'name', 'comment'); 4 + 5 + create macro if not exists mbz.fts_release (term, gid) AS CASE 6 + WHEN gid IS NOT NULL THEN 10 7 + ELSE fts_mbz_release.match_bm25 (release.id, term) * if (lower(release.name) = lower(term), 2.5, 1) 8 + END; 9 + 10 + create macro if not exists mbz.fts_recording (term, gid) AS CASE 11 + WHEN gid IS NOT NULL THEN 10 12 + ELSE fts_mbz_recording.match_bm25 (recording.id, term) * if (lower(recording.name) = lower(term), 2.5, 1) 13 + END;
+90
src/mbz/mod.rs
··· 1 + use duckdb::{Connection, params}; 2 + 1 3 mod replica; 2 4 3 5 pub use replica::start_replication; 6 + 7 + #[derive(Debug, Default)] 8 + pub struct FindMbzData<'a> { 9 + pub release_name: Option<&'a str>, 10 + pub release_mbid: Option<&'a str>, 11 + pub release_discrim: Option<&'a str>, 12 + pub recording_mbid: Option<&'a str>, 13 + pub artist_name: Option<&'a str>, 14 + pub track_discrim: Option<&'a str>, 15 + } 16 + 17 + #[derive(Debug)] 18 + pub struct MbzQueryRes { 19 + pub track: String, 20 + pub track_gid: String, 21 + pub release_gid: Option<String>, 22 + pub release: Option<String>, 23 + pub recording_gid: Option<String>, 24 + pub release_group_gid: Option<String>, 25 + pub release_group: Option<String>, 26 + pub artists: Option<String>, 27 + pub debug: String, 28 + } 29 + 30 + #[derive(Debug)] 31 + struct MbzDebug { 32 + release: f64, 33 + recording: f64, 34 + artist: f64, 35 + } 36 + 37 + impl std::fmt::Display for MbzDebug { 38 + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 39 + write!( 40 + f, 41 + "release={:.3};recording={:.3};artist={:.3}", 42 + self.release, self.recording, self.artist 43 + ) 44 + } 45 + } 46 + 47 + pub fn try_find_mbz_data( 48 + conn: &Connection, 49 + track_name: &str, 50 + opts: &FindMbzData, 51 + ) -> duckdb::Result<Option<MbzQueryRes>> { 52 + let mut prep = conn.prepare_cached(include_str!("query.sql"))?; 53 + 54 + let mut rows = prep 55 + .query_map( 56 + params![ 57 + opts.release_name, 58 + opts.release_mbid, 59 + track_name, 60 + opts.recording_mbid, 61 + opts.artist_name, 62 + ], 63 + |row| { 64 + let debug = MbzDebug { 65 + release: row.get(8)?, 66 + recording: row.get(9)?, 67 + artist: row.get(10)?, 68 + }; 69 + 70 + Ok(MbzQueryRes { 71 + track: row.get(0)?, 72 + track_gid: row.get(1)?, 73 + release_gid: row.get(2)?, 74 + release: row.get(3)?, 75 + recording_gid: row.get(4)?, 76 + release_group_gid: row.get(5)?, 77 + release_group: row.get(6)?, 78 + artists: row.get(7)?, 79 + debug: debug.to_string(), 80 + }) 81 + }, 82 + )? 83 + .collect::<Result<Vec<_>, _>>()?; 84 + 85 + if !rows.is_empty() { 86 + let row = rows.pop(); 87 + return Ok(row); 88 + } 89 + 90 + tracing::debug!("couldn't find '{track_name}' // {:?}", opts); 91 + 92 + Ok(None) 93 + }
+23
src/mbz/query.sql
··· 1 + with releases as (select *, rel_score: mbz.fts_release($1, $2) 2 + from mbz.release 3 + where (rel_score is not null and rel_score > 5 and $2 IS NULL) 4 + or ($2 IS NOT NULL and release.gid = $2)), 5 + recordings as (select *, rec_score: mbz.fts_recording($3, $4) 6 + from mbz.recording 7 + where (rec_score is not null and rec_score > 5 and $4 IS NULL) 8 + or ($4 IS NOT NULL and recording.gid = $4)), 9 + artists as (select *, artist_score: flashback.fts_mbz_artist_credit.match_bm25(artist_credit.id, $5), 10 + from mbz.artist_credit 11 + where artist_score is not null 12 + and artist_score > 2.5) 13 + select track_name: track.name, track_gid: track.gid, release_gid: releases.gid, release_name: releases.name, recording_gid: recordings.gid, release_grp_gid: release_group.gid, release_grp_name: release_group.name, artists: artists.name, rel_score, 14 + rec_score, 15 + artist_score 16 + from mbz.track 17 + inner join recordings on recordings.id = track.recording 18 + inner join mbz.medium on medium.id = track.medium 19 + inner join releases on releases.id = medium.release 20 + inner join mbz.release_group on release_group.id = releases.release_group 21 + inner join artists on artists.id = recordings.artist_credit 22 + where is_data_track = false 23 + order by rel_score desc, rec_score desc, artist_score desc limit 10;