From ca5f79b23a45758618ed48a909899deab4ec12e9 Mon Sep 17 00:00:00 2001 From: Markury Date: Sun, 5 Oct 2025 00:17:33 -0400 Subject: [PATCH] feat(settings): add button to open app data folder --- bun.lock | 3 ++ package.json | 1 + src-tauri/Cargo.lock | 50 +++++++++++++++++++++++++++++ src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 17 +++++++++- src-tauri/src/deezer_crypto.rs | 3 +- src-tauri/src/lib.rs | 35 ++++++++++++-------- src-tauri/src/metadata.rs | 13 ++++---- src/routes/settings/+page.svelte | 23 +++++++++++++ 9 files changed, 122 insertions(+), 24 deletions(-) diff --git a/bun.lock b/bun.lock index 65c5387..eca5ea4 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@tauri-apps/plugin-fs": "^2.4.2", "@tauri-apps/plugin-http": "~2", "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-os": "~2", "@tauri-apps/plugin-process": "~2", "@tauri-apps/plugin-sql": "^2.3.0", "@tauri-apps/plugin-store": "~2", @@ -187,6 +188,8 @@ "@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.0", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-B0LShOYae4CZjN8leiNDbnfjSrTwoZakqKaWpfoH6nXiJwt6Rgj6RnVIffG3DoJiKsffRhMkjmBV9VeilSb4TA=="], + "@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ty5V8XDUIFbSnrk3zsFoP3kzN+vAufYzalJSlmrVhQTImIZa1aL1a03bOaP2vuBvfR+WDRC6NgV2xBl8G07d+w=="], + "@tauri-apps/plugin-process": ["@tauri-apps/plugin-process@2.3.0", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-0DNj6u+9csODiV4seSxxRbnLpeGYdojlcctCuLOCgpH9X3+ckVZIEj6H7tRQ7zqWr7kSTEWnrxtAdBb0FbtrmQ=="], "@tauri-apps/plugin-sql": ["@tauri-apps/plugin-sql@2.3.0", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-JYwIocfsLaDWa41LMiZWuzts7yCJR+EpZPRmgpO7Gd7XiAS9S67dKz306j/k/d9XntB0YopMRBol2OIWMschuA=="], diff --git a/package.json b/package.json index 463a493..c8b339f 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@tauri-apps/plugin-fs": "^2.4.2", "@tauri-apps/plugin-http": "~2", "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-os": "~2", "@tauri-apps/plugin-process": "~2", "@tauri-apps/plugin-sql": "^2.3.0", "@tauri-apps/plugin-store": "~2", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ae5cae8..bf2c4d0 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -21,6 +21,7 @@ dependencies = [ "tauri-plugin-fs", "tauri-plugin-http", "tauri-plugin-opener", + "tauri-plugin-os", "tauri-plugin-process", "tauri-plugin-sql", "tauri-plugin-store", @@ -1471,6 +1472,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" +dependencies = [ + "rustix", + "windows-targets 0.52.6", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -2969,6 +2980,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +dependencies = [ + "log", + "plist", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "pango" version = "0.18.3" @@ -4609,6 +4632,15 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -4919,6 +4951,24 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-os" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1c77ebf6f20417ab2a74e8c310820ba52151406d0c80fbcea7df232e3f6ba" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.17", +] + [[package]] name = "tauri-plugin-process" version = "2.3.0" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c6a8005..9e5b83b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -36,4 +36,5 @@ byteorder = "1.5.0" reqwest = { version = "0.12.23", features = ["stream", "rustls-tls"] } tokio = { version = "1.47.1", features = ["fs", "io-util"] } futures-util = "0.3.31" +tauri-plugin-os = "2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 8f299d9..81799d9 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -8,6 +8,20 @@ "permissions": [ "core:default", "opener:default", + { + "identifier": "opener:allow-open-path", + "allow": [ + { + "path": "$APPDATA" + }, + { + "path": "$APPCONFIG" + }, + { + "path": "$APPLOCALDATA" + } + ] + }, "core:window:default", "core:window:allow-start-dragging", "core:window:allow-minimize", @@ -63,6 +77,7 @@ }, "sql:default", "sql:allow-execute", - "process:default" + "process:default", + "os:default" ] } \ No newline at end of file diff --git a/src-tauri/src/deezer_crypto.rs b/src-tauri/src/deezer_crypto.rs index 5f883ca..0b378ed 100644 --- a/src-tauri/src/deezer_crypto.rs +++ b/src-tauri/src/deezer_crypto.rs @@ -23,8 +23,7 @@ pub fn generate_blowfish_key(track_id: &str) -> Vec { /// Decrypt a single 2048-byte chunk using Blowfish CBC pub fn decrypt_chunk(chunk: &[u8], blowfish_key: &[u8]) -> Vec { - let cipher = Blowfish::::new_from_slice(blowfish_key) - .expect("Invalid key length"); + let cipher = Blowfish::::new_from_slice(blowfish_key).expect("Invalid key length"); let mut result = chunk.to_vec(); let iv = [0u8, 1, 2, 3, 4, 5, 6, 7]; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0538722..7b1267f 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,8 +1,8 @@ use tauri_plugin_sql::{Migration, MigrationKind}; -mod tagger; -mod metadata; mod deezer_crypto; +mod metadata; +mod tagger; // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ #[tauri::command] @@ -58,10 +58,10 @@ async fn download_and_decrypt_track( is_encrypted: bool, window: tauri::Window, ) -> Result<(), String> { - use tokio::io::AsyncWriteExt; - use tokio::fs::File; use deezer_crypto::StreamingDecryptor; use tauri::Emitter; + use tokio::fs::File; + use tokio::io::AsyncWriteExt; // Build HTTP client let client = reqwest::Client::builder() @@ -117,11 +117,14 @@ async fn download_and_decrypt_track( 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 - })); + let _ = window.emit( + "download-progress", + serde_json::json!({ + "downloaded": downloaded_bytes, + "total": total_size as u64, + "percentage": percentage + }), + ); } } } @@ -154,11 +157,14 @@ async fn download_and_decrypt_track( 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 - })); + let _ = window.emit( + "download-progress", + serde_json::json!({ + "downloaded": downloaded_bytes, + "total": total_size as u64, + "percentage": percentage + }), + ); } } } @@ -293,6 +299,7 @@ pub fn run() { }]; tauri::Builder::default() + .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_process::init()) .plugin( tauri_plugin_sql::Builder::new() diff --git a/src-tauri/src/metadata.rs b/src-tauri/src/metadata.rs index 49f1ab2..7431f71 100644 --- a/src-tauri/src/metadata.rs +++ b/src-tauri/src/metadata.rs @@ -1,5 +1,5 @@ -use metaflac::Tag as FlacTag; use id3::{Tag as ID3Tag, TagLike}; +use metaflac::Tag as FlacTag; use serde::{Deserialize, Serialize}; use std::path::Path; @@ -40,8 +40,8 @@ pub fn read_audio_metadata(path: &str) -> Result { /// Read metadata from MP3 file fn read_mp3_metadata(path: &str) -> Result { - let tag = ID3Tag::read_from_path(path) - .map_err(|e| format!("Failed to read MP3 tags: {}", e))?; + let tag = + ID3Tag::read_from_path(path).map_err(|e| format!("Failed to read MP3 tags: {}", e))?; Ok(AudioMetadata { title: tag.title().map(|s| s.to_string()), @@ -55,8 +55,8 @@ fn read_mp3_metadata(path: &str) -> Result { /// Read metadata from FLAC file fn read_flac_metadata(path: &str) -> Result { - let tag = FlacTag::read_from_path(path) - .map_err(|e| format!("Failed to read FLAC tags: {}", e))?; + let tag = + FlacTag::read_from_path(path).map_err(|e| format!("Failed to read FLAC tags: {}", e))?; // Helper to get first value from vorbis comment let get_first = |key: &str| -> Option { @@ -66,8 +66,7 @@ fn read_flac_metadata(path: &str) -> Result { }; // Parse track number - let track_number = get_first("TRACKNUMBER") - .and_then(|s| s.parse::().ok()); + let track_number = get_first("TRACKNUMBER").and_then(|s| s.parse::().ok()); // Get duration from streaminfo block (in samples) let duration = tag.get_streaminfo().map(|info| { diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte index 08a332f..331ed87 100644 --- a/src/routes/settings/+page.svelte +++ b/src/routes/settings/+page.svelte @@ -19,6 +19,8 @@ import { clearDeezerCache } from '$lib/library/deezer-database'; import { open, confirm, message } from '@tauri-apps/plugin-dialog'; import { relaunch } from '@tauri-apps/plugin-process'; + import { appDataDir } from '@tauri-apps/api/path'; + import { openPath } from '@tauri-apps/plugin-opener'; let currentMusicFolder = $state(null); let currentPlaylistsFolder = $state(null); @@ -122,6 +124,21 @@ } } } + + async function openAppDataFolder() { + try { + const dataPath = await appDataDir(); + console.log('App data path:', dataPath); + if (!dataPath) { + throw new Error('Could not get app data directory path'); + } + await openPath(dataPath); + } catch (error) { + console.error('Error opening app data folder:', error); + const errorMessage = error instanceof Error ? error.message : String(error); + await message('Error opening app data folder: ' + errorMessage, { title: 'Error', kind: 'error' }); + } + }
@@ -339,6 +356,12 @@ This will delete all cached Deezer favorites data. The next time you visit the Deezer page, it will refetch from the API.
+ +
+
Open App Data Folder
+ Opens the application data folder containing SQLite databases and other app data. + +
{/if}