mirror of
https://github.com/jeffvli/feishin.git
synced 2026-06-28 14:57:40 +02:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 062617bb40 | |||
| f8ca8861fc | |||
| 26eea7422d | |||
| 21d788226c | |||
| 9a1bf8f4a9 | |||
| 0fab3ba318 | |||
| 5ddbfcbfee | |||
| b6519e9839 | |||
| 2103c3b8c6 | |||
| 4a7f084b59 | |||
| 5ef4744c44 | |||
| ecda4ef8bc |
@@ -71,4 +71,5 @@ publish:
|
||||
provider: s3
|
||||
bucket: feishin-nightly
|
||||
channel: alpha
|
||||
region: auto
|
||||
endpoint: https://065f090c64de2dc707dd70ac72db9669.r2.cloudflarestorage.com
|
||||
|
||||
+2
-2
@@ -99,7 +99,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"dayjs": "^1.11.21",
|
||||
"dompurify": "^3.4.8",
|
||||
"dompurify": "^3.4.11",
|
||||
"electron-debug": "^3.2.0",
|
||||
"electron-localshortcut": "^3.2.1",
|
||||
"electron-log": "^5.4.4",
|
||||
@@ -162,7 +162,7 @@
|
||||
"concurrently": "^9.2.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"electron": "^41.7.1",
|
||||
"electron-builder": "^26.15.0",
|
||||
"electron-builder": "^26.15.5",
|
||||
"electron-devtools-installer": "^4.0.0",
|
||||
"electron-vite": "^4.0.1",
|
||||
"eslint": "^9.39.4",
|
||||
|
||||
Generated
+86
-376
@@ -94,8 +94,8 @@ importers:
|
||||
specifier: ^1.11.21
|
||||
version: 1.11.21
|
||||
dompurify:
|
||||
specifier: ^3.4.8
|
||||
version: 3.4.8
|
||||
specifier: ^3.4.11
|
||||
version: 3.4.11
|
||||
electron-debug:
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0
|
||||
@@ -278,8 +278,8 @@ importers:
|
||||
specifier: ^41.7.1
|
||||
version: 41.7.1
|
||||
electron-builder:
|
||||
specifier: ^26.15.0
|
||||
version: 26.15.0(electron-builder-squirrel-windows@26.15.0)
|
||||
specifier: ^26.15.5
|
||||
version: 26.15.5(electron-builder-squirrel-windows@26.15.5)
|
||||
electron-devtools-installer:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
@@ -1011,8 +1011,8 @@ packages:
|
||||
engines: {node: '>=12.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@electron/rebuild@4.0.3':
|
||||
resolution: {integrity: sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA==}
|
||||
'@electron/rebuild@4.0.4':
|
||||
resolution: {integrity: sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1546,14 +1546,6 @@ packages:
|
||||
resolution: {integrity: sha512-ugvXJjwF5ldtUpa7D95kruNJ41yFQDEKyF5CW4TgKJnh+W/zmlBzXXeKTyqIgwMFrkePN2JqOBqcF0M0oOunow==}
|
||||
engines: {node: '>=0.3.0'}
|
||||
|
||||
'@npmcli/agent@3.0.0':
|
||||
resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
'@npmcli/fs@4.0.0':
|
||||
resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
'@peculiar/asn1-schema@2.7.0':
|
||||
resolution: {integrity: sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg==}
|
||||
|
||||
@@ -2272,11 +2264,10 @@ packages:
|
||||
'@xmldom/xmldom@0.8.13':
|
||||
resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
deprecated: this version has critical issues, please update to the latest version
|
||||
|
||||
abbrev@3.0.1:
|
||||
resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
abbrev@4.0.0:
|
||||
resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
|
||||
abstract-socket@2.1.1:
|
||||
resolution: {integrity: sha512-YZJizsvS1aBua5Gd01woe4zuyYBGgSMeqDOB6/ChwdTI904KP6QGtJswXl4hcqWxbz86hQBe++HWV0hF1aGUtA==}
|
||||
@@ -2336,12 +2327,12 @@ packages:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
app-builder-lib@26.15.0:
|
||||
resolution: {integrity: sha512-j2+P6Lh+l/VuWfXZWSs7u+OAPqYJQGnZZO30M833XQQaRuyohm4RZk7Gw4nQXfeyQH9GqXaTwR16Y0LaVTlS+g==}
|
||||
app-builder-lib@26.15.5:
|
||||
resolution: {integrity: sha512-CJdzqy4YXQQdn+ivw1ssuY4yBTgVaBtniB2Dnjc6JsM9mbXoZ4shbuuysjenZloMOEIKEqkuRxltNQyG/NP/pA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
dmg-builder: 26.15.0
|
||||
electron-builder-squirrel-windows: 26.15.0
|
||||
dmg-builder: 26.15.5
|
||||
electron-builder-squirrel-windows: 26.15.5
|
||||
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
@@ -2486,9 +2477,6 @@ packages:
|
||||
bindings@1.5.0:
|
||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||
|
||||
bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
|
||||
bl@5.1.0:
|
||||
resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==}
|
||||
|
||||
@@ -2542,9 +2530,6 @@ packages:
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
|
||||
buffer@6.0.3:
|
||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||
|
||||
@@ -2552,8 +2537,8 @@ packages:
|
||||
resolution: {integrity: sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
builder-util@26.15.0:
|
||||
resolution: {integrity: sha512-dUx+HxVbiNsNQ4mGe1PyoC/tBmsHwBNDLdBuqWCj+rhHFE9lHgrXiGYKAM1uNlznhAaUSyMlms84VeSSr3gOBA==}
|
||||
builder-util@26.15.3:
|
||||
resolution: {integrity: sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
butterchurn-presets@3.0.0-beta.4:
|
||||
@@ -2570,10 +2555,6 @@ packages:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
cacache@19.0.1:
|
||||
resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
cacheable-lookup@5.0.4:
|
||||
resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==}
|
||||
engines: {node: '>=10.6.0'}
|
||||
@@ -2640,14 +2621,6 @@ packages:
|
||||
resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
cli-cursor@3.1.0:
|
||||
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
cli-spinners@2.9.2:
|
||||
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2655,10 +2628,6 @@ packages:
|
||||
clone-response@1.0.3:
|
||||
resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
|
||||
|
||||
clone@1.0.4:
|
||||
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
clone@2.1.2:
|
||||
resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
|
||||
engines: {node: '>=0.8'}
|
||||
@@ -2850,9 +2819,6 @@ packages:
|
||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
defaults@1.0.4:
|
||||
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
|
||||
|
||||
defer-to-connect@2.0.1:
|
||||
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -2873,10 +2839,6 @@ packages:
|
||||
resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
detect-newline@4.0.1:
|
||||
resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
@@ -2897,8 +2859,8 @@ packages:
|
||||
discord-api-types@0.38.48:
|
||||
resolution: {integrity: sha512-WFUE/2o0lBlLeCQonQ+Pu2RqHAqbytBJ2RlXR91gzk05InSS6k9ShzzLYoymrA4c2oRgRKGE7/VqQJNNdGWSxQ==}
|
||||
|
||||
dmg-builder@26.15.0:
|
||||
resolution: {integrity: sha512-oS8MWttbpIUF/2v8LOEY+f4ayL84ipMOarZvdRMl/pxlhLxAYjYMklTXHEXIl37Ig+qJv/bVF7HgyIoOoZyMWA==}
|
||||
dmg-builder@26.15.5:
|
||||
resolution: {integrity: sha512-Ts58Bs9QVCPhkhvkz9V1JwVoIwmbA06szZTM7W/ihzoDjHlf7KJo1Ci9nFknoFUC8uDeYgtbu5HW8eAeZ5qeSA==}
|
||||
|
||||
doctrine@2.1.0:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
@@ -2917,8 +2879,8 @@ packages:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
dompurify@3.4.8:
|
||||
resolution: {integrity: sha512-yb1cEmaOum7wFvOCSQxyfgVlv5D47Rc30iZWoMpbDIWTnJ6grDDQyu2KFJzB2k7u0pMuJcQ1zphH//fFnw2tjQ==}
|
||||
dompurify@3.4.11:
|
||||
resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==}
|
||||
|
||||
domutils@3.2.2:
|
||||
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
|
||||
@@ -2962,11 +2924,11 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
electron-builder-squirrel-windows@26.15.0:
|
||||
resolution: {integrity: sha512-WyiGTdAHOk8oncyg/Ik0+NsrPI6y7ftG/eB7uxr9iGjmF9RNuIdeW/BRpI7iLEe718WkBQCbspUAq/4Vi31wXg==}
|
||||
electron-builder-squirrel-windows@26.15.5:
|
||||
resolution: {integrity: sha512-+7D6F08V26p8dLLu2rK4MReQR50lA5W6hEgtNmjm6xbLrAGCJSWFbQEca6oNRKnFGlfHGS1TfgHLmOL3HX+6DA==}
|
||||
|
||||
electron-builder@26.15.0:
|
||||
resolution: {integrity: sha512-zd4cfvjHmtyGqMaDudg5rAjNUkwIJDz8ICaCsz77hFKcjMQHcZNNNCs/C4phwN9+gEVwmhvpKMzNFum6fs/n6A==}
|
||||
electron-builder@26.15.5:
|
||||
resolution: {integrity: sha512-ii+Befxc8diyoQv9iUchEzBAvFef4vrY/l2NID1wdZL2WCTLe80sYQz7Alc+yswWPpgowUdpsI5HtomE2Lj/Mg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -2989,8 +2951,8 @@ packages:
|
||||
resolution: {integrity: sha512-istWgaXjBfURBSS8LWVW9C3jsc6+ac+tY1lXrQEOTp0lVj+a4OlO1Tmqb36GgnEUDv92DGC9VI1HNXwJinWpgA==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
electron-publish@26.15.0:
|
||||
resolution: {integrity: sha512-pt6K3ol/a+o3HbqmYkL2NYlVH5pd34tL4FPRcgX8E88xQAqQyIsseXe4vWy7Pq2BaYy+iFGJrtInZe11FFAQwQ==}
|
||||
electron-publish@26.15.3:
|
||||
resolution: {integrity: sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==}
|
||||
|
||||
electron-store@8.2.0:
|
||||
resolution: {integrity: sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==}
|
||||
@@ -3030,9 +2992,6 @@ packages:
|
||||
encoding-sniffer@0.2.1:
|
||||
resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==}
|
||||
|
||||
encoding@0.1.13:
|
||||
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==}
|
||||
|
||||
@@ -3383,10 +3342,6 @@ packages:
|
||||
fs-merger@3.2.1:
|
||||
resolution: {integrity: sha512-AN6sX12liy0JE7C2evclwoo0aCG3PFulLjrTLsJpWh/2mM+DinhpSGqYLbHBBbIW1PLRNcFhJG8Axtz8mQW3ug==}
|
||||
|
||||
fs-minipass@3.0.3:
|
||||
resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
|
||||
fs-mkdirp-stream@2.0.1:
|
||||
resolution: {integrity: sha512-UTOY+59K6IA94tec8Wjqm0FSh5OVudGNB0NL/P6fB3HiE3bYOY3VYBGijsnOHNkQSwC1FKkU77pmq7xp9CskLw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@@ -3689,10 +3644,6 @@ packages:
|
||||
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
ip-address@10.2.0:
|
||||
resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
is-arguments@1.2.0:
|
||||
resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3758,10 +3709,6 @@ packages:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-interactive@1.0.0:
|
||||
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-map@2.0.3:
|
||||
resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -3833,10 +3780,6 @@ packages:
|
||||
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-unicode-supported@0.1.0:
|
||||
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
is-valid-glob@1.0.0:
|
||||
resolution: {integrity: sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3874,6 +3817,10 @@ packages:
|
||||
resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
isexe@4.0.0:
|
||||
resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
iterator.prototype@1.1.5:
|
||||
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4048,10 +3995,6 @@ packages:
|
||||
lodash@4.18.1:
|
||||
resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
|
||||
|
||||
log-symbols@4.1.0:
|
||||
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
long@4.0.0:
|
||||
resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==}
|
||||
|
||||
@@ -4086,10 +4029,6 @@ packages:
|
||||
resolution: {integrity: sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
make-fetch-happen@14.0.3:
|
||||
resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
map-stream@0.1.0:
|
||||
resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==}
|
||||
|
||||
@@ -4176,30 +4115,6 @@ packages:
|
||||
minimist@1.2.8:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
|
||||
minipass-collect@2.0.1:
|
||||
resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
||||
minipass-fetch@4.0.1:
|
||||
resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
minipass-flush@1.0.7:
|
||||
resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
minipass-pipeline@1.2.4:
|
||||
resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
minipass-sized@1.0.3:
|
||||
resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
minipass@3.3.6:
|
||||
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
minipass@7.1.3:
|
||||
resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
@@ -4260,10 +4175,6 @@ packages:
|
||||
resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
negotiator@1.0.0:
|
||||
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
node-abi@4.31.0:
|
||||
resolution: {integrity: sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==}
|
||||
engines: {node: '>=22.12.0'}
|
||||
@@ -4275,9 +4186,9 @@ packages:
|
||||
resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
node-gyp@11.5.0:
|
||||
resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
node-gyp@12.4.0:
|
||||
resolution: {integrity: sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
hasBin: true
|
||||
|
||||
node-int64@0.4.0:
|
||||
@@ -4291,9 +4202,9 @@ packages:
|
||||
resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
nopt@8.1.0:
|
||||
resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
nopt@9.0.0:
|
||||
resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
hasBin: true
|
||||
|
||||
normalize-path@3.0.0:
|
||||
@@ -4354,10 +4265,6 @@ packages:
|
||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
ora@5.4.1:
|
||||
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
overlayscrollbars-react@0.5.6:
|
||||
resolution: {integrity: sha512-E5To04bL5brn9GVCZ36SnfGanxa2I2MDkWoa4Cjo5wol7l+diAgi4DBc983V7l2nOk/OLJ6Feg4kySspQEGDBw==}
|
||||
peerDependencies:
|
||||
@@ -4391,10 +4298,6 @@ packages:
|
||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
p-map@7.0.4:
|
||||
resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
p-try@2.2.0:
|
||||
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -4584,9 +4487,9 @@ packages:
|
||||
resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
|
||||
engines: {node: ^14.13.1 || >=16.0.0}
|
||||
|
||||
proc-log@5.0.0:
|
||||
resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
proc-log@6.1.0:
|
||||
resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
|
||||
process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
@@ -4868,10 +4771,6 @@ packages:
|
||||
responselike@2.0.1:
|
||||
resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
|
||||
|
||||
restore-cursor@3.1.0:
|
||||
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
retry@0.12.0:
|
||||
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
|
||||
engines: {node: '>= 4'}
|
||||
@@ -5049,22 +4948,10 @@ packages:
|
||||
resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
smart-buffer@4.2.0:
|
||||
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||
|
||||
smob@1.6.2:
|
||||
resolution: {integrity: sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==}
|
||||
engines: {node: '>= 14'}
|
||||
|
||||
socks@2.8.9:
|
||||
resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==}
|
||||
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
||||
|
||||
sort-keys@5.1.0:
|
||||
resolution: {integrity: sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -5103,10 +4990,6 @@ packages:
|
||||
sprintf-js@1.1.3:
|
||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||
|
||||
ssri@12.0.0:
|
||||
resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
stat-mode@1.0.0:
|
||||
resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -5411,6 +5294,10 @@ packages:
|
||||
resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
undici@6.27.0:
|
||||
resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
undici@7.27.2:
|
||||
resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
@@ -5431,14 +5318,6 @@ packages:
|
||||
resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
unique-filename@4.0.0:
|
||||
resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
unique-slug@5.0.0:
|
||||
resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
|
||||
unique-string@2.0.0:
|
||||
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5595,9 +5474,6 @@ packages:
|
||||
wavesurfer.js@7.12.7:
|
||||
resolution: {integrity: sha512-TIe7hB6OCZysNOZ2cn2NR8Qpko22POWel6rauNcqOammFoH65NYQUM35unNLLMIlUMVYvjJ6w/TTl/G/m+w0nA==}
|
||||
|
||||
wcwidth@1.0.1:
|
||||
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
||||
|
||||
webcrypto-core@1.9.2:
|
||||
resolution: {integrity: sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==}
|
||||
|
||||
@@ -5646,6 +5522,11 @@ packages:
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
hasBin: true
|
||||
|
||||
which@6.0.1:
|
||||
resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
hasBin: true
|
||||
|
||||
word-wrap@1.2.5:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -6669,21 +6550,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@electron/rebuild@4.0.3':
|
||||
'@electron/rebuild@4.0.4':
|
||||
dependencies:
|
||||
'@malept/cross-spawn-promise': 2.0.0
|
||||
debug: 4.4.3
|
||||
detect-libc: 2.1.2
|
||||
got: 11.8.6
|
||||
graceful-fs: 4.2.11
|
||||
node-abi: 4.31.0
|
||||
node-api-version: 0.2.1
|
||||
node-gyp: 11.5.0
|
||||
ora: 5.4.1
|
||||
node-gyp: 12.4.0
|
||||
read-binary-file-arch: 1.0.6
|
||||
semver: 7.8.2
|
||||
tar: 7.5.16
|
||||
yargs: 17.7.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -7094,20 +6968,6 @@ snapshots:
|
||||
|
||||
'@nornagon/put@0.0.8': {}
|
||||
|
||||
'@npmcli/agent@3.0.0':
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
http-proxy-agent: 7.0.2
|
||||
https-proxy-agent: 7.0.6
|
||||
lru-cache: 10.4.3
|
||||
socks-proxy-agent: 8.0.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@npmcli/fs@4.0.0':
|
||||
dependencies:
|
||||
semver: 7.8.2
|
||||
|
||||
'@peculiar/asn1-schema@2.7.0':
|
||||
dependencies:
|
||||
'@peculiar/utils': 2.0.3
|
||||
@@ -7793,7 +7653,7 @@ snapshots:
|
||||
|
||||
'@xmldom/xmldom@0.8.13': {}
|
||||
|
||||
abbrev@3.0.1: {}
|
||||
abbrev@4.0.0: {}
|
||||
|
||||
abstract-socket@2.1.1:
|
||||
dependencies:
|
||||
@@ -7848,14 +7708,14 @@ snapshots:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.2
|
||||
|
||||
app-builder-lib@26.15.0(dmg-builder@26.15.0)(electron-builder-squirrel-windows@26.15.0):
|
||||
app-builder-lib@26.15.5(dmg-builder@26.15.5)(electron-builder-squirrel-windows@26.15.5):
|
||||
dependencies:
|
||||
'@electron/asar': 3.4.1
|
||||
'@electron/fuses': 1.8.0
|
||||
'@electron/get': 3.1.0
|
||||
'@electron/notarize': 2.5.0
|
||||
'@electron/osx-sign': 1.3.3
|
||||
'@electron/rebuild': 4.0.3
|
||||
'@electron/rebuild': 4.0.4
|
||||
'@electron/universal': 2.0.3
|
||||
'@malept/flatpak-bundler': 0.4.0
|
||||
'@noble/hashes': 2.2.0
|
||||
@@ -7864,17 +7724,17 @@ snapshots:
|
||||
ajv: 8.20.0
|
||||
asn1js: 3.0.10
|
||||
async-exit-hook: 2.0.1
|
||||
builder-util: 26.15.0
|
||||
builder-util: 26.15.3
|
||||
builder-util-runtime: 9.7.0
|
||||
chromium-pickle-js: 0.2.0
|
||||
ci-info: 4.3.1
|
||||
debug: 4.4.3
|
||||
dmg-builder: 26.15.0(electron-builder-squirrel-windows@26.15.0)
|
||||
dmg-builder: 26.15.5(electron-builder-squirrel-windows@26.15.5)
|
||||
dotenv: 16.6.1
|
||||
dotenv-expand: 11.0.7
|
||||
ejs: 3.1.10
|
||||
electron-builder-squirrel-windows: 26.15.0(dmg-builder@26.15.0)
|
||||
electron-publish: 26.15.0
|
||||
electron-builder-squirrel-windows: 26.15.5(dmg-builder@26.15.5)
|
||||
electron-publish: 26.15.3
|
||||
fs-extra: 10.1.0
|
||||
hosted-git-info: 4.1.0
|
||||
isbinaryfile: 5.0.7
|
||||
@@ -8052,12 +7912,6 @@ snapshots:
|
||||
file-uri-to-path: 1.0.0
|
||||
optional: true
|
||||
|
||||
bl@4.1.0:
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
|
||||
bl@5.1.0:
|
||||
dependencies:
|
||||
buffer: 6.0.3
|
||||
@@ -8124,11 +7978,6 @@ snapshots:
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
buffer@5.7.1:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
buffer@6.0.3:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
@@ -8141,7 +7990,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
builder-util@26.15.0:
|
||||
builder-util@26.15.3:
|
||||
dependencies:
|
||||
'@types/debug': 4.1.13
|
||||
builder-util-runtime: 9.7.0
|
||||
@@ -8174,21 +8023,6 @@ snapshots:
|
||||
|
||||
cac@6.7.14: {}
|
||||
|
||||
cacache@19.0.1:
|
||||
dependencies:
|
||||
'@npmcli/fs': 4.0.0
|
||||
fs-minipass: 3.0.3
|
||||
glob: 10.5.0
|
||||
lru-cache: 10.4.3
|
||||
minipass: 7.1.3
|
||||
minipass-collect: 2.0.1
|
||||
minipass-flush: 1.0.7
|
||||
minipass-pipeline: 1.2.4
|
||||
p-map: 7.0.4
|
||||
ssri: 12.0.0
|
||||
tar: 7.5.16
|
||||
unique-filename: 4.0.0
|
||||
|
||||
cacheable-lookup@5.0.4: {}
|
||||
|
||||
cacheable-request@7.0.4:
|
||||
@@ -8272,12 +8106,6 @@ snapshots:
|
||||
|
||||
ci-info@4.4.0: {}
|
||||
|
||||
cli-cursor@3.1.0:
|
||||
dependencies:
|
||||
restore-cursor: 3.1.0
|
||||
|
||||
cli-spinners@2.9.2: {}
|
||||
|
||||
cliui@8.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
@@ -8288,8 +8116,6 @@ snapshots:
|
||||
dependencies:
|
||||
mimic-response: 1.0.1
|
||||
|
||||
clone@1.0.4: {}
|
||||
|
||||
clone@2.1.2: {}
|
||||
|
||||
clsx@2.1.1: {}
|
||||
@@ -8480,10 +8306,6 @@ snapshots:
|
||||
|
||||
deepmerge@4.3.1: {}
|
||||
|
||||
defaults@1.0.4:
|
||||
dependencies:
|
||||
clone: 1.0.4
|
||||
|
||||
defer-to-connect@2.0.1: {}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
@@ -8502,8 +8324,6 @@ snapshots:
|
||||
|
||||
detect-indent@7.0.2: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
detect-newline@4.0.1: {}
|
||||
|
||||
detect-node-es@1.1.0: {}
|
||||
@@ -8522,10 +8342,10 @@ snapshots:
|
||||
|
||||
discord-api-types@0.38.48: {}
|
||||
|
||||
dmg-builder@26.15.0(electron-builder-squirrel-windows@26.15.0):
|
||||
dmg-builder@26.15.5(electron-builder-squirrel-windows@26.15.5):
|
||||
dependencies:
|
||||
app-builder-lib: 26.15.0(dmg-builder@26.15.0)(electron-builder-squirrel-windows@26.15.0)
|
||||
builder-util: 26.15.0
|
||||
app-builder-lib: 26.15.5(dmg-builder@26.15.5)(electron-builder-squirrel-windows@26.15.5)
|
||||
builder-util: 26.15.3
|
||||
fs-extra: 10.1.0
|
||||
js-yaml: 4.2.0
|
||||
transitivePeerDependencies:
|
||||
@@ -8553,7 +8373,7 @@ snapshots:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
dompurify@3.4.8:
|
||||
dompurify@3.4.11:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
|
||||
@@ -8597,23 +8417,23 @@ snapshots:
|
||||
dependencies:
|
||||
jake: 10.9.4
|
||||
|
||||
electron-builder-squirrel-windows@26.15.0(dmg-builder@26.15.0):
|
||||
electron-builder-squirrel-windows@26.15.5(dmg-builder@26.15.5):
|
||||
dependencies:
|
||||
app-builder-lib: 26.15.0(dmg-builder@26.15.0)(electron-builder-squirrel-windows@26.15.0)
|
||||
builder-util: 26.15.0
|
||||
app-builder-lib: 26.15.5(dmg-builder@26.15.5)(electron-builder-squirrel-windows@26.15.5)
|
||||
builder-util: 26.15.3
|
||||
electron-winstaller: 5.4.0
|
||||
transitivePeerDependencies:
|
||||
- dmg-builder
|
||||
- supports-color
|
||||
|
||||
electron-builder@26.15.0(electron-builder-squirrel-windows@26.15.0):
|
||||
electron-builder@26.15.5(electron-builder-squirrel-windows@26.15.5):
|
||||
dependencies:
|
||||
app-builder-lib: 26.15.0(dmg-builder@26.15.0)(electron-builder-squirrel-windows@26.15.0)
|
||||
builder-util: 26.15.0
|
||||
app-builder-lib: 26.15.5(dmg-builder@26.15.5)(electron-builder-squirrel-windows@26.15.5)
|
||||
builder-util: 26.15.3
|
||||
builder-util-runtime: 9.7.0
|
||||
chalk: 4.1.2
|
||||
ci-info: 4.4.0
|
||||
dmg-builder: 26.15.0(electron-builder-squirrel-windows@26.15.0)
|
||||
dmg-builder: 26.15.5(electron-builder-squirrel-windows@26.15.5)
|
||||
fs-extra: 10.1.0
|
||||
lazy-val: 1.0.5
|
||||
simple-update-notifier: 2.0.0
|
||||
@@ -8648,11 +8468,11 @@ snapshots:
|
||||
|
||||
electron-log@5.4.4: {}
|
||||
|
||||
electron-publish@26.15.0:
|
||||
electron-publish@26.15.3:
|
||||
dependencies:
|
||||
'@types/fs-extra': 9.0.13
|
||||
aws4: 1.13.2
|
||||
builder-util: 26.15.0
|
||||
builder-util: 26.15.3
|
||||
builder-util-runtime: 9.7.0
|
||||
chalk: 4.1.2
|
||||
form-data: 4.0.5
|
||||
@@ -8723,11 +8543,6 @@ snapshots:
|
||||
iconv-lite: 0.6.3
|
||||
whatwg-encoding: 3.1.1
|
||||
|
||||
encoding@0.1.13:
|
||||
dependencies:
|
||||
iconv-lite: 0.6.3
|
||||
optional: true
|
||||
|
||||
end-of-stream@1.4.5:
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
@@ -9245,10 +9060,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
fs-minipass@3.0.3:
|
||||
dependencies:
|
||||
minipass: 7.1.3
|
||||
|
||||
fs-mkdirp-stream@2.0.1:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
@@ -9609,8 +9420,6 @@ snapshots:
|
||||
hasown: 2.0.4
|
||||
side-channel: 1.1.0
|
||||
|
||||
ip-address@10.2.0: {}
|
||||
|
||||
is-arguments@1.2.0:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
@@ -9682,8 +9491,6 @@ snapshots:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
|
||||
is-interactive@1.0.0: {}
|
||||
|
||||
is-map@2.0.3: {}
|
||||
|
||||
is-module@1.0.0: {}
|
||||
@@ -9739,8 +9546,6 @@ snapshots:
|
||||
dependencies:
|
||||
which-typed-array: 1.1.22
|
||||
|
||||
is-unicode-supported@0.1.0: {}
|
||||
|
||||
is-valid-glob@1.0.0: {}
|
||||
|
||||
is-weakmap@2.0.2: {}
|
||||
@@ -9766,6 +9571,8 @@ snapshots:
|
||||
|
||||
isexe@3.1.5: {}
|
||||
|
||||
isexe@4.0.0: {}
|
||||
|
||||
iterator.prototype@1.1.5:
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
@@ -9924,11 +9731,6 @@ snapshots:
|
||||
|
||||
lodash@4.18.1: {}
|
||||
|
||||
log-symbols@4.1.0:
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
is-unicode-supported: 0.1.0
|
||||
|
||||
long@4.0.0: {}
|
||||
|
||||
loose-envify@1.4.0:
|
||||
@@ -9961,22 +9763,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
make-fetch-happen@14.0.3:
|
||||
dependencies:
|
||||
'@npmcli/agent': 3.0.0
|
||||
cacache: 19.0.1
|
||||
http-cache-semantics: 4.2.0
|
||||
minipass: 7.1.3
|
||||
minipass-fetch: 4.0.1
|
||||
minipass-flush: 1.0.7
|
||||
minipass-pipeline: 1.2.4
|
||||
negotiator: 1.0.0
|
||||
proc-log: 5.0.0
|
||||
promise-retry: 2.0.1
|
||||
ssri: 12.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
map-stream@0.1.0: {}
|
||||
|
||||
matcher-collection@2.0.1:
|
||||
@@ -10046,34 +9832,6 @@ snapshots:
|
||||
|
||||
minimist@1.2.8: {}
|
||||
|
||||
minipass-collect@2.0.1:
|
||||
dependencies:
|
||||
minipass: 7.1.3
|
||||
|
||||
minipass-fetch@4.0.1:
|
||||
dependencies:
|
||||
minipass: 7.1.3
|
||||
minipass-sized: 1.0.3
|
||||
minizlib: 3.1.0
|
||||
optionalDependencies:
|
||||
encoding: 0.1.13
|
||||
|
||||
minipass-flush@1.0.7:
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
|
||||
minipass-pipeline@1.2.4:
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
|
||||
minipass-sized@1.0.3:
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
|
||||
minipass@3.3.6:
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
|
||||
minipass@7.1.3: {}
|
||||
|
||||
minizlib@3.1.0:
|
||||
@@ -10119,8 +9877,6 @@ snapshots:
|
||||
|
||||
natural-orderby@5.0.0: {}
|
||||
|
||||
negotiator@1.0.0: {}
|
||||
|
||||
node-abi@4.31.0:
|
||||
dependencies:
|
||||
semver: 7.8.2
|
||||
@@ -10136,20 +9892,18 @@ snapshots:
|
||||
object.entries: 1.1.9
|
||||
semver: 6.3.1
|
||||
|
||||
node-gyp@11.5.0:
|
||||
node-gyp@12.4.0:
|
||||
dependencies:
|
||||
env-paths: 2.2.1
|
||||
exponential-backoff: 3.1.3
|
||||
graceful-fs: 4.2.11
|
||||
make-fetch-happen: 14.0.3
|
||||
nopt: 8.1.0
|
||||
proc-log: 5.0.0
|
||||
nopt: 9.0.0
|
||||
proc-log: 6.1.0
|
||||
semver: 7.8.2
|
||||
tar: 7.5.16
|
||||
tinyglobby: 0.2.17
|
||||
which: 5.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
undici: 6.27.0
|
||||
which: 6.0.1
|
||||
|
||||
node-int64@0.4.0: {}
|
||||
|
||||
@@ -10157,9 +9911,9 @@ snapshots:
|
||||
|
||||
node-releases@2.0.47: {}
|
||||
|
||||
nopt@8.1.0:
|
||||
nopt@9.0.0:
|
||||
dependencies:
|
||||
abbrev: 3.0.1
|
||||
abbrev: 4.0.0
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
@@ -10231,18 +9985,6 @@ snapshots:
|
||||
type-check: 0.4.0
|
||||
word-wrap: 1.2.5
|
||||
|
||||
ora@5.4.1:
|
||||
dependencies:
|
||||
bl: 4.1.0
|
||||
chalk: 4.1.2
|
||||
cli-cursor: 3.1.0
|
||||
cli-spinners: 2.9.2
|
||||
is-interactive: 1.0.0
|
||||
is-unicode-supported: 0.1.0
|
||||
log-symbols: 4.1.0
|
||||
strip-ansi: 6.0.1
|
||||
wcwidth: 1.0.1
|
||||
|
||||
overlayscrollbars-react@0.5.6(overlayscrollbars@2.16.0)(react@19.2.7):
|
||||
dependencies:
|
||||
overlayscrollbars: 2.16.0
|
||||
@@ -10274,8 +10016,6 @@ snapshots:
|
||||
dependencies:
|
||||
p-limit: 3.1.0
|
||||
|
||||
p-map@7.0.4: {}
|
||||
|
||||
p-try@2.2.0: {}
|
||||
|
||||
package-json-from-dist@1.0.1: {}
|
||||
@@ -10439,7 +10179,7 @@ snapshots:
|
||||
|
||||
pretty-bytes@6.1.1: {}
|
||||
|
||||
proc-log@5.0.0: {}
|
||||
proc-log@6.1.0: {}
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
|
||||
@@ -10725,11 +10465,6 @@ snapshots:
|
||||
dependencies:
|
||||
lowercase-keys: 2.0.0
|
||||
|
||||
restore-cursor@3.1.0:
|
||||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
|
||||
retry@0.12.0: {}
|
||||
|
||||
reusify@1.1.0: {}
|
||||
@@ -10936,23 +10671,8 @@ snapshots:
|
||||
astral-regex: 2.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
|
||||
smart-buffer@4.2.0: {}
|
||||
|
||||
smob@1.6.2: {}
|
||||
|
||||
socks-proxy-agent@8.0.5:
|
||||
dependencies:
|
||||
agent-base: 7.1.4
|
||||
debug: 4.4.3
|
||||
socks: 2.8.9
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
socks@2.8.9:
|
||||
dependencies:
|
||||
ip-address: 10.2.0
|
||||
smart-buffer: 4.2.0
|
||||
|
||||
sort-keys@5.1.0:
|
||||
dependencies:
|
||||
is-plain-obj: 4.1.0
|
||||
@@ -10990,10 +10710,6 @@ snapshots:
|
||||
|
||||
sprintf-js@1.1.3: {}
|
||||
|
||||
ssri@12.0.0:
|
||||
dependencies:
|
||||
minipass: 7.1.3
|
||||
|
||||
stat-mode@1.0.0: {}
|
||||
|
||||
stop-iteration-iterator@1.1.0:
|
||||
@@ -11422,6 +11138,8 @@ snapshots:
|
||||
|
||||
undici@6.24.1: {}
|
||||
|
||||
undici@6.27.0: {}
|
||||
|
||||
undici@7.27.2: {}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||
@@ -11435,14 +11153,6 @@ snapshots:
|
||||
|
||||
unicode-property-aliases-ecmascript@2.2.0: {}
|
||||
|
||||
unique-filename@4.0.0:
|
||||
dependencies:
|
||||
unique-slug: 5.0.0
|
||||
|
||||
unique-slug@5.0.0:
|
||||
dependencies:
|
||||
imurmurhash: 0.1.4
|
||||
|
||||
unique-string@2.0.0:
|
||||
dependencies:
|
||||
crypto-random-string: 2.0.0
|
||||
@@ -11606,10 +11316,6 @@ snapshots:
|
||||
|
||||
wavesurfer.js@7.12.7: {}
|
||||
|
||||
wcwidth@1.0.1:
|
||||
dependencies:
|
||||
defaults: 1.0.4
|
||||
|
||||
webcrypto-core@1.9.2:
|
||||
dependencies:
|
||||
'@peculiar/asn1-schema': 2.7.0
|
||||
@@ -11685,6 +11391,10 @@ snapshots:
|
||||
dependencies:
|
||||
isexe: 3.1.5
|
||||
|
||||
which@6.0.1:
|
||||
dependencies:
|
||||
isexe: 4.0.0
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
workbox-background-sync@7.3.0:
|
||||
|
||||
@@ -973,7 +973,43 @@
|
||||
"autoDJ_albumStrategy": "Mode de selecció d'àlbum",
|
||||
"autoDJ_songStrategy": "Mode de selecció de cançó",
|
||||
"autoDJ_strategy_option_library_random": "A l'atzar",
|
||||
"autoDJ_strategy_option_similar": "Similar"
|
||||
"autoDJ_strategy_option_similar": "Similar",
|
||||
"enableFurigana_description": "Mostra guies de pronunciació (furigana) per les lletres en japonès.",
|
||||
"enableFurigana": "Activa la generació de furigana",
|
||||
"equalizer_descriptionMpv": "Equalitzador paramètric amb FFmpeg lavfi (MPV)",
|
||||
"equalizer_descriptionWebAudio": "Equalitzador paramètric amb l'API de Web Audio",
|
||||
"equalizer": "Equalitzador",
|
||||
"equalizerBands_description": "Guany per banda. Arrossegueu-lo amunt o avall, o introduïu-hi un valor. Rang: -12 a +12 dB.",
|
||||
"equalizerBands": "Bandes",
|
||||
"equalizerPreamp_description": "Guany d'entrada previ a les bandes de l'equalitzador. Poseu-lo en negatiu quan realceu les bandes per evitar el clipping (MPV).",
|
||||
"equalizerPreamp": "Preamplificador",
|
||||
"equalizerPreset_description": "Aplica una corba d'equalitzador personalitzada integrada o desada",
|
||||
"equalizerPreset": "Preajustament",
|
||||
"equalizerPresetDeletePlaceholder": "Elimina la personalització...",
|
||||
"equalizerPresetGroupBuiltIn": "Integrat",
|
||||
"equalizerPresetGroupCustom": "Personalitzat",
|
||||
"equalizerPresetNamePlaceholder": "Nom de l'ajust predefinit...",
|
||||
"equalizerPresetSelectPlaceholder": "Seleccioneu un ajust predefinit",
|
||||
"equalizerSavePreset_description": "Desa la configuració actual de l'equalitzador com a ajust predefinit amb nom",
|
||||
"equalizerSavePreset": "Desa l'ajust",
|
||||
"compressor_descriptionMpv": "Compressor de rang dinàmic amb el compressor de FFmpeg (MPV)",
|
||||
"compressor_descriptionWebAudio": "Compressor de rang dinàmic amb l'API de Web Audio",
|
||||
"compressor": "Compressor",
|
||||
"compressorAttack_description": "La rapidesa amb què el compressor s'activa quan el senyal excedeix el llindar.",
|
||||
"compressorAttack": "Atac",
|
||||
"compressorKnee_description": "Amplada de la zona de resposta suau. Com més alt sigui, més gradual serà la transició cap a la compressió.",
|
||||
"compressorKnee": "Zona de resposta",
|
||||
"compressorMakeupGain_description": "Guany de sortida aplicat després de la compressió per recuperar volum.",
|
||||
"compressorMakeupGain": "Guany de compensació",
|
||||
"compressorPreset_description": "Aplica una configuració personalitzada del compressor integrada o desada",
|
||||
"compressorRatio_description": "Ràtio de compressio, p. ex. 4 = 4:1.",
|
||||
"compressorRatio": "Ràtio",
|
||||
"compressorRelease_description": "Com de ràpid el compressor es desactiva un cop el senyal sigui inferior al llindar.",
|
||||
"compressorRelease": "Desactivació",
|
||||
"compressorReset_description": "Restaura tots els paràmetres del compressor als seus valors per defecte",
|
||||
"compressorSavePreset_description": "Desa la configuració actual del compressor com un ajust predefinit amb nom",
|
||||
"compressorThreshold_description": "Nivell de senyal a partir del qual comença la compressió.",
|
||||
"compressorThreshold": "Llindar"
|
||||
},
|
||||
"table": {
|
||||
"column": {
|
||||
@@ -1266,7 +1302,9 @@
|
||||
"notContains": "No conté",
|
||||
"notInPlaylist": "No és a",
|
||||
"notInTheLast": "No és a l'últim",
|
||||
"startsWith": "Comença amb"
|
||||
"startsWith": "Comença amb",
|
||||
"isMissing": "Falta",
|
||||
"isPresent": "Està present"
|
||||
},
|
||||
"queryBuilder": {
|
||||
"standardTags": "Etiquetes estàndard",
|
||||
|
||||
@@ -457,7 +457,45 @@
|
||||
"autoDJ_albumStrategy": "Režim výběru alb",
|
||||
"autoDJ_songStrategy": "Režim výběru skladeb",
|
||||
"autoDJ_strategy_option_library_random": "Náhodně",
|
||||
"autoDJ_strategy_option_similar": "Podobné"
|
||||
"autoDJ_strategy_option_similar": "Podobné",
|
||||
"enableFurigana_description": "Zobrazit návody na výslovnost (furigana) u japonských kandži textů.",
|
||||
"enableFurigana": "Povolit generování furigana",
|
||||
"equalizer_descriptionMpv": "Parametrický ekvalizér skrze FFmpeg lavfi (MPV)",
|
||||
"equalizer_descriptionWebAudio": "Parametrický ekvalizér skrze Web Audio API",
|
||||
"equalizer": "Ekvalizér",
|
||||
"equalizerBands_description": "Zisk na pásmo. Posuňte nahoru/dolů nebo zadejte hodnotu. Rozsah: -12 do +12 dB.",
|
||||
"equalizerBands": "Pásma",
|
||||
"equalizerPreamp_description": "Vstupní zisk před pásmy ekvalizéru. Při zvýšení pásem nastavte na negativní hodnotu pro zabránění clippingu (MPV).",
|
||||
"equalizerPreamp": "Předzesilovač",
|
||||
"equalizerPreset_description": "Použít vestavěnou nebo uloženou vlastní křivku ekvalizéru",
|
||||
"equalizerPreset": "Předvolba",
|
||||
"equalizerPresetDeletePlaceholder": "Odstranit vlastní…",
|
||||
"equalizerPresetGroupBuiltIn": "Vestavěná",
|
||||
"equalizerPresetGroupCustom": "Vlastní",
|
||||
"equalizerPresetNamePlaceholder": "Název předvolby…",
|
||||
"equalizerPresetSelectPlaceholder": "Vybrat předvolbu",
|
||||
"equalizerSavePreset_description": "Uložit aktuální nastavení ekvalizéru jako pojmenovanou předvolbu",
|
||||
"equalizerSavePreset": "Uložit předvolbu",
|
||||
"compressor_descriptionMpv": "Kompresor dynamického rozsahu skrze FFmpeg acompressor (MPV)",
|
||||
"compressor_descriptionWebAudio": "Kompresor dynamického rozsahu skrze Web Audio API",
|
||||
"compressor": "Kompresor",
|
||||
"compressorAttack_description": "Jak rychle se kompresor spustí, když signál překročí hranici.",
|
||||
"compressorAttack": "Útok",
|
||||
"compressorKnee_description": "Měkká šířka. Čím vyšší jsou hodnoty, tím pozvolnější je přechod do komprese.",
|
||||
"compressorKnee": "Koleno",
|
||||
"compressorMakeupGain_description": "Výstupní zesílení aplikované po kompresi za účelem obnovení hlasitosti.",
|
||||
"compressorMakeupGain": "Následný zisk",
|
||||
"compressorPreset_description": "Použít vestavěné nebo uložené vlastní nastavení kompresoru",
|
||||
"compressorRatio_description": "Poměr komprese, např. 4 = 4:1.",
|
||||
"compressorRatio": "Poměr",
|
||||
"compressorRelease_description": "Jak rychle se kompresor uvolní, když signál spadne pod nastavenou hranici.",
|
||||
"compressorRelease": "Uvolnění",
|
||||
"compressorReset_description": "Obnovit všechny parametry kompresoru na jejich výchozí hodnoty",
|
||||
"compressorSavePreset_description": "Uložit aktuální nastavení kompresoru jako pojmenovanou předvolbu",
|
||||
"compressorThreshold_description": "Úroveň signálu, nad kterou začne komprese.",
|
||||
"compressorThreshold": "Hranice",
|
||||
"enableRomaji_description": "Zobrazit rómadži výslovnost pod japonskými texty.",
|
||||
"enableRomaji": "Povolit generování rómadži"
|
||||
},
|
||||
"action": {
|
||||
"editPlaylist": "Upravit $t(entity.playlist, {\"count\": 1})",
|
||||
|
||||
@@ -845,6 +845,24 @@
|
||||
"enableAutoTranslation": "Enable auto translation",
|
||||
"enableFurigana_description": "Display pronunciation guides (furigana) over Japanese kanji lyrics.",
|
||||
"enableFurigana": "Enable furigana generation",
|
||||
"enableRomaji_description": "Display a romaji pronunciation line under Japanese lyrics.",
|
||||
"enableRomaji": "Enable romaji generation",
|
||||
"equalizer_descriptionMpv": "Parametric equalizer via FFmpeg lavfi (MPV)",
|
||||
"equalizer_descriptionWebAudio": "Parametric equalizer via Web Audio API",
|
||||
"equalizer": "Equalizer",
|
||||
"equalizerBands_description": "Per-band gain. Drag up/down or type a value. Range: -12 to +12 dB.",
|
||||
"equalizerBands": "Bands",
|
||||
"equalizerPreamp_description": "Input gain before EQ bands. Set negative when boosting bands to prevent clipping (MPV).",
|
||||
"equalizerPreamp": "Preamp",
|
||||
"equalizerPreset_description": "Apply a built-in or saved custom EQ curve",
|
||||
"equalizerPreset": "Preset",
|
||||
"equalizerPresetDeletePlaceholder": "Delete custom...",
|
||||
"equalizerPresetGroupBuiltIn": "Built-in",
|
||||
"equalizerPresetGroupCustom": "Custom",
|
||||
"equalizerPresetNamePlaceholder": "Preset name...",
|
||||
"equalizerPresetSelectPlaceholder": "Select preset",
|
||||
"equalizerSavePreset_description": "Save the current EQ settings as a named preset",
|
||||
"equalizerSavePreset": "Save preset",
|
||||
"enableRemote_description": "Enables the remote control server to allow other devices to control the application",
|
||||
"enableRemote": "Enable remote control server",
|
||||
"exitToTray_description": "Exit the application to the system tray",
|
||||
@@ -1026,6 +1044,24 @@
|
||||
"showVisualizerInSidebar": "Show visualizer in player sidebar",
|
||||
"combinedLyricsAndVisualizer_description": "Combine lyrics and visualizer into the same panel",
|
||||
"combinedLyricsAndVisualizer": "Combine lyrics and visualizer in player sidebar",
|
||||
"compressor_descriptionMpv": "Dynamic range compressor via FFmpeg acompressor (MPV)",
|
||||
"compressor_descriptionWebAudio": "Dynamic range compressor via Web Audio API",
|
||||
"compressor": "Compressor",
|
||||
"compressorAttack_description": "How quickly the compressor engages after the signal exceeds the threshold.",
|
||||
"compressorAttack": "Attack",
|
||||
"compressorKnee_description": "Soft-knee width. Higher values make the transition into compression more gradual.",
|
||||
"compressorKnee": "Knee",
|
||||
"compressorMakeupGain_description": "Output gain applied after compression to restore loudness.",
|
||||
"compressorMakeupGain": "Makeup Gain",
|
||||
"compressorPreset_description": "Apply a built-in or saved custom compressor setting",
|
||||
"compressorRatio_description": "Compression ratio, e.g. 4 = 4:1.",
|
||||
"compressorRatio": "Ratio",
|
||||
"compressorRelease_description": "How quickly the compressor releases after the signal drops below the threshold.",
|
||||
"compressorRelease": "Release",
|
||||
"compressorReset_description": "Restore all compressor parameters to their default values",
|
||||
"compressorSavePreset_description": "Save the current compressor settings as a named preset",
|
||||
"compressorThreshold_description": "Signal level above which compression begins.",
|
||||
"compressorThreshold": "Threshold",
|
||||
"preservePitch_description": "Preserves pitch when modifying playback speed",
|
||||
"preservePitch": "Preserve pitch",
|
||||
"audioFadeOnStatusChange": "Audio fade on status change",
|
||||
|
||||
@@ -457,7 +457,45 @@
|
||||
"autoDJ_songStrategy": "Modo de selección de canción",
|
||||
"autoDJ_strategy_option_library_random": "Aleatorio",
|
||||
"autoDJ_strategy_option_similar": "Similar",
|
||||
"autoDJ_mode_description": "Elegir para añadir canciones o álbumes enteros a la cola"
|
||||
"autoDJ_mode_description": "Elegir para añadir canciones o álbumes enteros a la cola",
|
||||
"enableFurigana_description": "Mostrar guías de pronunciación (furigana) sobre letras kanji japonesas.",
|
||||
"enableFurigana": "Activar generación de furigana",
|
||||
"equalizer_descriptionMpv": "Ecualizador paramétrico a través de FFmpeg lavfi (MPV)",
|
||||
"equalizer_descriptionWebAudio": "Ecualizador paramétrico a través de la API de Web Audio",
|
||||
"equalizer": "Ecualizador",
|
||||
"equalizerBands": "Bandas",
|
||||
"equalizerBands_description": "Ganancia por banda. Arrastrar arriba/abajo o escribir un valor. Rango: -12 a +12 dB.",
|
||||
"equalizerPreamp_description": "Ganancia de entrada antes de las bandas de ecualización. Ajústala en negativo al realzar las bandas para evitar el clipping (MPV).",
|
||||
"equalizerPreamp": "Preamplificador",
|
||||
"equalizerPreset_description": "Aplica una curva de ecualizador personalizada integrada o guardada",
|
||||
"equalizerPreset": "Preajuste",
|
||||
"equalizerPresetDeletePlaceholder": "Elimianr personalizado...",
|
||||
"equalizerPresetGroupBuiltIn": "Integrado",
|
||||
"equalizerPresetGroupCustom": "Personalizado",
|
||||
"equalizerPresetNamePlaceholder": "Nombre del preajuste...",
|
||||
"equalizerPresetSelectPlaceholder": "Seleccionar preajuste",
|
||||
"equalizerSavePreset": "Guardar preajuste",
|
||||
"equalizerSavePreset_description": "Guarda la configuración del ecualizador actual como un preajuste nombrado",
|
||||
"compressor_descriptionMpv": "Compresor de rango dinámico a través del compresor de FFmpeg (MPV)",
|
||||
"compressor_descriptionWebAudio": "Compresor de rango dinámico a través de la API de Web Audio",
|
||||
"compressor": "Compresor",
|
||||
"compressorThreshold": "Umbral",
|
||||
"compressorThreshold_description": "Nivel de señal a partir del cual comienza la compresión.",
|
||||
"compressorSavePreset_description": "Guarda la configuración actual del compresor como un preajuste nombrado",
|
||||
"compressorReset_description": "Restaura todos los parámetros del compresor a sus valores predeterminados",
|
||||
"compressorRelease": "Liberación",
|
||||
"compressorRelease_description": "La rapidez con la que el compresor se libera una vez que la señal desciende por debajo del umbral.",
|
||||
"compressorRatio": "Ratio",
|
||||
"compressorRatio_description": "Ratio de compresión, p. ej. 4 = 4:1.",
|
||||
"compressorPreset_description": "Aplica una configuración personalizada del compresor integrada o guardada",
|
||||
"compressorKnee_description": "Ancho de la zona de respuesta suave. Cuanto mayor sea el valor, más gradual será la transición hacia la compresión.",
|
||||
"compressorKnee": "Zona de respuesta",
|
||||
"compressorMakeupGain_description": "Ganancia de salida aplicada tras la compresión para recuperar el volumen.",
|
||||
"compressorMakeupGain": "Ganancia de compensación",
|
||||
"compressorAttack_description": "La rapidez con la que el compresor entra en acción una vez que la señal supera el umbral.",
|
||||
"compressorAttack": "Ataque",
|
||||
"enableRomaji_description": "Muestra una línea de pronunciación en romaji debajo de las letras japonesas.",
|
||||
"enableRomaji": "Activar generación de romaji"
|
||||
},
|
||||
"action": {
|
||||
"editPlaylist": "Editar $t(entity.playlist, {\"count\": 1})",
|
||||
|
||||
@@ -1135,7 +1135,43 @@
|
||||
"queryBuilderCustomFields_inputLabel": "Nimetus",
|
||||
"queryBuilderCustomFields_inputTag": "Silt",
|
||||
"queryBuilderCustomFields": "Kohandatud väljad",
|
||||
"queryBuilderCustomFields_description": "Lisa kohandatud välju, mida päringukoosturis kasutada"
|
||||
"queryBuilderCustomFields_description": "Lisa kohandatud välju, mida päringukoosturis kasutada",
|
||||
"equalizer_descriptionMpv": "Parametriline ekvalaiser FFmpeg lavfi (MPV) kaudu",
|
||||
"equalizer_descriptionWebAudio": "Parametriline ekvalaiser Web Audio API kaudu",
|
||||
"equalizer": "Ekvalaiser",
|
||||
"equalizerBands_description": "Riba põhivõimendus. Lohista üles/alla või sisesta väärtus. Vahemik: -12 kuni +12 dB.",
|
||||
"equalizerBands": "Ribad",
|
||||
"equalizerPreamp_description": "Sisendvõimendus enne ekvalaiseri ribasid. Moonutuste vältimiseks määra ribade võimendamisel negatiivne väärtus (MPV).",
|
||||
"equalizerPreamp": "Eelvõimendus",
|
||||
"equalizerPreset_description": "Rakenda sisseehitatud või salvestatud kohandatud EQ-häälestus",
|
||||
"equalizerPreset": "Eelseadistus",
|
||||
"equalizerPresetDeletePlaceholder": "Kustuta kohandatud...",
|
||||
"equalizerPresetGroupBuiltIn": "Sisseehitatud",
|
||||
"equalizerPresetGroupCustom": "Kohandatud",
|
||||
"equalizerPresetNamePlaceholder": "Eelseadistuse nimi...",
|
||||
"equalizerPresetSelectPlaceholder": "Vali eelseadistus",
|
||||
"equalizerSavePreset_description": "Salvesta praegused EQ-seaded nimetatud eelseadistusena",
|
||||
"equalizerSavePreset": "Salvesta eelseadistus",
|
||||
"compressor_descriptionMpv": "Dünaamilise vahemiku kompressor FFmpeg acompressori kaudu (MPV)",
|
||||
"compressor_descriptionWebAudio": "Dünaamilise vahemiku kompressor Web Audio API kaudu",
|
||||
"enableFurigana_description": "Kuva jaapani kanji-märkide kohal hääldusjuhiseid (furigana).",
|
||||
"enableFurigana": "Luba furigana kuvamine",
|
||||
"compressor": "Kompressor",
|
||||
"compressorAttack_description": "Kui kiiresti kompressor pärast läve ületamist rakendub.",
|
||||
"compressorAttack": "Rakendumisaeg",
|
||||
"compressorKnee_description": "Sujuva ülemineku (soft-knee) ulatus. Suuremad väärtused muudavad kompressiooni rakendumise astmelisemaks.",
|
||||
"compressorKnee": "Üleminek",
|
||||
"compressorMakeupGain_description": "Väljundvõimendus helitugevuse taastamiseks pärast kompressiooni.",
|
||||
"compressorMakeupGain": "Väljundvõimendus",
|
||||
"compressorPreset_description": "Rakenda sisseehitatud või salvestatud kohandatud kompressoriseadistus",
|
||||
"compressorRatio_description": "Kompressiooniaste, nt 4 = 4:1.",
|
||||
"compressorRatio": "Kompressiooniaste",
|
||||
"compressorRelease_description": "Kui kiiresti kompressiooni mõju pärast lävest allapoole langemist lakkab.",
|
||||
"compressorRelease": "Vabastusaeg",
|
||||
"compressorReset_description": "Taasta kõigi kompressori parameetrite vaikeväärtused",
|
||||
"compressorSavePreset_description": "Salvesta praegused kompressori seaded nimetatud eelseadistusena",
|
||||
"compressorThreshold_description": "Signaali tase, mida ületades kompressioon algab.",
|
||||
"compressorThreshold": "Lävi"
|
||||
},
|
||||
"datetime": {
|
||||
"minuteShort": "m",
|
||||
|
||||
@@ -1108,7 +1108,43 @@
|
||||
"autoDJ_albumStrategy": "Tryb wyboru albumów",
|
||||
"autoDJ_songStrategy": "Tryb wyboru piosenek",
|
||||
"autoDJ_strategy_option_library_random": "Losowo",
|
||||
"autoDJ_strategy_option_similar": "Podobne"
|
||||
"autoDJ_strategy_option_similar": "Podobne",
|
||||
"enableFurigana_description": "Wyświetlaj pomoce wymowy (furigana) nad tekstami Japońskimi kanji.",
|
||||
"enableFurigana": "Włącz generowanie furigana",
|
||||
"equalizer_descriptionMpv": "Equalizer parametryczny przez FFmpeg lavfi (MPV)",
|
||||
"equalizer_descriptionWebAudio": "Parametryczny equalizer przez API Web Audio",
|
||||
"equalizer": "Equalizer",
|
||||
"equalizerBands_description": "Wzmocnienie dla poszczególnych pasm. Przesuń w górę/dół lub wpisz wartość. Zakres: -12 do +12 dB.",
|
||||
"equalizerBands": "Pasma",
|
||||
"equalizerPreamp_description": "Wzmocnienie sygnału przed pasmami EQ. Ustaw na wartość ujemną podczas wzmacniania pasm, aby zapobiec przesterowaniu (MPV).",
|
||||
"equalizerPreamp": "Przedwzmacnianie",
|
||||
"equalizerPreset_description": "Zastosuj wbudowaną lub niestandardową zapisaną krzywą EQ",
|
||||
"equalizerPreset": "Ustawienia wstępne",
|
||||
"equalizerPresetDeletePlaceholder": "Usuń niestandardowe...",
|
||||
"equalizerPresetGroupBuiltIn": "Wbudowane",
|
||||
"equalizerPresetGroupCustom": "Niestandardowe",
|
||||
"equalizerPresetNamePlaceholder": "Nazwa ustawień wstępnych...",
|
||||
"equalizerPresetSelectPlaceholder": "Wybierz ustawienia wstępne",
|
||||
"equalizerSavePreset_description": "Zapisz aktualne ustawienia EQ jako nazwany zestaw ustawień wstępnych",
|
||||
"equalizerSavePreset": "Zapisz ustawienia wstępne",
|
||||
"compressor_descriptionMpv": "Kompresor zakresu dynamicznego przez FFmpeg acompressor (MPV)",
|
||||
"compressor_descriptionWebAudio": "Kompresor zakresu dynamicznego poprzez API Web Audio",
|
||||
"compressor": "Kompresor",
|
||||
"compressorAttack_description": "Jak szybko załączany jest kompresor po przekroczeniu progu przez sygnał.",
|
||||
"compressorAttack": "Attack",
|
||||
"compressorKnee_description": "Szerokośc soft-knee. Większe wartości powodują przejście do kompresji bardziej stopniowym.",
|
||||
"compressorKnee": "Knee",
|
||||
"compressorMakeupGain_description": "Zwiększenie wyjściowe dodawane po kompresji aby, przywrócić głośność.",
|
||||
"compressorMakeupGain": "Makeup Gain",
|
||||
"compressorPreset_description": "Zastosuj wbudowane lub niestandardowe zapisane ustawienie kompresora",
|
||||
"compressorRatio_description": "Proporcje kompresji, np. 4 = 4:1.",
|
||||
"compressorRatio": "Proporcje",
|
||||
"compressorRelease_description": "Jak szybko kompresor odpuszcza po spadnięciu sygnału poniżej progu.",
|
||||
"compressorRelease": "Odpuszczenie",
|
||||
"compressorReset_description": "Przywróć wszystkie parametry kompresora do wartości domyślnych",
|
||||
"compressorSavePreset_description": "Zapisz aktualne ustawienia kompresora jako nazwany zestaw ustawień wstępnych",
|
||||
"compressorThreshold_description": "Poziom sygnału nad którym rozpoczyna się kompresja.",
|
||||
"compressorThreshold": "Próg"
|
||||
},
|
||||
"table": {
|
||||
"config": {
|
||||
|
||||
+186
-8
@@ -40,7 +40,10 @@
|
||||
"listenbrainz": "Відкрити у ListenBrainz",
|
||||
"qobuz": "Відкрити у Qobuz",
|
||||
"spotify": "Відкрити у Spotify"
|
||||
}
|
||||
},
|
||||
"goToCurrent": "Перейти до поточного елементу",
|
||||
"collapseAllFolders": "Згорнути всі папки",
|
||||
"expandAllFolders": "Розгорнути всі папки"
|
||||
},
|
||||
"common": {
|
||||
"countSelected": "Вибрано {{count}}",
|
||||
@@ -170,7 +173,8 @@
|
||||
"itemsMore": "{{count}} більше",
|
||||
"numberOfResults": "{{numberOfResults}} результатів",
|
||||
"newVersionAvailable": "Доступна нова версія",
|
||||
"back": "Повернутися"
|
||||
"back": "Повернутися",
|
||||
"openFolder": "Відкрити папку"
|
||||
},
|
||||
"entity": {
|
||||
"album_one": "Альбом",
|
||||
@@ -338,7 +342,9 @@
|
||||
"notContains": "Не містить",
|
||||
"notInPlaylist": "Немає в",
|
||||
"notInTheLast": "Не є в останньому",
|
||||
"startsWith": "Починається з"
|
||||
"startsWith": "Починається з",
|
||||
"isMissing": "Загублений",
|
||||
"isPresent": "Присутній"
|
||||
},
|
||||
"form": {
|
||||
"addServer": {
|
||||
@@ -369,7 +375,8 @@
|
||||
"input_skipDuplicates": "Пропустити дублікати",
|
||||
"searchOrCreate": "Шукайте $t(entity.playlist, {\"count\": 2}) або пишіть, щоб створити новий",
|
||||
"success": "Додано $t(entity.trackWithCount, {\"count\": {{message}} }) до $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
|
||||
"title": "Додати до $t(entity.playlist, {\"count\": 1})"
|
||||
"title": "Додати до $t(entity.playlist, {\"count\": 1})",
|
||||
"noneAdded": "Ніяких треків не було додано до $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
|
||||
},
|
||||
"createPlaylist": {
|
||||
"input_description": "$t(common.description)",
|
||||
@@ -437,7 +444,12 @@
|
||||
"input_played": "Відтворити фільтр",
|
||||
"input_played_optionAll": "Всі треки",
|
||||
"input_played_optionUnplayed": "Тільки не відтворені треки",
|
||||
"input_played_optionPlayed": "Тільки відтворені треки"
|
||||
"input_played_optionPlayed": "Тільки відтворені треки",
|
||||
"input_kind_albums": "Альбоми",
|
||||
"input_kind_songs": "Треки",
|
||||
"input_kind": "Випадкові вибори",
|
||||
"input_limit_albums": "Скільки альбомів?",
|
||||
"input_limit_songs": "Скільки треків?"
|
||||
},
|
||||
"updateServer": {
|
||||
"success": "Сервер успішно оновлено",
|
||||
@@ -453,7 +465,57 @@
|
||||
}
|
||||
},
|
||||
"player": {
|
||||
"skip": "Пропустити"
|
||||
"skip": "Пропустити",
|
||||
"repeat": "Повторювати",
|
||||
"repeat_all": "Повторювати всі",
|
||||
"repeat_off": "Повторювання вимкнено",
|
||||
"restoreQueueFromServer": "Відновити чергу з серверу",
|
||||
"saveQueueToServer": "Зберегти чергу до серверу",
|
||||
"shuffle": "Грати (перемішано)",
|
||||
"shuffle_off": "Перемішування вимкнено",
|
||||
"addLast": "Останній",
|
||||
"addNext": "Наступним",
|
||||
"addLastShuffled": "Останнім (перемішано)",
|
||||
"addNextShuffled": "Наступним (перемішано)",
|
||||
"albumRadio": "Радіо альбому",
|
||||
"artistRadio": "Радіо артиста",
|
||||
"holdToShuffle": "Утримуйте щоб перемішати",
|
||||
"favorite": "Додати до вибраних",
|
||||
"lyrics": "Тексти пісень",
|
||||
"mute": "Вимкнути звук",
|
||||
"muted": "Звук вимкнено",
|
||||
"next": "Наступний",
|
||||
"play": "Грати",
|
||||
"playbackFetchCancel": "Будь ласка, трошки почекайте... закрийте повідомлення, щоб скасувати",
|
||||
"playbackFetchInProgress": "Завантаження треків…",
|
||||
"playbackFetchNoResults": "Треків не знайдено",
|
||||
"playbackSpeed": "Швидкість відтворення",
|
||||
"playRandom": "Грати випадково",
|
||||
"playSimilarSongs": "Грати схожі треки",
|
||||
"previous": "Попередній",
|
||||
"queue_clear": "Очистити чергу",
|
||||
"queue_moveToBottom": "Пересунути виділені вниз",
|
||||
"queue_moveToTop": "Пересунути виділені угору",
|
||||
"queue_remove": "Видалити виділені",
|
||||
"skip_back": "Перемотати назад",
|
||||
"skip_forward": "Перемотати вперед",
|
||||
"stop": "Зупинити",
|
||||
"toggleFullscreenPlayer": "Перемкнути повноекранний плеєр",
|
||||
"trackRadio": "Радіо треку",
|
||||
"unfavorite": "Прибрати з вибраних",
|
||||
"pause": "Пауза",
|
||||
"viewQueue": "Переглянути чергу",
|
||||
"sleepTimer": "Таймер сну",
|
||||
"sleepTimer_endOfSong": "Кінець поточного треку",
|
||||
"sleepTimer_endOfAlbum": "Кінець поточного альбому",
|
||||
"sleepTimer_minutes": "{{count}} хв",
|
||||
"sleepTimer_hours": "{{count}} г",
|
||||
"sleepTimer_custom": "Користувацький",
|
||||
"sleepTimer_off": "Вимкнено",
|
||||
"sleepTimer_timeRemaining": "{{time}} залишилось",
|
||||
"sleepTimer_setCustom": "Встановити таймер",
|
||||
"sleepTimer_cancel": "Скасувати таймер",
|
||||
"scrobbleForceSubmit": "Змусити скробблинути"
|
||||
},
|
||||
"page": {
|
||||
"albumArtistDetail": {
|
||||
@@ -570,7 +632,9 @@
|
||||
"showLyricProvider": "Показувати джерело тексту пісень",
|
||||
"synchronized": "Синхронізовано",
|
||||
"unsynchronized": "Несинхронізовано",
|
||||
"useImageAspectRatio": "Використовувати співвідношення сторін зображення"
|
||||
"useImageAspectRatio": "Використовувати співвідношення сторін зображення",
|
||||
"lyricOpacityNonActive": "Непрозорість неактивних слів",
|
||||
"lyricScaleNonActive": "Масштаб неактивних слів"
|
||||
},
|
||||
"lyrics": "Текст пісні",
|
||||
"related": "Пов'язані",
|
||||
@@ -599,7 +663,121 @@
|
||||
"genres": "$t(entity.genre, {\"count\": 2})",
|
||||
"mostPlayed": "Найбільш відтворені",
|
||||
"newlyAdded": "Нещодавно додані релізи",
|
||||
"recentlyPlayed": "Нещодавно відтворені"
|
||||
"recentlyPlayed": "Нещодавно відтворені",
|
||||
"recentlyReleased": "Нещодавно випущені",
|
||||
"title": "$t(common.home)"
|
||||
},
|
||||
"itemDetail": {
|
||||
"copyPath": "Скопіювати шлях до буфера обміну",
|
||||
"copiedPath": "Шлях успішно скопійовано",
|
||||
"openFile": "Показати трек у файловому менеджері"
|
||||
},
|
||||
"setting": {
|
||||
"sidebar": "Бічна панель",
|
||||
"remote": "Віддалений",
|
||||
"exportImport": "Імпортувати/експортувати",
|
||||
"scrobble": "Скробблінг",
|
||||
"audio": "Аудіо",
|
||||
"lyrics": "Тексти пісень",
|
||||
"lyricsDisplay": "Відображення текстів пісень",
|
||||
"transcoding": "Транскодування",
|
||||
"discord": "Діскорд",
|
||||
"logger": "Логгер",
|
||||
"playerFilters": "Фільтри плеєра",
|
||||
"advanced": "Розширені",
|
||||
"analytics": "Аналітика",
|
||||
"generalTab": "Загальні",
|
||||
"hotkeysTab": "Гарячі клавіші",
|
||||
"playbackTab": "Відтворення",
|
||||
"windowTab": "Вікно",
|
||||
"updates": "Оновлення",
|
||||
"cache": "Кеш",
|
||||
"application": "Застосунок",
|
||||
"queryBuilder": "Конструктор черги",
|
||||
"theme": "Тема",
|
||||
"controls": "Керування"
|
||||
},
|
||||
"sidebar": {
|
||||
"albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
|
||||
"albums": "$t(entity.album, {\"count\": 2})",
|
||||
"collections": "Колекції",
|
||||
"artists": "$t(entity.artist, {\"count\": 2})",
|
||||
"favorites": "$t(entity.favorite, {\"count\": 2})",
|
||||
"folders": "$t(entity.folder, {\"count\": 2})",
|
||||
"genres": "$t(entity.genre, {\"count\": 2})",
|
||||
"home": "$t(common.home)",
|
||||
"radio": "$t(entity.radioStation, {\"count\": 2})",
|
||||
"myLibrary": "Моя бібліотека",
|
||||
"nowPlaying": "Зараз грає",
|
||||
"playlists": "$t(entity.playlist, {\"count\": 2})",
|
||||
"search": "$t(common.search)",
|
||||
"settings": "$t(common.setting, {\"count\": 2})",
|
||||
"shared": "Поширено $t(entity.playlist, {\"count\": 2})",
|
||||
"tracks": "$t(entity.track, {\"count\": 2})"
|
||||
},
|
||||
"trackList": {
|
||||
"artistTracks": "Треки {{artist}}",
|
||||
"genreTracks": "\"{{genre}}\" $t(entity.track, {\"count\": 2})",
|
||||
"title": "$t(entity.track, {\"count\": 2})"
|
||||
},
|
||||
"playlistList": {
|
||||
"title": "$t(entity.playlist, {\"count\": 2})"
|
||||
},
|
||||
"collections": {
|
||||
"overrideExisting": "Перевизначити існуючі",
|
||||
"saveAsCollection": "Зберегти як колекцію"
|
||||
}
|
||||
},
|
||||
"queryBuilder": {
|
||||
"standardTags": "Стандартні теги",
|
||||
"customTags": "Користувацькі теги"
|
||||
},
|
||||
"releaseType": {
|
||||
"primary": {
|
||||
"album": "$t(entity.album, {\"count\": 1})",
|
||||
"broadcast": "Транслювати",
|
||||
"ep": "Міні-альбом",
|
||||
"other": "Інші",
|
||||
"single": "Сінгл"
|
||||
},
|
||||
"secondary": {
|
||||
"audiobook": "Аудіокнига",
|
||||
"audioDrama": "Радіоп'єса",
|
||||
"compilation": "Збірка",
|
||||
"djMix": "DJ мікс",
|
||||
"demo": "Демо",
|
||||
"fieldRecording": "Запис поза студією",
|
||||
"interview": "Інтерв'ю",
|
||||
"live": "Наживо",
|
||||
"mixtape": "Мікстейп",
|
||||
"remix": "Ремікс",
|
||||
"soundtrack": "Саундтрек",
|
||||
"spokenWord": "Усне слово"
|
||||
}
|
||||
},
|
||||
"setting": {
|
||||
"autoDJ": "Авто DJ",
|
||||
"autoDJ_itemCount": "Кількість елементів",
|
||||
"autoDJ_itemCount_description": "Кількість елементів, які будуть додані до черги",
|
||||
"autoDJ_timing": "Таймінг",
|
||||
"autoDJ_timing_description": "Кількість треків залишившихся в черзі перед тим, як авто DJ запрацює",
|
||||
"autoDJ_mode": "Режим",
|
||||
"autoDJ_mode_albums": "Альбоми",
|
||||
"autoDJ_mode_description": "Оберіть, додавати треки чи цілі альбоми до черги",
|
||||
"autoDJ_mode_songs": "Треки",
|
||||
"autoDJ_enabled": "Увімкнути Авто DJ",
|
||||
"autoDJ_albumStrategy": "Режим вибора альбомів",
|
||||
"autoDJ_songStrategy": "Режим вибора треків",
|
||||
"autoDJ_strategy_option_library_random": "Випадково",
|
||||
"autoDJ_strategy_option_similar": "Схожі",
|
||||
"autosave": "Автоматично зберігати чергу відтворення",
|
||||
"autosave_description": "Увімкнути автоматичне збереження черги відтворення до вашого серверу. Це можливо тільки коли використовується Navidrome/Subsonic.Також, ви не можете мати міксовану чергу відтворення.",
|
||||
"autosaveCount": "Частота автоматичного збереження черги відтворення",
|
||||
"autosaveCount_description": "Кількість зміни трека перед збереженням черги. 1 (мінімум) означає змінення кожного трека",
|
||||
"accentColor_description": "Встановлює акцентний колір для застосунка",
|
||||
"accentColor": "Акцентний колір",
|
||||
"useThemeAccentColor": "Використовувати акцентний колір теми",
|
||||
"useThemeAccentColor_description": "Використовувати основний колір визначений у обраній темі замість користувацького акцентного коліру",
|
||||
"useThemePrimaryShade": "Використовувати основний відтінок теми"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,7 +828,45 @@
|
||||
"autoDJ_albumStrategy": "專輯選擇模式",
|
||||
"autoDJ_songStrategy": "歌曲選擇模式",
|
||||
"autoDJ_strategy_option_library_random": "隨機",
|
||||
"autoDJ_strategy_option_similar": "相似"
|
||||
"autoDJ_strategy_option_similar": "相似",
|
||||
"enableFurigana_description": "在日文歌詞漢字上方顯示發音標記(振假名)。",
|
||||
"enableFurigana": "啟用振假名顯示",
|
||||
"equalizer_descriptionMpv": "透過 FFmpeg lavfi (MPV) 使用參數等化器",
|
||||
"equalizer_descriptionWebAudio": "透過 Web Audio API 使用參數等化器",
|
||||
"equalizer": "等化器",
|
||||
"equalizerBands_description": "各頻段增益。可上下拖曳或輸入數值。範圍:-12 至 +12 dB。",
|
||||
"equalizerBands": "頻段",
|
||||
"equalizerPreamp_description": "EQ 頻段之前的輸入增益。提升頻段時可設為負值以避免削波 (MPV)。",
|
||||
"equalizerPreamp": "前級增益",
|
||||
"equalizerPreset_description": "套用內建或已儲存的自訂 EQ 曲線",
|
||||
"equalizerPreset": "預設",
|
||||
"equalizerPresetDeletePlaceholder": "刪除自訂…",
|
||||
"equalizerPresetGroupBuiltIn": "內建",
|
||||
"equalizerPresetGroupCustom": "自訂",
|
||||
"equalizerPresetNamePlaceholder": "預設名稱…",
|
||||
"equalizerPresetSelectPlaceholder": "選擇預設",
|
||||
"equalizerSavePreset_description": "將目前 EQ 設定儲存為具名預設",
|
||||
"equalizerSavePreset": "儲存預設",
|
||||
"compressor_descriptionMpv": "透過 FFmpeg acompressor (MPV) 使用動態範圍壓縮器",
|
||||
"compressor_descriptionWebAudio": "透過 Web Audio API 使用動態範圍壓縮器",
|
||||
"compressor": "壓縮器",
|
||||
"compressorAttack_description": "訊號超過閾值後,壓縮器開始作用的速度。",
|
||||
"compressorAttack": "啟動時間",
|
||||
"compressorKnee_description": "柔性拐點寬度。數值越高,進入壓縮的過渡越平滑。",
|
||||
"compressorKnee": "拐點",
|
||||
"compressorMakeupGain_description": "壓縮後套用的輸出增益,用於恢復音量。",
|
||||
"compressorMakeupGain": "補償增益",
|
||||
"compressorPreset_description": "套用內建或已儲存的自訂壓縮器設定",
|
||||
"compressorRatio_description": "壓縮比例,例如 4 表示 4:1。",
|
||||
"compressorRatio": "比例",
|
||||
"compressorRelease_description": "訊號低於閾值後,壓縮器解除作用的速度。",
|
||||
"compressorRelease": "釋放時間",
|
||||
"compressorReset_description": "將所有壓縮器參數恢復為預設值",
|
||||
"compressorSavePreset_description": "將目前壓縮器設定儲存為具名預設",
|
||||
"compressorThreshold_description": "開始進行壓縮的訊號電平。",
|
||||
"compressorThreshold": "閥值",
|
||||
"enableRomaji_description": "在日文歌詞下方顯示羅馬拼音。",
|
||||
"enableRomaji": "啟用羅馬拼音顯示"
|
||||
},
|
||||
"table": {
|
||||
"config": {
|
||||
|
||||
@@ -7,16 +7,19 @@ let kuroshiroInstance: any = null;
|
||||
let initPromise: null | Promise<void> = null;
|
||||
|
||||
const getKuroshiro = async () => {
|
||||
if (kuroshiroInstance) return kuroshiroInstance;
|
||||
if (initPromise) {
|
||||
await initPromise;
|
||||
return kuroshiroInstance;
|
||||
}
|
||||
|
||||
if (kuroshiroInstance) return kuroshiroInstance;
|
||||
|
||||
const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro;
|
||||
kuroshiroInstance = new KuroshiroClass();
|
||||
initPromise = kuroshiroInstance.init(new KuromojiAnalyzer());
|
||||
await initPromise;
|
||||
|
||||
initPromise = null;
|
||||
return kuroshiroInstance;
|
||||
};
|
||||
|
||||
@@ -35,3 +38,17 @@ export const convertFurigana = async (text: string): Promise<string> => {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
export const convertRomaji = async (text: string): Promise<string> => {
|
||||
const KuroshiroClass = (Kuroshiro as any).default || Kuroshiro;
|
||||
|
||||
if (!KuroshiroClass.Util.hasKana(text)) return text;
|
||||
|
||||
try {
|
||||
const kuroshiro = await getKuroshiro();
|
||||
return await kuroshiro.convert(text, { mode: 'spaced', to: 'romaji' });
|
||||
} catch (e) {
|
||||
console.error('Romaji conversion error: ', e);
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ipcMain } from 'electron';
|
||||
|
||||
import { store } from '../settings';
|
||||
import { convertFurigana } from './furigana';
|
||||
import { convertFurigana, convertRomaji } from './furigana';
|
||||
import { getLyricsBySongId as getGenius, getSearchResults as searchGenius } from './genius';
|
||||
import { getLyricsBySongId as getLrcLib, getSearchResults as searchLrcLib } from './lrclib';
|
||||
import { getLyricsBySongId as getNetease, getSearchResults as searchNetease } from './netease';
|
||||
@@ -236,3 +236,7 @@ ipcMain.handle('lyric-by-remote-id', async (_event, params: LyricGetQuery) => {
|
||||
ipcMain.handle('lyric-convert-furigana', async (_event, text: string) => {
|
||||
return await convertFurigana(text);
|
||||
});
|
||||
|
||||
ipcMain.handle('lyric-convert-romaji', async (_event, text: string) => {
|
||||
return await convertRomaji(text);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import console from 'console';
|
||||
import { app, ipcMain } from 'electron';
|
||||
import { app, ipcMain, powerMonitor } from 'electron';
|
||||
import { access, rm } from 'fs/promises';
|
||||
import uniq from 'lodash/uniq';
|
||||
import MpvAPI from 'node-mpv';
|
||||
@@ -85,6 +85,19 @@ const DEFAULT_MPV_PARAMETERS = (extraParameters?: string[]) => {
|
||||
parameters.push('--prefetch-playlist=yes');
|
||||
}
|
||||
|
||||
// Without these, mpv/ffmpeg will block indefinitely on a dead TCP connection
|
||||
// instead of failing or reconnecting. This commonly happens when the OS network
|
||||
// adapter resets after the system wakes from sleep while a stream is open.
|
||||
if (!extraParameters?.some((param) => param.startsWith('--network-timeout'))) {
|
||||
parameters.push('--network-timeout=10');
|
||||
}
|
||||
|
||||
if (!extraParameters?.some((param) => param.startsWith('--stream-lavf-o'))) {
|
||||
parameters.push(
|
||||
'--stream-lavf-o=reconnect=1,reconnect_streamed=1,reconnect_at_eof=1,reconnect_delay_max=5',
|
||||
);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
};
|
||||
|
||||
@@ -191,21 +204,44 @@ export const getMpvInstance = () => {
|
||||
return mpvInstance;
|
||||
};
|
||||
|
||||
const QUIT_TIMEOUT_MS = 3000;
|
||||
|
||||
const killMpvProcess = (mpv: MpvAPI) => {
|
||||
const mpvProcess = (mpv as any).process || (mpv as any).mpvProcess;
|
||||
if (mpvProcess && typeof mpvProcess.kill === 'function') {
|
||||
try {
|
||||
mpvProcess.kill('SIGTERM');
|
||||
} catch (killErr) {
|
||||
mpvLog({ action: 'Failed to kill mpv process' }, killErr as NodeMpvError);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const quit = async (instance?: MpvAPI | null) => {
|
||||
const mpv = instance || getMpvInstance();
|
||||
if (mpv) {
|
||||
try {
|
||||
await mpv.quit();
|
||||
// mpv.quit() resolves only when mpv replies over IPC. If mpv's command queue
|
||||
// is wedged (e.g. blocked on a dead network stream after the system resumes
|
||||
// from sleep), that reply never arrives, so this must not be allowed to hang
|
||||
// forever - fall back to killing the process directly.
|
||||
let timedOut = false;
|
||||
await Promise.race([
|
||||
mpv.quit(),
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
timedOut = true;
|
||||
resolve(undefined);
|
||||
}, QUIT_TIMEOUT_MS);
|
||||
}),
|
||||
]);
|
||||
|
||||
if (timedOut) {
|
||||
killMpvProcess(mpv);
|
||||
}
|
||||
} catch {
|
||||
// If quit() fails, try to kill the process directly
|
||||
const mpvProcess = (mpv as any).process || (mpv as any).mpvProcess;
|
||||
if (mpvProcess && typeof mpvProcess.kill === 'function') {
|
||||
try {
|
||||
mpvProcess.kill('SIGTERM');
|
||||
} catch (killErr) {
|
||||
mpvLog({ action: 'Failed to kill mpv process' }, killErr as NodeMpvError);
|
||||
}
|
||||
}
|
||||
killMpvProcess(mpv);
|
||||
}
|
||||
if (!isWindows()) {
|
||||
try {
|
||||
@@ -666,6 +702,17 @@ const cleanupMpv = async (force = false) => {
|
||||
}
|
||||
};
|
||||
|
||||
// When the OS resumes from sleep, any network stream mpv had open is likely dead
|
||||
// (the connection silently dropped while the network adapter was suspended). Tell
|
||||
// the renderer to reload mpv so it reconnects with a fresh stream instead of staying
|
||||
// stuck on the old, now-dead connection until the app is manually restarted.
|
||||
powerMonitor.on('resume', () => {
|
||||
if (getMpvInstance()) {
|
||||
mpvLog({ action: 'System resumed from sleep, reloading mpv' });
|
||||
getMainWindow()?.webContents.send('renderer-mpv-reconnect');
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', async (event) => {
|
||||
switch (mpvState) {
|
||||
case MpvState.DONE:
|
||||
|
||||
@@ -30,8 +30,13 @@ const convertFurigana = (text: string): Promise<string> => {
|
||||
return ipcRenderer.invoke('lyric-convert-furigana', text);
|
||||
};
|
||||
|
||||
const convertRomaji = (text: string): Promise<string> => {
|
||||
return ipcRenderer.invoke('lyric-convert-romaji', text);
|
||||
};
|
||||
|
||||
export const lyrics = {
|
||||
convertFurigana,
|
||||
convertRomaji,
|
||||
getRemoteLyricsByRemoteId,
|
||||
getRemoteLyricsBySong,
|
||||
searchRemoteLyrics,
|
||||
|
||||
@@ -174,6 +174,10 @@ const rendererPlayerFallback = (cb: (data: boolean) => void) => {
|
||||
ipcRenderer.on('renderer-player-fallback', (_, data) => cb(data));
|
||||
};
|
||||
|
||||
const rendererMpvReconnect = (cb: () => void) => {
|
||||
ipcRenderer.on('renderer-mpv-reconnect', () => cb());
|
||||
};
|
||||
|
||||
export const mpvPlayer = {
|
||||
autoNext,
|
||||
cleanup,
|
||||
@@ -205,6 +209,7 @@ export const mpvPlayerListener = {
|
||||
rendererAutoNext,
|
||||
rendererCurrentTime,
|
||||
rendererError,
|
||||
rendererMpvReconnect,
|
||||
rendererNext,
|
||||
rendererPause,
|
||||
rendererPlay,
|
||||
|
||||
+10
-4
@@ -64,6 +64,7 @@ export const useItemDragDropState = <TElement extends HTMLElement = HTMLDivEleme
|
||||
return draggedItems;
|
||||
},
|
||||
itemType,
|
||||
metadata: { playlistId },
|
||||
onDragStart: () => {
|
||||
if (!item || !isDataRow) {
|
||||
return;
|
||||
@@ -248,10 +249,15 @@ export const useItemDragDropState = <TElement extends HTMLElement = HTMLDivEleme
|
||||
case DragTarget.SONG: {
|
||||
const sourceItems = (args.source.item || []) as Song[];
|
||||
if (sourceItems.length > 0) {
|
||||
playerContext.addToQueueByData(sourceItems, {
|
||||
edge: args.edge,
|
||||
uniqueId: droppedOnUniqueId,
|
||||
});
|
||||
const sourcePlaylistId = args.source.metadata?.playlistId as
|
||||
| string
|
||||
| undefined;
|
||||
playerContext.addToQueueByData(
|
||||
sourceItems,
|
||||
{ edge: args.edge, uniqueId: droppedOnUniqueId },
|
||||
undefined,
|
||||
sourcePlaylistId ?? null,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -307,6 +307,20 @@ export const LyricsSettingsForm = ({ settingsKey }: LyricsSettingsFormProps) =>
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableFurigana'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
aria-label="Enable romaji"
|
||||
defaultChecked={lyricsSettings.enableRomaji}
|
||||
onChange={(e) => updateLyricsSetting({ enableRomaji: e.currentTarget.checked })}
|
||||
/>
|
||||
),
|
||||
description: t('setting.enableRomaji', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableRomaji'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
|
||||
@@ -28,3 +28,27 @@ export const useFuriganaLyrics = (lyrics: LyricsResponse | null | undefined, ena
|
||||
staleTime: Infinity,
|
||||
});
|
||||
};
|
||||
|
||||
export const useRomajiLyrics = (lyrics: LyricsResponse | null | undefined, enabled: boolean) => {
|
||||
return useQuery({
|
||||
enabled: enabled && !!lyrics && !!lyricsApi,
|
||||
queryFn: async () => {
|
||||
if (!lyrics || !lyricsApi || !enabled) return lyrics;
|
||||
|
||||
if (typeof lyrics === 'string') {
|
||||
return await lyricsApi.convertRomaji(lyrics);
|
||||
} else if (Array.isArray(lyrics)) {
|
||||
const text = lyrics.map(([, line]) => line).join('\n');
|
||||
const converted = await lyricsApi.convertRomaji(text);
|
||||
const convertedLines = converted.split('\n');
|
||||
return lyrics.map(([time], i) => [
|
||||
time,
|
||||
convertedLines[i] ?? lyrics[i][1],
|
||||
]) as SynchronizedLyricsArray;
|
||||
}
|
||||
return lyrics;
|
||||
},
|
||||
queryKey: ['romaji', lyrics],
|
||||
staleTime: Infinity,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -25,3 +25,8 @@
|
||||
.lyric-line:global(.synchronized) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.romaji-line {
|
||||
font-size: 0.8em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -10,11 +10,21 @@ import { Stack } from '/@/shared/components/stack/stack';
|
||||
interface LyricLineProps extends ComponentPropsWithoutRef<'div'> {
|
||||
alignment: 'center' | 'left' | 'right';
|
||||
fontSize: number;
|
||||
romajiText?: null | string;
|
||||
text: string;
|
||||
translatedText?: null | string;
|
||||
}
|
||||
|
||||
export const LyricLine = memo(
|
||||
({ alignment, className, fontSize, text, ...props }: LyricLineProps) => {
|
||||
({
|
||||
alignment,
|
||||
className,
|
||||
fontSize,
|
||||
romajiText,
|
||||
text,
|
||||
translatedText,
|
||||
...props
|
||||
}: LyricLineProps) => {
|
||||
const lines = useMemo(() => text.split('_BREAK_'), [text]);
|
||||
|
||||
const style = useMemo(
|
||||
@@ -31,6 +41,15 @@ export const LyricLine = memo(
|
||||
{lines.map((line, index) => (
|
||||
<span dangerouslySetInnerHTML={{ __html: sanitize(line) }} key={index} />
|
||||
))}
|
||||
{romajiText && (
|
||||
<span
|
||||
className={styles.romajiLine}
|
||||
dangerouslySetInnerHTML={{ __html: sanitize(romajiText) }}
|
||||
/>
|
||||
)}
|
||||
{translatedText && (
|
||||
<span dangerouslySetInnerHTML={{ __html: sanitize(translatedText) }} />
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,10 @@ import {
|
||||
type LyricsQueryResult,
|
||||
} from '/@/renderer/features/lyrics/api/lyrics-api';
|
||||
import { openLyricsExportModal } from '/@/renderer/features/lyrics/components/lyrics-export-form';
|
||||
import { useFuriganaLyrics } from '/@/renderer/features/lyrics/hooks/use-furigana-lyrics';
|
||||
import {
|
||||
useFuriganaLyrics,
|
||||
useRomajiLyrics,
|
||||
} from '/@/renderer/features/lyrics/hooks/use-furigana-lyrics';
|
||||
import { LyricsActions } from '/@/renderer/features/lyrics/lyrics-actions';
|
||||
import {
|
||||
SynchronizedLyrics,
|
||||
@@ -51,6 +54,7 @@ export const Lyrics = ({ fadeOutNoLyricsMessage = true, settingsKey = 'default'
|
||||
const {
|
||||
enableAutoTranslation,
|
||||
enableFurigana,
|
||||
enableRomaji,
|
||||
preferLocalLyrics,
|
||||
translationApiKey,
|
||||
translationApiProvider,
|
||||
@@ -119,6 +123,7 @@ export const Lyrics = ({ fadeOutNoLyricsMessage = true, settingsKey = 'default'
|
||||
}, [data, indexToUse, preferLocalLyrics]);
|
||||
|
||||
const { data: furiganaConvertedLyrics } = useFuriganaLyrics(lyrics?.lyrics, !!enableFurigana);
|
||||
const { data: romajiConvertedLyrics } = useRomajiLyrics(lyrics?.lyrics, !!enableRomaji);
|
||||
|
||||
const displayLyrics = useMemo(() => {
|
||||
if (isLyricsDisabled || !lyrics) return null;
|
||||
@@ -293,10 +298,10 @@ export const Lyrics = ({ fadeOutNoLyricsMessage = true, settingsKey = 'default'
|
||||
}, [isLoadingLyrics, hasNoLyrics, fadeOutNoLyricsMessage]);
|
||||
|
||||
const handleExportLyrics = useCallback(() => {
|
||||
if (displayLyrics) {
|
||||
openLyricsExportModal({ lyrics: displayLyrics, offsetMs: currentOffsetMs, synced });
|
||||
if (lyrics && !isLyricsDisabled) {
|
||||
openLyricsExportModal({ lyrics, offsetMs: currentOffsetMs, synced });
|
||||
}
|
||||
}, [currentOffsetMs, displayLyrics, synced]);
|
||||
}, [currentOffsetMs, isLyricsDisabled, lyrics, synced]);
|
||||
|
||||
const handleOpenSettings = () => {
|
||||
openLyricsSettingsModal(settingsKey);
|
||||
@@ -344,12 +349,22 @@ export const Lyrics = ({ fadeOutNoLyricsMessage = true, settingsKey = 'default'
|
||||
<SynchronizedLyrics
|
||||
{...(displayLyrics as SynchronizedLyricsProps)}
|
||||
offsetMs={displayOffsetMs}
|
||||
romajiLyrics={
|
||||
enableRomaji
|
||||
? (romajiConvertedLyrics as SynchronizedLyricsProps['romajiLyrics'])
|
||||
: null
|
||||
}
|
||||
settingsKey={settingsKey}
|
||||
translatedLyrics={showTranslation ? translatedLyrics : null}
|
||||
/>
|
||||
) : (
|
||||
<UnsynchronizedLyrics
|
||||
{...(displayLyrics as UnsynchronizedLyricsProps)}
|
||||
romajiLyrics={
|
||||
enableRomaji
|
||||
? (romajiConvertedLyrics as UnsynchronizedLyricsProps['romajiLyrics'])
|
||||
: null
|
||||
}
|
||||
settingsKey={settingsKey}
|
||||
translatedLyrics={showTranslation ? translatedLyrics : null}
|
||||
/>
|
||||
|
||||
@@ -23,6 +23,7 @@ const mpris = isElectron() && utils?.isLinux() ? window.api.mpris : null;
|
||||
export interface SynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
|
||||
lyrics: SynchronizedLyricsArray;
|
||||
offsetMs?: number;
|
||||
romajiLyrics?: null | SynchronizedLyricsArray;
|
||||
settingsKey?: string;
|
||||
style?: React.CSSProperties;
|
||||
translatedLyrics?: null | string;
|
||||
@@ -34,6 +35,7 @@ export const SynchronizedLyrics = ({
|
||||
name,
|
||||
offsetMs,
|
||||
remote,
|
||||
romajiLyrics,
|
||||
settingsKey = 'default',
|
||||
source,
|
||||
style,
|
||||
@@ -368,10 +370,9 @@ export const SynchronizedLyrics = ({
|
||||
handleSeek(time / 1000);
|
||||
}
|
||||
}}
|
||||
text={
|
||||
text +
|
||||
(translatedLyrics ? `_BREAK_${translatedLyrics.split('\n')[idx]}` : '')
|
||||
}
|
||||
romajiText={romajiLyrics?.[idx]?.[1]}
|
||||
text={text}
|
||||
translatedText={translatedLyrics?.split('\n')[idx]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { FullLyricsMetadata } from '/@/shared/types/domain-types';
|
||||
|
||||
export interface UnsynchronizedLyricsProps extends Omit<FullLyricsMetadata, 'lyrics'> {
|
||||
lyrics: string;
|
||||
romajiLyrics?: null | string;
|
||||
settingsKey?: string;
|
||||
translatedLyrics?: null | string;
|
||||
}
|
||||
@@ -17,6 +18,7 @@ export const UnsynchronizedLyrics = ({
|
||||
lyrics,
|
||||
name,
|
||||
remote,
|
||||
romajiLyrics,
|
||||
settingsKey = 'default',
|
||||
source,
|
||||
translatedLyrics,
|
||||
@@ -42,6 +44,10 @@ export const UnsynchronizedLyrics = ({
|
||||
return translatedLyrics ? translatedLyrics.split('\n') : [];
|
||||
}, [translatedLyrics]);
|
||||
|
||||
const romajiLines = useMemo(() => {
|
||||
return romajiLyrics ? romajiLyrics.split('\n') : [];
|
||||
}, [romajiLyrics]);
|
||||
|
||||
return (
|
||||
<div className={styles.container} style={{ gap: `${settings.gapUnsync}px` }}>
|
||||
{settings.showProvider && source && (
|
||||
@@ -67,7 +73,9 @@ export const UnsynchronizedLyrics = ({
|
||||
fontSize={settings.fontSizeUnsync}
|
||||
id={`lyric-${idx}`}
|
||||
key={idx}
|
||||
text={text + (translatedLines[idx] ? `_BREAK_${translatedLines[idx]}` : '')}
|
||||
romajiText={romajiLines[idx]}
|
||||
text={text}
|
||||
translatedText={translatedLines[idx]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -68,9 +68,13 @@ export const MpvPlayerEngine = (props: MpvPlayerEngineProps) => {
|
||||
};
|
||||
|
||||
eventEmitter.on('MPV_RELOAD', handleMpvReload);
|
||||
// The main process notifies us after the OS resumes from sleep, since the
|
||||
// stream mpv had open is likely on a now-dead connection.
|
||||
mpvPlayerListener?.rendererMpvReconnect(handleMpvReload);
|
||||
|
||||
return () => {
|
||||
eventEmitter.off('MPV_RELOAD', handleMpvReload);
|
||||
ipc?.removeAllListeners('renderer-mpv-reconnect');
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -39,7 +39,12 @@ import {
|
||||
import { Play, PlayerRepeat, PlayerShuffle } from '/@/shared/types/types';
|
||||
|
||||
export interface PlayerContext {
|
||||
addToQueueByData: (data: Song[], type: AddToQueueType, playSongId?: string) => void;
|
||||
addToQueueByData: (
|
||||
data: Song[],
|
||||
type: AddToQueueType,
|
||||
playSongId?: string,
|
||||
contextPlaylistId?: null | string,
|
||||
) => void;
|
||||
addToQueueByFetch: (
|
||||
serverId: string,
|
||||
id: string[],
|
||||
@@ -137,6 +142,23 @@ const getRootQueryKey = (itemType: LibraryItem, serverId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const isReplaceQueueType = (type: AddToQueueType): boolean => {
|
||||
if (typeof type === 'object') return false;
|
||||
return type === Play.NOW || type === Play.SHUFFLE;
|
||||
};
|
||||
|
||||
// HashRouter puts the route in location.hash, not pathname.
|
||||
const inferPlaylistContextFromUrl = (): null | string => {
|
||||
const route = window.location.hash.replace(/^#/, '');
|
||||
const match = route.match(/^\/playlists\/([^/]+)/);
|
||||
return match ? match[1] : null;
|
||||
};
|
||||
|
||||
// Stamps each song with the playlist it was queued from, so the sidebar highlight
|
||||
// can be derived from whichever song is currently playing (see useCurrentPlaylistContextId).
|
||||
const tagPlaylistContext = (songs: Song[], contextPlaylistId: string): Song[] =>
|
||||
songs.map((song) => ({ ...song, _contextPlaylistId: contextPlaylistId }));
|
||||
|
||||
export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const { t } = useTranslation();
|
||||
const queryClient = useQueryClient();
|
||||
@@ -187,9 +209,20 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
}, [doNotShowAgain, setDoNotShowAgain, t]);
|
||||
|
||||
const addToQueueByData = useCallback(
|
||||
(data: Song[], type: AddToQueueType, playSongId?: string) => {
|
||||
(
|
||||
data: Song[],
|
||||
type: AddToQueueType,
|
||||
playSongId?: string,
|
||||
contextPlaylistId?: null | string,
|
||||
) => {
|
||||
const filters = useSettingsStore.getState().playback.filters;
|
||||
const filteredData = filterSongsByPlayerFilters(data, filters);
|
||||
let filteredData = filterSongsByPlayerFilters(data, filters);
|
||||
const resolvedContextId =
|
||||
contextPlaylistId ??
|
||||
(isReplaceQueueType(type) ? inferPlaylistContextFromUrl() : null);
|
||||
if (resolvedContextId) {
|
||||
filteredData = tagPlaylistContext(filteredData, resolvedContextId);
|
||||
}
|
||||
|
||||
if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
|
||||
const edge = type.edge === 'top' ? 'top' : 'bottom';
|
||||
@@ -279,7 +312,21 @@ export const PlayerProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
}
|
||||
|
||||
const filters = useSettingsStore.getState().playback.filters;
|
||||
const filteredSongs = filterSongsByPlayerFilters(sortedSongs, filters);
|
||||
let filteredSongs = filterSongsByPlayerFilters(sortedSongs, filters);
|
||||
|
||||
// Songs from multiple playlists are merged together, so there is no single
|
||||
// playlist to attribute them to: skip tagging (and URL inference) entirely.
|
||||
const isMultiPlaylist = itemType === LibraryItem.PLAYLIST && id.length > 1;
|
||||
const explicitId =
|
||||
itemType === LibraryItem.PLAYLIST && id.length === 1 ? id[0] : null;
|
||||
const resolvedContextId =
|
||||
explicitId ??
|
||||
(!isMultiPlaylist && isReplaceQueueType(type)
|
||||
? inferPlaylistContextFromUrl()
|
||||
: null);
|
||||
if (resolvedContextId) {
|
||||
filteredSongs = tagPlaylistContext(filteredSongs, resolvedContextId);
|
||||
}
|
||||
|
||||
if (typeof type === 'object' && 'edge' in type && type.edge !== null) {
|
||||
const edge = type.edge === 'top' ? 'top' : 'bottom';
|
||||
|
||||
@@ -107,6 +107,20 @@ export const LyricSettings = memo(() => {
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableFurigana'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
aria-label="Enable romaji generation"
|
||||
defaultChecked={settings.enableRomaji}
|
||||
onChange={(e) => updateSetting({ enableRomaji: e.currentTarget.checked })}
|
||||
/>
|
||||
),
|
||||
description: t('setting.enableRomaji', {
|
||||
context: 'description',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.enableRomaji'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMove } from '@mantine/hooks';
|
||||
import isElectron from 'is-electron';
|
||||
import { memo, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
buildMpvAudioFilters,
|
||||
@@ -257,6 +258,7 @@ function EqBandSlider({
|
||||
|
||||
// ─── Main component ───────────────────────────────────────────────────────────
|
||||
export const EqSettings = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const settings = usePlaybackSettings();
|
||||
const { setSettings } = useSettingsStoreActions();
|
||||
|
||||
@@ -443,13 +445,13 @@ export const EqSettings = memo(() => {
|
||||
// ── Preset select data ────────────────────────────────────────────────────
|
||||
const eqPresetSelectData = [
|
||||
{
|
||||
group: 'Built-in',
|
||||
group: t('setting.equalizerPresetGroupBuiltIn'),
|
||||
items: Object.keys(EQ_PRESETS).map((name) => ({ label: name, value: name })),
|
||||
},
|
||||
...(Object.keys(customEqPresets).length > 0
|
||||
? [
|
||||
{
|
||||
group: 'Custom',
|
||||
group: t('setting.equalizerPresetGroupCustom'),
|
||||
items: Object.keys(customEqPresets).map((name) => ({
|
||||
label: name,
|
||||
value: name,
|
||||
@@ -461,13 +463,13 @@ export const EqSettings = memo(() => {
|
||||
|
||||
const compPresetSelectData = [
|
||||
{
|
||||
group: 'Built-in',
|
||||
group: t('setting.equalizerPresetGroupBuiltIn'),
|
||||
items: Object.keys(COMP_PRESETS).map((name) => ({ label: name, value: name })),
|
||||
},
|
||||
...(Object.keys(customCompPresets).length > 0
|
||||
? [
|
||||
{
|
||||
group: 'Custom',
|
||||
group: t('setting.equalizerPresetGroupCustom'),
|
||||
items: Object.keys(customCompPresets).map((name) => ({
|
||||
label: name,
|
||||
value: name,
|
||||
@@ -488,9 +490,9 @@ export const EqSettings = memo(() => {
|
||||
),
|
||||
description:
|
||||
settings.type === PlayerType.LOCAL
|
||||
? 'Parametric equalizer via FFmpeg lavfi (MPV)'
|
||||
: 'Parametric equalizer via Web Audio API',
|
||||
title: 'Equalizer',
|
||||
? t('setting.equalizer', { context: 'descriptionMpv' })
|
||||
: t('setting.equalizer', { context: 'descriptionWebAudio' }),
|
||||
title: t('setting.equalizer'),
|
||||
},
|
||||
...(settings.equalizer.enabled
|
||||
? ([
|
||||
@@ -505,7 +507,7 @@ export const EqSettings = memo(() => {
|
||||
const preset = customEqPresets[name] ?? EQ_PRESETS[name];
|
||||
if (preset) applyEqPreset(preset);
|
||||
}}
|
||||
placeholder="Select preset"
|
||||
placeholder={t('setting.equalizerPresetSelectPlaceholder')}
|
||||
searchable
|
||||
value={null}
|
||||
w={180}
|
||||
@@ -521,15 +523,15 @@ export const EqSettings = memo(() => {
|
||||
if (!name) return;
|
||||
handleDeleteEqPreset(name);
|
||||
}}
|
||||
placeholder="Delete custom..."
|
||||
placeholder={t('setting.equalizerPresetDeletePlaceholder')}
|
||||
value={null}
|
||||
w={160}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
),
|
||||
description: 'Apply a built-in or saved custom EQ curve',
|
||||
title: 'Preset',
|
||||
description: t('setting.equalizerPreset', { context: 'description' }),
|
||||
title: t('setting.equalizerPreset'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
@@ -539,7 +541,7 @@ export const EqSettings = memo(() => {
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') handleSaveEqPreset();
|
||||
}}
|
||||
placeholder="Preset name..."
|
||||
placeholder={t('setting.equalizerPresetNamePlaceholder')}
|
||||
value={saveEqName}
|
||||
w={180}
|
||||
/>
|
||||
@@ -548,12 +550,12 @@ export const EqSettings = memo(() => {
|
||||
onClick={handleSaveEqPreset}
|
||||
variant="subtle"
|
||||
>
|
||||
Save
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</Group>
|
||||
),
|
||||
description: 'Save the current EQ settings as a named preset',
|
||||
title: 'Save preset',
|
||||
description: t('setting.equalizerSavePreset', { context: 'description' }),
|
||||
title: t('setting.equalizerSavePreset'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
@@ -600,13 +602,12 @@ export const EqSettings = memo(() => {
|
||||
w={70}
|
||||
/>
|
||||
<Button onClick={handleResetEq} variant="subtle">
|
||||
Reset all
|
||||
{t('common.reset')}
|
||||
</Button>
|
||||
</Group>
|
||||
),
|
||||
description:
|
||||
'Input gain before EQ bands. Set negative when boosting bands to prevent clipping (MPV).',
|
||||
title: 'Preamp',
|
||||
description: t('setting.equalizerPreamp', { context: 'description' }),
|
||||
title: t('setting.equalizerPreamp'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
@@ -625,9 +626,8 @@ export const EqSettings = memo(() => {
|
||||
))}
|
||||
</Group>
|
||||
),
|
||||
description:
|
||||
'Per-band gain. Drag up/down or type a value. Range: -12 to +12 dB.',
|
||||
title: 'Bands',
|
||||
description: t('setting.equalizerBands', { context: 'description' }),
|
||||
title: t('setting.equalizerBands'),
|
||||
},
|
||||
] as SettingOption[])
|
||||
: []),
|
||||
@@ -644,60 +644,57 @@ export const EqSettings = memo(() => {
|
||||
unit: string;
|
||||
}[] = [
|
||||
{
|
||||
description: 'Signal level above which compression begins.',
|
||||
description: t('setting.compressorThreshold', { context: 'description' }),
|
||||
key: 'threshold',
|
||||
max: 0,
|
||||
min: -60,
|
||||
step: 1,
|
||||
title: 'Threshold',
|
||||
title: t('setting.compressorThreshold'),
|
||||
unit: 'dB',
|
||||
},
|
||||
{
|
||||
description: 'Compression ratio, e.g. 4 = 4:1.',
|
||||
description: t('setting.compressorRatio', { context: 'description' }),
|
||||
key: 'ratio',
|
||||
max: 20,
|
||||
min: 1,
|
||||
step: 0.5,
|
||||
title: 'Ratio',
|
||||
title: t('setting.compressorRatio'),
|
||||
unit: ':1',
|
||||
},
|
||||
{
|
||||
description:
|
||||
'How quickly the compressor engages after the signal exceeds the threshold.',
|
||||
description: t('setting.compressorAttack', { context: 'description' }),
|
||||
key: 'attack',
|
||||
max: 2000,
|
||||
min: 0.1,
|
||||
step: 1,
|
||||
title: 'Attack',
|
||||
title: t('setting.compressorAttack'),
|
||||
unit: 'ms',
|
||||
},
|
||||
{
|
||||
description:
|
||||
'How quickly the compressor releases after the signal drops below the threshold.',
|
||||
description: t('setting.compressorRelease', { context: 'description' }),
|
||||
key: 'release',
|
||||
max: 9000,
|
||||
min: 1,
|
||||
step: 10,
|
||||
title: 'Release',
|
||||
title: t('setting.compressorRelease'),
|
||||
unit: 'ms',
|
||||
},
|
||||
{
|
||||
description: 'Output gain applied after compression to restore loudness.',
|
||||
description: t('setting.compressorMakeupGain', { context: 'description' }),
|
||||
key: 'makeup',
|
||||
max: 30,
|
||||
min: 0,
|
||||
step: 0.5,
|
||||
title: 'Makeup Gain',
|
||||
title: t('setting.compressorMakeupGain'),
|
||||
unit: 'dB',
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Soft-knee width. Higher values make the transition into compression more gradual.',
|
||||
description: t('setting.compressorKnee', { context: 'description' }),
|
||||
key: 'knee',
|
||||
max: 10,
|
||||
min: 1,
|
||||
step: 0.5,
|
||||
title: 'Knee',
|
||||
title: t('setting.compressorKnee'),
|
||||
unit: 'dB',
|
||||
},
|
||||
];
|
||||
@@ -713,9 +710,9 @@ export const EqSettings = memo(() => {
|
||||
),
|
||||
description:
|
||||
settings.type === PlayerType.LOCAL
|
||||
? 'Dynamic range compressor via FFmpeg acompressor (MPV)'
|
||||
: 'Dynamic range compressor via Web Audio API',
|
||||
title: 'Compressor',
|
||||
? t('setting.compressor', { context: 'descriptionMpv' })
|
||||
: t('setting.compressor', { context: 'descriptionWebAudio' }),
|
||||
title: t('setting.compressor'),
|
||||
},
|
||||
...(settings.compressor.enabled
|
||||
? ([
|
||||
@@ -730,7 +727,7 @@ export const EqSettings = memo(() => {
|
||||
const preset = customCompPresets[name] ?? COMP_PRESETS[name];
|
||||
if (preset) applyCompPreset(preset);
|
||||
}}
|
||||
placeholder="Select preset"
|
||||
placeholder={t('setting.equalizerPresetSelectPlaceholder')}
|
||||
searchable
|
||||
value={null}
|
||||
w={180}
|
||||
@@ -746,15 +743,15 @@ export const EqSettings = memo(() => {
|
||||
if (!name) return;
|
||||
handleDeleteCompPreset(name);
|
||||
}}
|
||||
placeholder="Delete custom..."
|
||||
placeholder={t('setting.equalizerPresetDeletePlaceholder')}
|
||||
value={null}
|
||||
w={160}
|
||||
/>
|
||||
)}
|
||||
</Group>
|
||||
),
|
||||
description: 'Apply a built-in or saved custom compressor setting',
|
||||
title: 'Preset',
|
||||
description: t('setting.compressorPreset', { context: 'description' }),
|
||||
title: t('setting.equalizerPreset'),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
@@ -764,7 +761,7 @@ export const EqSettings = memo(() => {
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') handleSaveCompPreset();
|
||||
}}
|
||||
placeholder="Preset name..."
|
||||
placeholder={t('setting.equalizerPresetNamePlaceholder')}
|
||||
value={saveCompName}
|
||||
w={180}
|
||||
/>
|
||||
@@ -773,12 +770,12 @@ export const EqSettings = memo(() => {
|
||||
onClick={handleSaveCompPreset}
|
||||
variant="subtle"
|
||||
>
|
||||
Save
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</Group>
|
||||
),
|
||||
description: 'Save the current compressor settings as a named preset',
|
||||
title: 'Save preset',
|
||||
description: t('setting.compressorSavePreset', { context: 'description' }),
|
||||
title: t('setting.equalizerSavePreset'),
|
||||
},
|
||||
// One SettingOption per compressor parameter — Slider + NumberInput
|
||||
...compParams.map(({ description, key, max, min, step, title, unit }) => ({
|
||||
@@ -834,11 +831,11 @@ export const EqSettings = memo(() => {
|
||||
{
|
||||
control: (
|
||||
<Button onClick={handleResetComp} variant="subtle">
|
||||
Reset to defaults
|
||||
{t('common.resetToDefault')}
|
||||
</Button>
|
||||
),
|
||||
description: 'Restore all compressor parameters to their default values',
|
||||
title: 'Reset',
|
||||
description: t('setting.compressorReset', { context: 'description' }),
|
||||
title: t('common.reset'),
|
||||
},
|
||||
] as SettingOption[])
|
||||
: []),
|
||||
|
||||
@@ -43,8 +43,18 @@ export const ShareItemContextModal = ({
|
||||
});
|
||||
|
||||
const handleSubmit = form.onSubmit(async (values) => {
|
||||
shareItemMutation.mutate(
|
||||
{
|
||||
const canUseClipboard = Boolean(navigator.clipboard) && window.isSecureContext;
|
||||
|
||||
// The share URL only exists once the create request resolves. Calling
|
||||
// navigator.clipboard.writeText() from that async callback runs outside
|
||||
// the click's user activation, so Firefox/Safari reject it ("Clipboard
|
||||
// write was blocked due to lack of user activation") and nothing is
|
||||
// copied. Instead, call clipboard.write() synchronously within this
|
||||
// gesture with a ClipboardItem whose value is a promise that resolves to
|
||||
// the URL — this preserves the activation while the share is created.
|
||||
// Falls back to writeText, then to the "click to open" toast.
|
||||
const shareUrlPromise = shareItemMutation
|
||||
.mutateAsync({
|
||||
apiClientProps: { serverId: server?.id || '' },
|
||||
body: {
|
||||
description: values.description,
|
||||
@@ -53,51 +63,63 @@ export const ShareItemContextModal = ({
|
||||
resourceIds: itemIds.join(),
|
||||
resourceType,
|
||||
},
|
||||
})
|
||||
.then((data) => {
|
||||
if (!server) throw new Error('Server not found');
|
||||
if (!data?.id) throw new Error('Failed to share item');
|
||||
|
||||
const serverUrl = getServerUrl(server, true);
|
||||
if (!serverUrl) throw new Error('Server URL not found');
|
||||
return `${serverUrl}/share/${data.id}`;
|
||||
});
|
||||
|
||||
let copied = false;
|
||||
if (canUseClipboard) {
|
||||
try {
|
||||
if (typeof ClipboardItem !== 'undefined') {
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'text/plain': shareUrlPromise.then(
|
||||
(url) => new Blob([url], { type: 'text/plain' }),
|
||||
),
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
await navigator.clipboard.writeText(await shareUrlPromise);
|
||||
}
|
||||
copied = true;
|
||||
} catch {
|
||||
copied = false;
|
||||
}
|
||||
}
|
||||
|
||||
let shareUrl: string;
|
||||
try {
|
||||
shareUrl = await shareUrlPromise;
|
||||
} catch {
|
||||
toast.error({
|
||||
message: t('form.shareItem.createFailed'),
|
||||
});
|
||||
closeModal(id);
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success({
|
||||
autoClose: copied ? 5000 : 15000,
|
||||
id: 'share-item-toast',
|
||||
message: t(copied ? 'form.shareItem.success' : 'form.shareItem.successMustClick', {}),
|
||||
onClick: (a) => {
|
||||
if (!(a.target instanceof HTMLElement)) return;
|
||||
|
||||
// Make sure we weren't clicking close (otherwise clicking close /also/ opens the url)
|
||||
if (a.target.nodeName !== 'svg') {
|
||||
window.open(shareUrl);
|
||||
toast.hide('share-item-toast');
|
||||
}
|
||||
},
|
||||
{
|
||||
onError: () => {
|
||||
toast.error({
|
||||
message: t('form.shareItem.createFailed'),
|
||||
});
|
||||
},
|
||||
onSuccess: (_data) => {
|
||||
if (!server) throw new Error('Server not found');
|
||||
if (!_data?.id) throw new Error('Failed to share item');
|
||||
|
||||
const serverUrl = getServerUrl(server, true);
|
||||
if (!serverUrl) throw new Error('Server URL not found');
|
||||
const shareUrl = `${serverUrl}/share/${_data.id}`;
|
||||
|
||||
const canUseClipboard = navigator.clipboard && window.isSecureContext;
|
||||
if (canUseClipboard) {
|
||||
navigator.clipboard.writeText(shareUrl);
|
||||
}
|
||||
|
||||
toast.success({
|
||||
autoClose: canUseClipboard ? 5000 : 15000,
|
||||
id: 'share-item-toast',
|
||||
message: t(
|
||||
canUseClipboard
|
||||
? 'form.shareItem.success'
|
||||
: 'form.shareItem.successMustClick',
|
||||
{},
|
||||
),
|
||||
onClick: (a) => {
|
||||
if (!(a.target instanceof HTMLElement)) return;
|
||||
|
||||
// Make sure we weren't clicking close (otherwise clicking close /also/ opens the url)
|
||||
if (a.target.nodeName !== 'svg') {
|
||||
window.open(shareUrl);
|
||||
toast.hide('share-item-toast');
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
closeModal(id);
|
||||
return null;
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -136,6 +136,10 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.name-active {
|
||||
color: var(--theme-colors-primary);
|
||||
}
|
||||
|
||||
.image-container {
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
|
||||
@@ -28,6 +28,7 @@ import { useDragDrop } from '/@/renderer/hooks/use-drag-drop';
|
||||
import { useDragMonitor } from '/@/renderer/hooks/use-drag-monitor';
|
||||
import { AppRoute } from '/@/renderer/router/routes';
|
||||
import {
|
||||
useCurrentPlaylistContextId,
|
||||
useCurrentServer,
|
||||
useCurrentServerId,
|
||||
usePermissions,
|
||||
@@ -116,6 +117,8 @@ export const PlaylistRowButton = memo(
|
||||
const sidebarPlaylistSorting = useSidebarPlaylistSorting();
|
||||
const sidebarPlaylistMode = useSidebarPlaylistMode();
|
||||
const isCompact = sidebarPlaylistMode === 'compact';
|
||||
const activePlaylistId = useCurrentPlaylistContextId();
|
||||
const isActive = activePlaylistId === item.id;
|
||||
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const isSmartPlaylist = Boolean(item.rules);
|
||||
@@ -292,7 +295,13 @@ export const PlaylistRowButton = memo(
|
||||
>
|
||||
{isCompact ? (
|
||||
<>
|
||||
<Text className={styles.compactName} fw={500} size="md">
|
||||
<Text
|
||||
className={clsx(styles.compactName, {
|
||||
[styles.nameActive]: isActive,
|
||||
})}
|
||||
fw={500}
|
||||
size="md"
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
{isHovered && (
|
||||
@@ -307,7 +316,13 @@ export const PlaylistRowButton = memo(
|
||||
<div className={styles.rowGroup}>
|
||||
<Image containerClassName={styles.imageContainer} src={imageUrl} />
|
||||
<div className={styles.metadata}>
|
||||
<Text className={styles.name} fw={500} size="md">
|
||||
<Text
|
||||
className={clsx(styles.name, {
|
||||
[styles.nameActive]: isActive,
|
||||
})}
|
||||
fw={500}
|
||||
size="md"
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
<div className={styles.metadataGroup}>
|
||||
|
||||
@@ -1640,6 +1640,7 @@ export const usePlayerStoreBase = createWithEqualityFn<PlayerState>()(
|
||||
const excludedPlayerKeys = ['playerNum', 'seekToTimestamp', 'status'];
|
||||
|
||||
// If we're not restoring the play queue, we don't need the index property
|
||||
// (it is meaningless without the queue)
|
||||
if (!shouldRestorePlayQueue) {
|
||||
excludedPlayerKeys.push('index');
|
||||
}
|
||||
@@ -2076,6 +2077,7 @@ export const updateQueueSong = (songId: string, updatedSong: Song) => {
|
||||
const uniqueId = song._uniqueId;
|
||||
state.queue.songs[song._uniqueId] = {
|
||||
...updatedSong,
|
||||
_contextPlaylistId: song._contextPlaylistId,
|
||||
_uniqueId: uniqueId,
|
||||
};
|
||||
}
|
||||
@@ -2083,6 +2085,10 @@ export const updateQueueSong = (songId: string, updatedSong: Song) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useCurrentPlaylistContextId = () => {
|
||||
return usePlayerStoreBase((state) => state.getCurrentSong()?._contextPlaylistId ?? null);
|
||||
};
|
||||
|
||||
export const usePlayerMuted = () => {
|
||||
return usePlayerStoreBase((state) => state.player.muted);
|
||||
};
|
||||
|
||||
@@ -578,6 +578,7 @@ const LyricsSettingsSchema = z.object({
|
||||
enableAutoTranslation: z.boolean(),
|
||||
enableFurigana: z.boolean().optional(),
|
||||
enableNeteaseTranslation: z.boolean(),
|
||||
enableRomaji: z.boolean().optional(),
|
||||
fetch: z.boolean(),
|
||||
follow: z.boolean(),
|
||||
preferLocalLyrics: z.boolean(),
|
||||
@@ -1848,6 +1849,7 @@ const initialState: SettingsState = {
|
||||
enableAutoTranslation: false,
|
||||
enableFurigana: false,
|
||||
enableNeteaseTranslation: false,
|
||||
enableRomaji: false,
|
||||
fetch: true,
|
||||
follow: true,
|
||||
preferLocalLyrics: true,
|
||||
|
||||
@@ -73,6 +73,7 @@ export interface QueueData {
|
||||
}
|
||||
|
||||
export type QueueSong = Song & {
|
||||
_contextPlaylistId?: null | string;
|
||||
_uniqueId: string;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user