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

Merge pull request #1 from jchoi2x/copilot/update-lyrics-fetching

Replace broken genius-rust lyrics API with direct HTTP calls

authored by

James Choi and committed by
GitHub
0b7b0aba 9d815262

+139 -13
+3 -2
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" ··· 286 286 287 287 [[package]] 288 288 name = "genius-cli" 289 - version = "0.1.2" 289 + version = "0.2.0" 290 290 dependencies = [ 291 291 "clap", 292 292 "colored_json", 293 293 "genius-rust", 294 294 "owo-colors", 295 295 "rand", 296 + "reqwest", 296 297 "serde", 297 298 "serde_json", 298 299 "tabled",
+4 -3
Cargo.toml
··· 1 1 [package] 2 2 name = "genius-cli" 3 - version = "0.1.2" 3 + version = "0.2.0" 4 4 edition = "2021" 5 5 description = "A command-line interface for Genius.com" 6 6 readme = "README.md" 7 7 repository = "https://github.com/tsirysndr/genius-cli" 8 8 license = "MIT" 9 - authors = ["Tsiry Sandratraina <tsiry.sndr@aol.com>"] 9 + authors = ["Tsiry Sandratraina <tsiry.sndr@aol.com>", "James Choi <choijjames@gmail.com>"] 10 10 categories = ["command-line-utilities"] 11 11 keywords = ["tokio", "lyrics", "cli", "genius"] 12 12 ··· 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"] }
+132 -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 + // API response structure from https://genius-mcp.xvzf.workers.dev/api/song/{id}/lyrics 14 + // All fields are part of the API response and need to be present for deserialization 15 + // Some fields aren't directly used in the code but are required for proper JSON parsing 16 + #[allow(dead_code)] 17 + #[derive(Debug, Deserialize)] 18 + struct LyricsApiResponse { 19 + #[serde(default)] 20 + id: u32, 21 + title: String, 22 + #[serde(default)] 23 + artist_names: String, // Alternative to primary_artist.name 24 + url: String, 25 + lyrics: String, 26 + primary_artist: ApiPrimaryArtist, 27 + } 28 + 29 + #[allow(dead_code)] 30 + #[derive(Debug, Deserialize)] 31 + struct ApiPrimaryArtist { 32 + #[serde(default)] 33 + id: u32, 34 + name: String, 35 + url: String, 36 + } 37 + 38 + fn print_song_info(artist_name: &str, title: &str, url: &str) { 39 + let mut rng = rand::thread_rng(); 40 + match rng.gen_range(0..6) { 41 + 0 => { 42 + println!( 43 + "\n{}{}{}", 44 + artist_name.fg::<Black>().bg::<Magenta>(), 45 + " - ".fg::<Black>().bg::<Magenta>(), 46 + title.fg::<Black>().bg::<Magenta>() 47 + ); 48 + println!("{}\n", url.fg::<Magenta>()); 49 + } 50 + 1 => { 51 + println!( 52 + "\n{}{}{}", 53 + artist_name.fg::<Black>().bg::<Cyan>(), 54 + " - ".fg::<Black>().bg::<Cyan>(), 55 + title.fg::<Black>().bg::<Cyan>() 56 + ); 57 + println!("{}\n", url.fg::<Cyan>()); 58 + } 59 + 2 => { 60 + println!( 61 + "\n{}{}{}", 62 + artist_name.fg::<Black>().bg::<Orange>(), 63 + " - ".fg::<Black>().bg::<Orange>(), 64 + title.fg::<Black>().bg::<Orange>() 65 + ); 66 + println!("{}\n", url.fg::<Orange>()); 67 + } 68 + 3 => { 69 + println!( 70 + "\n{}{}{}", 71 + artist_name.fg::<Black>().bg::<BrightGreen>(), 72 + " - ".fg::<Black>().bg::<BrightGreen>(), 73 + title.fg::<Black>().bg::<BrightGreen>() 74 + ); 75 + println!("{}\n", url.fg::<BrightGreen>()); 76 + } 77 + 4 => { 78 + println!( 79 + "\n{}{}{}", 80 + artist_name.fg::<Black>().bg::<Yellow>(), 81 + " - ".fg::<Black>().bg::<Yellow>(), 82 + title.fg::<Black>().bg::<Yellow>() 83 + ); 84 + println!("{}\n", url.fg::<Yellow>()); 85 + } 86 + 5 => { 87 + println!( 88 + "\n{}{}{}", 89 + artist_name.fg::<Black>().bg::<BrightYellow>(), 90 + " - ".fg::<Black>().bg::<BrightYellow>(), 91 + title.fg::<Black>().bg::<BrightYellow>() 92 + ); 93 + println!("{}\n", url.fg::<BrightYellow>()); 94 + } 95 + _ => { 96 + println!( 97 + "\n{}{}{}", 98 + artist_name.fg::<Black>().bg::<Magenta>(), 99 + " - ".fg::<Black>().bg::<Magenta>(), 100 + title.fg::<Black>().bg::<Magenta>() 101 + ); 102 + println!("{}\n", url.fg::<Magenta>()); 103 + } 104 + } 105 + } 7 106 8 107 fn cli() -> Command<'static> { 9 108 const VERSION: &str = env!("CARGO_PKG_VERSION"); ··· 79 178 .unwrap() 80 179 .parse::<u32>() 81 180 .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")); 181 + 182 + // Fetch lyrics from the new API 183 + let url = format!("https://genius-mcp.xvzf.workers.dev/api/song/{}/lyrics", id); 184 + let client = reqwest::Client::new(); 185 + 186 + match client.get(&url).send().await { 187 + Ok(response) => { 188 + match response.json::<LyricsApiResponse>().await { 189 + Ok(lyrics_data) => { 190 + // Print song info using our helper function 191 + print_song_info( 192 + &lyrics_data.primary_artist.name, 193 + &lyrics_data.title, 194 + &lyrics_data.url 195 + ); 196 + 197 + // Print the lyrics 198 + println!("{}", lyrics_data.lyrics); 199 + } 200 + Err(e) => { 201 + eprintln!("Error parsing API response: {}", e); 202 + eprintln!("Please check that the song ID exists."); 203 + exit(1); 204 + } 205 + } 206 + } 207 + Err(e) => { 208 + eprintln!("Error fetching lyrics from API: {}", e); 209 + eprintln!("Please check your internet connection and try again."); 210 + exit(1); 211 + } 212 + } 89 213 } 90 214 Some(("song", sub_matches)) => { 91 215 let id = sub_matches