Initial commit
This commit is contained in:
129
src/matching.rs
Normal file
129
src/matching.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use sea_orm::DatabaseConnection;
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
||||
use shanty_db::queries;
|
||||
|
||||
use crate::error::WatchResult;
|
||||
|
||||
/// Normalize a string for fuzzy comparison: NFC unicode, lowercase, trim.
|
||||
pub fn normalize(s: &str) -> String {
|
||||
s.nfc().collect::<String>().to_lowercase().trim().to_string()
|
||||
}
|
||||
|
||||
/// Check if an artist is "owned" — i.e., any tracks by this artist exist in the indexed library.
|
||||
pub async fn artist_is_owned(conn: &DatabaseConnection, artist_name: &str) -> WatchResult<bool> {
|
||||
let normalized = normalize(artist_name);
|
||||
|
||||
// Check by exact artist record first
|
||||
if let Some(artist) = queries::artists::find_by_name(conn, artist_name).await? {
|
||||
let tracks = queries::tracks::get_by_artist(conn, artist.id).await?;
|
||||
if !tracks.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Fuzzy: search tracks where the artist field matches
|
||||
let tracks = queries::tracks::search(conn, artist_name).await?;
|
||||
for track in &tracks {
|
||||
if let Some(ref track_artist) = track.artist {
|
||||
let sim = strsim::jaro_winkler(&normalized, &normalize(track_artist));
|
||||
if sim > 0.85 {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
if let Some(ref album_artist) = track.album_artist {
|
||||
let sim = strsim::jaro_winkler(&normalized, &normalize(album_artist));
|
||||
if sim > 0.85 {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Check if an album is "owned" — tracks from this album by this artist exist.
|
||||
pub async fn album_is_owned(
|
||||
conn: &DatabaseConnection,
|
||||
artist_name: &str,
|
||||
album_name: &str,
|
||||
) -> WatchResult<bool> {
|
||||
let norm_artist = normalize(artist_name);
|
||||
let norm_album = normalize(album_name);
|
||||
|
||||
// Try exact lookup
|
||||
if let Some(album) = queries::albums::find_by_name_and_artist(conn, album_name, artist_name).await? {
|
||||
let tracks = queries::tracks::get_by_album(conn, album.id).await?;
|
||||
if !tracks.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Fuzzy: search tracks matching album name
|
||||
let tracks = queries::tracks::search(conn, album_name).await?;
|
||||
for track in &tracks {
|
||||
let album_match = track
|
||||
.album
|
||||
.as_deref()
|
||||
.map(|a| strsim::jaro_winkler(&norm_album, &normalize(a)) > 0.85)
|
||||
.unwrap_or(false);
|
||||
let artist_match = track
|
||||
.artist
|
||||
.as_deref()
|
||||
.or(track.album_artist.as_deref())
|
||||
.map(|a| strsim::jaro_winkler(&norm_artist, &normalize(a)) > 0.85)
|
||||
.unwrap_or(false);
|
||||
if album_match && artist_match {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Check if a specific track is "owned" — a track with matching artist + title exists.
|
||||
pub async fn track_is_owned(
|
||||
conn: &DatabaseConnection,
|
||||
artist_name: &str,
|
||||
title: &str,
|
||||
) -> WatchResult<bool> {
|
||||
let norm_artist = normalize(artist_name);
|
||||
let norm_title = normalize(title);
|
||||
|
||||
// Fuzzy: search tracks matching the title
|
||||
let tracks = queries::tracks::search(conn, title).await?;
|
||||
for track in &tracks {
|
||||
let title_match = track
|
||||
.title
|
||||
.as_deref()
|
||||
.map(|t| strsim::jaro_winkler(&norm_title, &normalize(t)) > 0.85)
|
||||
.unwrap_or(false);
|
||||
let artist_match = track
|
||||
.artist
|
||||
.as_deref()
|
||||
.or(track.album_artist.as_deref())
|
||||
.map(|a| strsim::jaro_winkler(&norm_artist, &normalize(a)) > 0.85)
|
||||
.unwrap_or(false);
|
||||
if title_match && artist_match {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_normalize() {
|
||||
assert_eq!(normalize(" Pink Floyd "), "pink floyd");
|
||||
assert_eq!(normalize("RADIOHEAD"), "radiohead");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_unicode() {
|
||||
assert_eq!(normalize("café"), normalize("café"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user