mirror of
https://github.com/markuryy/shark.git
synced 2025-12-12 11:41:02 +00:00
feat(settings): add button to open app data folder
This commit is contained in:
3
bun.lock
3
bun.lock
@@ -10,6 +10,7 @@
|
|||||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
"@tauri-apps/plugin-fs": "^2.4.2",
|
||||||
"@tauri-apps/plugin-http": "~2",
|
"@tauri-apps/plugin-http": "~2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
|
"@tauri-apps/plugin-os": "~2",
|
||||||
"@tauri-apps/plugin-process": "~2",
|
"@tauri-apps/plugin-process": "~2",
|
||||||
"@tauri-apps/plugin-sql": "^2.3.0",
|
"@tauri-apps/plugin-sql": "^2.3.0",
|
||||||
"@tauri-apps/plugin-store": "~2",
|
"@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-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-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=="],
|
"@tauri-apps/plugin-sql": ["@tauri-apps/plugin-sql@2.3.0", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-JYwIocfsLaDWa41LMiZWuzts7yCJR+EpZPRmgpO7Gd7XiAS9S67dKz306j/k/d9XntB0YopMRBol2OIWMschuA=="],
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"@tauri-apps/plugin-fs": "^2.4.2",
|
"@tauri-apps/plugin-fs": "^2.4.2",
|
||||||
"@tauri-apps/plugin-http": "~2",
|
"@tauri-apps/plugin-http": "~2",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
|
"@tauri-apps/plugin-os": "~2",
|
||||||
"@tauri-apps/plugin-process": "~2",
|
"@tauri-apps/plugin-process": "~2",
|
||||||
"@tauri-apps/plugin-sql": "^2.3.0",
|
"@tauri-apps/plugin-sql": "^2.3.0",
|
||||||
"@tauri-apps/plugin-store": "~2",
|
"@tauri-apps/plugin-store": "~2",
|
||||||
|
|||||||
50
src-tauri/Cargo.lock
generated
50
src-tauri/Cargo.lock
generated
@@ -21,6 +21,7 @@ dependencies = [
|
|||||||
"tauri-plugin-fs",
|
"tauri-plugin-fs",
|
||||||
"tauri-plugin-http",
|
"tauri-plugin-http",
|
||||||
"tauri-plugin-opener",
|
"tauri-plugin-opener",
|
||||||
|
"tauri-plugin-os",
|
||||||
"tauri-plugin-process",
|
"tauri-plugin-process",
|
||||||
"tauri-plugin-sql",
|
"tauri-plugin-sql",
|
||||||
"tauri-plugin-store",
|
"tauri-plugin-store",
|
||||||
@@ -1471,6 +1472,16 @@ dependencies = [
|
|||||||
"version_check",
|
"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]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
@@ -2969,6 +2980,18 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"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]]
|
[[package]]
|
||||||
name = "pango"
|
name = "pango"
|
||||||
version = "0.18.3"
|
version = "0.18.3"
|
||||||
@@ -4609,6 +4632,15 @@ dependencies = [
|
|||||||
"syn 2.0.106",
|
"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]]
|
[[package]]
|
||||||
name = "system-configuration"
|
name = "system-configuration"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
@@ -4919,6 +4951,24 @@ dependencies = [
|
|||||||
"zbus",
|
"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]]
|
[[package]]
|
||||||
name = "tauri-plugin-process"
|
name = "tauri-plugin-process"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
|
|||||||
@@ -36,4 +36,5 @@ byteorder = "1.5.0"
|
|||||||
reqwest = { version = "0.12.23", features = ["stream", "rustls-tls"] }
|
reqwest = { version = "0.12.23", features = ["stream", "rustls-tls"] }
|
||||||
tokio = { version = "1.47.1", features = ["fs", "io-util"] }
|
tokio = { version = "1.47.1", features = ["fs", "io-util"] }
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
|
tauri-plugin-os = "2"
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,20 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
"opener:default",
|
"opener:default",
|
||||||
|
{
|
||||||
|
"identifier": "opener:allow-open-path",
|
||||||
|
"allow": [
|
||||||
|
{
|
||||||
|
"path": "$APPDATA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$APPCONFIG"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "$APPLOCALDATA"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"core:window:default",
|
"core:window:default",
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"core:window:allow-minimize",
|
"core:window:allow-minimize",
|
||||||
@@ -63,6 +77,7 @@
|
|||||||
},
|
},
|
||||||
"sql:default",
|
"sql:default",
|
||||||
"sql:allow-execute",
|
"sql:allow-execute",
|
||||||
"process:default"
|
"process:default",
|
||||||
|
"os:default"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -23,8 +23,7 @@ pub fn generate_blowfish_key(track_id: &str) -> Vec<u8> {
|
|||||||
|
|
||||||
/// Decrypt a single 2048-byte chunk using Blowfish CBC
|
/// Decrypt a single 2048-byte chunk using Blowfish CBC
|
||||||
pub fn decrypt_chunk(chunk: &[u8], blowfish_key: &[u8]) -> Vec<u8> {
|
pub fn decrypt_chunk(chunk: &[u8], blowfish_key: &[u8]) -> Vec<u8> {
|
||||||
let cipher = Blowfish::<BigEndian>::new_from_slice(blowfish_key)
|
let cipher = Blowfish::<BigEndian>::new_from_slice(blowfish_key).expect("Invalid key length");
|
||||||
.expect("Invalid key length");
|
|
||||||
|
|
||||||
let mut result = chunk.to_vec();
|
let mut result = chunk.to_vec();
|
||||||
let iv = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
let iv = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||||
|
|
||||||
mod tagger;
|
|
||||||
mod metadata;
|
|
||||||
mod deezer_crypto;
|
mod deezer_crypto;
|
||||||
|
mod metadata;
|
||||||
|
mod tagger;
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -58,10 +58,10 @@ async fn download_and_decrypt_track(
|
|||||||
is_encrypted: bool,
|
is_encrypted: bool,
|
||||||
window: tauri::Window,
|
window: tauri::Window,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
use tokio::io::AsyncWriteExt;
|
|
||||||
use tokio::fs::File;
|
|
||||||
use deezer_crypto::StreamingDecryptor;
|
use deezer_crypto::StreamingDecryptor;
|
||||||
use tauri::Emitter;
|
use tauri::Emitter;
|
||||||
|
use tokio::fs::File;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
|
||||||
// Build HTTP client
|
// Build HTTP client
|
||||||
let client = reqwest::Client::builder()
|
let client = reqwest::Client::builder()
|
||||||
@@ -117,11 +117,14 @@ async fn download_and_decrypt_track(
|
|||||||
|
|
||||||
if rounded_percentage > last_reported_percentage || percentage == 100 {
|
if rounded_percentage > last_reported_percentage || percentage == 100 {
|
||||||
last_reported_percentage = rounded_percentage;
|
last_reported_percentage = rounded_percentage;
|
||||||
let _ = window.emit("download-progress", serde_json::json!({
|
let _ = window.emit(
|
||||||
"downloaded": downloaded_bytes,
|
"download-progress",
|
||||||
"total": total_size as u64,
|
serde_json::json!({
|
||||||
"percentage": percentage
|
"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 {
|
if rounded_percentage > last_reported_percentage || percentage == 100 {
|
||||||
last_reported_percentage = rounded_percentage;
|
last_reported_percentage = rounded_percentage;
|
||||||
let _ = window.emit("download-progress", serde_json::json!({
|
let _ = window.emit(
|
||||||
"downloaded": downloaded_bytes,
|
"download-progress",
|
||||||
"total": total_size as u64,
|
serde_json::json!({
|
||||||
"percentage": percentage
|
"downloaded": downloaded_bytes,
|
||||||
}));
|
"total": total_size as u64,
|
||||||
|
"percentage": percentage
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,6 +299,7 @@ pub fn run() {
|
|||||||
}];
|
}];
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.plugin(tauri_plugin_os::init())
|
||||||
.plugin(tauri_plugin_process::init())
|
.plugin(tauri_plugin_process::init())
|
||||||
.plugin(
|
.plugin(
|
||||||
tauri_plugin_sql::Builder::new()
|
tauri_plugin_sql::Builder::new()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use metaflac::Tag as FlacTag;
|
|
||||||
use id3::{Tag as ID3Tag, TagLike};
|
use id3::{Tag as ID3Tag, TagLike};
|
||||||
|
use metaflac::Tag as FlacTag;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ pub fn read_audio_metadata(path: &str) -> Result<AudioMetadata, String> {
|
|||||||
|
|
||||||
/// Read metadata from MP3 file
|
/// Read metadata from MP3 file
|
||||||
fn read_mp3_metadata(path: &str) -> Result<AudioMetadata, String> {
|
fn read_mp3_metadata(path: &str) -> Result<AudioMetadata, String> {
|
||||||
let tag = ID3Tag::read_from_path(path)
|
let tag =
|
||||||
.map_err(|e| format!("Failed to read MP3 tags: {}", e))?;
|
ID3Tag::read_from_path(path).map_err(|e| format!("Failed to read MP3 tags: {}", e))?;
|
||||||
|
|
||||||
Ok(AudioMetadata {
|
Ok(AudioMetadata {
|
||||||
title: tag.title().map(|s| s.to_string()),
|
title: tag.title().map(|s| s.to_string()),
|
||||||
@@ -55,8 +55,8 @@ fn read_mp3_metadata(path: &str) -> Result<AudioMetadata, String> {
|
|||||||
|
|
||||||
/// Read metadata from FLAC file
|
/// Read metadata from FLAC file
|
||||||
fn read_flac_metadata(path: &str) -> Result<AudioMetadata, String> {
|
fn read_flac_metadata(path: &str) -> Result<AudioMetadata, String> {
|
||||||
let tag = FlacTag::read_from_path(path)
|
let tag =
|
||||||
.map_err(|e| format!("Failed to read FLAC tags: {}", e))?;
|
FlacTag::read_from_path(path).map_err(|e| format!("Failed to read FLAC tags: {}", e))?;
|
||||||
|
|
||||||
// Helper to get first value from vorbis comment
|
// Helper to get first value from vorbis comment
|
||||||
let get_first = |key: &str| -> Option<String> {
|
let get_first = |key: &str| -> Option<String> {
|
||||||
@@ -66,8 +66,7 @@ fn read_flac_metadata(path: &str) -> Result<AudioMetadata, String> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Parse track number
|
// Parse track number
|
||||||
let track_number = get_first("TRACKNUMBER")
|
let track_number = get_first("TRACKNUMBER").and_then(|s| s.parse::<u32>().ok());
|
||||||
.and_then(|s| s.parse::<u32>().ok());
|
|
||||||
|
|
||||||
// Get duration from streaminfo block (in samples)
|
// Get duration from streaminfo block (in samples)
|
||||||
let duration = tag.get_streaminfo().map(|info| {
|
let duration = tag.get_streaminfo().map(|info| {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
import { clearDeezerCache } from '$lib/library/deezer-database';
|
import { clearDeezerCache } from '$lib/library/deezer-database';
|
||||||
import { open, confirm, message } from '@tauri-apps/plugin-dialog';
|
import { open, confirm, message } from '@tauri-apps/plugin-dialog';
|
||||||
import { relaunch } from '@tauri-apps/plugin-process';
|
import { relaunch } from '@tauri-apps/plugin-process';
|
||||||
|
import { appDataDir } from '@tauri-apps/api/path';
|
||||||
|
import { openPath } from '@tauri-apps/plugin-opener';
|
||||||
|
|
||||||
let currentMusicFolder = $state<string | null>(null);
|
let currentMusicFolder = $state<string | null>(null);
|
||||||
let currentPlaylistsFolder = $state<string | null>(null);
|
let currentPlaylistsFolder = $state<string | null>(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' });
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div style="padding: 8px;">
|
<div style="padding: 8px;">
|
||||||
@@ -339,6 +356,12 @@
|
|||||||
<small class="help-text">This will delete all cached Deezer favorites data. The next time you visit the Deezer page, it will refetch from the API.</small>
|
<small class="help-text">This will delete all cached Deezer favorites data. The next time you visit the Deezer page, it will refetch from the API.</small>
|
||||||
<button onclick={clearDeezerDatabase}>Clear Deezer Cache</button>
|
<button onclick={clearDeezerDatabase}>Clear Deezer Cache</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field-row-stacked">
|
||||||
|
<div class="setting-heading">Open App Data Folder</div>
|
||||||
|
<small class="help-text">Opens the application data folder containing SQLite databases and other app data.</small>
|
||||||
|
<button onclick={openAppDataFolder}>Open Folder</button>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user