use std::borrow::Cow;
use std::error::Error;
use regex::Regex;
#[derive(Debug, Clone)]
pub struct FilterRule(Regex, &'static str);
impl FilterRule {
pub fn new(pattern: &str, replacement: &'static str) -> Result<Self, Box<dyn Error>> {
Ok(Self(
Regex::new(&pattern)?,
replacement
))
}
pub fn apply<'t>(&self, text: &'t str) -> Cow<'t, str> {
self.0.replace(text, self.1)
}
}
macro_rules! filter_rules {
($(#[$meta:meta])* $name:ident, $rules:expr) => {
$(#[$meta])*
pub fn $name() -> Vec<FilterRule> {
$rules
.iter()
.map(|rule| FilterRule(Regex::new(rule.0).unwrap(), rule.1))
.collect()
}
};
}
filter_rules!(
youtube_track_filter_rules,
[
(r"^\s+", ""),
(r"\s+$", ""),
(r"\*+\s?\S+\s?\*+$", ""),
(r"\[[^\]]+\]", ""),
(r"(?i)\([^)]*version\)$", ""),
(r"(?i)\.(avi|wmv|mpg|mpeg|flv)$", ""),
(r"(?i)\(.*lyrics?\s*(video)?\)", ""),
(r"(?i)\((of+icial\s*)?(track\s*)?stream\)", ""),
(r"(?i)\((of+icial\s*)?(music\s*)?video\)", ""),
(r"(?i)\((of+icial\s*)?(music\s*)?audio\)", ""),
(r"(?i)(album track\s*)", ""),
(r"(?i)(cover art\s*)", ""),
(r"(?i)\(\s*of+icial\s*\)", ""),
(r"(?i)\(\s*[0-9]{4}\s*\)", ""),
(r"(HD|HQ)\s*$", ""),
("(?i)(vid[\u{00E9}e]o)?\\s?clip\\sof+ici[ae]l", ""),
(r"(?i)of+iziel+es\s*video", ""),
("(?i)vid[\u{00E9}e]o\\s?clip", ""),
(r"(?i)\sclip", ""),
(r"(?i)full\s*album", ""),
(r"(?i)\(live.*?\)$", ""),
(r"(?i)\|.*$", ""),
(r#"^(|.*\s)"(.{5,})"(\s.*|)$"#, "$2"),
(r"^(|.*\s)'(.{5,})'(\s.*|)$", "$2"),
(r"(?i)\(.*[0-9]{1,2}/[0-9]{1,2}/[0-9]{2,4}.*\)", ""),
(r"(?i)sub\s*español", ""),
(r"(?i)\s\(Letra/Lyrics\)", ""),
(r"(?i)\s\(Letra\)", ""),
(r"(?i)\s\(En\svivo\)", ""),
]
);
filter_rules!(
trim_symbols_filter_rules,
[
(r"\(+\s*\)+", ""),
(r#"^[/,:;~\-\s"]+"#, ""),
(r#"[/,:;~\-\s"]+$"#, ""),
]
);
filter_rules!(
remastered_filter_rules,
[
(r"-\sRemastered$", ""),
(r"-\sRemastered\s\d+$", ""),
(r"\(Remaster(ed)?\s\d+\)$", ""),
(r"\[\d+\s-\sRemaster\]$", ""),
(r"\(\d+(\s-)?\sRemaster\)$", ""),
(r"-\s\d+(\s-)?\sRemaster$", ""),
(r"-\s\d+\s.+?\sRemaster$", ""),
(r"-\s\d+\sRemastered Version$", ""),
(r"\(Live\s/\sRemastered\)$", ""),
(r"-\sLive\s/\sRemastered$", ""),
(r"[(\[]Remastered[)\]]$", ""),
(r"[(\[]\d{4} Re[Mm]astered Version[)\]]$", ""),
(r"[(\[]\d{4} Re-?[Mm]astered Digital Version[)\]]$", ""),
(r"\([^(]*Remaster[^)]*\)$", ""),
]
);
filter_rules!(
live_filter_rules,
[
(r"-\sLive?$", ""),
(r"-\sLive\s.+?$", ""),
(r"(?i)[(\[]Live[)\]]", ""),
]
);
filter_rules!(
clean_explicit_filter_rules,
[
(r"(?i)\s[(\[]Explicit[)\]]", ""),
(r"(?i)\s[(\[]Clean[)\]]", ""),
]
);
filter_rules!(
feature_filter_rules,
[
(r"(?i)\s[(\[]feat. .+[)\]]", ""),
]
);
filter_rules!(
normalize_feature_filter_rules,
[
(r"(?i)\s[(\[](feat. .+)[)\]]", " $1"),
]
);
filter_rules!(
version_filter_rules,
[
(r"[(\[]Album Version[)\]]$", ""),
(r"[(\[]Re-?[Rr]ecorded[)\]]$", ""),
(r"[(\[]Single Version[)\]]$", ""),
(r"[(\[]Edit[)\]]$", ""),
(r"-\sMono Version$", ""),
(r"-\sStereo Version$", ""),
(r"\(Deluxe Edition\)$", ""),
(r"(?i)[(\[]Explicit Version[)\]]", ""),
]
);
filter_rules!(
suffix_filter_rules,
[
(
r"(?i)-\s(.+?)\s((Re)?mix|edit|dub|mix|vip|version)$",
"($1 $2)"
),
(r"(?i)-\s(Remix|VIP)$", "($1)"),
]
);
filter_rules!(
trim_whitespace_filter_rules,
[(r"^\s+", ""), (r"\s+$", ""),]
);
#[cfg(test)]
mod tests {
use super::*;
fn apply_rules(text: &str, rules: &[FilterRule]) -> String {
rules.iter().fold(text.to_string(), |mut result, rule| {
let filtered = rule.apply(&result);
if let Cow::Owned(filtered) = filtered {
result.clear();
result.push_str(&filtered);
}
result
})
}
fn test_rules(values: &[(&str, &str)], rules: &[FilterRule]) {
for value in values {
let filtered = apply_rules(value.0, rules);
println!("value: {:?}\nexpected: {:?}\nactual: {:?}\n-----", value.0, value.1, filtered);
assert_eq!(filtered, value.1);
}
}
#[test]
fn test_youtube_track_filter_rules() {
let titles = [
(" whitespace prefix", "whitespace prefix"),
("whitespace suffix ", "whitespace suffix"),
("Artist - Song Title **NEW**", "Artist - Song Title "),
("Artist - Song Title [something]", "Artist - Song Title "),
("Artist - Song Title (xyz version)", "Artist - Song Title "),
("Artist - Song Title.avi", "Artist - Song Title"),
("Artist - Song Title (lyric video)", "Artist - Song Title "),
("Artist - Song Title (official track stream)", "Artist - Song Title "),
("Artist - Song Title (official music video)", "Artist - Song Title "),
("Artist - Song Title (official audio)", "Artist - Song Title "),
("Artist - Song Title (Album Track)", "Artist - Song Title ()"),
("Artist - Song Title (Cover Art)", "Artist - Song Title ()"),
("Artist - Song Title (official)", "Artist - Song Title "),
("Artist - Song Title (1999)", "Artist - Song Title "),
("Artist - Song Title HD", "Artist - Song Title "),
("Artist - Song Title (vidéo clip official)", "Artist - Song Title ()"),
("Artist - Song Title offizielles video", "Artist - Song Title "),
("Artist - Song Title video clip", "Artist - Song Title "),
("Artist - Song Title clip", "Artist - Song Title"),
("Artist - Album Title Full Album", "Artist - Album Title "),
("Artist - Song Title (live)", "Artist - Song Title "),
("Artist - Song Title | something", "Artist - Song Title "),
("Artist - Song Title (01/01/1999)", "Artist - Song Title "),
("Artist - Song Title (sub español)", "Artist - Song Title ()"),
("Artist - Song Title (Letra/Lyrics)", "Artist - Song Title "),
("Artist - Song Title (Letra)", "Artist - Song Title"),
("Artist - Song Title (En vivo)", "Artist - Song Title"),
];
test_rules(&titles, &youtube_track_filter_rules());
}
#[test]
fn test_trim_symbols_filter_rules() {
let titles = [
("Artist - Song Title ()", "Artist - Song Title"),
("Artist - Song Title - ", "Artist - Song Title"),
(" - Artist - Song Title", "Artist - Song Title"),
];
test_rules(&titles, &trim_symbols_filter_rules());
}
#[test]
fn test_remastered_filter_rules() {
let titles = [
("Here Comes The Sun - Remastered", "Here Comes The Sun "),
("Hey Jude - Remastered 2015", "Hey Jude "),
("Let It Be (Remastered 2009)", "Let It Be "),
("Red Rain (Remaster 2012)", "Red Rain "),
(
"Pigs On The Wing (Part One) [2011 - Remaster]",
"Pigs On The Wing (Part One) ",
),
("Comfortably Numb (2011 - Remaster)", "Comfortably Numb "),
("Dancing Days (2012 Remaster)", "Dancing Days "),
("Outside The Wall - 2011 - Remaster", "Outside The Wall "),
("China Grove - 2006 Remaster", "China Grove "),
(
"Learning To Fly - 2001 Digital Remaster",
"Learning To Fly ",
),
(
"Your Possible Pasts - 2011 Remastered Version",
"Your Possible Pasts ",
),
(
"Roll Over Beethoven (Live / Remastered)",
"Roll Over Beethoven ",
),
("Ticket To Ride - Live / Remastered", "Ticket To Ride "),
("Mothership (Remastered)", "Mothership "),
("How The West Was Won [Remastered]", "How The West Was Won "),
(
"A Well Respected Man (2014 Remastered Version)",
"A Well Respected Man ",
),
(
"A Well Respected Man [2014 Remastered Version]",
"A Well Respected Man ",
),
(
"She Was Hot (2009 Re-Mastered Digital Version)",
"She Was Hot ",
),
(
"She Was Hot (2009 Remastered Digital Version)",
"She Was Hot ",
),
(
"In The Court Of The Crimson King (Expanded & Remastered Original Album Mix)",
"In The Court Of The Crimson King ",
),
];
test_rules(&titles, &remastered_filter_rules());
}
#[test]
fn test_live_filter_rules() {
let titles = [
("Song Title - Live", "Song Title "),
("Song Title - Live at Location", "Song Title "),
("Song Title (Live)", "Song Title "),
("Song Title [Live]", "Song Title "),
];
test_rules(&titles, &live_filter_rules());
}
#[test]
fn test_clean_explicit_filter_rules() {
let titles = [
("Song Title (Explicit)", "Song Title"),
("Song Title [Explicit]", "Song Title"),
("Song Title (Clean)", "Song Title"),
("Song Title [Clean]", "Song Title"),
];
test_rules(&titles, &clean_explicit_filter_rules());
}
#[test]
fn test_feature_filter_rules() {
let titles = [
("Song Title (Feat. Other Artist)", "Song Title"),
("Song Title [Feat. Other Artist]", "Song Title"),
];
test_rules(&titles, &feature_filter_rules());
}
#[test]
fn test_normalize_feature_filter_rules() {
let titles = [
("Song Title (Feat. Other Artist)", "Song Title Feat. Other Artist"),
("Song Title [Feat. Other Artist]", "Song Title Feat. Other Artist"),
];
test_rules(&titles, &normalize_feature_filter_rules());
}
#[test]
fn test_version_filter_rules() {
let titles = [
(
"Love Will Come To You (Album Version)",
"Love Will Come To You ",
),
("I Melt With You (Rerecorded)", "I Melt With You "),
("When I Need You [Re-Recorded]", "When I Need You "),
(
"Your Cheatin' Heart (Single Version)",
"Your Cheatin' Heart ",
),
("All Over Now (Edit)", "All Over Now "),
(
"(I Can't Get No) Satisfaction - Mono Version",
"(I Can't Get No) Satisfaction ",
),
("Ruby Tuesday - Stereo Version", "Ruby Tuesday "),
("Pure McCartney (Deluxe Edition)", "Pure McCartney "),
("6 Foot 7 Foot (Explicit Version)", "6 Foot 7 Foot "),
];
test_rules(&titles, &version_filter_rules());
}
#[test]
fn test_suffix_filter_rules() {
let titles = [
("Song Title - X Remix", "Song Title (X Remix)"),
("Song Title - Y-Z Remix", "Song Title (Y-Z Remix)"),
("Song Title - Y-Z Abc Remix", "Song Title (Y-Z Abc Remix)"),
("Song Title - Abc Xyz Remix", "Song Title (Abc Xyz Remix)"),
("Song Title - Remix", "Song Title (Remix)"),
("Song Title - VIP", "Song Title (VIP)"),
];
test_rules(&titles, &suffix_filter_rules());
}
#[test]
fn test_trim_whitespace_filter_rules() {
let titles = [
(" Text ", "Text"),
(" Text", "Text"),
("Text ", "Text"),
];
test_rules(&titles, &trim_whitespace_filter_rules());
}
}