Repository: aome510/spotify-player (language: rust)

Issue:
Genius API is sometimes weird
**Is your feature request related to a problem? Please describe.**
Getting lyrics from its API behaves a bit weird sometimes.

Searching in their api "Peace Sells Megadeth" show the correct song, but "Peace Sells Remastered Megadeth" does not! And it even does not show Megadeth after the first results.



**Describe the solution you'd like**
Don't know how this could be solved.

**Describe alternatives you've considered**
Maybe we could search for a different API service which we could pass the song and the author as parameters, unlike Genius which is a general term.
Or maybe we could ask the user "is this lyrics wrong" yes -> "please type a search"?

I don't think this has a solution in the very near future, but tracking it could be useful, that's why I created this issue.

By the way, awesome project!! I'm enjoying a lot using it :)



Discussion / hints:
Genius lyric doesn't work sometimes, especially when the query "{track} {artists}" has some additional info like "Remastered", "Remix", etc. 

This might be addressed by using the unreleased Spotify lyric API from https://github.com/librespot-org/librespot, so don't need to rely on third-party APIs. I'm not sure what their plan is to release the new version, so for now, I'll stick with just using Genius API.

Related: #155 

Fix patch:
diff --git a/lyric_finder/rustfmt.toml b/lyric_finder/rustfmt.toml
new file mode 100644
index 00000000..eec13eb4
--- /dev/null
+++ b/lyric_finder/rustfmt.toml
@@ -0,0 +1,1 @@
+edition="2021"
diff --git a/lyric_finder/src/lib.rs b/lyric_finder/src/lib.rs
index 9ab4db8d..7f3cfb4a 100644
--- a/lyric_finder/src/lib.rs
+++ b/lyric_finder/src/lib.rs
@@ -58,6 +58,8 @@ impl Client {
 
     /// Search songs satisfying a given `query`.
     pub async fn search_songs(&self, query: &str) -> anyhow::Result<Vec<search::Result>> {
+        let query = improve_query(query);
+
         log::debug!("search songs: query={query}");
 
         let body = self
@@ -136,6 +138,66 @@ impl Default for Client {
     }
 }
 
+/// Returns `query` without `remaster` & `remix` information from track/artist query.
+/// Returned value is lowercase.
+/// These caused wildly invalid lyrics to be found.
+/// (try yourself adding remastered 2011 to a song's name when searching in Genius!)
+fn improve_query(query: &str) -> String {
+    // flag for doing something wrong if the song name (after removing remix metadata) is too short.
+    const SONG_MIN_LENGTH_WO_REMIX_METADATA: usize = 3;
+
+    let is_dash = |c: char| c == '-';
+
+    // reverse finder for non-filler (space, dashes) chars before an index.
+    // Acts like a trim to remove undesired spaces and dashes.
+    let rfind_non_filler = |s: &str, idx: usize| {
+        let Some(s) = s.get(..idx) else { return idx };
+        s.char_indices()
+            .rfind(|(_, c)| !(is_dash(*c) || c.is_whitespace()))
+            .map_or(idx, |(idx, c)| idx + c.len_utf8())
+    };
+    // used to handle longer variants of words: `remixed`, `remastered`, etc.
+    let end_of_word = |s: &str, idx: usize| {
+        let Some(s) = s.get(idx..) else { return idx };
+        s.find(|c: char| !c.is_alphanumeric())
+            .map_or(idx, |found| found + idx)
+    };
+
+    let mut query = query.to_lowercase();
+    // remove "xxxx Remaster" from the query
+    // For example, `{song} xxxx Remastered {artists}` becomes `{song} {artists}`.
+    if let Some(remaster_start) = query.find("remaster") {
+        let end = remaster_start + "remaster".len();
+        let end = end_of_word(&query, end);
+
+        let mut start = remaster_start.saturating_sub(1);
+        let prev = query.get(..remaster_start.saturating_sub(2)).unwrap_or("");
+        let end_of_prev_word = prev.rfind(' ').unwrap_or(0);
+
+        if let Some(year) = query.get(end_of_prev_word + 1..remaster_start.saturating_sub(1)) {
+            if year.chars().all(|c| c.is_whitespace() || c.is_numeric()) {
+                start = end_of_prev_word;
+            }
+        }
+        start = rfind_non_filler(&query, start);
+        query.drain(start..end);
+    }
+    // remove "- xxxx yyy remix" from the query
+    // For example, `{song} - xxxx yyy remix {artists}` becomes `{song} {artists}`.
+    if let Some(remix_start) = query.find("remix") {
+        let end = remix_start + "remix".len();
+        let end = end_of_word(&query, end);
+
+        if let Some(metadata_start) = query.rfind(is_dash) {
+            if metadata_start >= SONG_MIN_LENGTH_WO_REMIX_METADATA {
+                let start = rfind_non_filler(&query, metadata_start);
+                query.drain(start..end);
+            }
+        }
+    }
+    query
+}
+
 mod parse {
     use html5ever::tendril::TendrilSink;
     use html5ever::*;


Test patch: