Compare commits

...

19 Commits

Author SHA1 Message Date
jeffvli 0c7c0e488d temp 2 2025-09-07 12:24:21 -07:00
jeffvli a7430dae31 temp 2025-09-07 12:24:21 -07:00
jeffvli 98e8bda45d progress on subsonic api 2025-09-07 12:24:20 -07:00
jeffvli 96221c8fa7 fix various imports 2025-09-07 12:23:01 -07:00
jeffvli 8c7cac369a add experimental request logger 2025-09-07 12:21:07 -07:00
jeffvli f1c011f677 temp progress 2025-09-07 12:21:06 -07:00
jeffvli 351464c52d add missing i18n path resolution to main/preload 2025-09-07 12:21:06 -07:00
jeffvli da8ba31a88 scaffold new OS controller 2025-09-07 12:21:06 -07:00
jeffvli d8a8880e48 add console logger utility 2025-09-07 12:21:06 -07:00
jeffvli fe36535aee improve domain types to better match OS, update normalizer functions 2025-09-07 12:21:06 -07:00
jeffvli 67eec51e5f add new date format utility function 2025-09-07 12:21:06 -07:00
jeffvli a7f21db563 add new api controller, rework and rename types 2025-09-07 12:21:06 -07:00
jeffvli 6c360c3c19 add autogen opensubsonic schema 2025-09-07 12:21:06 -07:00
jeffvli 4d7779eae1 rename preload types file 2025-09-07 12:18:33 -07:00
jeffvli 71b307e4a6 add new api controller types 2025-09-07 12:18:33 -07:00
jeffvli a3a67d20a9 fix imports 2025-09-07 12:18:33 -07:00
jeffvli 1c22461ee4 move all domain types to separate files 2025-09-07 12:18:33 -07:00
jeffvli 7785874605 add i18n path to node tsconfig 2025-09-07 12:18:33 -07:00
jeffvli 9147b041f3 begin reorganizing domain types 2025-09-07 12:18:33 -07:00
231 changed files with 15051 additions and 3419 deletions
+2
View File
@@ -30,6 +30,7 @@ const config: UserConfig = {
],
resolve: {
alias: {
'/@/i18n': resolve('src/i18n'),
'/@/main': resolve('src/main'),
'/@/shared': resolve('src/shared'),
},
@@ -39,6 +40,7 @@ const config: UserConfig = {
plugins: [externalizeDepsPlugin()],
resolve: {
alias: {
'/@/i18n': resolve('src/i18n'),
'/@/preload': resolve('src/preload'),
'/@/shared': resolve('src/shared'),
},
+1 -1
View File
@@ -6,7 +6,7 @@ import eslintPluginReactHooks from 'eslint-plugin-react-hooks';
import eslintPluginReactRefresh from 'eslint-plugin-react-refresh';
export default tseslint.config(
{ ignores: ['**/node_modules', '**/dist', '**/out'] },
{ ignores: ['**/node_modules', '**/dist', '**/out', '**/*-schema.d.ts'] },
tseslint.configs.recommended,
perfectionist.configs['recommended-natural'],
eslintPluginReact.configs.flat.recommended,
+8 -3
View File
@@ -27,6 +27,8 @@
"dev": "electron-vite dev",
"dev:remote": "vite dev --config remote.vite.config.ts",
"dev:watch": "electron-vite dev --watch",
"generate-api": "pnpm run generate-api:subsonic",
"generate-api:subsonic": "openapi-typescript https://opensubsonic.netlify.app/docs/openapi/openapi.json -o ./src/shared/api/subsonic/subsonic-schema.d.ts",
"i18next": "i18next -c src/i18n/i18next-parser.config.js",
"postinstall": "electron-builder install-app-deps",
"lint": "pnpm run lint-code && pnpm run lint-styles",
@@ -71,9 +73,9 @@
"@mantine/hooks": "^8.2.8",
"@mantine/modals": "^8.2.8",
"@mantine/notifications": "^8.2.8",
"@tanstack/react-query": "^4.32.1",
"@tanstack/react-query-devtools": "^4.32.1",
"@tanstack/react-query-persist-client": "^4.32.1",
"@tanstack/react-query": "^5.83.0",
"@tanstack/react-query-devtools": "^5.83.0",
"@tanstack/react-query-persist-client": "^5.83.0",
"@ts-rest/core": "^3.23.0",
"@xhayper/discord-rpc": "^1.3.0",
"audiomotion-analyzer": "^4.5.0",
@@ -103,6 +105,7 @@
"mpris-service": "^2.1.2",
"nanoid": "^3.3.3",
"node-mpv": "github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f",
"openapi-fetch": "^0.14.0",
"overlayscrollbars": "^2.11.1",
"overlayscrollbars-react": "^0.5.6",
"qs": "^6.14.0",
@@ -135,6 +138,7 @@
"@types/lodash": "^4.17.18",
"@types/md5": "^2.3.5",
"@types/node": "^22.15.32",
"@types/qs": "^6.14.0",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
"@types/react-window": "^1.8.5",
@@ -155,6 +159,7 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.19",
"i18next-parser": "^9.0.2",
"openapi-typescript": "^7.8.0",
"postcss-preset-mantine": "^1.17.0",
"prettier": "^3.5.3",
"prettier-plugin-packagejson": "^2.5.14",
+226 -122
View File
@@ -60,14 +60,14 @@ importers:
specifier: ^8.2.8
version: 8.2.8(@mantine/core@8.2.8(@mantine/hooks@8.2.8(react@19.1.0))(@types/react@18.3.23)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mantine/hooks@8.2.8(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/react-query':
specifier: ^4.32.1
version: 4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
specifier: ^5.83.0
version: 5.83.0(react@19.1.0)
'@tanstack/react-query-devtools':
specifier: ^4.32.1
version: 4.36.1(@tanstack/react-query@4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
specifier: ^5.83.0
version: 5.83.0(@tanstack/react-query@5.83.0(react@19.1.0))(react@19.1.0)
'@tanstack/react-query-persist-client':
specifier: ^4.32.1
version: 4.36.1(@tanstack/react-query@4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))
specifier: ^5.83.0
version: 5.83.0(@tanstack/react-query@5.83.0(react@19.1.0))(react@19.1.0)
'@ts-rest/core':
specifier: ^3.23.0
version: 3.52.1(@types/node@22.15.32)(zod@3.25.23)
@@ -155,6 +155,9 @@ importers:
node-mpv:
specifier: github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f
version: https://codeload.github.com/jeffvli/Node-MPV/tar.gz/32b4d64395289ad710c41d481d2707a7acfc228f
openapi-fetch:
specifier: ^0.14.0
version: 0.14.0
overlayscrollbars:
specifier: ^2.11.1
version: 2.11.3
@@ -246,6 +249,9 @@ importers:
'@types/node':
specifier: ^22.15.32
version: 22.15.32
'@types/qs':
specifier: ^6.14.0
version: 6.14.0
'@types/react':
specifier: ^18.3.23
version: 18.3.23
@@ -266,7 +272,7 @@ importers:
version: 8.18.1
'@vitejs/plugin-react':
specifier: ^4.3.4
version: 4.5.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2))
version: 4.5.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0))
concurrently:
specifier: ^7.1.0
version: 7.6.0
@@ -284,7 +290,7 @@ importers:
version: 3.2.1
electron-vite:
specifier: ^3.1.0
version: 3.1.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2))
version: 3.1.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0))
eslint:
specifier: ^9.24.0
version: 9.27.0
@@ -306,6 +312,9 @@ importers:
i18next-parser:
specifier: ^9.0.2
version: 9.3.0
openapi-typescript:
specifier: ^7.8.0
version: 7.8.0(typescript@5.8.3)
postcss-preset-mantine:
specifier: ^1.17.0
version: 1.17.0(postcss@8.5.3)
@@ -335,7 +344,7 @@ importers:
version: 5.8.3
vite:
specifier: ^6.3.5
version: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)
version: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0)
vite-plugin-conditional-import:
specifier: ^0.1.7
version: 0.1.7
@@ -344,7 +353,7 @@ importers:
version: 1.6.0
vite-plugin-ejs:
specifier: ^1.7.0
version: 1.7.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2))
version: 1.7.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0))
packages:
@@ -1040,6 +1049,16 @@ packages:
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
'@redocly/ajv@8.11.2':
resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
'@redocly/config@0.22.2':
resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
'@redocly/openapi-core@1.34.3':
resolution: {integrity: sha512-3arRdUp1fNx55itnjKiUhO6t4Mf91TsrTIYINDNLAZPS0TPd5YpiXRctwjel0qqWoOOhjA34cZ3m4dksLDFUYg==}
engines: {node: '>=18.17.0', npm: '>=9.5.0'}
'@remix-run/router@1.23.0':
resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==}
engines: {node: '>=14.0.0'}
@@ -1163,39 +1182,31 @@ packages:
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
engines: {node: '>=10'}
'@tanstack/match-sorter-utils@8.19.4':
resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==}
engines: {node: '>=12'}
'@tanstack/query-core@5.83.0':
resolution: {integrity: sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA==}
'@tanstack/query-core@4.36.1':
resolution: {integrity: sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==}
'@tanstack/query-devtools@5.81.2':
resolution: {integrity: sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg==}
'@tanstack/query-persist-client-core@4.36.1':
resolution: {integrity: sha512-eocgCeI7D7TRv1IUUBMfVwOI0wdSmMkBIbkKhqEdTrnUHUQEeOaYac8oeZk2cumAWJdycu6P/wB+WqGynTnzXg==}
'@tanstack/query-persist-client-core@5.83.0':
resolution: {integrity: sha512-hdKgHkr1MYnwZX+QHj/9JjXZx9gL2RUCD5xSX0EHZiqUQhMk4Gcryq9xosn8LmYRMlhkjk7n9uV+X4UXRvgoIg==}
'@tanstack/react-query-devtools@4.36.1':
resolution: {integrity: sha512-WYku83CKP3OevnYSG8Y/QO9g0rT75v1om5IvcWUwiUZJ4LanYGLVCZ8TdFG5jfsq4Ej/lu2wwDAULEUnRIMBSw==}
'@tanstack/react-query-devtools@5.83.0':
resolution: {integrity: sha512-yfp8Uqd3I1jgx8gl0lxbSSESu5y4MO2ThOPBnGNTYs0P+ZFu+E9g5IdOngyUGuo6Uz6Qa7p9TLdZEX3ntik2fQ==}
peerDependencies:
'@tanstack/react-query': ^4.36.1
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
'@tanstack/react-query': ^5.83.0
react: ^18 || ^19
'@tanstack/react-query-persist-client@4.36.1':
resolution: {integrity: sha512-32I5b9aAu4NCiXZ7Te/KEQLfHbYeTNriVPrKYcvEThnZ9tlW01vLcSoxpUIsMYRsembvJUUAkzYBAiZHLOd6pQ==}
'@tanstack/react-query-persist-client@5.83.0':
resolution: {integrity: sha512-uEqJnSbqlvzlhYJ+RU+2c2DmbbT7cw6eFjiewEXZFXaSGWNjvUG02LePrwL8cdLlRQFcZKas30IdckboOoVg9Q==}
peerDependencies:
'@tanstack/react-query': ^4.36.1
'@tanstack/react-query': ^5.83.0
react: ^18 || ^19
'@tanstack/react-query@4.36.1':
resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==}
'@tanstack/react-query@5.83.0':
resolution: {integrity: sha512-/XGYhZ3foc5H0VM2jLSD/NyBRIOK4q9kfeml4+0x2DlL6xVuAcVEW+hTlTapAmejObg0i3eNqhkr2dT+eciwoQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
react: ^18 || ^19
'@tootallnate/once@2.0.0':
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
@@ -1269,6 +1280,9 @@ packages:
'@types/prop-types@15.7.14':
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
'@types/qs@6.14.0':
resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==}
'@types/react-dom@18.3.7':
resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==}
peerDependencies:
@@ -1428,6 +1442,10 @@ packages:
ajv@8.17.1:
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
ansi-colors@4.1.3:
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
engines: {node: '>=6'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -1674,6 +1692,9 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
change-case@5.4.4:
resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==}
charenc@0.0.2:
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
@@ -1752,6 +1773,9 @@ packages:
colord@2.9.3:
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
colorette@1.4.0:
resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
colorjs.io@0.5.2:
resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==}
@@ -1800,10 +1824,6 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
copy-anything@3.0.5:
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
engines: {node: '>=12.13'}
core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@@ -2701,6 +2721,10 @@ packages:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'}
index-to-position@1.1.0:
resolution: {integrity: sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==}
engines: {node: '>=18'}
infer-owner@1.0.4:
resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
@@ -2870,10 +2894,6 @@ packages:
resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
engines: {node: '>= 0.4'}
is-what@4.1.16:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
engines: {node: '>=12.13'}
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
@@ -2903,6 +2923,10 @@ packages:
engines: {node: '>=10'}
hasBin: true
js-levenshtein@1.1.6:
resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
engines: {node: '>=0.10.0'}
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -3339,6 +3363,18 @@ packages:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
openapi-fetch@0.14.0:
resolution: {integrity: sha512-PshIdm1NgdLvb05zp8LqRQMNSKzIlPkyMxYFxwyHR+UlKD4t2nUjkDhNxeRbhRSEd3x5EUNh2w5sJYwkhOH4fg==}
openapi-typescript-helpers@0.0.15:
resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==}
openapi-typescript@7.8.0:
resolution: {integrity: sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==}
hasBin: true
peerDependencies:
typescript: ^5.x
optionator@0.9.4:
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
@@ -3402,6 +3438,10 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
parse-json@8.3.0:
resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
engines: {node: '>=18'}
parse5-htmlparser2-tree-adapter@7.1.0:
resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==}
@@ -3470,6 +3510,10 @@ packages:
resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==}
engines: {node: '>=10.4.0'}
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -3797,9 +3841,6 @@ packages:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
remove-accents@0.5.0:
resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
remove-trailing-separator@1.1.0:
resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==}
@@ -4288,9 +4329,9 @@ packages:
resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==}
engines: {node: '>= 8.0'}
superjson@1.13.3:
resolution: {integrity: sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg==}
engines: {node: '>=10'}
supports-color@10.0.0:
resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==}
engines: {node: '>=18'}
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
@@ -4489,6 +4530,9 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
uri-js-replace@1.0.1:
resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -4744,6 +4788,14 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
yaml-ast-parser@0.0.43:
resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
yaml@2.8.0:
resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==}
engines: {node: '>= 14.6'}
hasBin: true
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -4851,7 +4903,7 @@ snapshots:
'@babel/traverse': 7.27.1
'@babel/types': 7.27.1
convert-source-map: 2.0.0
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -4937,7 +4989,7 @@ snapshots:
'@babel/parser': 7.27.2
'@babel/template': 7.27.2
'@babel/types': 7.27.1
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@@ -5039,7 +5091,7 @@ snapshots:
'@electron/get@2.0.3':
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
env-paths: 2.2.1
fs-extra: 8.1.0
got: 11.8.6
@@ -5069,7 +5121,7 @@ snapshots:
'@electron/notarize@2.5.0':
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fs-extra: 9.1.0
promise-retry: 2.0.1
transitivePeerDependencies:
@@ -5078,7 +5130,7 @@ snapshots:
'@electron/osx-sign@1.3.1':
dependencies:
compare-version: 0.1.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fs-extra: 10.1.0
isbinaryfile: 4.0.10
minimist: 1.2.8
@@ -5091,7 +5143,7 @@ snapshots:
'@electron/node-gyp': https://codeload.github.com/electron/node-gyp/tar.gz/06b29aafb7708acef8b3669835c8a7857ebc92d2
'@malept/cross-spawn-promise': 2.0.0
chalk: 4.1.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
detect-libc: 2.0.4
fs-extra: 10.1.0
got: 11.8.6
@@ -5110,7 +5162,7 @@ snapshots:
dependencies:
'@electron/asar': 3.2.18
'@malept/cross-spawn-promise': 2.0.0
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
dir-compare: 4.2.0
fs-extra: 11.3.1
minimatch: 9.0.5
@@ -5121,7 +5173,7 @@ snapshots:
'@electron/windows-sign@1.2.2':
dependencies:
cross-dirname: 0.1.0
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fs-extra: 11.3.1
minimist: 1.2.8
postject: 1.0.0-alpha.6
@@ -5214,7 +5266,7 @@ snapshots:
'@eslint/config-array@0.20.0':
dependencies:
'@eslint/object-schema': 2.1.6
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
minimatch: 3.1.2
transitivePeerDependencies:
- supports-color
@@ -5232,7 +5284,7 @@ snapshots:
'@eslint/eslintrc@3.3.1':
dependencies:
ajv: 6.12.6
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
espree: 10.3.0
globals: 14.0.0
ignore: 5.3.2
@@ -5337,7 +5389,7 @@ snapshots:
'@malept/flatpak-bundler@0.4.0':
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fs-extra: 9.1.0
lodash: 4.17.21
tmp-promise: 3.0.3
@@ -5547,6 +5599,29 @@ snapshots:
'@babel/runtime': 7.27.1
react: 19.1.0
'@redocly/ajv@8.11.2':
dependencies:
fast-deep-equal: 3.1.3
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
uri-js-replace: 1.0.1
'@redocly/config@0.22.2': {}
'@redocly/openapi-core@1.34.3(supports-color@10.0.0)':
dependencies:
'@redocly/ajv': 8.11.2
'@redocly/config': 0.22.2
colorette: 1.4.0
https-proxy-agent: 7.0.6(supports-color@10.0.0)
js-levenshtein: 1.1.6
js-yaml: 4.1.0
minimatch: 5.1.6
pluralize: 8.0.0
yaml-ast-parser: 0.0.43
transitivePeerDependencies:
- supports-color
'@remix-run/router@1.23.0': {}
'@rolldown/pluginutils@1.0.0-beta.9': {}
@@ -5621,37 +5696,30 @@ snapshots:
dependencies:
defer-to-connect: 2.0.1
'@tanstack/match-sorter-utils@8.19.4':
dependencies:
remove-accents: 0.5.0
'@tanstack/query-core@5.83.0': {}
'@tanstack/query-core@4.36.1': {}
'@tanstack/query-devtools@5.81.2': {}
'@tanstack/query-persist-client-core@4.36.1':
'@tanstack/query-persist-client-core@5.83.0':
dependencies:
'@tanstack/query-core': 4.36.1
'@tanstack/query-core': 5.83.0
'@tanstack/react-query-devtools@4.36.1(@tanstack/react-query@4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
'@tanstack/react-query-devtools@5.83.0(@tanstack/react-query@5.83.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@tanstack/match-sorter-utils': 8.19.4
'@tanstack/react-query': 4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/query-devtools': 5.81.2
'@tanstack/react-query': 5.83.0(react@19.1.0)
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
superjson: 1.13.3
use-sync-external-store: 1.5.0(react@19.1.0)
'@tanstack/react-query-persist-client@4.36.1(@tanstack/react-query@4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))':
'@tanstack/react-query-persist-client@5.83.0(@tanstack/react-query@5.83.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@tanstack/query-persist-client-core': 4.36.1
'@tanstack/react-query': 4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@tanstack/react-query@4.36.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@tanstack/query-core': 4.36.1
'@tanstack/query-persist-client-core': 5.83.0
'@tanstack/react-query': 5.83.0(react@19.1.0)
react: 19.1.0
'@tanstack/react-query@5.83.0(react@19.1.0)':
dependencies:
'@tanstack/query-core': 5.83.0
react: 19.1.0
use-sync-external-store: 1.5.0(react@19.1.0)
optionalDependencies:
react-dom: 19.1.0(react@19.1.0)
'@tootallnate/once@2.0.0': {}
@@ -5732,6 +5800,8 @@ snapshots:
'@types/prop-types@15.7.14': {}
'@types/qs@6.14.0': {}
'@types/react-dom@18.3.7(@types/react@18.3.23)':
dependencies:
'@types/react': 18.3.23
@@ -5798,7 +5868,7 @@ snapshots:
'@typescript-eslint/types': 8.32.1
'@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3)
'@typescript-eslint/visitor-keys': 8.32.1
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
eslint: 9.27.0
typescript: 5.8.3
transitivePeerDependencies:
@@ -5813,7 +5883,7 @@ snapshots:
dependencies:
'@typescript-eslint/typescript-estree': 8.32.1(typescript@5.8.3)
'@typescript-eslint/utils': 8.32.1(eslint@9.27.0)(typescript@5.8.3)
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
eslint: 9.27.0
ts-api-utils: 2.1.0(typescript@5.8.3)
typescript: 5.8.3
@@ -5826,7 +5896,7 @@ snapshots:
dependencies:
'@typescript-eslint/types': 8.32.1
'@typescript-eslint/visitor-keys': 8.32.1
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fast-glob: 3.3.3
is-glob: 4.0.3
minimatch: 9.0.5
@@ -5852,7 +5922,7 @@ snapshots:
'@typescript-eslint/types': 8.32.1
eslint-visitor-keys: 4.2.0
'@vitejs/plugin-react@4.5.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2))':
'@vitejs/plugin-react@4.5.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0))':
dependencies:
'@babel/core': 7.27.1
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.1)
@@ -5860,7 +5930,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.9
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
vite: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)
vite: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0)
transitivePeerDependencies:
- supports-color
@@ -5896,7 +5966,7 @@ snapshots:
agent-base@6.0.2:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
@@ -5933,6 +6003,8 @@ snapshots:
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
ansi-colors@4.1.3: {}
ansi-regex@5.0.1: {}
ansi-regex@6.1.0: {}
@@ -5966,7 +6038,7 @@ snapshots:
builder-util-runtime: 9.3.1
chromium-pickle-js: 0.2.0
config-file-ts: 0.2.8-rc1
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
dmg-builder: 26.0.12(electron-builder-squirrel-windows@26.0.12)
dotenv: 16.5.0
dotenv-expand: 11.0.7
@@ -6186,7 +6258,7 @@ snapshots:
builder-util-runtime@9.3.1:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
sax: 1.4.1
transitivePeerDependencies:
- supports-color
@@ -6199,10 +6271,10 @@ snapshots:
builder-util-runtime: 9.3.1
chalk: 4.1.2
cross-spawn: 7.0.6
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fs-extra: 10.1.0
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
https-proxy-agent: 7.0.6(supports-color@10.0.0)
is-ci: 3.0.1
js-yaml: 4.1.0
sanitize-filename: 1.6.3
@@ -6283,6 +6355,8 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
change-case@5.4.4: {}
charenc@0.0.2: {}
cheerio-select@2.1.0:
@@ -6364,6 +6438,8 @@ snapshots:
colord@2.9.3: {}
colorette@1.4.0: {}
colorjs.io@0.5.2: {}
colors@1.4.0: {}
@@ -6418,10 +6494,6 @@ snapshots:
convert-source-map@2.0.0: {}
copy-anything@3.0.5:
dependencies:
is-what: 4.1.16
core-util-is@1.0.2:
optional: true
@@ -6521,9 +6593,11 @@ snapshots:
dependencies:
ms: 2.0.0
debug@4.4.1:
debug@4.4.1(supports-color@10.0.0):
dependencies:
ms: 2.1.3
optionalDependencies:
supports-color: 10.0.0
decompress-response@6.0.0:
dependencies:
@@ -6713,7 +6787,7 @@ snapshots:
electron-localshortcut@3.2.1:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
electron-is-accelerator: 0.1.2
keyboardevent-from-electron-accelerator: 2.0.0
keyboardevents-areequal: 0.2.2
@@ -6755,7 +6829,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron-vite@3.1.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)):
electron-vite@3.1.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0)):
dependencies:
'@babel/core': 7.27.1
'@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.1)
@@ -6763,14 +6837,14 @@ snapshots:
esbuild: 0.25.4
magic-string: 0.30.17
picocolors: 1.1.1
vite: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)
vite: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0)
transitivePeerDependencies:
- supports-color
electron-winstaller@5.4.0:
dependencies:
'@electron/asar': 3.4.1
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fs-extra: 7.0.1
lodash: 4.17.21
temp: 0.9.4
@@ -7038,7 +7112,7 @@ snapshots:
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
escape-string-regexp: 4.0.0
eslint-scope: 8.3.0
eslint-visitor-keys: 4.2.0
@@ -7092,7 +7166,7 @@ snapshots:
extract-zip@2.0.1:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
@@ -7499,14 +7573,14 @@ snapshots:
dependencies:
'@tootallnate/once': 2.0.0
agent-base: 6.0.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.3
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
@@ -7518,14 +7592,14 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6:
https-proxy-agent@7.0.6(supports-color@10.0.0):
dependencies:
agent-base: 7.1.3
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
@@ -7598,6 +7672,8 @@ snapshots:
indent-string@4.0.0: {}
index-to-position@1.1.0: {}
infer-owner@1.0.4: {}
inflight@1.0.6:
@@ -7758,8 +7834,6 @@ snapshots:
call-bound: 1.0.4
get-intrinsic: 1.3.0
is-what@4.1.16: {}
isarray@1.0.0: {}
isarray@2.0.5: {}
@@ -7792,6 +7866,8 @@ snapshots:
filelist: 1.0.4
minimatch: 3.1.2
js-levenshtein@1.1.6: {}
js-tokens@4.0.0: {}
js-yaml@4.1.0:
@@ -8192,6 +8268,22 @@ snapshots:
dependencies:
mimic-fn: 2.1.0
openapi-fetch@0.14.0:
dependencies:
openapi-typescript-helpers: 0.0.15
openapi-typescript-helpers@0.0.15: {}
openapi-typescript@7.8.0(typescript@5.8.3):
dependencies:
'@redocly/openapi-core': 1.34.3(supports-color@10.0.0)
ansi-colors: 4.1.3
change-case: 5.4.4
parse-json: 8.3.0
supports-color: 10.0.0
typescript: 5.8.3
yargs-parser: 21.1.1
optionator@0.9.4:
dependencies:
deep-is: 0.1.4
@@ -8265,6 +8357,12 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
parse-json@8.3.0:
dependencies:
'@babel/code-frame': 7.27.1
index-to-position: 1.1.0
type-fest: 4.41.0
parse5-htmlparser2-tree-adapter@7.1.0:
dependencies:
domhandler: 5.0.3
@@ -8321,6 +8419,8 @@ snapshots:
base64-js: 1.5.1
xmlbuilder: 15.1.1
pluralize@8.0.0: {}
possible-typed-array-names@1.1.0: {}
postcss-js@4.0.1(postcss@8.5.3):
@@ -8596,7 +8696,7 @@ snapshots:
read-binary-file-arch@1.0.6:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
@@ -8636,8 +8736,6 @@ snapshots:
gopd: 1.2.0
set-function-name: 2.0.2
remove-accents@0.5.0: {}
remove-trailing-separator@1.1.0: {}
replace-ext@2.0.0: {}
@@ -8966,7 +9064,7 @@ snapshots:
socks-proxy-agent@7.0.0:
dependencies:
agent-base: 6.0.2
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
socks: 2.8.6
transitivePeerDependencies:
- supports-color
@@ -9157,7 +9255,7 @@ snapshots:
cosmiconfig: 9.0.0(typescript@5.8.3)
css-functions-list: 3.2.3
css-tree: 3.1.0
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
fast-glob: 3.3.3
fastest-levenshtein: 1.0.16
file-entry-cache: 10.1.1
@@ -9195,13 +9293,11 @@ snapshots:
sumchecker@3.0.1:
dependencies:
debug: 4.4.1
debug: 4.4.1(supports-color@10.0.0)
transitivePeerDependencies:
- supports-color
superjson@1.13.3:
dependencies:
copy-anything: 3.0.5
supports-color@10.0.0: {}
supports-color@7.2.0:
dependencies:
@@ -9421,6 +9517,8 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
uri-js-replace@1.0.1: {}
uri-js@4.4.1:
dependencies:
punycode: 2.3.1
@@ -9529,12 +9627,12 @@ snapshots:
fast-glob: 3.3.3
magic-string: 0.30.17
vite-plugin-ejs@1.7.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)):
vite-plugin-ejs@1.7.0(vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0)):
dependencies:
ejs: 3.1.10
vite: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)
vite: 6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0)
vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2):
vite@6.3.5(@types/node@22.15.32)(sass-embedded@1.89.0)(sugarss@4.0.1(postcss@8.5.3))(terser@5.39.2)(yaml@2.8.0):
dependencies:
esbuild: 0.25.4
fdir: 6.4.4(picomatch@4.0.2)
@@ -9548,6 +9646,7 @@ snapshots:
sass-embedded: 1.89.0
sugarss: 4.0.1(postcss@8.5.3)
terser: 5.39.2
yaml: 2.8.0
void-elements@3.1.0: {}
@@ -9661,6 +9760,11 @@ snapshots:
yallist@4.0.0: {}
yaml-ast-parser@0.0.43: {}
yaml@2.8.0:
optional: true
yargs-parser@21.1.1: {}
yargs@17.7.2:
+3 -1
View File
@@ -1,5 +1,7 @@
module.exports = {
plugins: {
'postcss-preset-mantine': {},
'postcss-preset-mantine': {
mixins: {},
},
},
};
+5 -1
View File
@@ -227,7 +227,11 @@
"songCount": "song count",
"title": "title",
"toYear": "to year",
"trackNumber": "track"
"trackNumber": "track",
"createdAt": "created at",
"updatedAt": "updated at",
"type": "type",
"email": "email"
},
"form": {
"addServer": {
+3 -2
View File
@@ -1,13 +1,14 @@
import axios, { AxiosResponse } from 'axios';
import { load } from 'cheerio';
import { orderSearchResults } from './shared';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
} from '.';
import { orderSearchResults } from './shared';
} from '/@/shared/types/domain/lyric-domain-types';
const SEARCH_URL = 'https://genius.com/api/search/song';
+6 -29
View File
@@ -17,35 +17,12 @@ import {
getSearchResults as searchNetease,
} from './netease';
import { Song } from '/@/shared/types/domain-types';
export enum LyricSource {
GENIUS = 'Genius',
LRCLIB = 'lrclib.net',
NETEASE = 'NetEase',
}
export type FullLyricsMetadata = Omit<InternetProviderLyricResponse, 'id' | 'lyrics' | 'source'> & {
lyrics: LyricsResponse;
remote: boolean;
source: string;
};
export type InternetProviderLyricResponse = {
artist: string;
id: string;
lyrics: string;
name: string;
source: LyricSource;
};
export type InternetProviderLyricSearchResponse = {
artist: string;
id: string;
name: string;
score?: number;
source: LyricSource;
};
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSource,
} from '/@/shared/types/domain/lyric-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
export type LyricGetQuery = {
remoteSongId: string;
+3 -2
View File
@@ -1,13 +1,14 @@
// Credits to https://github.com/tranxuanthang/lrcget for API implementation
import axios, { AxiosResponse } from 'axios';
import { orderSearchResults } from './shared';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
} from '.';
import { orderSearchResults } from './shared';
} from '/@/shared/types/domain/lyric-domain-types';
const FETCH_URL = 'https://lrclib.net/api/get';
const SEEARCH_URL = 'https://lrclib.net/api/search';
+28 -27
View File
@@ -1,43 +1,20 @@
import axios, { AxiosResponse } from 'axios';
import { store } from '../settings';
import { orderSearchResults } from './shared';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
} from '.';
import { store } from '../settings';
import { orderSearchResults } from './shared';
} from '/@/shared/types/domain/lyric-domain-types';
const SEARCH_URL = 'https://music.163.com/api/search/get';
const LYRICS_URL = 'https://music.163.com/api/song/lyric';
// Adapted from https://github.com/NyaomiDEV/Sunamu/blob/master/src/main/lyricproviders/netease.ts
export interface Result {
hasMore: boolean;
songCount: number;
songs: Song[];
}
export interface Song {
album: Album;
alias: string[];
artists: Artist[];
copyrightId: number;
duration: number;
fee: number;
ftype: number;
id: number;
mark: number;
mvid: number;
name: string;
rtype: number;
rUrl: null;
status: number;
transNames?: string[];
}
interface Album {
artist: Artist;
copyrightId: number;
@@ -69,6 +46,30 @@ interface NetEaseResponse {
result: Result;
}
interface Result {
hasMore: boolean;
songCount: number;
songs: Song[];
}
interface Song {
album: Album;
alias: string[];
artists: Artist[];
copyrightId: number;
duration: number;
fee: number;
ftype: number;
id: number;
mark: number;
mvid: number;
name: string;
rtype: number;
rUrl: null;
status: number;
transNames?: string[];
}
export async function getLyricsBySongId(songId: string): Promise<null | string> {
let result: AxiosResponse<any, any>;
try {
+2 -4
View File
@@ -1,9 +1,7 @@
import Fuse from 'fuse.js';
import {
InternetProviderLyricSearchResponse,
LyricSearchQuery,
} from '/@/shared/types/domain-types';
import { InternetProviderLyricSearchResponse } from '/@/shared/types/domain/lyric-domain-types';
import { LyricSearchQuery } from '/@/shared/types/domain/lyric-domain-types';
export const orderSearchResults = (args: {
params: LyricSearchQuery;
+2 -2
View File
@@ -11,8 +11,8 @@ import manifest from './manifest.json';
import { getMainWindow } from '/@/main/index';
import { isLinux } from '/@/main/utils';
import { QueueSong } from '/@/shared/types/domain-types';
import { ClientEvent, ServerEvent } from '/@/shared/types/remote-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { ClientEvent, ServerEvent } from '/@/shared/types/domain/remote-types';
import { PlayerRepeat, PlayerStatus, SongState } from '/@/shared/types/types';
let mprisPlayer: any | undefined;
+2 -2
View File
@@ -2,7 +2,7 @@ import { ipcMain } from 'electron';
import Player from 'mpris-service';
import { getMainWindow } from '/@/main/index';
import { QueueSong } from '/@/shared/types/domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { PlayerRepeat, PlayerStatus } from '/@/shared/types/types';
const mprisPlayer = Player({
@@ -168,7 +168,7 @@ ipcMain.on('update-song', (_event, song: QueueSong | undefined) => {
'xesam:contentCreated': song.releaseDate,
'xesam:discNumber': song.discNumber ? song.discNumber : null,
'xesam:genre': song.genres?.length ? song.genres.map((genre: any) => genre.name) : null,
'xesam:lastUsed': song.lastPlayedAt,
'xesam:lastUsed': song.userLastPlayedDate,
'xesam:title': song.name || null,
'xesam:trackNumber': song.trackNumber ? song.trackNumber : null,
'xesam:useCount':
+1 -1
View File
@@ -7,7 +7,7 @@ import {
LyricSource,
} from '../main/features/core/lyrics';
import { QueueSong } from '/@/shared/types/domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
const getRemoteLyricsBySong = (song: QueueSong) => {
const result = ipcRenderer.invoke('lyric-by-song', song);
+1 -1
View File
@@ -1,6 +1,6 @@
import { ipcRenderer, IpcRendererEvent } from 'electron';
import { PlayerData } from '/@/shared/types/domain-types';
import { PlayerData } from '/@/shared/types/domain/player-domain-types';
const initialize = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
return ipcRenderer.invoke('player-initialize', data);
+1 -1
View File
@@ -1,6 +1,6 @@
import { ipcRenderer, IpcRendererEvent } from 'electron';
import { QueueSong } from '/@/shared/types/domain-types';
import { QueueSong, QueueSong } from '/@/shared/types/domain/player-domain-types';
import { PlayerStatus } from '/@/shared/types/types';
const requestFavorite = (
+172
View File
@@ -0,0 +1,172 @@
import {
ApiController,
ApiControllerError,
ApiControllerFn,
} from '/@/shared/types/adapter/api-controller-types';
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
import { logger } from '/@/shared/utils/logger';
export interface LoggingOptions {
logErrors?: boolean;
logPerformance?: boolean;
logRequests?: boolean;
logResponses?: boolean;
maxRequestSize?: number;
maxResponseSize?: number;
}
type LoggedApiControllerFn<TRequest, TResponse> = (
request: TRequest,
server: ServerListItem,
options?: any,
) => Promise<[ApiControllerError, null] | [null, TResponse]>;
export function createLoggedApiController(
controller: ApiController,
options: LoggingOptions = {},
): ApiController {
const loggedController: any = {};
// Log utility functions
loggedController._utility = createLoggedUtility(controller._utility);
// Log all controller methods
for (const [sectionKey, section] of Object.entries(controller)) {
if (sectionKey === '_utility') continue;
loggedController[sectionKey] = {};
for (const [methodKey, method] of Object.entries(section as Record<string, any>)) {
if (typeof method === 'function') {
const functionName = `${sectionKey}.${methodKey}`;
if (methodKey === 'authenticate' || methodKey === 'getType') {
// Special handling for non-standard API functions
loggedController[sectionKey][methodKey] = (...args: any[]) => {
logger.info(`[API] ${functionName} called`, {
args: JSON.stringify(args, null, 2),
});
return method(...args);
};
} else {
loggedController[sectionKey][methodKey] = createLoggedFunction(
method as ApiControllerFn<any, any>,
functionName,
options,
);
}
} else {
loggedController[sectionKey][methodKey] = method;
}
}
}
return loggedController as ApiController;
}
function createLoggedFunction<TRequest, TResponse>(
originalFn: ApiControllerFn<TRequest, TResponse> | undefined,
functionName: string,
options: LoggingOptions = {},
): LoggedApiControllerFn<TRequest, TResponse> | undefined {
if (!originalFn) {
return undefined;
}
return async (request: TRequest, requestOptions?: any) => {
const startTime = Date.now();
const requestId = Math.random().toString(36).substring(2, 15);
const {
logErrors = true,
logPerformance = true,
logRequests = true,
logResponses = true,
maxRequestSize = 1000,
maxResponseSize = 1000,
} = options;
if (logRequests) {
const requestStr = JSON.stringify(request, null, 2);
const truncatedRequest =
requestStr.length > maxRequestSize
? requestStr.substring(0, maxRequestSize) + '...'
: requestStr;
logger.info(`[API] ${functionName} called`, {
request: truncatedRequest,
requestId,
});
}
try {
const result = await originalFn(request, requestOptions);
const duration = Date.now() - startTime;
if (result[0]) {
// Error response
if (logErrors) {
const error = result[0] as ApiControllerError;
logger.error(`[API] ${functionName} failed`, {
duration: logPerformance ? `${duration}ms` : undefined,
error: {
code: error.code,
message: error.message,
},
requestId,
});
}
} else {
// Success response
if (logResponses) {
const response = result[1];
const responseStr = JSON.stringify(response);
const truncatedResponse =
responseStr.length > maxResponseSize
? responseStr.substring(0, maxResponseSize) + '...'
: responseStr;
logger.info(`[API] ${functionName} succeeded`, {
duration: logPerformance ? `${duration}ms` : undefined,
requestId,
response: truncatedResponse,
responseSize: responseStr.length,
responseType: typeof response,
});
}
}
return result;
} catch (error) {
const duration = Date.now() - startTime;
if (logErrors) {
logger.error(`[API] ${functionName} threw exception`, {
duration: logPerformance ? `${duration}ms` : undefined,
error: error instanceof Error ? error.message : String(error),
requestId,
stack: error instanceof Error ? error.stack : undefined,
});
}
throw error;
}
};
}
function createLoggedUtility<T extends Record<string, any>>(utility: T): T {
const loggedUtility: any = {};
for (const [key, value] of Object.entries(utility)) {
if (typeof value === 'function') {
loggedUtility[key] = (...args: any[]) => {
logger.debug(`[API] _utility.${key} called`, {
args: JSON.stringify(args, null, 2),
});
return value(...args);
};
} else {
loggedUtility[key] = value;
}
}
return loggedUtility as T;
}
+75
View File
@@ -0,0 +1,75 @@
import { createLoggedApiController } from '/@/renderer/api/api-controller-logger';
import { useAuthStore } from '/@/renderer/store';
import {
createApiClient as subsonicApiClient,
authenticate as subsonicAuthenticate,
controller as subsonicController,
middleware as subsonicMiddleware,
} from '/@/shared/api/subsonic/subsonic-controller';
import { ApiController } from '/@/shared/types/adapter/api-controller-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
export const serverApiMap = {
[ServerType.JELLYFIN]: {
apiClient: null,
authenticate: null,
controller: {},
middleware: null,
},
[ServerType.NAVIDROME]: {
apiClient: null,
authenticate: null,
controller: {},
middleware: null,
},
[ServerType.SUBSONIC]: {
apiClient: subsonicApiClient,
authenticate: subsonicAuthenticate,
controller: subsonicController,
middleware: subsonicMiddleware,
},
};
const getApiByServer = (serverId: string): ApiController => {
const servers = useAuthStore.getState().serverList;
const server = servers[serverId];
if (!server) {
throw new Error('No server or api client selected');
}
const { apiClient, controller, middleware } = serverApiMap[server.type];
if (!apiClient) {
throw new Error('No api client found');
}
const client = apiClient(server, middleware);
return createLoggedApiController(controller(client, server));
};
const getAppApi = () => {
const servers = useAuthStore.getState().serverList;
return Object.entries(servers).reduce(
(acc, [id]) => {
acc[id] = getApiByServer(id);
return acc;
},
{} as Record<string, ApiController>,
);
};
export const api = {
authenticate: (serverType: ServerType) => {
const { authenticate } = serverApiMap[serverType];
if (!serverType || !authenticate) {
throw new Error();
}
return authenticate;
},
controller: getAppApi(),
};
+3 -5
View File
@@ -4,11 +4,9 @@ import { NavidromeController } from '/@/renderer/api/navidrome/navidrome-control
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
import { useAuthStore } from '/@/renderer/store';
import { toast } from '/@/shared/components/toast/toast';
import {
AuthenticationResponse,
ControllerEndpoint,
ServerType,
} from '/@/shared/types/domain-types';
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
import { AuthenticationResponse } from '/@/shared/types/domain/auth-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
type ApiController = {
jellyfin: ControllerEndpoint;
+1 -1
View File
@@ -11,7 +11,7 @@ import { authenticationFailure } from '/@/renderer/api/utils';
import { useAuthStore } from '/@/renderer/store';
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
import { getClientType } from '/@/shared/api/utils';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
const c = initContract();
@@ -6,19 +6,13 @@ import { JFSongListSort, JFSortOrder } from '/@/shared/api/jellyfin.types';
import { jfNormalize } from '/@/shared/api/jellyfin/jellyfin-normalize';
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
import {
albumArtistListSortMap,
albumListSortMap,
ControllerEndpoint,
genreListSortMap,
LibraryItem,
Played,
playlistListSortMap,
Song,
songListSortMap,
sortOrderMap,
} from '/@/shared/types/domain-types';
import { ServerFeature } from '/@/shared/types/features-types';
import { albumListSortMap } from '/@/shared/types/domain/album-domain-types';
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
import { albumArtistListSortMap } from '/@/shared/types/domain/artist-domain-types';
import { Played } from '/@/shared/types/domain/player-domain-types';
import { ServerFeature } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, sortOrderMap } from '/@/shared/types/domain/shared-domain-types';
import { Song, songListSortMap } from '/@/shared/types/domain/song-domain-types';
const formatCommaDelimitedString = (value: string[]) => {
return value.join(',');
@@ -326,7 +320,7 @@ export const JellyfinController: ControllerEndpoint = {
SearchTerm: query.searchTerm,
SortBy: albumListSortMap.jellyfin[query.sortBy] || 'SortName',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: query.startIndex,
StartIndex: query.offset,
...query._custom?.jellyfin,
Years: yearsFilter,
},
@@ -338,14 +332,14 @@ export const JellyfinController: ControllerEndpoint = {
return {
items: res.body.Items.map((item) => jfNormalize.album(item, apiClientProps.server)),
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount: res.body.TotalRecordCount,
};
},
getAlbumListCount: async ({ apiClientProps, query }) =>
JellyfinController.getAlbumList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getArtistList: async (args) => {
const { apiClientProps, query } = args;
@@ -360,7 +354,7 @@ export const JellyfinController: ControllerEndpoint = {
SearchTerm: query.searchTerm,
SortBy: albumArtistListSortMap.jellyfin[query.sortBy] || 'SortName,Name',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: query.startIndex,
StartIndex: query.offset,
UserId: apiClientProps.server?.userId || undefined,
},
});
@@ -373,14 +367,14 @@ export const JellyfinController: ControllerEndpoint = {
items: res.body.Items.map((item) =>
jfNormalize.albumArtist(item, apiClientProps.server),
),
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount: res.body.TotalRecordCount,
};
},
getArtistListCount: async ({ apiClientProps, query }) =>
JellyfinController.getArtistList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getDownloadUrl: (args) => {
const { apiClientProps, query } = args;
@@ -402,7 +396,7 @@ export const JellyfinController: ControllerEndpoint = {
SearchTerm: query?.searchTerm,
SortBy: genreListSortMap.jellyfin[query.sortBy] || 'SortName',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: query.startIndex,
StartIndex: query.offset,
UserId: apiClientProps.server?.userId,
},
});
@@ -413,7 +407,7 @@ export const JellyfinController: ControllerEndpoint = {
return {
items: res.body.Items.map((item) => jfNormalize.genre(item, apiClientProps.server)),
startIndex: query.startIndex || 0,
offset: query.offset || 0,
totalRecordCount: res.body?.TotalRecordCount || 0,
};
},
@@ -462,7 +456,7 @@ export const JellyfinController: ControllerEndpoint = {
return {
items: musicFolders.map(jfNormalize.musicFolder),
startIndex: 0,
offset: 0,
totalRecordCount: musicFolders?.length || 0,
};
},
@@ -509,7 +503,7 @@ export const JellyfinController: ControllerEndpoint = {
SearchTerm: query.searchTerm,
SortBy: playlistListSortMap.jellyfin[query.sortBy],
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: query.startIndex,
StartIndex: query.offset,
},
});
@@ -519,14 +513,14 @@ export const JellyfinController: ControllerEndpoint = {
return {
items: res.body.Items.map((item) => jfNormalize.playlist(item, apiClientProps.server)),
startIndex: 0,
offset: 0,
totalRecordCount: res.body.TotalRecordCount,
};
},
getPlaylistListCount: async ({ apiClientProps, query }) =>
JellyfinController.getPlaylistList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getPlaylistSongList: async (args) => {
const { apiClientProps, query } = args;
@@ -556,7 +550,7 @@ export const JellyfinController: ControllerEndpoint = {
return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
startIndex: query.startIndex,
offset: query.startIndex,
totalRecordCount: res.body.TotalRecordCount,
};
},
@@ -606,7 +600,7 @@ export const JellyfinController: ControllerEndpoint = {
return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
startIndex: 0,
offset: 0,
totalRecordCount: res.body.Items.length || 0,
};
},
@@ -746,7 +740,7 @@ export const JellyfinController: ControllerEndpoint = {
SearchTerm: query.searchTerm,
SortBy: songListSortMap.jellyfin[query.sortBy] || 'Album,SortName',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: query.startIndex,
StartIndex: query.offset,
...query._custom?.jellyfin,
Years: yearsFilter,
},
@@ -781,7 +775,7 @@ export const JellyfinController: ControllerEndpoint = {
SearchTerm: query.searchTerm,
SortBy: songListSortMap.jellyfin[query.sortBy] || 'Album,SortName',
SortOrder: sortOrderMap.jellyfin[query.sortOrder],
StartIndex: query.startIndex,
StartIndex: query.offset,
...query._custom?.jellyfin,
Years: yearsFilter,
},
@@ -810,14 +804,14 @@ export const JellyfinController: ControllerEndpoint = {
items: items.map((item) =>
jfNormalize.song(item, apiClientProps.server, '', query.imageSize),
),
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount,
};
},
getSongListCount: async ({ apiClientProps, query }) =>
JellyfinController.getSongList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getTags: async (args) => {
const { apiClientProps, query } = args;
@@ -873,7 +867,7 @@ export const JellyfinController: ControllerEndpoint = {
return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server, '')),
startIndex: 0,
offset: 0,
totalRecordCount: res.body.TotalRecordCount,
};
},
+1 -1
View File
@@ -11,7 +11,7 @@ import { useAuthStore } from '/@/renderer/store';
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import { resultWithHeaders } from '/@/shared/api/utils';
import { toast } from '/@/shared/components/toast/toast';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
const localSettings = isElectron() ? window.api.localSettings : null;
@@ -4,25 +4,24 @@ import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller
import { NDSongListSort } from '/@/shared/api/navidrome.types';
import { ndNormalize } from '/@/shared/api/navidrome/navidrome-normalize';
import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import { ssNormalize } from '/@/shared/api/subsonic/subsonic-normalize';
import { normalize } from '/@/shared/api/subsonic/subsonic-normalize';
import { SubsonicExtensions } from '/@/shared/api/subsonic/subsonic-types';
import { getFeatures, hasFeature, VersionInfo } from '/@/shared/api/utils';
import { albumListSortMap } from '/@/shared/types/domain/album-domain-types';
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
import { albumArtistListSortMap } from '/@/shared/types/domain/artist-domain-types';
import { AuthenticationResponse } from '/@/shared/types/domain/auth-domain-types';
import {
albumArtistListSortMap,
albumListSortMap,
AuthenticationResponse,
ControllerEndpoint,
genreListSortMap,
playlistListSortMap,
PlaylistSongListArgs,
PlaylistSongListRequest,
PlaylistSongListResponse,
} from '/@/shared/types/domain/playlist-domain-types';
import {
ServerFeature,
ServerFeatures,
ServerListItem,
Song,
songListSortMap,
sortOrderMap,
userListSortMap,
} from '/@/shared/types/domain-types';
import { ServerFeature, ServerFeatures } from '/@/shared/types/features-types';
} from '/@/shared/types/domain/server-domain-types';
import { sortOrderMap } from '/@/shared/types/domain/shared-domain-types';
import { Song, songListSortMap } from '/@/shared/types/domain/song-domain-types';
const VERSION_INFO: VersionInfo = [
['0.55.0', { [ServerFeature.BFR]: [1] }],
@@ -271,10 +270,10 @@ export const NavidromeController: ControllerEndpoint = {
const res = await ndApiClient(apiClientProps).getAlbumList({
query: {
_end: query.startIndex + (query.limit || 0),
_end: query.offset + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: albumListSortMap.navidrome[query.sortBy],
_start: query.startIndex,
_start: query.offset,
artist_id: query.artistIds?.[0],
compilation: query.compilation,
genre_id: query.genres?.[0],
@@ -291,24 +290,24 @@ export const NavidromeController: ControllerEndpoint = {
return {
items: res.body.data.map((album) => ndNormalize.album(album, apiClientProps.server)),
startIndex: query?.startIndex || 0,
offset: query?.offset || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
};
},
getAlbumListCount: async ({ apiClientProps, query }) =>
NavidromeController.getAlbumList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getArtistList: async (args) => {
const { apiClientProps, query } = args;
const res = await ndApiClient(apiClientProps).getAlbumArtistList({
query: {
_end: query.startIndex + (query.limit || 0),
_end: query.offset + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: albumArtistListSortMap.navidrome[query.sortBy],
_start: query.startIndex,
_start: query.offset,
name: query.searchTerm,
...query._custom?.navidrome,
role: query.role || undefined,
@@ -333,14 +332,14 @@ export const NavidromeController: ControllerEndpoint = {
apiClientProps.server,
),
),
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
};
},
getArtistListCount: async ({ apiClientProps, query }) =>
NavidromeController.getArtistList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getDownloadUrl: SubsonicController.getDownloadUrl,
getGenreList: async (args) => {
@@ -348,10 +347,10 @@ export const NavidromeController: ControllerEndpoint = {
const res = await ndApiClient(apiClientProps).getGenreList({
query: {
_end: query.startIndex + (query.limit || 0),
_end: query.offset + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: genreListSortMap.navidrome[query.sortBy],
_start: query.startIndex,
_start: query.offset,
name: query.searchTerm,
},
});
@@ -362,7 +361,7 @@ export const NavidromeController: ControllerEndpoint = {
return {
items: res.body.data.map((genre) => ndNormalize.genre(genre)),
startIndex: query.startIndex || 0,
offset: query.offset || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
};
},
@@ -398,10 +397,10 @@ export const NavidromeController: ControllerEndpoint = {
const res = await ndApiClient(apiClientProps).getPlaylistList({
query: {
_end: query.startIndex + (query.limit || 0),
_end: query.offset + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: query.sortBy ? playlistListSortMap.navidrome[query.sortBy] : undefined,
_start: query.startIndex,
_start: query.offset,
q: query.searchTerm,
...customQuery,
},
@@ -413,16 +412,18 @@ export const NavidromeController: ControllerEndpoint = {
return {
items: res.body.data.map((item) => ndNormalize.playlist(item, apiClientProps.server)),
startIndex: query?.startIndex || 0,
offset: query?.offset || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
};
},
getPlaylistListCount: async ({ apiClientProps, query }) =>
NavidromeController.getPlaylistList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getPlaylistSongList: async (args: PlaylistSongListArgs): Promise<PlaylistSongListResponse> => {
getPlaylistSongList: async (
args: PlaylistSongListRequest,
): Promise<PlaylistSongListResponse> => {
const { apiClientProps, query } = args;
const res = await ndApiClient(apiClientProps).getPlaylistSongList({
@@ -446,7 +447,7 @@ export const NavidromeController: ControllerEndpoint = {
return {
items: res.body.data.map((item) => ndNormalize.song(item, apiClientProps.server)),
startIndex: query?.startIndex || 0,
offset: query?.startIndex || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
};
},
@@ -518,7 +519,7 @@ export const NavidromeController: ControllerEndpoint = {
if (res.status === 200 && res.body.similarSongs?.song) {
const similar = res.body.similarSongs.song.reduce<Song[]>((acc, song) => {
if (song.id !== query.songId) {
acc.push(ssNormalize.song(song, apiClientProps.server));
acc.push(normalize.song(song, apiClientProps.server));
}
return acc;
@@ -572,10 +573,10 @@ export const NavidromeController: ControllerEndpoint = {
const res = await ndApiClient(apiClientProps).getSongList({
query: {
_end: query.startIndex + (query.limit || -1),
_end: query.offset + (query.limit || -1),
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: songListSortMap.navidrome[query.sortBy],
_start: query.startIndex,
_start: query.offset,
album_artist_id: query.albumArtistIds,
album_id: query.albumIds,
artist_id: query.artistIds,
@@ -595,14 +596,14 @@ export const NavidromeController: ControllerEndpoint = {
items: res.body.data.map((song) =>
ndNormalize.song(song, apiClientProps.server, query.imageSize),
),
startIndex: query?.startIndex || 0,
offset: query?.offset || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
};
},
getSongListCount: async ({ apiClientProps, query }) =>
NavidromeController.getSongList({
apiClientProps,
query: { ...query, limit: 1, startIndex: 0 },
query: { ...query, limit: 1, offset: 0 },
}).then((result) => result!.totalRecordCount!),
getStructuredLyrics: SubsonicController.getStructuredLyrics,
getTags: async (args) => {
@@ -651,10 +652,10 @@ export const NavidromeController: ControllerEndpoint = {
const res = await ndApiClient(apiClientProps).getUserList({
query: {
_end: query.startIndex + (query.limit || 0),
_end: query.offset + (query.limit || 0),
_order: sortOrderMap.navidrome[query.sortOrder],
_sort: userListSortMap.navidrome[query.sortBy],
_start: query.startIndex,
_start: query.offset,
...query._custom?.navidrome,
},
});
@@ -665,7 +666,7 @@ export const NavidromeController: ControllerEndpoint = {
return {
items: res.body.data.map((user) => ndNormalize.user(user)),
startIndex: query?.startIndex || 0,
offset: query?.offset || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
};
},
+15 -11
View File
@@ -1,27 +1,31 @@
import type {
import { QueryFunctionContext } from '@tanstack/react-query';
import { AlbumDetailQuery, AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
import {
AlbumArtistDetailQuery,
AlbumArtistListQuery,
AlbumDetailQuery,
AlbumListQuery,
ArtistListQuery,
GenreListQuery,
} from '/@/shared/types/domain/artist-domain-types';
import { GenreListQuery } from '/@/shared/types/domain/genre-domain-types';
import {
LyricSearchQuery,
LyricSource,
LyricsQuery,
} from '/@/shared/types/domain/lyric-domain-types';
import {
PlaylistDetailQuery,
PlaylistListQuery,
PlaylistSongListQuery,
} from '/@/shared/types/domain/playlist-domain-types';
import { SearchQuery } from '/@/shared/types/domain/search-domain-types';
import {
RandomSongListQuery,
SearchQuery,
SimilarSongsQuery,
SongDetailQuery,
SongListQuery,
TopSongListQuery,
UserListQuery,
} from '/@/shared/types/domain-types';
import { QueryFunctionContext } from '@tanstack/react-query';
import { LyricSource } from '/@/shared/types/domain-types';
} from '/@/shared/types/domain/song-domain-types';
import { UserListQuery } from '/@/shared/types/domain/user-domain-types';
export const splitPaginatedQuery = (key: any) => {
const { limit, startIndex, ...filter } = key || {};
+1 -1
View File
@@ -7,7 +7,7 @@ import { z } from 'zod';
import i18n from '/@/i18n/i18n';
import { ssType } from '/@/shared/api/subsonic/subsonic-types';
import { toast } from '/@/shared/components/toast/toast';
import { ServerListItem } from '/@/shared/types/domain-types';
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
const c = initContract();
@@ -8,25 +8,18 @@ import { z } from 'zod';
import { contract, ssApiClient } from '/@/renderer/api/subsonic/subsonic-api';
import { randomString } from '/@/renderer/utils';
import { ssNormalize } from '/@/shared/api/subsonic/subsonic-normalize';
import { normalize } from '/@/shared/api/subsonic/subsonic-normalize';
import {
AlbumListSortType,
ssType,
SubsonicExtensions,
} from '/@/shared/api/subsonic/subsonic-types';
import {
AlbumListSort,
ControllerEndpoint,
GenreListSort,
LibraryItem,
PlaylistListSort,
Song,
sortAlbumArtistList,
sortAlbumList,
SortOrder,
sortSongList,
} from '/@/shared/types/domain-types';
import { ServerFeatures } from '/@/shared/types/features-types';
import { AlbumListSort, sortAlbumList } from '/@/shared/types/domain/album-domain-types';
import { ControllerEndpoint } from '/@/shared/types/domain/api-domain-types';
import { sortAlbumArtistList } from '/@/shared/types/domain/artist-domain-types';
import { ServerFeatures } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { Song, sortSongList } from '/@/shared/types/domain/song-domain-types';
const ALBUM_LIST_SORT_MAPPING: Record<AlbumListSort, AlbumListSortType | undefined> = {
[AlbumListSort.ALBUM_ARTIST]: AlbumListSortType.ALPHABETICAL_BY_ARTIST,
@@ -205,11 +198,11 @@ export const SubsonicController: ControllerEndpoint = {
}
return {
...ssNormalize.albumArtist(artist, apiClientProps.server, 300),
albums: artist.album?.map((album) => ssNormalize.album(album, apiClientProps.server)),
...normalize.albumArtist(artist, apiClientProps.server, 300),
albums: artist.album?.map((album) => normalize.album(album, apiClientProps.server)),
similarArtists:
artistInfo?.similarArtist?.map((artist) =>
ssNormalize.albumArtist(artist, apiClientProps.server, 300),
normalize.albumArtist(artist, apiClientProps.server, 300),
) || null,
};
},
@@ -229,7 +222,7 @@ export const SubsonicController: ControllerEndpoint = {
const artists = (res.body.artists?.index || []).flatMap((index) => index.artist);
let results = artists.map((artist) =>
ssNormalize.albumArtist(artist, apiClientProps.server, 300),
normalize.albumArtist(artist, apiClientProps.server, 300),
);
if (query.searchTerm) {
@@ -265,7 +258,7 @@ export const SubsonicController: ControllerEndpoint = {
throw new Error('Failed to get album detail');
}
return ssNormalize.album(res.body.album, apiClientProps.server);
return normalize.album(res.body.album, apiClientProps.server);
},
getAlbumList: async (args) => {
const { apiClientProps, query } = args;
@@ -274,7 +267,7 @@ export const SubsonicController: ControllerEndpoint = {
const res = await ssApiClient(apiClientProps).search3({
query: {
albumCount: query.limit,
albumOffset: query.startIndex,
albumOffset: query.offset,
artistCount: 0,
artistOffset: 0,
query: query.searchTerm || '',
@@ -289,12 +282,12 @@ export const SubsonicController: ControllerEndpoint = {
const results =
res.body.searchResult3?.album?.map((album) =>
ssNormalize.album(album, apiClientProps.server),
normalize.album(album, apiClientProps.server),
) || [];
return {
items: results,
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount: null,
};
}
@@ -324,11 +317,11 @@ export const SubsonicController: ControllerEndpoint = {
return artist.body.artist.album ?? [];
});
const items = albums.map((album) => ssNormalize.album(album, apiClientProps.server));
const items = albums.map((album) => normalize.album(album, apiClientProps.server));
return {
items: sortAlbumList(items, query.sortBy, query.sortOrder),
startIndex: 0,
offset: 0,
totalRecordCount: albums.length,
};
}
@@ -346,12 +339,12 @@ export const SubsonicController: ControllerEndpoint = {
const results =
res.body.starred?.album?.map((album) =>
ssNormalize.album(album, apiClientProps.server),
normalize.album(album, apiClientProps.server),
) || [];
return {
items: sortAlbumList(results, query.sortBy, query.sortOrder),
startIndex: 0,
offset: 0,
totalRecordCount: res.body.starred?.album?.length || 0,
};
}
@@ -381,7 +374,7 @@ export const SubsonicController: ControllerEndpoint = {
}
if (type === AlbumListSortType.BY_YEAR && !fromYear && !toYear) {
if (query.sortOrder === SortOrder.ASC) {
if (query.sortOrder === ListSortOrder.ASC) {
fromYear = 0;
toYear = dayjs().year();
} else {
@@ -395,7 +388,7 @@ export const SubsonicController: ControllerEndpoint = {
fromYear,
genre: query.genres?.length ? query.genres[0] : undefined,
musicFolderId: query.musicFolderId,
offset: query.startIndex,
offset: query.offset,
size: query.limit,
toYear,
type,
@@ -409,9 +402,9 @@ export const SubsonicController: ControllerEndpoint = {
return {
items:
res.body.albumList2.album?.map((album) =>
ssNormalize.album(album, apiClientProps.server, 300),
normalize.album(album, apiClientProps.server, 300),
) || [],
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount: null,
};
},
@@ -579,7 +572,7 @@ export const SubsonicController: ControllerEndpoint = {
}
let results = artists.map((artist) =>
ssNormalize.albumArtist(artist, apiClientProps.server, 300),
normalize.albumArtist(artist, apiClientProps.server, 300),
);
if (query.searchTerm) {
@@ -596,7 +589,7 @@ export const SubsonicController: ControllerEndpoint = {
return {
items: results,
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount: results?.length || 0,
};
},
@@ -640,11 +633,11 @@ export const SubsonicController: ControllerEndpoint = {
break;
}
const genres = results.map(ssNormalize.genre);
const genres = results.map(normalize.genre);
return {
items: genres,
startIndex: 0,
offset: 0,
totalRecordCount: genres.length,
};
},
@@ -662,7 +655,7 @@ export const SubsonicController: ControllerEndpoint = {
id: folder.id.toString(),
name: folder.name,
})),
startIndex: 0,
offset: 0,
totalRecordCount: res.body.musicFolders.musicFolder.length,
};
},
@@ -679,7 +672,7 @@ export const SubsonicController: ControllerEndpoint = {
throw new Error('Failed to get playlist detail');
}
return ssNormalize.playlist(res.body.playlist, apiClientProps.server);
return normalize.playlist(res.body.playlist, apiClientProps.server);
},
getPlaylistList: async ({ apiClientProps, query }) => {
const sortOrder = query.sortOrder.toLowerCase() as 'asc' | 'desc';
@@ -724,8 +717,8 @@ export const SubsonicController: ControllerEndpoint = {
}
return {
items: results.map((playlist) => ssNormalize.playlist(playlist, apiClientProps.server)),
startIndex: 0,
items: results.map((playlist) => normalize.playlist(playlist, apiClientProps.server)),
offset: 0,
totalRecordCount: results.length,
};
},
@@ -760,7 +753,7 @@ export const SubsonicController: ControllerEndpoint = {
}
let results =
res.body.playlist.entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) ||
res.body.playlist.entry?.map((song) => normalize.song(song, apiClientProps.server)) ||
[];
if (query.sortBy && query.sortOrder) {
@@ -769,7 +762,7 @@ export const SubsonicController: ControllerEndpoint = {
return {
items: results,
startIndex: 0,
offset: 0,
totalRecordCount: results?.length || 0,
};
},
@@ -793,8 +786,8 @@ export const SubsonicController: ControllerEndpoint = {
const results = res.body.randomSongs?.song || [];
return {
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)),
startIndex: 0,
items: results.map((song) => normalize.song(song, apiClientProps.server)),
offset: 0,
totalRecordCount: res.body.randomSongs?.song?.length || 0,
};
},
@@ -878,7 +871,7 @@ export const SubsonicController: ControllerEndpoint = {
return res.body.similarSongs.song.reduce<Song[]>((acc, song) => {
if (song.id !== query.songId) {
acc.push(ssNormalize.song(song, apiClientProps.server));
acc.push(normalize.song(song, apiClientProps.server));
}
return acc;
@@ -897,7 +890,7 @@ export const SubsonicController: ControllerEndpoint = {
throw new Error('Failed to get song detail');
}
return ssNormalize.song(res.body.song, apiClientProps.server);
return normalize.song(res.body.song, apiClientProps.server);
},
getSongList: async ({ apiClientProps, query }) => {
const fromAlbumPromises: Promise<ServerInferResponses<typeof contract.getAlbum>>[] = [];
@@ -912,7 +905,7 @@ export const SubsonicController: ControllerEndpoint = {
artistOffset: 0,
query: query.searchTerm || '',
songCount: query.limit,
songOffset: query.startIndex,
songOffset: query.offset,
},
});
@@ -923,9 +916,9 @@ export const SubsonicController: ControllerEndpoint = {
return {
items:
res.body.searchResult3?.song?.map((song) =>
ssNormalize.song(song, apiClientProps.server),
normalize.song(song, apiClientProps.server),
) || [],
startIndex: query.startIndex,
offset: query.offset,
totalRecordCount: null,
};
}
@@ -936,7 +929,7 @@ export const SubsonicController: ControllerEndpoint = {
count: query.limit,
genre: query.genreIds[0],
musicFolderId: query.musicFolderId,
offset: query.startIndex,
offset: query.offset,
},
});
@@ -947,8 +940,8 @@ export const SubsonicController: ControllerEndpoint = {
const results = res.body.songsByGenre?.song || [];
return {
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)) || [],
startIndex: 0,
items: results.map((song) => normalize.song(song, apiClientProps.server)) || [],
offset: 0,
totalRecordCount: null,
};
}
@@ -966,12 +959,12 @@ export const SubsonicController: ControllerEndpoint = {
const results =
(res.body.starred?.song || []).map((song) =>
ssNormalize.song(song, apiClientProps.server),
normalize.song(song, apiClientProps.server),
) || [];
return {
items: sortSongList(results, query.sortBy, query.sortOrder),
startIndex: 0,
offset: 0,
totalRecordCount: (res.body.starred?.song || []).length || 0,
};
}
@@ -1040,8 +1033,8 @@ export const SubsonicController: ControllerEndpoint = {
}
return {
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)),
startIndex: 0,
items: results.map((song) => normalize.song(song, apiClientProps.server)),
offset: 0,
totalRecordCount: results.length,
};
}
@@ -1054,7 +1047,7 @@ export const SubsonicController: ControllerEndpoint = {
artistOffset: 0,
query: query.searchTerm || '',
songCount: query.limit,
songOffset: query.startIndex,
songOffset: query.offset,
},
});
@@ -1065,9 +1058,9 @@ export const SubsonicController: ControllerEndpoint = {
return {
items:
res.body.searchResult3?.song?.map((song) =>
ssNormalize.song(song, apiClientProps.server),
normalize.song(song, apiClientProps.server),
) || [],
startIndex: 0,
offset: 0,
totalRecordCount: null,
};
},
@@ -1302,9 +1295,9 @@ export const SubsonicController: ControllerEndpoint = {
return {
items:
res.body.topSongs?.song?.map((song) =>
ssNormalize.song(song, apiClientProps.server),
normalize.song(song, apiClientProps.server),
) || [],
startIndex: 0,
offset: 0,
totalRecordCount: res.body.topSongs?.song?.length || 0,
};
},
@@ -1372,13 +1365,13 @@ export const SubsonicController: ControllerEndpoint = {
return {
albumArtists: (res.body.searchResult3?.artist || [])?.map((artist) =>
ssNormalize.albumArtist(artist, apiClientProps.server),
normalize.albumArtist(artist, apiClientProps.server),
),
albums: (res.body.searchResult3?.album || []).map((album) =>
ssNormalize.album(album, apiClientProps.server),
normalize.album(album, apiClientProps.server),
),
songs: (res.body.searchResult3?.song || []).map((song) =>
ssNormalize.song(song, apiClientProps.server),
normalize.song(song, apiClientProps.server),
),
};
},
+14 -12
View File
@@ -1,4 +1,3 @@
import type { Song } from '/@/shared/types/domain-types';
import type { CrossfadeStyle } from '/@/shared/types/types';
import type { ReactPlayerProps } from 'react-player';
@@ -20,9 +19,11 @@ import {
gaplessHandler,
} from '/@/renderer/components/audio-player/utils/list-handlers';
import { useWebAudio } from '/@/renderer/features/player/hooks/use-webaudio';
import { getServerById, TranscodingConfig, usePlaybackSettings, useSpeed } from '/@/renderer/store';
import { TranscodingConfig, usePlaybackSettings, useSpeed } from '/@/renderer/store';
import { useSettingsStore, useSettingsStoreActions } from '/@/renderer/store/settings.store';
import { toast } from '/@/shared/components/toast/toast';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
import { PlaybackStyle, PlayerStatus } from '/@/shared/types/types';
export type AudioPlayerProgress = {
@@ -57,27 +58,28 @@ const getDuration = (ref: any) => {
const EMPTY_SOURCE =
'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU2LjM2LjEwMAAAAAAAAAAAAAAA//OEAAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAEAAABIADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDV1dXV1dXV1dXV1dXV1dXV1dXV1dXV1dXV6urq6urq6urq6urq6urq6urq6urq6urq6v////////////////////////////////8AAAAATGF2YzU2LjQxAAAAAAAAAAAAAAAAJAAAAAAAAAAAASDs90hvAAAAAAAAAAAAAAAAAAAA//MUZAAAAAGkAAAAAAAAA0gAAAAATEFN//MUZAMAAAGkAAAAAAAAA0gAAAAARTMu//MUZAYAAAGkAAAAAAAAA0gAAAAAOTku//MUZAkAAAGkAAAAAAAAA0gAAAAANVVV';
const useSongUrl = (transcode: TranscodingConfig, current: boolean, song?: Song): null | string => {
const useSongUrl = (
transcode: TranscodingConfig,
current: boolean,
song?: QueueSong,
): null | string => {
const prior = useRef(['', '']);
return useMemo(() => {
if (song?.serverId) {
if (song?._serverId) {
// If we are the current track, we do not want a transcoding
// reconfiguration to force a restart.
if (current && prior.current[0] === song.uniqueId) {
return prior.current[1];
if (current && prior.current[0] === song._uniqueId) {
return prior.current[1] as string;
}
if (!transcode.enabled) {
// transcoding disabled; save the result
prior.current = [song.uniqueId, song.streamUrl];
prior.current = [song._uniqueId, song.streamUrl];
return song.streamUrl;
}
const result = api.controller.getTranscodingUrl({
apiClientProps: {
server: getServerById(song.serverId),
},
query: {
base: song.streamUrl,
...transcode,
@@ -85,14 +87,14 @@ const useSongUrl = (transcode: TranscodingConfig, current: boolean, song?: Song)
})!;
// transcoding enabled; save the updated result
prior.current = [song.uniqueId, result];
prior.current = [song._uniqueId, result];
return result;
}
// no track; clear result
prior.current = ['', ''];
return null;
}, [current, song?.uniqueId, song?.serverId, song?.streamUrl, transcode]);
}, [song?._serverId, song?._uniqueId, song?.streamUrl, current, transcode]);
};
export interface AudioPlayerRef {
@@ -13,7 +13,7 @@ import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Button } from '/@/shared/components/button/button';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Play } from '/@/shared/types/types';
export const CardControls = ({
+5 -2
View File
@@ -3,18 +3,21 @@ import formatDuration from 'format-duration';
import React from 'react';
import { generatePath } from 'react-router';
import { Link } from 'react-router-dom';
import { Song } from 'src/main/features/core/lyrics/netease';
import styles from './card-rows.module.css';
import { AppRoute } from '/@/renderer/router/routes';
import { formatDateAbsolute, formatDateRelative, formatRating } from '/@/renderer/utils/format';
import { Text } from '/@/shared/components/text/text';
import { Album, AlbumArtist, Artist, Playlist, Song } from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
import { CardRow } from '/@/shared/types/types';
interface CardRowsProps {
data: any;
rows: CardRow<Album>[] | CardRow<AlbumArtist>[] | CardRow<Artist>[];
rows: CardRow<Album>[] | CardRow<Artist>[];
}
export const CardRows = ({ data, rows }: CardRowsProps) => {
+4 -2
View File
@@ -8,12 +8,14 @@ import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/
import { Image } from '/@/shared/components/image/image';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Stack } from '/@/shared/components/stack/stack';
import { Album, AlbumArtist, Artist, LibraryItem } from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { CardRoute, CardRow, Play, PlayQueueAddOptions } from '/@/shared/types/types';
interface BaseGridCardProps {
controls: {
cardRows: CardRow<Album>[] | CardRow<AlbumArtist>[] | CardRow<Artist>[];
cardRows: CardRow<Album>[] | CardRow<Artist>[];
handleFavorite: (options: {
id: string[];
isFavorite: boolean;
@@ -20,7 +20,8 @@ import { Image } from '/@/shared/components/image/image';
import { Stack } from '/@/shared/components/stack/stack';
import { TextTitle } from '/@/shared/components/text-title/text-title';
import { Text } from '/@/shared/components/text/text';
import { Album, LibraryItem } from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Play } from '/@/shared/types/types';
const variants: Variants = {
@@ -24,13 +24,9 @@ import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { Stack } from '/@/shared/components/stack/stack';
import { TextTitle } from '/@/shared/components/text-title/text-title';
import {
Album,
AlbumArtist,
Artist,
LibraryItem,
RelatedArtist,
} from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Artist, RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { CardRoute, CardRow } from '/@/shared/types/types';
const getSlidesPerView = (windowWidth: number) => {
@@ -10,20 +10,17 @@ import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/
import { Image } from '/@/shared/components/image/image';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Stack } from '/@/shared/components/stack/stack';
import {
Album,
AlbumArtist,
Artist,
LibraryItem,
Playlist,
Song,
} from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
import { CardRoute, CardRow, Play, PlayQueueAddOptions } from '/@/shared/types/types';
interface BaseGridCardProps {
columnIndex: number;
controls: {
cardRows: CardRow<Album | AlbumArtist | Artist | Playlist | Song>[];
cardRows: CardRow<Album | Artist | Playlist | Song>[];
handleFavorite: (options: {
id: string[];
isFavorite: boolean;
@@ -13,7 +13,7 @@ import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Button } from '/@/shared/components/button/button';
import { Icon } from '/@/shared/components/icon/icon';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Play, PlayQueueAddOptions } from '/@/shared/types/types';
export const GridCardControls = ({
@@ -10,20 +10,17 @@ import { GridCardControls } from '/@/renderer/components/virtual-grid/grid-card/
import { Image } from '/@/shared/components/image/image';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Stack } from '/@/shared/components/stack/stack';
import {
Album,
AlbumArtist,
Artist,
LibraryItem,
Playlist,
Song,
} from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
import { CardRoute, CardRow, Play, PlayQueueAddOptions } from '/@/shared/types/types';
interface BaseGridCardProps {
columnIndex: number;
controls: {
cardRows: CardRow<Album | AlbumArtist | Artist | Playlist | Song>[];
cardRows: CardRow<Album | Artist | Playlist | Song>[];
handleFavorite: (options: {
id: string[];
isFavorite: boolean;
@@ -14,7 +14,9 @@ import { FixedSizeList } from 'react-window';
import styles from './virtual-grid-wrapper.module.css';
import { GridCard } from '/@/renderer/components/virtual-grid/grid-card';
import { Album, AlbumArtist, Artist, LibraryItem } from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
const createItemData = memoize(
(
@@ -72,7 +74,7 @@ export const VirtualGridWrapper = ({
width,
...rest
}: Omit<FixedSizeListProps, 'children' | 'height' | 'itemSize' | 'ref' | 'width'> & {
cardRows: CardRow<Album | AlbumArtist | Artist>[];
cardRows: CardRow<Album | Artist>[];
columnCount: number;
display: ListDisplayType;
handleFavorite?: (options: {
@@ -14,7 +14,8 @@ import {
import InfiniteLoader from 'react-window-infinite-loader';
import { VirtualGridWrapper } from '/@/renderer/components/virtual-grid/virtual-grid-wrapper';
import { AnyLibraryItem, Genre, LibraryItem } from '/@/shared/types/domain-types';
import { Genre } from '/@/shared/types/domain/genre-domain-types';
import { AnyLibraryItem, LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { ListDisplayType } from '/@/shared/types/types';
export type VirtualInfiniteGridRef = {
@@ -1,4 +1,3 @@
import type { AlbumArtist, Artist } from '/@/shared/types/domain-types';
import type { ICellRendererParams } from '@ag-grid-community/core';
import React from 'react';
@@ -10,6 +9,7 @@ import { AppRoute } from '/@/renderer/router/routes';
import { Separator } from '/@/shared/components/separator/separator';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Text } from '/@/shared/components/text/text';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
export const AlbumArtistCell = ({ data, value }: ICellRendererParams) => {
if (value === undefined) {
@@ -23,7 +23,7 @@ export const AlbumArtistCell = ({ data, value }: ICellRendererParams) => {
return (
<CellContainer position="left">
<Text isMuted overflow="hidden" size="md">
{value?.map((item: AlbumArtist | Artist, index: number) => (
{value?.map((item: Artist, index: number) => (
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && <Separator />}
{item.id ? (
@@ -1,4 +1,3 @@
import type { AlbumArtist, Artist } from '/@/shared/types/domain-types';
import type { ICellRendererParams } from '@ag-grid-community/core';
import React from 'react';
@@ -10,6 +9,7 @@ import { AppRoute } from '/@/renderer/router/routes';
import { Separator } from '/@/shared/components/separator/separator';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Text } from '/@/shared/components/text/text';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
export const ArtistCell = ({ data, value }: ICellRendererParams) => {
if (value === undefined) {
@@ -23,7 +23,7 @@ export const ArtistCell = ({ data, value }: ICellRendererParams) => {
return (
<CellContainer position="left">
<Text isMuted overflow="hidden" size="md">
{value?.map((item: AlbumArtist | Artist, index: number) => (
{value?.map((item: Artist, index: number) => (
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && <Separator />}
{item.id ? (
@@ -5,7 +5,7 @@ import styles from './combined-title-cell-controls.module.css';
import { usePlayQueueAdd } from '/@/renderer/features/player';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Play } from '/@/shared/types/types';
export const ListCoverControls = ({
@@ -12,7 +12,7 @@ import { SEPARATOR_STRING } from '/@/shared/api/utils';
import { Image } from '/@/shared/components/image/image';
import { Skeleton } from '/@/shared/components/skeleton/skeleton';
import { Text } from '/@/shared/components/text/text';
import { AlbumArtist, Artist } from '/@/shared/types/domain-types';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
export const CombinedTitleCell = ({
context,
@@ -74,7 +74,7 @@ export const CombinedTitleCell = ({
</Text>
<Text isMuted overflow="hidden" size="md">
{artists?.length ? (
artists.map((artist: AlbumArtist | Artist, index: number) => (
artists.map((artist: Artist, index: number) => (
<React.Fragment key={`queue-${rowIndex}-artist-${artist.id}`}>
{index > 0 ? SEPARATOR_STRING : null}
{artist.id ? (
@@ -1,4 +1,3 @@
import type { AlbumArtist, Artist } from '/@/shared/types/domain-types';
import type { ICellRendererParams } from '@ag-grid-community/core';
import React from 'react';
@@ -8,13 +7,14 @@ import { CellContainer } from '/@/renderer/components/virtual-table/cells/generi
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
import { Separator } from '/@/shared/components/separator/separator';
import { Text } from '/@/shared/components/text/text';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
export const GenreCell = ({ data, value }: ICellRendererParams) => {
const genrePath = useGenreRoute();
return (
<CellContainer position="left">
<Text isMuted overflow="hidden" size="md">
{value?.map((item: AlbumArtist | Artist, index: number) => (
{value?.map((item: Artist, index: number) => (
<React.Fragment key={`row-${item.id}-${data.uniqueId}`}>
{index > 0 && <Separator />}
<Text
@@ -5,7 +5,7 @@ import { MutableRefObject, useEffect, useMemo, useRef } from 'react';
import { useAppFocus } from '/@/renderer/hooks';
import { useCurrentSong, usePlayerStore } from '/@/renderer/store';
import { Song } from '/@/shared/types/domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
import { PlayerStatus } from '/@/shared/types/types';
interface UseCurrentSongRowStylesProps {
@@ -24,11 +24,11 @@ import { AppRoute } from '/@/renderer/router/routes';
import { PersistedTableColumn, useListStoreActions } from '/@/renderer/store';
import { ListKey, useListStoreByKey } from '/@/renderer/store/list.store';
import {
BasePaginatedQuery,
BasePaginatedResponse,
BaseQuery,
LibraryItem,
ServerListItem,
} from '/@/shared/types/domain-types';
} from '/@/shared/types/adapter/api-controller-types';
import { ServerListItem } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { ListDisplayType, TablePagination } from '/@/shared/types/types';
export type AgGridFetchFn<TResponse, TFilter> = (
@@ -52,7 +52,7 @@ interface UseAgGridProps<TFilter> {
const BLOCK_SIZE = 500;
export const useVirtualTable = <TFilter extends BaseQuery<any>>({
export const useVirtualTable = <TFilter extends BasePaginatedQuery<any>>({
columnType,
contextMenu,
customFilters,
@@ -18,7 +18,7 @@ import { Icon } from '/@/shared/components/icon/icon';
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { ServerListItem, ServerType } from '/@/shared/types/domain-types';
import { ServerListItem, ServerType } from '/@/shared/types/domain/server-domain-types';
const localSettings = isElectron() ? window.api.localSettings : null;
@@ -0,0 +1,45 @@
import { queryOptions, UseQueryOptions } from '@tanstack/react-query';
import { api } from '/@/renderer/api/api-controller';
import { AlbumListRequest } from '/@/shared/types/domain/album-domain-types';
export const getAlbumListQueryKey = (serverId: string, request?: AlbumListRequest) => {
if (!request) {
return [serverId, 'albums'];
}
return [serverId, 'albums', request];
};
export const getInfiniteAlbumListQueryKey = (serverId: string, request?: AlbumListRequest) => {
if (!request) {
return [serverId, 'albums', 'infinite'];
}
return [serverId, 'albums', 'infinite', request];
};
export const getAlbumList = async (serverId: string, request: AlbumListRequest) => {
const [error, response] = await api.controller[serverId]!.album.getList!({
query: request.query,
});
if (error) {
throw new Error(error.message);
}
return response;
};
export const getAlbumListQuery = (
serverId: string,
request: AlbumListRequest,
options?: UseQueryOptions,
) => {
return queryOptions({
enabled: !!serverId,
queryFn: () => getAlbumList(serverId, request),
queryKey: getAlbumListQueryKey(serverId, request),
...options,
});
};
@@ -49,13 +49,9 @@ import { Group } from '/@/shared/components/group/group';
import { Popover } from '/@/shared/components/popover/popover';
import { Spoiler } from '/@/shared/components/spoiler/spoiler';
import { Stack } from '/@/shared/components/stack/stack';
import {
AlbumListQuery,
AlbumListSort,
LibraryItem,
QueueSong,
SortOrder,
} from '/@/shared/types/domain-types';
import { AlbumListQuery, AlbumListSort } from '/@/shared/types/domain/album-domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { Play } from '/@/shared/types/types';
const isFullWidthRow = (node: RowNode) => {
@@ -154,8 +150,8 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
const artistQuery = useAlbumList({
options: {
cacheTime: 1000 * 60,
enabled: detailQuery?.data?.albumArtists[0]?.id !== undefined,
gcTime: 1000 * 60,
keepPreviousData: true,
staleTime: 1000 * 60,
},
@@ -169,9 +165,9 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
? [detailQuery?.data?.albumArtists[0].id]
: undefined,
limit: 15,
offset: 0,
sortBy: AlbumListSort.YEAR,
sortOrder: SortOrder.DESC,
startIndex: 0,
sortOrder: ListSortOrder.DESC,
},
serverId: server?.id,
});
@@ -179,15 +175,15 @@ export const AlbumDetailContent = ({ background, tableRef }: AlbumDetailContentP
const relatedAlbumGenresRequest: AlbumListQuery = {
genres: detailQuery.data?.genres.length ? [detailQuery.data.genres[0].id] : undefined,
limit: 15,
offset: 0,
sortBy: AlbumListSort.RANDOM,
sortOrder: SortOrder.ASC,
startIndex: 0,
sortOrder: ListSortOrder.ASC,
};
const relatedAlbumGenresQuery = useAlbumList({
options: {
cacheTime: 1000 * 60,
enabled: !!detailQuery?.data?.genres?.[0],
gcTime: 1000 * 60,
queryKey: queryKeys.albums.related(
server?.id || '',
albumId,
@@ -16,7 +16,9 @@ import { Group } from '/@/shared/components/group/group';
import { Rating } from '/@/shared/components/rating/rating';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { AlbumDetailResponse, LibraryItem, ServerType } from '/@/shared/types/domain-types';
import { AlbumDetailResponse } from '/@/shared/types/domain/album-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface AlbumDetailHeaderProps {
background: {
@@ -21,8 +21,8 @@ import {
AlbumListQuery,
AlbumListResponse,
AlbumListSort,
LibraryItem,
} from '/@/shared/types/domain-types';
} from '/@/shared/types/domain/album-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { CardRow, ListDisplayType } from '/@/shared/types/types';
export const AlbumListGridView = ({ gridRef, itemCount }: any) => {
@@ -137,15 +137,11 @@ export const AlbumListGridView = ({ gridRef, itemCount }: any) => {
const itemData: Album[] = [];
for (const [, data] of queriesFromCache) {
const { items, startIndex } = data || {};
const { items, offset } = data || {};
if (items && items.length !== 1 && startIndex !== undefined) {
if (items && items.length !== 1 && offset !== undefined) {
let itemIndex = 0;
for (
let rowIndex = startIndex;
rowIndex < startIndex + items.length;
rowIndex += 1
) {
for (let rowIndex = offset; rowIndex < offset + items.length; rowIndex += 1) {
itemData[rowIndex] = items[itemIndex];
itemIndex += 1;
}
@@ -165,7 +161,7 @@ export const AlbumListGridView = ({ gridRef, itemCount }: any) => {
limit: take,
...filter,
...customFilters,
startIndex: skip,
offset: skip,
};
const queryKey = queryKeys.albums.list(server?.id || '', query, id);
@@ -34,158 +34,154 @@ import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import {
AlbumListQuery,
AlbumListSort,
LibraryItem,
ServerType,
SortOrder,
} from '/@/shared/types/domain-types';
import { AlbumListQuery, AlbumListSort } from '/@/shared/types/domain/album-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { ListDisplayType, Play, TableColumn } from '/@/shared/types/types';
const FILTERS = {
jellyfin: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.albumArtist', { postProcess: 'titleCase' }),
value: AlbumListSort.ALBUM_ARTIST,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.communityRating', { postProcess: 'titleCase' }),
value: AlbumListSort.COMMUNITY_RATING,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.criticRating', { postProcess: 'titleCase' }),
value: AlbumListSort.CRITIC_RATING,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: AlbumListSort.NAME,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.playCount', { postProcess: 'titleCase' }),
value: AlbumListSort.PLAY_COUNT,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
value: AlbumListSort.RANDOM,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
value: AlbumListSort.RECENTLY_ADDED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.releaseDate', { postProcess: 'titleCase' }),
value: AlbumListSort.RELEASE_DATE,
},
],
navidrome: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.albumArtist', { postProcess: 'titleCase' }),
value: AlbumListSort.ALBUM_ARTIST,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.artist', { postProcess: 'titleCase' }),
value: AlbumListSort.ARTIST,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.duration', { postProcess: 'titleCase' }),
value: AlbumListSort.DURATION,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.mostPlayed', { postProcess: 'titleCase' }),
value: AlbumListSort.PLAY_COUNT,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: AlbumListSort.NAME,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
value: AlbumListSort.RANDOM,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
value: AlbumListSort.RATING,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
value: AlbumListSort.RECENTLY_ADDED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.recentlyPlayed', { postProcess: 'titleCase' }),
value: AlbumListSort.RECENTLY_PLAYED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.songCount', { postProcess: 'titleCase' }),
value: AlbumListSort.SONG_COUNT,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.favorited', { postProcess: 'titleCase' }),
value: AlbumListSort.FAVORITED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.releaseYear', { postProcess: 'titleCase' }),
value: AlbumListSort.YEAR,
},
],
subsonic: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.albumArtist', { postProcess: 'titleCase' }),
value: AlbumListSort.ALBUM_ARTIST,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.mostPlayed', { postProcess: 'titleCase' }),
value: AlbumListSort.PLAY_COUNT,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: AlbumListSort.NAME,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
value: AlbumListSort.RANDOM,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
value: AlbumListSort.RECENTLY_ADDED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.recentlyPlayed', { postProcess: 'titleCase' }),
value: AlbumListSort.RECENTLY_PLAYED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.favorited', { postProcess: 'titleCase' }),
value: AlbumListSort.FAVORITED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.releaseYear', { postProcess: 'titleCase' }),
value: AlbumListSort.YEAR,
},
@@ -299,7 +295,7 @@ export const AlbumListHeaderFilters = ({
customFilters,
data: {
sortBy: e.currentTarget.value as AlbumListSort,
sortOrder: sortOrder || SortOrder.ASC,
sortOrder: sortOrder || ListSortOrder.ASC,
},
itemType: LibraryItem.ALBUM,
key: pageKey,
@@ -337,7 +333,8 @@ export const AlbumListHeaderFilters = ({
);
const handleToggleSortOrder = useCallback(() => {
const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
const newSortOrder =
filter.sortOrder === ListSortOrder.ASC ? ListSortOrder.DESC : ListSortOrder.ASC;
const updatedFilters = setFilter({
customFilters,
data: { sortOrder: newSortOrder },
@@ -16,7 +16,8 @@ import { titleCase } from '/@/renderer/utils';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Stack } from '/@/shared/components/stack/stack';
import { AlbumListQuery, LibraryItem } from '/@/shared/types/domain-types';
import { AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface AlbumListHeaderProps {
genreId?: string;
@@ -4,7 +4,7 @@ import { useVirtualTable } from '/@/renderer/components/virtual-table/hooks/use-
import { useListContext } from '/@/renderer/context/list-context';
import { ALBUM_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { useCurrentServer } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
export const AlbumListTableView = ({ itemCount, tableRef }: any) => {
const server = useCurrentServer();
@@ -14,13 +14,10 @@ import { SpinnerIcon } from '/@/shared/components/spinner/spinner';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { YesNoSelect } from '/@/shared/components/yes-no-select/yes-no-select';
import {
AlbumArtistListSort,
AlbumListQuery,
GenreListSort,
LibraryItem,
SortOrder,
} from '/@/shared/types/domain-types';
import { AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
import { AlbumArtistListSort } from '/@/shared/types/domain/artist-domain-types';
import { GenreListSort } from '/@/shared/types/domain/genre-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
interface JellyfinAlbumFiltersProps {
customFilters?: Partial<AlbumListFilter>;
@@ -44,14 +41,14 @@ export const JellyfinAlbumFilters = ({
// TODO - eventually replace with /items/filters endpoint to fetch genres and tags specific to the selected library
const genreListQuery = useGenreList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
musicFolderId: filter?.musicFolderId,
sortBy: GenreListSort.NAME,
sortOrder: SortOrder.ASC,
startIndex: 0,
sortOrder: ListSortOrder.ASC,
offset: 0,
},
serverId,
});
@@ -66,7 +63,7 @@ export const JellyfinAlbumFilters = ({
const tagsQuery = useTagList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
@@ -173,12 +170,12 @@ export const JellyfinAlbumFilters = ({
const albumArtistListQuery = useAlbumArtistList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
sortBy: AlbumArtistListSort.NAME,
sortOrder: SortOrder.ASC,
sortOrder: ListSortOrder.ASC,
startIndex: 0,
},
serverId,
@@ -16,13 +16,10 @@ import { Stack } from '/@/shared/components/stack/stack';
import { Switch } from '/@/shared/components/switch/switch';
import { Text } from '/@/shared/components/text/text';
import { YesNoSelect } from '/@/shared/components/yes-no-select/yes-no-select';
import {
AlbumArtistListSort,
AlbumListQuery,
GenreListSort,
LibraryItem,
SortOrder,
} from '/@/shared/types/domain-types';
import { AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
import { AlbumArtistListSort } from '/@/shared/types/domain/artist-domain-types';
import { GenreListSort } from '/@/shared/types/domain/genre-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
interface NavidromeAlbumFiltersProps {
customFilters?: Partial<AlbumListFilter>;
@@ -45,13 +42,13 @@ export const NavidromeAlbumFilters = ({
const genreListQuery = useGenreList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
sortBy: GenreListSort.NAME,
sortOrder: SortOrder.ASC,
startIndex: 0,
sortOrder: ListSortOrder.ASC,
offset: 0,
},
serverId,
});
@@ -79,7 +76,7 @@ export const NavidromeAlbumFilters = ({
const tagsQuery = useTagList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
@@ -188,13 +185,13 @@ export const NavidromeAlbumFilters = ({
const albumArtistListQuery = useAlbumArtistList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
// searchTerm: debouncedSearchTerm,
sortBy: AlbumArtistListSort.NAME,
sortOrder: SortOrder.ASC,
sortOrder: ListSortOrder.ASC,
startIndex: 0,
},
serverId,
@@ -14,13 +14,10 @@ import { SpinnerIcon } from '/@/shared/components/spinner/spinner';
import { Stack } from '/@/shared/components/stack/stack';
import { Switch } from '/@/shared/components/switch/switch';
import { Text } from '/@/shared/components/text/text';
import {
AlbumArtistListSort,
AlbumListQuery,
GenreListSort,
LibraryItem,
SortOrder,
} from '/@/shared/types/domain-types';
import { AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
import { AlbumArtistListSort } from '/@/shared/types/domain/artist-domain-types';
import { GenreListSort } from '/@/shared/types/domain/genre-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
interface SubsonicAlbumFiltersProps {
disableArtistFilter?: boolean;
@@ -42,12 +39,12 @@ export const SubsonicAlbumFilters = ({
const albumArtistListQuery = useAlbumArtistList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
sortBy: AlbumArtistListSort.NAME,
sortOrder: SortOrder.ASC,
sortOrder: ListSortOrder.ASC,
startIndex: 0,
},
serverId,
@@ -75,13 +72,13 @@ export const SubsonicAlbumFilters = ({
const genreListQuery = useGenreList({
options: {
cacheTime: 1000 * 60 * 2,
gcTime: 1000 * 60 * 2,
staleTime: 1000 * 60 * 1,
},
query: {
sortBy: GenreListSort.NAME,
sortOrder: SortOrder.ASC,
startIndex: 0,
sortOrder: ListSortOrder.ASC,
offset: 0,
},
serverId,
});
@@ -1,15 +1,15 @@
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
import type { AlbumDetailQuery } from '/@/shared/types/domain-types';
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useQuery } from '@tanstack/react-query';
import { controller } from '/@/renderer/api/controller';
import { queryKeys } from '/@/renderer/api/query-keys';
import { getServerById } from '/@/renderer/store';
import { useServerById } from '/@/renderer/store';
import { AlbumDetailQuery } from '/@/shared/types/domain/album-domain-types';
export const useAlbumDetail = (args: QueryHookArgs<AlbumDetailQuery>) => {
export const useAlbumDetail = (args: RQueryHookArgs<AlbumDetailQuery>) => {
const { options, query, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
queryFn: ({ signal }) => {
@@ -1,15 +1,15 @@
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
import type { AlbumListQuery } from '/@/shared/types/domain-types';
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { getServerById } from '/@/renderer/store';
import { useServerById } from '/@/renderer/store';
import { AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
export const useAlbumListCount = (args: QueryHookArgs<AlbumListQuery>) => {
export const useAlbumListCount = (args: RQueryHookArgs<AlbumListQuery>) => {
const { options, query, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!serverId,
@@ -1,16 +1,16 @@
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
import type { AlbumListQuery, AlbumListResponse } from '/@/shared/types/domain-types';
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { controller } from '/@/renderer/api/controller';
import { queryKeys } from '/@/renderer/api/query-keys';
import { getServerById } from '/@/renderer/store';
import { useServerById } from '/@/renderer/store';
import { AlbumListQuery, AlbumListResponse } from '/@/shared/types/domain/album-domain-types';
export const useAlbumList = (args: QueryHookArgs<AlbumListQuery>) => {
export const useAlbumList = (args: RQueryHookArgs<AlbumListQuery>) => {
const { options, query, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!serverId,
@@ -33,9 +33,9 @@ export const useAlbumList = (args: QueryHookArgs<AlbumListQuery>) => {
});
};
export const useAlbumListInfinite = (args: QueryHookArgs<AlbumListQuery>) => {
export const useAlbumListInfinite = (args: RQueryHookArgs<AlbumListQuery>) => {
const { options, query, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
return useInfiniteQuery({
enabled: !!serverId,
@@ -57,7 +57,7 @@ export const useAlbumListInfinite = (args: QueryHookArgs<AlbumListQuery>) => {
query: {
...query,
limit: query.limit || 50,
startIndex: pageParam * (query.limit || 50),
offset: pageParam * (query.limit || 50),
},
});
},
@@ -12,7 +12,7 @@ import { AnimatedPage, LibraryHeaderBar } from '/@/renderer/features/shared';
import { useFastAverageColor } from '/@/renderer/hooks';
import { useCurrentServer, useGeneralSettings } from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
const AlbumDetailRoute = () => {
const tableRef = useRef<AgGridReactType | null>(null);
@@ -16,12 +16,9 @@ import { usePlayQueueAdd } from '/@/renderer/features/player';
import { AnimatedPage } from '/@/renderer/features/shared';
import { queryClient } from '/@/renderer/lib/react-query';
import { useCurrentServer, useListFilterByKey } from '/@/renderer/store';
import {
AlbumListQuery,
GenreListSort,
LibraryItem,
SortOrder,
} from '/@/shared/types/domain-types';
import { AlbumListQuery } from '/@/shared/types/domain/album-domain-types';
import { GenreListSort } from '/@/shared/types/domain/genre-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { Play } from '/@/shared/types/types';
const AlbumListRoute = () => {
@@ -55,13 +52,13 @@ const AlbumListRoute = () => {
const genreList = useGenreList({
options: {
cacheTime: 1000 * 60 * 60,
gcTime: 1000 * 60 * 60,
enabled: !!genreId,
},
query: {
sortBy: GenreListSort.NAME,
sortOrder: SortOrder.ASC,
startIndex: 0,
sortOrder: ListSortOrder.ASC,
offset: 0,
},
serverId: server?.id,
});
@@ -77,7 +74,7 @@ const AlbumListRoute = () => {
const itemCountCheck = useAlbumListCount({
options: {
cacheTime: 1000 * 60,
gcTime: 1000 * 60,
staleTime: 1000 * 60,
},
query: {
@@ -33,7 +33,8 @@ import { Icon } from '/@/shared/components/icon/icon';
import { Spoiler } from '/@/shared/components/spoiler/spoiler';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { LibraryItem, SongDetailResponse } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { SongDetailResponse } from '/@/shared/types/domain/song-domain-types';
const DummyAlbumDetailRoute = () => {
const cq = useContainerQuery();
@@ -35,15 +35,10 @@ import { Group } from '/@/shared/components/group/group';
import { Spoiler } from '/@/shared/components/spoiler/spoiler';
import { Stack } from '/@/shared/components/stack/stack';
import { TextTitle } from '/@/shared/components/text-title/text-title';
import {
Album,
AlbumArtist,
AlbumListSort,
LibraryItem,
QueueSong,
ServerType,
SortOrder,
} from '/@/shared/types/domain-types';
import { Album, AlbumListSort } from '/@/shared/types/domain/album-domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { CardRow, Play, TableColumn } from '/@/shared/types/types';
interface AlbumArtistDetailContentProps {
@@ -106,8 +101,8 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
compilation: false,
limit: 15,
sortBy: AlbumListSort.RELEASE_DATE,
sortOrder: SortOrder.DESC,
startIndex: 0,
sortOrder: ListSortOrder.DESC,
offset: 0,
},
serverId: server?.id,
});
@@ -121,8 +116,8 @@ export const AlbumArtistDetailContent = ({ background }: AlbumArtistDetailConten
compilation: true,
limit: 15,
sortBy: AlbumListSort.RELEASE_DATE,
sortOrder: SortOrder.DESC,
startIndex: 0,
sortOrder: ListSortOrder.DESC,
offset: 0,
},
serverId: server?.id,
});
@@ -11,7 +11,8 @@ import { Group } from '/@/shared/components/group/group';
import { Rating } from '/@/shared/components/rating/rating';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface AlbumArtistDetailHeaderProps {
background?: string;
@@ -12,7 +12,9 @@ import { SONG_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/conte
import { usePlayQueueAdd } from '/@/renderer/features/player';
import { useCurrentServer } from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { LibraryItem, QueueSong, SongListQuery } from '/@/shared/types/domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { SongListQuery } from '/@/shared/types/domain/song-domain-types';
interface AlbumArtistSongListContentProps {
data: QueueSong[];
@@ -6,7 +6,7 @@ import { LibraryHeaderBar } from '/@/renderer/features/shared';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { Badge } from '/@/shared/components/badge/badge';
import { SpinnerIcon } from '/@/shared/components/spinner/spinner';
import { QueueSong } from '/@/shared/types/domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { Play } from '/@/shared/types/types';
interface AlbumArtistDetailTopSongsListHeaderProps {
@@ -17,12 +17,11 @@ import { useHandleFavorite } from '/@/renderer/features/shared/hooks/use-handle-
import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, useListStoreActions, useListStoreByKey } from '/@/renderer/store';
import {
AlbumArtist,
AlbumArtistListQuery,
AlbumArtistListResponse,
AlbumArtistListSort,
LibraryItem,
} from '/@/shared/types/domain-types';
} from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { CardRow, ListDisplayType } from '/@/shared/types/types';
interface AlbumArtistListGridViewProps {
@@ -34,91 +34,90 @@ import { Icon } from '/@/shared/components/icon/icon';
import {
AlbumArtistListQuery,
AlbumArtistListSort,
LibraryItem,
ServerType,
SortOrder,
} from '/@/shared/types/domain-types';
} from '/@/shared/types/domain/artist-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { ListDisplayType } from '/@/shared/types/types';
const FILTERS = {
jellyfin: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.album', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.ALBUM,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.duration', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.DURATION,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.NAME,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.RANDOM,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.RECENTLY_ADDED,
},
// { defaultOrder: SortOrder.DESC, name: 'Release Date', value: AlbumArtistListSort.RELEASE_DATE },
// { defaultOrder: ListSortOrder.DESC, name: 'Release Date', value: AlbumArtistListSort.RELEASE_DATE },
],
navidrome: [
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.ALBUM_COUNT,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.FAVORITED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.mostPlayed', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.PLAY_COUNT,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.NAME,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.RATING,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.songCount', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.SONG_COUNT,
},
],
subsonic: [
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.ALBUM_COUNT,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.FAVORITED,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.NAME,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
value: AlbumArtistListSort.RATING,
},
@@ -270,7 +269,7 @@ export const AlbumArtistListHeaderFilters = ({
const updatedFilters = setFilter({
data: {
sortBy: e.currentTarget.value as AlbumArtistListSort,
sortOrder: sortOrder || SortOrder.ASC,
sortOrder: sortOrder || ListSortOrder.ASC,
},
itemType: LibraryItem.ALBUM_ARTIST,
key: pageKey,
@@ -306,7 +305,8 @@ export const AlbumArtistListHeaderFilters = ({
);
const handleToggleSortOrder = useCallback(() => {
const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
const newSortOrder =
filter.sortOrder === ListSortOrder.ASC ? ListSortOrder.DESC : ListSortOrder.ASC;
const updatedFilters = setFilter({
data: { sortOrder: newSortOrder },
itemType: LibraryItem.ALBUM_ARTIST,
@@ -15,7 +15,8 @@ import { AlbumArtistListFilter, useCurrentServer } from '/@/renderer/store';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Stack } from '/@/shared/components/stack/stack';
import { AlbumArtistListQuery, LibraryItem } from '/@/shared/types/domain-types';
import { AlbumArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface AlbumArtistListHeaderProps {
gridRef: MutableRefObject<null | VirtualInfiniteGridRef>;
@@ -8,7 +8,7 @@ import { useVirtualTable } from '/@/renderer/components/virtual-table/hooks/use-
import { useListContext } from '/@/renderer/context/list-context';
import { ARTIST_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { useCurrentServer } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface AlbumArtistListTableViewProps {
itemCount?: number;
@@ -18,12 +18,11 @@ import { AppRoute } from '/@/renderer/router/routes';
import { useCurrentServer, useListStoreActions } from '/@/renderer/store';
import { useListStoreByKey } from '/@/renderer/store/list.store';
import {
AlbumArtist,
ArtistListQuery,
ArtistListResponse,
ArtistListSort,
LibraryItem,
} from '/@/shared/types/domain-types';
} from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { CardRow, ListDisplayType } from '/@/shared/types/types';
interface ArtistListGridViewProps {
@@ -56,15 +55,11 @@ export const ArtistListGridView = ({ gridRef, itemCount }: ArtistListGridViewPro
const itemData: AlbumArtist[] = [];
for (const [, data] of queriesFromCache) {
const { items, startIndex } = data || {};
const { items, offset } = data || {};
if (items && items.length !== 1 && startIndex !== undefined) {
if (items && items.length !== 1 && offset !== undefined) {
let itemIndex = 0;
for (
let rowIndex = startIndex;
rowIndex < startIndex + items.length;
rowIndex += 1
) {
for (let rowIndex = offset; rowIndex < offset + items.length; rowIndex += 1) {
itemData[rowIndex] = items[itemIndex];
itemIndex += 1;
}
@@ -79,7 +74,7 @@ export const ArtistListGridView = ({ gridRef, itemCount }: ArtistListGridViewPro
const query: ArtistListQuery = {
...filter,
limit,
startIndex,
offset,
};
const queryKey = queryKeys.artists.list(server?.id || '', query);
@@ -33,93 +33,89 @@ import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { Select } from '/@/shared/components/select/select';
import {
ArtistListQuery,
ArtistListSort,
LibraryItem,
ServerType,
SortOrder,
} from '/@/shared/types/domain-types';
import { ArtistListQuery, ArtistListSort } from '/@/shared/types/domain/artist-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { ListDisplayType } from '/@/shared/types/types';
const FILTERS = {
jellyfin: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.album', { postProcess: 'titleCase' }),
value: ArtistListSort.ALBUM,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.duration', { postProcess: 'titleCase' }),
value: ArtistListSort.DURATION,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: ArtistListSort.NAME,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.random', { postProcess: 'titleCase' }),
value: ArtistListSort.RANDOM,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.recentlyAdded', { postProcess: 'titleCase' }),
value: ArtistListSort.RECENTLY_ADDED,
},
],
navidrome: [
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
value: ArtistListSort.ALBUM_COUNT,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
value: ArtistListSort.FAVORITED,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.mostPlayed', { postProcess: 'titleCase' }),
value: ArtistListSort.PLAY_COUNT,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: ArtistListSort.NAME,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
value: ArtistListSort.RATING,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.songCount', { postProcess: 'titleCase' }),
value: ArtistListSort.SONG_COUNT,
},
],
subsonic: [
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.albumCount', { postProcess: 'titleCase' }),
value: ArtistListSort.ALBUM_COUNT,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.isFavorited', { postProcess: 'titleCase' }),
value: ArtistListSort.FAVORITED,
},
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: ArtistListSort.NAME,
},
{
defaultOrder: SortOrder.DESC,
defaultOrder: ListSortOrder.DESC,
name: i18n.t('filter.rating', { postProcess: 'titleCase' }),
value: ArtistListSort.RATING,
},
@@ -144,7 +140,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
const cq = useContainerQuery();
const roles = useRoles({
options: {
cacheTime: 1000 * 60 * 60 * 2,
gcTime: 1000 * 60 * 60 * 2,
staleTime: 1000 * 60 * 60 * 2,
},
query: {},
@@ -192,7 +188,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
},
query: {
limit,
startIndex,
offset,
...filters,
},
}),
@@ -228,7 +224,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
},
query: {
limit,
startIndex,
offset,
...filters,
},
}),
@@ -276,7 +272,7 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
const updatedFilters = setFilter({
data: {
sortBy: e.currentTarget.value as ArtistListSort,
sortOrder: sortOrder || SortOrder.ASC,
sortOrder: sortOrder || ListSortOrder.ASC,
},
itemType: LibraryItem.ARTIST,
key: pageKey,
@@ -312,7 +308,8 @@ export const ArtistListHeaderFilters = ({ gridRef, tableRef }: ArtistListHeaderF
);
const handleToggleSortOrder = useCallback(() => {
const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
const newSortOrder =
filter.sortOrder === ListSortOrder.ASC ? ListSortOrder.DESC : ListSortOrder.ASC;
const updatedFilters = setFilter({
data: { sortOrder: newSortOrder },
itemType: LibraryItem.ARTIST,
@@ -15,7 +15,8 @@ import { ArtistListFilter, useCurrentServer } from '/@/renderer/store';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Stack } from '/@/shared/components/stack/stack';
import { ArtistListQuery, LibraryItem } from '/@/shared/types/domain-types';
import { ArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface ArtistListHeaderProps {
gridRef: MutableRefObject<null | VirtualInfiniteGridRef>;
@@ -8,7 +8,7 @@ import { useVirtualTable } from '/@/renderer/components/virtual-table/hooks/use-
import { useListContext } from '/@/renderer/context/list-context';
import { ARTIST_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { useCurrentServer } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface ArtistListTableViewProps {
itemCount?: number;
@@ -1,15 +1,14 @@
import type { AlbumArtistDetailQuery } from '/@/shared/types/domain-types';
import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useServerById } from '/@/renderer/store';
import { AlbumArtistDetailQuery } from '/@/shared/types/domain/artist-domain-types';
export const useAlbumArtistDetail = (args: QueryHookArgs<AlbumArtistDetailQuery>) => {
export const useAlbumArtistDetail = (args: RQueryHookArgs<AlbumArtistDetailQuery>) => {
const { options, query, serverId } = args || {};
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!server?.id && !!query.id,
@@ -2,13 +2,13 @@ import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { AlbumArtistListQuery } from '/@/shared/types/domain-types';
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useServerById } from '/@/renderer/store';
import { AlbumArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
export const useAlbumArtistListCount = (args: QueryHookArgs<AlbumArtistListQuery>) => {
export const useAlbumArtistListCount = (args: RQueryHookArgs<AlbumArtistListQuery>) => {
const { options, query, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!serverId,
@@ -1,15 +1,14 @@
import type { AlbumArtistListQuery } from '/@/shared/types/domain-types';
import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useServerById } from '/@/renderer/store';
import { AlbumArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
export const useAlbumArtistList = (args: QueryHookArgs<AlbumArtistListQuery>) => {
export const useAlbumArtistList = (args: RQueryHookArgs<AlbumArtistListQuery>) => {
const { options, query, serverId } = args || {};
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!server?.id,
@@ -1,15 +1,14 @@
import type { AlbumArtistDetailQuery } from '/@/shared/types/domain-types';
import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useServerById } from '/@/renderer/store';
import { AlbumArtistDetailQuery } from '/@/shared/types/domain/artist-domain-types';
export const useAlbumArtistInfo = (args: QueryHookArgs<AlbumArtistDetailQuery>) => {
export const useAlbumArtistInfo = (args: RQueryHookArgs<AlbumArtistDetailQuery>) => {
const { options, query, serverId } = args || {};
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!server?.id && !!query.id,
@@ -2,13 +2,13 @@ import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { ArtistListQuery } from '/@/shared/types/domain-types';
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useServerById } from '/@/renderer/store';
import { ArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
export const useArtistListCount = (args: QueryHookArgs<ArtistListQuery>) => {
export const useArtistListCount = (args: RQueryHookArgs<ArtistListQuery>) => {
const { options, query, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!serverId,
@@ -2,12 +2,12 @@ import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useServerById } from '/@/renderer/store';
export const useRoles = (args: QueryHookArgs<object>) => {
export const useRoles = (args: RQueryHookArgs<object>) => {
const { options, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!serverId,
@@ -1,15 +1,15 @@
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
import type { TopSongListQuery } from '/@/shared/types/domain-types';
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { getServerById } from '/@/renderer/store';
import { useServerById } from '/@/renderer/store';
import { TopSongListQuery } from '/@/shared/types/domain/song-domain-types';
export const useTopSongsList = (args: QueryHookArgs<TopSongListQuery>) => {
export const useTopSongsList = (args: RQueryHookArgs<TopSongListQuery>) => {
const { options, query, serverId } = args || {};
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!server?.id,
@@ -10,7 +10,7 @@ import { AnimatedPage, LibraryHeaderBar } from '/@/renderer/features/shared';
import { useFastAverageColor } from '/@/renderer/hooks';
import { useCurrentServer } from '/@/renderer/store';
import { usePlayButtonBehavior } from '/@/renderer/store/settings.store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
const AlbumArtistDetailRoute = () => {
const scrollAreaRef = useRef<HTMLDivElement>(null);
@@ -10,7 +10,7 @@ import { useAlbumArtistDetail } from '/@/renderer/features/artists/queries/album
import { useTopSongsList } from '/@/renderer/features/artists/queries/top-songs-list-query';
import { AnimatedPage } from '/@/renderer/features/shared';
import { useCurrentServer } from '/@/renderer/store/auth.store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
const AlbumArtistDetailTopSongsListRoute = () => {
const tableRef = useRef<AgGridReactType | null>(null);
@@ -10,7 +10,8 @@ import { useAlbumArtistListCount } from '/@/renderer/features/artists/queries/al
import { AnimatedPage } from '/@/renderer/features/shared';
import { useCurrentServer } from '/@/renderer/store/auth.store';
import { useListFilterByKey } from '/@/renderer/store/list.store';
import { AlbumArtistListQuery, LibraryItem } from '/@/shared/types/domain-types';
import { AlbumArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
const AlbumArtistListRoute = () => {
const gridRef = useRef<null | VirtualInfiniteGridRef>(null);
@@ -22,7 +23,7 @@ const AlbumArtistListRoute = () => {
const itemCountCheck = useAlbumArtistListCount({
options: {
cacheTime: 1000 * 60,
gcTime: 1000 * 60,
staleTime: 1000 * 60,
},
query: albumArtistListFilter,
@@ -10,7 +10,8 @@ import { useArtistListCount } from '/@/renderer/features/artists/queries/artist-
import { AnimatedPage } from '/@/renderer/features/shared';
import { useCurrentServer } from '/@/renderer/store/auth.store';
import { useListFilterByKey } from '/@/renderer/store/list.store';
import { ArtistListQuery, LibraryItem } from '/@/shared/types/domain-types';
import { ArtistListQuery } from '/@/shared/types/domain/artist-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
const ArtistListRoute = () => {
const gridRef = useRef<null | VirtualInfiniteGridRef>(null);
@@ -22,7 +23,7 @@ const ArtistListRoute = () => {
const itemCountCheck = useArtistListCount({
options: {
cacheTime: 1000 * 60,
gcTime: 1000 * 60,
staleTime: 1000 * 60,
},
query: artistListFilter,
@@ -37,7 +37,7 @@ import { useRemoveFromPlaylist } from '/@/renderer/features/playlists/mutations/
import { useCreateFavorite, useDeleteFavorite, useSetRating } from '/@/renderer/features/shared';
import { AppRoute } from '/@/renderer/router/routes';
import {
getServerById,
useServerById,
useAuthStore,
useCurrentServer,
usePlayerStore,
@@ -57,13 +57,12 @@ import { Rating } from '/@/shared/components/rating/rating';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { toast } from '/@/shared/components/toast/toast';
import { ServerFeature, ServerType } from '/@/shared/types/domain/server-domain-types';
import {
AnyLibraryItem,
AnyLibraryItems,
LibraryItem,
ServerType,
} from '/@/shared/types/domain-types';
import { ServerFeature } from '/@/shared/types/features-types';
} from '/@/shared/types/domain/shared-domain-types';
import { Play, PlaybackType } from '/@/shared/types/types';
type ContextMenuContextProps = {
@@ -713,7 +712,7 @@ export const ContextMenuProvider = ({ children }: ContextMenuProviderProps) => {
const item = ctx.data[0];
const songs = await controller.getSimilarSongs({
apiClientProps: {
server: getServerById(item.serverId),
server: useServerById(item.serverId),
signal: undefined,
},
query: { albumArtistIds: item.albumArtistIds, songId: item.id },
+1 -1
View File
@@ -1,6 +1,6 @@
import { GridOptions, RowNode } from '@ag-grid-community/core';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { createUseExternalEvents } from '/@/shared/utils/create-use-external-events';
export type ContextMenuEvents = {
@@ -2,14 +2,11 @@ import { CellContextMenuEvent, GridApi } from '@ag-grid-community/core';
import sortBy from 'lodash/sortBy';
import { openContextMenu, SetContextMenuItems } from '/@/renderer/features/context-menu/events';
import {
Album,
AlbumArtist,
Artist,
LibraryItem,
QueueSong,
Song,
} from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { Artist } from '/@/shared/types/domain/artist-domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
export const useHandleTableContextMenu = (
itemType: LibraryItem,
@@ -5,13 +5,14 @@ import { useCallback, useEffect, useState } from 'react';
import { controller } from '/@/renderer/api/controller';
import {
DiscordDisplayType,
getServerById,
useAppStore,
useDiscordSettings,
useServerById,
useGeneralSettings,
usePlayerStore,
} from '/@/renderer/store';
import { QueueSong, ServerType } from '/@/shared/types/domain-types';
import { QueueSong } from '/@/shared/types/domain/player-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { PlayerStatus } from '/@/shared/types/types';
const discordRpc = isElectron() ? window.api.discordRpc : null;
@@ -92,7 +93,7 @@ export const useDiscordRpc = () => {
if (song.serverType === ServerType.JELLYFIN && song.imageUrl) {
activity.largeImageKey = song.imageUrl;
} else if (song.serverType === ServerType.NAVIDROME) {
const server = getServerById(song.serverId);
const server = useServerById(song.serverId);
try {
const info = await controller.getAlbumInfo({
@@ -15,13 +15,13 @@ import { useListContext } from '/@/renderer/context/list-context';
import { usePlayQueueAdd } from '/@/renderer/features/player';
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
import { useCurrentServer, useListStoreActions, useListStoreByKey } from '/@/renderer/store';
import { Album } from '/@/shared/types/domain/album-domain-types';
import {
Album,
Genre,
GenreListQuery,
GenreListResponse,
LibraryItem,
} from '/@/shared/types/domain-types';
} from '/@/shared/types/domain/genre-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { CardRow, ListDisplayType } from '/@/shared/types/types';
export const GenreListGridView = ({ gridRef, itemCount }: any) => {
@@ -74,15 +74,11 @@ export const GenreListGridView = ({ gridRef, itemCount }: any) => {
const itemData: Genre[] = [];
for (const [, data] of queriesFromCache) {
const { items, startIndex } = data || {};
const { items, offset } = data || {};
if (items && items.length !== 1 && startIndex !== undefined) {
if (items && items.length !== 1 && offset !== undefined) {
let itemIndex = 0;
for (
let rowIndex = startIndex;
rowIndex < startIndex + items.length;
rowIndex += 1
) {
for (let rowIndex = offset; rowIndex < offset + items.length; rowIndex += 1) {
itemData[rowIndex] = items[itemIndex];
itemIndex += 1;
}
@@ -101,7 +97,7 @@ export const GenreListGridView = ({ gridRef, itemCount }: any) => {
const query: GenreListQuery = {
...filter,
limit: take,
startIndex: skip,
offset: skip,
};
const queryKey = queryKeys.albums.list(server?.id || '', query);
@@ -33,33 +33,29 @@ import { DropdownMenu } from '/@/shared/components/dropdown-menu/dropdown-menu';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import {
GenreListQuery,
GenreListSort,
LibraryItem,
ServerType,
SortOrder,
} from '/@/shared/types/domain-types';
import { GenreListQuery, GenreListSort } from '/@/shared/types/domain/genre-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { ListDisplayType } from '/@/shared/types/types';
const FILTERS = {
jellyfin: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: GenreListSort.NAME,
},
],
navidrome: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: GenreListSort.NAME,
},
],
subsonic: [
{
defaultOrder: SortOrder.ASC,
defaultOrder: ListSortOrder.ASC,
name: i18n.t('filter.name', { postProcess: 'titleCase' }),
value: GenreListSort.NAME,
},
@@ -137,7 +133,7 @@ export const GenreListHeaderFilters = ({
customFilters,
data: {
sortBy: e.currentTarget.value as GenreListSort,
sortOrder: sortOrder || SortOrder.ASC,
sortOrder: sortOrder || ListSortOrder.ASC,
},
itemType: LibraryItem.GENRE,
key: pageKey,
@@ -175,7 +171,8 @@ export const GenreListHeaderFilters = ({
);
const handleToggleSortOrder = useCallback(() => {
const newSortOrder = filter.sortOrder === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
const newSortOrder =
filter.sortOrder === ListSortOrder.ASC ? ListSortOrder.DESC : ListSortOrder.ASC;
const updatedFilters = setFilter({
customFilters,
data: { sortOrder: newSortOrder },
@@ -15,7 +15,8 @@ import { GenreListFilter, useCurrentServer } from '/@/renderer/store';
import { Flex } from '/@/shared/components/flex/flex';
import { Group } from '/@/shared/components/group/group';
import { Stack } from '/@/shared/components/stack/stack';
import { GenreListQuery, LibraryItem } from '/@/shared/types/domain-types';
import { GenreListQuery } from '/@/shared/types/domain/genre-domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface GenreListHeaderProps {
gridRef: MutableRefObject<null | VirtualInfiniteGridRef>;
@@ -11,7 +11,7 @@ import { useListContext } from '/@/renderer/context/list-context';
import { GENRE_CONTEXT_MENU_ITEMS } from '/@/renderer/features/context-menu/context-menu-items';
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
import { useCurrentServer } from '/@/renderer/store';
import { LibraryItem } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain/shared-domain-types';
interface GenreListTableViewProps {
itemCount?: number;
@@ -1,15 +1,15 @@
import type { QueryHookArgs } from '/@/renderer/lib/react-query';
import type { GenreListQuery } from '/@/shared/types/domain-types';
import type { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { getServerById } from '/@/renderer/store';
import { useServerById } from '/@/renderer/store';
import { GenreListQuery } from '/@/shared/types/domain/genre-domain-types';
export const useGenreList = (args: QueryHookArgs<GenreListQuery>) => {
export const useGenreList = (args: RQueryHookArgs<GenreListQuery>) => {
const { options, query, serverId } = args || {};
const server = getServerById(serverId);
const server = useServerById(serverId);
return useQuery({
enabled: !!server,
@@ -10,7 +10,7 @@ import { useGenreList } from '/@/renderer/features/genres/queries/genre-list-que
import { AnimatedPage } from '/@/renderer/features/shared';
import { useCurrentServer } from '/@/renderer/store';
import { useListStoreByKey } from '/@/renderer/store/list.store';
import { GenreListQuery } from '/@/shared/types/domain-types';
import { GenreListQuery } from '/@/shared/types/domain/genre-domain-types';
const GenreListRoute = () => {
const gridRef = useRef<null | VirtualInfiniteGridRef>(null);
@@ -23,7 +23,7 @@ const GenreListRoute = () => {
query: {
...filter,
limit: 1,
startIndex: 0,
offset: 0,
},
serverId: server?.id,
});
@@ -2,19 +2,20 @@ import { useQuery } from '@tanstack/react-query';
import { api } from '/@/renderer/api';
import { queryKeys } from '/@/renderer/api/query-keys';
import { QueryHookArgs } from '/@/renderer/lib/react-query';
import { getServerById } from '/@/renderer/store';
import { AlbumListQuery, AlbumListSort, SortOrder } from '/@/shared/types/domain-types';
import { RQueryHookArgs } from '/@/renderer/lib/react-query';
import { useServerById } from '/@/renderer/store';
import { AlbumListQuery, AlbumListSort } from '/@/shared/types/domain/album-domain-types';
import { ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
export const useRecentlyPlayed = (args: QueryHookArgs<Partial<AlbumListQuery>>) => {
export const useRecentlyPlayed = (args: RQueryHookArgs<Partial<AlbumListQuery>>) => {
const { options, query, serverId } = args;
const server = getServerById(serverId);
const server = useServerById(serverId);
const requestQuery: AlbumListQuery = {
limit: 5,
sortBy: AlbumListSort.RECENTLY_PLAYED,
sortOrder: SortOrder.ASC,
startIndex: 0,
sortOrder: ListSortOrder.ASC,
offset: 0,
...query,
};
+195 -195
View File
@@ -4,32 +4,24 @@ import { useTranslation } from 'react-i18next';
import { queryKeys } from '/@/renderer/api/query-keys';
import { FeatureCarousel } from '/@/renderer/components/feature-carousel/feature-carousel';
import { MemoizedSwiperGridCarousel } from '/@/renderer/components/grid-carousel/grid-carousel';
import { NativeScrollArea } from '/@/renderer/components/native-scroll-area/native-scroll-area';
import { useAlbumList } from '/@/renderer/features/albums';
import { useRecentlyPlayed } from '/@/renderer/features/home/queries/recently-played-query';
import { AnimatedPage, LibraryHeaderBar } from '/@/renderer/features/shared';
import { AlbumInfiniteCarousel } from '/@/renderer/features/shared/components/infinite-album-carousel/infinite-album-carousel';
import { useSongList } from '/@/renderer/features/songs';
import { AppRoute } from '/@/renderer/router/routes';
import {
HomeItem,
useCurrentServer,
useGeneralSettings,
useWindowSettings,
} from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { Spinner } from '/@/shared/components/spinner/spinner';
import { Stack } from '/@/shared/components/stack/stack';
import { TextTitle } from '/@/shared/components/text-title/text-title';
import {
AlbumListSort,
LibraryItem,
ServerType,
SongListSort,
SortOrder,
} from '/@/shared/types/domain-types';
import { AlbumListSort, AlbumListSortOptions } from '/@/shared/types/domain/album-domain-types';
import { ServerType } from '/@/shared/types/domain/server-domain-types';
import { LibraryItem, ListSortOrder } from '/@/shared/types/domain/shared-domain-types';
import { SongListSort } from '/@/shared/types/domain/song-domain-types';
import { Platform } from '/@/shared/types/types';
const HomeRoute = () => {
@@ -41,198 +33,198 @@ const HomeRoute = () => {
const { windowBarStyle } = useWindowSettings();
const { homeFeature, homeItems } = useGeneralSettings();
const feature = useAlbumList({
options: {
cacheTime: 1000 * 60,
enabled: homeFeature,
staleTime: 1000 * 60,
},
query: {
limit: 20,
sortBy: AlbumListSort.RANDOM,
sortOrder: SortOrder.DESC,
startIndex: 0,
},
serverId: server?.id,
});
// const feature = useAlbumList({
// options: {
// enabled: homeFeature,
// gcTime: 1000 * 60,
// staleTime: 1000 * 60,
// },
// query: {
// limit: 20,
// offset: 0,
// sortBy: AlbumListSort.RANDOM,
// sortOrder: ListSortOrder.DESC,
// },
// serverId: server?.id,
// });
const featureItemsWithImage = useMemo(() => {
return feature.data?.items?.filter((item) => item.imageUrl) ?? [];
}, [feature.data?.items]);
// const featureItemsWithImage = useMemo(() => {
// return feature.data?.items?.filter((item) => item.imageUrl) ?? [];
// }, [feature.data?.items]);
const random = useAlbumList({
options: {
staleTime: 1000 * 60 * 5,
},
query: {
limit: itemsPerPage,
sortBy: AlbumListSort.RANDOM,
sortOrder: SortOrder.ASC,
startIndex: 0,
},
serverId: server?.id,
});
// const random = useAlbumList({
// options: {
// staleTime: 1000 * 60 * 5,
// },
// query: {
// limit: itemsPerPage,
// offset: 0,
// sortBy: AlbumListSort.RANDOM,
// sortOrder: ListSortOrder.ASC,
// },
// serverId: server?.id,
// });
const recentlyPlayed = useRecentlyPlayed({
options: {
staleTime: 0,
},
query: {
limit: itemsPerPage,
sortBy: AlbumListSort.RECENTLY_PLAYED,
sortOrder: SortOrder.DESC,
startIndex: 0,
},
serverId: server?.id,
});
// const recentlyPlayed = useRecentlyPlayed({
// options: {
// staleTime: 0,
// },
// query: {
// limit: itemsPerPage,
// offset: 0,
// sortBy: AlbumListSort.RECENTLY_PLAYED,
// sortOrder: ListSortOrder.DESC,
// },
// serverId: server?.id,
// });
const recentlyAdded = useAlbumList({
options: {
staleTime: 1000 * 60 * 5,
},
query: {
limit: itemsPerPage,
sortBy: AlbumListSort.RECENTLY_ADDED,
sortOrder: SortOrder.DESC,
startIndex: 0,
},
serverId: server?.id,
});
// const recentlyAdded = useAlbumList({
// options: {
// staleTime: 1000 * 60 * 5,
// },
// query: {
// limit: itemsPerPage,
// offset: 0,
// sortBy: AlbumListSort.RECENTLY_ADDED,
// sortOrder: ListSortOrder.DESC,
// },
// serverId: server?.id,
// });
const mostPlayedAlbums = useAlbumList({
options: {
enabled: server?.type === ServerType.SUBSONIC || server?.type === ServerType.NAVIDROME,
staleTime: 1000 * 60 * 5,
},
query: {
limit: itemsPerPage,
sortBy: AlbumListSort.PLAY_COUNT,
sortOrder: SortOrder.DESC,
startIndex: 0,
},
serverId: server?.id,
});
// const mostPlayedAlbums = useAlbumList({
// options: {
// enabled: server?.type === ServerType.SUBSONIC || server?.type === ServerType.NAVIDROME,
// staleTime: 1000 * 60 * 5,
// },
// query: {
// limit: itemsPerPage,
// offset: 0,
// sortBy: AlbumListSort.PLAY_COUNT,
// sortOrder: ListSortOrder.DESC,
// },
// serverId: server?.id,
// });
const mostPlayedSongs = useSongList(
{
options: {
enabled: server?.type === ServerType.JELLYFIN,
staleTime: 1000 * 60 * 5,
},
query: {
limit: itemsPerPage,
sortBy: SongListSort.PLAY_COUNT,
sortOrder: SortOrder.DESC,
startIndex: 0,
},
serverId: server?.id,
},
300,
);
// const mostPlayedSongs = useSongList(
// {
// options: {
// enabled: server?.type === ServerType.JELLYFIN,
// staleTime: 1000 * 60 * 5,
// },
// query: {
// limit: itemsPerPage,
// offset: 0,
// sortBy: SongListSort.PLAY_COUNT,
// sortOrder: ListSortOrder.DESC,
// },
// serverId: server?.id,
// },
// 300,
// );
const isLoading =
random.isLoading ||
recentlyPlayed.isLoading ||
recentlyAdded.isLoading ||
(server?.type === ServerType.JELLYFIN && mostPlayedSongs.isLoading) ||
((server?.type === ServerType.SUBSONIC || server?.type === ServerType.NAVIDROME) &&
mostPlayedAlbums.isLoading);
// const isLoading =
// random.isLoading ||
// recentlyPlayed.isLoading ||
// recentlyAdded.isLoading ||
// (server?.type === ServerType.JELLYFIN && mostPlayedSongs.isLoading) ||
// ((server?.type === ServerType.SUBSONIC || server?.type === ServerType.NAVIDROME) &&
// mostPlayedAlbums.isLoading);
if (isLoading) {
return <Spinner container />;
}
// if (isLoading) {
// return <Spinner container />;
// }
const carousels = {
[HomeItem.MOST_PLAYED]: {
data:
server?.type === ServerType.JELLYFIN
? mostPlayedSongs?.data?.items
: mostPlayedAlbums?.data?.items,
itemType: server?.type === ServerType.JELLYFIN ? LibraryItem.SONG : LibraryItem.ALBUM,
pagination: {
itemsPerPage,
},
sortBy:
server?.type === ServerType.JELLYFIN
? SongListSort.PLAY_COUNT
: AlbumListSort.PLAY_COUNT,
sortOrder: SortOrder.DESC,
title: t('page.home.mostPlayed', { postProcess: 'sentenceCase' }),
},
[HomeItem.RANDOM]: {
data: random?.data?.items,
itemType: LibraryItem.ALBUM,
sortBy: AlbumListSort.RANDOM,
sortOrder: SortOrder.ASC,
title: t('page.home.explore', { postProcess: 'sentenceCase' }),
},
[HomeItem.RECENTLY_ADDED]: {
data: recentlyAdded?.data?.items,
itemType: LibraryItem.ALBUM,
pagination: {
itemsPerPage,
},
sortBy: AlbumListSort.RECENTLY_ADDED,
sortOrder: SortOrder.DESC,
title: t('page.home.newlyAdded', { postProcess: 'sentenceCase' }),
},
[HomeItem.RECENTLY_PLAYED]: {
data: recentlyPlayed?.data?.items,
itemType: LibraryItem.ALBUM,
pagination: {
itemsPerPage,
},
sortBy: AlbumListSort.RECENTLY_PLAYED,
sortOrder: SortOrder.DESC,
title: t('page.home.recentlyPlayed', { postProcess: 'sentenceCase' }),
},
};
// const carousels = {
// [HomeItem.MOST_PLAYED]: {
// data:
// server?.type === ServerType.JELLYFIN
// ? mostPlayedSongs?.data?.items
// : mostPlayedAlbums?.data?.items,
// itemType: server?.type === ServerType.JELLYFIN ? LibraryItem.SONG : LibraryItem.ALBUM,
// pagination: {
// itemsPerPage,
// },
// sortBy:
// server?.type === ServerType.JELLYFIN
// ? SongListSort.PLAY_COUNT
// : AlbumListSort.PLAY_COUNT,
// sortOrder: ListSortOrder.DESC,
// title: t('page.home.mostPlayed', { postProcess: 'sentenceCase' }),
// },
// [HomeItem.RANDOM]: {
// data: random?.data?.items,
// itemType: LibraryItem.ALBUM,
// sortBy: AlbumListSort.RANDOM,
// sortOrder: ListSortOrder.ASC,
// title: t('page.home.explore', { postProcess: 'sentenceCase' }),
// },
// [HomeItem.RECENTLY_ADDED]: {
// data: recentlyAdded?.data?.items,
// itemType: LibraryItem.ALBUM,
// pagination: {
// itemsPerPage,
// },
// sortBy: AlbumListSort.RECENTLY_ADDED,
// sortOrder: ListSortOrder.DESC,
// title: t('page.home.newlyAdded', { postProcess: 'sentenceCase' }),
// },
// [HomeItem.RECENTLY_PLAYED]: {
// data: recentlyPlayed?.data?.items,
// itemType: LibraryItem.ALBUM,
// pagination: {
// itemsPerPage,
// },
// sortBy: AlbumListSort.RECENTLY_PLAYED,
// sortOrder: ListSortOrder.DESC,
// title: t('page.home.recentlyPlayed', { postProcess: 'sentenceCase' }),
// },
// };
const sortedCarousel = homeItems
.filter((item) => {
if (item.disabled) {
return false;
}
if (server?.type === ServerType.JELLYFIN && item.id === HomeItem.RECENTLY_PLAYED) {
return false;
}
// const sortedCarousel = homeItems
// .filter((item) => {
// if (item.disabled) {
// return false;
// }
// if (server?.type === ServerType.JELLYFIN && item.id === HomeItem.RECENTLY_PLAYED) {
// return false;
// }
return true;
})
.map((item) => ({
...carousels[item.id],
uniqueId: item.id,
}));
// return true;
// })
// .map((item) => ({
// ...carousels[item.id],
// uniqueId: item.id,
// }));
const invalidateCarouselQuery = (carousel: {
itemType: LibraryItem;
sortBy: AlbumListSort | SongListSort;
sortOrder: SortOrder;
}) => {
if (carousel.itemType === LibraryItem.ALBUM) {
queryClient.invalidateQueries({
exact: false,
queryKey: queryKeys.albums.list(server?.id, {
limit: itemsPerPage,
sortBy: carousel.sortBy,
sortOrder: carousel.sortOrder,
startIndex: 0,
}),
});
}
// const invalidateCarouselQuery = (carousel: {
// itemType: LibraryItem;
// sortBy: AlbumListSort | SongListSort;
// sortOrder: ListSortOrder;
// }) => {
// if (carousel.itemType === LibraryItem.ALBUM) {
// queryClient.invalidateQueries({
// exact: false,
// queryKey: queryKeys.albums.list(server?.id, {
// limit: itemsPerPage,
// sortBy: carousel.sortBy,
// sortOrder: carousel.sortOrder,
// startIndex: 0,
// }),
// });
// }
if (carousel.itemType === LibraryItem.SONG) {
queryClient.invalidateQueries({
exact: false,
queryKey: queryKeys.songs.list(server?.id, {
limit: itemsPerPage,
sortBy: carousel.sortBy,
sortOrder: carousel.sortOrder,
startIndex: 0,
}),
});
}
};
// if (carousel.itemType === LibraryItem.SONG) {
// queryClient.invalidateQueries({
// exact: false,
// queryKey: queryKeys.songs.list(server?.id, {
// limit: itemsPerPage,
// sortBy: carousel.sortBy,
// sortOrder: carousel.sortOrder,
// startIndex: 0,
// }),
// });
// }
// };
return (
<AnimatedPage>
@@ -255,8 +247,16 @@ const HomeRoute = () => {
pt={windowBarStyle === Platform.WEB ? '5rem' : '3rem'}
px="2rem"
>
{homeFeature && <FeatureCarousel data={featureItemsWithImage} />}
{sortedCarousel.map((carousel) => (
{/* {homeFeature && <FeatureCarousel data={featureItemsWithImage} />} */}
<AlbumInfiniteCarousel
serverId={server?.id ?? ''}
sortBy={AlbumListSortOptions.NAME}
sortOrder={ListSortOrder.ASC}
title={t('page.home.explore', { postProcess: 'sentenceCase' })}
/>
{/* {sortedCarousel.map((carousel) => (
<MemoizedSwiperGridCarousel
cardRows={[
{
@@ -320,7 +320,7 @@ const HomeRoute = () => {
}}
uniqueId={carousel.uniqueId}
/>
))}
))} */}
</Stack>
</NativeScrollArea>
</AnimatedPage>
@@ -16,18 +16,14 @@ import { Separator } from '/@/shared/components/separator/separator';
import { Spoiler } from '/@/shared/components/spoiler/spoiler';
import { Table } from '/@/shared/components/table/table';
import { Text } from '/@/shared/components/text/text';
import {
Album,
AlbumArtist,
AnyLibraryItem,
LibraryItem,
Playlist,
RelatedArtist,
Song,
} from '/@/shared/types/domain-types';
import { Album } from '/@/shared/types/domain/album-domain-types';
import { RelatedArtist } from '/@/shared/types/domain/artist-domain-types';
import { Playlist } from '/@/shared/types/domain/playlist-domain-types';
import { AnyLibraryItem, LibraryItem } from '/@/shared/types/domain/shared-domain-types';
import { Song } from '/@/shared/types/domain/song-domain-types';
export type ItemDetailsModalProps = {
item: Album | AlbumArtist | Playlist | Song;
item: Album | Playlist | Song;
};
type ItemDetailRow<T> = {
@@ -20,7 +20,7 @@ import {
InternetProviderLyricSearchResponse,
LyricSource,
LyricsOverride,
} from '/@/shared/types/domain-types';
} from '/@/shared/types/domain/lyric-domain-types';
interface SearchResultProps {
data: InternetProviderLyricSearchResponse;
@@ -15,7 +15,7 @@ import { Group } from '/@/shared/components/group/group';
import { NumberInput } from '/@/shared/components/number-input/number-input';
import { Select } from '/@/shared/components/select/select';
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
import { LyricsOverride } from '/@/shared/types/domain-types';
import { LyricsOverride } from '/@/shared/types/domain/lyric-domain-types';
interface LyricsActionsProps {
index: number;

Some files were not shown because too many files have changed in this diff Show More