mirror of
https://github.com/qier222/YesPlayMusic.git
synced 2025-01-20 03:32:44 +08:00
feat: updates
This commit is contained in:
parent
fc1c25f404
commit
7b6579e068
16
package.json
16
package.json
|
@ -25,7 +25,10 @@
|
|||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"test:types": "tsc --noEmit --project src/renderer/tsconfig.json",
|
||||
"test:types": "npm run test:types-renderer && npm run test:types-main && npm run test:types-shared",
|
||||
"test:types-renderer": "tsc --noEmit --project src/renderer/tsconfig.json",
|
||||
"test:types-main": "tsc --noEmit --project src/main/tsconfig.json",
|
||||
"test:types-shared": "tsc --noEmit --project src/shared/tsconfig.json",
|
||||
"eslint": "eslint --ext .ts,.js ./",
|
||||
"prettier": "prettier --write './**/*.{ts,js,tsx,jsx}'"
|
||||
},
|
||||
|
@ -35,7 +38,7 @@
|
|||
"dependencies": {
|
||||
"@sentry/node": "^6.19.6",
|
||||
"@sentry/tracing": "^6.19.6",
|
||||
"NeteaseCloudMusicApi": "^4.5.11",
|
||||
"NeteaseCloudMusicApi": "^4.5.12",
|
||||
"better-sqlite3": "7.5.1",
|
||||
"change-case": "^4.1.2",
|
||||
"cookie-parser": "^1.4.6",
|
||||
|
@ -46,6 +49,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@sentry/react": "^6.19.6",
|
||||
"@testing-library/react": "^13.1.0",
|
||||
"@types/better-sqlite3": "^7.5.0",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/express": "^4.17.13",
|
||||
|
@ -56,7 +60,7 @@
|
|||
"@types/md5": "^2.3.2",
|
||||
"@types/qrcode": "^1.4.2",
|
||||
"@types/react": "^18.0.5",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.19.0",
|
||||
"@typescript-eslint/parser": "^5.19.0",
|
||||
"@vitejs/plugin-react": "^1.3.1",
|
||||
|
@ -69,13 +73,13 @@
|
|||
"colord": "^2.9.2",
|
||||
"concurrently": "^7.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "^1.11.0",
|
||||
"dayjs": "^1.11.1",
|
||||
"dotenv": "^16.0.0",
|
||||
"electron": "^18.0.3",
|
||||
"electron": "^18.0.4",
|
||||
"electron-builder": "^23.0.3",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-rebuild": "^3.2.7",
|
||||
"electron-releases": "^3.985.0",
|
||||
"electron-releases": "^3.987.0",
|
||||
"esbuild": "^0.14.36",
|
||||
"eslint": "^8.13.0",
|
||||
"eslint-plugin-react": "^7.29.4",
|
||||
|
|
206
pnpm-lock.yaml
206
pnpm-lock.yaml
|
@ -4,6 +4,7 @@ specifiers:
|
|||
'@sentry/node': ^6.19.6
|
||||
'@sentry/react': ^6.19.6
|
||||
'@sentry/tracing': ^6.19.6
|
||||
'@testing-library/react': ^13.1.0
|
||||
'@types/better-sqlite3': ^7.5.0
|
||||
'@types/cookie-parser': ^1.4.2
|
||||
'@types/express': ^4.17.13
|
||||
|
@ -14,12 +15,12 @@ specifiers:
|
|||
'@types/md5': ^2.3.2
|
||||
'@types/qrcode': ^1.4.2
|
||||
'@types/react': ^18.0.5
|
||||
'@types/react-dom': ^18.0.0
|
||||
'@types/react-dom': ^18.0.1
|
||||
'@typescript-eslint/eslint-plugin': ^5.19.0
|
||||
'@typescript-eslint/parser': ^5.19.0
|
||||
'@vitejs/plugin-react': ^1.3.1
|
||||
'@vitest/ui': ^0.9.3
|
||||
NeteaseCloudMusicApi: ^4.5.11
|
||||
NeteaseCloudMusicApi: ^4.5.12
|
||||
autoprefixer: ^10.4.4
|
||||
axios: ^0.26.1
|
||||
better-sqlite3: 7.5.1
|
||||
|
@ -31,15 +32,14 @@ specifiers:
|
|||
concurrently: ^7.1.0
|
||||
cookie-parser: ^1.4.6
|
||||
cross-env: ^7.0.3
|
||||
csstype: ^3.0.11
|
||||
dayjs: ^1.11.0
|
||||
dayjs: ^1.11.1
|
||||
dotenv: ^16.0.0
|
||||
electron: ^18.0.3
|
||||
electron: ^18.0.4
|
||||
electron-builder: ^23.0.3
|
||||
electron-devtools-installer: ^3.2.0
|
||||
electron-log: ^4.4.6
|
||||
electron-rebuild: ^3.2.7
|
||||
electron-releases: ^3.985.0
|
||||
electron-releases: ^3.987.0
|
||||
electron-store: ^8.0.1
|
||||
esbuild: ^0.14.36
|
||||
eslint: ^8.13.0
|
||||
|
@ -64,18 +64,17 @@ specifiers:
|
|||
qrcode: ^1.5.0
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
react-ga4: ^1.4.1
|
||||
react-hot-toast: ^2.2.0
|
||||
react-query: ^3.34.19
|
||||
react-router-dom: ^6.3.0
|
||||
react-use: ^17.3.2
|
||||
rollup: ^2.70.1
|
||||
rollup-plugin-visualizer: ^5.6.0
|
||||
sass: ^1.50.0
|
||||
tailwindcss: ^3.0.24
|
||||
typescript: ^4.6.3
|
||||
unplugin-auto-import: ^0.7.1
|
||||
valtio: ^1.5.2
|
||||
valtio-persist: ^1.0.2
|
||||
vite: ^2.9.5
|
||||
vite-plugin-svg-icons: ^2.0.1
|
||||
vitest: ^0.9.3
|
||||
|
@ -84,7 +83,7 @@ specifiers:
|
|||
dependencies:
|
||||
'@sentry/node': 6.19.6
|
||||
'@sentry/tracing': 6.19.6
|
||||
NeteaseCloudMusicApi: 4.5.11
|
||||
NeteaseCloudMusicApi: 4.5.12
|
||||
better-sqlite3: 7.5.1
|
||||
change-case: 4.1.2
|
||||
cookie-parser: 1.4.6
|
||||
|
@ -95,6 +94,7 @@ dependencies:
|
|||
|
||||
devDependencies:
|
||||
'@sentry/react': 6.19.6_react@18.0.0
|
||||
'@testing-library/react': 13.1.0_react-dom@18.0.0+react@18.0.0
|
||||
'@types/better-sqlite3': 7.5.0
|
||||
'@types/cookie-parser': 1.4.2
|
||||
'@types/express': 4.17.13
|
||||
|
@ -105,7 +105,7 @@ devDependencies:
|
|||
'@types/md5': 2.3.2
|
||||
'@types/qrcode': 1.4.2
|
||||
'@types/react': 18.0.5
|
||||
'@types/react-dom': 18.0.0
|
||||
'@types/react-dom': 18.0.1
|
||||
'@typescript-eslint/eslint-plugin': 5.19.0_f34adc8488d2e4f014fe61432d70cbf2
|
||||
'@typescript-eslint/parser': 5.19.0_eslint@8.13.0+typescript@4.6.3
|
||||
'@vitejs/plugin-react': 1.3.1
|
||||
|
@ -118,14 +118,13 @@ devDependencies:
|
|||
colord: 2.9.2
|
||||
concurrently: 7.1.0
|
||||
cross-env: 7.0.3
|
||||
csstype: 3.0.11
|
||||
dayjs: 1.11.0
|
||||
dayjs: 1.11.1
|
||||
dotenv: 16.0.0
|
||||
electron: 18.0.3
|
||||
electron: 18.0.4
|
||||
electron-builder: 23.0.3
|
||||
electron-devtools-installer: 3.2.0
|
||||
electron-rebuild: 3.2.7
|
||||
electron-releases: 3.985.0
|
||||
electron-releases: 3.987.0
|
||||
esbuild: 0.14.36
|
||||
eslint: 8.13.0
|
||||
eslint-plugin-react: 7.29.4_eslint@8.13.0
|
||||
|
@ -147,18 +146,17 @@ devDependencies:
|
|||
qrcode: 1.5.0
|
||||
react: 18.0.0
|
||||
react-dom: 18.0.0_react@18.0.0
|
||||
react-hot-toast: 2.2.0_aee3b59847029cfc9aee5217330a3daf
|
||||
react-ga4: 1.4.1
|
||||
react-hot-toast: 2.2.0_react-dom@18.0.0+react@18.0.0
|
||||
react-query: 3.34.19_react-dom@18.0.0+react@18.0.0
|
||||
react-router-dom: 6.3.0_react-dom@18.0.0+react@18.0.0
|
||||
react-use: 17.3.2_react-dom@18.0.0+react@18.0.0
|
||||
rollup: 2.70.1
|
||||
rollup-plugin-visualizer: 5.6.0_rollup@2.70.1
|
||||
rollup-plugin-visualizer: 5.6.0
|
||||
sass: 1.50.0
|
||||
tailwindcss: 3.0.24
|
||||
typescript: 4.6.3
|
||||
unplugin-auto-import: 0.7.1_05062056c3506028f60dc849695a4e6b
|
||||
unplugin-auto-import: 0.7.1_esbuild@0.14.36+vite@2.9.5
|
||||
valtio: 1.5.2_react@18.0.0+vite@2.9.5
|
||||
valtio-persist: 1.0.2_valtio@1.5.2
|
||||
vite: 2.9.5_sass@1.50.0
|
||||
vite-plugin-svg-icons: 2.0.1_vite@2.9.5
|
||||
vitest: 0.9.3_ac1eaec0e6cd6e44c577f894fa1b602e
|
||||
|
@ -767,6 +765,34 @@ packages:
|
|||
defer-to-connect: 2.0.1
|
||||
dev: true
|
||||
|
||||
/@testing-library/dom/8.13.0:
|
||||
resolution: {integrity: sha512-9VHgfIatKNXQNaZTtLnalIy0jNZzY35a4S3oi08YAt9Hv1VsfZ/DfA45lM8D/UhtHBGJ4/lGwp0PZkVndRkoOQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.16.7
|
||||
'@babel/runtime': 7.17.8
|
||||
'@types/aria-query': 4.2.2
|
||||
aria-query: 5.0.0
|
||||
chalk: 4.1.2
|
||||
dom-accessibility-api: 0.5.13
|
||||
lz-string: 1.4.4
|
||||
pretty-format: 27.5.1
|
||||
dev: true
|
||||
|
||||
/@testing-library/react/13.1.0_react-dom@18.0.0+react@18.0.0:
|
||||
resolution: {integrity: sha512-neStnDZdhkvZNNmPhhhi8+BXg3YCvjNmd8yGdr44VLVcFUDPForwokJWpDRCh3DvuX/M37Pt94fLwkM7aNut/A==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
dependencies:
|
||||
'@babel/runtime': 7.17.8
|
||||
'@testing-library/dom': 8.13.0
|
||||
'@types/react-dom': 18.0.1
|
||||
react: 18.0.0
|
||||
react-dom: 18.0.0_react@18.0.0
|
||||
dev: true
|
||||
|
||||
/@tokenizer/token/0.3.0:
|
||||
resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
|
||||
|
||||
|
@ -784,6 +810,10 @@ packages:
|
|||
engines: {node: '>=10.13.0'}
|
||||
dev: true
|
||||
|
||||
/@types/aria-query/4.2.2:
|
||||
resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==}
|
||||
dev: true
|
||||
|
||||
/@types/better-sqlite3/7.5.0:
|
||||
resolution: {integrity: sha512-G9ZbMjydW2yj1AgiPlUtdgF3a1qNpLJLudc9ynJCeJByS3XFWpmT9LT+VSHrKHFbxb31CvtYwetLTOvG9zdxdg==}
|
||||
dependencies:
|
||||
|
@ -936,8 +966,8 @@ packages:
|
|||
resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==}
|
||||
dev: true
|
||||
|
||||
/@types/node/16.11.26:
|
||||
resolution: {integrity: sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==}
|
||||
/@types/node/16.11.27:
|
||||
resolution: {integrity: sha512-C1pD3kgLoZ56Uuy5lhfOxie4aZlA3UMGLX9rXteq4WitEZH6Rl80mwactt9QG0w0gLFlN/kLBTFnGXtDVWvWQw==}
|
||||
dev: true
|
||||
|
||||
/@types/node/17.0.23:
|
||||
|
@ -970,8 +1000,8 @@ packages:
|
|||
resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==}
|
||||
dev: true
|
||||
|
||||
/@types/react-dom/18.0.0:
|
||||
resolution: {integrity: sha512-49897Y0UiCGmxZqpC8Blrf6meL8QUla6eb+BBhn69dTXlmuOlzkfr7HHY/O8J25e1lTUMs+YYxSlVDAaGHCOLg==}
|
||||
/@types/react-dom/18.0.1:
|
||||
resolution: {integrity: sha512-jCwTXvHtRLiyVvKm9aEdHXs8rflVOGd5Sl913JZrPshfXjn8NYsTNZOz70bCsA31IR0TOqwi3ad+X4tSCBoMTw==}
|
||||
dependencies:
|
||||
'@types/react': 18.0.5
|
||||
dev: true
|
||||
|
@ -1174,8 +1204,8 @@ packages:
|
|||
resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==}
|
||||
dev: true
|
||||
|
||||
/NeteaseCloudMusicApi/4.5.11:
|
||||
resolution: {integrity: sha512-v/L3I5NA+tCTfD9Nkr0i3igTEHJcvHQeZXM0Sqw1iBCqN6z62qtKoTON9yNSEgIGoZrzFfMyqHloLkv1iBo5sQ==}
|
||||
/NeteaseCloudMusicApi/4.5.12:
|
||||
resolution: {integrity: sha512-tlATnWTyOVH6hAxiH2f+nhgFtWulC1xMTz/7VyRjP/axcoMWff2mEa2Bw9kNl+Wb2bUrP/1oC2GXF41oyWwAJQ==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
|
@ -1353,6 +1383,11 @@ packages:
|
|||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
|
||||
/ansi-styles/5.2.0:
|
||||
resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/anymatch/3.1.2:
|
||||
resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
@ -1424,6 +1459,11 @@ packages:
|
|||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
dev: true
|
||||
|
||||
/aria-query/5.0.0:
|
||||
resolution: {integrity: sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==}
|
||||
engines: {node: '>=6.0'}
|
||||
dev: true
|
||||
|
||||
/arr-diff/4.0.0:
|
||||
resolution: {integrity: sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -2523,8 +2563,8 @@ packages:
|
|||
engines: {node: '>=0.11'}
|
||||
dev: true
|
||||
|
||||
/dayjs/1.11.0:
|
||||
resolution: {integrity: sha512-JLC809s6Y948/FuCZPm5IX8rRhQwOiyMb2TfVVQEixG7P8Lm/gt5S7yoQZmC8x1UehI9Pb7sksEt4xx14m+7Ug==}
|
||||
/dayjs/1.11.1:
|
||||
resolution: {integrity: sha512-ER7EjqVAMkRRsxNCC5YqJ9d9VQYuWdGt7aiH2qA5R5wt8ZmWaP2dLUSIK6y/kVzLMlmh1Tvu5xUf4M/wdGJ5KA==}
|
||||
dev: true
|
||||
|
||||
/debounce-fn/4.0.0:
|
||||
|
@ -2617,6 +2657,15 @@ packages:
|
|||
object-keys: 1.1.1
|
||||
dev: true
|
||||
|
||||
/define-properties/1.1.4:
|
||||
resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
has-property-descriptors: 1.0.0
|
||||
object-keys: 1.1.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/define-property/0.2.5:
|
||||
resolution: {integrity: sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -2781,6 +2830,10 @@ packages:
|
|||
esutils: 2.0.3
|
||||
dev: true
|
||||
|
||||
/dom-accessibility-api/0.5.13:
|
||||
resolution: {integrity: sha512-R305kwb5CcMDIpSHUnLyIAp7SrSPBx6F0VfQFB3M75xVMHhXJJIdePYgbPPh1o57vCHNu5QztokWUPsLjWzFqw==}
|
||||
dev: true
|
||||
|
||||
/dom-serializer/0.2.2:
|
||||
resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
|
||||
dependencies:
|
||||
|
@ -2980,8 +3033,8 @@ packages:
|
|||
- supports-color
|
||||
dev: true
|
||||
|
||||
/electron-releases/3.985.0:
|
||||
resolution: {integrity: sha512-1HJzI4m6kS0aV45NWqs99eR7ekSAN7JIKsklsV+1eGfcRwwyP6O8c3458rL4vdvlhZ9+QnUM3B0S03+XV5M0Tg==}
|
||||
/electron-releases/3.987.0:
|
||||
resolution: {integrity: sha512-Rol/iOHhTdEiVqD5O9p4rLkNSlK82FFX3M99BrNvE7Gs52/mRWJYNdhPuY9d/Cs3jx2H8KJsB49Tfpm4TOqJHw==}
|
||||
dev: true
|
||||
|
||||
/electron-store/8.0.1:
|
||||
|
@ -2995,14 +3048,14 @@ packages:
|
|||
resolution: {integrity: sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg==}
|
||||
dev: true
|
||||
|
||||
/electron/18.0.3:
|
||||
resolution: {integrity: sha512-QRUZkGL8O/8CyDmTLSjBeRsZmGTPlPVeWnnpkdNqgHYYaOc/A881FKMiNzvQ9Cj0a+rUavDdwBUfUL82U3Ay7w==}
|
||||
/electron/18.0.4:
|
||||
resolution: {integrity: sha512-xfsozNpFr3WzeM1EFlw2qqiqXbCrgQNBJJMlcC4/DUYVpkF8364SZenX7FFFA42NmwXiOEahkvvho/u7UrAcGg==}
|
||||
engines: {node: '>= 8.6'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@electron/get': 1.14.1
|
||||
'@types/node': 16.11.26
|
||||
'@types/node': 16.11.27
|
||||
extract-zip: 1.7.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -4040,7 +4093,7 @@ packages:
|
|||
es6-error: 4.1.1
|
||||
matcher: 3.0.0
|
||||
roarr: 2.15.4
|
||||
semver: 7.3.6
|
||||
semver: 7.3.7
|
||||
serialize-error: 7.0.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
@ -4080,7 +4133,7 @@ packages:
|
|||
resolution: {integrity: sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
dependencies:
|
||||
define-properties: 1.1.3
|
||||
define-properties: 1.1.4
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
|
@ -4096,12 +4149,10 @@ packages:
|
|||
slash: 3.0.0
|
||||
dev: true
|
||||
|
||||
/goober/2.1.8_csstype@3.0.11:
|
||||
/goober/2.1.8:
|
||||
resolution: {integrity: sha512-S0C85gCzcfFCMSdjD/CxyQMt1rbf2qEg6hmDzxk2FfD7+7Ogk55m8ZFUMtqNaZM4VVX/qaU9AzSORG+Gf4ZpAQ==}
|
||||
peerDependencies:
|
||||
csstype: ^3.0.10
|
||||
dependencies:
|
||||
csstype: 3.0.11
|
||||
dev: true
|
||||
|
||||
/got/11.8.3:
|
||||
|
@ -4174,6 +4225,13 @@ packages:
|
|||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/has-property-descriptors/1.0.0:
|
||||
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
||||
dependencies:
|
||||
get-intrinsic: 1.1.1
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/has-symbols/1.0.3:
|
||||
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
@ -4357,6 +4415,16 @@ packages:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
/https-proxy-agent/5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/humanize-ms/1.2.1:
|
||||
resolution: {integrity: sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=}
|
||||
dependencies:
|
||||
|
@ -4967,7 +5035,7 @@ packages:
|
|||
dependencies:
|
||||
universalify: 2.0.0
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.9
|
||||
graceful-fs: 4.2.10
|
||||
dev: true
|
||||
|
||||
/jsx-ast-utils/3.2.2:
|
||||
|
@ -5171,6 +5239,11 @@ packages:
|
|||
resolution: {integrity: sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=}
|
||||
dev: false
|
||||
|
||||
/lz-string/1.4.4:
|
||||
resolution: {integrity: sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/lzma-native/8.0.6:
|
||||
resolution: {integrity: sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
@ -5902,7 +5975,7 @@ packages:
|
|||
debug: 4.3.4
|
||||
get-uri: 3.0.2
|
||||
http-proxy-agent: 4.0.1
|
||||
https-proxy-agent: 5.0.0
|
||||
https-proxy-agent: 5.0.1
|
||||
pac-resolver: 5.0.0
|
||||
raw-body: 2.5.1
|
||||
socks-proxy-agent: 5.0.1
|
||||
|
@ -6230,6 +6303,15 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/pretty-format/27.5.1:
|
||||
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
|
||||
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
ansi-styles: 5.2.0
|
||||
react-is: 17.0.2
|
||||
dev: true
|
||||
|
||||
/process-nextick-args/2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
|
||||
|
@ -6372,14 +6454,18 @@ packages:
|
|||
scheduler: 0.21.0
|
||||
dev: true
|
||||
|
||||
/react-hot-toast/2.2.0_aee3b59847029cfc9aee5217330a3daf:
|
||||
/react-ga4/1.4.1:
|
||||
resolution: {integrity: sha512-ioBMEIxd4ePw4YtaloTUgqhQGqz5ebDdC4slEpLgy2sLx1LuZBC9iYCwDymTXzcntw6K1dHX183ulP32nNdG7w==}
|
||||
dev: true
|
||||
|
||||
/react-hot-toast/2.2.0_react-dom@18.0.0+react@18.0.0:
|
||||
resolution: {integrity: sha512-248rXw13uhf/6TNDVzagX+y7R8J183rp7MwUMNkcrBRyHj/jWOggfXTGlM8zAOuh701WyVW+eUaWG2LeSufX9g==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
react: '>=16'
|
||||
react-dom: '>=16'
|
||||
dependencies:
|
||||
goober: 2.1.8_csstype@3.0.11
|
||||
goober: 2.1.8
|
||||
react: 18.0.0
|
||||
react-dom: 18.0.0_react@18.0.0
|
||||
transitivePeerDependencies:
|
||||
|
@ -6390,6 +6476,10 @@ packages:
|
|||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
dev: true
|
||||
|
||||
/react-is/17.0.2:
|
||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||
dev: true
|
||||
|
||||
/react-query/3.34.19_react-dom@18.0.0+react@18.0.0:
|
||||
resolution: {integrity: sha512-JO0Ymi58WKmvnhgg6bGIrYIeKb64KsKaPWo8JcGnmK2jJxAs2XmMBzlP75ZepSU7CHzcsWtIIyhMrLbX3pb/3w==}
|
||||
peerDependencies:
|
||||
|
@ -6697,7 +6787,7 @@ packages:
|
|||
dev: true
|
||||
optional: true
|
||||
|
||||
/rollup-plugin-visualizer/5.6.0_rollup@2.70.1:
|
||||
/rollup-plugin-visualizer/5.6.0:
|
||||
resolution: {integrity: sha512-CKcc8GTUZjC+LsMytU8ocRr/cGZIfMR7+mdy4YnlyetlmIl/dM8BMnOEpD4JPIGt+ZVW7Db9ZtSsbgyeBH3uTA==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
|
@ -6706,13 +6796,12 @@ packages:
|
|||
dependencies:
|
||||
nanoid: 3.3.2
|
||||
open: 8.4.0
|
||||
rollup: 2.70.1
|
||||
source-map: 0.7.3
|
||||
yargs: 17.4.0
|
||||
dev: true
|
||||
|
||||
/rollup/2.70.1:
|
||||
resolution: {integrity: sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==}
|
||||
/rollup/2.70.2:
|
||||
resolution: {integrity: sha512-EitogNZnfku65I1DD5Mxe8JYRUCy0hkK5X84IlDtUs+O6JRMpRciXTzyCUuX11b5L5pvjH+OmFXiQ3XjabcXgg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
hasBin: true
|
||||
optionalDependencies:
|
||||
|
@ -6833,6 +6922,15 @@ packages:
|
|||
lru-cache: 7.8.1
|
||||
dev: true
|
||||
|
||||
/semver/7.3.7:
|
||||
resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
lru-cache: 6.0.0
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/send/0.17.2:
|
||||
resolution: {integrity: sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
@ -7715,7 +7813,7 @@ packages:
|
|||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/unplugin-auto-import/0.7.1_05062056c3506028f60dc849695a4e6b:
|
||||
/unplugin-auto-import/0.7.1_esbuild@0.14.36+vite@2.9.5:
|
||||
resolution: {integrity: sha512-9865OV9eP99PNxHR2mtTDExeN01m4M9boT5U2BtIwsU1wDRsaFIYWLwcCBEjvXzXfTTC2NNMskhHGVAMfL2WgA==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
|
@ -7729,7 +7827,7 @@ packages:
|
|||
local-pkg: 0.4.1
|
||||
magic-string: 0.26.1
|
||||
resolve: 1.22.0
|
||||
unplugin: 0.6.2_05062056c3506028f60dc849695a4e6b
|
||||
unplugin: 0.6.2_esbuild@0.14.36+vite@2.9.5
|
||||
transitivePeerDependencies:
|
||||
- esbuild
|
||||
- rollup
|
||||
|
@ -7737,7 +7835,7 @@ packages:
|
|||
- webpack
|
||||
dev: true
|
||||
|
||||
/unplugin/0.6.2_05062056c3506028f60dc849695a4e6b:
|
||||
/unplugin/0.6.2_esbuild@0.14.36+vite@2.9.5:
|
||||
resolution: {integrity: sha512-+QONc2uBFQbeo4x5mlJHjTKjR6pmuchMpGVrWhwdGFFMb4ttFZ4E9KqhOOrNcm3Q8NNyB1vJ4s5e36IZC7UWYw==}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.13'
|
||||
|
@ -7756,7 +7854,6 @@ packages:
|
|||
dependencies:
|
||||
chokidar: 3.5.3
|
||||
esbuild: 0.14.36
|
||||
rollup: 2.70.1
|
||||
vite: 2.9.5_sass@1.50.0
|
||||
webpack-sources: 3.2.3
|
||||
webpack-virtual-modules: 0.4.3
|
||||
|
@ -7880,15 +7977,6 @@ packages:
|
|||
source-map: 0.7.3
|
||||
dev: true
|
||||
|
||||
/valtio-persist/1.0.2_valtio@1.5.2:
|
||||
resolution: {integrity: sha512-OBVEUZTS1heiA5R3j8CPDuXIMmmjIvq/4CiO+pElXd7f7b7nR3vIH5qql35hXw/AkLdftqTUcVCNVf6yAJ1ypA==}
|
||||
peerDependencies:
|
||||
valtio: ^1.2.5
|
||||
dependencies:
|
||||
lodash: 4.17.21
|
||||
valtio: 1.5.2_react@18.0.0+vite@2.9.5
|
||||
dev: true
|
||||
|
||||
/valtio/1.5.2_react@18.0.0+vite@2.9.5:
|
||||
resolution: {integrity: sha512-4oqGO+7xSKZJJgLsfwRdzQxxy4hiOjiE0IZv0xoNNLtJQ+Y6mtWoEl0Y0JyUCrU/HBmY+0W/yL3lwjrpTBCJ/w==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
|
@ -7970,7 +8058,7 @@ packages:
|
|||
esbuild: 0.14.36
|
||||
postcss: 8.4.12
|
||||
resolve: 1.22.0
|
||||
rollup: 2.70.1
|
||||
rollup: 2.70.2
|
||||
sass: 1.50.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
export enum APIs {
|
||||
UserPlaylist = 'user/playlist',
|
||||
UserAccount = 'user/account',
|
||||
Personalized = 'personalized',
|
||||
RecommendResource = 'recommend/resource',
|
||||
Likelist = 'likelist',
|
||||
SongDetail = 'song/detail',
|
||||
SongUrl = 'song/url',
|
||||
Album = 'album',
|
||||
PlaylistDetail = 'playlist/detail',
|
||||
Artists = 'artists',
|
||||
ArtistAlbum = 'artist/album',
|
||||
Lyric = 'lyric',
|
||||
CoverColor = 'cover_color',
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
export enum IpcChannels {
|
||||
ClearAPICache = 'clear-api-cache',
|
||||
Minimize = 'minimize',
|
||||
MaximizeOrUnmaximize = 'maximize-or-unmaximize',
|
||||
Close = 'close',
|
||||
IsMaximized = 'is-maximized',
|
||||
GetApiCacheSync = 'get-api-cache-sync',
|
||||
DevDbExportJson = 'dev-db-export-json',
|
||||
CacheCoverColor = 'cache-cover-color',
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
import { db, Tables } from './db'
|
||||
import type { FetchTracksResponse } from '../renderer/api/track'
|
||||
import type { FetchTracksResponse } from '@/shared/api/Track'
|
||||
import { app } from 'electron'
|
||||
import { Request, Response } from 'express'
|
||||
import log from './log'
|
||||
import fs from 'fs'
|
||||
import * as musicMetadata from 'music-metadata'
|
||||
import { APIs } from './CacheAPIsName'
|
||||
import { APIs, APIsParams, APIsResponse } from '../shared/CacheAPIs'
|
||||
|
||||
class Cache {
|
||||
constructor() {
|
||||
|
@ -18,6 +18,8 @@ class Cache {
|
|||
case APIs.UserAccount:
|
||||
case APIs.Personalized:
|
||||
case APIs.RecommendResource:
|
||||
case APIs.UserAlbums:
|
||||
case APIs.UserArtists:
|
||||
case APIs.Likelist: {
|
||||
if (!data) return
|
||||
db.upsert(Tables.AccountData, {
|
||||
|
@ -27,7 +29,7 @@ class Cache {
|
|||
})
|
||||
break
|
||||
}
|
||||
case APIs.SongDetail: {
|
||||
case APIs.Track: {
|
||||
if (!data.songs) return
|
||||
const tracks = (data as FetchTracksResponse).songs.map(t => ({
|
||||
id: t.id,
|
||||
|
@ -47,7 +49,7 @@ class Cache {
|
|||
})
|
||||
break
|
||||
}
|
||||
case APIs.PlaylistDetail: {
|
||||
case APIs.Playlist: {
|
||||
if (!data.playlist) return
|
||||
db.upsert(Tables.Playlist, {
|
||||
id: data.playlist.id,
|
||||
|
@ -56,7 +58,7 @@ class Cache {
|
|||
})
|
||||
break
|
||||
}
|
||||
case APIs.Artists: {
|
||||
case APIs.Artist: {
|
||||
if (!data.artist) return
|
||||
db.upsert(Tables.Artist, {
|
||||
id: data.artist.id,
|
||||
|
@ -108,7 +110,7 @@ class Cache {
|
|||
}
|
||||
}
|
||||
|
||||
get(api: string, query: any): any {
|
||||
get<T extends keyof APIsParams>(api: T, params: any): any {
|
||||
switch (api) {
|
||||
case APIs.UserPlaylist:
|
||||
case APIs.UserAccount:
|
||||
|
@ -119,15 +121,13 @@ class Cache {
|
|||
if (data?.json) return JSON.parse(data.json)
|
||||
break
|
||||
}
|
||||
case APIs.SongDetail: {
|
||||
const ids: string[] = query?.ids.split(',')
|
||||
case APIs.Track: {
|
||||
const ids: number[] = params?.ids
|
||||
.split(',')
|
||||
.map((id: string) => Number(id))
|
||||
if (ids.length === 0) return
|
||||
|
||||
let isIDsValid = true
|
||||
ids.forEach(id => {
|
||||
if (id === '' || isNaN(Number(id))) isIDsValid = false
|
||||
})
|
||||
if (!isIDsValid) return
|
||||
if (ids.includes(NaN)) return
|
||||
|
||||
const tracksRaw = db.findMany(Tables.Track, ids)
|
||||
|
||||
|
@ -138,7 +138,6 @@ class Cache {
|
|||
const track = tracksRaw.find(t => t.id === Number(id)) as any
|
||||
return JSON.parse(track.json)
|
||||
})
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
songs: tracks,
|
||||
|
@ -146,8 +145,8 @@ class Cache {
|
|||
}
|
||||
}
|
||||
case APIs.Album: {
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const data = db.find(Tables.Album, query.id)
|
||||
if (isNaN(Number(params?.id))) return
|
||||
const data = db.find(Tables.Album, params.id)
|
||||
if (data?.json)
|
||||
return {
|
||||
resourceState: true,
|
||||
|
@ -157,22 +156,22 @@ class Cache {
|
|||
}
|
||||
break
|
||||
}
|
||||
case APIs.PlaylistDetail: {
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const data = db.find(Tables.Playlist, query.id)
|
||||
case APIs.Playlist: {
|
||||
if (isNaN(Number(params?.id))) return
|
||||
const data = db.find(Tables.Playlist, params.id)
|
||||
if (data?.json) return JSON.parse(data.json)
|
||||
break
|
||||
}
|
||||
case APIs.Artists: {
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const data = db.find(Tables.Artist, query.id)
|
||||
case APIs.Artist: {
|
||||
if (isNaN(Number(params?.id))) return
|
||||
const data = db.find(Tables.Artist, params.id)
|
||||
if (data?.json) return JSON.parse(data.json)
|
||||
break
|
||||
}
|
||||
case APIs.ArtistAlbum: {
|
||||
if (isNaN(Number(query?.id))) return
|
||||
if (isNaN(Number(params?.id))) return
|
||||
|
||||
const artistAlbumsRaw = db.find(Tables.ArtistAlbum, query.id)
|
||||
const artistAlbumsRaw = db.find(Tables.ArtistAlbum, params.id)
|
||||
if (!artistAlbumsRaw?.json) return
|
||||
const artistAlbums = JSON.parse(artistAlbumsRaw.json)
|
||||
|
||||
|
@ -186,21 +185,21 @@ class Cache {
|
|||
return artistAlbums
|
||||
}
|
||||
case APIs.Lyric: {
|
||||
if (isNaN(Number(query?.id))) return
|
||||
const data = db.find(Tables.Lyric, query.id)
|
||||
if (isNaN(Number(params?.id))) return
|
||||
const data = db.find(Tables.Lyric, params.id)
|
||||
if (data?.json) return JSON.parse(data.json)
|
||||
break
|
||||
}
|
||||
case APIs.CoverColor: {
|
||||
if (isNaN(Number(query?.id))) return
|
||||
return db.find(Tables.CoverColor, query.id)?.color
|
||||
if (isNaN(Number(params?.id))) return
|
||||
return db.find(Tables.CoverColor, params.id)?.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getForExpress(api: string, req: Request) {
|
||||
// Get track detail cache
|
||||
if (api === APIs.SongDetail) {
|
||||
if (api === APIs.Track) {
|
||||
const cache = this.get(api, req.query)
|
||||
if (cache) {
|
||||
log.debug(`[cache] Cache hit for ${req.path}`)
|
||||
|
|
|
@ -38,6 +38,7 @@ export interface TablesStructures {
|
|||
[Tables.Audio]: {
|
||||
id: number
|
||||
br: number
|
||||
type: 'mp3' | 'flac' | 'ogg' | 'wav' | 'm4a' | 'aac' | 'unknown'
|
||||
source: 'netease' | 'migu' | 'kuwo' | 'kugou' | 'youtube'
|
||||
url: string
|
||||
updatedAt: number
|
||||
|
@ -155,10 +156,7 @@ class DB {
|
|||
upsertMany(data)
|
||||
}
|
||||
|
||||
delete<T extends TableNames>(
|
||||
table: T,
|
||||
key: TablesStructures[T]['id']
|
||||
) {
|
||||
delete<T extends TableNames>(table: T, key: TablesStructures[T]['id']) {
|
||||
return this.sqlite.prepare(`DELETE FROM ${table} WHERE id = ?`).run(key)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,19 +18,6 @@ const isMac = process.platform === 'darwin'
|
|||
const isLinux = process.platform === 'linux'
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
|
||||
log.info('[index] Main process start')
|
||||
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (release().startsWith('6.1')) app.disableHardwareAcceleration()
|
||||
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
interface TypedElectronStore {
|
||||
window: {
|
||||
width: number
|
||||
|
@ -40,115 +27,138 @@ interface TypedElectronStore {
|
|||
}
|
||||
}
|
||||
|
||||
const store = new Store<TypedElectronStore>({
|
||||
defaults: {
|
||||
window: {
|
||||
width: 1440,
|
||||
height: 960,
|
||||
class Main {
|
||||
win: BrowserWindow | null = null
|
||||
store = new Store<TypedElectronStore>({
|
||||
defaults: {
|
||||
window: {
|
||||
width: 1440,
|
||||
height: 960,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
let win: BrowserWindow | null = null
|
||||
|
||||
async function createWindow() {
|
||||
// Create window
|
||||
|
||||
const options: BrowserWindowConstructorOptions = {
|
||||
title: 'Main window',
|
||||
webPreferences: {
|
||||
preload: join(__dirname, 'rendererPreload.js'),
|
||||
},
|
||||
width: store.get('window.width'),
|
||||
height: store.get('window.height'),
|
||||
minWidth: 1080,
|
||||
minHeight: 720,
|
||||
vibrancy: 'fullscreen-ui',
|
||||
titleBarStyle: 'hiddenInset',
|
||||
frame: !(isWindows || isLinux), // TODO: 适用于linux下独立的启用开关
|
||||
}
|
||||
if (store.get('window')) {
|
||||
options.x = store.get('window.x')
|
||||
options.y = store.get('window.y')
|
||||
}
|
||||
win = new BrowserWindow(options)
|
||||
|
||||
// Web server
|
||||
const url = `http://localhost:${process.env.ELECTRON_WEB_SERVER_PORT}`
|
||||
win.loadURL(url)
|
||||
|
||||
if (isDev) {
|
||||
win.webContents.openDevTools()
|
||||
}
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('https:')) shell.openExternal(url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
|
||||
// Save window position
|
||||
const saveBounds = () => {
|
||||
const bounds = win?.getBounds()
|
||||
if (bounds) {
|
||||
store.set('window', bounds)
|
||||
constructor() {
|
||||
log.info('[index] Main process start')
|
||||
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (release().startsWith('6.1')) app.disableHardwareAcceleration()
|
||||
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
|
||||
|
||||
// Make sure the app only run on one instance
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
log.info('[index] App ready')
|
||||
this.createWindow()
|
||||
this.handleAppEvents()
|
||||
this.handleWindowEvents()
|
||||
initIpcMain(this.win)
|
||||
this.initDevTools()
|
||||
})
|
||||
}
|
||||
win.on('resized', saveBounds)
|
||||
win.on('moved', saveBounds)
|
||||
}
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
log.info('[index] App ready')
|
||||
createWindow()
|
||||
handleWindowEvents()
|
||||
initIpcMain(win)
|
||||
initDevTools() {
|
||||
if (!isDev || !this.win) return
|
||||
|
||||
// Install devtool extension
|
||||
if (isDev) {
|
||||
// Install devtool extension
|
||||
const {
|
||||
default: installExtension,
|
||||
REACT_DEVELOPER_TOOLS,
|
||||
REDUX_DEVTOOLS,
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
} = require('electron-devtools-installer')
|
||||
installExtension(REACT_DEVELOPER_TOOLS.id).catch(err =>
|
||||
installExtension(REACT_DEVELOPER_TOOLS.id).catch((err: any) =>
|
||||
log.info('An error occurred: ', err)
|
||||
)
|
||||
installExtension(REDUX_DEVTOOLS.id).catch(err =>
|
||||
installExtension(REDUX_DEVTOOLS.id).catch((err: any) =>
|
||||
log.info('An error occurred: ', err)
|
||||
)
|
||||
|
||||
this.win.webContents.openDevTools()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
win = null
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
createWindow() {
|
||||
const options: BrowserWindowConstructorOptions = {
|
||||
title: 'YesPlayMusic',
|
||||
webPreferences: {
|
||||
preload: join(__dirname, 'rendererPreload.js'),
|
||||
},
|
||||
width: this.store.get('window.width'),
|
||||
height: this.store.get('window.height'),
|
||||
minWidth: 1080,
|
||||
minHeight: 720,
|
||||
vibrancy: 'fullscreen-ui',
|
||||
titleBarStyle: 'hiddenInset',
|
||||
frame: !(isWindows || isLinux), // TODO: 适用于linux下独立的启用开关
|
||||
}
|
||||
if (this.store.get('window')) {
|
||||
options.x = this.store.get('window.x')
|
||||
options.y = this.store.get('window.y')
|
||||
}
|
||||
this.win = new BrowserWindow(options)
|
||||
|
||||
app.on('second-instance', () => {
|
||||
if (win) {
|
||||
// Focus on the main window if the user tried to open another
|
||||
if (win.isMinimized()) win.restore()
|
||||
win.focus()
|
||||
// Web server
|
||||
const url = `http://localhost:${process.env.ELECTRON_WEB_SERVER_PORT}`
|
||||
this.win.loadURL(url)
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
this.win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('https:')) shell.openExternal(url)
|
||||
return { action: 'deny' }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length) {
|
||||
allWindows[0].focus()
|
||||
} else {
|
||||
createWindow()
|
||||
handleWindowEvents() {
|
||||
if (!this.win) return
|
||||
|
||||
// Window maximize and minimize
|
||||
this.win.on('maximize', () => {
|
||||
this.win && this.win.webContents.send('is-maximized', true)
|
||||
})
|
||||
|
||||
this.win.on('unmaximize', () => {
|
||||
this.win && this.win.webContents.send('is-maximized', false)
|
||||
})
|
||||
|
||||
// Save window position
|
||||
const saveBounds = () => {
|
||||
const bounds = this.win?.getBounds()
|
||||
if (bounds) {
|
||||
this.store.set('window', bounds)
|
||||
}
|
||||
}
|
||||
this.win.on('resized', saveBounds)
|
||||
this.win.on('moved', saveBounds)
|
||||
}
|
||||
})
|
||||
|
||||
const handleWindowEvents = () => {
|
||||
win?.on('maximize', () => {
|
||||
win?.webContents.send('is-maximized', true)
|
||||
})
|
||||
handleAppEvents() {
|
||||
app.on('window-all-closed', () => {
|
||||
this.win = null
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
win?.on('unmaximize', () => {
|
||||
win?.webContents.send('is-maximized', false)
|
||||
})
|
||||
app.on('second-instance', () => {
|
||||
if (!this.win) return
|
||||
// Focus on the main window if the user tried to open another
|
||||
if (this.win.isMinimized()) this.win.restore()
|
||||
this.win.focus()
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
const allWindows = BrowserWindow.getAllWindows()
|
||||
if (allWindows.length) {
|
||||
allWindows[0].focus()
|
||||
} else {
|
||||
this.createWindow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
new Main()
|
||||
|
|
|
@ -1,26 +1,33 @@
|
|||
import { BrowserWindow, ipcMain, app } from 'electron'
|
||||
import { db, Tables } from './db'
|
||||
import { IpcChannels } from './IpcChannelsName'
|
||||
import { IpcChannels, IpcChannelsParams } from '../shared/IpcChannels'
|
||||
import cache from './cache'
|
||||
import log from './log'
|
||||
import fs from 'fs'
|
||||
import { APIs } from './CacheAPIsName'
|
||||
import { APIs } from '../shared/CacheAPIs'
|
||||
|
||||
const on = <T extends keyof IpcChannelsParams>(
|
||||
channel: T,
|
||||
listener: (event: Electron.IpcMainEvent, params: IpcChannelsParams[T]) => void
|
||||
) => {
|
||||
ipcMain.on(channel, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理需要win对象的事件
|
||||
* @param {BrowserWindow} win
|
||||
*/
|
||||
export function initIpcMain(win: BrowserWindow | null) {
|
||||
ipcMain.on(IpcChannels.Minimize, () => {
|
||||
on(IpcChannels.Minimize, () => {
|
||||
win?.minimize()
|
||||
})
|
||||
|
||||
ipcMain.on(IpcChannels.MaximizeOrUnmaximize, () => {
|
||||
on(IpcChannels.MaximizeOrUnmaximize, () => {
|
||||
if (!win) return
|
||||
win.isMaximized() ? win.unmaximize() : win.maximize()
|
||||
})
|
||||
|
||||
ipcMain.on(IpcChannels.Close, () => {
|
||||
on(IpcChannels.Close, () => {
|
||||
app.exit()
|
||||
})
|
||||
}
|
||||
|
@ -28,7 +35,7 @@ export function initIpcMain(win: BrowserWindow | null) {
|
|||
/**
|
||||
* 清除API缓存
|
||||
*/
|
||||
ipcMain.on(IpcChannels.ClearAPICache, () => {
|
||||
on(IpcChannels.ClearAPICache, () => {
|
||||
db.truncate(Tables.Track)
|
||||
db.truncate(Tables.Album)
|
||||
db.truncate(Tables.Artist)
|
||||
|
@ -42,7 +49,7 @@ ipcMain.on(IpcChannels.ClearAPICache, () => {
|
|||
/**
|
||||
* Get API cache
|
||||
*/
|
||||
ipcMain.on(IpcChannels.GetApiCacheSync, (event, args) => {
|
||||
on(IpcChannels.GetApiCacheSync, (event, args) => {
|
||||
const { api, query } = args
|
||||
const data = cache.get(api, query)
|
||||
event.returnValue = data
|
||||
|
@ -51,8 +58,8 @@ ipcMain.on(IpcChannels.GetApiCacheSync, (event, args) => {
|
|||
/**
|
||||
* 缓存封面颜色
|
||||
*/
|
||||
ipcMain.on(IpcChannels.CacheCoverColor, (event, args) => {
|
||||
const { id, color } = args.query
|
||||
on(IpcChannels.CacheCoverColor, (event, args) => {
|
||||
const { id, color } = args
|
||||
cache.set(APIs.CoverColor, { id, color })
|
||||
})
|
||||
|
||||
|
@ -60,7 +67,7 @@ ipcMain.on(IpcChannels.CacheCoverColor, (event, args) => {
|
|||
* 导出tables到json文件,方便查看table大小(dev环境)
|
||||
*/
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
ipcMain.on(IpcChannels.DevDbExportJson, () => {
|
||||
on(IpcChannels.DevDbExportJson, () => {
|
||||
const tables = [
|
||||
Tables.ArtistAlbum,
|
||||
Tables.Playlist,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
const log = require('electron-log')
|
||||
|
||||
|
|
|
@ -6,92 +6,119 @@ import cache from './cache'
|
|||
import fileUpload from 'express-fileupload'
|
||||
import path from 'path'
|
||||
|
||||
log.info('[server] starting http server')
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development'
|
||||
const isProd = process.env.NODE_ENV === 'production'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const neteaseApi = require('NeteaseCloudMusicApi') as (params: any) => any[]
|
||||
class Server {
|
||||
port = Number(
|
||||
isProd
|
||||
? process.env.ELECTRON_WEB_SERVER_PORT ?? 42710
|
||||
: process.env.ELECTRON_DEV_NETEASE_API_PORT ?? 3000
|
||||
)
|
||||
app = express()
|
||||
|
||||
const app = express()
|
||||
app.use(cookieParser())
|
||||
app.use(fileUpload())
|
||||
constructor() {
|
||||
log.info('[server] starting http server')
|
||||
this.app.use(cookieParser())
|
||||
this.app.use(fileUpload())
|
||||
this.neteaseHandler()
|
||||
this.cacheAudioHandler()
|
||||
this.serveStaticForProd()
|
||||
this.listen()
|
||||
}
|
||||
|
||||
Object.entries(neteaseApi).forEach(([name, handler]) => {
|
||||
if (['serveNcmApi', 'getModulesDefinitions'].includes(name)) return
|
||||
neteaseHandler() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const neteaseApi = require('NeteaseCloudMusicApi') as (params: any) => any[]
|
||||
|
||||
name = pathCase(name)
|
||||
Object.entries(neteaseApi).forEach(([name, handler]) => {
|
||||
if (['serveNcmApi', 'getModulesDefinitions'].includes(name)) return
|
||||
|
||||
const wrappedHandler = async (req: Request, res: Response) => {
|
||||
log.debug(`[server] Handling request: ${req.path}`)
|
||||
name = pathCase(name)
|
||||
|
||||
// Get from cache
|
||||
const cacheData = await cache.getForExpress(name, req)
|
||||
if (cacheData) return res.json(cacheData)
|
||||
const wrappedHandler = async (req: Request, res: Response) => {
|
||||
log.debug(`[server] Handling request: ${req.path}`)
|
||||
|
||||
// Request netease api
|
||||
try {
|
||||
const result = await handler({
|
||||
...req.query,
|
||||
cookie: req.cookies,
|
||||
})
|
||||
// Get from cache
|
||||
const cacheData = await cache.getForExpress(name, req)
|
||||
if (cacheData) return res.json(cacheData)
|
||||
|
||||
cache.set(name, result.body, req.query)
|
||||
return res.send(result.body)
|
||||
} catch (error) {
|
||||
return res.status(500).send(error)
|
||||
// Request netease api
|
||||
try {
|
||||
const result = await handler({
|
||||
...req.query,
|
||||
cookie: req.cookies,
|
||||
})
|
||||
|
||||
cache.set(name, result.body, req.query)
|
||||
return res.send(result.body)
|
||||
} catch (error: any) {
|
||||
if ([400, 301].includes(error.status)) {
|
||||
return res.status(error.status).send(error.body)
|
||||
}
|
||||
return res.status(500).send(error)
|
||||
}
|
||||
}
|
||||
|
||||
this.app.get(`/netease/${name}`, wrappedHandler)
|
||||
this.app.post(`/netease/${name}`, wrappedHandler)
|
||||
})
|
||||
}
|
||||
|
||||
serveStaticForProd() {
|
||||
if (isProd) {
|
||||
this.app.use('/', express.static(path.join(__dirname, '../renderer/')))
|
||||
}
|
||||
}
|
||||
|
||||
app.get(`/netease/${name}`, wrappedHandler)
|
||||
app.post(`/netease/${name}`, wrappedHandler)
|
||||
})
|
||||
cacheAudioHandler() {
|
||||
this.app.get(
|
||||
'/yesplaymusic/audio/:filename',
|
||||
async (req: Request, res: Response) => {
|
||||
cache.getAudio(req.params.filename, res)
|
||||
}
|
||||
)
|
||||
this.app.post(
|
||||
'/yesplaymusic/audio/:id',
|
||||
async (req: Request, res: Response) => {
|
||||
const id = Number(req.params.id)
|
||||
const { url } = req.query
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).send({ error: 'Invalid param id' })
|
||||
}
|
||||
if (!url) {
|
||||
return res.status(400).send({ error: 'Invalid query url' })
|
||||
}
|
||||
|
||||
// Cache audio
|
||||
app.get(
|
||||
'/yesplaymusic/audio/:filename',
|
||||
async (req: Request, res: Response) => {
|
||||
cache.getAudio(req.params.filename, res)
|
||||
}
|
||||
)
|
||||
app.post('/yesplaymusic/audio/:id', async (req: Request, res: Response) => {
|
||||
const id = Number(req.params.id)
|
||||
const { url } = req.query
|
||||
if (isNaN(id)) {
|
||||
return res.status(400).send({ error: 'Invalid param id' })
|
||||
}
|
||||
if (!url) {
|
||||
return res.status(400).send({ error: 'Invalid query url' })
|
||||
if (
|
||||
!req.files ||
|
||||
Object.keys(req.files).length === 0 ||
|
||||
!req.files.file
|
||||
) {
|
||||
return res.status(400).send('No audio were uploaded.')
|
||||
}
|
||||
if ('length' in req.files.file) {
|
||||
return res.status(400).send('Only can upload one audio at a time.')
|
||||
}
|
||||
|
||||
try {
|
||||
await cache.setAudio(req.files.file.data, {
|
||||
id: id,
|
||||
source: 'netease',
|
||||
})
|
||||
res.status(200).send('Audio cached!')
|
||||
} catch (error) {
|
||||
res.status(500).send({ error })
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (!req.files || Object.keys(req.files).length === 0 || !req.files.file) {
|
||||
return res.status(400).send('No audio were uploaded.')
|
||||
}
|
||||
if ('length' in req.files.file) {
|
||||
return res.status(400).send('Only can upload one audio at a time.')
|
||||
}
|
||||
|
||||
try {
|
||||
await cache.setAudio(req.files.file.data, {
|
||||
id: id,
|
||||
source: 'netease',
|
||||
listen() {
|
||||
this.app.listen(this.port, () => {
|
||||
log.info(`[server] API server listening on port ${this.port}`)
|
||||
})
|
||||
res.status(200).send('Audio cached!')
|
||||
} catch (error) {
|
||||
res.status(500).send({ error })
|
||||
}
|
||||
})
|
||||
|
||||
if (isProd) {
|
||||
app.use('/', express.static(path.join(__dirname, '../renderer/')))
|
||||
}
|
||||
|
||||
const port = Number(
|
||||
isProd
|
||||
? process.env.ELECTRON_WEB_SERVER_PORT ?? 42710
|
||||
: process.env.ELECTRON_DEV_NETEASE_API_PORT ?? 3000
|
||||
)
|
||||
app.listen(port, () => {
|
||||
log.info(`[server] API server listening on port ${port}`)
|
||||
})
|
||||
export default new Server()
|
||||
|
|
|
@ -15,5 +15,5 @@
|
|||
"@/*": ["../*"]
|
||||
}
|
||||
},
|
||||
"include": ["./**/*.ts"]
|
||||
"include": ["./**/*.ts", "../shared/**/*.ts"]
|
||||
}
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
import request from '@/renderer/utils/request'
|
||||
|
||||
export enum AlbumApiNames {
|
||||
FETCH_ALBUM = 'fetchAlbum',
|
||||
}
|
||||
import {
|
||||
FetchAlbumParams,
|
||||
FetchAlbumResponse,
|
||||
LikeAAlbumParams,
|
||||
LikeAAlbumResponse,
|
||||
} from '@/shared/api/Album'
|
||||
|
||||
// 专辑详情
|
||||
export interface FetchAlbumParams {
|
||||
id: number
|
||||
}
|
||||
export interface FetchAlbumResponse {
|
||||
code: number
|
||||
resourceState: boolean
|
||||
album: Album
|
||||
songs: Track[]
|
||||
description: string
|
||||
}
|
||||
export function fetchAlbum(
|
||||
params: FetchAlbumParams,
|
||||
noCache: boolean
|
||||
|
@ -28,13 +20,6 @@ export function fetchAlbum(
|
|||
})
|
||||
}
|
||||
|
||||
export interface LikeAAlbumParams {
|
||||
t: 1 | 2
|
||||
id: number
|
||||
}
|
||||
export interface LikeAAlbumResponse {
|
||||
code: number
|
||||
}
|
||||
export function likeAAlbum(
|
||||
params: LikeAAlbumParams
|
||||
): Promise<LikeAAlbumResponse> {
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
import request from '@/renderer/utils/request'
|
||||
|
||||
export enum ArtistApiNames {
|
||||
FETCH_ARTIST = 'fetchArtist',
|
||||
FETCH_ARTIST_ALBUMS = 'fetchArtistAlbums',
|
||||
}
|
||||
import {
|
||||
FetchArtistParams,
|
||||
FetchArtistResponse,
|
||||
FetchArtistAlbumsParams,
|
||||
FetchArtistAlbumsResponse,
|
||||
} from '@/shared/api/Artist'
|
||||
|
||||
// 歌手详情
|
||||
export interface FetchArtistParams {
|
||||
id: number
|
||||
}
|
||||
export interface FetchArtistResponse {
|
||||
code: number
|
||||
more: boolean
|
||||
artist: Artist
|
||||
hotSongs: Track[]
|
||||
}
|
||||
export function fetchArtist(
|
||||
params: FetchArtistParams,
|
||||
noCache: boolean
|
||||
|
@ -29,17 +21,6 @@ export function fetchArtist(
|
|||
}
|
||||
|
||||
// 获取歌手的专辑列表
|
||||
export interface FetchArtistAlbumsParams {
|
||||
id: number
|
||||
limit?: number // default: 50
|
||||
offset?: number // default: 0
|
||||
}
|
||||
export interface FetchArtistAlbumsResponse {
|
||||
code: number
|
||||
hotAlbums: Album[]
|
||||
more: boolean
|
||||
artist: Artist
|
||||
}
|
||||
export function fetchArtistAlbums(
|
||||
params: FetchArtistAlbumsParams
|
||||
): Promise<FetchArtistAlbumsResponse> {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { fetchUserAccountResponse } from '@/renderer/api/user'
|
||||
import request from '@/renderer/utils/request'
|
||||
import { FetchUserAccountResponse } from '@/shared/api/User'
|
||||
|
||||
// 手机号登录
|
||||
interface LoginWithPhoneParams {
|
||||
|
@ -30,7 +30,7 @@ export interface LoginWithEmailParams {
|
|||
password?: string
|
||||
md5_password?: string
|
||||
}
|
||||
export interface loginWithEmailResponse extends fetchUserAccountResponse {
|
||||
export interface loginWithEmailResponse extends FetchUserAccountResponse {
|
||||
code: number
|
||||
cookie: string
|
||||
loginType: number
|
||||
|
|
|
@ -1,26 +1,15 @@
|
|||
import request from '@/renderer/utils/request'
|
||||
|
||||
export enum PlaylistApiNames {
|
||||
FETCH_PLAYLIST = 'fetchPlaylist',
|
||||
FETCH_RECOMMENDED_PLAYLISTS = 'fetchRecommendedPlaylists',
|
||||
FETCH_DAILY_RECOMMEND_PLAYLISTS = 'fetchDailyRecommendPlaylists',
|
||||
LIKE_A_PLAYLIST = 'likeAPlaylist',
|
||||
}
|
||||
import {
|
||||
FetchPlaylistParams,
|
||||
FetchPlaylistResponse,
|
||||
FetchRecommendedPlaylistsParams,
|
||||
FetchRecommendedPlaylistsResponse,
|
||||
FetchDailyRecommendPlaylistsResponse,
|
||||
LikeAPlaylistParams,
|
||||
LikeAPlaylistResponse,
|
||||
} from '@/shared/api/Playlists'
|
||||
|
||||
// 歌单详情
|
||||
export interface FetchPlaylistParams {
|
||||
id: number
|
||||
s?: number // 歌单最近的 s 个收藏者
|
||||
}
|
||||
export interface FetchPlaylistResponse {
|
||||
code: number
|
||||
playlist: Playlist
|
||||
privileges: unknown // TODO: unknown type
|
||||
relatedVideos: null
|
||||
resEntrance: null
|
||||
sharedPrivilege: null
|
||||
urls: null
|
||||
}
|
||||
export function fetchPlaylist(
|
||||
params: FetchPlaylistParams,
|
||||
noCache: boolean
|
||||
|
@ -39,15 +28,6 @@ export function fetchPlaylist(
|
|||
}
|
||||
|
||||
// 推荐歌单
|
||||
interface FetchRecommendedPlaylistsParams {
|
||||
limit?: number
|
||||
}
|
||||
export interface FetchRecommendedPlaylistsResponse {
|
||||
code: number
|
||||
category: number
|
||||
hasTaste: boolean
|
||||
result: Playlist[]
|
||||
}
|
||||
export function fetchRecommendedPlaylists(
|
||||
params: FetchRecommendedPlaylistsParams
|
||||
): Promise<FetchRecommendedPlaylistsResponse> {
|
||||
|
@ -59,12 +39,7 @@ export function fetchRecommendedPlaylists(
|
|||
}
|
||||
|
||||
// 每日推荐歌单(需要登录)
|
||||
export interface FetchDailyRecommendPlaylistsResponse {
|
||||
code: number
|
||||
featureFirst: boolean
|
||||
haveRcmdSongs: boolean
|
||||
recommend: Playlist[]
|
||||
}
|
||||
|
||||
export function fetchDailyRecommendPlaylists(): Promise<FetchDailyRecommendPlaylistsResponse> {
|
||||
return request({
|
||||
url: '/recommend/resource',
|
||||
|
@ -72,13 +47,6 @@ export function fetchDailyRecommendPlaylists(): Promise<FetchDailyRecommendPlayl
|
|||
})
|
||||
}
|
||||
|
||||
export interface LikeAPlaylistParams {
|
||||
t: 1 | 2
|
||||
id: number
|
||||
}
|
||||
export interface LikeAPlaylistResponse {
|
||||
code: number
|
||||
}
|
||||
export function likeAPlaylist(
|
||||
params: LikeAPlaylistParams
|
||||
): Promise<LikeAPlaylistResponse> {
|
||||
|
|
|
@ -1,72 +1,13 @@
|
|||
import request from '@/renderer/utils/request'
|
||||
|
||||
export enum SearchApiNames {
|
||||
SEARCH = 'search',
|
||||
MULTI_MATCH_SEARCH = 'multiMatchSearch',
|
||||
}
|
||||
import {
|
||||
SearchParams,
|
||||
SearchResponse,
|
||||
SearchTypes,
|
||||
MultiMatchSearchParams,
|
||||
MultiMatchSearchResponse,
|
||||
} from '@/shared/api/Search'
|
||||
|
||||
// 搜索
|
||||
export enum SearchTypes {
|
||||
SINGLE = '1',
|
||||
ALBUM = '10',
|
||||
ARTIST = '100',
|
||||
PLAYLIST = '1000',
|
||||
USER = '1002',
|
||||
MV = '1004',
|
||||
LYRICS = '1006',
|
||||
RADIO = '1009',
|
||||
VIDEO = '1014',
|
||||
ALL = '1018',
|
||||
}
|
||||
export interface SearchParams {
|
||||
keywords: string
|
||||
limit?: number // 返回数量 , 默认为 30
|
||||
offset?: number // 偏移数量,用于分页 , 如 : 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
|
||||
type: keyof typeof SearchTypes // type: 搜索类型
|
||||
}
|
||||
interface SearchResponse {
|
||||
code: number
|
||||
result: {
|
||||
album: {
|
||||
albums: Album[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
artist: {
|
||||
artists: Artist[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
playList: {
|
||||
playLists: Playlist[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
song: {
|
||||
songs: Track[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
user: {
|
||||
users: User[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
circle: unknown
|
||||
new_mlog: unknown
|
||||
order: string[]
|
||||
rec_type: null
|
||||
rec_query: null[]
|
||||
sim_query: unknown
|
||||
voice: unknown
|
||||
voiceList: unknown
|
||||
}
|
||||
}
|
||||
export function search(params: SearchParams): Promise<SearchResponse> {
|
||||
return request({
|
||||
url: '/search',
|
||||
|
@ -79,19 +20,6 @@ export function search(params: SearchParams): Promise<SearchResponse> {
|
|||
}
|
||||
|
||||
// 搜索多重匹配
|
||||
export interface MultiMatchSearchParams {
|
||||
keywords: string
|
||||
}
|
||||
interface MultiMatchSearchResponse {
|
||||
code: number
|
||||
result: {
|
||||
album: Album[]
|
||||
artist: Artist[]
|
||||
playlist: Playlist[]
|
||||
orpheus: unknown
|
||||
orders: Array<'artist' | 'album'>
|
||||
}
|
||||
}
|
||||
export function multiMatchSearch(
|
||||
params: MultiMatchSearchParams
|
||||
): Promise<MultiMatchSearchResponse> {
|
||||
|
|
|
@ -1,22 +1,16 @@
|
|||
import request from '@/renderer/utils/request'
|
||||
|
||||
export enum TrackApiNames {
|
||||
FETCH_TRACKS = 'fetchTracks',
|
||||
FETCH_AUDIO_SOURCE = 'fetchAudioSource',
|
||||
FETCH_LYRIC = 'fetchLyric',
|
||||
}
|
||||
import {
|
||||
FetchAudioSourceParams,
|
||||
FetchAudioSourceResponse,
|
||||
FetchLyricParams,
|
||||
FetchLyricResponse,
|
||||
FetchTracksParams,
|
||||
FetchTracksResponse,
|
||||
LikeATrackParams,
|
||||
LikeATrackResponse,
|
||||
} from '@/shared/api/Track'
|
||||
|
||||
// 获取歌曲详情
|
||||
export interface FetchTracksParams {
|
||||
ids: number[]
|
||||
}
|
||||
export interface FetchTracksResponse {
|
||||
code: number
|
||||
songs: Track[]
|
||||
privileges: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
export function fetchTracks(
|
||||
params: FetchTracksParams
|
||||
): Promise<FetchTracksResponse> {
|
||||
|
@ -30,39 +24,6 @@ export function fetchTracks(
|
|||
}
|
||||
|
||||
// 获取音源URL
|
||||
export interface FetchAudioSourceParams {
|
||||
id: number
|
||||
br?: number // bitrate, default 999000,320000 = 320kbps
|
||||
}
|
||||
export interface FetchAudioSourceResponse {
|
||||
code: number
|
||||
data: {
|
||||
br: number
|
||||
canExtend: boolean
|
||||
code: number
|
||||
encodeType: 'mp3' | null
|
||||
expi: number
|
||||
fee: number
|
||||
flag: number
|
||||
freeTimeTrialPrivilege: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
freeTrialPrivilege: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
freeTrialInfo: null
|
||||
gain: number
|
||||
id: number
|
||||
level: 'standard' | 'null'
|
||||
md5: string | null
|
||||
payed: number
|
||||
size: number
|
||||
type: 'mp3' | null
|
||||
uf: null
|
||||
url: string | null
|
||||
urlSource: number
|
||||
}[]
|
||||
}
|
||||
export function fetchAudioSource(
|
||||
params: FetchAudioSourceParams
|
||||
): Promise<FetchAudioSourceResponse> {
|
||||
|
@ -74,43 +35,6 @@ export function fetchAudioSource(
|
|||
}
|
||||
|
||||
// 获取歌词
|
||||
export interface FetchLyricParams {
|
||||
id: number
|
||||
}
|
||||
export interface FetchLyricResponse {
|
||||
code: number
|
||||
sgc: boolean
|
||||
sfy: boolean
|
||||
qfy: boolean
|
||||
lyricUser?: {
|
||||
id: number
|
||||
status: number
|
||||
demand: number
|
||||
userid: number
|
||||
nickname: string
|
||||
uptime: number
|
||||
}
|
||||
transUser?: {
|
||||
id: number
|
||||
status: number
|
||||
demand: number
|
||||
userid: number
|
||||
nickname: string
|
||||
uptime: number
|
||||
}
|
||||
lrc: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
klyric?: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
tlyric?: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
}
|
||||
export function fetchLyric(
|
||||
params: FetchLyricParams
|
||||
): Promise<FetchLyricResponse> {
|
||||
|
@ -121,15 +45,7 @@ export function fetchLyric(
|
|||
})
|
||||
}
|
||||
|
||||
export interface LikeATrackParams {
|
||||
id: number
|
||||
like: boolean
|
||||
}
|
||||
export interface LikeATrackResponse {
|
||||
code: number
|
||||
playlistId: number
|
||||
songs: Track[]
|
||||
}
|
||||
// 收藏歌曲
|
||||
export function likeATrack(
|
||||
params: LikeATrackParams
|
||||
): Promise<LikeATrackResponse> {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import request from '@/renderer/utils/request'
|
||||
|
||||
export enum UserApiNames {
|
||||
FETCH_USER_ACCOUNT = 'fetchUserAccount',
|
||||
FETCH_USER_LIKED_TRACKS_IDS = 'fetchUserLikedTracksIDs',
|
||||
FETCH_USER_PLAYLISTS = 'fetchUserPlaylists',
|
||||
FETCH_USER_ALBUMS = 'fetchUserAlbums',
|
||||
FETCH_USER_ARTIST = 'fetchUserArtists',
|
||||
}
|
||||
import {
|
||||
FetchUserAccountResponse,
|
||||
FetchUserPlaylistsParams,
|
||||
FetchUserPlaylistsResponse,
|
||||
FetchUserLikedTracksIDsParams,
|
||||
FetchUserLikedTracksIDsResponse,
|
||||
FetchUserAlbumsParams,
|
||||
FetchUserAlbumsResponse,
|
||||
FetchUserArtistsResponse,
|
||||
} from '@/shared/api/User'
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
|
@ -26,64 +28,7 @@ export function userDetail(uid: number) {
|
|||
}
|
||||
|
||||
// 获取账号详情
|
||||
export interface fetchUserAccountResponse {
|
||||
code: number
|
||||
account: {
|
||||
anonimousUser: boolean
|
||||
ban: number
|
||||
baoyueVersion: number
|
||||
createTime: number
|
||||
donateVersion: number
|
||||
id: number
|
||||
paidFee: boolean
|
||||
status: number
|
||||
tokenVersion: number
|
||||
type: number
|
||||
userName: string
|
||||
vipType: number
|
||||
whitelistAuthority: number
|
||||
} | null
|
||||
profile: {
|
||||
userId: number
|
||||
userType: number
|
||||
nickname: string
|
||||
avatarImgId: number
|
||||
avatarUrl: string
|
||||
backgroundImgId: number
|
||||
backgroundUrl: string
|
||||
signature: string
|
||||
createTime: number
|
||||
userName: string
|
||||
accountType: number
|
||||
shortUserName: string
|
||||
birthday: number
|
||||
authority: number
|
||||
gender: number
|
||||
accountStatus: number
|
||||
province: number
|
||||
city: number
|
||||
authStatus: number
|
||||
description: string | null
|
||||
detailDescription: string | null
|
||||
defaultAvatar: boolean
|
||||
expertTags: [] | null
|
||||
experts: [] | null
|
||||
djStatus: number
|
||||
locationStatus: number
|
||||
vipType: number
|
||||
followed: boolean
|
||||
mutual: boolean
|
||||
authenticated: boolean
|
||||
lastLoginTime: number
|
||||
lastLoginIP: string
|
||||
remarkName: string | null
|
||||
viptypeVersion: number
|
||||
authenticationTypes: number
|
||||
avatarDetail: string | null
|
||||
anchor: boolean
|
||||
} | null
|
||||
}
|
||||
export function fetchUserAccount(): Promise<fetchUserAccountResponse> {
|
||||
export function fetchUserAccount(): Promise<FetchUserAccountResponse> {
|
||||
return request({
|
||||
url: '/user/account',
|
||||
method: 'get',
|
||||
|
@ -94,17 +39,6 @@ export function fetchUserAccount(): Promise<fetchUserAccountResponse> {
|
|||
}
|
||||
|
||||
// 获取用户歌单
|
||||
export interface FetchUserPlaylistsParams {
|
||||
uid: number
|
||||
offset: number
|
||||
limit?: number // default 30
|
||||
}
|
||||
export interface FetchUserPlaylistsResponse {
|
||||
code: number
|
||||
more: boolean
|
||||
version: string
|
||||
playlist: Playlist[]
|
||||
}
|
||||
export function fetchUserPlaylists(
|
||||
params: FetchUserPlaylistsParams
|
||||
): Promise<FetchUserPlaylistsResponse> {
|
||||
|
@ -115,14 +49,6 @@ export function fetchUserPlaylists(
|
|||
})
|
||||
}
|
||||
|
||||
export interface FetchUserLikedTracksIDsParams {
|
||||
uid: number
|
||||
}
|
||||
export interface FetchUserLikedTracksIDsResponse {
|
||||
code: number
|
||||
checkPoint: number
|
||||
ids: number[]
|
||||
}
|
||||
export function fetchUserLikedTracksIDs(
|
||||
params: FetchUserLikedTracksIDsParams
|
||||
): Promise<FetchUserLikedTracksIDsResponse> {
|
||||
|
@ -153,17 +79,6 @@ export function dailySignin(type = 0) {
|
|||
})
|
||||
}
|
||||
|
||||
export interface FetchUserAlbumsParams {
|
||||
offset?: number // default 0
|
||||
limit?: number // default 25
|
||||
}
|
||||
export interface FetchUserAlbumsResponse {
|
||||
code: number
|
||||
hasMore: boolean
|
||||
paidCount: number
|
||||
count: number
|
||||
data: Album[]
|
||||
}
|
||||
export function fetchUserAlbums(
|
||||
params: FetchUserAlbumsParams
|
||||
): Promise<FetchUserAlbumsResponse> {
|
||||
|
@ -178,12 +93,6 @@ export function fetchUserAlbums(
|
|||
}
|
||||
|
||||
// 获取收藏的歌手
|
||||
export interface FetchUserArtistsResponse {
|
||||
code: number
|
||||
hasMore: boolean
|
||||
count: number
|
||||
data: Artist[]
|
||||
}
|
||||
export function fetchUserArtists(): Promise<FetchUserArtistsResponse> {
|
||||
return request({
|
||||
url: '/artist/sublist',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { player } from '@/renderer/store'
|
||||
import SvgIcon from './SvgIcon'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
|
||||
const Controls = () => {
|
||||
const [isMaximized, setIsMaximized] = useState(false)
|
||||
|
|
22
src/renderer/global.d.ts
vendored
22
src/renderer/global.d.ts
vendored
|
@ -1,17 +1,25 @@
|
|||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannelsParams, IpcChannelsReturns } from '@/shared/IpcChannels'
|
||||
import { ElectronLog } from 'electron-log'
|
||||
|
||||
export {}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
// Expose some Api through preload script
|
||||
ipcRenderer?: {
|
||||
sendSync: (channel: IpcChannels, ...args: any[]) => any
|
||||
send: (channel: IpcChannels, ...args: any[]) => void
|
||||
on: (
|
||||
channel: IpcChannels,
|
||||
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
|
||||
sendSync: <T extends keyof IpcChannelsParams>(
|
||||
channel: T,
|
||||
params?: IpcChannelsParams[T]
|
||||
) => IpcChannelsReturns[T]
|
||||
send: <T extends keyof IpcChannelsParams>(
|
||||
channel: T,
|
||||
params?: IpcChannelsParams[T]
|
||||
) => void
|
||||
on: <T extends keyof IpcChannelsParams>(
|
||||
channel: T,
|
||||
listener: (
|
||||
event: Electron.IpcRendererEvent,
|
||||
value: IpcChannelsReturns[T]
|
||||
) => void
|
||||
) => void
|
||||
}
|
||||
env?: {
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { fetchAlbum } from '@/renderer/api/album'
|
||||
import { AlbumApiNames } from '@/renderer/api/album'
|
||||
import type { FetchAlbumParams, FetchAlbumResponse } from '@/renderer/api/album'
|
||||
import reactQueryClient from '@/renderer/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchAlbumParams,
|
||||
AlbumApiNames,
|
||||
FetchAlbumResponse,
|
||||
} from '@/shared/api/Album'
|
||||
|
||||
const fetch = async (params: FetchAlbumParams, noCache?: boolean) => {
|
||||
const album = await fetchAlbum(params, !!noCache)
|
||||
|
@ -21,7 +25,7 @@ export default function useAlbum(params: FetchAlbumParams, noCache?: boolean) {
|
|||
staleTime: 24 * 60 * 60 * 1000, // 24 hours
|
||||
placeholderData: (): FetchAlbumResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'album',
|
||||
api: APIs.Album,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { fetchArtist } from '@/renderer/api/artist'
|
||||
import { ArtistApiNames } from '@/renderer/api/artist'
|
||||
import type {
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchArtistParams,
|
||||
ArtistApiNames,
|
||||
FetchArtistResponse,
|
||||
} from '@/renderer/api/artist'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
} from '@/shared/api/Artist'
|
||||
|
||||
export default function useArtist(
|
||||
params: FetchArtistParams,
|
||||
|
@ -18,7 +19,7 @@ export default function useArtist(
|
|||
staleTime: 5 * 60 * 1000, // 5 mins
|
||||
placeholderData: (): FetchArtistResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'artists',
|
||||
api: APIs.Artist,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import { fetchArtistAlbums } from '@/renderer/api/artist'
|
||||
import { ArtistApiNames } from '@/renderer/api/artist'
|
||||
import type {
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchArtistAlbumsParams,
|
||||
ArtistApiNames,
|
||||
FetchArtistAlbumsResponse,
|
||||
} from '@/renderer/api/artist'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
} from '@/shared/api/Artist'
|
||||
|
||||
export default function useUserAlbums(params: FetchArtistAlbumsParams) {
|
||||
return useQuery(
|
||||
|
@ -18,7 +19,7 @@ export default function useUserAlbums(params: FetchArtistAlbumsParams) {
|
|||
staleTime: 3600000,
|
||||
placeholderData: (): FetchArtistAlbumsResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'artist/album',
|
||||
api: APIs.ArtistAlbum,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { TrackApiNames, fetchLyric } from '@/renderer/api/track'
|
||||
import type { FetchLyricParams, FetchLyricResponse } from '@/renderer/api/track'
|
||||
import { fetchLyric } from '@/renderer/api/track'
|
||||
import reactQueryClient from '@/renderer/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import {
|
||||
FetchLyricParams,
|
||||
FetchLyricResponse,
|
||||
TrackApiNames,
|
||||
} from '@/shared/api/Track'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
|
||||
export default function useLyric(params: FetchLyricParams) {
|
||||
return useQuery(
|
||||
|
@ -15,7 +20,7 @@ export default function useLyric(params: FetchLyricParams) {
|
|||
staleTime: Infinity,
|
||||
initialData: (): FetchLyricResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'lyric',
|
||||
api: APIs.Lyric,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { fetchPlaylist } from '@/renderer/api/playlist'
|
||||
import { PlaylistApiNames } from '@/renderer/api/playlist'
|
||||
import type {
|
||||
FetchPlaylistParams,
|
||||
FetchPlaylistResponse,
|
||||
} from '@/renderer/api/playlist'
|
||||
import reactQueryClient from '@/renderer/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchPlaylistParams,
|
||||
PlaylistApiNames,
|
||||
FetchPlaylistResponse,
|
||||
} from '@/shared/api/Playlists'
|
||||
|
||||
const fetch = (params: FetchPlaylistParams, noCache?: boolean) => {
|
||||
return fetchPlaylist(params, !!noCache)
|
||||
|
@ -23,7 +24,7 @@ export default function usePlaylist(
|
|||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchPlaylistResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'playlist/detail',
|
||||
api: APIs.Playlist,
|
||||
query: {
|
||||
id: params.id,
|
||||
},
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import { fetchAudioSource, fetchTracks } from '@/renderer/api/track'
|
||||
import type {} from '@/renderer/api/track'
|
||||
import reactQueryClient from '@/renderer/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import {
|
||||
TrackApiNames,
|
||||
fetchAudioSource,
|
||||
fetchTracks,
|
||||
} from '@/renderer/api/track'
|
||||
import type {
|
||||
FetchAudioSourceParams,
|
||||
FetchTracksParams,
|
||||
FetchTracksResponse,
|
||||
} from '@/renderer/api/track'
|
||||
import reactQueryClient from '@/renderer/utils/reactQueryClient'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
TrackApiNames,
|
||||
} from '@/shared/api/Track'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
|
||||
export default function useTracks(params: FetchTracksParams) {
|
||||
return useQuery(
|
||||
|
@ -23,7 +22,7 @@ export default function useTracks(params: FetchTracksParams) {
|
|||
staleTime: Infinity,
|
||||
initialData: (): FetchTracksResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'song/detail',
|
||||
api: APIs.Track,
|
||||
query: {
|
||||
ids: params.ids.join(','),
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TrackApiNames, fetchTracks } from '@/renderer/api/track'
|
||||
import type { FetchTracksParams } from '@/renderer/api/track'
|
||||
import { FetchTracksParams, TrackApiNames } from '@/shared/api/Track'
|
||||
import { fetchTracks } from 'api/track'
|
||||
|
||||
// 100 tracks each page
|
||||
const offset = 100
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { fetchUserAccount, fetchUserAccountResponse } from '@/renderer/api/user'
|
||||
import { UserApiNames } from '@/renderer/api/user'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { fetchUserAccount } from '@/renderer/api/user'
|
||||
import { UserApiNames, FetchUserAccountResponse } from '@/shared/api/User'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
|
||||
export default function useUser() {
|
||||
return useQuery(UserApiNames.FETCH_USER_ACCOUNT, fetchUserAccount, {
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): fetchUserAccountResponse | undefined =>
|
||||
placeholderData: (): FetchUserAccountResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'user/account',
|
||||
api: APIs.UserAccount,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { likeAAlbum } from '@/renderer/api/album'
|
||||
import type {
|
||||
FetchUserAlbumsParams,
|
||||
FetchUserAlbumsResponse,
|
||||
} from '@/renderer/api/user'
|
||||
import { UserApiNames, fetchUserAlbums } from '@/renderer/api/user'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import useUser from './useUser'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import {
|
||||
FetchUserAlbumsParams,
|
||||
UserApiNames,
|
||||
FetchUserAlbumsResponse,
|
||||
} from '@/shared/api/User'
|
||||
import { fetchUserAlbums } from 'api/user'
|
||||
|
||||
export default function useUserAlbums(params: FetchUserAlbumsParams = {}) {
|
||||
const { data: user } = useUser()
|
||||
|
@ -17,7 +19,7 @@ export default function useUserAlbums(params: FetchUserAlbumsParams = {}) {
|
|||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserAlbumsResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'album/sublist',
|
||||
api: APIs.UserAlbums,
|
||||
query: params,
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import type { FetchUserArtistsResponse } from '@/renderer/api/user'
|
||||
import { UserApiNames, fetchUserArtists } from '@/renderer/api/user'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { fetchUserArtists } from '@/renderer/api/user'
|
||||
import { UserApiNames, FetchUserArtistsResponse } from '@/shared/api/User'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
|
||||
export default function useUserArtists() {
|
||||
return useQuery([UserApiNames.FETCH_USER_ARTIST], fetchUserArtists, {
|
||||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserArtistsResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'album/sublist',
|
||||
api: APIs.UserArtists,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import type { FetchUserLikedTracksIDsResponse } from '@/renderer/api/user'
|
||||
import { UserApiNames, fetchUserLikedTracksIDs } from '@/renderer/api/user'
|
||||
import { likeATrack } from '@/renderer/api/track'
|
||||
import useUser from './useUser'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { fetchUserLikedTracksIDs } from 'api/user'
|
||||
import {
|
||||
FetchUserLikedTracksIDsResponse,
|
||||
UserApiNames,
|
||||
} from '@/shared/api/User'
|
||||
|
||||
export default function useUserLikedTracksIDs() {
|
||||
const { data: user } = useUser()
|
||||
|
@ -17,7 +21,7 @@ export default function useUserLikedTracksIDs() {
|
|||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserLikedTracksIDsResponse | undefined =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'likelist',
|
||||
api: APIs.Likelist,
|
||||
query: {
|
||||
uid,
|
||||
},
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { likeAPlaylist } from '@/renderer/api/playlist'
|
||||
import type { FetchUserPlaylistsResponse } from '@/renderer/api/user'
|
||||
import { UserApiNames, fetchUserPlaylists } from '@/renderer/api/user'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import useUser from './useUser'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { fetchUserPlaylists } from 'api/user'
|
||||
import { FetchUserPlaylistsResponse, UserApiNames } from '@/shared/api/User'
|
||||
|
||||
export default function useUserPlaylists() {
|
||||
const { data: user } = useUser()
|
||||
|
@ -33,7 +34,7 @@ export default function useUserPlaylists() {
|
|||
refetchOnWindowFocus: true,
|
||||
placeholderData: (): FetchUserPlaylistsResponse =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'user/playlist',
|
||||
api: APIs.UserPlaylist,
|
||||
query: {
|
||||
uid: params.uid,
|
||||
},
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import {
|
||||
PlaylistApiNames,
|
||||
fetchRecommendedPlaylists,
|
||||
fetchDailyRecommendPlaylists,
|
||||
} from '@/renderer/api/playlist'
|
||||
import CoverRow from '@/renderer/components/CoverRow'
|
||||
import DailyTracksCard from '@/renderer/components/DailyTracksCard'
|
||||
import FMCard from '@/renderer/components/FMCard'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { PlaylistApiNames } from '@/shared/api/Playlists'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
|
||||
export default function Home() {
|
||||
const {
|
||||
|
@ -19,7 +20,7 @@ export default function Home() {
|
|||
retry: false,
|
||||
placeholderData: () =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'recommend/resource',
|
||||
api: APIs.RecommendResource,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
@ -35,7 +36,7 @@ export default function Home() {
|
|||
{
|
||||
placeholderData: () =>
|
||||
window.ipcRenderer?.sendSync(IpcChannels.GetApiCacheSync, {
|
||||
api: 'personalized',
|
||||
api: APIs.Personalized,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import {
|
||||
multiMatchSearch,
|
||||
search,
|
||||
SearchApiNames,
|
||||
SearchTypes,
|
||||
} from '@/renderer/api/search'
|
||||
import { multiMatchSearch, search } from '@/renderer/api/search'
|
||||
import Cover from '@/renderer/components/Cover'
|
||||
import TrackGrid from '@/renderer/components/TracksGrid'
|
||||
import { player } from '@/renderer/store'
|
||||
import { resizeImage } from '@/renderer/utils/common'
|
||||
import { SearchTypes, SearchApiNames } from '@/shared/api/Search'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const Artists = ({ artists }: { artists: Artist[] }) => {
|
||||
|
|
|
@ -8,8 +8,8 @@ import {
|
|||
getCoverColor,
|
||||
storage,
|
||||
} from '@/renderer/utils/common'
|
||||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { APIs } from '@/main/CacheAPIsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
|
||||
test('resizeImage', () => {
|
||||
expect(resizeImage('https://test.com/test.jpg', 'xs')).toBe(
|
||||
|
@ -62,28 +62,48 @@ test('formatDuration', () => {
|
|||
expect(formatDuration(0, 'zh-CN', 'hh[hr] mm[min]')).toBe('0 分钟')
|
||||
})
|
||||
|
||||
test('cacheCoverColor', () => {
|
||||
vi.stubGlobal('ipcRenderer', {
|
||||
send: (channel: IpcChannels, ...args: any[]) => {
|
||||
expect(channel).toBe(IpcChannels.CacheCoverColor)
|
||||
expect(args[0].api).toBe(APIs.CoverColor)
|
||||
expect(args[0].query).toEqual({
|
||||
id: '109951165911363',
|
||||
color: '#fff',
|
||||
})
|
||||
},
|
||||
})
|
||||
describe('cacheCoverColor', () => {
|
||||
test('cache with valid url', () => {
|
||||
vi.stubGlobal('ipcRenderer', {
|
||||
send: (channel: IpcChannels, ...args: any[]) => {
|
||||
expect(channel).toBe(IpcChannels.CacheCoverColor)
|
||||
expect(args[0].api).toBe(APIs.CoverColor)
|
||||
expect(args[0].query).toEqual({
|
||||
id: '109951165911363',
|
||||
color: '#fff',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const sendSpy = vi.spyOn(window.ipcRenderer as any, 'send')
|
||||
expect(
|
||||
const sendSpy = vi.spyOn(window.ipcRenderer as any, 'send')
|
||||
cacheCoverColor(
|
||||
'https://p2.music.126.net/2qW-OYZod7SgrzxTwtyBqA==/109951165911363.jpg?param=256y256',
|
||||
'#fff'
|
||||
)
|
||||
)
|
||||
expect(sendSpy).toHaveBeenCalledTimes(1)
|
||||
|
||||
vi.stubGlobal('ipcRenderer', undefined)
|
||||
expect(sendSpy).toHaveBeenCalledTimes(1)
|
||||
|
||||
vi.stubGlobal('ipcRenderer', undefined)
|
||||
})
|
||||
|
||||
test('cache with invalid url', () => {
|
||||
vi.stubGlobal('ipcRenderer', {
|
||||
send: (channel: IpcChannels, ...args: any[]) => {
|
||||
expect(channel).toBe(IpcChannels.CacheCoverColor)
|
||||
expect(args[0].api).toBe(APIs.CoverColor)
|
||||
expect(args[0].query).toEqual({
|
||||
id: '',
|
||||
color: '#fff',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const sendSpy = vi.spyOn(window.ipcRenderer as any, 'send')
|
||||
cacheCoverColor('not a valid url', '#fff')
|
||||
expect(sendSpy).toHaveBeenCalledTimes(0)
|
||||
|
||||
vi.stubGlobal('ipcRenderer', undefined)
|
||||
})
|
||||
})
|
||||
|
||||
test('calcCoverColor', async () => {
|
||||
|
@ -117,7 +137,6 @@ test('calcCoverColor', async () => {
|
|||
)
|
||||
).toBe('#808080')
|
||||
|
||||
expect(sendSpy).toHaveBeenCalledTimes(1)
|
||||
vi.stubGlobal('ipcRenderer', undefined)
|
||||
})
|
||||
|
||||
|
@ -174,6 +193,10 @@ describe('getCoverColor', () => {
|
|||
expect(sendSpy).toHaveBeenCalledTimes(1)
|
||||
vi.stubGlobal('ipcRenderer', undefined)
|
||||
})
|
||||
|
||||
test('invalid url', async () => {
|
||||
expect(await getCoverColor('not a valid url')).toBe(undefined)
|
||||
})
|
||||
})
|
||||
|
||||
test('storage', () => {
|
||||
|
|
|
@ -20,5 +20,5 @@
|
|||
"@/*": ["../*"]
|
||||
}
|
||||
},
|
||||
"include": ["./**/*.ts", "./**/*.tsx"]
|
||||
"include": ["./**/*.ts", "./**/*.tsx", "../shared/**/*.ts"]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { IpcChannels } from '@/main/IpcChannelsName'
|
||||
import { IpcChannels } from '@/shared/IpcChannels'
|
||||
import dayjs from 'dayjs'
|
||||
import duration from 'dayjs/plugin/duration'
|
||||
import { APIs } from '@/main/CacheAPIsName'
|
||||
import { APIs } from '@/shared/CacheAPIs'
|
||||
import { average } from 'color.js'
|
||||
import { colord } from 'colord'
|
||||
|
||||
|
@ -110,7 +110,13 @@ export function scrollToTop(smooth = false) {
|
|||
}
|
||||
|
||||
export async function getCoverColor(coverUrl: string) {
|
||||
const id = new URL(coverUrl).pathname.split('/').pop()?.split('.')[0]
|
||||
let id: string | undefined
|
||||
try {
|
||||
id = new URL(coverUrl).pathname.split('/').pop()?.split('.')[0]
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
|
||||
const colorFromCache = window.ipcRenderer?.sendSync(
|
||||
IpcChannels.GetApiCacheSync,
|
||||
{
|
||||
|
@ -124,14 +130,18 @@ export async function getCoverColor(coverUrl: string) {
|
|||
}
|
||||
|
||||
export async function cacheCoverColor(coverUrl: string, color: string) {
|
||||
const id = new URL(coverUrl).pathname.split('/').pop()?.split('.')[0]
|
||||
let id: string | undefined
|
||||
try {
|
||||
id = new URL(coverUrl).pathname.split('/').pop()?.split('.')[0]
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!id || isNaN(Number(id))) return
|
||||
|
||||
window.ipcRenderer?.send(IpcChannels.CacheCoverColor, {
|
||||
api: APIs.CoverColor,
|
||||
query: {
|
||||
id,
|
||||
color,
|
||||
},
|
||||
id: Number(id),
|
||||
color,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FetchLyricResponse } from '@/renderer/api/track'
|
||||
import { FetchLyricResponse } from '@/shared/api/Track'
|
||||
|
||||
export function lyricParser(lrc: FetchLyricResponse) {
|
||||
return {
|
||||
|
|
72
src/shared/CacheAPIs.ts
Normal file
72
src/shared/CacheAPIs.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { FetchArtistAlbumsResponse, FetchArtistResponse } from './api/Artist'
|
||||
import { FetchAlbumResponse } from './api/Album'
|
||||
import {
|
||||
FetchUserAccountResponse,
|
||||
FetchUserAlbumsResponse,
|
||||
FetchUserArtistsResponse,
|
||||
FetchUserLikedTracksIDsResponse,
|
||||
FetchUserPlaylistsResponse,
|
||||
} from './api/User'
|
||||
import {
|
||||
FetchAudioSourceResponse,
|
||||
FetchLyricResponse,
|
||||
FetchTracksResponse,
|
||||
} from './api/Track'
|
||||
import {
|
||||
FetchPlaylistResponse,
|
||||
FetchRecommendedPlaylistsResponse,
|
||||
} from './api/Playlists'
|
||||
|
||||
export const enum APIs {
|
||||
Album = 'album',
|
||||
Artist = 'artists',
|
||||
ArtistAlbum = 'artist/album',
|
||||
CoverColor = 'cover_color',
|
||||
Likelist = 'likelist',
|
||||
Lyric = 'lyric',
|
||||
Personalized = 'personalized',
|
||||
Playlist = 'playlist/detail',
|
||||
RecommendResource = 'recommend/resource',
|
||||
SongUrl = 'song/url',
|
||||
Track = 'song/detail',
|
||||
UserAccount = 'user/account',
|
||||
UserAlbums = 'album/sublist',
|
||||
UserArtists = 'artist/sublist',
|
||||
UserPlaylist = 'user/playlist',
|
||||
}
|
||||
|
||||
export interface APIsParams {
|
||||
[APIs.Album]: { id: number }
|
||||
[APIs.Artist]: { id: number }
|
||||
[APIs.ArtistAlbum]: { id: number }
|
||||
[APIs.CoverColor]: { id: number }
|
||||
[APIs.Likelist]: void
|
||||
[APIs.Lyric]: { id: number }
|
||||
[APIs.Personalized]: void
|
||||
[APIs.Playlist]: { id: number }
|
||||
[APIs.RecommendResource]: void
|
||||
[APIs.SongUrl]: { id: string }
|
||||
[APIs.Track]: { ids: string }
|
||||
[APIs.UserAccount]: void
|
||||
[APIs.UserAlbums]: void
|
||||
[APIs.UserArtists]: void
|
||||
[APIs.UserPlaylist]: void
|
||||
}
|
||||
|
||||
export interface APIsResponse {
|
||||
[APIs.Album]: FetchAlbumResponse
|
||||
[APIs.Artist]: FetchArtistResponse
|
||||
[APIs.ArtistAlbum]: FetchArtistAlbumsResponse
|
||||
[APIs.CoverColor]: string | undefined
|
||||
[APIs.Likelist]: FetchUserLikedTracksIDsResponse
|
||||
[APIs.Lyric]: FetchLyricResponse
|
||||
[APIs.Personalized]: FetchRecommendedPlaylistsResponse
|
||||
[APIs.Playlist]: FetchPlaylistResponse
|
||||
[APIs.RecommendResource]: FetchRecommendedPlaylistsResponse
|
||||
[APIs.SongUrl]: FetchAudioSourceResponse
|
||||
[APIs.Track]: FetchTracksResponse
|
||||
[APIs.UserAccount]: FetchUserAccountResponse
|
||||
[APIs.UserAlbums]: FetchUserAlbumsResponse
|
||||
[APIs.UserArtists]: FetchUserArtistsResponse
|
||||
[APIs.UserPlaylist]: FetchUserPlaylistsResponse
|
||||
}
|
40
src/shared/IpcChannels.ts
Normal file
40
src/shared/IpcChannels.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
import { APIs } from './CacheAPIs'
|
||||
|
||||
export const enum IpcChannels {
|
||||
ClearAPICache = 'clear-api-cache',
|
||||
Minimize = 'minimize',
|
||||
MaximizeOrUnmaximize = 'maximize-or-unmaximize',
|
||||
Close = 'close',
|
||||
IsMaximized = 'is-maximized',
|
||||
GetApiCacheSync = 'get-api-cache-sync',
|
||||
DevDbExportJson = 'dev-db-export-json',
|
||||
CacheCoverColor = 'cache-cover-color',
|
||||
}
|
||||
|
||||
export interface IpcChannelsParams {
|
||||
[IpcChannels.ClearAPICache]: void
|
||||
[IpcChannels.Minimize]: void
|
||||
[IpcChannels.MaximizeOrUnmaximize]: void
|
||||
[IpcChannels.Close]: void
|
||||
[IpcChannels.IsMaximized]: void
|
||||
[IpcChannels.GetApiCacheSync]: {
|
||||
api: APIs
|
||||
query?: any
|
||||
}
|
||||
[IpcChannels.DevDbExportJson]: void
|
||||
[IpcChannels.CacheCoverColor]: {
|
||||
id: number
|
||||
color: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface IpcChannelsReturns {
|
||||
[IpcChannels.ClearAPICache]: void
|
||||
[IpcChannels.Minimize]: void
|
||||
[IpcChannels.MaximizeOrUnmaximize]: void
|
||||
[IpcChannels.Close]: void
|
||||
[IpcChannels.IsMaximized]: boolean
|
||||
[IpcChannels.GetApiCacheSync]: any
|
||||
[IpcChannels.DevDbExportJson]: void
|
||||
[IpcChannels.CacheCoverColor]: void
|
||||
}
|
23
src/shared/api/Album.ts
Normal file
23
src/shared/api/Album.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
export enum AlbumApiNames {
|
||||
FETCH_ALBUM = 'fetchAlbum',
|
||||
}
|
||||
|
||||
// 专辑详情
|
||||
export interface FetchAlbumParams {
|
||||
id: number
|
||||
}
|
||||
export interface FetchAlbumResponse {
|
||||
code: number
|
||||
resourceState: boolean
|
||||
album: Album
|
||||
songs: Track[]
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface LikeAAlbumParams {
|
||||
t: 1 | 2
|
||||
id: number
|
||||
}
|
||||
export interface LikeAAlbumResponse {
|
||||
code: number
|
||||
}
|
28
src/shared/api/Artist.ts
Normal file
28
src/shared/api/Artist.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
export enum ArtistApiNames {
|
||||
FETCH_ARTIST = 'fetchArtist',
|
||||
FETCH_ARTIST_ALBUMS = 'fetchArtistAlbums',
|
||||
}
|
||||
|
||||
// 歌手详情
|
||||
export interface FetchArtistParams {
|
||||
id: number
|
||||
}
|
||||
export interface FetchArtistResponse {
|
||||
code: number
|
||||
more: boolean
|
||||
artist: Artist
|
||||
hotSongs: Track[]
|
||||
}
|
||||
|
||||
// 获取歌手的专辑列表
|
||||
export interface FetchArtistAlbumsParams {
|
||||
id: number
|
||||
limit?: number // default: 50
|
||||
offset?: number // default: 0
|
||||
}
|
||||
export interface FetchArtistAlbumsResponse {
|
||||
code: number
|
||||
hotAlbums: Album[]
|
||||
more: boolean
|
||||
artist: Artist
|
||||
}
|
48
src/shared/api/Playlists.ts
Normal file
48
src/shared/api/Playlists.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
export enum PlaylistApiNames {
|
||||
FETCH_PLAYLIST = 'fetchPlaylist',
|
||||
FETCH_RECOMMENDED_PLAYLISTS = 'fetchRecommendedPlaylists',
|
||||
FETCH_DAILY_RECOMMEND_PLAYLISTS = 'fetchDailyRecommendPlaylists',
|
||||
LIKE_A_PLAYLIST = 'likeAPlaylist',
|
||||
}
|
||||
|
||||
// 歌单详情
|
||||
export interface FetchPlaylistParams {
|
||||
id: number
|
||||
s?: number // 歌单最近的 s 个收藏者
|
||||
}
|
||||
export interface FetchPlaylistResponse {
|
||||
code: number
|
||||
playlist: Playlist
|
||||
privileges: unknown // TODO: unknown type
|
||||
relatedVideos: null
|
||||
resEntrance: null
|
||||
sharedPrivilege: null
|
||||
urls: null
|
||||
}
|
||||
|
||||
// 推荐歌单
|
||||
export interface FetchRecommendedPlaylistsParams {
|
||||
limit?: number
|
||||
}
|
||||
export interface FetchRecommendedPlaylistsResponse {
|
||||
code: number
|
||||
category: number
|
||||
hasTaste: boolean
|
||||
result: Playlist[]
|
||||
}
|
||||
|
||||
// 每日推荐歌单(需要登录)
|
||||
export interface FetchDailyRecommendPlaylistsResponse {
|
||||
code: number
|
||||
featureFirst: boolean
|
||||
haveRcmdSongs: boolean
|
||||
recommend: Playlist[]
|
||||
}
|
||||
|
||||
export interface LikeAPlaylistParams {
|
||||
t: 1 | 2
|
||||
id: number
|
||||
}
|
||||
export interface LikeAPlaylistResponse {
|
||||
code: number
|
||||
}
|
82
src/shared/api/Search.ts
Normal file
82
src/shared/api/Search.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
export enum SearchApiNames {
|
||||
SEARCH = 'search',
|
||||
MULTI_MATCH_SEARCH = 'multiMatchSearch',
|
||||
}
|
||||
|
||||
// 搜索
|
||||
export enum SearchTypes {
|
||||
SINGLE = '1',
|
||||
ALBUM = '10',
|
||||
ARTIST = '100',
|
||||
PLAYLIST = '1000',
|
||||
USER = '1002',
|
||||
MV = '1004',
|
||||
LYRICS = '1006',
|
||||
RADIO = '1009',
|
||||
VIDEO = '1014',
|
||||
ALL = '1018',
|
||||
}
|
||||
export interface SearchParams {
|
||||
keywords: string
|
||||
limit?: number // 返回数量 , 默认为 30
|
||||
offset?: number // 偏移数量,用于分页 , 如 : 如 :( 页数 -1)*30, 其中 30 为 limit 的值 , 默认为 0
|
||||
type: keyof typeof SearchTypes // type: 搜索类型
|
||||
}
|
||||
export interface SearchResponse {
|
||||
code: number
|
||||
result: {
|
||||
album: {
|
||||
albums: Album[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
artist: {
|
||||
artists: Artist[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
playList: {
|
||||
playLists: Playlist[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
song: {
|
||||
songs: Track[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
user: {
|
||||
users: User[]
|
||||
more: boolean
|
||||
moreText: string
|
||||
resourceIds: number[]
|
||||
}
|
||||
circle: unknown
|
||||
new_mlog: unknown
|
||||
order: string[]
|
||||
rec_type: null
|
||||
rec_query: null[]
|
||||
sim_query: unknown
|
||||
voice: unknown
|
||||
voiceList: unknown
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索多重匹配
|
||||
export interface MultiMatchSearchParams {
|
||||
keywords: string
|
||||
}
|
||||
export interface MultiMatchSearchResponse {
|
||||
code: number
|
||||
result: {
|
||||
album: Album[]
|
||||
artist: Artist[]
|
||||
playlist: Playlist[]
|
||||
orpheus: unknown
|
||||
orders: Array<'artist' | 'album'>
|
||||
}
|
||||
}
|
104
src/shared/api/Track.ts
Normal file
104
src/shared/api/Track.ts
Normal file
|
@ -0,0 +1,104 @@
|
|||
export enum TrackApiNames {
|
||||
FETCH_TRACKS = 'fetchTracks',
|
||||
FETCH_AUDIO_SOURCE = 'fetchAudioSource',
|
||||
FETCH_LYRIC = 'fetchLyric',
|
||||
}
|
||||
|
||||
// 获取歌曲详情
|
||||
export interface FetchTracksParams {
|
||||
ids: number[]
|
||||
}
|
||||
export interface FetchTracksResponse {
|
||||
code: number
|
||||
songs: Track[]
|
||||
privileges: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
// 获取音源URL
|
||||
|
||||
export interface FetchAudioSourceParams {
|
||||
id: number
|
||||
br?: number // bitrate, default 999000,320000 = 320kbps
|
||||
}
|
||||
export interface FetchAudioSourceResponse {
|
||||
code: number
|
||||
data: {
|
||||
br: number
|
||||
canExtend: boolean
|
||||
code: number
|
||||
encodeType: 'mp3' | null
|
||||
expi: number
|
||||
fee: number
|
||||
flag: number
|
||||
freeTimeTrialPrivilege: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
freeTrialPrivilege: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
freeTrialInfo: null
|
||||
gain: number
|
||||
id: number
|
||||
level: 'standard' | 'null'
|
||||
md5: string | null
|
||||
payed: number
|
||||
size: number
|
||||
type: 'mp3' | null
|
||||
uf: null
|
||||
url: string | null
|
||||
urlSource: number
|
||||
}[]
|
||||
}
|
||||
|
||||
// 获取歌词
|
||||
|
||||
export interface FetchLyricParams {
|
||||
id: number
|
||||
}
|
||||
export interface FetchLyricResponse {
|
||||
code: number
|
||||
sgc: boolean
|
||||
sfy: boolean
|
||||
qfy: boolean
|
||||
lyricUser?: {
|
||||
id: number
|
||||
status: number
|
||||
demand: number
|
||||
userid: number
|
||||
nickname: string
|
||||
uptime: number
|
||||
}
|
||||
transUser?: {
|
||||
id: number
|
||||
status: number
|
||||
demand: number
|
||||
userid: number
|
||||
nickname: string
|
||||
uptime: number
|
||||
}
|
||||
lrc: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
klyric?: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
tlyric?: {
|
||||
version: number
|
||||
lyric: string
|
||||
}
|
||||
}
|
||||
|
||||
// 收藏歌曲
|
||||
export interface LikeATrackParams {
|
||||
id: number
|
||||
like: boolean
|
||||
}
|
||||
export interface LikeATrackResponse {
|
||||
code: number
|
||||
playlistId: number
|
||||
songs: Track[]
|
||||
}
|
108
src/shared/api/User.ts
Normal file
108
src/shared/api/User.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
export enum UserApiNames {
|
||||
FETCH_USER_ACCOUNT = 'fetchUserAccount',
|
||||
FETCH_USER_LIKED_TRACKS_IDS = 'fetchUserLikedTracksIDs',
|
||||
FETCH_USER_PLAYLISTS = 'fetchUserPlaylists',
|
||||
FETCH_USER_ALBUMS = 'fetchUserAlbums',
|
||||
FETCH_USER_ARTIST = 'fetchUserArtists',
|
||||
}
|
||||
|
||||
// 获取账号详情
|
||||
export interface FetchUserAccountResponse {
|
||||
code: number
|
||||
account: {
|
||||
anonimousUser: boolean
|
||||
ban: number
|
||||
baoyueVersion: number
|
||||
createTime: number
|
||||
donateVersion: number
|
||||
id: number
|
||||
paidFee: boolean
|
||||
status: number
|
||||
tokenVersion: number
|
||||
type: number
|
||||
userName: string
|
||||
vipType: number
|
||||
whitelistAuthority: number
|
||||
} | null
|
||||
profile: {
|
||||
userId: number
|
||||
userType: number
|
||||
nickname: string
|
||||
avatarImgId: number
|
||||
avatarUrl: string
|
||||
backgroundImgId: number
|
||||
backgroundUrl: string
|
||||
signature: string
|
||||
createTime: number
|
||||
userName: string
|
||||
accountType: number
|
||||
shortUserName: string
|
||||
birthday: number
|
||||
authority: number
|
||||
gender: number
|
||||
accountStatus: number
|
||||
province: number
|
||||
city: number
|
||||
authStatus: number
|
||||
description: string | null
|
||||
detailDescription: string | null
|
||||
defaultAvatar: boolean
|
||||
expertTags: [] | null
|
||||
experts: [] | null
|
||||
djStatus: number
|
||||
locationStatus: number
|
||||
vipType: number
|
||||
followed: boolean
|
||||
mutual: boolean
|
||||
authenticated: boolean
|
||||
lastLoginTime: number
|
||||
lastLoginIP: string
|
||||
remarkName: string | null
|
||||
viptypeVersion: number
|
||||
authenticationTypes: number
|
||||
avatarDetail: string | null
|
||||
anchor: boolean
|
||||
} | null
|
||||
}
|
||||
|
||||
// 获取用户歌单
|
||||
export interface FetchUserPlaylistsParams {
|
||||
uid: number
|
||||
offset: number
|
||||
limit?: number // default 30
|
||||
}
|
||||
export interface FetchUserPlaylistsResponse {
|
||||
code: number
|
||||
more: boolean
|
||||
version: string
|
||||
playlist: Playlist[]
|
||||
}
|
||||
|
||||
export interface FetchUserLikedTracksIDsParams {
|
||||
uid: number
|
||||
}
|
||||
export interface FetchUserLikedTracksIDsResponse {
|
||||
code: number
|
||||
checkPoint: number
|
||||
ids: number[]
|
||||
}
|
||||
|
||||
export interface FetchUserAlbumsParams {
|
||||
offset?: number // default 0
|
||||
limit?: number // default 25
|
||||
}
|
||||
export interface FetchUserAlbumsResponse {
|
||||
code: number
|
||||
hasMore: boolean
|
||||
paidCount: number
|
||||
count: number
|
||||
data: Album[]
|
||||
}
|
||||
|
||||
// 获取收藏的歌手
|
||||
export interface FetchUserArtistsResponse {
|
||||
code: number
|
||||
hasMore: boolean
|
||||
count: number
|
||||
data: Artist[]
|
||||
}
|
19
src/shared/tsconfig.json
Normal file
19
src/shared/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "Node",
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["../*"]
|
||||
}
|
||||
},
|
||||
"include": ["./**/*.ts"]
|
||||
}
|
Loading…
Reference in New Issue
Block a user