Merge pull request #22 from Polochon-street/add-dedup-method
Add playlist dedup methods
This commit is contained in:
commit
5107bc6087
3 changed files with 154 additions and 2 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -77,7 +77,7 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bliss-audio"
|
name = "bliss-audio"
|
||||||
version = "0.3.5"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bliss-audio-aubio-rs",
|
"bliss-audio-aubio-rs",
|
||||||
"crossbeam",
|
"crossbeam",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bliss-audio"
|
name = "bliss-audio"
|
||||||
version = "0.3.5"
|
version = "0.4.0"
|
||||||
authors = ["Polochon-street <polochonstreet@gmx.fr>"]
|
authors = ["Polochon-street <polochonstreet@gmx.fr>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
|
152
src/distance.rs
152
src/distance.rs
|
@ -73,6 +73,50 @@ pub fn song_to_song(first_song: &Song, songs: &mut Vec<Song>, distance: impl Dis
|
||||||
*songs = new_songs;
|
*songs = new_songs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove duplicate songs from a playlist, in place.
|
||||||
|
///
|
||||||
|
/// Two songs are considered duplicates if they either have the same,
|
||||||
|
/// non-empty title and artist name, or if they are close enough in terms
|
||||||
|
/// of distance.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `songs`: The playlist to remove duplicates from.
|
||||||
|
/// * `distance_threshold`: The distance threshold under which two songs are
|
||||||
|
/// considered identical. If `None`, a default value of 0.05 will be used.
|
||||||
|
pub fn dedup_playlist(songs: &mut Vec<Song>, distance_threshold: Option<f32>) {
|
||||||
|
dedup_playlist_custom_distance(songs, distance_threshold, euclidean_distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove duplicate songs from a playlist, in place, using a custom distance
|
||||||
|
/// metric.
|
||||||
|
///
|
||||||
|
/// Two songs are considered duplicates if they either have the same,
|
||||||
|
/// non-empty title and artist name, or if they are close enough in terms
|
||||||
|
/// of distance.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `songs`: The playlist to remove duplicates from.
|
||||||
|
/// * `distance_threshold`: The distance threshold under which two songs are
|
||||||
|
/// considered identical. If `None`, a default value of 0.05 will be used.
|
||||||
|
/// * `distance`: A custom distance metric.
|
||||||
|
pub fn dedup_playlist_custom_distance(
|
||||||
|
songs: &mut Vec<Song>,
|
||||||
|
distance_threshold: Option<f32>,
|
||||||
|
distance: impl DistanceMetric,
|
||||||
|
) {
|
||||||
|
songs.dedup_by(|s1, s2| {
|
||||||
|
n32(s1.custom_distance(&s2, &distance)) < distance_threshold.unwrap_or(0.05)
|
||||||
|
|| (s1.title.is_some()
|
||||||
|
&& s2.title.is_some()
|
||||||
|
&& s1.artist.is_some()
|
||||||
|
&& s2.artist.is_some()
|
||||||
|
&& s1.title == s2.title
|
||||||
|
&& s1.artist == s2.artist)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -80,6 +124,114 @@ mod test {
|
||||||
use ndarray::arr1;
|
use ndarray::arr1;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dedup_playlist_custom_distance() {
|
||||||
|
let first_song = Song {
|
||||||
|
path: Path::new("path-to-first").to_path_buf(),
|
||||||
|
analysis: Analysis::new([
|
||||||
|
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
|
||||||
|
]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let first_song_dupe = Song {
|
||||||
|
path: Path::new("path-to-dupe").to_path_buf(),
|
||||||
|
analysis: Analysis::new([
|
||||||
|
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
|
||||||
|
]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let second_song = Song {
|
||||||
|
path: Path::new("path-to-second").to_path_buf(),
|
||||||
|
analysis: Analysis::new([
|
||||||
|
2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 1.9, 1., 1., 1.,
|
||||||
|
]),
|
||||||
|
title: Some(String::from("dupe-title")),
|
||||||
|
artist: Some(String::from("dupe-artist")),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let third_song = Song {
|
||||||
|
path: Path::new("path-to-third").to_path_buf(),
|
||||||
|
title: Some(String::from("dupe-title")),
|
||||||
|
artist: Some(String::from("dupe-artist")),
|
||||||
|
analysis: Analysis::new([
|
||||||
|
2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2.5, 1., 1., 1.,
|
||||||
|
]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let fourth_song = Song {
|
||||||
|
path: Path::new("path-to-fourth").to_path_buf(),
|
||||||
|
artist: Some(String::from("no-dupe-artist")),
|
||||||
|
title: Some(String::from("dupe-title")),
|
||||||
|
analysis: Analysis::new([
|
||||||
|
2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 0., 1., 1., 1.,
|
||||||
|
]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let fifth_song = Song {
|
||||||
|
path: Path::new("path-to-fourth").to_path_buf(),
|
||||||
|
analysis: Analysis::new([
|
||||||
|
2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 2., 0.001, 1., 1., 1.,
|
||||||
|
]),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut playlist = vec![
|
||||||
|
first_song.to_owned(),
|
||||||
|
first_song_dupe.to_owned(),
|
||||||
|
second_song.to_owned(),
|
||||||
|
third_song.to_owned(),
|
||||||
|
fourth_song.to_owned(),
|
||||||
|
fifth_song.to_owned(),
|
||||||
|
];
|
||||||
|
dedup_playlist_custom_distance(&mut playlist, None, euclidean_distance);
|
||||||
|
assert_eq!(
|
||||||
|
playlist,
|
||||||
|
vec![
|
||||||
|
first_song.to_owned(),
|
||||||
|
second_song.to_owned(),
|
||||||
|
fourth_song.to_owned(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
let mut playlist = vec![
|
||||||
|
first_song.to_owned(),
|
||||||
|
first_song_dupe.to_owned(),
|
||||||
|
second_song.to_owned(),
|
||||||
|
third_song.to_owned(),
|
||||||
|
fourth_song.to_owned(),
|
||||||
|
fifth_song.to_owned(),
|
||||||
|
];
|
||||||
|
dedup_playlist_custom_distance(&mut playlist, Some(20.), cosine_distance);
|
||||||
|
assert_eq!(playlist, vec![first_song.to_owned()]);
|
||||||
|
let mut playlist = vec![
|
||||||
|
first_song.to_owned(),
|
||||||
|
first_song_dupe.to_owned(),
|
||||||
|
second_song.to_owned(),
|
||||||
|
third_song.to_owned(),
|
||||||
|
fourth_song.to_owned(),
|
||||||
|
fifth_song.to_owned(),
|
||||||
|
];
|
||||||
|
dedup_playlist(&mut playlist, Some(20.));
|
||||||
|
assert_eq!(playlist, vec![first_song.to_owned()]);
|
||||||
|
let mut playlist = vec![
|
||||||
|
first_song.to_owned(),
|
||||||
|
first_song_dupe.to_owned(),
|
||||||
|
second_song.to_owned(),
|
||||||
|
third_song.to_owned(),
|
||||||
|
fourth_song.to_owned(),
|
||||||
|
fifth_song.to_owned(),
|
||||||
|
];
|
||||||
|
dedup_playlist(&mut playlist, None);
|
||||||
|
assert_eq!(
|
||||||
|
playlist,
|
||||||
|
vec![
|
||||||
|
first_song.to_owned(),
|
||||||
|
second_song.to_owned(),
|
||||||
|
fourth_song.to_owned()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_song_to_song() {
|
fn test_song_to_song() {
|
||||||
let first_song = Song {
|
let first_song = Song {
|
||||||
|
|
Loading…
Reference in a new issue