//! Broad discography audit over the PRODUCTION artist pipeline. //! //! Runs `catalog_artist` (Deezer + Apple supplement, dedup, edition folding) //! plus the deferred `catalog_artist_identity` MusicBrainz pass for each //! requested artist, and prints one JSON object per artist (JSONL) with the //! final album rows and MB type hints — exactly the data the artist page //! files into tabs. An external analyzer replicates the frontend's category //! rules over this dump to flag duplicates and misfiled releases. //! //! Usage: //! cargo run -p metadata --example discography_audit -- "Artist" ... //! cargo run -p metadata --example discography_audit -- --broad use metadata::MetadataClient; use std::sync::Arc; const BROAD_ARTISTS: [&str; 44] = [ "Weezer", "Radiohead", "Queen", "The Beatles", "Taylor Swift", "Beyoncé", "Coldplay", "Nirvana", "Fleetwood Mac", "Elton John", "David Bowie", "Pink Floyd", "Led Zeppelin", "Bob Dylan", "Miles Davis", "Daft Punk", "deadmau5", "Fatboy Slim", "Bonobo", "Four Tet", "Calvin Harris", "Aphex Twin", "The Chemical Brothers", "Crazy P", "Moby", "Björk", "Depeche Mode", "Kanye West", "Kendrick Lamar", "Eminem", "Drake", "Travis Scott", "Hans Zimmer", "John Williams", "Danny Elfman", "Ludwig Göransson", "Nine Inch Nails", "MGMT", "Panda Bear", "Tame Impala", "Mac DeMarco", "Gorillaz", "Metallica", "ABBA", ]; #[tokio::main] async fn main() { let mut requested: Vec = std::env::args().skip(1).collect(); if requested.iter().any(|a| a == "--broad") { requested = BROAD_ARTISTS.iter().map(|s| s.to_string()).collect(); } if requested.is_empty() { eprintln!("pass artist names or --broad"); std::process::exit(2); } let client = Arc::new(MetadataClient::new()); for name in requested { let search = client.catalog_search(&name).await; let want = normalize(&name); let artist = search .artists .iter() .filter(|a| normalize(&a.name) == want) .max_by_key(|a| a.fans) .or_else(|| search.artists.first()); let Some(artist) = artist else { println!( "{}", serde_json::json!({ "requested": name, "error": "not_found" }) ); continue; }; let id = artist.id; let display = artist.name.clone(); let fans = artist.fans; let page = client.catalog_artist(id).await; let Some(page) = page else { println!( "{}", serde_json::json!({ "requested": name, "artist_id": id, "error": "page_failed" }) ); continue; }; let identity = client.catalog_artist_identity(id, &display).await; println!( "{}", serde_json::json!({ "requested": name, "artist_id": id, "artist": display, "fans": fans, "albums": page.albums, "type_hints": identity.type_hints, "identity_albums": identity.albums, }) ); } } fn normalize(s: &str) -> String { s.to_lowercase() .chars() .filter(|c| c.is_alphanumeric()) .collect() }