diff --git a/MusicPlayerV2-GUI/eslint.config.mjs b/MusicPlayerV2-GUI/eslint.config.mjs index 9d317d9..6e703a0 100644 --- a/MusicPlayerV2-GUI/eslint.config.mjs +++ b/MusicPlayerV2-GUI/eslint.config.mjs @@ -75,7 +75,7 @@ const style = { ], '@stylistic/eol-last': [ 'error', - 'always' + 'never' ], '@stylistic/function-call-spacing': [ 'error', @@ -83,7 +83,9 @@ const style = { ], '@stylistic/function-paren-newline': [ 'error', - 'multiline' + { + 'minItems': 3 + } ], '@stylistic/function-call-argument-newline': [ 'error', diff --git a/MusicPlayerV2-GUI/package-lock.json b/MusicPlayerV2-GUI/package-lock.json index 67ff7f6..1b433fb 100644 --- a/MusicPlayerV2-GUI/package-lock.json +++ b/MusicPlayerV2-GUI/package-lock.json @@ -13,7 +13,7 @@ "@melloware/coloris": "^0.24.0", "@rollup/plugin-inject": "^5.0.5", "buffer": "^6.0.3", - "colorthief": "^2.2.0", + "colorthief": "^2.6.0", "music-metadata-browser": "^2.5.10", "musickit-typescript": "^1.2.4", "pinia": "^2.1.7", @@ -23,9 +23,10 @@ "vue-router": "^4.2.5" }, "devDependencies": { - "@eslint/js": "^9.34.0", + "@eslint/js": "^9.35.0", "@stylistic/eslint-plugin": "^5.3.1", "@tsconfig/node20": "^20.1.2", + "@types/jquery": "^3.5.33", "@types/node": "^20.11.10", "@vitejs/plugin-vue": "^6.0.0", "@vue/tsconfig": "^0.5.1", @@ -33,7 +34,7 @@ "npm-run-all2": "^6.1.1", "sass-embedded": "^1.92.0", "typescript": "~5.3.0", - "typescript-eslint": "^8.42.0", + "typescript-eslint": "^8.43.0", "vite": "^7.1.4", "vue-tsc": "^2.0.29" } @@ -639,9 +640,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", - "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "dev": true, "license": "MIT", "engines": { @@ -1816,6 +1817,16 @@ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, + "node_modules/@types/jquery": { + "version": "3.5.33", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz", + "integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sizzle": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1840,18 +1851,25 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/sizzle": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.10.tgz", + "integrity": "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==", + "dev": true, + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.42.0.tgz", - "integrity": "sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.43.0.tgz", + "integrity": "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/type-utils": "8.42.0", - "@typescript-eslint/utils": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/type-utils": "8.43.0", + "@typescript-eslint/utils": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -1865,7 +1883,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.42.0", + "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -1881,16 +1899,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.42.0.tgz", - "integrity": "sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.43.0.tgz", + "integrity": "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4" }, "engines": { @@ -1906,14 +1924,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.42.0.tgz", - "integrity": "sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.43.0.tgz", + "integrity": "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.42.0", - "@typescript-eslint/types": "^8.42.0", + "@typescript-eslint/tsconfig-utils": "^8.43.0", + "@typescript-eslint/types": "^8.43.0", "debug": "^4.3.4" }, "engines": { @@ -1928,14 +1946,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.42.0.tgz", - "integrity": "sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.43.0.tgz", + "integrity": "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0" + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1946,9 +1964,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.42.0.tgz", - "integrity": "sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.43.0.tgz", + "integrity": "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==", "dev": true, "license": "MIT", "engines": { @@ -1963,15 +1981,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.42.0.tgz", - "integrity": "sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.43.0.tgz", + "integrity": "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/utils": "8.42.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/utils": "8.43.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -1988,9 +2006,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.42.0.tgz", - "integrity": "sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.43.0.tgz", + "integrity": "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==", "dev": true, "license": "MIT", "engines": { @@ -2002,16 +2020,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.42.0.tgz", - "integrity": "sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.43.0.tgz", + "integrity": "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.42.0", - "@typescript-eslint/tsconfig-utils": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/visitor-keys": "8.42.0", + "@typescript-eslint/project-service": "8.43.0", + "@typescript-eslint/tsconfig-utils": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2057,16 +2075,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.42.0.tgz", - "integrity": "sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.43.0.tgz", + "integrity": "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.42.0", - "@typescript-eslint/types": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0" + "@typescript-eslint/scope-manager": "8.43.0", + "@typescript-eslint/types": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2081,13 +2099,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.42.0.tgz", - "integrity": "sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.43.0.tgz", + "integrity": "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.42.0", + "@typescript-eslint/types": "8.43.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3420,6 +3438,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/js": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -4014,9 +4046,9 @@ } }, "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", "license": "MIT" }, "node_modules/is-buffer": { @@ -5923,9 +5955,9 @@ } }, "node_modules/sharp/node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -6040,9 +6072,9 @@ } }, "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", "license": "MIT", "dependencies": { "is-arrayish": "^0.3.1" @@ -6406,16 +6438,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.42.0.tgz", - "integrity": "sha512-ozR/rQn+aQXQxh1YgbCzQWDFrsi9mcg+1PM3l/z5o1+20P7suOIaNg515bpr/OYt6FObz/NHcBstydDLHWeEKg==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.43.0.tgz", + "integrity": "sha512-FyRGJKUGvcFekRRcBKFBlAhnp4Ng8rhe8tuvvkR9OiU0gfd4vyvTRQHEckO6VDlH57jbeUQem2IpqPq9kLJH+w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.42.0", - "@typescript-eslint/parser": "8.42.0", - "@typescript-eslint/typescript-estree": "8.42.0", - "@typescript-eslint/utils": "8.42.0" + "@typescript-eslint/eslint-plugin": "8.43.0", + "@typescript-eslint/parser": "8.43.0", + "@typescript-eslint/typescript-estree": "8.43.0", + "@typescript-eslint/utils": "8.43.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/MusicPlayerV2-GUI/package.json b/MusicPlayerV2-GUI/package.json index 9000b66..42bead6 100644 --- a/MusicPlayerV2-GUI/package.json +++ b/MusicPlayerV2-GUI/package.json @@ -17,7 +17,7 @@ "@melloware/coloris": "^0.24.0", "@rollup/plugin-inject": "^5.0.5", "buffer": "^6.0.3", - "colorthief": "^2.2.0", + "colorthief": "^2.6.0", "music-metadata-browser": "^2.5.10", "musickit-typescript": "^1.2.4", "pinia": "^2.1.7", @@ -27,9 +27,10 @@ "vue-router": "^4.2.5" }, "devDependencies": { - "@eslint/js": "^9.34.0", + "@eslint/js": "^9.35.0", "@stylistic/eslint-plugin": "^5.3.1", "@tsconfig/node20": "^20.1.2", + "@types/jquery": "^3.5.33", "@types/node": "^20.11.10", "@vitejs/plugin-vue": "^6.0.0", "@vue/tsconfig": "^0.5.1", @@ -37,7 +38,7 @@ "npm-run-all2": "^6.1.1", "sass-embedded": "^1.92.0", "typescript": "~5.3.0", - "typescript-eslint": "^8.42.0", + "typescript-eslint": "^8.43.0", "vite": "^7.1.4", "vue-tsc": "^2.0.29" } diff --git a/MusicPlayerV2-GUI/src/App.vue b/MusicPlayerV2-GUI/src/App.vue index 0ccb3fd..710d3c5 100644 --- a/MusicPlayerV2-GUI/src/App.vue +++ b/MusicPlayerV2-GUI/src/App.vue @@ -1,23 +1,52 @@ - - + + - - \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/main.ts b/MusicPlayerV2-GUI/src/main.ts index 6a55093..8b3df75 100644 --- a/MusicPlayerV2-GUI/src/main.ts +++ b/MusicPlayerV2-GUI/src/main.ts @@ -1,15 +1,18 @@ -import { createApp } from 'vue' -import { createPinia } from 'pinia' +import { + createApp +} from 'vue'; +import { + createPinia +} from 'pinia'; +import App from './App.vue'; +import router from './router'; -import App from './App.vue' -import router from './router' +const app = createApp( App ); -const app = createApp(App) - -app.use(createPinia()) -app.use(router) +app.use( createPinia() ); +app.use( router ); // localStorage.setItem( 'url', 'http://localhost:8082' ); localStorage.setItem( 'url', 'https://music-api.janishutz.com' ); -app.mount('#app') +app.mount( '#app' ); diff --git a/MusicPlayerV2-GUI/src/scripts/bizualizer.ts b/MusicPlayerV2-GUI/src/scripts/bizualizer.ts index b0c0369..c4231d7 100644 --- a/MusicPlayerV2-GUI/src/scripts/bizualizer.ts +++ b/MusicPlayerV2-GUI/src/scripts/bizualizer.ts @@ -1,9 +1,11 @@ import ColorThief from 'colorthief'; + const colorThief = new ColorThief(); const getImageData = (): Promise => { - return new Promise( ( resolve ) => { - const img = ( document.getElementById( 'current-image' ) as HTMLImageElement ); + return new Promise( resolve => { + const img = document.getElementById( 'current-image' ) as HTMLImageElement; + if ( img.complete ) { resolve( colorThief.getPalette( img ) ); } else { @@ -12,32 +14,39 @@ const getImageData = (): Promise => { } ); } } ); -} +}; const createBackground = () => { - return new Promise( ( resolve ) => { + return new Promise( resolve => { getImageData().then( palette => { const colourDetails: number[][] = []; const colours: string[] = []; + let differentEnough = true; + if ( palette[ 0 ] ) { for ( const i in palette ) { for ( const colour in colourDetails ) { const colourDiff = ( Math.abs( colourDetails[ colour ][ 0 ] - palette[ i ][ 0 ] ) / 255 + Math.abs( colourDetails[ colour ][ 1 ] - palette[ i ][ 1 ] ) / 255 + Math.abs( colourDetails[ colour ][ 2 ] - palette[ i ][ 2 ] ) / 255 ) / 3 * 100; + if ( colourDiff > 15 ) { differentEnough = true; } } + if ( differentEnough ) { colourDetails.push( palette[ i ] ); colours.push( 'rgb(' + palette[ i ][ 0 ] + ',' + palette[ i ][ 1 ] + ',' + palette[ i ][ 2 ] + ')' ); } + differentEnough = false; } } + let outColours = 'conic-gradient('; + if ( colours.length < 3 ) { for ( let i = 0; i < 3; i++ ) { if ( colours[ i ] ) { @@ -61,45 +70,56 @@ const createBackground = () => { outColours += colours[ i ] + ','; } } + outColours += colours[ 0 ] ?? 'blue' + ')'; resolve( outColours ); } ); } ); -} +}; + +let callbackFun = () => {}; -let callbackFun = () => {} const subscribeToBeatUpdate = ( cb: () => void ) => { callbackFun = cb; micAudioHandler(); -} +}; const unsubscribeFromBeatUpdate = () => { - callbackFun = () => {} + callbackFun = () => {}; + try { clearInterval( micAnalyzer ); } catch ( e ) { /* empty */ } -} +}; const coolDown = () => { beatDetected = false; -} +}; let micAnalyzer = 0; let beatDetected = false; + const micAudioHandler = () => { const audioContext = new ( window.AudioContext || window.webkitAudioContext )(); const analyser = audioContext.createAnalyser(); + analyser.fftSize = 256; const bufferLength = analyser.frequencyBinCount; const dataArray = new Uint8Array( bufferLength ); + beatDetected = false; - navigator.mediaDevices.getUserMedia( { audio: true } ).then( ( stream ) => { + navigator.mediaDevices.getUserMedia( { + 'audio': true + } ).then( stream => { const mic = audioContext.createMediaStreamSource( stream ); + mic.connect( analyser ); analyser.getByteFrequencyData( dataArray ); let prevSpectrum: number[] = []; + const threshold = 10; // Adjust as needed + micAnalyzer = setInterval( () => { analyser.getByteFrequencyData( dataArray ); // Convert the frequency data to a numeric array @@ -115,25 +135,27 @@ const micAudioHandler = () => { callbackFun(); } } + prevSpectrum = currentSpectrum; }, 60 / 180 * 250 ); } ); -} +}; const calculateSpectralFlux = ( prevSpectrum: number[], currentSpectrum: number[] ) => { let flux = 0; for ( let i = 0; i < prevSpectrum.length; i++ ) { const diff = currentSpectrum[ i ] - prevSpectrum[ i ]; + flux += Math.max( 0, diff ); } return flux; -} +}; export default { createBackground, subscribeToBeatUpdate, unsubscribeFromBeatUpdate, coolDown, -} \ No newline at end of file +}; diff --git a/MusicPlayerV2-GUI/src/scripts/connection.ts b/MusicPlayerV2-GUI/src/scripts/connection.ts index b5610e3..91a9c15 100644 --- a/MusicPlayerV2-GUI/src/scripts/connection.ts +++ b/MusicPlayerV2-GUI/src/scripts/connection.ts @@ -1,30 +1,33 @@ -/* -* MusicPlayerV2 - notificationHandler.ts -* -* Created by Janis Hutz 06/26/2024, Licensed under the GPL V3 License -* https://janishutz.com, development@janishutz.com -* -* -*/ - // These functions handle connections to the backend with socket.io -import { io, type Socket } from "socket.io-client"; -import type { SSEMap } from "./song"; +import { + io, type Socket +} from 'socket.io-client'; +import type { + SSEMap +} from './song'; class SocketConnection { + socket: Socket; + roomName: string; + isConnected: boolean; + useSocket: boolean; + eventSource?: EventSource; + toBeListenedForItems: SSEMap; + reconnectRetryCount: number; + openConnectionsCount: number; constructor () { this.socket = io( localStorage.getItem( 'url' ) ?? '', { - autoConnect: false, + 'autoConnect': false, } ); this.roomName = location.pathname.split( '/' )[ 2 ]; this.isConnected = false; @@ -35,55 +38,71 @@ class SocketConnection { } /** - * Create a room token and connect to + * Create a room token and connect to * @returns {Promise} */ - connect (): Promise { + connect (): Promise { return new Promise( ( resolve, reject ) => { if ( this.reconnectRetryCount < 5 ) { if ( this.useSocket ) { this.socket.connect(); - this.socket.emit( 'join-room', this.roomName, ( res: { status: boolean, msg: string, data: any } ) => { - if ( res.status === true ) { - this.isConnected = true; - resolve( res.data ); - } else { - console.debug( res.msg ); - reject( 'ERR_ROOM_CONNECTING' ); + this.socket.emit( + 'join-room', this.roomName, ( res: { + 'status': boolean, + 'msg': string, + 'data': unknown + } ) => { + if ( res.status === true ) { + this.isConnected = true; + resolve( res.data ); + } else { + console.debug( res.msg ); + reject( 'ERR_ROOM_CONNECTING' ); + } } - } ); + ); } else { if ( this.openConnectionsCount < 1 && !this.isConnected ) { this.openConnectionsCount += 1; - fetch( localStorage.getItem( 'url' ) + '/socket/joinRoom?room=' + this.roomName, { credentials: 'include' } ).then( res => { + fetch( localStorage.getItem( 'url' ) + '/socket/joinRoom?room=' + this.roomName, { + 'credentials': 'include' + } ).then( res => { if ( res.status === 200 ) { - this.eventSource = new EventSource( localStorage.getItem( 'url' ) + '/socket/connection?room=' + this.roomName, { withCredentials: true } ); + this.eventSource + = new EventSource( localStorage.getItem( 'url' ) + + '/socket/connection?room=' + this.roomName, { + 'withCredentials': true + } ); this.eventSource.onopen = () => { this.isConnected = true; this.reconnectRetryCount = 0; - console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Connection successfully established!' ); - } + console.log( '[ SSE Connection ] - ' + + new Date().toISOString() + ': Connection successfully established!' ); + }; - this.eventSource.onmessage = ( e ) => { + this.eventSource.onmessage = e => { const d = JSON.parse( e.data ); + if ( this.toBeListenedForItems[ d.type ] ) { this.toBeListenedForItems[ d.type ]( d.data ); } else if ( d.type === 'basics' ) { resolve( d.data ); } - } + }; this.eventSource.onerror = () => { if ( this.isConnected ) { this.isConnected = false; this.openConnectionsCount -= 1; this.eventSource?.close(); - console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to connection error!' ); + console.log( '[ SSE Connection ] - ' + + new Date().toISOString() + + ': Reconnecting due to connection error!' ); // console.debug( e ); - + this.eventSource = undefined; - + this.reconnectRetryCount += 1; setTimeout( () => { this.connect(); @@ -91,13 +110,18 @@ class SocketConnection { } }; } else { - console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Could not connect due to error ' + res.status ); + console.log( '[ SSE Connection ] - ' + + new Date().toISOString() + + ': Could not connect due to error ' + res.status ); reject( 'ERR_ROOM_CONNECTING' ); } - } ).catch( () => { - console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Could not connect due to error.' ); - reject( 'ERR_ROOM_CONNECTING' ); - } ); + } ) + .catch( () => { + console.log( '[ SSE Connection ] - ' + + new Date().toISOString() + + ': Could not connect due to error.' ); + reject( 'ERR_ROOM_CONNECTING' ); + } ); } else { console.log( '[ SSE Connection ]: Trimmed connections' ); reject( 'ERR_TOO_MANY_CONNECTIONS' ); @@ -116,16 +140,23 @@ class SocketConnection { * @param {any} data * @returns {void} */ - emit ( event: string, data: any ): void { + emit ( event: string, data: unknown ): void { if ( this.isConnected ) { if ( this.useSocket ) { - this.socket.emit( event, { 'roomName': this.roomName, 'data': data } ); + this.socket.emit( event, { + 'roomName': this.roomName, + 'data': data + } ); } else { fetch( localStorage.getItem( 'url' ) + '/socket/update', { - method: 'post', - body: JSON.stringify( { 'event': event, 'roomName': this.roomName, 'data': data } ), - credentials: 'include', - headers: { + 'method': 'post', + 'body': JSON.stringify( { + 'event': event, + 'roomName': this.roomName, + 'data': data + } ), + 'credentials': 'include', + 'headers': { 'Content-Type': 'application/json', 'charset': 'utf-8' } @@ -140,11 +171,11 @@ class SocketConnection { * @param {( data: any ) => void} cb The callback function / listener function * @returns {void} */ - registerListener ( event: string, cb: ( data: any ) => void ): void { + registerListener ( event: string, cb: ( data: unknown ) => void ): void { if ( this.useSocket ) { if ( this.isConnected ) { this.socket.on( event, cb ); - } + } } else { this.toBeListenedForItems[ event ] = cb; } @@ -171,9 +202,11 @@ class SocketConnection { if ( this.eventSource ) { return this.eventSource!.OPEN && this.isConnected; } + return false; } } + } -export default SocketConnection; \ No newline at end of file +export default SocketConnection; diff --git a/MusicPlayerV2-GUI/src/scripts/music-player.ts b/MusicPlayerV2-GUI/src/scripts/music-player.ts index 4c0e091..065bed3 100644 --- a/MusicPlayerV2-GUI/src/scripts/music-player.ts +++ b/MusicPlayerV2-GUI/src/scripts/music-player.ts @@ -1,25 +1,39 @@ -import type { SearchResult, Song, SongMove } from "./song"; +import type { + SearchResult, Song, SongMove +} from './song'; interface Config { - devToken: string; - userToken: string; + 'devToken': string; + 'userToken': string; } type ControlAction = 'play' | 'pause' | 'next' | 'previous' | 'skip-10' | 'back-10'; type RepeatMode = 'off' | 'once' | 'all'; class MusicKitJSWrapper { + playingSongID: number; + playlist: Song[]; + queue: number[]; + config: Config; + musicKit: any; + isLoggedIn: boolean; + isPreparedToPlay: boolean; + repeatMode: RepeatMode; + isShuffleEnabled: boolean; + hasEncounteredAuthError: boolean; + queuePos: number; + audioPlayer: HTMLAudioElement; constructor () { @@ -27,8 +41,8 @@ class MusicKitJSWrapper { this.playlist = []; this.queue = []; this.config = { - devToken: '', - userToken: '', + 'devToken': '', + 'userToken': '', }; this.isShuffleEnabled = false; this.repeatMode = 'off'; @@ -58,16 +72,18 @@ class MusicKitJSWrapper { this.musicKit.authorize().then( () => { this.isLoggedIn = true; this.init(); - } ).catch( () => { - this.hasEncounteredAuthError = true; - } ); + } ) + .catch( () => { + this.hasEncounteredAuthError = true; + } ); } else { this.musicKit.authorize().then( () => { this.isLoggedIn = true; this.init(); - } ).catch( () => { - this.hasEncounteredAuthError = true; - } ); + } ) + .catch( () => { + this.hasEncounteredAuthError = true; + } ); } } @@ -76,25 +92,29 @@ class MusicKitJSWrapper { * @returns {void} */ init (): void { - fetch( localStorage.getItem( 'url' ) + '/getAppleMusicDevToken', { credentials: 'include' } ).then( res => { + fetch( localStorage.getItem( 'url' ) + '/getAppleMusicDevToken', { + 'credentials': 'include' + } ).then( res => { if ( res.status === 200 ) { res.text().then( token => { this.audioPlayer = document.getElementById( 'local-audio' ) as HTMLAudioElement; // MusicKit global is now defined MusicKit.configure( { - developerToken: token, - app: { - name: 'MusicPlayer', - build: '3' + 'developerToken': token, + 'app': { + 'name': 'MusicPlayer', + 'build': '3' }, - storefrontId: 'CH', + 'storefrontId': 'CH', } ).then( () => { this.config.devToken = token; this.musicKit = MusicKit.getInstance(); + if ( this.musicKit.isAuthorized ) { this.isLoggedIn = true; this.config.userToken = this.musicKit.musicUserToken; } + this.musicKit.shuffleMode = MusicKit.PlayerShuffleMode.off; } ); } ); @@ -107,7 +127,10 @@ class MusicKitJSWrapper { * @returns {boolean[]} Returns an array, where the first element indicates login status, the second one, if an error was encountered */ getAuth (): boolean[] { - return [ this.isLoggedIn, this.hasEncounteredAuthError ]; + return [ + this.isLoggedIn, + this.hasEncounteredAuthError + ]; } /** @@ -119,8 +142,8 @@ class MusicKitJSWrapper { apiGetRequest ( url: string, callback: ( data: object ) => void ): void { if ( this.config.devToken != '' && this.config.userToken != '' ) { fetch( url, { - method: 'GET', - headers: { + 'method': 'GET', + 'headers': { 'Authorization': `Bearer ${ this.config.devToken }`, 'Music-User-Token': this.config.userToken } @@ -128,13 +151,19 @@ class MusicKitJSWrapper { if ( res.status === 200 ) { res.json().then( json => { try { - callback( { 'status': 'ok', 'data': json } ); - } catch( err ) { /* empty */} + callback( { + 'status': 'ok', + 'data': json + } ); + } catch ( err ) { /* empty */ } } ); } else { try { - callback( { 'status': 'error', 'error': res.status } ); - } catch( err ) { /* empty */} + callback( { + 'status': 'error', + 'error': res.status + } ); + } catch ( err ) { /* empty */ } } } ); } else return; @@ -152,34 +181,41 @@ class MusicKitJSWrapper { setPlaylistByID ( id: string ): Promise { return new Promise( ( resolve, reject ) => { - this.musicKit.setQueue( { playlist: id } ).then( () => { + this.musicKit.setQueue( { + 'playlist': id + } ).then( () => { const pl = this.musicKit.queue.items; const songs: Song[] = []; + for ( const item in pl ) { let url = pl[ item ].attributes.artwork.url; + url = url.replace( '{w}', pl[ item ].attributes.artwork.width ); url = url.replace( '{h}', pl[ item ].attributes.artwork.height ); const song: Song = { - artist: pl[ item ].attributes.artistName, - cover: url, - duration: pl[ item ].attributes.durationInMillis / 1000, - id: pl[ item ].id, - origin: 'apple-music', - title: pl[ item ].attributes.name, - genres: pl[ item ].attributes.genreNames - } + 'artist': pl[ item ].attributes.artistName, + 'cover': url, + 'duration': pl[ item ].attributes.durationInMillis / 1000, + 'id': pl[ item ].id, + 'origin': 'apple-music', + 'title': pl[ item ].attributes.name, + 'genres': pl[ item ].attributes.genreNames + }; + songs.push( song ); } + this.playlist = songs; this.setShuffle( this.isShuffleEnabled ); this.queuePos = 0; this.playingSongID = this.queue[ 0 ]; this.prepare( this.playingSongID ); resolve(); - } ).catch( err => { - console.error( err ); - reject( err ); - } ); + } ) + .catch( err => { + console.error( err ); + reject( err ); + } ); } ); } @@ -192,20 +228,25 @@ class MusicKitJSWrapper { if ( this.playlist.length > 0 ) { this.playingSongID = playlistID; this.isPreparedToPlay = true; + for ( const el in this.queue ) { if ( this.queue[ el ] === playlistID ) { this.queuePos = parseInt( el ); break; } } + if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { - this.musicKit.setQueue( { 'song': this.playlist[ this.playingSongID ].id } ).then( () => { + this.musicKit.setQueue( { + 'song': this.playlist[ this.playingSongID ].id + } ).then( () => { setTimeout( () => { this.control( 'play' ); }, 500 ); - } ).catch( ( err ) => { - console.log( err ); - } ); + } ) + .catch( err => { + console.log( err ); + } ); } else { this.audioPlayer = document.getElementById( 'local-audio' ) as HTMLAudioElement; this.audioPlayer.src = this.playlist[ this.playingSongID ].id; @@ -213,6 +254,7 @@ class MusicKitJSWrapper { this.control( 'play' ); }, 500 ); } + return true; } else { return false; @@ -226,50 +268,63 @@ class MusicKitJSWrapper { */ control ( action: ControlAction ): boolean { switch ( action ) { - case "play": + case 'play': if ( this.isPreparedToPlay ) { this.control( 'pause' ); + if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { this.musicKit.play(); + return false; } else { this.audioPlayer.play(); + return false; } } else { return false; } - case "pause": + + case 'pause': if ( this.isPreparedToPlay ) { if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { this.musicKit.pause(); + return false; } else { this.audioPlayer.pause(); + return false; } } else { return false; } - case "back-10": + + case 'back-10': if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { this.musicKit.seekToTime( this.musicKit.currentPlaybackTime > 10 ? this.musicKit.currentPlaybackTime - 10 : 0 ); + return false; } else { this.audioPlayer.currentTime = this.audioPlayer.currentTime > 10 ? this.audioPlayer.currentTime - 10 : 0; + return false; } - case "skip-10": + + case 'skip-10': if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { if ( this.musicKit.currentPlaybackTime < ( this.playlist[ this.playingSongID ].duration - 10 ) ) { this.musicKit.seekToTime( this.musicKit.currentPlaybackTime + 10 ); + return false; } else { if ( this.repeatMode !== 'once' ) { this.control( 'next' ); + return true; } else { this.musicKit.seekToTime( 0 ); + return false; } } @@ -283,32 +338,42 @@ class MusicKitJSWrapper { this.audioPlayer.currentTime = 0; } } + return false; } - case "next": + + case 'next': this.control( 'pause' ); + if ( this.queuePos < this.queue.length - 1 ) { this.queuePos += 1; this.prepare( this.queue[ this.queuePos ] ); + return true; } else { this.queuePos = 0; + if ( this.repeatMode !== 'all' ) { this.control( 'pause' ); } else { this.playingSongID = this.queue[ this.queuePos ]; this.prepare( this.queue[ this.queuePos ] ); } + return true; } - case "previous": + + case 'previous': this.control( 'pause' ); + if ( this.queuePos > 0 ) { this.queuePos -= 1; this.prepare( this.queue[ this.queuePos ] ); + return true; } else { this.queuePos = this.queue.length - 1; + return true; } } @@ -317,15 +382,22 @@ class MusicKitJSWrapper { setShuffle ( enabled: boolean ) { this.isShuffleEnabled = enabled; this.queue = []; + if ( enabled ) { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const d = []; + for ( const el in this.playlist ) { d.push( parseInt( el ) ); } - this.queue = d.map( value => ( { value, sort: Math.random() } ) ) - .sort( ( a, b ) => a.sort - b.sort ) - .map( ( { value } ) => value ); + + this.queue = d.map( value => ( { + value, + 'sort': Math.random() + } ) ) + .sort( ( a, b ) => a.sort - b.sort ) + .map( ( { + value + } ) => value ); this.queue.splice( this.queue.indexOf( this.playingSongID ), 1 ); this.queue.push( this.playingSongID ); this.queue.reverse(); @@ -334,6 +406,7 @@ class MusicKitJSWrapper { this.queue.push( parseInt( song ) ); } } + // Find current song ID in queue for ( const el in this.queue ) { if ( this.queue[ el ] === this.playingSongID ) { @@ -359,29 +432,37 @@ class MusicKitJSWrapper { moveSong ( move: SongMove ) { const newQueue = []; const finishedQueue = []; + let songID = 0; + for ( const song in this.playlist ) { if ( this.playlist[ song ].id === move.songID ) { songID = parseInt( song ); break; } } + for ( const el in this.queue ) { if ( this.queue[ el ] !== songID ) { newQueue.push( this.queue[ el ] ); } } + let hasBeenAdded = false; + for ( const el in newQueue ) { if ( parseInt( el ) === move.newPos ) { finishedQueue.push( songID ); hasBeenAdded = true; } + finishedQueue.push( newQueue[ el ] ); } + if ( !hasBeenAdded ) { finishedQueue.push( songID ); } + this.queue = finishedQueue; } @@ -435,9 +516,11 @@ class MusicKitJSWrapper { */ getQueue (): Song[] { const data = []; + for ( const el in this.queue ) { data.push( this.playlist[ this.queue[ el ] ] ); } + return data; } @@ -449,13 +532,14 @@ class MusicKitJSWrapper { getUserPlaylists ( cb: ( data: object ) => void ): boolean { if ( this.isLoggedIn ) { this.apiGetRequest( 'https://api.music.apple.com/v1/me/library/playlists', cb ); + return true; } else { return false; } } - getPlaying ( ): boolean { + getPlaying ( ): boolean { if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { return this.musicKit.isPlaying; } else { @@ -466,18 +550,21 @@ class MusicKitJSWrapper { findSongOnAppleMusic ( searchTerm: string ): Promise { // TODO: Make storefront adjustable return new Promise( ( resolve, reject ) => { - const queryParameters = { - term: ( searchTerm ), - types: [ 'songs' ], + const queryParameters = { + 'term': searchTerm, + 'types': [ 'songs' ], }; - this.musicKit.api.music( `v1/catalog/ch/search`, queryParameters ).then( results => { + + this.musicKit.api.music( 'v1/catalog/ch/search', queryParameters ).then( results => { resolve( results ); - } ).catch( e => { - console.error( e ); - reject( e ); - } ); + } ) + .catch( e => { + console.error( e ); + reject( e ); + } ); } ); } + } -export default MusicKitJSWrapper; \ No newline at end of file +export default MusicKitJSWrapper; diff --git a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts index 67cb34b..3e9b5fb 100644 --- a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts +++ b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts @@ -1,34 +1,41 @@ -/* -* MusicPlayerV2 - notificationHandler.ts -* -* Created by Janis Hutz 06/26/2024, Licensed under the GPL V3 License -* https://janishutz.com, development@janishutz.com -* -* -*/ - // These functions handle connections to the backend with socket.io -import { io, type Socket } from "socket.io-client" -import type { SSEMap } from "./song"; +import { + io, type Socket +} from 'socket.io-client'; +import type { + SSEMap +} from './song'; class NotificationHandler { + socket: Socket; + roomName: string; + roomToken: string; + isConnected: boolean; + useSocket: boolean; + eventSource?: EventSource; + toBeListenedForItems: SSEMap; + reconnectRetryCount: number; + lastEmitTimestamp: number; + openConnectionsCount: number; + pendingRequestCount: number; + connectionWasSuccessful: boolean; constructor () { this.socket = io( localStorage.getItem( 'url' ) ?? '', { - autoConnect: false, + 'autoConnect': false, } ); this.roomName = ''; this.roomToken = ''; @@ -43,35 +50,44 @@ class NotificationHandler { } /** - * Create a room token and connect to - * @param {string} roomName + * Create a room token and connect to + * @param {string} roomName * @param {boolean} useAntiTamper * @returns {Promise} */ connect ( roomName: string, useAntiTamper: boolean ): Promise { return new Promise( ( resolve, reject ) => { - fetch( localStorage.getItem( 'url' ) + '/createRoomToken?roomName=' + roomName + '&useAntiTamper=' + useAntiTamper, { credentials: 'include' } ).then( res => { + fetch( localStorage.getItem( 'url' ) + '/createRoomToken?roomName=' + roomName + '&useAntiTamper=' + useAntiTamper, { + 'credentials': 'include' + } ).then( res => { if ( res.status === 200 ) { res.text().then( text => { this.roomToken = text; this.roomName = roomName; + if ( this.useSocket ) { this.socket.connect(); - this.socket.emit( 'create-room', { - name: this.roomName, - token: this.roomToken - }, ( res: { status: boolean, msg: string } ) => { - if ( res.status === true ) { - this.isConnected = true; - resolve(); - } else { - reject( 'ERR_ROOM_CONNECTING' ); + this.socket.emit( + 'create-room', { + 'name': this.roomName, + 'token': this.roomToken + }, ( res: { + 'status': boolean, + 'msg': string + } ) => { + if ( res.status === true ) { + this.isConnected = true; + resolve(); + } else { + reject( 'ERR_ROOM_CONNECTING' ); + } } - } ); + ); } else { this.sseConnect().then( () => { resolve(); - } ).catch( ); + } ) + .catch( ); } } ); } else if ( res.status === 409 ) { @@ -90,9 +106,13 @@ class NotificationHandler { if ( this.reconnectRetryCount < 5 ) { if ( this.openConnectionsCount < 1 && !this.isConnected ) { this.openConnectionsCount += 1; - fetch( localStorage.getItem( 'url' ) + '/socket/joinRoom?room=' + this.roomName, { credentials: 'include' } ).then( res => { + fetch( localStorage.getItem( 'url' ) + '/socket/joinRoom?room=' + this.roomName, { + 'credentials': 'include' + } ).then( res => { if ( res.status === 200 ) { - this.eventSource = new EventSource( localStorage.getItem( 'url' ) + '/socket/connection?room=' + this.roomName, { withCredentials: true } ); + this.eventSource = new EventSource( localStorage.getItem( 'url' ) + '/socket/connection?room=' + this.roomName, { + 'withCredentials': true + } ); this.eventSource.onopen = () => { this.isConnected = true; @@ -100,25 +120,26 @@ class NotificationHandler { this.reconnectRetryCount = 0; console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Connection successfully established!' ); resolve(); - } - - this.eventSource.onmessage = ( e ) => { + }; + + this.eventSource.onmessage = e => { const d = JSON.parse( e.data ); + if ( this.toBeListenedForItems[ d.type ] ) { this.toBeListenedForItems[ d.type ]( d.data ); } - } - - this.eventSource.onerror = ( e ) => { + }; + + this.eventSource.onerror = e => { if ( this.isConnected ) { this.isConnected = false; this.eventSource?.close(); this.openConnectionsCount -= 1; console.debug( e ); - console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to connection error!' ); - + console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to connection error!' ); + this.eventSource = undefined; - + this.reconnectRetryCount += 1; setTimeout( () => { this.sseConnect(); @@ -131,21 +152,22 @@ class NotificationHandler { } else { reject( 'ERR_ROOM_CONNECTING_STATUS_CODE' ); } - } ).catch( () => { - if ( !this.connectionWasSuccessful ) { - reject( 'ERR_ROOM_CONNECTING' ); - } else { - this.openConnectionsCount -= 1; - console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to severe connection error!' ); - - this.eventSource = undefined; - - this.reconnectRetryCount += 1; - setTimeout( () => { - this.sseConnect(); - }, 1000 * this.reconnectRetryCount ); - } - } ); + } ) + .catch( () => { + if ( !this.connectionWasSuccessful ) { + reject( 'ERR_ROOM_CONNECTING' ); + } else { + this.openConnectionsCount -= 1; + console.log( '[ SSE Connection ] - ' + new Date().toISOString() + ': Reconnecting due to severe connection error!' ); + + this.eventSource = undefined; + + this.reconnectRetryCount += 1; + setTimeout( () => { + this.sseConnect(); + }, 1000 * this.reconnectRetryCount ); + } + } ); } else { resolve(); } @@ -169,9 +191,14 @@ class NotificationHandler { emit ( event: string, data: any ): void { if ( this.isConnected ) { if ( this.useSocket ) { - this.socket.emit( event, { 'roomToken': this.roomToken, 'roomName': this.roomName, 'data': data } ); + this.socket.emit( event, { + 'roomToken': this.roomToken, + 'roomName': this.roomName, + 'data': data + } ); } else { const now = new Date().getTime(); + if ( this.lastEmitTimestamp < now - 250 ) { this.lastEmitTimestamp = now; this.sendEmitConventionally( event, data ); @@ -189,10 +216,15 @@ class NotificationHandler { sendEmitConventionally ( event: string, data: any ): void { fetch( localStorage.getItem( 'url' ) + '/socket/update', { - method: 'post', - body: JSON.stringify( { 'event': event, 'roomName': this.roomName, 'roomToken': this.roomToken, 'data': data } ), - credentials: 'include', - headers: { + 'method': 'post', + 'body': JSON.stringify( { + 'event': event, + 'roomName': this.roomName, + 'roomToken': this.roomToken, + 'data': data + } ), + 'credentials': 'include', + 'headers': { 'Content-Type': 'application/json', 'charset': 'utf-8' } @@ -209,7 +241,7 @@ class NotificationHandler { if ( this.useSocket ) { if ( this.isConnected ) { this.socket.on( event, cb ); - } + } } else { this.toBeListenedForItems[ event ] = cb; } @@ -222,22 +254,32 @@ class NotificationHandler { async disconnect (): Promise { if ( this.isConnected ) { if ( this.useSocket ) { - this.socket.emit( 'delete-room', { - name: this.roomName, - token: this.roomToken - }, ( res: { status: boolean, msg: string } ) => { - this.socket.disconnect(); - if ( !res.status ) { - alert( 'Unable to delete the room you were just in. The name will be blocked until the next server restart!' ); + this.socket.emit( + 'delete-room', { + 'name': this.roomName, + 'token': this.roomToken + }, ( res: { + 'status': boolean, + 'msg': string + } ) => { + this.socket.disconnect(); + + if ( !res.status ) { + alert( 'Unable to delete the room you were just in. The name will be blocked until the next server restart!' ); + } + + return; } - return; - } ); + ); } else { fetch( localStorage.getItem( 'url' ) + '/socket/deleteRoom', { - method: 'post', - body: JSON.stringify( { 'roomName': this.roomName, 'roomToken': this.roomToken } ), - credentials: 'include', - headers: { + 'method': 'post', + 'body': JSON.stringify( { + 'roomName': this.roomName, + 'roomToken': this.roomToken + } ), + 'credentials': 'include', + 'headers': { 'Content-Type': 'application/json', 'charset': 'utf-8' } @@ -247,10 +289,12 @@ class NotificationHandler { } else { alert( 'Unable to delete the room you were just in. The name will be blocked until the next server restart!' ); } + return; - } ).catch( () => { - return; - } ); + } ) + .catch( () => { + return; + } ); } } } @@ -258,6 +302,7 @@ class NotificationHandler { getRoomName (): string { return this.roomName; } + } export default NotificationHandler; diff --git a/MusicPlayerV2-GUI/src/scripts/song.d.ts b/MusicPlayerV2-GUI/src/scripts/song.d.ts index b879b06..4c98e32 100644 --- a/MusicPlayerV2-GUI/src/scripts/song.d.ts +++ b/MusicPlayerV2-GUI/src/scripts/song.d.ts @@ -4,92 +4,92 @@ export interface Song { /** * The ID. Either the apple music ID, or if from local disk, an ID starting in local_ */ - id: string; + 'id': string; /** * Origin of the song */ - origin: Origin; + 'origin': Origin; /** * The cover image as a URL */ - cover: string; + 'cover': string; /** * The artist of the song */ - artist: string; + 'artist': string; /** * The name of the song */ - title: string; + 'title': string; /** * Duration of the song in milliseconds */ - duration: number; + 'duration': number; /** * (OPTIONAL) The genres this song belongs to. Can be displayed on the showcase screen, but requires settings there */ - genres?: string[]; + 'genres'?: string[]; /** * (OPTIONAL) This will be displayed in brackets on the showcase screens */ - additionalInfo?: string; + 'additionalInfo'?: string; } export interface SongTransmitted { - title: string; - artist: string; - duration: number; - cover: string; - additionalInfo?: string; + 'title': string; + 'artist': string; + 'duration': number; + 'cover': string; + 'additionalInfo'?: string; } export interface ReadFile { - url: string; - filename: string; + 'url': string; + 'filename': string; } export interface SearchResult { - data: { - results: { - songs: { - data: AppleMusicSongData[], - href: string; + 'data': { + 'results': { + 'songs': { + 'data': AppleMusicSongData[], + 'href': string; } }; } } export interface AppleMusicSongData { - id: string, - type: string; - href: string; - attributes: { - albumName: string; - artistName: string; - artwork: { - width: number, - height: number, - url: string + 'id': string, + 'type': string; + 'href': string; + 'attributes': { + 'albumName': string; + 'artistName': string; + 'artwork': { + 'width': number, + 'height': number, + 'url': string }, - name: string; - genreNames: string[]; - durationInMillis: number; + 'name': string; + 'genreNames': string[]; + 'durationInMillis': number; } } export interface SongMove { - songID: string; - newPos: number; + 'songID': string; + 'newPos': number; } export interface SSEMap { [key: string]: ( data: any ) => void; -} \ No newline at end of file +} diff --git a/MusicPlayerV2-GUI/src/stores/userStore.ts b/MusicPlayerV2-GUI/src/stores/userStore.ts index 0954b73..836a7ee 100644 --- a/MusicPlayerV2-GUI/src/stores/userStore.ts +++ b/MusicPlayerV2-GUI/src/stores/userStore.ts @@ -1,23 +1,22 @@ -/* -* LanguageSchoolHossegorBookingSystem - userStore.js -* -* Created by Janis Hutz 10/27/2023, Licensed under a proprietary License -* https://janishutz.com, development@janishutz.com -* -* -*/ - -import { defineStore } from 'pinia'; +import { + defineStore +} from 'pinia'; // FOSS-VERSION: To enable the UI to be used with the FOSS version, change "isUserAuth" to true, you will be "logged in" export const useUserStore = defineStore( 'user', { - state: () => ( { 'isUserAuth': true, 'hasSubscribed': false, 'isUsingKeyboard': false, 'username': '', 'isFOSSVersion': false } ), - getters: { - getUserAuthenticated: ( state ) => state.isUserAuth, - getSubscriptionStatus: ( state ) => state.hasSubscribed, + 'state': () => ( { + 'isUserAuth': true, + 'hasSubscribed': false, + 'isUsingKeyboard': false, + 'username': '', + 'isFOSSVersion': false + } ), + 'getters': { + 'getUserAuthenticated': state => state.isUserAuth, + 'getSubscriptionStatus': state => state.hasSubscribed, }, - actions: { + 'actions': { setUserAuth ( auth: boolean ) { this.isUserAuth = auth; }, @@ -31,4 +30,4 @@ export const useUserStore = defineStore( 'user', { this.isUsingKeyboard = status; } } -} ); \ No newline at end of file +} ); diff --git a/MusicPlayerV2-GUI/src/views/404View.vue b/MusicPlayerV2-GUI/src/views/404View.vue index ba6bde7..8baf6a6 100644 --- a/MusicPlayerV2-GUI/src/views/404View.vue +++ b/MusicPlayerV2-GUI/src/views/404View.vue @@ -21,4 +21,4 @@ .logo { height: 50vh; } - \ No newline at end of file + diff --git a/MusicPlayerV2-GUI/src/views/AppView.vue b/MusicPlayerV2-GUI/src/views/AppView.vue index ee95830..9232da1 100644 --- a/MusicPlayerV2-GUI/src/views/AppView.vue +++ b/MusicPlayerV2-GUI/src/views/AppView.vue @@ -1,20 +1,40 @@ @@ -22,11 +42,17 @@ \ No newline at end of file + diff --git a/MusicPlayerV2-GUI/src/views/HomeView.vue b/MusicPlayerV2-GUI/src/views/HomeView.vue index 8b04216..979b566 100644 --- a/MusicPlayerV2-GUI/src/views/HomeView.vue +++ b/MusicPlayerV2-GUI/src/views/HomeView.vue @@ -1,54 +1,60 @@ \ No newline at end of file + diff --git a/MusicPlayerV2-GUI/src/views/showcase-backup.txt b/MusicPlayerV2-GUI/src/views/showcase-backup.txt deleted file mode 100644 index e949e1b..0000000 --- a/MusicPlayerV2-GUI/src/views/showcase-backup.txt +++ /dev/null @@ -1,416 +0,0 @@ - - - - \ No newline at end of file diff --git a/MusicPlayerV2-GUI/tsconfig.node.json b/MusicPlayerV2-GUI/tsconfig.node.json index f094063..88cadba 100644 --- a/MusicPlayerV2-GUI/tsconfig.node.json +++ b/MusicPlayerV2-GUI/tsconfig.node.json @@ -3,7 +3,6 @@ "include": [ "vite.config.*", "vitest.config.*", - "cypress.config.*", "nightwatch.conf.*", "playwright.config.*" ], @@ -14,6 +13,6 @@ "module": "ESNext", "moduleResolution": "Bundler", - "types": ["node"] + "types": ["node", "jquery"] } }