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
|
/// Tag an audio file with metadata, cover art, and lyrics
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn tag_audio_file(
|
async fn tag_audio_file(
|
||||||
path: String,
|
path: String,
|
||||||
metadata: tagger::TaggingMetadata,
|
metadata: tagger::TaggingMetadata,
|
||||||
cover_data: Option<Vec<u8>>,
|
cover_data: Option<Vec<u8>>,
|
||||||
embed_lyrics: bool,
|
embed_lyrics: bool,
|
||||||
) -> Result<(), String> {
|
) -> 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)
|
/// 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
|
/// Decrypt Deezer track data
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn decrypt_deezer_track(data: Vec<u8>, track_id: String) -> Result<Vec<u8>, String> {
|
async fn decrypt_deezer_track(data: Vec<u8>, track_id: String) -> Result<Vec<u8>, String> {
|
||||||
Ok(deezer_crypto::decrypt_track(&data, &track_id))
|
// 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)]
|
#[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> {
|
async function readAudioMetadata(filePath: string, format: AudioFormat): Promise<TrackMetadata> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export async function downloadTrack(
|
|||||||
const reader = response.body!.getReader();
|
const reader = response.body!.getReader();
|
||||||
const chunks: Uint8Array[] = [];
|
const chunks: Uint8Array[] = [];
|
||||||
let downloadedBytes = 0;
|
let downloadedBytes = 0;
|
||||||
|
let lastReportedPercentage = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
@@ -89,15 +90,19 @@ export async function downloadTrack(
|
|||||||
chunks.push(value);
|
chunks.push(value);
|
||||||
downloadedBytes += value.length;
|
downloadedBytes += value.length;
|
||||||
|
|
||||||
// Call progress callback
|
// Call progress callback every 5%
|
||||||
if (onProgress && totalSize > 0) {
|
if (onProgress && totalSize > 0) {
|
||||||
const percentage = (downloadedBytes / totalSize) * 100;
|
const percentage = (downloadedBytes / totalSize) * 100;
|
||||||
console.log(`[Download Progress] ${downloadedBytes}/${totalSize} bytes (${percentage.toFixed(1)}%)`);
|
const roundedPercentage = Math.floor(percentage / 5) * 5;
|
||||||
onProgress({
|
|
||||||
downloaded: downloadedBytes,
|
if (roundedPercentage > lastReportedPercentage || percentage === 100) {
|
||||||
total: totalSize,
|
lastReportedPercentage = roundedPercentage;
|
||||||
percentage
|
onProgress({
|
||||||
});
|
downloaded: downloadedBytes,
|
||||||
|
total: totalSize,
|
||||||
|
percentage
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,20 +116,23 @@ export async function downloadTrack(
|
|||||||
|
|
||||||
console.log(`Downloaded ${encryptedData.length} bytes, encrypted: ${isCrypted}`);
|
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
|
// Decrypt if needed
|
||||||
let decryptedData: Uint8Array;
|
let decryptedData: Uint8Array;
|
||||||
|
|
||||||
if (isCrypted) {
|
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
|
// 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}`);
|
console.log(`Decrypting with track ID: ${trackIdForDecryption}`);
|
||||||
// Call Rust decryption function
|
// Call Rust decryption function - Tauri returns Vec<u8> as number[]
|
||||||
const decrypted = await invoke<number[]>('decrypt_deezer_track', {
|
const decryptedArray = await invoke<number[]>('decrypt_deezer_track', {
|
||||||
data: Array.from(encryptedData),
|
data: encryptedData,
|
||||||
trackId: trackIdForDecryption
|
trackId: trackIdForDecryption
|
||||||
});
|
});
|
||||||
decryptedData = new Uint8Array(decrypted);
|
decryptedData = new Uint8Array(decryptedArray);
|
||||||
} else {
|
} else {
|
||||||
decryptedData = encryptedData;
|
decryptedData = encryptedData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,14 +90,12 @@ export async function tagAudioFile(
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const metadata = convertToTaggingMetadata(track);
|
const metadata = convertToTaggingMetadata(track);
|
||||||
|
|
||||||
// Convert Uint8Array to regular array for JSON serialization
|
// Tauri handles Uint8Array -> Vec<u8> conversion automatically
|
||||||
const coverArray = coverData ? Array.from(coverData) : undefined;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await invoke('tag_audio_file', {
|
await invoke('tag_audio_file', {
|
||||||
path: filePath,
|
path: filePath,
|
||||||
metadata,
|
metadata,
|
||||||
coverData: coverArray,
|
coverData,
|
||||||
embedLyrics,
|
embedLyrics,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user