mirror of
https://github.com/markuryy/shark.git
synced 2025-12-12 11:41:02 +00:00
refactor(api): make audio tagging and decryption async to avoid UI blocking
This commit is contained in:
@@ -12,13 +12,22 @@ fn greet(name: &str) -> String {
|
||||
|
||||
/// Tag an audio file with metadata, cover art, and lyrics
|
||||
#[tauri::command]
|
||||
fn tag_audio_file(
|
||||
async fn tag_audio_file(
|
||||
path: String,
|
||||
metadata: tagger::TaggingMetadata,
|
||||
cover_data: Option<Vec<u8>>,
|
||||
embed_lyrics: bool,
|
||||
) -> Result<(), String> {
|
||||
tagger::tag_audio_file(&path, &metadata, cover_data.as_deref(), embed_lyrics)
|
||||
// Run tagging on a background thread to avoid blocking the UI
|
||||
tauri::async_runtime::spawn_blocking(move || {
|
||||
tagger::tag_audio_file(&path, &metadata, cover_data.as_deref(), embed_lyrics)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| format!("Tagging task failed: {}", e))?
|
||||
// Flatten the inner Result
|
||||
?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read metadata from an audio file (MP3 or FLAC)
|
||||
@@ -29,8 +38,15 @@ fn read_audio_metadata(path: String) -> Result<metadata::AudioMetadata, String>
|
||||
|
||||
/// Decrypt Deezer track data
|
||||
#[tauri::command]
|
||||
fn decrypt_deezer_track(data: Vec<u8>, track_id: String) -> Result<Vec<u8>, String> {
|
||||
Ok(deezer_crypto::decrypt_track(&data, &track_id))
|
||||
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
|
||||
let result = tauri::async_runtime::spawn_blocking(move || {
|
||||
deezer_crypto::decrypt_track(&data, &track_id)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| format!("Decryption task failed: {}", e))?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
|
||||
@@ -142,7 +142,7 @@ async function findActualFilePath(basePath: string): Promise<string | null> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read metadata from audio file using Rust backend
|
||||
* Read metadata from audio file on backend
|
||||
*/
|
||||
async function readAudioMetadata(filePath: string, format: AudioFormat): Promise<TrackMetadata> {
|
||||
try {
|
||||
|
||||
@@ -81,6 +81,7 @@ export async function downloadTrack(
|
||||
const reader = response.body!.getReader();
|
||||
const chunks: Uint8Array[] = [];
|
||||
let downloadedBytes = 0;
|
||||
let lastReportedPercentage = 0;
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
@@ -89,15 +90,19 @@ export async function downloadTrack(
|
||||
chunks.push(value);
|
||||
downloadedBytes += value.length;
|
||||
|
||||
// Call progress callback
|
||||
// Call progress callback every 5%
|
||||
if (onProgress && totalSize > 0) {
|
||||
const percentage = (downloadedBytes / totalSize) * 100;
|
||||
console.log(`[Download Progress] ${downloadedBytes}/${totalSize} bytes (${percentage.toFixed(1)}%)`);
|
||||
onProgress({
|
||||
downloaded: downloadedBytes,
|
||||
total: totalSize,
|
||||
percentage
|
||||
});
|
||||
const roundedPercentage = Math.floor(percentage / 5) * 5;
|
||||
|
||||
if (roundedPercentage > lastReportedPercentage || percentage === 100) {
|
||||
lastReportedPercentage = roundedPercentage;
|
||||
onProgress({
|
||||
downloaded: downloadedBytes,
|
||||
total: totalSize,
|
||||
percentage
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,20 +116,23 @@ export async function downloadTrack(
|
||||
|
||||
console.log(`Downloaded ${encryptedData.length} bytes, encrypted: ${isCrypted}`);
|
||||
|
||||
// Yield to the browser to keep UI responsive
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
|
||||
// Decrypt if needed
|
||||
let decryptedData: Uint8Array;
|
||||
|
||||
if (isCrypted) {
|
||||
console.log('Decrypting track using Rust...');
|
||||
console.log('Decrypting track...');
|
||||
// Use the provided decryption track ID (for fallback tracks) or the original track ID
|
||||
const trackIdForDecryption = decryptionTrackId || track.id.toString();
|
||||
const trackIdForDecryption = decryptionTrackId ? decryptionTrackId.toString() : track.id.toString();
|
||||
console.log(`Decrypting with track ID: ${trackIdForDecryption}`);
|
||||
// Call Rust decryption function
|
||||
const decrypted = await invoke<number[]>('decrypt_deezer_track', {
|
||||
data: Array.from(encryptedData),
|
||||
// Call Rust decryption function - Tauri returns Vec<u8> as number[]
|
||||
const decryptedArray = await invoke<number[]>('decrypt_deezer_track', {
|
||||
data: encryptedData,
|
||||
trackId: trackIdForDecryption
|
||||
});
|
||||
decryptedData = new Uint8Array(decrypted);
|
||||
decryptedData = new Uint8Array(decryptedArray);
|
||||
} else {
|
||||
decryptedData = encryptedData;
|
||||
}
|
||||
|
||||
@@ -90,14 +90,12 @@ export async function tagAudioFile(
|
||||
): Promise<void> {
|
||||
const metadata = convertToTaggingMetadata(track);
|
||||
|
||||
// Convert Uint8Array to regular array for JSON serialization
|
||||
const coverArray = coverData ? Array.from(coverData) : undefined;
|
||||
|
||||
// Tauri handles Uint8Array -> Vec<u8> conversion automatically
|
||||
try {
|
||||
await invoke('tag_audio_file', {
|
||||
path: filePath,
|
||||
metadata,
|
||||
coverData: coverArray,
|
||||
coverData,
|
||||
embedLyrics,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user