Big Debug of PMOQobuz step 3

This commit is contained in:
2025-12-13 13:57:20 +01:00
parent 50693aaf7a
commit d2f6abf4bb
28 changed files with 1765 additions and 59 deletions

View File

@@ -45,6 +45,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("💾 Initializing caches...");
let cover_cache = Arc::new(CoverCache::new("./cache/qobuz-covers", 500)?);
let audio_cache = Arc::new(AudioCache::new("./cache/qobuz-audio", 100)?);
// IMPORTANT: Register audio cache globally so PlaylistManager can access it
pmoplaylist::register_audio_cache(audio_cache.clone());
println!("✅ Caches initialized\n");
// Step 3: Create QobuzSource with caches

View File

@@ -0,0 +1,47 @@
//! Affiche tous les secrets du Spoofer Rust
use anyhow::Result;
use pmoqobuz::api::Spoofer;
#[tokio::main]
async fn main() -> Result<()> {
println!("=== Rust Spoofer Secrets ===\n");
let spoofer = Spoofer::new().await?;
// App ID
let app_id = spoofer.get_app_id()?;
println!("App ID: {}\n", app_id);
// App Secret (MD5 hash from bundle)
match spoofer.get_app_secret() {
Ok(app_secret) => {
println!("App Secret (from bundle.js):");
println!(" Full value: {}", app_secret);
println!(" Length: {}\n", app_secret.len());
}
Err(e) => {
println!("App Secret: Error - {}\n", e);
}
}
// Timezone secrets
match spoofer.get_secrets() {
Ok(secrets) => {
println!("Timezone Secrets:");
println!(" Number of secrets: {}\n", secrets.len());
for (i, (tz, secret)) in secrets.iter().enumerate() {
println!("Secret {} (timezone: {}):", i + 1, tz);
println!(" Full value: {}", secret);
println!(" Length: {}", secret.len());
println!();
}
}
Err(e) => {
println!("Timezone Secrets: Error - {}\n", e);
}
}
Ok(())
}

View File

@@ -92,6 +92,20 @@ impl Spoofer {
.to_string())
}
/// Extrait l'appSecret depuis le bundle (secret à 32 caractères)
fn get_app_secret(&self) -> Result<String> {
let captures = self
.app_id_regex
.captures(&self.bundle)
.ok_or_else(|| anyhow::anyhow!("AppSecret non trouvé dans le bundle"))?;
Ok(captures
.name("secret")
.ok_or_else(|| anyhow::anyhow!("Groupe secret non trouvé"))?
.as_str()
.to_string())
}
/// Extrait les secrets depuis le bundle
fn get_secrets(&self) -> Result<IndexMap<String, String>> {
// Étape 1: Extraire tous les seed/timezone pairs
@@ -222,8 +236,15 @@ async fn main() -> Result<()> {
Err(e) => eprintln!("Erreur lors de l'extraction de l'App ID: {}", e),
}
// Extraire les secrets
println!("\n--- Secrets ---");
// Extraire l'appSecret (32 caractères)
println!("\n--- AppSecret (32 chars) ---");
match spoofer.get_app_secret() {
Ok(secret) => println!("AppSecret: {}", secret),
Err(e) => eprintln!("Erreur lors de l'extraction de l'AppSecret: {}", e),
}
// Extraire les secrets timezone
println!("\n--- Secrets timezone (décodés base64) ---");
match spoofer.get_secrets() {
Ok(secrets) => {
for (timezone, secret) in secrets {

View File

@@ -0,0 +1,64 @@
//! Test simplifié - va directement à get_file_url
//! Pour comparaison avec Python via fake server
//!
//! Ce test est conçu pour être utilisé avec le fake_qobuz_server.py
//! Les credentials sont hardcodés car le fake server les accepte tous
use anyhow::Result;
use pmoqobuz::api::{QobuzApi, Spoofer};
#[tokio::main]
async fn main() -> Result<()> {
// Initialiser le logging
tracing_subscriber::fmt::init();
println!("=== Test get_file_url (Rust) ===\n");
// Track ID connu (récupéré du test Python)
let track_id = "19557883";
let format_id = 27;
println!("1. Creating QobuzApi (auto-initializes Spoofer)...");
let spoofer = Spoofer::new().await?;
let app_id = spoofer.get_app_id()?;
let app_secret = spoofer.get_app_secret()?;
let mut api = QobuzApi::with_raw_secret(&app_id, &app_secret)?;
// Configurer format_id = 27 (comme Python)
use pmoqobuz::AudioFormat;
api.set_format(AudioFormat::Flac_HiRes_192);
println!(" ✓ API created");
println!(" App ID: {}", app_id);
println!(" Format: 27 (Flac_HiRes_192) - same as Python");
println!("\n2. Logging in...");
println!(" Using fake credentials (fake server accepts all)");
let username = "eric@coissac.eu";
let password = "fake_password";
let user = api.login(username, password).await?;
println!(" ✓ Login successful - User ID: {}", user.user_id);
println!(" Token: {}...", &user.token[..20.min(user.token.len())]);
println!("\n3. Calling track/getFileUrl...");
println!(" Track ID: {}", track_id);
println!(" Format ID: {} (default)", format_id);
println!(" ⚠️ THIS CALL IS SIGNED - Watch fake server logs!\n");
match api.get_file_url(track_id).await {
Ok(stream_info) => {
println!(" ✓ Success!");
println!(" URL: {}...", &stream_info.url[..80.min(stream_info.url.len())]);
println!(" MIME type: {}", stream_info.mime_type);
}
Err(e) => {
println!(" ✗ Failed: {}", e);
return Err(e.into());
}
}
println!("\n=== Test completed successfully! ===");
Ok(())
}

View File

@@ -0,0 +1,57 @@
//! Test de signature MD5 avec timestamp fixe
//! Compare la signature générée par Rust avec celle de Python
use anyhow::Result;
use pmoqobuz::api::{signing, Spoofer};
#[tokio::main]
async fn main() -> Result<()> {
println!("=== Test de signature MD5 ===\n");
// Récupérer le secret depuis Spoofer (timezone secrets, comme Python)
println!("1. Getting secret from Spoofer...");
let spoofer = Spoofer::new().await?;
let timezone_secrets = spoofer.get_secrets()?;
let app_secret = timezone_secrets.values().next().unwrap(); // Premier secret (comme Python)
println!(" ✓ Secret retrieved (length: {} bytes)", app_secret.len());
println!(" Using timezone secret (like Python), not App Secret");
// Paramètres du test
let track_id = "19557883";
let format_id = "27";
let intent = "stream";
// TIMESTAMP FIXE pour comparaison
let timestamp = "1234567890.123456";
println!("\n2. Computing signature with FIXED timestamp:");
println!(" track_id: {}", track_id);
println!(" format_id: {}", format_id);
println!(" intent: {}", intent);
println!(" timestamp: {}", timestamp);
println!(" secret: {}... (first 10 chars)", &app_secret[..10.min(app_secret.len())]);
// Calculer la signature
let signature = signing::sign_track_get_file_url(
format_id,
intent,
track_id,
timestamp,
app_secret.as_bytes(),
);
println!("\n3. Result:");
println!(" Signature: {}", signature);
println!("\n✓ You can now compare this signature with Python using the same timestamp.");
println!("\nPython test command:");
println!(" python3 -c \"");
println!("import hashlib");
println!("secret = '{}'", app_secret);
println!("ts = '{}'", timestamp);
println!("s = 'trackgetFileUrlformat_id27intentstreamtrack_id19557883' + ts");
println!("s = s.encode('ASCII') + secret.encode('utf-8')");
println!("print('Signature:', hashlib.md5(s).hexdigest())");
println!(" \"");
Ok(())
}