Merge branch 'development' into sharing

This commit is contained in:
Benjamin 2024-03-31 22:33:36 -05:00 committed by GitHub
commit acedd249aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 180 additions and 126 deletions

148
package-lock.json generated
View File

@ -6734,21 +6734,21 @@
} }
}, },
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.0", "version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
"content-type": "~1.0.4", "content-type": "~1.0.5",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
"destroy": "1.2.0", "destroy": "1.2.0",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"iconv-lite": "0.4.24", "iconv-lite": "0.4.24",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"qs": "6.10.3", "qs": "6.11.0",
"raw-body": "2.5.1", "raw-body": "2.5.2",
"type-is": "~1.6.18", "type-is": "~1.6.18",
"unpipe": "1.0.0" "unpipe": "1.0.0"
}, },
@ -6799,7 +6799,7 @@
"node_modules/body-parser/node_modules/ms": { "node_modules/body-parser/node_modules/ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true "dev": true
}, },
"node_modules/bonjour-service": { "node_modules/bonjour-service": {
@ -7958,9 +7958,9 @@
] ]
}, },
"node_modules/content-type": { "node_modules/content-type": {
"version": "1.0.4", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
@ -7975,9 +7975,9 @@
} }
}, },
"node_modules/cookie": { "node_modules/cookie": {
"version": "0.5.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
@ -10957,17 +10957,17 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "4.18.0", "version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.0.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-EJEXxiTQJS3lIPrU1AE2vRuT7X7E+0KBbpm5GSoK524yl0K8X+er8zS2P14E64eqsVNoWbMCT7MpmQ+ErAhgRg==", "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
"body-parser": "1.20.0", "body-parser": "1.20.2",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"content-type": "~1.0.4", "content-type": "~1.0.4",
"cookie": "0.5.0", "cookie": "0.6.0",
"cookie-signature": "1.0.6", "cookie-signature": "1.0.6",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
@ -10983,7 +10983,7 @@
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.7", "path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.10.3", "qs": "6.11.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
"safe-buffer": "5.2.1", "safe-buffer": "5.2.1",
"send": "0.18.0", "send": "0.18.0",
@ -11563,9 +11563,9 @@
} }
}, },
"node_modules/fs-monkey": { "node_modules/fs-monkey": {
"version": "1.0.3", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==",
"dev": true "dev": true
}, },
"node_modules/fs-readdir-recursive": { "node_modules/fs-readdir-recursive": {
@ -15019,19 +15019,19 @@
"node_modules/media-typer": { "node_modules/media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/memfs": { "node_modules/memfs": {
"version": "3.4.1", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"fs-monkey": "1.0.3" "fs-monkey": "^1.0.4"
}, },
"engines": { "engines": {
"node": ">= 4.0.0" "node": ">= 4.0.0"
@ -17214,9 +17214,9 @@
} }
}, },
"node_modules/qs": { "node_modules/qs": {
"version": "6.10.3", "version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"side-channel": "^1.0.4" "side-channel": "^1.0.4"
@ -17314,9 +17314,9 @@
} }
}, },
"node_modules/raw-body": { "node_modules/raw-body": {
"version": "2.5.1", "version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"bytes": "3.1.2", "bytes": "3.1.2",
@ -21374,13 +21374,13 @@
} }
}, },
"node_modules/webpack-dev-middleware": { "node_modules/webpack-dev-middleware": {
"version": "5.3.1", "version": "5.3.4",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==", "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"colorette": "^2.0.10", "colorette": "^2.0.10",
"memfs": "^3.4.1", "memfs": "^3.4.3",
"mime-types": "^2.1.31", "mime-types": "^2.1.31",
"range-parser": "^1.2.1", "range-parser": "^1.2.1",
"schema-utils": "^4.0.0" "schema-utils": "^4.0.0"
@ -26891,21 +26891,21 @@
} }
}, },
"body-parser": { "body-parser": {
"version": "1.20.0", "version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dev": true, "dev": true,
"requires": { "requires": {
"bytes": "3.1.2", "bytes": "3.1.2",
"content-type": "~1.0.4", "content-type": "~1.0.5",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
"destroy": "1.2.0", "destroy": "1.2.0",
"http-errors": "2.0.0", "http-errors": "2.0.0",
"iconv-lite": "0.4.24", "iconv-lite": "0.4.24",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"qs": "6.10.3", "qs": "6.11.0",
"raw-body": "2.5.1", "raw-body": "2.5.2",
"type-is": "~1.6.18", "type-is": "~1.6.18",
"unpipe": "1.0.0" "unpipe": "1.0.0"
}, },
@ -26943,7 +26943,7 @@
"ms": { "ms": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"dev": true "dev": true
} }
} }
@ -27815,9 +27815,9 @@
} }
}, },
"content-type": { "content-type": {
"version": "1.0.4", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true "dev": true
}, },
"convert-source-map": { "convert-source-map": {
@ -27829,9 +27829,9 @@
} }
}, },
"cookie": { "cookie": {
"version": "0.5.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true "dev": true
}, },
"cookie-signature": { "cookie-signature": {
@ -30064,17 +30064,17 @@
} }
}, },
"express": { "express": {
"version": "4.18.0", "version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.0.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-EJEXxiTQJS3lIPrU1AE2vRuT7X7E+0KBbpm5GSoK524yl0K8X+er8zS2P14E64eqsVNoWbMCT7MpmQ+ErAhgRg==", "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
"body-parser": "1.20.0", "body-parser": "1.20.2",
"content-disposition": "0.5.4", "content-disposition": "0.5.4",
"content-type": "~1.0.4", "content-type": "~1.0.4",
"cookie": "0.5.0", "cookie": "0.6.0",
"cookie-signature": "1.0.6", "cookie-signature": "1.0.6",
"debug": "2.6.9", "debug": "2.6.9",
"depd": "2.0.0", "depd": "2.0.0",
@ -30090,7 +30090,7 @@
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.7", "path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.10.3", "qs": "6.11.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
"safe-buffer": "5.2.1", "safe-buffer": "5.2.1",
"send": "0.18.0", "send": "0.18.0",
@ -30543,9 +30543,9 @@
} }
}, },
"fs-monkey": { "fs-monkey": {
"version": "1.0.3", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz",
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==",
"dev": true "dev": true
}, },
"fs-readdir-recursive": { "fs-readdir-recursive": {
@ -33149,16 +33149,16 @@
"media-typer": { "media-typer": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"dev": true "dev": true
}, },
"memfs": { "memfs": {
"version": "3.4.1", "version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz", "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==", "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"dev": true, "dev": true,
"requires": { "requires": {
"fs-monkey": "1.0.3" "fs-monkey": "^1.0.4"
} }
}, },
"memoize-one": { "memoize-one": {
@ -34717,9 +34717,9 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
}, },
"qs": { "qs": {
"version": "6.10.3", "version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"side-channel": "^1.0.4" "side-channel": "^1.0.4"
@ -34787,9 +34787,9 @@
"dev": true "dev": true
}, },
"raw-body": { "raw-body": {
"version": "2.5.1", "version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dev": true, "dev": true,
"requires": { "requires": {
"bytes": "3.1.2", "bytes": "3.1.2",
@ -37856,13 +37856,13 @@
} }
}, },
"webpack-dev-middleware": { "webpack-dev-middleware": {
"version": "5.3.1", "version": "5.3.4",
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==", "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"colorette": "^2.0.10", "colorette": "^2.0.10",
"memfs": "^3.4.1", "memfs": "^3.4.3",
"mime-types": "^2.1.31", "mime-types": "^2.1.31",
"range-parser": "^1.2.1", "range-parser": "^1.2.1",
"schema-utils": "^4.0.0" "schema-utils": "^4.0.0"

View File

@ -1,6 +1,6 @@
import { ndApiClient } from '/@/renderer/api/navidrome/navidrome-api'; import { ndApiClient } from '/@/renderer/api/navidrome/navidrome-api';
import { ndNormalize } from '/@/renderer/api/navidrome/navidrome-normalize'; import { ndNormalize } from '/@/renderer/api/navidrome/navidrome-normalize';
import { NavidromeExtensions, ndType } from '/@/renderer/api/navidrome/navidrome-types'; import { ndType } from '/@/renderer/api/navidrome/navidrome-types';
import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api'; import { ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
import semverCoerce from 'semver/functions/coerce'; import semverCoerce from 'semver/functions/coerce';
import semverGte from 'semver/functions/gte'; import semverGte from 'semver/functions/gte';
@ -546,7 +546,7 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
const features: ServerFeatures = { const features: ServerFeatures = {
lyricsMultipleStructured: !!navidromeFeatures[SubsonicExtensions.SONG_LYRICS], lyricsMultipleStructured: !!navidromeFeatures[SubsonicExtensions.SONG_LYRICS],
playlistsSmart: !!navidromeFeatures[NavidromeExtensions.SMART_PLAYLISTS], playlistsSmart: !!navidromeFeatures[ServerFeature.PLAYLISTS_SMART],
sharingAlbumSong: !!navidromeFeatures[ServerFeature.SHARING_ALBUM_SONG], sharingAlbumSong: !!navidromeFeatures[ServerFeature.SHARING_ALBUM_SONG],
}; };

View File

@ -14,6 +14,7 @@ import { Badge } from '/@/renderer/components/badge';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { usePlayQueueAdd } from '/@/renderer/features/player/hooks/use-playqueue-add'; import { usePlayQueueAdd } from '/@/renderer/features/player/hooks/use-playqueue-add';
import { Play } from '/@/renderer/types'; import { Play } from '/@/renderer/types';
import { usePlayButtonBehavior } from '/@/renderer/store';
const Carousel = styled(motion.div)` const Carousel = styled(motion.div)`
position: relative; position: relative;
@ -114,6 +115,7 @@ export const FeatureCarousel = ({ data }: FeatureCarouselProps) => {
const handlePlayQueueAdd = usePlayQueueAdd(); const handlePlayQueueAdd = usePlayQueueAdd();
const [itemIndex, setItemIndex] = useState(0); const [itemIndex, setItemIndex] = useState(0);
const [direction, setDirection] = useState(0); const [direction, setDirection] = useState(0);
const playType = usePlayButtonBehavior();
const currentItem = data?.[itemIndex]; const currentItem = data?.[itemIndex];
@ -222,11 +224,18 @@ export const FeatureCarousel = ({ data }: FeatureCarouselProps) => {
id: [currentItem.id], id: [currentItem.id],
type: LibraryItem.ALBUM, type: LibraryItem.ALBUM,
}, },
playType: Play.NOW, playType,
}); });
}} }}
> >
{t('player.play', { postProcess: 'titleCase' })} {t(
playType === Play.NOW
? 'player.play'
: playType === Play.NEXT
? 'player.addNext'
: 'player.addLast',
{ postProcess: 'titleCase' },
)}
</Button> </Button>
<Group spacing="sm"> <Group spacing="sm">
<Button <Button

View File

@ -1,6 +1,6 @@
import { Center, Group, Stack } from '@mantine/core'; import { Center, Group, Stack } from '@mantine/core';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { RiCheckFill } from 'react-icons/ri'; import { RiCheckFill, RiEdit2Line, RiHome4Line } from 'react-icons/ri';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Button, PageHeader, Text } from '/@/renderer/components'; import { Button, PageHeader, Text } from '/@/renderer/components';
import { ActionRequiredContainer } from '/@/renderer/features/action-required/components/action-required-container'; import { ActionRequiredContainer } from '/@/renderer/features/action-required/components/action-required-container';
@ -9,6 +9,8 @@ import { ServerRequired } from '/@/renderer/features/action-required/components/
import { AnimatedPage } from '/@/renderer/features/shared'; import { AnimatedPage } from '/@/renderer/features/shared';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer } from '/@/renderer/store'; import { useCurrentServer } from '/@/renderer/store';
import { openModal } from '@mantine/modals';
import { ServerList } from '/@/renderer/features/servers';
const ActionRequiredRoute = () => { const ActionRequiredRoute = () => {
const { t } = useTranslation(); const { t } = useTranslation();
@ -32,6 +34,13 @@ const ActionRequiredRoute = () => {
const canReturnHome = checks.every((c) => c.valid); const canReturnHome = checks.every((c) => c.valid);
const displayedCheck = checks.find((c) => !c.valid); const displayedCheck = checks.find((c) => !c.valid);
const handleManageServersModal = () => {
openModal({
children: <ServerList />,
title: 'Manage Servers',
});
};
return ( return (
<AnimatedPage> <AnimatedPage>
<PageHeader /> <PageHeader />
@ -63,6 +72,7 @@ const ActionRequiredRoute = () => {
<Button <Button
component={Link} component={Link}
disabled={!canReturnHome} disabled={!canReturnHome}
leftIcon={<RiHome4Line />}
to={AppRoute.HOME} to={AppRoute.HOME}
variant="filled" variant="filled"
> >
@ -70,6 +80,19 @@ const ActionRequiredRoute = () => {
</Button> </Button>
</> </>
)} )}
<Group
noWrap
position="center"
>
<Button
fullWidth
leftIcon={<RiEdit2Line />}
variant="filled"
onClick={handleManageServersModal}
>
{t('page.appMenu.manageServers', { postProcess: 'sentenceCase' })}
</Button>
</Group>
</Stack> </Stack>
</Stack> </Stack>
</Center> </Center>

View File

@ -104,7 +104,7 @@ export const useSongLyricsBySong = (
}) })
.catch(console.error); .catch(console.error);
if (subsonicLyrics) { if (subsonicLyrics?.length) {
return subsonicLyrics; return subsonicLyrics;
} }
} else if (hasFeature(server, ServerFeature.LYRICS_SINGLE_STRUCTURED)) { } else if (hasFeature(server, ServerFeature.LYRICS_SINGLE_STRUCTURED)) {

View File

@ -311,7 +311,6 @@ export const useCenterControls = (args: { playersRef: any }) => {
const playerData = next(); const playerData = next();
mprisUpdateSong({ song: playerData.current.song, status: PlayerStatus.PLAYING }); mprisUpdateSong({ song: playerData.current.song, status: PlayerStatus.PLAYING });
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData);
mpvPlayer!.next();
}, },
web: () => { web: () => {
const playerData = next(); const playerData = next();
@ -324,8 +323,7 @@ export const useCenterControls = (args: { playersRef: any }) => {
if (isLastTrack) { if (isLastTrack) {
const playerData = setCurrentIndex(0); const playerData = setCurrentIndex(0);
mprisUpdateSong({ song: playerData.current.song, status: PlayerStatus.PAUSED }); mprisUpdateSong({ song: playerData.current.song, status: PlayerStatus.PAUSED });
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData, true);
mpvPlayer!.pause();
pause(); pause();
} else { } else {
const playerData = next(); const playerData = next();
@ -334,7 +332,6 @@ export const useCenterControls = (args: { playersRef: any }) => {
status: PlayerStatus.PLAYING, status: PlayerStatus.PLAYING,
}); });
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData);
mpvPlayer!.next();
} }
}, },
web: () => { web: () => {
@ -359,10 +356,14 @@ export const useCenterControls = (args: { playersRef: any }) => {
const handleRepeatOne = { const handleRepeatOne = {
local: () => { local: () => {
if (!isLastTrack) {
const playerData = next(); const playerData = next();
mprisUpdateSong({ song: playerData.current.song, status: PlayerStatus.PLAYING }); mprisUpdateSong({
song: playerData.current.song,
status: PlayerStatus.PLAYING,
});
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData);
mpvPlayer!.next(); }
}, },
web: () => { web: () => {
if (!isLastTrack) { if (!isLastTrack) {
@ -429,7 +430,6 @@ export const useCenterControls = (args: { playersRef: any }) => {
status: PlayerStatus.PLAYING, status: PlayerStatus.PLAYING,
}); });
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData);
mpvPlayer!.previous();
} else { } else {
const playerData = setCurrentIndex(queue.length - 1); const playerData = setCurrentIndex(queue.length - 1);
mprisUpdateSong({ mprisUpdateSong({
@ -437,7 +437,6 @@ export const useCenterControls = (args: { playersRef: any }) => {
status: PlayerStatus.PLAYING, status: PlayerStatus.PLAYING,
}); });
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData);
mpvPlayer!.previous();
} }
}, },
web: () => { web: () => {
@ -461,13 +460,19 @@ export const useCenterControls = (args: { playersRef: any }) => {
const handleRepeatNone = { const handleRepeatNone = {
local: () => { local: () => {
if (isFirstTrack) {
const playerData = setCurrentIndex(0);
mprisUpdateSong({ song: playerData.current.song, status: PlayerStatus.PAUSED });
mpvPlayer!.setQueue(playerData, true);
pause();
} else {
const playerData = previous(); const playerData = previous();
remote?.updateSong({ remote?.updateSong({
currentTime: usePlayerStore.getState().current.time, currentTime: usePlayerStore.getState().current.time,
song: playerData.current.song, song: playerData.current.song,
}); });
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData);
mpvPlayer!.previous(); }
}, },
web: () => { web: () => {
if (isFirstTrack) { if (isFirstTrack) {
@ -487,17 +492,12 @@ export const useCenterControls = (args: { playersRef: any }) => {
const handleRepeatOne = { const handleRepeatOne = {
local: () => { local: () => {
if (!isFirstTrack) {
const playerData = previous(); const playerData = previous();
mprisUpdateSong({ mprisUpdateSong({
song: playerData.current.song, song: playerData.current.song,
status: PlayerStatus.PLAYING, status: PlayerStatus.PLAYING,
}); });
mpvPlayer!.setQueue(playerData); mpvPlayer!.setQueue(playerData);
mpvPlayer!.previous();
} else {
mpvPlayer!.stop();
}
}, },
web: () => { web: () => {
const playerData = previous(); const playerData = previous();

View File

@ -76,7 +76,7 @@ export const UpdatePlaylistForm = ({ users, query, body, onCancel }: UpdatePlayl
}); });
const isPublicDisplayed = server?.type === ServerType.NAVIDROME; const isPublicDisplayed = server?.type === ServerType.NAVIDROME;
const isOwnerDisplayed = server?.type === ServerType.NAVIDROME; const isOwnerDisplayed = server?.type === ServerType.NAVIDROME && userList;
const isSubmitDisabled = !form.values.name || mutation.isLoading; const isSubmitDisabled = !form.values.name || mutation.isLoading;
return ( return (
@ -154,11 +154,17 @@ export const openUpdatePlaylistModal = async (args: {
const users = const users =
server?.type === ServerType.NAVIDROME server?.type === ServerType.NAVIDROME
? await queryClient.fetchQuery({ ? await queryClient
.fetchQuery({
queryFn: ({ signal }) => queryFn: ({ signal }) =>
api.controller.getUserList({ apiClientProps: { server, signal }, query }), api.controller.getUserList({ apiClientProps: { server, signal }, query }),
queryKey: queryKeys.users.list(server?.id || '', query), queryKey: queryKeys.users.list(server?.id || '', query),
}) })
.catch((error) => {
// This eror most likely happens if the user is not an admin
console.error(error);
return null;
})
: null; : null;
openModal({ openModal({

View File

@ -15,7 +15,7 @@ import {
SONG_CONTEXT_MENU_ITEMS, SONG_CONTEXT_MENU_ITEMS,
} from '/@/renderer/features/context-menu/context-menu-items'; } from '/@/renderer/features/context-menu/context-menu-items';
import { usePlayQueueAdd } from '/@/renderer/features/player'; import { usePlayQueueAdd } from '/@/renderer/features/player';
import { useCurrentServer, usePlayButtonBehavior } from '/@/renderer/store'; import { useCurrentServer, useListStoreByKey, usePlayButtonBehavior } from '/@/renderer/store';
interface SearchContentProps { interface SearchContentProps {
tableRef: MutableRefObject<AgGridReactType | null>; tableRef: MutableRefObject<AgGridReactType | null>;
@ -27,6 +27,10 @@ export const SearchContent = ({ tableRef }: SearchContentProps) => {
const { itemType } = useParams() as { itemType: LibraryItem }; const { itemType } = useParams() as { itemType: LibraryItem };
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const pageKey = itemType; const pageKey = itemType;
const { filter } = useListStoreByKey({
filter: { searchTerm: searchParams.get('query') || '' },
key: itemType,
});
const handlePlayQueueAdd = usePlayQueueAdd(); const handlePlayQueueAdd = usePlayQueueAdd();
const playButtonBehavior = usePlayButtonBehavior(); const playButtonBehavior = usePlayButtonBehavior();
@ -59,22 +63,26 @@ export const SearchContent = ({ tableRef }: SearchContentProps) => {
break; break;
case LibraryItem.SONG: case LibraryItem.SONG:
handlePlayQueueAdd?.({ handlePlayQueueAdd?.({
byData: [e.data], byItemType: {
id: [],
type: LibraryItem.SONG,
},
initialSongId: e.data.id,
playType: playButtonBehavior, playType: playButtonBehavior,
query: {
startIndex: 0,
...filter,
},
}); });
break; break;
} }
}; };
const customFilters = {
searchTerm: searchParams.get('query') || '',
};
const { rowClassRules } = useCurrentSongRowStyles({ tableRef }); const { rowClassRules } = useCurrentSongRowStyles({ tableRef });
const tableProps = useVirtualTable({ const tableProps = useVirtualTable({
contextMenu: contextMenuItems(), contextMenu: contextMenuItems(),
customFilters, customFilters: filter,
itemType, itemType,
pageKey, pageKey,
server, server,

View File

@ -11,6 +11,7 @@ import { FilterBar, LibraryHeaderBar } from '/@/renderer/features/shared';
import { useContainerQuery } from '/@/renderer/hooks'; import { useContainerQuery } from '/@/renderer/hooks';
import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh'; import { useListFilterRefresh } from '/@/renderer/hooks/use-list-filter-refresh';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { useListStoreByKey } from '/@/renderer/store';
interface SearchHeaderProps { interface SearchHeaderProps {
navigationId: string; navigationId: string;
@ -23,6 +24,7 @@ export const SearchHeader = ({ tableRef, navigationId }: SearchHeaderProps) => {
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
const cq = useContainerQuery(); const cq = useContainerQuery();
const server = useCurrentServer(); const server = useCurrentServer();
const { filter } = useListStoreByKey({ key: itemType });
const { handleRefreshTable } = useListFilterRefresh({ const { handleRefreshTable } = useListFilterRefresh({
itemType, itemType,
@ -30,9 +32,8 @@ export const SearchHeader = ({ tableRef, navigationId }: SearchHeaderProps) => {
}); });
const handleSearch = debounce((e: ChangeEvent<HTMLInputElement>) => { const handleSearch = debounce((e: ChangeEvent<HTMLInputElement>) => {
if (!e.target.value) return;
setSearchParams({ query: e.target.value }, { replace: true, state: { navigationId } }); setSearchParams({ query: e.target.value }, { replace: true, state: { navigationId } });
handleRefreshTable(tableRef, { searchTerm: e.target.value }); handleRefreshTable(tableRef, { ...filter, searchTerm: e.target.value });
}, 200); }, 200);
return ( return (

View File

@ -1,6 +1,6 @@
import { ChangeEvent } from 'react'; import { ChangeEvent } from 'react';
import { Divider, Group, Stack } from '@mantine/core'; import { Divider, Group, Stack } from '@mantine/core';
import { Accordion, Button, ContextModalVars, Switch } from '/@/renderer/components'; import { Accordion, Button, ContextModalVars, Switch, Text } from '/@/renderer/components';
import { useLocalStorage } from '@mantine/hooks'; import { useLocalStorage } from '@mantine/hooks';
import { openContextModal } from '@mantine/modals'; import { openContextModal } from '@mantine/modals';
import isElectron from 'is-electron'; import isElectron from 'is-electron';
@ -8,13 +8,14 @@ import { useTranslation } from 'react-i18next';
import { RiAddFill, RiServerFill } from 'react-icons/ri'; import { RiAddFill, RiServerFill } from 'react-icons/ri';
import { AddServerForm } from '/@/renderer/features/servers/components/add-server-form'; import { AddServerForm } from '/@/renderer/features/servers/components/add-server-form';
import { ServerListItem } from '/@/renderer/features/servers/components/server-list-item'; import { ServerListItem } from '/@/renderer/features/servers/components/server-list-item';
import { useServerList } from '/@/renderer/store'; import { useCurrentServer, useServerList } from '/@/renderer/store';
import { titleCase } from '/@/renderer/utils'; import { titleCase } from '/@/renderer/utils';
const localSettings = isElectron() ? window.electron.localSettings : null; const localSettings = isElectron() ? window.electron.localSettings : null;
export const ServerList = () => { export const ServerList = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const currentServer = useCurrentServer();
const serverListQuery = useServerList(); const serverListQuery = useServerList();
const handleAddServerModal = () => { const handleAddServerModal = () => {
@ -90,7 +91,9 @@ export const ServerList = () => {
> >
<Accordion.Control icon={<RiServerFill size={15} />}> <Accordion.Control icon={<RiServerFill size={15} />}>
<Group position="apart"> <Group position="apart">
<Text weight={server.id === currentServer?.id ? 800 : 400}>
{titleCase(server?.type)} - {server?.name} {titleCase(server?.type)} - {server?.name}
</Text>
</Group> </Group>
</Accordion.Control> </Accordion.Control>
<Accordion.Panel> <Accordion.Panel>

View File

@ -4,6 +4,7 @@ import { AuthState, ServerListItem, ServerType } from '/@/renderer/types';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { SongListSort, SortOrder } from '/@/renderer/api/types'; import { SongListSort, SortOrder } from '/@/renderer/api/types';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { toast } from '/@/renderer/components';
export const useServerAuthenticated = () => { export const useServerAuthenticated = () => {
const priorServerId = useRef<string>(); const priorServerId = useRef<string>();
@ -29,6 +30,7 @@ export const useServerAuthenticated = () => {
setReady(AuthState.VALID); setReady(AuthState.VALID);
} catch (error) { } catch (error) {
toast.error({ message: (error as Error).message });
setReady(AuthState.INVALID); setReady(AuthState.INVALID);
} }
}, []); }, []);

View File

@ -4,6 +4,8 @@ body[data-theme='defaultLight'] {
--main-bg-transparent: 255, 255, 255; --main-bg-transparent: 255, 255, 255;
--main-fg: rgb(25, 25, 25); --main-fg: rgb(25, 25, 25);
--main-fg-secondary: rgb(80, 80, 80); --main-fg-secondary: rgb(80, 80, 80);
--window-bar-bg: rgb(255, 255, 255);
--window-bar-fg: rgb(16, 16, 16);
--titlebar-fg: rgb(25, 25, 25); --titlebar-fg: rgb(25, 25, 25);
--titlebar-bg: rgb(240, 241, 242); --titlebar-bg: rgb(240, 241, 242);
--titlebar-controls-bg: rgba(0, 0, 0, 0%); --titlebar-controls-bg: rgba(0, 0, 0, 0%);