mirror of
https://github.com/markuryy/shark.git
synced 2025-12-12 19:51:01 +00:00
refactor: move download/decryption to backend to fix UI freezing
Now implements streaming download+decryption entirely in Rust: - Added reqwest/tokio/futures-util dependencies - Created StreamingDecryptor for chunk-by-chunk decryption - New download_and_decrypt_track command streams to disk directly - Frontend simplified to single invoke() call
This commit is contained in:
@@ -36,7 +36,7 @@ fn read_audio_metadata(path: String) -> Result<metadata::AudioMetadata, String>
|
||||
metadata::read_audio_metadata(&path)
|
||||
}
|
||||
|
||||
/// Decrypt Deezer track data
|
||||
/// Decrypt Deezer track data (legacy - kept for backwards compatibility)
|
||||
#[tauri::command]
|
||||
async fn decrypt_deezer_track(data: Vec<u8>, track_id: String) -> Result<Vec<u8>, String> {
|
||||
// Run decryption on a background thread to avoid blocking the UI
|
||||
@@ -49,6 +49,89 @@ async fn decrypt_deezer_track(data: Vec<u8>, track_id: String) -> Result<Vec<u8>
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Download and decrypt a Deezer track, streaming directly to disk
|
||||
#[tauri::command]
|
||||
async fn download_and_decrypt_track(
|
||||
url: String,
|
||||
track_id: String,
|
||||
output_path: String,
|
||||
is_encrypted: bool,
|
||||
) -> Result<(), String> {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::fs::File;
|
||||
use deezer_crypto::StreamingDecryptor;
|
||||
|
||||
// Build HTTP client
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(60))
|
||||
.build()
|
||||
.map_err(|e| format!("Failed to create HTTP client: {}", e))?;
|
||||
|
||||
// Start download
|
||||
let response = client
|
||||
.get(&url)
|
||||
.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36")
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| format!("Download failed: {}", e))?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(format!("HTTP error: {}", response.status()));
|
||||
}
|
||||
|
||||
// Open output file
|
||||
let mut file = File::create(&output_path)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to create output file: {}", e))?;
|
||||
|
||||
// Stream download with optional decryption
|
||||
if is_encrypted {
|
||||
let mut decryptor = StreamingDecryptor::new(&track_id);
|
||||
let mut stream = response.bytes_stream();
|
||||
|
||||
use futures_util::StreamExt;
|
||||
|
||||
while let Some(chunk_result) = stream.next().await {
|
||||
let chunk = chunk_result.map_err(|e| format!("Download stream error: {}", e))?;
|
||||
|
||||
// Decrypt chunk and write to file
|
||||
let decrypted = decryptor.process(&chunk);
|
||||
if !decrypted.is_empty() {
|
||||
file.write_all(&decrypted)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to write to file: {}", e))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Write any remaining buffered data
|
||||
let final_data = decryptor.finalize();
|
||||
if !final_data.is_empty() {
|
||||
file.write_all(&final_data)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to write final data: {}", e))?;
|
||||
}
|
||||
} else {
|
||||
// No encryption - just stream directly
|
||||
let mut stream = response.bytes_stream();
|
||||
|
||||
use futures_util::StreamExt;
|
||||
|
||||
while let Some(chunk_result) = stream.next().await {
|
||||
let chunk = chunk_result.map_err(|e| format!("Download stream error: {}", e))?;
|
||||
file.write_all(&chunk)
|
||||
.await
|
||||
.map_err(|e| format!("Failed to write to file: {}", e))?;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all data is flushed
|
||||
file.flush()
|
||||
.await
|
||||
.map_err(|e| format!("Failed to flush file: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let library_migrations = vec![Migration {
|
||||
@@ -171,7 +254,8 @@ pub fn run() {
|
||||
greet,
|
||||
tag_audio_file,
|
||||
read_audio_metadata,
|
||||
decrypt_deezer_track
|
||||
decrypt_deezer_track,
|
||||
download_and_decrypt_track
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
Reference in New Issue
Block a user