Search lyrics or song metadata from your terminal
genius genius-lyrics-search genius-lyrics cli rust

Replace broken genius-rust lyrics API with direct HTTP calls

- Add reqwest dependency with json feature
- Add serde derive feature
- Create LyricsApiResponse and ApiPrimaryArtist structs for deserialization
- Replace genius.get_lyrics() with direct HTTP GET to genius-mcp.xvzf.workers.dev
- Create print_song_info() helper function to maintain colorized output format
- Add proper error handling with descriptive messages
- Keep search and song subcommands using genius-rust (they still work)

Co-authored-by: jchoi2x <2028917+jchoi2x@users.noreply.github.com>

+133 -10
+2 -1
Cargo.lock
··· 1 1 # This file is automatically @generated by Cargo. 2 2 # It is not intended for manual editing. 3 - version = 3 3 + version = 4 4 4 5 5 [[package]] 6 6 name = "android_system_properties" ··· 293 293 "genius-rust", 294 294 "owo-colors", 295 295 "rand", 296 + "reqwest", 296 297 "serde", 297 298 "serde_json", 298 299 "tabled",
+2 -1
Cargo.toml
··· 22 22 genius-rust = "0.1.1" 23 23 owo-colors = "3.5.0" 24 24 rand = "0.8.5" 25 - serde = "1.0.144" 25 + reqwest = { version = "0.11", features = ["json"] } 26 + serde = { version = "1.0.144", features = ["derive"] } 26 27 serde_json = "1.0.85" 27 28 tabled = "0.8.0" 28 29 tokio = { version = "1.20.1", features = ["full"] }
+129 -8
src/main.rs
··· 2 2 3 3 use clap::{Arg, Command}; 4 4 use colored_json::ToColoredJson; 5 - use genius_cli::colorizer::print_colorized; 6 5 use genius_rust::Genius; 6 + use owo_colors::{ 7 + colors::{css::Orange, Black, BrightGreen, BrightYellow, Cyan, Magenta, Yellow}, 8 + OwoColorize, 9 + }; 10 + use rand::Rng; 11 + use serde::Deserialize; 12 + 13 + #[allow(dead_code)] 14 + #[derive(Debug, Deserialize)] 15 + struct LyricsApiResponse { 16 + #[serde(default)] 17 + id: u32, 18 + title: String, 19 + #[serde(default)] 20 + artist_names: String, 21 + url: String, 22 + lyrics: String, 23 + primary_artist: ApiPrimaryArtist, 24 + } 25 + 26 + #[allow(dead_code)] 27 + #[derive(Debug, Deserialize)] 28 + struct ApiPrimaryArtist { 29 + #[serde(default)] 30 + id: u32, 31 + name: String, 32 + url: String, 33 + } 34 + 35 + fn print_song_info(artist_name: &str, title: &str, url: &str) { 36 + let mut rng = rand::thread_rng(); 37 + match rng.gen_range(0..5) { 38 + 0 => { 39 + println!( 40 + "\n{}{}{}", 41 + artist_name.fg::<Black>().bg::<Magenta>(), 42 + " - ".fg::<Black>().bg::<Magenta>(), 43 + title.fg::<Black>().bg::<Magenta>() 44 + ); 45 + println!("{}\n", url.fg::<Magenta>()); 46 + } 47 + 1 => { 48 + println!( 49 + "\n{}{}{}", 50 + artist_name.fg::<Black>().bg::<Cyan>(), 51 + " - ".fg::<Black>().bg::<Cyan>(), 52 + title.fg::<Black>().bg::<Cyan>() 53 + ); 54 + println!("{}\n", url.fg::<Cyan>()); 55 + } 56 + 2 => { 57 + println!( 58 + "\n{}{}{}", 59 + artist_name.fg::<Black>().bg::<Orange>(), 60 + " - ".fg::<Black>().bg::<Orange>(), 61 + title.fg::<Black>().bg::<Orange>() 62 + ); 63 + println!("{}\n", url.fg::<Orange>()); 64 + } 65 + 3 => { 66 + println!( 67 + "\n{}{}{}", 68 + artist_name.fg::<Black>().bg::<BrightGreen>(), 69 + " - ".fg::<Black>().bg::<BrightGreen>(), 70 + title.fg::<Black>().bg::<BrightGreen>() 71 + ); 72 + println!("{}\n", url.fg::<BrightGreen>()); 73 + } 74 + 4 => { 75 + println!( 76 + "\n{}{}{}", 77 + artist_name.fg::<Black>().bg::<Yellow>(), 78 + " - ".fg::<Black>().bg::<Yellow>(), 79 + title.fg::<Black>().bg::<Yellow>() 80 + ); 81 + println!("{}\n", url.fg::<Yellow>()); 82 + } 83 + 5 => { 84 + println!( 85 + "\n{}{}{}", 86 + artist_name.fg::<Black>().bg::<BrightYellow>(), 87 + " - ".fg::<Black>().bg::<BrightYellow>(), 88 + title.fg::<Black>().bg::<BrightYellow>() 89 + ); 90 + println!("{}\n", url.fg::<BrightYellow>()); 91 + } 92 + _ => { 93 + println!( 94 + "\n{}{}{}", 95 + artist_name.fg::<Black>().bg::<Magenta>(), 96 + " - ".fg::<Black>().bg::<Magenta>(), 97 + title.fg::<Black>().bg::<Magenta>() 98 + ); 99 + println!("{}\n", url.fg::<Magenta>()); 100 + } 101 + } 102 + } 7 103 8 104 fn cli() -> Command<'static> { 9 105 const VERSION: &str = env!("CARGO_PKG_VERSION"); ··· 79 175 .unwrap() 80 176 .parse::<u32>() 81 177 .unwrap(); 82 - let result = genius.get_song(id, "plain").await; 83 - let song = result.unwrap(); 84 - 85 - print_colorized(&song); 86 - 87 - let lyrics = genius.get_lyrics(id).await.unwrap(); 88 - println!("{}", lyrics.join("\n")); 178 + 179 + // Fetch lyrics from the new API 180 + let url = format!("https://genius-mcp.xvzf.workers.dev/api/song/{}/lyrics", id); 181 + let client = reqwest::Client::new(); 182 + 183 + match client.get(&url).send().await { 184 + Ok(response) => { 185 + match response.json::<LyricsApiResponse>().await { 186 + Ok(lyrics_data) => { 187 + // Print song info using our helper function 188 + print_song_info( 189 + &lyrics_data.primary_artist.name, 190 + &lyrics_data.title, 191 + &lyrics_data.url 192 + ); 193 + 194 + // Print the lyrics 195 + println!("{}", lyrics_data.lyrics); 196 + } 197 + Err(e) => { 198 + eprintln!("Error parsing API response: {}", e); 199 + eprintln!("Please check that the song ID exists."); 200 + exit(1); 201 + } 202 + } 203 + } 204 + Err(e) => { 205 + eprintln!("Error fetching lyrics from API: {}", e); 206 + eprintln!("Please check your internet connection and try again."); 207 + exit(1); 208 + } 209 + } 89 210 } 90 211 Some(("song", sub_matches)) => { 91 212 let id = sub_matches