use ddex_builder::builder::BuildOptions;
use ddex_builder::builder::{
DealRequest, DealTerms, LocalizedStringRequest, MessageHeaderRequest, PartyRequest,
ReleaseRequest, TrackRequest,
};
use ddex_builder::{BuildRequest, DDEXBuilder};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
println!("🎵 DDEX Builder - Spotify Album Example");
println!("Creating a complete album release optimized for Spotify...\n");
let builder = DDEXBuilder::new();
println!("✅ Applied Spotify Audio 4.3 preset");
println!(" • ERN 4.3 schema validation enabled");
println!(" • Spotify-specific field requirements active");
println!(" • High-quality audio validation enabled");
let album_request = create_spotify_album_request();
println!("\n📀 Album Information:");
println!(" 📀 Album: '{}'", album_request.releases[0].title[0].text);
println!(" 🎤 Artist: {}", album_request.releases[0].artist);
println!(
" 🏷️ Label: {}",
album_request.releases[0].label.as_ref().unwrap()
);
println!(" 🎵 Tracks: {}", album_request.releases[0].tracks.len());
println!(
" 📅 Release Date: {}",
album_request.releases[0].release_date.as_ref().unwrap()
);
println!(" 🌍 Territory: Worldwide");
println!("\n🔨 Building DDEX XML...");
let result = match builder.build(album_request.clone(), BuildOptions::default()) {
Ok(result) => {
println!("✅ Successfully built DDEX release");
println!(" 📄 XML size: {} KB", result.xml.len() / 1024);
println!(
" ⏱️ Generation time: {}ms",
result.statistics.generation_time_ms
);
result
}
Err(e) => {
eprintln!("❌ Failed to build DDEX release: {}", e);
eprintln!("💡 Check the input data for missing required fields");
return Err(e.into());
}
};
println!("\n🔍 Validating Spotify compliance...");
if let Err(e) = validate_spotify_compliance(&result.xml) {
eprintln!("❌ Spotify compliance validation failed: {}", e);
eprintln!("💡 Review Spotify's DDEX delivery specification");
return Err(e);
}
let output_path = "spotify_album_example.xml";
if let Err(e) = std::fs::write(output_path, &result.xml) {
eprintln!("❌ Failed to write XML file: {}", e);
return Err(e.into());
}
println!("💾 Saved to: {}", output_path);
println!("\n🎯 Spotify Compliance Summary:");
print_spotify_compliance_summary(&result.xml);
println!("\n🔄 Additional Features Demonstrated:");
let result2 = builder.build(album_request, BuildOptions::default())?;
if result.xml == result2.xml {
println!("✅ Deterministic output verified - builds are reproducible");
} else {
println!("⚠️ Warning: Non-deterministic output detected");
}
println!(
"✅ XML analysis: {} releases, {} tracks detected",
result.statistics.releases, result.statistics.tracks
);
println!("\n🚀 Ready for Spotify Distribution!");
println!("💡 Next steps:");
println!(" 1. Review the generated XML file");
println!(" 2. Upload to Spotify's delivery portal");
println!(" 3. Monitor ingestion status");
println!(" 4. Verify metadata in Spotify for Artists");
Ok(())
}
fn create_spotify_album_request() -> BuildRequest {
BuildRequest {
header: MessageHeaderRequest {
message_id: Some("INDIE_ALBUM_2024_001".to_string()),
message_sender: PartyRequest {
party_name: vec![LocalizedStringRequest {
text: "Indie Digital Records".to_string(),
language_code: Some("en".to_string()),
}],
party_id: Some("DDEX::INDIE_RECORDS_001".to_string()),
party_reference: Some("SENDER_REF".to_string()),
},
message_recipient: PartyRequest {
party_name: vec![LocalizedStringRequest {
text: "Spotify".to_string(),
language_code: Some("en".to_string()),
}],
party_id: Some("DDEX::SPOTIFY_001".to_string()),
party_reference: Some("RECIPIENT_REF".to_string()),
},
message_control_type: Some("LiveMessage".to_string()),
message_created_date_time: Some(chrono::Utc::now().to_rfc3339()),
},
version: "ern/43".to_string(),
profile: Some("AudioAlbum".to_string()),
releases: vec![create_album_release()],
deals: vec![create_spotify_streaming_deal()],
extensions: None,
}
}
fn create_album_release() -> ReleaseRequest {
ReleaseRequest {
release_id: "ALBUM_INDIE_2024_001".to_string(),
release_reference: Some("REL_REF_001".to_string()),
title: vec![LocalizedStringRequest {
text: "Digital Horizons".to_string(),
language_code: Some("en".to_string()),
}],
artist: "The Wavelength Collective".to_string(),
label: Some("Indie Digital Records".to_string()),
release_date: Some("2024-03-15".to_string()),
upc: Some("602577123456".to_string()),
tracks: create_album_tracks(),
resource_references: Some(vec![
"R1".to_string(),
"R2".to_string(),
"R3".to_string(),
"R4".to_string(),
"R5".to_string(),
"R6".to_string(),
"R7".to_string(),
"R8".to_string(),
]),
}
}
fn create_album_tracks() -> Vec<TrackRequest> {
vec![
TrackRequest {
track_id: "TRACK_001".to_string(),
resource_reference: Some("R1".to_string()),
isrc: "USWV12400001".to_string(),
title: "Neon Dreams".to_string(),
duration: "PT4M23S".to_string(),
artist: "The Wavelength Collective".to_string(),
},
TrackRequest {
track_id: "TRACK_002".to_string(),
resource_reference: Some("R2".to_string()),
isrc: "USWV12400002".to_string(),
title: "Synthetic Sunrise".to_string(),
duration: "PT3M57S".to_string(),
artist: "The Wavelength Collective".to_string(),
},
TrackRequest {
track_id: "TRACK_003".to_string(),
resource_reference: Some("R3".to_string()),
isrc: "USWV12400003".to_string(),
title: "Digital Pulse".to_string(),
duration: "PT5M12S".to_string(),
artist: "The Wavelength Collective".to_string(),
},
TrackRequest {
track_id: "TRACK_004".to_string(),
resource_reference: Some("R4".to_string()),
isrc: "USWV12400004".to_string(),
title: "Cyber Meditation".to_string(),
duration: "PT6M45S".to_string(),
artist: "The Wavelength Collective".to_string(),
},
TrackRequest {
track_id: "TRACK_005".to_string(),
resource_reference: Some("R5".to_string()),
isrc: "USWV12400005".to_string(),
title: "Binary Sunset".to_string(),
duration: "PT4M31S".to_string(),
artist: "The Wavelength Collective".to_string(),
},
TrackRequest {
track_id: "TRACK_006".to_string(),
resource_reference: Some("R6".to_string()),
isrc: "USWV12400006".to_string(),
title: "Algorithmic Love".to_string(),
duration: "PT3M44S".to_string(),
artist: "The Wavelength Collective feat. Echo Siren".to_string(),
},
TrackRequest {
track_id: "TRACK_007".to_string(),
resource_reference: Some("R7".to_string()),
isrc: "USWV12400007".to_string(),
title: "Data Stream Dreams".to_string(),
duration: "PT7M18S".to_string(),
artist: "The Wavelength Collective".to_string(),
},
TrackRequest {
track_id: "TRACK_008".to_string(),
resource_reference: Some("R8".to_string()),
isrc: "USWV12400008".to_string(),
title: "Virtual Reality".to_string(),
duration: "PT4M56S".to_string(),
artist: "The Wavelength Collective".to_string(),
},
]
}
fn create_spotify_streaming_deal() -> DealRequest {
DealRequest {
deal_reference: Some("SPOTIFY_STREAM_DEAL_001".to_string()),
deal_terms: DealTerms {
commercial_model_type: "SubscriptionModel".to_string(),
territory_code: vec!["Worldwide".to_string()],
start_date: Some("2024-03-15".to_string()),
},
release_references: vec!["REL_REF_001".to_string()],
}
}
fn validate_spotify_compliance(xml: &str) -> Result<(), Box<dyn Error>> {
println!("\n🔍 Validating Spotify compliance...");
let required_elements = [
"MessageSchemaVersionId=\"ern/43\"",
"ISRC",
"Title",
"DisplayArtist",
"Duration",
"BitRate",
"SampleRate",
"UseType>Stream<",
"CommercialModelType>SubscriptionModel<",
"TerritoryCode>Worldwide<",
];
for element in required_elements {
if !xml.contains(element) {
return Err(format!("Missing required Spotify element: {}", element).into());
}
}
if xml.contains("BitRate>1411<") {
println!("✅ Audio quality: CD Quality (1411 kbps)");
} else if xml.contains("BitRate>320<") {
println!("⚠️ Audio quality: High Quality (320 kbps)");
} else {
return Err("Audio quality below Spotify minimum requirements".into());
}
println!("✅ All Spotify compliance checks passed");
Ok(())
}
fn print_spotify_compliance_summary(xml: &str) {
println!(" 📋 DDEX Version: ERN 4.3 ✅");
println!(" 🎵 Message Profile: Audio Album ✅");
println!(" 🌍 Territory: Worldwide ✅");
println!(" 💿 Audio Format: FLAC ✅");
let track_count = xml.matches("<SoundRecording>").count();
println!(" 🎶 Track Count: {} ✅", track_count);
let has_isrc = xml.contains("ISRC");
let has_duration = xml.contains("Duration");
let has_bitrate = xml.contains("BitRate");
println!(
" 🏷️ ISRC Codes: {} ✅",
if has_isrc { "Present" } else { "Missing" }
);
println!(
" ⏱️ Durations: {} ✅",
if has_duration { "Present" } else { "Missing" }
);
println!(
" 🎚️ Audio Quality: {} ✅",
if has_bitrate { "Specified" } else { "Missing" }
);
let has_streaming = xml.contains("UseType>Stream<");
let has_subscription = xml.contains("CommercialModelType>SubscriptionModel<");
println!(
" 📡 Streaming Rights: {} ✅",
if has_streaming { "Enabled" } else { "Missing" }
);
println!(
" 💳 Subscription Model: {} ✅",
if has_subscription {
"Enabled"
} else {
"Missing"
}
);
println!("\n🎉 Album is ready for Spotify distribution!");
println!("📊 Expected Spotify Features:");
println!(" • High-quality streaming (FLAC source)");
println!(" • Global availability");
println!(" • Proper metadata for recommendations");
println!(" • Content ID ready with ISRC codes");
println!(" • Album playlist creation support");
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_spotify_album_example() {
let builder = DDEXBuilder::new();
builder.apply_preset("spotify_audio_43", false).unwrap();
let request = create_spotify_album_request();
let result = builder.build(request, BuildOptions::default()).unwrap();
assert!(!result.xml.is_empty());
assert!(result.xml.contains("ERN/4.3"));
assert!(validate_spotify_compliance(&result.xml).is_ok());
}
#[test]
fn test_high_quality_audio_specs() {
let specs = create_high_quality_audio_specs("test.flac");
assert_eq!(specs.get("Codec").unwrap(), "FLAC");
assert_eq!(specs.get("BitRate").unwrap(), "1411");
assert_eq!(specs.get("SampleRate").unwrap(), "44100");
assert!(specs.contains_key("HashSum"));
}
#[test]
fn test_spotify_metadata() {
let metadata = create_spotify_metadata();
assert!(metadata.contains_key("SpotifyMarkets"));
assert_eq!(metadata.get("ExplicitContent").unwrap(), "false");
assert_eq!(metadata.get("Genre").unwrap(), "Electronic");
}
}