From d89088e8fefef2906f2a3c6fac16858824ef27b7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 03:05:40 +0000 Subject: [PATCH 001/210] chore(deps-dev): bump picomatch from 4.0.3 to 4.0.4 Bumps [picomatch](https://github.com/micromatch/picomatch) from 4.0.3 to 4.0.4. - [Release notes](https://github.com/micromatch/picomatch/releases) - [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/picomatch/compare/4.0.3...4.0.4) --- updated-dependencies: - dependency-name: picomatch dependency-version: 4.0.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72ec3386a..2f58a3e37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2517,9 +2517,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { From ef1915f83d4d8d931ecc5a2f36b18e84590d06ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 05:35:32 +0000 Subject: [PATCH 002/210] chore(deps): bump axum from 0.7.9 to 0.8.8 in /src-tauri Bumps [axum](https://github.com/tokio-rs/axum) from 0.7.9 to 0.8.8. - [Release notes](https://github.com/tokio-rs/axum/releases) - [Changelog](https://github.com/tokio-rs/axum/blob/main/CHANGELOG.md) - [Commits](https://github.com/tokio-rs/axum/compare/axum-v0.7.9...axum-v0.8.8) --- updated-dependencies: - dependency-name: axum dependency-version: 0.8.8 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 56 ++++++++++++++++++++++++++++++++------------ src-tauri/Cargo.toml | 2 +- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c97bd5d5f..dcd90518f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -463,14 +463,14 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" dependencies = [ - "async-trait", "axum-core", "base64 0.22.1", "bytes", + "form_urlencoded", "futures-util", "http", "http-body", @@ -483,15 +483,14 @@ dependencies = [ "mime", "percent-encoding", "pin-project-lite", - "rustversion", - "serde", + "serde_core", "serde_json", "serde_path_to_error", "serde_urlencoded", "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.28.0", "tower", "tower-layer", "tower-service", @@ -500,19 +499,17 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ - "async-trait", "bytes", - "futures-util", + "futures-core", "http", "http-body", "http-body-util", "mime", "pin-project-lite", - "rustversion", "sync_wrapper", "tower-layer", "tower-service", @@ -3265,9 +3262,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "md-5" @@ -4755,7 +4752,7 @@ dependencies = [ "serde", "thiserror 2.0.18", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.24.0", ] [[package]] @@ -6877,10 +6874,22 @@ dependencies = [ "rustls-pki-types", "tokio", "tokio-rustls", - "tungstenite", + "tungstenite 0.24.0", "webpki-roots 0.26.11", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.28.0", +] + [[package]] name = "tokio-util" version = "0.7.18" @@ -7153,6 +7162,23 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "typeid" version = "1.0.3" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 789c2ac08..86445ea44 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -49,7 +49,7 @@ async-trait = "0.1" tokio-stream = { version = "0.1", features = ["sync"] } # Local API server -axum = { version = "0.7", features = ["ws"] } +axum = { version = "0.8", features = ["ws"] } axum-server = { version = "0.8", features = ["tls-rustls"] } rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"] } tower-http = { version = "0.5", features = ["cors"] } From 7c0d67f157ebab307f352e02ffde4674f4f1106e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 05:35:44 +0000 Subject: [PATCH 003/210] chore(deps): bump image from 0.25.9 to 0.25.10 in /src-tauri Bumps [image](https://github.com/image-rs/image) from 0.25.9 to 0.25.10. - [Changelog](https://github.com/image-rs/image/blob/v0.25.10/CHANGES.md) - [Commits](https://github.com/image-rs/image/compare/v0.25.9...v0.25.10) --- updated-dependencies: - dependency-name: image dependency-version: 0.25.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c97bd5d5f..39a7d3549 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2759,9 +2759,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.9" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", @@ -2770,8 +2770,8 @@ dependencies = [ "num-traits", "png 0.18.1", "tiff", - "zune-core 0.5.1", - "zune-jpeg 0.5.12", + "zune-core", + "zune-jpeg", ] [[package]] @@ -3363,9 +3363,9 @@ dependencies = [ [[package]] name = "moxcms" -version = "0.7.11" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" dependencies = [ "num-traits", "pxfm", @@ -6724,16 +6724,16 @@ dependencies = [ [[package]] name = "tiff" -version = "0.10.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" dependencies = [ "fax", "flate2", "half", "quick-error", "weezl", - "zune-jpeg 0.4.21", + "zune-jpeg", ] [[package]] @@ -8748,34 +8748,19 @@ version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - [[package]] name = "zune-core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" -[[package]] -name = "zune-jpeg" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" -dependencies = [ - "zune-core 0.4.12", -] - [[package]] name = "zune-jpeg" version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe" dependencies = [ - "zune-core 0.5.1", + "zune-core", ] [[package]] From ce980f2c7be94440dc45c29f31feaea5ea6d5063 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 05:35:47 +0000 Subject: [PATCH 004/210] chore(deps-dev): bump svelte from 5.54.0 to 5.55.0 Bumps [svelte](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte) from 5.54.0 to 5.55.0. - [Release notes](https://github.com/sveltejs/svelte/releases) - [Changelog](https://github.com/sveltejs/svelte/blob/main/packages/svelte/CHANGELOG.md) - [Commits](https://github.com/sveltejs/svelte/commits/svelte@5.55.0/packages/svelte) --- updated-dependencies: - dependency-name: svelte dependency-version: 5.55.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72ec3386a..5c917dd30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@tauri-apps/cli": "^2", "@testing-library/svelte": "^5.3.1", "jsdom": "^27.4.0", - "svelte": "^5.54.0", + "svelte": "^5.55.0", "svelte-check": "^4.4.5", "typescript": "~5.9.3", "vite": "^6.0.3", @@ -2738,9 +2738,9 @@ "license": "MIT" }, "node_modules/svelte": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.54.0.tgz", - "integrity": "sha512-TTDxwYnHkova6Wsyj1PGt9TByuWqvMoeY1bQiuAf2DM/JeDSMw7FjRKzk8K/5mJ99vGOKhbCqTDpyAKwjp4igg==", + "version": "5.55.0", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.0.tgz", + "integrity": "sha512-SThllKq6TRMBwPtat7ASnm/9CDXnIhBR0NPGw0ujn2DVYx9rVwsPZxDaDQcYGdUz/3BYVsCzdq7pZarRQoGvtw==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", diff --git a/package.json b/package.json index 1ef619e3a..4e2cadfb0 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@tauri-apps/cli": "^2", "@testing-library/svelte": "^5.3.1", "jsdom": "^27.4.0", - "svelte": "^5.54.0", + "svelte": "^5.55.0", "svelte-check": "^4.4.5", "typescript": "~5.9.3", "vite": "^6.0.3", From 15577998562353b19dd60093661b395875846a8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 05:35:54 +0000 Subject: [PATCH 005/210] chore(deps): bump lucide-svelte from 0.562.0 to 1.0.1 Bumps [lucide-svelte](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-svelte) from 0.562.0 to 1.0.1. - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.0.1/packages/lucide-svelte) --- updated-dependencies: - dependency-name: lucide-svelte dependency-version: 1.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72ec3386a..6217649b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@tauri-apps/plugin-clipboard-manager": "^2", "@tauri-apps/plugin-dialog": "^2.5.0", "@tauri-apps/plugin-opener": "^2", - "lucide-svelte": "^0.562.0", + "lucide-svelte": "^1.0.1", "svelte-i18n": "^4.0.0" }, "devDependencies": { @@ -2374,9 +2374,9 @@ } }, "node_modules/lucide-svelte": { - "version": "0.562.0", - "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.562.0.tgz", - "integrity": "sha512-kSJDH/55lf0mun/o4nqWBXOcq0fWYzPeIjbTD97ywoeumAB9kWxtM06gC7oynqjtK3XhAljWSz5RafIzPEYIQA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-1.0.1.tgz", + "integrity": "sha512-WvzZgk0pqzgda+AErLvgWxHkfg/+GgUwqKMRHvzt0IqyMdmyEDzDCk3Z+Wo/3y753oIgx8u9Q4eUbWkghFa8Jg==", "license": "ISC", "peerDependencies": { "svelte": "^3 || ^4 || ^5.0.0-next.42" diff --git a/package.json b/package.json index 1ef619e3a..455e81872 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@tauri-apps/plugin-clipboard-manager": "^2", "@tauri-apps/plugin-dialog": "^2.5.0", "@tauri-apps/plugin-opener": "^2", - "lucide-svelte": "^0.562.0", + "lucide-svelte": "^1.0.1", "svelte-i18n": "^4.0.0" }, "devDependencies": { From 63dd054815012f493c5c667f319f589341671e64 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 05:35:57 +0000 Subject: [PATCH 006/210] chore(deps): bump lofty from 0.18.2 to 0.23.3 in /src-tauri Bumps [lofty](https://github.com/Serial-ATA/lofty-rs) from 0.18.2 to 0.23.3. - [Release notes](https://github.com/Serial-ATA/lofty-rs/releases) - [Changelog](https://github.com/Serial-ATA/lofty-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/Serial-ATA/lofty-rs/compare/0.18.2...0.23.3) --- updated-dependencies: - dependency-name: lofty dependency-version: 0.23.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 43 +++++++++++++++++++++++++++++++++++++++---- src-tauri/Cargo.toml | 2 +- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c97bd5d5f..0efa3835f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -3167,9 +3167,24 @@ dependencies = [ "byteorder", "data-encoding", "flate2", - "lofty_attr", + "lofty_attr 0.9.0", "log", - "ogg_pager", + "ogg_pager 0.6.1", + "paste", +] + +[[package]] +name = "lofty" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0c107dba5af049cf1c36b646fc1ba0cd2705f40d766d2c4c64f1b797c5fbed" +dependencies = [ + "byteorder", + "data-encoding", + "flate2", + "lofty_attr 0.12.0", + "log", + "ogg_pager 0.7.1", "paste", ] @@ -3184,6 +3199,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "lofty_attr" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458ace39169e4b83c4f77ae3d42d5d1d11c422feef590219a97c973d3b524557" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "log" version = "0.4.29" @@ -3791,6 +3817,15 @@ dependencies = [ "byteorder", ] +[[package]] +name = "ogg_pager" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6d1ca8364b84e0cf725eed06b1460c44671e6c0fb28765f5262de3ece07fdc" +dependencies = [ + "byteorder", +] + [[package]] name = "oid-registry" version = "0.8.1" @@ -4574,7 +4609,7 @@ dependencies = [ "dirs 5.0.1", "glob", "image", - "lofty", + "lofty 0.18.2", "log", "rusqlite", "serde", @@ -4619,7 +4654,7 @@ dependencies = [ "image", "keyring", "lazy_static", - "lofty", + "lofty 0.23.3", "log", "md-5", "mupdf", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 789c2ac08..48047b981 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -115,7 +115,7 @@ open = "5" # Local library walkdir = "2" -lofty = "0.18" +lofty = "0.23" rusqlite = { version = "0.31", features = ["bundled"] } dirs = "5" filetime = "0.2" From d0064d122a234f284e9f5328ce8e4cb74bf61ff6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 05:36:08 +0000 Subject: [PATCH 007/210] chore(deps): bump souvlaki from 0.7.3 to 0.8.3 in /src-tauri Bumps [souvlaki](https://github.com/Sinono3/souvlaki) from 0.7.3 to 0.8.3. - [Release notes](https://github.com/Sinono3/souvlaki/releases) - [Changelog](https://github.com/Sinono3/souvlaki/blob/master/CHANGELOG.md) - [Commits](https://github.com/Sinono3/souvlaki/compare/0.7.3...0.8.3) --- updated-dependencies: - dependency-name: souvlaki dependency-version: 0.8.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 5 +++-- src-tauri/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c97bd5d5f..635863e26 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -5880,10 +5880,11 @@ dependencies = [ [[package]] name = "souvlaki" -version = "0.7.3" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4544ba17df4ac03d6503ae8abba19adad3ae89203a425945dc4c12d7790bfa" +checksum = "5855c8f31521af07d896b852eaa9eca974ddd3211fc2ae292e58dda8eb129bc8" dependencies = [ + "base64 0.22.1", "block", "cocoa", "core-graphics 0.22.3", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 789c2ac08..8c0160b31 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -91,7 +91,7 @@ spectrum-analyzer = "1.5" ebur128 = "0.1" # Media controls (MPRIS on Linux) -souvlaki = "0.7" +souvlaki = "0.8" # Desktop notifications → ashpd in [target.linux] deps below From e5556f78427b4c783b1b7b3e4d8c685833817efe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 05:36:29 +0000 Subject: [PATCH 008/210] chore(deps): bump rand from 0.8.5 to 0.10.0 in /src-tauri Bumps [rand](https://github.com/rust-random/rand) from 0.8.5 to 0.10.0. - [Release notes](https://github.com/rust-random/rand/releases) - [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-random/rand/compare/0.8.5...0.10.0) --- updated-dependencies: - dependency-name: rand dependency-version: 0.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index c97bd5d5f..0e37ea1d8 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4638,7 +4638,7 @@ dependencies = [ "qconnect-core", "qconnect-transport-ws", "qrcode-generator", - "rand 0.8.5", + "rand 0.10.0", "rcgen", "regex", "reqwest 0.12.28", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 789c2ac08..4748db1a9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -78,7 +78,7 @@ log = "0.4" env_logger = "0.11" # Random number generation (for shuffle) -rand = "0.8" +rand = "0.10" # Audio playback rodio = { version = "0.22", features = ["symphonia-all"] } From 10d3cfc27077dc0b01e09793caa878ce5c324ea5 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 26 Mar 2026 09:15:32 +0100 Subject: [PATCH 009/210] refactor(i18n): add translation strings to AlbumMenu --- src/lib/components/AlbumMenu.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/components/AlbumMenu.svelte b/src/lib/components/AlbumMenu.svelte index 65f989336..8bbf8bd2a 100644 --- a/src/lib/components/AlbumMenu.svelte +++ b/src/lib/components/AlbumMenu.svelte @@ -435,14 +435,14 @@ }} > - Make album available offline + {$t('actions.makeAvailableOffline')} {#if downloadOpen} @@ -451,7 +451,7 @@ {:else} {/if} {/if} From b389efccd7d5c3df14f01f2121b83e480416be40 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 26 Mar 2026 09:15:51 +0100 Subject: [PATCH 010/210] refactor(i18n): add translation strings to AlbumDetailView --- .../components/views/AlbumDetailView.svelte | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/lib/components/views/AlbumDetailView.svelte b/src/lib/components/views/AlbumDetailView.svelte index 0d7600990..3b9f4dd63 100644 --- a/src/lib/components/views/AlbumDetailView.svelte +++ b/src/lib/components/views/AlbumDetailView.svelte @@ -3,7 +3,7 @@ import { invoke, convertFileSrc } from '@tauri-apps/api/core'; import { open, save } from '@tauri-apps/plugin-dialog'; import { openUrl } from '@tauri-apps/plugin-opener'; - import { t } from 'svelte-i18n'; + import { t, locale } from 'svelte-i18n'; import { showToast } from '$lib/stores/toastStore'; import { hasCustomAlbumCover, @@ -319,7 +319,7 @@ if (album.releaseDate) { const date = new Date(album.releaseDate); if (!isNaN(date.getTime())) { - return date.toLocaleDateString('en-US', { + return date.toLocaleDateString($locale ? $locale : 'en-us', { year: 'numeric', month: 'long', day: 'numeric' @@ -479,7 +479,7 @@ @@ -518,21 +518,21 @@ • {album.genre}
{album.quality}
-
{album.trackCount} tracks • {album.duration}
+
{album.trackCount} {$t('album.tracks')} • {album.duration}
@@ -541,7 +541,7 @@ class:is-active={isFavorite} onclick={toggleFavorite} disabled={isFavoriteLoading} - title={isFavorite ? 'Remove from favorites' : 'Add to favorites'} + title={isFavorite ? $t('actions.removeFromFavorites') : $t('actions.addToFavorites')} > @@ -615,9 +615,9 @@
#
-
Title
-
Duration
-
Quality
+
{$t('tracklist.title')}
+
{$t('tracklist.duration')}
+
{$t('tracklist.quality')}
@@ -697,7 +697,7 @@ {#if filteredArtistAlbums.length > 0 && !isVariousArtists}
-

By the same artist

+

{$t('album.sameArtist')}

{#if hasMoreThanVisible}
From fa2061afba1fb9c0fbf168dc107ef4dd4e858b92 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 26 Mar 2026 09:16:16 +0100 Subject: [PATCH 011/210] refactor(i18n): add translation strings to PlaylistDetailView --- src/lib/components/views/PlaylistDetailView.svelte | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/components/views/PlaylistDetailView.svelte b/src/lib/components/views/PlaylistDetailView.svelte index bbec7a9a6..829276b69 100644 --- a/src/lib/components/views/PlaylistDetailView.svelte +++ b/src/lib/components/views/PlaylistDetailView.svelte @@ -1520,9 +1520,9 @@ const hours = Math.floor(seconds / 3600); const mins = Math.floor((seconds % 3600) / 60); if (hours > 0) { - return `${hours} hr ${mins} min`; + return `${hours} ` + $t('time.hours') + ` ${mins} ` + $t('time.minutes'); } - return `${mins} min`; + return `${mins} ` + $t('time.minutes'); } function resolveArtworkPath(path: string): string { @@ -2287,10 +2287,10 @@ {/if}
#
-
{$t('playlist.trackTitle')}
-
{$t('playlist.trackAlbum')}
-
{$t('playlist.trackDuration')}
-
{$t('playlist.trackQuality')}
+
{$t('tracklist.title')}
+
{$t('tracklist.album')}
+
{$t('tracklist.duration')}
+
{$t('tracklist.quality')}
From 67522aff30ad9175fad5221ee5cb83fb9f17fb7e Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 26 Mar 2026 09:16:51 +0100 Subject: [PATCH 012/210] refactor(i18n): add translation strings to WeeklySuggestView --- .../components/views/WeeklySuggestView.svelte | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/lib/components/views/WeeklySuggestView.svelte b/src/lib/components/views/WeeklySuggestView.svelte index 67ed2c01e..8c01b614b 100644 --- a/src/lib/components/views/WeeklySuggestView.svelte +++ b/src/lib/components/views/WeeklySuggestView.svelte @@ -1,7 +1,7 @@

- QBZ detected it's running inside a Flatpak sandbox. - There are some considerations to ensure the best experience. + {$t('updates.runningInsideSandbox', { values: { env: environment } })} {$t('updates.flatpak.intro')}

-

What you should know:

+

{$t('updates.flatpak.whatYouShouldKnow.heading')}

    -
  • Access to external music folders (NAS, network mounts) requires manual permission grants
  • -
  • Some audio features may behave differently due to sandbox restrictions
  • -
  • The Settings page includes Flatpak-specific commands you can copy and run
  • +
  • {$t('updates.flatpak.whatYouShouldKnow.externalMusicFolderAccess')}
  • +
  • {$t('updates.flatpak.whatYouShouldKnow.audioFeatures')}
  • +
  • {$t('updates.flatpak.whatYouShouldKnow.settingsPage')}

- Check the Flatpak Sandbox section in Settings for detailed instructions and copy-paste commands. + {$t('updates.flatpak.note')}

{#snippet footer()} {/snippet} From 4b0cd5acf71ffe19a174281514b4ac3e9bb2eab3 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 09:11:49 +0200 Subject: [PATCH 107/210] feat(i18n): add translation strings to SnapWelcomeModal --- .../updates/SnapWelcomeModal.svelte | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/lib/components/updates/SnapWelcomeModal.svelte b/src/lib/components/updates/SnapWelcomeModal.svelte index 9fc769228..8ed05a776 100644 --- a/src/lib/components/updates/SnapWelcomeModal.svelte +++ b/src/lib/components/updates/SnapWelcomeModal.svelte @@ -1,6 +1,7 @@

- QBZ detected it's running inside a Snap sandbox. - Some audio plugs need to be connected manually for the best experience. + {$t('updates.runningInsideSandbox', { values: { env: environment } })} {$t('updates.snap.intro')}

-

Required plug connections:

+

{$t('updates.snap.requiredPlugConnections')}

@@ -75,19 +77,19 @@ sudo snap connect qbz-player:mpris`;
-

Optional (for external drives / NAS):

+

{$t('updates.snap.optional')}

@@ -95,14 +97,14 @@ sudo snap connect qbz-player:mpris`;

- Run these commands in a terminal, then restart QBZ. This only needs to be done once. + {$t('updates.snap.note')}

{#snippet footer()} {/snippet} From 60f0a7808a52543f1b087ffe9003528d651ae9dd Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 09:12:07 +0200 Subject: [PATCH 108/210] feat(i18n): add translation strings to UpdateAvailableModal --- src/lib/components/updates/UpdateAvailableModal.svelte | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/components/updates/UpdateAvailableModal.svelte b/src/lib/components/updates/UpdateAvailableModal.svelte index 1b0a330e2..b8d89af50 100644 --- a/src/lib/components/updates/UpdateAvailableModal.svelte +++ b/src/lib/components/updates/UpdateAvailableModal.svelte @@ -1,5 +1,6 @@ - +
-

Can be re-enabled from Settings

+

{$t('udpates.updateReminder.hint')}

{#snippet footer()} {/snippet}
From 2677da680f0c348316be46708644cdfabba3395e Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 09:13:21 +0200 Subject: [PATCH 111/210] feat(i18n): add translation strings to WhatsNewModal --- src/lib/components/updates/WhatsNewModal.svelte | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib/components/updates/WhatsNewModal.svelte b/src/lib/components/updates/WhatsNewModal.svelte index 7b2ec303d..ff47da84d 100644 --- a/src/lib/components/updates/WhatsNewModal.svelte +++ b/src/lib/components/updates/WhatsNewModal.svelte @@ -1,6 +1,7 @@ @@ -98,15 +99,15 @@ {@html releaseHtml}
{:else} -

Release notes are not available for this version.

+

{$t('updates.whatsNew.releaseNotesNotAvailableForVersion')}

{/if} {:else} -

Release notes are not available.

+

{$t('updates.whatsNew.releaseNotesNotAvailable')}

{/if} {#snippet footer()} {/snippet} From 820b513183b257c5ca075c2fb44bcb6574b471fa Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 09:13:36 +0200 Subject: [PATCH 112/210] feat(i18n): add translation strings to BitPerfectAppSelector --- src/lib/components/wizard/BitPerfectAppSelector.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/wizard/BitPerfectAppSelector.svelte b/src/lib/components/wizard/BitPerfectAppSelector.svelte index 6bdf24750..8dd4b0bcc 100644 --- a/src/lib/components/wizard/BitPerfectAppSelector.svelte +++ b/src/lib/components/wizard/BitPerfectAppSelector.svelte @@ -98,7 +98,7 @@ {#if selectedApps.filter(a => !predefinedApps.some(p => p.binary === a)).length > 0}
- Custom apps: + {$t('dacWizard.pulseConfig.customApps')} {#each selectedApps.filter(a => !predefinedApps.some(p => p.binary === a)) as app} {app} From bf47ad7be8f7cc718a7366ced03710fc3376c6e2 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 09:15:35 +0200 Subject: [PATCH 113/210] feat(i18n): add english translation strings --- src/lib/i18n/locales/en.json | 68 +++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index 599e1843d..7885bc599 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -273,6 +273,8 @@ "by": "by", "released": "Released", "releaseDate": "Release Date", + "releasedBy": "Released by", + "releasedOn": "on", "label": "Label", "genre": "Genre", "duration": "Duration", @@ -553,6 +555,8 @@ "recentlyPlayed": "Recently Played", "noHistoryYet": "No tracks played yet", "historyEmpty": "No tracks played yet", + "enableInfinitePlay": "Enable Infinite Play", + "disableInfinitePlay": "Disable Infinite Play", "infinitePlay": "Infinite Play", "infinitePlayEnabled": "Infinite play enabled", "infinitePlayDisabled": "Infinite play disabled", @@ -597,6 +601,7 @@ "noTracksMatch": "No tracks match \"{query}\"", "noTrackPlaying": "No track playing", "castToDevice": "Cast to device", + "connectedTo": "Connected To", "castingManage": "Casting - Click to manage", "qobuzConnect": "Qobuz Connect", "qobuzConnectManage": "Qobuz Connect - Click to manage", @@ -604,9 +609,12 @@ "fullScreen": "Immersive Mode", "exitFullScreen": "Exit Immersive Mode", "maximize": "Maximize", + "maximizeWindow": "Maximize Window", "restore": "Restore", + "restoreWindow": "Restore Window", "minimize": "Minimize", "exitImmersive": "Exit Immersive", + "exitImmersiveWithKey": "Exit Immersive ({key})", "dragWindow": "Move Window", "normalizationOff": "Volume Normalization: Off", "normalizationOn": "Volume Normalization: On", @@ -621,7 +629,10 @@ "miniWindowControls": "Mini Player Controls", "miniAlwaysOnTopEnable": "Always on Top", "miniAlwaysOnTopDisable": "Disable Always on Top", - "miniAlwaysOnTopLimited": "Always-on-top is limited by your window manager" + "miniAlwaysOnTopLimited": "Always-on-top is limited by your window manager", + "fullscreenWithKey": "Fullscreen ({key})", + "exitFullscreenWithKey": "Exit Fullscreen ({key})", + "closeApp": "Close App" }, "audioBadges": { "castingTo": "Casting to", @@ -664,6 +675,7 @@ "play": "Play", "playAll": "Play All", "playNow": "Play Now", + "playRadio": "Play Radio", "shuffle": "Shuffle", "shuffleAll": "Shuffle All", "playNext": "Play Next", @@ -729,7 +741,14 @@ }, "newFolder": "New folder", "previousArtists": "Previous artists", - "nextArtists": "Next artists" + "nextArtists": "Next artists", + "qobuzLink": "Qobuz™ link", + "switchToSplitView": "Switch to Split View (V)", + "switchToImmersiveView": "Switch to Immersive View (V)", + "gotIt": "Got it", + "copyCommand": "Copy command", + "downloadOnGitHub": "Download on GitHub", + "visitReleasePage": "Visit release page" }, "settings": { "title": "Settings", @@ -1217,7 +1236,9 @@ "qobuzLinkHandlerDesc": "Register QBZ as the default app for qobuzapp:// links from your browser.", "qobuzLinkHandlerError": "Failed to update link handler registration.", "qconnectDeviceName": "Device Name", - "qconnectDeviceNameDesc": "Name displayed in Qobuz Connect" + "qconnectDeviceNameDesc": "Name displayed in Qobuz Connect", + "noProtocolDeviceFound": "No {protocol} devices found", + "noProtocolDeviceFoundHint": "Make sure devices are on the same network" }, "storage": { "title": "Storage", @@ -1495,6 +1516,7 @@ "loadingPlaylist": "Loading playlist...", "loadingPlaylists": "Loading playlists...", "loadingTracks": "Loading tracks...", + "laodingDevices": "Loading devices...", "lookingUp": "Looking up {name}...", "failedLoadAlbum": "Failed to load album", "failedLoadArtist": "Failed to load artist", @@ -1676,6 +1698,7 @@ }, "keys": { "enter": "Enter", + "esc": "Esc", "shift": "Shift" }, "keybindings": { @@ -1837,7 +1860,8 @@ "custom": "Custom..." }, "customPlaceholder": "Binary name (e.g., my-player)", - "warning": "Best results: use app-scoped bit-perfect only. Global Pulse bit-perfect can break browsers and apps that output fixed 48kHz." + "warning": "Best results: use app-scoped bit-perfect only. Global Pulse bit-perfect can break browsers and apps that output fixed 48kHz.", + "customApps": "Custom apps:" }, "wireplumberConfig": { "title": "Target Your DAC Only", @@ -2282,5 +2306,41 @@ "author": "Author", "contributors": "Contributors", "signatureDetail": "Hatred towards all those companies that discriminate against the Linux community and don't provide us with a decent client for their product." + }, + "updates": { + "runningIn": "Running in {env}", + "runningInsideSandbox": "QBZ detected it's running inside a {env} sandbox.", + "flatpak": { + "intro": "There are some considerations to ensure the best experience.", + "whatYouShouldKnow": { + "heading": "What you should know:", + "externalMusicFolderAccess": "Access to external music folders (NAS, network mounts) requires manual permission grants", + "audioFeatures": "ome audio features may behave differently due to sandbox restrictions", + "settingsPage": "The Settings page includes Flatpak-specific commands you can copy and run" + }, + "note": "Check the Flatpak Sandbox section in Settings for detailed instructions and copy-paste commands." + }, + "snap": { + "intro": "Some audio plugs need to be connected manually for the best experience.", + "requiredPlugConnections": "Required plug connections:", + "optional": "Optional (for external drives / NAS):", + "note": "Run these commands in a terminal, then restart QBZ. This only needs to be done once." + }, + "newVersionReleased": "A new version of QBZ has been released", + "newVersionAvailable": "New version available: v{version}", + "noUpdatesFound": "No updates found.", + "updateReminder": { + "newVersionReminder": "New version reminder", + "remindMeLater": "Remind me later", + "doNotNotifyAgain": "Do not notify me again about this release", + "doNotNotifyAboutReleases": "Do not notify me about new releases", + "hint": "Can be re-enabled from Settings" + }, + "whatsNew": { + "whatsNew": "What's new", + "whatsNewIn": "What's new in v${version}", + "releaseNotesNotAvailable": "Release notes are not available.", + "releaseNotesNotAvailableForVersion": "Release notes are not available for this version." + } } } From 8283bec614c045382038a8eedd1034e14f66417b Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Wed, 1 Apr 2026 14:38:06 +0200 Subject: [PATCH 114/210] feat: add Cmd+, keyboard shortcut to open settings --- src/lib/stores/keybindingsStore.ts | 7 +++++++ src/routes/+page.svelte | 1 + 2 files changed, 8 insertions(+) diff --git a/src/lib/stores/keybindingsStore.ts b/src/lib/stores/keybindingsStore.ts index 950eed3b6..6da0deb9e 100644 --- a/src/lib/stores/keybindingsStore.ts +++ b/src/lib/stores/keybindingsStore.ts @@ -102,6 +102,13 @@ export const ACTIONS: KeybindingAction[] = [ defaultShortcut: 'Ctrl+f', description: 'Focus search field', }, + { + id: 'nav.settings', + label: 'Settings', + category: 'navigation', + defaultShortcut: 'Ctrl+,', + description: 'Open settings', + }, // UI { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 350e3ac6b..0255352ff 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -4001,6 +4001,7 @@ sidebarRef?.focusSearch(); } }); + registerAction('nav.settings', () => navigateTo('settings')); registerAction('ui.sidebar', toggleSidebar); registerAction('ui.focusMode', toggleFocusMode); registerAction('ui.miniPlayer', () => { void enterMiniplayerMode(); }); From 833cc2b35cdd266faf37213578a7668b126fec9e Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Wed, 1 Apr 2026 14:50:22 +0200 Subject: [PATCH 115/210] fix: boost immersive drag region z-index on macOS --- src/lib/components/immersive/ImmersivePlayer.svelte | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/components/immersive/ImmersivePlayer.svelte b/src/lib/components/immersive/ImmersivePlayer.svelte index 9430bd727..861ec7280 100644 --- a/src/lib/components/immersive/ImmersivePlayer.svelte +++ b/src/lib/components/immersive/ImmersivePlayer.svelte @@ -866,6 +866,11 @@ app-region: drag; } + /* macOS: overlay titlebar needs drag region above all UI (matches homepage pattern) */ + :global(html.macos) .immersive-drag-region { + z-index: 9999; + } + @keyframes fadeIn { from { opacity: 0; From 84b1f1a542ae4c021ce5a2c298817aae9716a143 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Wed, 1 Apr 2026 14:51:26 +0200 Subject: [PATCH 116/210] docs: remove Apple Silicon only note from macOS section --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f851bfb2f..f2e1b8a24 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Download from [Releases](https://github.com/vicrodh/qbz/releases): `chmod +x QBZ > **QBZ is a Linux-first application.** macOS support is experimental and limited. Features like PipeWire, ALSA Direct, casting, and device control are unavailable. -Download the unsigned DMG from [Releases](https://github.com/vicrodh/qbz/releases) (Apple Silicon only). +Download the unsigned DMG from [Releases](https://github.com/vicrodh/qbz/releases). Since the DMG is unsigned, you may need to allow it in System Settings > Privacy & Security after first launch. From 7edffcf537480b1df6092f1f3983134ba42bde48 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Wed, 1 Apr 2026 15:44:02 +0200 Subject: [PATCH 117/210] feat(i18n): add macOS-specific menu bar tray keys for all locales --- src/lib/i18n/locales/de.json | 3 +++ src/lib/i18n/locales/en.json | 3 +++ src/lib/i18n/locales/es.json | 3 +++ src/lib/i18n/locales/fr.json | 3 +++ src/lib/i18n/locales/pt.json | 3 +++ 5 files changed, 15 insertions(+) diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index 2fce7d115..d57c978c1 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -969,8 +969,11 @@ "forceX11Desc": "X11 (XWayland) statt nativem Wayland verwenden. Hilft bei Abstürzen mit bestimmten NVIDIA/Wayland-Konfigurationen. Neustart erforderlich. Override: QBZ_FORCE_X11=0 qbz", "tray": { "title": "Systemtray", + "titleMacos": "Menüleiste", "enableTray": "Tray-Symbol aktivieren", + "enableTrayMacos": "Menüleisten-Symbol aktivieren", "enableTrayDesc": "Symbol im Systemtray anzeigen (Neustart erforderlich)", + "enableTrayDescMacos": "Symbol in der Menüleiste anzeigen (Neustart erforderlich)", "minimizeToTray": "In den Tray minimieren", "minimizeToTrayDesc": "Fenster beim Minimieren in den Tray verschieben", "closeToTray": "In den Systemtray schließen", diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index fab6db735..2d2fec965 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -1018,8 +1018,11 @@ "forceX11Desc": "Use X11 (XWayland) instead of native Wayland. Helps with crashes on some NVIDIA/Wayland setups. Requires restart. Override: QBZ_FORCE_X11=0 qbz", "tray": { "title": "System Tray", + "titleMacos": "Menu Bar", "enableTray": "Enable tray icon", + "enableTrayMacos": "Enable menu bar icon", "enableTrayDesc": "Show icon in system tray (requires restart)", + "enableTrayDescMacos": "Show icon in menu bar (requires restart)", "minimizeToTray": "Minimize to tray", "minimizeToTrayDesc": "Hide window to tray when minimizing", "closeToTray": "Close to tray", diff --git a/src/lib/i18n/locales/es.json b/src/lib/i18n/locales/es.json index 66bee70f5..9af2322c1 100644 --- a/src/lib/i18n/locales/es.json +++ b/src/lib/i18n/locales/es.json @@ -1018,8 +1018,11 @@ "forceX11Desc": "Usar X11 (XWayland) en lugar de Wayland nativo. Ayuda con crashes en configuraciones NVIDIA/Wayland. Requiere reinicio. Override: QBZ_FORCE_X11=0 qbz", "tray": { "title": "Bandeja del Sistema", + "titleMacos": "Barra de Menú", "enableTray": "Habilitar icono de bandeja", + "enableTrayMacos": "Habilitar icono en barra de menú", "enableTrayDesc": "Mostrar icono en la bandeja del sistema (requiere reinicio)", + "enableTrayDescMacos": "Mostrar icono en la barra de menú (requiere reinicio)", "minimizeToTray": "Minimizar a bandeja", "minimizeToTrayDesc": "Ocultar ventana a la bandeja al minimizar", "closeToTray": "Cerrar a bandeja", diff --git a/src/lib/i18n/locales/fr.json b/src/lib/i18n/locales/fr.json index 4ce98d38b..0962d50a2 100644 --- a/src/lib/i18n/locales/fr.json +++ b/src/lib/i18n/locales/fr.json @@ -993,8 +993,11 @@ "forceX11Desc": "Utiliser X11 (XWayland) au lieu de Wayland natif. Aide avec les plantages sur certaines configurations NVIDIA/Wayland. Redémarrage requis. Override : QBZ_FORCE_X11=0 qbz", "tray": { "title": "Barre d'état système", + "titleMacos": "Barre des menus", "enableTray": "Activer l'icône dans la barre système", + "enableTrayMacos": "Activer l'icône dans la barre des menus", "enableTrayDesc": "Afficher l'icône dans la barre système (nécessite un redémarrage)", + "enableTrayDescMacos": "Afficher l'icône dans la barre des menus (nécessite un redémarrage)", "minimizeToTray": "Réduire à la barre d'état", "minimizeToTrayDesc": "Masquer la fenêtre dans la barre d'état lors de la minimisation", "closeToTray": "Réduire dans la barre d'état système", diff --git a/src/lib/i18n/locales/pt.json b/src/lib/i18n/locales/pt.json index b5b948a9e..0394b27b7 100644 --- a/src/lib/i18n/locales/pt.json +++ b/src/lib/i18n/locales/pt.json @@ -1011,8 +1011,11 @@ "forceX11Desc": "Usar X11 (XWayland) em vez do Wayland nativo. Ajuda com travamentos em algumas configurações NVIDIA/Wayland. Requer reinicialização. Substituir: QBZ_FORCE_X11=0 qbz", "tray": { "title": "Bandeja do Sistema", + "titleMacos": "Barra de Menus", "enableTray": "Ativar ícone na bandeja", + "enableTrayMacos": "Ativar ícone na barra de menus", "enableTrayDesc": "Mostrar ícone na bandeja do sistema (requer reinicialização)", + "enableTrayDescMacos": "Mostrar ícone na barra de menus (requer reinicialização)", "minimizeToTray": "Minimizar para a bandeja", "minimizeToTrayDesc": "Ocultar janela na bandeja ao minimizar", "closeToTray": "Fechar para a bandeja", From ba3ccf9aaa95288050a2bf26194ec0997d7034c5 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Wed, 1 Apr 2026 15:44:07 +0200 Subject: [PATCH 118/210] feat(settings): hide tray sub-settings on macOS and use menu bar terminology --- src/lib/components/views/SettingsView.svelte | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib/components/views/SettingsView.svelte b/src/lib/components/views/SettingsView.svelte index 3b7d6fe88..e80b528b6 100644 --- a/src/lib/components/views/SettingsView.svelte +++ b/src/lib/components/views/SettingsView.svelte @@ -4516,15 +4516,16 @@
- -

{$t('settings.appearance.tray.title')}

+ +

{$t(document.documentElement.classList.contains('macos') ? 'settings.appearance.tray.titleMacos' : 'settings.appearance.tray.title')}

- {$t('settings.appearance.tray.enableTray')} - {$t('settings.appearance.tray.enableTrayDesc')} + {$t(document.documentElement.classList.contains('macos') ? 'settings.appearance.tray.enableTrayMacos' : 'settings.appearance.tray.enableTray')} + {$t(document.documentElement.classList.contains('macos') ? 'settings.appearance.tray.enableTrayDescMacos' : 'settings.appearance.tray.enableTrayDesc')}
handleEnableTrayChange(v)} />
+ {#if !document.documentElement.classList.contains('macos')}
{$t('settings.appearance.tray.minimizeToTray')} @@ -4539,6 +4540,7 @@
handleCloseToTrayChange(v)} disabled={!enableTray} />
+ {/if}

{$t('settings.appearance.immersive.title')}

From 81aeba3f273dafa204f992f73f016502bf5fd43c Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:44:48 +0200 Subject: [PATCH 119/210] feat(i18n): translation strings in AlbumCreditsModal --- src/lib/components/AlbumCreditsModal.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/components/AlbumCreditsModal.svelte b/src/lib/components/AlbumCreditsModal.svelte index 43a1243fa..b51f5a107 100644 --- a/src/lib/components/AlbumCreditsModal.svelte +++ b/src/lib/components/AlbumCreditsModal.svelte @@ -176,7 +176,7 @@ {#if loading}
- Loading credits... + { $t('toast.loadingCredits') }
{:else if error}
@@ -190,7 +190,7 @@

{credits.album.title}

{credits.album.artist}
-
@@ -264,7 +264,7 @@ class:active={activeTab === 'credits'} onclick={() => activeTab = 'credits'} > - Credits + { $t('player.credits') } {/if} -
From e3b29d9181e2b862e7a9670724acb60ab3d5416a Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:45:36 +0200 Subject: [PATCH 121/210] feat(i18n): translation strings in DeviceDropdown --- src/lib/components/DeviceDropdown.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/DeviceDropdown.svelte b/src/lib/components/DeviceDropdown.svelte index 5c7faaff9..15acc45b0 100644 --- a/src/lib/components/DeviceDropdown.svelte +++ b/src/lib/components/DeviceDropdown.svelte @@ -356,7 +356,7 @@ {/each}
{:else} -
No devices found
+
{$t('settings.audio.noDevicesFound')}
{/each} From 61bc08a26f63095cf29434f94bf059d946abe425 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:45:48 +0200 Subject: [PATCH 122/210] feat(i18n): translation strings in Dropdown --- src/lib/components/Dropdown.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/Dropdown.svelte b/src/lib/components/Dropdown.svelte index f70ca60f4..6822e8c18 100644 --- a/src/lib/components/Dropdown.svelte +++ b/src/lib/components/Dropdown.svelte @@ -264,7 +264,7 @@ {option} {:else} -
No matches found
+
{$t('settings.noMatchesFound')}
{/each} From 830fd2d9ec4ed5b3e4c9a3b95652d34b9d031151 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:46:47 +0200 Subject: [PATCH 123/210] feat(i18n): translation strings in ExpandedPlayer --- src/lib/components/ExpandedPlayer.svelte | 31 ++++++++++++------------ 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/lib/components/ExpandedPlayer.svelte b/src/lib/components/ExpandedPlayer.svelte index 907bfc44b..9af580484 100644 --- a/src/lib/components/ExpandedPlayer.svelte +++ b/src/lib/components/ExpandedPlayer.svelte @@ -1,5 +1,6 @@ - +
{#if status === 'update_available'} -

{$t('udpates.newVersionAvailable', { values: { version: newVersion } })}

+

{$t('updates.newVersionAvailable', { values: { version: newVersion } })}

{:else} -

{$t('udpates.noUpdatesFound')}

+

{$t('updates.noUpdatesFound')}

{/if}²
diff --git a/src/lib/components/updates/UpdateReminderModal.svelte b/src/lib/components/updates/UpdateReminderModal.svelte index f30c3823e..35a8b3be0 100644 --- a/src/lib/components/updates/UpdateReminderModal.svelte +++ b/src/lib/components/updates/UpdateReminderModal.svelte @@ -28,29 +28,29 @@ } - +
-

{$t('udpates.updateReminder.hint')}

+

{$t('updates.updateReminder.hint')}

{#snippet footer()} {/snippet}
From 430b93436194315be507b89fcc0ee41686dc9646 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:53:41 +0200 Subject: [PATCH 134/210] feat(i18n): translation strings in HistoryPanel --- src/lib/components/immersive/panels/HistoryPanel.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/components/immersive/panels/HistoryPanel.svelte b/src/lib/components/immersive/panels/HistoryPanel.svelte index e72e997dd..8036773cf 100644 --- a/src/lib/components/immersive/panels/HistoryPanel.svelte +++ b/src/lib/components/immersive/panels/HistoryPanel.svelte @@ -37,10 +37,10 @@
- {$t('player.history') || 'History'} + { $t('player.history' )}
{#if hasHistory} - {tracks.length} {$t('player.tracks') || 'tracks'} + {tracks.length} { $t('library.tracks') } {/if}
@@ -53,7 +53,7 @@ From 6805f9b38ebb89aa43396399c5e865d124f111ad Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:53:56 +0200 Subject: [PATCH 135/210] feat(i18n): translation strings in SpectralRibbon --- .../components/immersive/panels/SpectralRibbon.svelte | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/components/immersive/panels/SpectralRibbon.svelte b/src/lib/components/immersive/panels/SpectralRibbon.svelte index 5529b5099..0de07760d 100644 --- a/src/lib/components/immersive/panels/SpectralRibbon.svelte +++ b/src/lib/components/immersive/panels/SpectralRibbon.svelte @@ -90,11 +90,11 @@ function formatTypeLabel(): string { const raw = (format || '').toLowerCase(); - if (raw.includes('flac')) return 'FLAC (Free Lossless Audio Codec)'; - if (raw.includes('mp3')) return 'MP3'; - if (raw.includes('aac')) return 'AAC'; - if (raw.includes('opus')) return 'Opus'; - if (raw.includes('alac')) return 'ALAC'; + if (raw.includes('flac')) return $t('fileFormat.flac'); + if (raw.includes('mp3')) return $t('fileFormat.mp3'); + if (raw.includes('aac')) return $t('fileFormat.aac'); + if (raw.includes('opus')) return $t('fileFormat.opus'); + if (raw.includes('alac')) return $t('fileFormat.alac'); return 'Audio Stream'; } From e993638c058d8cb8d06789310878e27baa15b6a0 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:54:13 +0200 Subject: [PATCH 136/210] feat(i18n): translation strings in TrackInfoPanel --- src/lib/components/immersive/panels/TrackInfoPanel.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/immersive/panels/TrackInfoPanel.svelte b/src/lib/components/immersive/panels/TrackInfoPanel.svelte index b1e9683ca..4c8204f6e 100644 --- a/src/lib/components/immersive/panels/TrackInfoPanel.svelte +++ b/src/lib/components/immersive/panels/TrackInfoPanel.svelte @@ -35,7 +35,7 @@ parts.push(`${track.maximum_sampling_rate}kHz`); } if (parts.length === 0) { - return track.hires_streamable ? 'Hi-Res' : 'Lossless'; + return track.hires_streamable ? $t('quality.hiRes') : $t('quality.lossless'); } return parts.join(' / '); } From 363506f1377a5637f13928722a48c8d7109761bb Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:54:29 +0200 Subject: [PATCH 137/210] feat(i18n): translation strings in SuggestionsPanel --- src/lib/components/immersive/panels/SuggestionsPanel.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/immersive/panels/SuggestionsPanel.svelte b/src/lib/components/immersive/panels/SuggestionsPanel.svelte index ca3584ddf..de2bd264c 100644 --- a/src/lib/components/immersive/panels/SuggestionsPanel.svelte +++ b/src/lib/components/immersive/panels/SuggestionsPanel.svelte @@ -266,7 +266,7 @@ {@const fallbackImage = playlist.images?.[Math.min(1, (playlist.images?.length || 1) - 1)]}
- Qobuz + { $t('platforms.qobuz') }
{#if hasCovers} From b18ccd4fc8308fd2b90afe889ca7787c53e78652 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:54:53 +0200 Subject: [PATCH 138/210] feat(i18n): translation strings in Playlists views --- src/lib/components/views/PlaylistDetailView.svelte | 12 ++++++------ src/lib/components/views/PlaylistManagerView.svelte | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/components/views/PlaylistDetailView.svelte b/src/lib/components/views/PlaylistDetailView.svelte index a90736501..8b35633f3 100644 --- a/src/lib/components/views/PlaylistDetailView.svelte +++ b/src/lib/components/views/PlaylistDetailView.svelte @@ -2264,18 +2264,18 @@
{#if selectedTrackKeys.size > 0} {selectedTrackKeys.size} selected - + {:else} - + {/if}
{#if selectedTrackKeys.size > 0}
{/if} @@ -2356,7 +2356,7 @@ class="reorder-btn" onclick={() => moveTrackUp(track, idx)} disabled={idx === 0} - title="Move up" + title={ $t('favorites.moveUp') } > @@ -2367,7 +2367,7 @@ class="reorder-btn" onclick={() => moveTrackDown(track, idx)} disabled={idx === displayTracks.length - 1} - title="Move down" + title={ $t('favorites.moveDown') } > diff --git a/src/lib/components/views/PlaylistManagerView.svelte b/src/lib/components/views/PlaylistManagerView.svelte index edc6b1bb3..c860697ad 100644 --- a/src/lib/components/views/PlaylistManagerView.svelte +++ b/src/lib/components/views/PlaylistManagerView.svelte @@ -1248,7 +1248,7 @@ class="reorder-btn" onclick={(e) => { e.stopPropagation(); movePlaylistUp(playlist.id); }} disabled={playlistIndex === 0} - title="Move up" + title={ $t('favorites.moveUp') } > @@ -1259,7 +1259,7 @@ class="reorder-btn" onclick={(e) => { e.stopPropagation(); movePlaylistDown(playlist.id); }} disabled={playlistIndex === displayPlaylists.length - 1} - title="Move down" + title={ $t('favorites.moveDown') } > @@ -1321,7 +1321,7 @@ @@ -1564,7 +1564,7 @@ {/if} {#if stats && stats.play_count > 0} - + {stats.play_count} From 3e53d7008cec1ac6a816ead11a9074e375b66b8f Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Wed, 1 Apr 2026 22:55:21 +0200 Subject: [PATCH 139/210] feat(i18n): missing translation strings --- src/lib/components/updates/UpdateAvailableModal.svelte | 2 +- src/lib/components/views/LabelView.svelte | 2 +- src/lib/components/views/LoginView.svelte | 6 +++--- src/lib/components/views/MusicianPageView.svelte | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/components/updates/UpdateAvailableModal.svelte b/src/lib/components/updates/UpdateAvailableModal.svelte index b8d89af50..a0c57b6d0 100644 --- a/src/lib/components/updates/UpdateAvailableModal.svelte +++ b/src/lib/components/updates/UpdateAvailableModal.svelte @@ -17,7 +17,7 @@ } - +

{$t('updates.newVersionReleased')}

diff --git a/src/lib/components/views/LabelView.svelte b/src/lib/components/views/LabelView.svelte index 9bcea9d20..cc0f39365 100644 --- a/src/lib/components/views/LabelView.svelte +++ b/src/lib/components/views/LabelView.svelte @@ -823,7 +823,7 @@

{$t('label.popularTracks')}

- - + +
{:else if isInitializing} @@ -406,7 +406,7 @@ id="password" type="password" bind:value={password} - placeholder="Password" + placeholder={ $t('auth.password') } disabled={isLoading} autocomplete="current-password" /> diff --git a/src/lib/components/views/MusicianPageView.svelte b/src/lib/components/views/MusicianPageView.svelte index a980a58f2..882b0d17f 100644 --- a/src/lib/components/views/MusicianPageView.svelte +++ b/src/lib/components/views/MusicianPageView.svelte @@ -151,7 +151,7 @@ {:else if appearances.length === 0}
- No album appearances found + { $t('artist.noAlbumAppearancesFound') }
{:else}
From 211f45a5572259ccabd5be5a340040ae74bb1277 Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:39:51 -0600 Subject: [PATCH 140/210] fix(favorites): bulk make offline now actually downloads tracks handleBulkMakeOffline was calling v2_cache_favorite_track which only saves metadata to the favorites cache. Replaced with cacheTracksForOfflineBatch which triggers the real offline download. cc --- src/lib/components/views/FavoritesView.svelte | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/lib/components/views/FavoritesView.svelte b/src/lib/components/views/FavoritesView.svelte index 51f9ab356..e430ee506 100644 --- a/src/lib/components/views/FavoritesView.svelte +++ b/src/lib/components/views/FavoritesView.svelte @@ -16,7 +16,7 @@ import FavoritesEditModal from '../FavoritesEditModal.svelte'; import BulkActionBar from '../BulkActionBar.svelte'; import ViewTransition from '../ViewTransition.svelte'; - import { type OfflineCacheStatus } from '$lib/stores/offlineCacheState'; + import { type OfflineCacheStatus, cacheTracksForOfflineBatch } from '$lib/stores/offlineCacheState'; import { consumeContextTrackFocus, setPlaybackContext } from '$lib/stores/playbackContextStore'; import { normalizeFavoritesTabOrder } from '$lib/utils/favorites'; import { syncCache as syncTrackCache, subscribe as subscribeFavorites, isTrackFavorite } from '$lib/stores/favoritesStore'; @@ -302,11 +302,26 @@ } async function handleBulkMakeOffline() { - for (const id of selectedTrackIds) { - invoke('v2_cache_favorite_track', { trackId: id }).catch(() => {}); - } + const tracksToCache = favoriteTracks + .filter(track => selectedTrackIds.has(track.id)) + .map(track => ({ + id: track.id, + title: track.title, + artist: track.performer?.name || 'Unknown', + album: track.album?.title, + albumId: track.album?.id, + durationSecs: track.duration, + quality: track.hires ? 'Hi-Res' : '-', + bitDepth: track.maximum_bit_depth, + sampleRate: track.maximum_sampling_rate, + })); trackSelectMode = false; selectedTrackIds = new Set(); + if (tracksToCache.length > 0) { + await cacheTracksForOfflineBatch(tracksToCache).catch(err => { + console.error('Failed to batch cache tracks for offline:', err); + }); + } } let albumViewMode = $state<'grid' | 'list'>('grid'); From 57993864bdc3154f1e29ec0ea905a1555658e799 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 09:08:24 +0200 Subject: [PATCH 141/210] fix(i18n): wrong translation keys --- src/lib/components/CastPicker.svelte | 2 +- src/lib/components/views/TopQView.svelte | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/components/CastPicker.svelte b/src/lib/components/CastPicker.svelte index 378ccd6da..13089f118 100644 --- a/src/lib/components/CastPicker.svelte +++ b/src/lib/components/CastPicker.svelte @@ -195,7 +195,7 @@
{:else} diff --git a/src/lib/components/views/TopQView.svelte b/src/lib/components/views/TopQView.svelte index 8ea172f34..f4a3bf8a7 100644 --- a/src/lib/components/views/TopQView.svelte +++ b/src/lib/components/views/TopQView.svelte @@ -591,10 +591,10 @@
#
-
{$t('common.title')}
-
{$t('purchases.sort.album')}
-
{$t('album.duration')}
-
{$t('album.quality')}
+
{ $t('tracklist.title') }
+
{ $t('tracklist.album') }
+
{ $t('tracklist.duration') }
+
{ $t('tracklist.quality') }
From e680c92bc2ccf2c882cb113b44f1353447d04de4 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 09:08:52 +0200 Subject: [PATCH 142/210] chore(i18n): format keys --- src/lib/components/FavoritesEditModal.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/FavoritesEditModal.svelte b/src/lib/components/FavoritesEditModal.svelte index ab24a8e26..cdbd4bec8 100644 --- a/src/lib/components/FavoritesEditModal.svelte +++ b/src/lib/components/FavoritesEditModal.svelte @@ -100,7 +100,7 @@ class="tab-move-btn" onclick={() => moveUp(index)} disabled={index === 0} - title={$t('actions.moveUp')} + title={ $t('actions.moveUp') } > ↑ @@ -108,7 +108,7 @@ class="tab-move-btn" onclick={() => moveDown(index)} disabled={index === tabOrder.length - 1} - title={$t('actions.moveDown')} + title={ $t('actions.moveDown') } > ↓ From 023b424a66a1ed3f5d781b5795ef961e5ecc3667 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 09:09:10 +0200 Subject: [PATCH 143/210] feat(i18n): add de translations --- src/lib/i18n/locales/de.json | 291 +++++++++++++++++++++++++++++++++-- 1 file changed, 275 insertions(+), 16 deletions(-) diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index 3e828378f..efeed70ff 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -254,7 +254,16 @@ "refreshMetadata": "Metadaten aktualisieren", "albumSettings": "Albumeinstellungen", "albumArtwork": "Album-Artwork", - "uploadCover": "Cover hochladen" + "uploadCover": "Cover hochladen", + "background": "Hintergrund", + "explicit": "Explizit", + "folderName": "Name", + "localFiles": "Lokale Dateien", + "plexLibrary": "Plex-Bibliothek", + "plexSyncFailed": "Die Plex-Bibliothek konnte nicht synchronisiert werden", + "syncPlexLibrary": "Plex-Bibliothek synchronisieren", + "syncingPlexLibrary": "Plex wird synchronisiert...", + "singleFileAlbumNotice": "Dieses Album scheint eine einzelne Datei zu sein, die die gesamte CD enthält. Für die individuelle Titelnavigation muss der Plex-Server die Datei aufteilen (z. B. über einen Cue-Sheet-Scanner)." }, "album": { "tracks": "Titel", @@ -289,7 +298,8 @@ "bookletRotate": "Drehen", "bookletDownload": "Booklet speichern", "sameArtist": "Vom selben Künstler", - "seeFullDiscography": "Vollständige Diskografie ansehen" + "seeFullDiscography": "Vollständige Diskografie ansehen", + "loadError": "Albumtitel konnten nicht geladen werden. Bitte versuchen Sie es erneut." }, "artist": { "albums": "Alben", @@ -348,7 +358,9 @@ "nextResult": "Nächstes Ergebnis", "close": "Suche schließen", "searchInPage": "Auf dieser Seite suchen" - } + }, + "noAlbumAppearancesFound": "Keine Albumauftritte gefunden", + "noAlbumsFound": "Keine Alben gefunden" }, "sort": { "nameAZ": "Name (A-Z)", @@ -436,7 +448,13 @@ "playlists": "Playlists", "folders": "Ordner", "dragPlaylists": "Playlists zum Neuordnen ziehen", - "dropOntoFolder": "oder auf einen Ordner ablegen, um sie zu verschieben." + "dropOntoFolder": "oder auf einen Ordner ablegen, um sie zu verschieben.", + "newFolder": "Neuer Ordner", + "folderNamePlaceholder": "Geben Sie den Ordnernamen ein", + "moveToFolder": "In Ordner verschieben", + "moveToRoot": "Zum Root wechseln", + "noFoldersYet": "Noch keine Ordner", + "noFoldersMatch": "Es stimmen keine Ordner überein" }, "favorites": { "title": "Favoriten", @@ -581,7 +599,25 @@ "dragWindow": "Fenster verschieben", "normalizationOff": "Lautstärkenormalisierung: Aus", "normalizationOn": "Lautstärkenormalisierung: Ein", - "normalizationApplied": "Lautstärkenormalisierung: Aktiv" + "normalizationApplied": "Lautstärkenormalisierung: Aktiv", + "disableInfinitePlay": "Deaktivieren Sie die Infinite Play", + "enableInfinitePlay": "Aktivieren Sie „Infinite Play“.", + "exitFullscreenWithKey": "Vollbild beenden ({key})", + "miniClose": "Schließen Sie den Miniplayer", + "miniExpand": "Erweitern Sie es zur vollständigen App", + "exitImmersiveWithKey": "Immersive beenden ({key})", + "fullscreenWithKey": "Vollbild ({key})", + "miniSurfaceLyrics": "Songtext", + "qobuzConnect": "Qobuz Connect", + "restoreWindow": "Fenster wiederherstellen", + "miniAlwaysOnTopEnable": "Immer an der Spitze", + "qobuzConnectManage": "Qobuz Connect – Zum Verwalten klicken", + "maximizeWindow": "Fenster maximieren", + "miniSurfaceMicro": "Mikro", + "miniWindowControls": "Mini-Player-Steuerung", + "windowControls": "Fenstersteuerung", + "miniAlwaysOnTopDisable": "Deaktivieren Sie „Immer im Vordergrund“.", + "miniSurfaceArtwork": "Kunstwerk" }, "audioBadges": { "castingTo": "Übertragung an", @@ -689,7 +725,13 @@ }, "newFolder": "Neuer Ordner", "previousArtists": "Vorherige Künstler", - "nextArtists": "Nächste Künstler" + "nextArtists": "Nächste Künstler", + "switchToImmersiveView": "Zur immersiven Ansicht wechseln (V)", + "switchToSplitView": "Zur geteilten Ansicht wechseln (V)", + "visitReleasePage": "Besuchen Sie die Release-Seite", + "moveUp": "Bewegen Sie sich nach oben", + "moveDown": "Bewegen Sie sich nach unten", + "backToHome": "Zurück nach Hause" }, "settings": { "title": "Einstellungen", @@ -762,7 +804,8 @@ "resetConfirmTitle": "Audio- & Wiedergabeeinstellungen zurücksetzen?", "resetConfirmDesc": "Dies setzt alle Audioausgabe-, Backend- und Wiedergabeeinstellungen auf die Standardwerte zurück. Die Wiedergabe wird gestoppt.", "resetSuccess": "Audio- und Wiedergabeeinstellungen wurden auf die Standardwerte zurückgesetzt.", - "resetError": "Fehler beim Zurücksetzen der Audioeinstellungen: {error}" + "resetError": "Fehler beim Zurücksetzen der Audioeinstellungen: {error}", + "noDevicesFound": "Keine Geräte gefunden" }, "playback": { "title": "Wiedergabe", @@ -951,11 +994,11 @@ } }, "fpsOptions": { - "disabled": "Deaktiviert", "15": "15 FPS (Niedrig)", "30": "30 FPS (Mittel)", "60": "60 FPS (Flüssig)", - "120": "120 FPS (Ultra)" + "120": "120 FPS (Ultra)", + "disabled": "Deaktiviert" }, "startupPage": "Startseite", "startupPageDesc": "Wähle welche Seite beim App-Start angezeigt wird", @@ -1145,7 +1188,23 @@ "qobuzLinkHandlerDesc": "QBZ als Standard-App fuer qobuzapp://-Links aus dem Browser registrieren.", "qobuzLinkHandlerError": "Fehler beim Aktualisieren der Link-Handler-Registrierung.", "qconnectDeviceName": "Gerätename", - "qconnectDeviceNameDesc": "Angezeigter Name in Qobuz Connect" + "qconnectDeviceNameDesc": "Angezeigter Name in Qobuz Connect", + "plexActionLoadTracks": "Titel laden", + "remoteControlSetupGuide": "Setup-Anleitung", + "plexBaseUrl": "Plex-Basis-URL", + "plexTrackMeta": "{artist} - {album} - {rate} Hz / {bits}-bit", + "plexActionPing": "Ping-Plex", + "plexActionPlay": "Spielen", + "plexActionPlaying": "Spielen...", + "plexMusicSection": "Musikabteilung", + "plexBaseUrlPlaceholder": "http://127.0.0.1:32400", + "plexNoTracksLoaded": "Es sind noch keine Titel geladen.", + "plexPoc": "Plex LAN POC", + "plexPocDesc": "Experimentelle Nur-lokale Integration. Verwendet direkte Medienteile von Plex, ohne die QBZ-Wiedergabe zu ersetzen.", + "plexSelectSection": "Wählen Sie einen Abschnitt aus", + "plexStatusConnected": "Verbunden mit {server} (v{version}).", + "plexStatusError": "Plex-Fehler: {error}", + "plexStatusPlaying": "Spielt: {title}" }, "storage": { "title": "Speicher", @@ -1287,7 +1346,13 @@ "clearAllCaches": "Alle Caches löschen", "clearAllCachesDesc": "Löscht alle oben aufgeführten zwischengespeicherten Daten", "cachesDesc": "Cache für Warteschlange, Songtexte, Artwork und Metadaten" - } + }, + "immersive": { + "title": "Leistungseinbußen", + "perfDegraded": "Leistungseinbußen", + "perfDontShowAgain": "Nicht mehr anzeigen" + }, + "noMatchesFound": "Keine Übereinstimmungen gefunden" }, "auth": { "login": "Anmelden", @@ -1309,7 +1374,7 @@ }, "legal": { "brandingLine": "Diese Anwendung verwendet die Qobuz-API, ist jedoch nicht von Qobuz™ zertifiziert.", - "brandingLineSuffix": "", + "brandingLineSuffix": "Diese Anwendung verwendet die Qobuz™-API, ist jedoch nicht von Qobuz™ zertifiziert.", "tosAgreementPrefix": "Ich habe die", "tosLinkText": "Qobuz™-Nutzungsbedingungen", "tosRequiredToLogin": "Bitte bestätigen Sie, dass Sie die Qobuz™-Nutzungsbedingungen gelesen haben, um fortzufahren.", @@ -1342,7 +1407,8 @@ "loadFailed": "Laden fehlgeschlagen", "playbackFailed": "Wiedergabe fehlgeschlagen", "cachingFailed": "Vorbereitung für Offline fehlgeschlagen", - "saveFailed": "Speichern fehlgeschlagen" + "saveFailed": "Speichern fehlgeschlagen", + "failedToLoad": "Laden fehlgeschlagen" }, "time": { "hours": "h", @@ -1498,7 +1564,12 @@ "failedSaveAutoplay": "Speichern der Autoplay-Voreinstellung fehlgeschlagen", "failedSaveIconVisibility": "Speichern der Icon-Sichtbarkeit fehlgeschlagen", "failedRepairOffline": "Reparatur der Offline-Bibliothek fehlgeschlagen: {error}", - "failedOpenCacheFolder": "Öffnen des Cache-Ordners fehlgeschlagen" + "failedOpenCacheFolder": "Öffnen des Cache-Ordners fehlgeschlagen", + "loadingDevices": "Geräte werden geladen...", + "addedToPlaylist": "Zur Playlist hinzugefügt", + "copied": "Kopiert!", + "loadingCredits": "Credits werden geladen...", + "playlistUpdated": "Playlist aktualisiert" }, "offline": { "title": "Offline-Modus", @@ -1752,7 +1823,8 @@ "custom": "Benutzerdefiniert..." }, "customPlaceholder": "Binärname (z.B. mein-player)", - "warning": "Beste Ergebnisse: Verwenden Sie Bit-Perfect nur pro App. Globales Pulse-Bit-Perfect kann Browser und Apps beschädigen, die festes 48kHz ausgeben." + "warning": "Beste Ergebnisse: Verwenden Sie Bit-Perfect nur pro App. Globales Pulse-Bit-Perfect kann Browser und Apps beschädigen, die festes 48kHz ausgeben.", + "customApps": "Benutzerdefinierte Apps:" }, "wireplumberConfig": { "title": "Nur Ihren DAC ansteuern", @@ -1936,7 +2008,25 @@ "algorithmInfo": "Basierend auf dem Qobuz™-Vorschlagsalgorithmus, kuratiert aus deinem lokalen Hoerverlauf.", "errors": { "fetchFailed": "Dynamische Vorschlaege konnten nicht geladen werden.", - "insufficientHistory": "Nicht genuegend Hoerverlauf fuer Vorschlaege. Hoere etwas Musik und versuche es spaeter erneut." + "insufficientHistory": "Nicht genuegend Hoerverlauf fuer Vorschlaege. Hoere etwas Musik und versuche es spaeter erneut.", + "emptyPlaylist": "Keine Titel zum Speichern als Playlist verfügbar.", + "playlistsLoadFailed": "Deine Playlists konnten nicht geladen werden.", + "prefillFailed": "Aktuelle Daten aus QBZ-Protokollen konnten nicht geladen werden.", + "rawProbeFailed": "Die rohe Endpunktantwort konnte nicht abgerufen werden." + }, + "form": { + "limit": "Limit", + "run": "Vorschläge abrufen", + "listenedTrackIdsPlaceholder": "123,456,789", + "listenedTrackIds": "IDs der gehörten Titel (durch Kommas getrennt)", + "prefillFromRecents": "Verwenden Sie das aktuelle QBZ-Protokoll", + "rawResponse": "Rohe Antwort", + "probeRaw": "Rohantwort prüfen", + "tracksToAnalyse": "Zu analysierende Titel (eine Zeile: Titel-ID, Künstler-ID, Genre-ID, Label-ID)", + "tracksToAnalysePlaceholder": "123,45,78,90" + }, + "result": { + "emptyHint": "Versuchte Fallback-Payload-Strategien. \nDieses Konto/Seed-Set gibt derzeit möglicherweise keine Mix-Kandidaten zurück." } }, "weeklyMixes": { @@ -2027,5 +2117,174 @@ "rememberChoice": "Meine Wahl merken", "autoRetrying": "Erneuter Versuch mit niedrigerer Qualitaet...", "autoSkipping": "Titel wird uebersprungen (Serverproblem)..." + }, + "fileFormat": { + "aac": "AAC", + "alac": "ALAC", + "flac": "FLAC", + "mp3": "MP3", + "opus": "Opus" + }, + "keys": { + "esc": "Esc" + }, + "platforms": { + "appleMusic": "Apple Music", + "deezer": "Deezer", + "qobuz": "Qobuz", + "spotify": "Spotify", + "tidal": "Tidal" + }, + "updates": { + "snap": { + "intro": "Für ein optimales Erlebnis müssen einige Audiostecker manuell angeschlossen werden.", + "note": "Führen Sie diese Befehle in einem Terminal aus und starten Sie dann QBZ neu. Dies muss nur einmal durchgeführt werden.", + "optional": "Optional (für externe Laufwerke / NAS):", + "requiredPlugConnections": "Erforderliche Steckverbindungen:" + }, + "checkForUpdates": "Suchen Sie nach Updates", + "flatpak": { + "intro": "Es gibt einige Überlegungen, um das beste Erlebnis zu gewährleisten.", + "note": "Detaillierte Anweisungen und Befehle zum Kopieren und Einfügen finden Sie im Abschnitt Flatpak Sandbox in den Einstellungen.", + "whatYouShouldKnow": { + "audioFeatures": "Einige Audiofunktionen verhalten sich aufgrund von Sandbox-Einschränkungen möglicherweise anders", + "settingsPage": "Die Seite Einstellungen enthält Flatpak-spezifische Befehle, die Sie kopieren und ausführen können", + "externalMusicFolderAccess": "Der Zugriff auf externe Musikordner (NAS, Netzwerk-Mounts) erfordert manuelle Berechtigungserteilungen", + "heading": "Was Sie wissen sollten:" + } + }, + "whatsNew": { + "whatsNew": "Was ist neu", + "whatsNewIn": "Was ist neu in v${version}?", + "releaseNotesNotAvailable": "Versionshinweise sind nicht verfügbar.", + "releaseNotesNotAvailableForVersion": "Für diese Version sind keine Versionshinweise verfügbar." + }, + "newVersionAvailable": "Neue Version verfügbar: v{version}", + "runningIn": "Läuft in {env}", + "noUpdatesFound": "Keine Updates gefunden.", + "updateReminder": { + "hint": "Kann über die Einstellungen wieder aktiviert werden", + "doNotNotifyAboutReleases": "Benachrichtigen Sie mich nicht über Neuerscheinungen", + "doNotNotifyAgain": "Benachrichtigen Sie mich nicht noch einmal über diese Veröffentlichung", + "newVersionReminder": "Erinnerung an neue Version", + "remindMeLater": "Erinnere mich später daran" + }, + "runningInsideSandbox": "QBZ hat erkannt, dass es in einer {env}-Sandbox ausgeführt wird.", + "newReleaseAvailable": "Neue Version verfügbar", + "newVersionReleased": "Eine neue Version von QBZ wurde veröffentlicht" + }, + "common": { + "name": "Name" + }, + "integrations": { + "connecting": "Verbinden..." + }, + "remoteControlWizard": { + "buttons": { + "back": "Zurück", + "close": "Schließen", + "next": "Nächste" + }, + "enable": { + "step2": "Schalten Sie „Fernbedienung aktivieren“ ein", + "subtitle": "Schalten Sie den lokalen API-Server ein", + "screenshot": "[Screenshot: Einstellungen > Fernbedienung umschalten]", + "step1": "Gehen Sie zu Einstellungen → Fernbedienung", + "step3": "Notieren Sie sich die Portnummer (Standard: 8182)", + "title": "Aktivieren Sie die Fernbedienung" + }, + "https": { + "step2": "QBZ generiert ein selbstsigniertes Zertifikat", + "intro": "Um die Fernbedienung als Progressive Web App (PWA) auf Ihrem Telefon oder Tablet zu installieren, ist HTTPS erforderlich.", + "screenshot": "[Screenshot: HTTPS-Umschaltung aktiviert]", + "subtitle": "Erforderlich für die PWA-Installation", + "step1": "Aktivieren Sie „Sichere Verbindung (HTTPS)“", + "step3": "Eine Zertifikats-URL wird angezeigt", + "title": "Aktivieren Sie die sichere Verbindung", + "whyReason1": "Moderne Browser erfordern HTTPS für die PWA-Installation", + "whyReason2": "Eine sichere Verbindung schützt Ihr Pairing-Token", + "whyReason3": "Erforderlich für Servicemitarbeiter und Offlinefunktionen", + "whyRequired": "Warum HTTPS?" + }, + "certificate": { + "androidStep1": "Öffnen Sie die Zertifikats-URL in Chrome", + "androidStep2": "Öffnen Sie die heruntergeladene .crt-Datei", + "androidStep3": "Geben Sie einen Namen ein (z. B. „QBZ“)", + "intro": "Da QBZ ein selbstsigniertes Zertifikat verwendet, müssen Sie es auf Ihrem steuernden Gerät installieren und als vertrauenswürdig einstufen.", + "iosStep1": "Öffnen Sie die Zertifikats-URL in Safari", + "iosStep2": "Tippen Sie auf „Zulassen“, um das Profil herunterzuladen", + "iosStep3": "Gehen Sie zu Einstellungen → Profil heruntergeladen → Installieren", + "macosStep1": "Öffnen Sie die Zertifikats-URL in Safari", + "androidStep4": "Wählen Sie als Nutzungsart „VPN und Apps“ oder „WLAN“.", + "androidStep5": "Tippen Sie zum Installieren auf OK", + "androidTitle": "Android", + "iosStep4": "Gehen Sie zu Einstellungen → Allgemein → Info → Zertifikatsvertrauenseinstellungen", + "iosStep5": "Aktivieren Sie volle Vertrauenswürdigkeit für das QBZ-Zertifikat", + "iosTitle": "iOS (iPhone/iPad)", + "macosStep2": "Öffnen Sie die heruntergeladene .crt-Datei", + "macosStep3": "Der Schlüsselbundzugriff wird geöffnet – zum Anmeldeschlüsselbund hinzufügen", + "macosStep4": "Doppelklicken Sie auf das Zertifikat und erweitern Sie „Vertrauen“.", + "macosStep5": "Stellen Sie „Bei Verwendung dieses Zertifikats“ auf „Immer vertrauen“ ein.", + "macosTitle": "macOS", + "step1": "Kopieren Sie die Zertifikats-URL aus den QBZ-Einstellungen", + "screenshot": "[Screenshot: Zertifikat-URL in den QBZ-Einstellungen]", + "step2": "Öffnen Sie die URL im Browser Ihres steuernden Geräts", + "step3": "Laden Sie das Zertifikat herunter und installieren Sie es", + "step4": "Vertrauen Sie dem Zertifikat beim Surfen im Internet", + "subtitle": "Vertrauen Sie dem QBZ-Zertifikat auf Ihrem Gerät", + "title": "Zertifikat installieren", + "windowsStep1": "Öffnen Sie die Zertifikats-URL in einem beliebigen Browser", + "windowsStep2": "Öffnen Sie die heruntergeladene .crt-Datei", + "windowsStep3": "Klicken Sie auf „Zertifikat installieren…“", + "windowsStep4": "Wählen Sie „Lokaler Computer“ und dann „Weiter“.", + "windowsStep5": "Wählen Sie „Alle Zertifikate platzieren in: Vertrauenswürdige Stammzertifizierungsstellen“", + "windowsStep6": "Beenden Sie den Assistenten und bestätigen Sie", + "windowsTitle": "Windows" + }, + "done": { + "close": "Schließen", + "pwaHint": "Für das beste Erlebnis installieren Sie QBZ Control als PWA:", + "pwaStep1": "Öffnen Sie die Kontroll-URL auf Ihrem Gerät", + "pwaStep2": "Verwenden Sie „Zum Startbildschirm hinzufügen“ oder „App installieren“.", + "pwaStep3": "Starten Sie von Ihrem Startbildschirm aus für ein Vollbilderlebnis", + "subtitle": "Sie können QBZ jetzt fernsteuern" + }, + "pair": { + "intro": "Verbinden Sie nun Ihr Steuergerät über den QR-Code oder die manuelle Eingabe mit QBZ.", + "manualMethod": "Manuelle Eingabe", + "manualStep1": "Beachten Sie die in QBZ angezeigte lokale URL", + "manualStep2": "Öffnen Sie diese URL auf Ihrem steuernden Gerät", + "manualStep3": "Geben Sie die URL und das Token manuell ein", + "qrMethod": "QR-Code (empfohlen)", + "qrStep1": "Klicken Sie in den QBZ-Einstellungen auf „QR anzeigen“.", + "qrStep2": "Öffnen Sie die QBZ-Kontroll-URL auf Ihrem Gerät", + "qrStep3": "Scannen Sie den QR-Code, um die Verbindungsdetails automatisch auszufüllen", + "screenshot": "[Screenshot: QR-Code-Kopplungsbildschirm]", + "subtitle": "Verbinden Sie sich mit QBZ", + "title": "Koppeln Sie Ihr Gerät", + "trouble1": "Stellen Sie sicher, dass sich beide Geräte im selben Netzwerk befinden", + "trouble2": "Überprüfen Sie, ob die Firewall Port 8182 zulässt", + "troubleshooting": "Wenn die Verbindung fehlschlägt:", + "trouble3": "Stellen Sie sicher, dass HTTPS aktiviert ist und das Zertifikat vertrauenswürdig ist" + }, + "steps": { + "certificate": "Zertifikat", + "done": "Erledigt", + "enable": "Aktivieren", + "https": "HTTPS", + "pair": "Koppeln", + "welcome": "Willkommen" + }, + "welcome": { + "feature1": "Steuern Sie die Wiedergabe (Wiedergabe, Pause, Überspringen)", + "feature2": "Verwalten Sie Ihre Warteschlange", + "feature3": "Titel suchen und hinzufügen", + "feature4": "Als PWA auf Ihrem Gerät installieren", + "intro": "Diese Anleitung hilft Ihnen bei der Einrichtung der Fernbedienung, damit Sie QBZ von Ihrem Telefon, Tablet oder einem anderen Gerät im selben Netzwerk aus steuern können.", + "screenshot": "[Screenshot: Fernbedienung der PWA auf Mobilgeräten]", + "start": "Legen Sie los", + "subtitle": "Steuern Sie QBZ von einem anderen Gerät aus", + "title": "Einrichtung der Fernbedienung" + } } } From a22a87e7b1009fe90188707093900d084077c42a Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 09:09:18 +0200 Subject: [PATCH 144/210] feat(i18n): add en translations --- src/lib/i18n/locales/en.json | 147 ++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 20 deletions(-) diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index 7885bc599..66892510d 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -154,6 +154,7 @@ "albums": "Albums", "artists": "Artists", "tracks": "Tracks", + "explicit": "Explicit", "folders": "Library Folders", "noAlbums": "No albums in library", "noArtists": "No artists in library", @@ -260,12 +261,14 @@ "hideAlbum": "Hide this album from library", "hideAlbumHint": "Hidden albums can be viewed from Settings", "localTrackIndicator": "Local track - only available on this device", - "singleFileAlbumNotice": "This album appears to be a single file containing the full disc. Individual track navigation requires the Plex server to split the file (e.g. via cue sheet scanner).", + "singleFileAlbumNotice": "This album appears to be a single file containing the full disc. Individual track navigation requires the Plex server to split the file (e.g., via cue sheet scanner).", "plexTrackIndicator": "Plex track - served from your local Plex library", "refreshMetadata": "Refresh metadata", "albumSettings": "Album settings", "albumArtwork": "Album Artwork", - "uploadCover": "Upload cover" + "uploadCover": "Upload cover", + "folderName": "Name", + "background": "Background" }, "album": { "tracks": "tracks", @@ -362,7 +365,10 @@ "nextResult": "Next result", "close": "Close search", "searchInPage": "Search in this page" - } + }, + "noAlbumsFound": "No albums found", + "bandProjects": "Bands & Projects", + "noAlbumAppearancesFound": "No album appearances found" }, "sort": { "sort": "Sort", @@ -380,7 +386,8 @@ "title": "Title", "titleAZ": "Title (A-Z)", "titleZA": "Title (Z-A)", - "trackCount": "# of Tracks" + "trackCount": "# of Tracks", + "duration": "Duration" }, "playlist": { "tracks": "tracks", @@ -465,7 +472,9 @@ "playCount": "Play count", "playlists": "playlists", "dragPlaylists": "Drag playlists to reorder them", - "dropOntoFolder": "or drop onto a folder to move." + "dropOntoFolder": "or drop onto a folder to move.", + "newFolder": "New folder", + "folderNamePlaceholder": "Enter folder name" }, "favorites": { "title": "Favorites", @@ -542,8 +551,8 @@ "enterApiKey": "Enter your API key", "enterSharedSecret": "Enter your shared secret", "pasteToken": "Paste your ListenBrainz token", - "yearExample": "e.g. 1999", - "genreExample": "e.g. ROCK, POP, etc" + "yearExample": "e.g., 1999", + "genreExample": "e.g., ROCK, POP" }, "player": { "nowPlaying": "Now Playing", @@ -632,7 +641,8 @@ "miniAlwaysOnTopLimited": "Always-on-top is limited by your window manager", "fullscreenWithKey": "Fullscreen ({key})", "exitFullscreenWithKey": "Exit Fullscreen ({key})", - "closeApp": "Close App" + "closeApp": "Close App", + "windowControls": "Window Controls" }, "audioBadges": { "castingTo": "Casting to", @@ -726,9 +736,9 @@ "moreOptions": "More options", "shareQobuz": "Share (Qobuz)", "albumCredits": "Album credits", - "startArtistRadio" : "Start Artist Radio", + "startArtistRadio": "Start Artist Radio", "radio": { - "startArtistRadio" : "Start Artist Radio" + "startArtistRadio": "Start Artist Radio" }, "previousAlbums": "Previous albums", "nextAlbums": "Next albums", @@ -748,7 +758,15 @@ "gotIt": "Got it", "copyCommand": "Copy command", "downloadOnGitHub": "Download on GitHub", - "visitReleasePage": "Visit release page" + "visitReleasePage": "Visit release page", + "okClose": "OK, close", + "tryAgain": "Try Again", + "startOffline": "Start Offline", + "selectAll": "Select All", + "clearSelection": "Clear selection", + "moveUp": "Move up", + "moveDown": "Move down", + "backToHome": "Back to home" }, "settings": { "title": "Settings", @@ -798,6 +816,7 @@ "maxSampleRate": "Maximum Sample Rate", "maxSampleRateDesc": "Select your DAC's maximum supported sample rate. Tracks above this rate will be requested at a lower quality from Qobuz.", "refreshDevices": "Refresh device list", + "noDevicesFound": "No devices found", "alsaPlugin": "ALSA Plugin", "alsaPluginDesc": "hw: Bit-perfect, exclusive. plughw: Auto-convert. pcm: Most compatible.", "hardwareVolume": "Enable Hardware Volume Control", @@ -1010,11 +1029,11 @@ } }, "fpsOptions": { - "disabled": "Disabled", "15": "15 FPS (Low)", "30": "30 FPS (Medium)", "60": "60 FPS (Smooth)", - "120": "120 FPS (Ultra)" + "120": "120 FPS (Ultra)", + "disabled": "Disabled" }, "miniplayerDefaultView": "Mini player default view", "miniplayerViews": { @@ -1382,6 +1401,12 @@ "clearAllCaches": "Clear All Caches", "clearAllCachesDesc": "Clears all cached data above", "cachesDesc": "Queue, lyrics, artwork and metadata caches" + }, + "noMatchesFound": "No matches found", + "immersive": { + "title": "Degraded performance", + "perfDegraded": "Degraded performance", + "perfDontShowAgain": "Don't show again" } }, "metadata": { @@ -1427,7 +1452,7 @@ }, "legal": { "brandingLine": "This application uses the Qobuz API but is not certified by Qobuz™.", - "brandingLineSuffix": "", + "brandingLineSuffix": "This application uses the Qobuz™ API but is not certified by Qobuz™.", "tosAgreementPrefix": "I have read the", "tosLinkText": "Qobuz™ Terms of Service", "tosRequiredToLogin": "Please confirm that you have read the Qobuz™ Terms of Service to continue.", @@ -1460,7 +1485,8 @@ "loadFailed": "Failed to load", "playbackFailed": "Playback failed", "cachingFailed": "Failed to prepare for offline", - "saveFailed": "Failed to save" + "saveFailed": "Failed to save", + "failedToLoad": "Failed to load" }, "time": { "hours": "h", @@ -1508,7 +1534,8 @@ "sectionsDesc": "Reorder with arrows. Toggle visibility with switch.", "moveUp": "Move up", "moveDown": "Move down", - "resetToDefaults": "Reset to defaults" + "resetToDefaults": "Reset to defaults", + "itemsToShow": "Items to show" }, "toast": { "loadingAlbum": "Loading album...", @@ -1623,7 +1650,11 @@ "newState": "Blacklist {status}", "enabled": "enabled", "disabled": "disabled" - } + }, + "loadingCredits": "Loading credits...", + "loadingDevices": "Loading devices...", + "addedToPlaylist": "Added to playlist", + "copied": "Copied!" }, "offline": { "title": "Offline Mode", @@ -1920,7 +1951,8 @@ "restart": "Restart", "verify": "Verify", "done": "Done" - } + }, + "title": "DAC Wizard" }, "remoteControlWizard": { "steps": { @@ -2315,7 +2347,7 @@ "whatYouShouldKnow": { "heading": "What you should know:", "externalMusicFolderAccess": "Access to external music folders (NAS, network mounts) requires manual permission grants", - "audioFeatures": "ome audio features may behave differently due to sandbox restrictions", + "audioFeatures": "Some audio features may behave differently due to sandbox restrictions", "settingsPage": "The Settings page includes Flatpak-specific commands you can copy and run" }, "note": "Check the Flatpak Sandbox section in Settings for detailed instructions and copy-paste commands." @@ -2341,6 +2373,81 @@ "whatsNewIn": "What's new in v${version}", "releaseNotesNotAvailable": "Release notes are not available.", "releaseNotesNotAvailableForVersion": "Release notes are not available for this version." - } + }, + "newReleaseAvailable": "New release available", + "checkForUpdates": "Check for updates" + }, + "performerRoles": { + "a&R": "A&R", + "acousticGuitar": "Acoustic Guitar", + "additionalEngineer": "Additional Engineer", + "additionalKeyboard": "Additional Keyboard", + "additionalVocalist": "Additional Vocalist", + "arranger": "Arranger", + "assistantEngineer": "Assistant Engineer", + "assistantMixer": "Assistant Mixer", + "associatedPerformer": "Associated Performer", + "backgroundVocalist": "Background Vocalist", + "bass": "Bass", + "bassGuitar": "Bass Guitar", + "choir": "Choir", + "composer": "Composer", + "composerLyricist": "Composer Lyricist", + "coProducer": "Co-Producer", + "digitalEditingEngineer": "Digital Editing Engineer", + "drum": "Drum", + "drums": "Drums", + "drumKit": "Drum Kit", + "electricGuitar": "Electric Guitar", + "engineer": "Engineer", + "featuredArtist": "Featured Artist", + "guitar": "Guitar", + "keyboard": "Keyboard", + "keyboards": "Keyboards", + "lyricist": "Lyricist", + "mainArtist": "Main Artist", + "masteringEngineer": "Mastering Engineer", + "mixer": "Mixer", + "mixingEngineer": "Mixing Engineer", + "musicPublisher": "Music Publisher", + "organ": "Organ", + "percussion": "Percussion", + "performance": "Performance", + "piano": "Piano", + "producer": "Producer", + "programmer": "Programmer", + "recordingEngineer": "Recording Engineer", + "recordingSecondEngineer": "Recording Second Engineer", + "remixer": "Remixer", + "secondEngineer": "Second Engineer", + "stringArranger": "String Arranger", + "strings": "Strings", + "studioPersonnel": "Studio Personnel", + "ukulele": "Ukulele", + "vocal": "Vocal", + "vocals": "Vocals", + "vocalEditingEngineer": "Vocal Editing Engineer", + "vocalist": "Vocalist", + "writer": "Writer" + }, + "platforms": { + "appleMusic": "Apple Music", + "qobuz": "Qobuz", + "spotify": "Spotify", + "tidal": "Tidal", + "deezer": "Deezer" + }, + "fileFormat": { + "flac": "FLAC", + "mp3": "MP3", + "aac": "AAC", + "opus": "Opus", + "alac": "ALAC" + }, + "integrations": { + "connecting": "Connecting..." + }, + "common": { + "name": "Name" } } From 0ea54b7e4270e1040861fac8074ec2d4eb3e6915 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 09:09:25 +0200 Subject: [PATCH 145/210] feat(i18n): add es translations --- src/lib/i18n/locales/es.json | 124 ++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 15 deletions(-) diff --git a/src/lib/i18n/locales/es.json b/src/lib/i18n/locales/es.json index 20ab26680..dd86595b4 100644 --- a/src/lib/i18n/locales/es.json +++ b/src/lib/i18n/locales/es.json @@ -265,7 +265,10 @@ "refreshMetadata": "Actualizar metadatos", "albumSettings": "Configuración del Álbum", "albumArtwork": "Portada del Álbum", - "uploadCover": "Subir Portada" + "uploadCover": "Subir Portada", + "background": "Fondo", + "explicit": "Explícito", + "folderName": "Nombre" }, "album": { "tracks": "pistas", @@ -360,7 +363,9 @@ "nextResult": "Siguiente resultado", "close": "Cerrar búsqueda", "searchInPage": "Buscar en esta página" - } + }, + "noAlbumAppearancesFound": "No se encontraron apariciones en álbumes", + "noAlbumsFound": "No se encontraron álbumes" }, "sort": { "sort": "Ordenar", @@ -464,7 +469,9 @@ "playCount": "Reproducciones", "playlists": "listas", "dragPlaylists": "Arrastra las listas para cambiarlas de orden", - "dropOntoFolder": "o arrástralo a una carpeta para moverlo." + "dropOntoFolder": "o arrástralo a una carpeta para moverlo.", + "newFolder": "Nueva carpeta", + "folderNamePlaceholder": "Introduzca el nombre de la carpeta" }, "favorites": { "title": "Favoritos", @@ -622,7 +629,15 @@ "miniWindowControls": "Controles de mini reproductor", "miniAlwaysOnTopEnable": "Siempre encima", "miniAlwaysOnTopDisable": "Desactivar siempre encima", - "miniAlwaysOnTopLimited": "Siempre encima está limitado por tu gestor de ventanas" + "miniAlwaysOnTopLimited": "Siempre encima está limitado por tu gestor de ventanas", + "disableInfinitePlay": "Desactivar Infinite Play", + "enableInfinitePlay": "Habilitar Infinite Play", + "exitFullscreenWithKey": "Salir de pantalla completa ({key})", + "exitImmersiveWithKey": "Salir de Inmersivo ({key})", + "fullscreenWithKey": "Pantalla completa ({key})", + "restoreWindow": "Restaurar ventana", + "maximizeWindow": "Maximizar ventana", + "windowControls": "Controles de ventana" }, "audioBadges": { "castingTo": "Transmitiendo a", @@ -716,7 +731,7 @@ "shareQobuz": "Compartir (Qobuz)", "albumCredits": "Créditos del álbum", "radio": { - "startArtistRadio" : "Iniciar Artist Radio" + "startArtistRadio": "Iniciar Artist Radio" }, "previousAlbums": "Álbumes anteriores", "nextAlbums": "Próximos álbumes", @@ -729,7 +744,13 @@ }, "newFolder": "Nueva carpeta", "previousArtists": "Artistas anteriores", - "nextArtists": "Próximos artistas" + "nextArtists": "Próximos artistas", + "switchToImmersiveView": "Cambiar a vista inmersiva (V)", + "switchToSplitView": "Cambiar a vista dividida (V)", + "visitReleasePage": "Visitar la página de lanzamiento", + "moveUp": "Subir", + "moveDown": "Bajar", + "backToHome": "De vuelta a la página principal" }, "settings": { "title": "Ajustes", @@ -802,7 +823,8 @@ "resetConfirmTitle": "¿Restablecer Configuración de Audio y Reproducción?", "resetConfirmDesc": "Esto restablecerá toda la configuración de salida de audio, backend y reproducción a los valores predeterminados. La reproducción se detendrá.", "resetSuccess": "La configuración de audio y reproducción se ha restablecido a los valores predeterminados.", - "resetError": "Error al restablecer la configuración de audio: {error}" + "resetError": "Error al restablecer la configuración de audio: {error}", + "noDevicesFound": "No se encontraron dispositivos" }, "playback": { "title": "Reproducción", @@ -991,11 +1013,11 @@ } }, "fpsOptions": { - "disabled": "Desactivado", "15": "15 FPS (Bajo)", "30": "30 FPS (Medio)", "60": "60 FPS (Fluido)", - "120": "120 FPS (Ultra)" + "120": "120 FPS (Ultra)", + "disabled": "Desactivado" }, "miniplayerDefaultView": "Vista predeterminada del mini reproductor", "miniplayerViews": { @@ -1361,7 +1383,13 @@ "clearAllCaches": "Limpiar Todos los Cachés", "clearAllCachesDesc": "Limpia todos los datos en caché anteriores", "cachesDesc": "Cachés de cola, letras, portadas y metadatos" - } + }, + "immersive": { + "title": "Rendimiento degradado", + "perfDegraded": "Rendimiento degradado", + "perfDontShowAgain": "No volver a mostrar" + }, + "noMatchesFound": "No se encontraron coincidencias" }, "metadata": { "editMetadata": "Editar metadatos", @@ -1439,7 +1467,8 @@ "loadFailed": "Error al cargar", "playbackFailed": "Error de reproducción", "cachingFailed": "Error al preparar para offline", - "saveFailed": "Error al guardar" + "saveFailed": "Error al guardar", + "failedToLoad": "No se pudo cargar" }, "time": { "hours": "h", @@ -1601,7 +1630,11 @@ "newState": "Lista negra {status}", "enabled": "activada", "disabled": "desactivada" - } + }, + "loadingDevices": "Cargando dispositivos...", + "addedToPlaylist": "Agregado a la lista", + "copied": "¡Copiado!", + "loadingCredits": "Cargando créditos..." }, "offline": { "title": "Modo Offline", @@ -1676,7 +1709,8 @@ }, "keys": { "enter": "Enter", - "shift": "Shift" + "shift": "Shift", + "esc": "Esc" }, "keybindings": { "title": "Atajos de Teclado", @@ -1837,7 +1871,8 @@ "custom": "Personalizado..." }, "customPlaceholder": "Nombre del binario (ej. mi-player)", - "warning": "Mejor resultado: usa bit-perfect por app. El bit-perfect global en Pulse puede romper navegadores y apps que usan 48kHz fijo." + "warning": "Mejor resultado: usa bit-perfect por app. El bit-perfect global en Pulse puede romper navegadores y apps que usan 48kHz fijo.", + "customApps": "Aplicaciones personalizadas:" }, "wireplumberConfig": { "title": "Apuntar Solo a tu DAC", @@ -1896,7 +1931,8 @@ "restart": "Reiniciar", "verify": "Verificar", "done": "Completado" - } + }, + "title": "Asistente DAC" }, "remoteControlWizard": { "steps": { @@ -2282,5 +2318,63 @@ "author": "Autor", "contributors": "Contribuidores", "signatureDetail": "Odio hacia todas aquellas empresas que discriminan a la comunidad Linux y no nos ofrecen un cliente decente para su producto." + }, + "fileFormat": { + "aac": "AAC", + "alac": "ALAC", + "flac": "FLAC", + "mp3": "MP3", + "opus": "Opus" + }, + "platforms": { + "appleMusic": "Apple Music", + "deezer": "Deezer", + "qobuz": "Qobuz", + "spotify": "Spotify", + "tidal": "Tidal" + }, + "updates": { + "snap": { + "intro": "Algunos enchufes de audio deben conectarse manualmente para obtener la mejor experiencia.", + "note": "Ejecute estos comandos en una terminal, luego reinicie QBZ. Esto sólo debe hacerse una vez.", + "optional": "Opcional (para unidades externas/NAS):", + "requiredPlugConnections": "Conexiones de enchufe necesarias:" + }, + "checkForUpdates": "Buscar actualizaciones", + "flatpak": { + "intro": "Hay algunas consideraciones para garantizar la mejor experiencia.", + "note": "Consulte la sección Flatpak Sandbox en Configuración para obtener instrucciones detalladas y comandos de copiar y pegar.", + "whatYouShouldKnow": { + "audioFeatures": "Algunas funciones de audio pueden comportarse de manera diferente debido a restricciones de la zona de pruebas", + "settingsPage": "La página Configuración incluye comandos específicos de Flatpak que puedes copiar y ejecutar", + "externalMusicFolderAccess": "El acceso a carpetas de música externas (NAS, soportes de red) requiere concesión de permisos manuales", + "heading": "Lo que debes saber:" + } + }, + "whatsNew": { + "whatsNew": "Qué hay de nuevo", + "whatsNewIn": "Novedades de v${version}", + "releaseNotesNotAvailable": "Las notas de la versión no están disponibles.", + "releaseNotesNotAvailableForVersion": "Las notas de la versión no están disponibles para esta versión." + }, + "newVersionAvailable": "Nueva versión disponible: v{version}", + "runningIn": "Ejecutando en {env}", + "noUpdatesFound": "No se encontraron actualizaciones.", + "updateReminder": { + "hint": "Se puede volver a habilitar desde Configuración", + "doNotNotifyAboutReleases": "No avisarme sobre nuevos lanzamientos", + "doNotNotifyAgain": "No volver a notificarme sobre este lanzamiento.", + "newVersionReminder": "Recordatorio de nueva versión", + "remindMeLater": "Recuérdamelo más tarde" + }, + "runningInsideSandbox": "QBZ detectó que se está ejecutando dentro de un {env} sandbox.", + "newReleaseAvailable": "Nueva versión disponible", + "newVersionReleased": "Se ha lanzado una nueva versión de QBZ." + }, + "common": { + "name": "Nombre" + }, + "integrations": { + "connecting": "Conectando..." } } From 117227eac271c1452bd14cbca1fd21121a90ccae Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 09:09:35 +0200 Subject: [PATCH 146/210] feat(i18n): add pt translations --- src/lib/i18n/locales/pt.json | 126 +++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 14 deletions(-) diff --git a/src/lib/i18n/locales/pt.json b/src/lib/i18n/locales/pt.json index 8f81cf61d..af597105e 100644 --- a/src/lib/i18n/locales/pt.json +++ b/src/lib/i18n/locales/pt.json @@ -265,7 +265,10 @@ "refreshMetadata": "Atualizar metadados", "albumSettings": "Configurações do álbum", "albumArtwork": "Capa do Álbum", - "uploadCover": "Enviar capa" + "uploadCover": "Enviar capa", + "background": "Fundo", + "explicit": "Explícito", + "folderName": "Nome" }, "album": { "tracks": "faixas", @@ -360,7 +363,9 @@ "nextResult": "Próximo resultado", "close": "Fechar busca", "searchInPage": "Buscar nesta página" - } + }, + "noAlbumAppearancesFound": "Nenhuma aparição em álbum encontrada", + "noAlbumsFound": "Nenhum álbum encontrado" }, "sort": { "nameAZ": "Nome (A-Z)", @@ -458,7 +463,9 @@ "playlists": "playlists", "folders": "Pastas", "dragPlaylists": "Arraste playlists para reordená-las", - "dropOntoFolder": "ou solte sobre uma pasta para mover." + "dropOntoFolder": "ou solte sobre uma pasta para mover.", + "newFolder": "Nova pasta", + "folderNamePlaceholder": "Digite o nome da pasta" }, "favorites": { "title": "Favoritos", @@ -614,7 +621,17 @@ "miniWindowControls": "Controles do Mini Player", "miniAlwaysOnTopEnable": "Sempre no Topo", "miniAlwaysOnTopDisable": "Desativar Sempre no Topo", - "miniAlwaysOnTopLimited": "Sempre no topo é limitado pelo seu gerenciador de janelas" + "miniAlwaysOnTopLimited": "Sempre no topo é limitado pelo seu gerenciador de janelas", + "disableInfinitePlay": "Desativar Infinite Play", + "enableInfinitePlay": "Ativar Infinite Play", + "exitFullscreenWithKey": "Sair da tela cheia ({key})", + "exitImmersiveWithKey": "Sair da imersão ({key})", + "fullscreenWithKey": "Tela cheia ({key})", + "qobuzConnect": "Qobuz Connect", + "restoreWindow": "Janela Restaurar", + "qobuzConnectManage": "Qobuz Connect - Clique para gerenciar", + "maximizeWindow": "Maximizar janela", + "windowControls": "Controles de janela" }, "audioBadges": { "castingTo": "Transmitindo para", @@ -722,7 +739,13 @@ }, "newFolder": "Nova pasta", "previousArtists": "Artistas anteriores", - "nextArtists": "Próximos artistas" + "nextArtists": "Próximos artistas", + "switchToImmersiveView": "Mudar para visualização imersiva (V)", + "switchToSplitView": "Mudar para visualização dividida (V)", + "visitReleasePage": "Visite a página de lançamento", + "moveUp": "Subir", + "moveDown": "Mover para baixo", + "backToHome": "De volta para casa" }, "settings": { "title": "Configurações", @@ -795,7 +818,8 @@ "resetConfirmTitle": "Redefinir Configurações de Áudio e Reprodução?", "resetConfirmDesc": "Isso irá redefinir todas as configurações de saída de áudio, backend e reprodução para os padrões. A reprodução será interrompida.", "resetSuccess": "Configurações de áudio e reprodução foram redefinidas para os padrões.", - "resetError": "Falha ao redefinir configurações de áudio: {error}" + "resetError": "Falha ao redefinir configurações de áudio: {error}", + "noDevicesFound": "Nenhum dispositivo encontrado" }, "playback": { "title": "Reprodução", @@ -984,11 +1008,11 @@ } }, "fpsOptions": { - "disabled": "Desativado", "15": "15 FPS (Baixo)", "30": "30 FPS (Médio)", "60": "60 FPS (Suave)", - "120": "120 FPS (Ultra)" + "120": "120 FPS (Ultra)", + "disabled": "Desativado" }, "miniplayerDefaultView": "Visualização padrão do mini player", "miniplayerViews": { @@ -1354,7 +1378,13 @@ "clearAllCaches": "Limpar Todos os Caches", "clearAllCachesDesc": "Limpa todos os dados em cache acima", "cachesDesc": "Caches de fila, letras, capas e metadados" - } + }, + "immersive": { + "title": "Desempenho degradado", + "perfDegraded": "Desempenho degradado", + "perfDontShowAgain": "Não mostre novamente" + }, + "noMatchesFound": "Nenhuma correspondência encontrada" }, "metadata": { "editMetadata": "Editar metadados", @@ -1399,7 +1429,7 @@ }, "legal": { "brandingLine": "Este aplicativo utiliza a API do Qobuz, mas não é certificado pelo Qobuz™.", - "brandingLineSuffix": "", + "brandingLineSuffix": "Este aplicativo usa a API Qobuz™, mas não é certificado pela Qobuz™.", "tosAgreementPrefix": "Eu li os", "tosLinkText": "Termos de Serviço do Qobuz™", "tosRequiredToLogin": "Confirme que você leu os Termos de Serviço do Qobuz™ para continuar.", @@ -1432,7 +1462,8 @@ "loadFailed": "Falha ao carregar", "playbackFailed": "Falha na reprodução", "cachingFailed": "Falha ao preparar para offline", - "saveFailed": "Falha ao salvar" + "saveFailed": "Falha ao salvar", + "failedToLoad": "Falha ao carregar" }, "time": { "hours": "h", @@ -1589,7 +1620,11 @@ "failedSaveAutoplay": "Falha ao salvar preferência de reprodução automática", "failedSaveIconVisibility": "Falha ao salvar preferência de visibilidade do ícone", "failedRepairOffline": "Falha ao reparar biblioteca offline: {error}", - "failedOpenCacheFolder": "Falha ao abrir pasta de cache" + "failedOpenCacheFolder": "Falha ao abrir pasta de cache", + "loadingDevices": "Carregando dispositivos...", + "addedToPlaylist": "Adicionado à lista de reprodução", + "copied": "Copiado!", + "loadingCredits": "Carregando créditos..." }, "offline": { "title": "Modo Offline", @@ -1819,7 +1854,8 @@ "custom": "Personalizado..." }, "customPlaceholder": "Nome do binário (ex.: my-player)", - "warning": "Melhores resultados: use bit-perfect apenas por aplicativo. Bit-perfect global do Pulse pode quebrar navegadores e aplicativos que enviam 48kHz fixo." + "warning": "Melhores resultados: use bit-perfect apenas por aplicativo. Bit-perfect global do Pulse pode quebrar navegadores e aplicativos que enviam 48kHz fixo.", + "customApps": "Aplicativos personalizados:" }, "wireplumberConfig": { "title": "Direcionar Apenas ao Seu DAC", @@ -1878,7 +1914,8 @@ "restart": "Reiniciar", "verify": "Verificar", "done": "Concluído" - } + }, + "title": "Assistente DAC" }, "remoteControlWizard": { "steps": { @@ -2229,5 +2266,66 @@ "rememberChoice": "Lembrar minha escolha", "autoRetrying": "Tentando novamente com qualidade mais baixa...", "autoSkipping": "Pulando faixa (problema no servidor)..." + }, + "fileFormat": { + "aac": "AAC", + "alac": "ALAC", + "flac": "FLAC", + "mp3": "MP3", + "opus": "Opus" + }, + "keys": { + "esc": "Esc" + }, + "platforms": { + "appleMusic": "Apple Music", + "deezer": "Deezer", + "qobuz": "Qobuz", + "spotify": "Spotify", + "tidal": "Tidal" + }, + "updates": { + "snap": { + "intro": "Alguns plugues de áudio precisam ser conectados manualmente para obter a melhor experiência.", + "note": "Execute esses comandos em um terminal e reinicie o QBZ. Isso só precisa ser feito uma vez.", + "optional": "Opcional (para unidades externas/NAS):", + "requiredPlugConnections": "Conexões de plugue necessárias:" + }, + "checkForUpdates": "Verifique se há atualizações", + "flatpak": { + "intro": "Existem algumas considerações para garantir a melhor experiência.", + "note": "Verifique a seção Flatpak Sandbox em Configurações para obter instruções detalhadas e comandos de copiar e colar.", + "whatYouShouldKnow": { + "audioFeatures": "Alguns recursos de áudio podem se comportar de maneira diferente devido às restrições do sandbox", + "settingsPage": "A página Configurações inclui comandos específicos do Flatpak que você pode copiar e executar", + "externalMusicFolderAccess": "O acesso a pastas de música externas (NAS, montagens de rede) requer permissões manuais", + "heading": "O que você deve saber:" + } + }, + "whatsNew": { + "whatsNew": "O que há de novo", + "whatsNewIn": "O que há de novo em v${version}", + "releaseNotesNotAvailable": "As notas de versão não estão disponíveis.", + "releaseNotesNotAvailableForVersion": "As notas de lançamento não estão disponíveis para esta versão." + }, + "newVersionAvailable": "Nova versão disponível: v{version}", + "runningIn": "Executando em {env}", + "noUpdatesFound": "Nenhuma atualização encontrada.", + "updateReminder": { + "hint": "Pode ser reativado em Configurações", + "doNotNotifyAboutReleases": "Não me avise sobre novos lançamentos", + "doNotNotifyAgain": "Não me avise novamente sobre este lançamento", + "newVersionReminder": "Lembrete de nova versão", + "remindMeLater": "Lembre-me mais tarde" + }, + "runningInsideSandbox": "QBZ detectou que está sendo executado dentro de um {env} sandbox.", + "newReleaseAvailable": "Novo lançamento disponível", + "newVersionReleased": "Uma nova versão do QBZ foi lançada" + }, + "common": { + "name": "Nome" + }, + "integrations": { + "connecting": "Conectando..." } } From 6e0f4abd64b441a8f5ce983e1165aad86b372c86 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 09:09:44 +0200 Subject: [PATCH 147/210] feat(i18n): add fr translations --- src/lib/i18n/locales/fr.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/i18n/locales/fr.json b/src/lib/i18n/locales/fr.json index 6b641493f..0fab1c8f1 100644 --- a/src/lib/i18n/locales/fr.json +++ b/src/lib/i18n/locales/fr.json @@ -691,7 +691,7 @@ "shareQobuz": "Partager (Qobuz)", "albumCredits": "Crédits de l'album", "radio": { - "startArtistRadio" : "Lancer la radio de l'artiste" + "startArtistRadio": "Lancer la radio de l'artiste" }, "previousAlbums": "Albums précédents", "nextAlbums": "Albums suivants", @@ -966,11 +966,11 @@ } }, "fpsOptions": { - "disabled": "Désactivé", "15": "15 IPS (Bas)", "30": "30 IPS (Moyen)", "60": "60 IPS (Élevé)", - "120": "120 IPS (Ultra)" + "120": "120 IPS (Ultra)", + "disabled": "Désactivé" }, "miniplayerDefaultView": "Vue par défaut du mini-lecteur", "miniplayerViews": { @@ -1530,7 +1530,8 @@ "newState": "Liste noire {status}", "enabled": "activée", "disabled": "désactivée" - } + }, + "copied": "Copié!" }, "offline": { "title": "Mode hors ligne", From c346861db05d0c2178d9c399662a7704818a2388 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:10:47 +0000 Subject: [PATCH 148/210] chore(deps): bump tempfile from 3.26.0 to 3.27.0 in /src-tauri Bumps [tempfile](https://github.com/Stebalien/tempfile) from 3.26.0 to 3.27.0. - [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md) - [Commits](https://github.com/Stebalien/tempfile/compare/v3.26.0...v3.27.0) --- updated-dependencies: - dependency-name: tempfile dependency-version: 3.27.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 680bd4e36..854fb9c58 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -194,7 +194,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.52.0", + "windows-sys 0.60.2", "wl-clipboard-rs", "x11rb", ] @@ -1405,7 +1405,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1668,7 +1668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2631,7 +2631,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.2", "system-configuration", "tokio", "tower-service", @@ -3631,7 +3631,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3946,7 +3946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] @@ -4863,7 +4863,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.2", "thiserror 2.0.18", "tokio", "tracing", @@ -4900,9 +4900,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -5445,7 +5445,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6758,15 +6758,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7894,7 +7894,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] From 8b220bf3f84358eb9247bd5516d2bdd4cabed4fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:10:50 +0000 Subject: [PATCH 149/210] chore(deps-dev): bump svelte-check from 4.4.5 to 4.4.6 Bumps [svelte-check](https://github.com/sveltejs/language-tools) from 4.4.5 to 4.4.6. - [Release notes](https://github.com/sveltejs/language-tools/releases) - [Commits](https://github.com/sveltejs/language-tools/compare/svelte-check@4.4.5...svelte-check@4.4.6) --- updated-dependencies: - dependency-name: svelte-check dependency-version: 4.4.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 43 ++++++++++++++++++++++++++++--------------- package.json | 2 +- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6daf4510b..0895f2bb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@testing-library/svelte": "^5.3.1", "jsdom": "^27.4.0", "svelte": "^5.55.0", - "svelte-check": "^4.4.5", + "svelte-check": "^4.4.6", "typescript": "~5.9.3", "vite": "^8.0.3", "vitest": "^4.1.2" @@ -196,7 +196,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -240,17 +239,40 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } }, + "node_modules/@emnapi/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", + "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", + "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -692,7 +714,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.55.0.tgz", "integrity": "sha512-MdFRjevVxmAknf2NbaUkDF16jSIzXMWd4Nfah0Qp8TtQVoSp3bV4jKt8mX7z7qTUTWvgSaxtR0EG5WJf53gcuA==", "license": "MIT", - "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", @@ -734,7 +755,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.0.0.tgz", "integrity": "sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==", "license": "MIT", - "peer": true, "dependencies": { "deepmerge": "^4.3.1", "magic-string": "^0.30.21", @@ -1244,7 +1264,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1791,7 +1810,6 @@ "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.28", "@asamuzakjp/dom-selector": "^6.7.6", @@ -2253,7 +2271,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2458,7 +2475,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.0.tgz", "integrity": "sha512-SThllKq6TRMBwPtat7ASnm/9CDXnIhBR0NPGw0ujn2DVYx9rVwsPZxDaDQcYGdUz/3BYVsCzdq7pZarRQoGvtw==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2482,9 +2498,9 @@ } }, "node_modules/svelte-check": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.5.tgz", - "integrity": "sha512-1bSwIRCvvmSHrlK52fOlZmVtUZgil43jNL/2H18pRpa+eQjzGt6e3zayxhp1S7GajPFKNM/2PMCG+DZFHlG9fw==", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.6.tgz", + "integrity": "sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==", "dev": true, "license": "MIT", "dependencies": { @@ -3090,7 +3106,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -3104,7 +3119,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -3202,7 +3216,6 @@ "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.1.2", "@vitest/mocker": "4.1.2", diff --git a/package.json b/package.json index 32dd08769..ba29e1126 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@testing-library/svelte": "^5.3.1", "jsdom": "^27.4.0", "svelte": "^5.55.0", - "svelte-check": "^4.4.5", + "svelte-check": "^4.4.6", "typescript": "~5.9.3", "vite": "^8.0.3", "vitest": "^4.1.2" From 88b5df19a9def02ab50bcb8a45c294eadfb75bc8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:11:13 +0000 Subject: [PATCH 150/210] chore(deps): bump env_logger from 0.11.9 to 0.11.10 in /src-tauri Bumps [env_logger](https://github.com/rust-cli/env_logger) from 0.11.9 to 0.11.10. - [Release notes](https://github.com/rust-cli/env_logger/releases) - [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-cli/env_logger/compare/v0.11.9...v0.11.10) --- updated-dependencies: - dependency-name: env_logger dependency-version: 0.11.10 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 680bd4e36..16f21a2e7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -124,9 +124,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -145,9 +145,9 @@ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -194,7 +194,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.52.0", + "windows-sys 0.60.2", "wl-clipboard-rs", "x11rb", ] @@ -1405,7 +1405,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1633,9 +1633,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.9" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ "anstream", "anstyle", @@ -1668,7 +1668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2631,7 +2631,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.2", "system-configuration", "tokio", "tower-service", @@ -3631,7 +3631,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3946,7 +3946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] @@ -4863,7 +4863,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.2", "thiserror 2.0.18", "tokio", "tracing", @@ -4900,9 +4900,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -5445,7 +5445,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6766,7 +6766,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7894,7 +7894,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] From 32923d3fc30d49e0c3e951737a20a59e03442781 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:11:31 +0000 Subject: [PATCH 151/210] chore(deps): bump uuid from 1.22.0 to 1.23.0 in /src-tauri Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.22.0 to 1.23.0. - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](https://github.com/uuid-rs/uuid/compare/v1.22.0...v1.23.0) --- updated-dependencies: - dependency-name: uuid dependency-version: 1.23.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- src-tauri/Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 680bd4e36..7533a10d1 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -194,7 +194,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.52.0", + "windows-sys 0.60.2", "wl-clipboard-rs", "x11rb", ] @@ -1405,7 +1405,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1668,7 +1668,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2631,7 +2631,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.2", "system-configuration", "tokio", "tower-service", @@ -3631,7 +3631,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 1.3.1", + "proc-macro-crate 3.4.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3946,7 +3946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.45.0", + "windows-sys 0.61.2", ] [[package]] @@ -4863,7 +4863,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.2", "thiserror 2.0.18", "tokio", "tracing", @@ -4900,9 +4900,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -5445,7 +5445,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6766,7 +6766,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -7450,9 +7450,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -7894,7 +7894,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] From 7c7aba5f8003c81e09412202cdb0ba9d87df4130 Mon Sep 17 00:00:00 2001 From: Gwendal Beaumont Date: Thu, 2 Apr 2026 12:02:18 +0200 Subject: [PATCH 152/210] feat(i18n): translation strings to TrackMenu --- src/lib/components/TrackMenu.svelte | 45 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/lib/components/TrackMenu.svelte b/src/lib/components/TrackMenu.svelte index 84e935774..e62d96b6f 100644 --- a/src/lib/components/TrackMenu.svelte +++ b/src/lib/components/TrackMenu.svelte @@ -1,5 +1,6 @@
{#if release} diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index 5d25f93bc..2ca9790db 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -1259,7 +1259,7 @@ "plexStatusConnected": "Verbunden mit {server} (v{version}).", "plexStatusError": "Plex-Fehler: {error}", "plexStatusPlaying": "Spielt: {title}", - "noProtocolDeviceFound": "Keine {Protokoll}-Geräte gefunden", + "noProtocolDeviceFound": "Keine {protocol}-Geräte gefunden", "noProtocolDeviceFoundHint": "Stellen Sie sicher, dass sich die Geräte im selben Netzwerk befinden", "plexStatusPlayingDirect": "Direkte Wiedergabe von Plex: {title} ({rate} Hz / {bits}-bit, {contentType}).", "plexStatusPlayingFallback": "{title} wird abgespielt, aber die direkte Wiedergabe ist nicht bestätigt (Teil: {partKey}).", @@ -2240,7 +2240,7 @@ }, "whatsNew": { "whatsNew": "Was ist neu", - "whatsNewIn": "Was ist neu in v${version}?", + "whatsNewIn": "Was ist neu in v{version}?", "releaseNotesNotAvailable": "Versionshinweise sind nicht verfügbar.", "releaseNotesNotAvailableForVersion": "Für diese Version sind keine Versionshinweise verfügbar." }, diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index b6b81cc43..dea190182 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -2381,7 +2381,7 @@ }, "whatsNew": { "whatsNew": "What's new", - "whatsNewIn": "What's new in v${version}", + "whatsNewIn": "What's new in v{version}", "releaseNotesNotAvailable": "Release notes are not available.", "releaseNotesNotAvailableForVersion": "Release notes are not available for this version." }, diff --git a/src/lib/i18n/locales/es.json b/src/lib/i18n/locales/es.json index 820505f9f..81d9299db 100644 --- a/src/lib/i18n/locales/es.json +++ b/src/lib/i18n/locales/es.json @@ -1266,7 +1266,7 @@ "qobuzLinkHandlerError": "Error al actualizar el registro del manejador de enlaces.", "qconnectDeviceName": "Nombre del dispositivo", "qconnectDeviceNameDesc": "Nombre mostrado en Qobuz Connect", - "noProtocolDeviceFound": "No se encontraron dispositivos {protocolo}", + "noProtocolDeviceFound": "No se encontraron dispositivos {protocol}", "noProtocolDeviceFoundHint": "Asegúrese de que los dispositivos estén en la misma red" }, "storage": { @@ -2384,7 +2384,7 @@ }, "whatsNew": { "whatsNew": "Qué hay de nuevo", - "whatsNewIn": "Novedades de v${version}", + "whatsNewIn": "Novedades de v{version}", "releaseNotesNotAvailable": "Las notas de la versión no están disponibles.", "releaseNotesNotAvailableForVersion": "Las notas de la versión no están disponibles para esta versión." }, diff --git a/src/lib/i18n/locales/fr.json b/src/lib/i18n/locales/fr.json index 8cdb13c4a..ceeaf733d 100644 --- a/src/lib/i18n/locales/fr.json +++ b/src/lib/i18n/locales/fr.json @@ -2405,7 +2405,7 @@ "releaseNotesNotAvailable": "Les notes de version ne sont pas disponibles.", "releaseNotesNotAvailableForVersion": "Les notes de version ne sont pas disponibles pour cette version.", "whatsNew": "Quoi de neuf", - "whatsNewIn": "Quoi de neuf dans v${version}" + "whatsNewIn": "Quoi de neuf dans v{version}" } }, "musician": { diff --git a/src/lib/i18n/locales/pt.json b/src/lib/i18n/locales/pt.json index 594943824..1a9d42084 100644 --- a/src/lib/i18n/locales/pt.json +++ b/src/lib/i18n/locales/pt.json @@ -2350,7 +2350,7 @@ }, "whatsNew": { "whatsNew": "O que há de novo", - "whatsNewIn": "O que há de novo em v${version}", + "whatsNewIn": "O que há de novo em v{version}", "releaseNotesNotAvailable": "As notas de versão não estão disponíveis.", "releaseNotesNotAvailableForVersion": "As notas de lançamento não estão disponíveis para esta versão." }, From 2e5b9368e443c7c50dd970fd43d728bd9ef0baa7 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Sun, 5 Apr 2026 01:12:53 +0200 Subject: [PATCH 181/210] fix: add windows to Platform type --- src/lib/utils/platform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/utils/platform.ts b/src/lib/utils/platform.ts index c2679c178..b5341b0ab 100644 --- a/src/lib/utils/platform.ts +++ b/src/lib/utils/platform.ts @@ -10,7 +10,7 @@ import { platform as tauriPlatform } from '@tauri-apps/plugin-os'; -type Platform = 'macos' | 'linux'; +type Platform = 'macos' | 'linux' | 'windows'; let detectedPlatform: Platform; try { From 86d5286a29feaed9bb41afd19024f29e00042c9e Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Sun, 5 Apr 2026 01:12:53 +0200 Subject: [PATCH 182/210] fix: add Windows case to AboutModal platform label --- src/lib/components/AboutModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/AboutModal.svelte b/src/lib/components/AboutModal.svelte index fbabb2110..e78990bf8 100644 --- a/src/lib/components/AboutModal.svelte +++ b/src/lib/components/AboutModal.svelte @@ -38,7 +38,7 @@ } }); - const platformLabel = platform === 'macos' ? 'macOS (Tauri 2.0)' : 'Linux (Tauri 2.0)'; + const platformLabel = platform === 'macos' ? 'macOS (Tauri 2.0)' : platform === 'windows' ? 'Windows (Tauri 2.0)' : 'Linux (Tauri 2.0)'; function handleOpenUrl(url: string) { openUrl(url).catch(err => console.error('Failed to open URL:', err)); From 787196db99c704357ae101956cea5b0106540636 Mon Sep 17 00:00:00 2001 From: Afonso Jorge Ramos Date: Sun, 5 Apr 2026 01:12:54 +0200 Subject: [PATCH 183/210] fix: keep exclusive mode and DAC passthrough visible on all platforms --- src/lib/components/views/SettingsView.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/components/views/SettingsView.svelte b/src/lib/components/views/SettingsView.svelte index 49928768e..ff5a0db93 100644 --- a/src/lib/components/views/SettingsView.svelte +++ b/src/lib/components/views/SettingsView.svelte @@ -3977,6 +3977,7 @@
{/if} + {/if}
{$t('settings.audio.exclusiveMode')} (?) @@ -3994,6 +3995,7 @@ {#if dacPassthrough} {$t('settings.audio.dacPassthroughNote')} {/if} + {#if platform === 'linux'} {#if isFlatpak && selectedBackend === 'PipeWire' && dacPassthrough}
⚠️
From a8e6d151dc20e9e87398572e2c06646bedcd785c Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Sat, 4 Apr 2026 20:38:09 -0600 Subject: [PATCH 184/210] fix(security): remove sensitive values from log lines flagged by CodeQL session_uuid and user_id removed from log output entirely. CodeQL does not recognize custom sanitizer functions as sufficient. cc --- src-tauri/src/qconnect_service.rs | 2 +- src-tauri/src/session_lifecycle.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/qconnect_service.rs b/src-tauri/src/qconnect_service.rs index 41dfba610..b731edf05 100644 --- a/src-tauri/src/qconnect_service.rs +++ b/src-tauri/src/qconnect_service.rs @@ -3926,7 +3926,7 @@ async fn deferred_renderer_join( log::error!("[QConnect] Deferred renderer max quality report failed: {err}"); } - log::info!("[QConnect] Deferred renderer join complete for session {}", crate::log_sanitize::mask_uuid(&session_uuid)); + log::info!("[QConnect] Deferred renderer join complete"); // Re-request session state so the server sends an updated renderer list // (including ourselves). Without this, the frontend may not see QBZ as a diff --git a/src-tauri/src/session_lifecycle.rs b/src-tauri/src/session_lifecycle.rs index d61ff67f0..a97c2dacf 100644 --- a/src-tauri/src/session_lifecycle.rs +++ b/src-tauri/src/session_lifecycle.rs @@ -345,8 +345,7 @@ pub async fn activate_offline_session(app: &tauri::AppHandle) -> Result<(), Stri // settings, and cached data. Fall back to 0 if never logged in. let offline_user_id = UserDataPaths::load_last_user_id().unwrap_or(0); log::info!( - "[SessionLifecycle] Activating offline session (user_id={}{})", - crate::log_sanitize::mask_id(offline_user_id), + "[SessionLifecycle] Activating offline session{}", if offline_user_id == 0 { " — no previous session" } else { "" } ); From 648f690a0b8522eb7fe8504d6e6ba4447703dea3 Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:03:11 -0600 Subject: [PATCH 185/210] feat(auth): add system browser OAuth login and extract shared finalize_oauth_login Refactor: shared finalize_oauth_login() handles code exchange, CoreBridge injection, session activation, and token persistence. Frontend-agnostic, reusable by GUI, TUI, and headless clients. New command: v2_start_system_browser_oauth spawns a local axum listener, opens the user's default browser for Qobuz OAuth, and captures the redirect code automatically. cc --- src-tauri/src/commands_v2.rs | 185 ++++++++++++++++++++++++++++++----- src-tauri/src/lib.rs | 1 + 2 files changed, 163 insertions(+), 23 deletions(-) diff --git a/src-tauri/src/commands_v2.rs b/src-tauri/src/commands_v2.rs index a84043aea..2efdaf3e7 100644 --- a/src-tauri/src/commands_v2.rs +++ b/src-tauri/src/commands_v2.rs @@ -1687,7 +1687,6 @@ pub async fn v2_start_oauth_login( core_bridge: State<'_, CoreBridgeState>, legal_state: State<'_, LegalSettingsState>, ) -> Result { - let manager = runtime.manager(); log::info!("[V2] v2_start_oauth_login starting..."); // Get app_id from initialized client @@ -1859,12 +1858,31 @@ pub async fn v2_start_oauth_login( } }; - log::info!("[V2] OAuth code received, exchanging for session..."); + finalize_oauth_login(&code, &app, &runtime, &app_state, &core_bridge, &legal_state).await +} - // Exchange code for UserSession via legacy client +/// Complete the OAuth login flow after obtaining an authorization code. +/// +/// Frontend-agnostic: does not depend on how the code was obtained (WebView, +/// system browser, manual paste, etc.). Suitable for GUI, TUI, and headless. +/// +/// Steps: exchange code → establish session → inject into CoreBridge → +/// activate per-user data → persist token. +async fn finalize_oauth_login( + code: &str, + app: &tauri::AppHandle, + runtime: &State<'_, RuntimeManagerState>, + app_state: &State<'_, AppState>, + core_bridge: &State<'_, CoreBridgeState>, + legal_state: &State<'_, LegalSettingsState>, +) -> Result { + let manager = runtime.manager(); + log::info!("[OAuth] Exchanging authorization code for session..."); + + // Exchange code for UserSession let session = { let client = app_state.client.read().await; - match client.login_with_oauth_code(&code).await { + match client.login_with_oauth_code(code).await { Ok(s) => s, Err(e) => { return Ok(V2LoginResponse { @@ -1880,7 +1898,7 @@ pub async fn v2_start_oauth_login( } }; - log::info!("[V2] OAuth session established"); + log::info!("[OAuth] Session established, activating..."); manager.set_legacy_auth(true, Some(session.user_id)).await; let _ = app.emit( "runtime:event", @@ -1891,13 +1909,12 @@ pub async fn v2_start_oauth_login( ); // Convert api::models::UserSession → qbz_models::UserSession for CoreBridge - // Both types are structurally identical; serde round-trip is safe. let core_session: UserSession = match serde_json::to_value(&session).and_then(serde_json::from_value) { Ok(s) => s, Err(e) => { - log::error!("[V2] Failed to convert session for CoreBridge: {}", e); - rollback_auth_state(&manager, &app).await; + log::error!("[OAuth] Failed to convert session for CoreBridge: {}", e); + rollback_auth_state(&manager, app).await; return Ok(V2LoginResponse { success: false, user_name: None, @@ -1910,16 +1927,16 @@ pub async fn v2_start_oauth_login( } }; - // CoreBridge auth — inject session directly (OAuth has no email/password) + // Inject session into CoreBridge if let Some(bridge) = core_bridge.try_get().await { match bridge.login_with_session(core_session).await { Ok(_) => { - log::info!("[V2] CoreBridge session injected for OAuth user"); + log::info!("[OAuth] CoreBridge session injected"); manager.set_corebridge_auth(true).await; } Err(e) => { - log::error!("[V2] CoreBridge session injection failed: {}", e); - rollback_auth_state(&manager, &app).await; + log::error!("[OAuth] CoreBridge session injection failed: {}", e); + rollback_auth_state(&manager, app).await; let _ = app.emit( "runtime:event", RuntimeEvent::CoreBridgeAuthFailed { @@ -1938,8 +1955,8 @@ pub async fn v2_start_oauth_login( } } } else { - log::error!("[V2] CoreBridge not initialized for OAuth login"); - rollback_auth_state(&manager, &app).await; + log::error!("[OAuth] CoreBridge not initialized"); + rollback_auth_state(&manager, app).await; return Ok(V2LoginResponse { success: false, user_name: Some(session.display_name), @@ -1951,10 +1968,10 @@ pub async fn v2_start_oauth_login( }); } - // Activate per-user session (same as manual login) - if let Err(e) = crate::session_lifecycle::activate_session(&app, session.user_id).await { - log::error!("[V2] Session activation failed after OAuth: {}", e); - rollback_auth_state(&manager, &app).await; + // Activate per-user session + if let Err(e) = crate::session_lifecycle::activate_session(app, session.user_id).await { + log::error!("[OAuth] Session activation failed: {}", e); + rollback_auth_state(&manager, app).await; return Ok(V2LoginResponse { success: false, user_name: Some(session.display_name.clone()), @@ -1966,14 +1983,12 @@ pub async fn v2_start_oauth_login( }); } - // Persist the OAuth token so bootstrap can restore the session on next launch. - // Non-fatal: if saving fails, the user just has to re-login via OAuth. + // Persist OAuth token for session restore on next launch if let Err(e) = crate::credentials::save_oauth_token(&session.user_auth_token) { - log::warn!("[V2] Failed to persist OAuth token: {}", e); + log::warn!("[OAuth] Failed to persist token: {}", e); } - // Persist ToS acceptance now that login succeeded. - accept_tos_best_effort(&legal_state); + accept_tos_best_effort(legal_state); let _ = app.emit( "runtime:event", @@ -1993,6 +2008,130 @@ pub async fn v2_start_oauth_login( }) } +/// OAuth login via the user's system browser. +/// +/// Spawns a temporary local HTTP server, opens the system browser to the Qobuz +/// OAuth page with a localhost redirect, captures the authorization code when +/// Qobuz redirects back, then completes the login via `finalize_oauth_login`. +#[tauri::command] +pub async fn v2_start_system_browser_oauth( + app: tauri::AppHandle, + runtime: State<'_, RuntimeManagerState>, + app_state: State<'_, AppState>, + core_bridge: State<'_, CoreBridgeState>, + legal_state: State<'_, LegalSettingsState>, +) -> Result { + log::info!("[V2] System browser OAuth starting..."); + + // Get app_id from initialized client + let app_id = { + let client = app_state.client.read().await; + client.app_id().await.map_err(|e| e.to_string())? + }; + + // Bind to a random available port + let listener = tokio::net::TcpListener::bind("127.0.0.1:0") + .await + .map_err(|e| format!("Failed to bind local OAuth listener: {}", e))?; + let port = listener + .local_addr() + .map_err(|e| format!("Failed to get listener address: {}", e))? + .port(); + + let oauth_url = format!( + "https://www.qobuz.com/signin/oauth?ext_app_id={}&redirect_url={}", + app_id, + urlencoding::encode(&format!("http://localhost:{}", port)), + ); + + // Channel for the authorization code + let (tx, mut rx) = tokio::sync::mpsc::channel::(1); + + // Local HTTP handler: serves a success page and forwards the code + let oauth_handler = axum::Router::new().route( + "/", + axum::routing::get(move |axum::extract::Query(params): axum::extract::Query>| { + let tx = tx.clone(); + async move { + if let Some(code) = params.get("code_autorisation").or_else(|| params.get("code")) { + let _ = tx.send(code.clone()).await; + axum::response::Html( + "\ +

Login successful

\ +

You can close this tab and return to QBZ.

\ + " + ) + } else { + axum::response::Html( + "\ +

Login failed

\ +

No authorization code received. Please try again.

\ + " + ) + } + } + }), + ); + + // Spawn the server in the background + let server_handle = tokio::spawn(async move { + axum::serve(listener, oauth_handler).await.ok(); + }); + + // Open the user's default browser + log::info!("[OAuth] Opening system browser to Qobuz login (port {})", port); + if let Err(e) = open::that(&oauth_url) { + log::error!("[OAuth] Failed to open system browser: {}", e); + server_handle.abort(); + return Ok(V2LoginResponse { + success: false, + user_name: None, + user_id: None, + subscription: None, + subscription_valid_until: None, + error: Some(format!("Failed to open browser: {}", e)), + error_code: Some("browser_open_failed".to_string()), + }); + } + + // Wait up to 5 minutes for the redirect + let code = tokio::time::timeout( + std::time::Duration::from_secs(300), + rx.recv(), + ) + .await; + + server_handle.abort(); + + let code = match code { + Ok(Some(c)) => c, + Ok(None) => { + return Ok(V2LoginResponse { + success: false, + user_name: None, + user_id: None, + subscription: None, + subscription_valid_until: None, + error: Some("OAuth login cancelled".to_string()), + error_code: Some("oauth_cancelled".to_string()), + }); + } + Err(_) => { + return Ok(V2LoginResponse { + success: false, + user_name: None, + user_id: None, + subscription: None, + subscription_valid_until: None, + error: Some("OAuth login timed out after 5 minutes".to_string()), + error_code: Some("oauth_timeout".to_string()), + }); + } + }; + + finalize_oauth_login(&code, &app, &runtime, &app_state, &core_bridge, &legal_state).await +} + // ==================== Prefetch (V2) ==================== /// Number of Qobuz tracks to prefetch (not total tracks, just Qobuz) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 11eadbbea..f9c4321f3 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1217,6 +1217,7 @@ pub fn run() { commands_v2::v2_auto_login, commands_v2::v2_manual_login, commands_v2::v2_start_oauth_login, + commands_v2::v2_start_system_browser_oauth, commands_v2::v2_get_user_info, commands_v2::v2_save_credentials, commands_v2::v2_clear_saved_credentials, From 7588a4804af057daefdefd1c474ee321824c8aba Mon Sep 17 00:00:00 2001 From: vicrodh <103399283+vicrodh@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:05:36 -0600 Subject: [PATCH 186/210] feat(ui): add system browser OAuth link in login view Secondary link-style option below the main OAuth button. Opens the user's default browser (Firefox, Chrome, etc.) instead of the Tauri WebView. Useful for password managers and saved sessions. i18n keys added to all 5 locales. cc --- src/lib/components/views/LoginView.svelte | 129 +++++++++++++++------- src/lib/i18n/locales/de.json | 2 + src/lib/i18n/locales/en.json | 2 + src/lib/i18n/locales/es.json | 2 + src/lib/i18n/locales/fr.json | 2 + src/lib/i18n/locales/pt.json | 2 + 6 files changed, 100 insertions(+), 39 deletions(-) diff --git a/src/lib/components/views/LoginView.svelte b/src/lib/components/views/LoginView.svelte index ba5ad21fa..5d75cb033 100644 --- a/src/lib/components/views/LoginView.svelte +++ b/src/lib/components/views/LoginView.svelte @@ -20,6 +20,7 @@ let { onLoginSuccess, onStartOffline }: Props = $props(); let isOAuthLoading = $state(false); + let isSystemBrowserLoading = $state(false); let isInitializing = $state(true); let initStatus = $state('Connecting to Qobuz™...'); let error = $state(null); @@ -185,6 +186,43 @@ } } + type OAuthResponse = { + success: boolean; + user_name?: string; + user_id?: number; + subscription?: string; + subscription_valid_until?: string | null; + error?: string; + error_code?: string; + }; + + function handleOAuthResponse(response: OAuthResponse): boolean { + if (response.success) { + if (!response.user_id || response.user_id === 0) { + error = $t('auth.v2AuthFailed'); + return false; + } + onLoginSuccess({ + userName: response.user_name || 'User', + userId: response.user_id, + subscription: response.subscription || 'Active', + subscriptionValidUntil: response.subscription_valid_until ?? null, + }); + return true; + } + + if (response.error_code === 'v2_auth_failed') { + error = $t('auth.v2AuthFailed'); + } else if (response.error_code === 'v2_not_initialized') { + error = $t('auth.v2NotInitialized'); + } else if (response.error_code === 'oauth_cancelled') { + error = null; + } else { + error = response.error || 'Login failed'; + } + return false; + } + async function handleOAuthLogin() { if (!get(qobuzTosAccepted)) { error = $t('legal.tosRequiredToLogin'); @@ -196,46 +234,12 @@ try { await setTosAcceptance(true); - } catch { - // Continue even if persistence fails - } + } catch { /* continue */ } try { - const response = await invoke<{ - success: boolean; - user_name?: string; - user_id?: number; - subscription?: string; - subscription_valid_until?: string | null; - error?: string; - error_code?: string; - }>('v2_start_oauth_login'); - + const response = await invoke('v2_start_oauth_login'); console.log('[LoginView] v2_start_oauth_login: success =', response.success); - - if (response.success) { - if (!response.user_id || response.user_id === 0) { - console.error('[LoginView] OAuth login returned success but invalid user_id'); - error = $t('auth.v2AuthFailed'); - return; - } - onLoginSuccess({ - userName: response.user_name || 'User', - userId: response.user_id, - subscription: response.subscription || 'Active', - subscriptionValidUntil: response.subscription_valid_until ?? null, - }); - } else { - if (response.error_code === 'v2_auth_failed') { - error = $t('auth.v2AuthFailed'); - } else if (response.error_code === 'v2_not_initialized') { - error = $t('auth.v2NotInitialized'); - } else if (response.error_code === 'oauth_cancelled') { - error = null; // User closed the window, no error to show - } else { - error = response.error || 'Browser login failed'; - } - } + handleOAuthResponse(response); } catch (err) { console.error('OAuth login error:', err); error = formatErrorMessage(err); @@ -243,6 +247,31 @@ isOAuthLoading = false; } } + + async function handleSystemBrowserLogin() { + if (!get(qobuzTosAccepted)) { + error = $t('legal.tosRequiredToLogin'); + return; + } + + isSystemBrowserLoading = true; + error = null; + + try { + await setTosAcceptance(true); + } catch { /* continue */ } + + try { + const response = await invoke('v2_start_system_browser_oauth'); + console.log('[LoginView] v2_start_system_browser_oauth: success =', response.success); + handleOAuthResponse(response); + } catch (err) { + console.error('System browser OAuth error:', err); + error = formatErrorMessage(err); + } finally { + isSystemBrowserLoading = false; + } + }