fix(dl): add progress events for tracks from new downloader

This commit is contained in:
2025-10-04 20:58:34 -04:00
parent 96a01bdced
commit 7b84bc32df
2 changed files with 61 additions and 8 deletions

View File

@@ -56,10 +56,12 @@ async fn download_and_decrypt_track(
track_id: String, track_id: String,
output_path: String, output_path: String,
is_encrypted: bool, is_encrypted: bool,
window: tauri::Window,
) -> Result<(), String> { ) -> Result<(), String> {
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
use tokio::fs::File; use tokio::fs::File;
use deezer_crypto::StreamingDecryptor; use deezer_crypto::StreamingDecryptor;
use tauri::Emitter;
// Build HTTP client // Build HTTP client
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
@@ -79,11 +81,16 @@ async fn download_and_decrypt_track(
return Err(format!("HTTP error: {}", response.status())); return Err(format!("HTTP error: {}", response.status()));
} }
let total_size = response.content_length().unwrap_or(0) as f64;
// Open output file // Open output file
let mut file = File::create(&output_path) let mut file = File::create(&output_path)
.await .await
.map_err(|e| format!("Failed to create output file: {}", e))?; .map_err(|e| format!("Failed to create output file: {}", e))?;
let mut downloaded_bytes = 0u64;
let mut last_reported_percentage = 0u8;
// Stream download with optional decryption // Stream download with optional decryption
if is_encrypted { if is_encrypted {
let mut decryptor = StreamingDecryptor::new(&track_id); let mut decryptor = StreamingDecryptor::new(&track_id);
@@ -93,6 +100,7 @@ async fn download_and_decrypt_track(
while let Some(chunk_result) = stream.next().await { while let Some(chunk_result) = stream.next().await {
let chunk = chunk_result.map_err(|e| format!("Download stream error: {}", e))?; let chunk = chunk_result.map_err(|e| format!("Download stream error: {}", e))?;
downloaded_bytes += chunk.len() as u64;
// Decrypt chunk and write to file // Decrypt chunk and write to file
let decrypted = decryptor.process(&chunk); let decrypted = decryptor.process(&chunk);
@@ -101,6 +109,21 @@ async fn download_and_decrypt_track(
.await .await
.map_err(|e| format!("Failed to write to file: {}", e))?; .map_err(|e| format!("Failed to write to file: {}", e))?;
} }
// Emit progress every 5%
if total_size > 0.0 {
let percentage = ((downloaded_bytes as f64 / total_size) * 100.0) as u8;
let rounded_percentage = (percentage / 5) * 5;
if rounded_percentage > last_reported_percentage || percentage == 100 {
last_reported_percentage = rounded_percentage;
let _ = window.emit("download-progress", serde_json::json!({
"downloaded": downloaded_bytes,
"total": total_size as u64,
"percentage": percentage
}));
}
}
} }
// Write any remaining buffered data // Write any remaining buffered data
@@ -118,9 +141,26 @@ async fn download_and_decrypt_track(
while let Some(chunk_result) = stream.next().await { while let Some(chunk_result) = stream.next().await {
let chunk = chunk_result.map_err(|e| format!("Download stream error: {}", e))?; let chunk = chunk_result.map_err(|e| format!("Download stream error: {}", e))?;
downloaded_bytes += chunk.len() as u64;
file.write_all(&chunk) file.write_all(&chunk)
.await .await
.map_err(|e| format!("Failed to write to file: {}", e))?; .map_err(|e| format!("Failed to write to file: {}", e))?;
// Emit progress every 5%
if total_size > 0.0 {
let percentage = ((downloaded_bytes as f64 / total_size) * 100.0) as u8;
let rounded_percentage = (percentage / 5) * 5;
if rounded_percentage > last_reported_percentage || percentage == 100 {
last_reported_percentage = rounded_percentage;
let _ = window.emit("download-progress", serde_json::json!({
"downloaded": downloaded_bytes,
"total": total_size as u64,
"percentage": percentage
}));
}
}
} }
} }

View File

@@ -31,6 +31,7 @@ export async function downloadTrack(
retryCount: number = 0, retryCount: number = 0,
decryptionTrackId?: string decryptionTrackId?: string
): Promise<string> { ): Promise<string> {
const { listen } = await import('@tauri-apps/api/event');
// Generate paths // Generate paths
const paths = generateTrackPath(track, musicFolder, format, false); const paths = generateTrackPath(track, musicFolder, format, false);
@@ -60,16 +61,28 @@ export async function downloadTrack(
// Use the provided decryption track ID (for fallback tracks) or the original track ID // Use the provided decryption track ID (for fallback tracks) or the original track ID
const trackIdForDecryption = decryptionTrackId ? decryptionTrackId.toString() : track.id.toString(); const trackIdForDecryption = decryptionTrackId ? decryptionTrackId.toString() : track.id.toString();
// Download and decrypt in Rust backend (streaming, no memory accumulation) // Set up progress listener
console.log('Downloading and decrypting track in Rust backend...'); const unlisten = await listen<DownloadProgress>('download-progress', (event) => {
await invoke('download_and_decrypt_track', { if (onProgress) {
url: downloadURL, onProgress(event.payload);
trackId: trackIdForDecryption, }
outputPath: paths.tempPath,
isEncrypted: isCrypted
}); });
console.log('Download and decryption complete!'); try {
// Download and decrypt in Rust backend (streaming, no memory accumulation)
console.log('Downloading and decrypting track in Rust backend...');
await invoke('download_and_decrypt_track', {
url: downloadURL,
trackId: trackIdForDecryption,
outputPath: paths.tempPath,
isEncrypted: isCrypted
});
console.log('Download and decryption complete!');
} finally {
// Clean up event listener
unlisten();
}
// Get user settings // Get user settings
const appSettings = get(settings); const appSettings = get(settings);