diff --git a/MusicPlayerV2-GUI/index.html b/MusicPlayerV2-GUI/index.html index c6d5ecc..4005727 100644 --- a/MusicPlayerV2-GUI/index.html +++ b/MusicPlayerV2-GUI/index.html @@ -7,6 +7,7 @@ + MusicPlayer diff --git a/MusicPlayerV2-GUI/package-lock.json b/MusicPlayerV2-GUI/package-lock.json index 17cf510..9900e3a 100644 --- a/MusicPlayerV2-GUI/package-lock.json +++ b/MusicPlayerV2-GUI/package-lock.json @@ -8,8 +8,15 @@ "name": "musicplayerv2-gui", "version": "0.0.0", "dependencies": { + "@esbuild-plugins/node-globals-polyfill": "^0.2.3", + "@esbuild-plugins/node-modules-polyfill": "^0.2.2", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@rollup/plugin-inject": "^5.0.5", + "buffer": "^6.0.3", + "music-metadata-browser": "^2.5.10", "musickit-typescript": "^1.2.4", "pinia": "^2.1.7", + "socket.io-client": "^4.7.5", "vue": "^3.4.15", "vue-router": "^4.2.5" }, @@ -40,6 +47,28 @@ "node": ">=6.0.0" } }, + "node_modules/@esbuild-plugins/node-globals-polyfill": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz", + "integrity": "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==", + "license": "ISC", + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/@esbuild-plugins/node-modules-polyfill": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz", + "integrity": "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==", + "license": "ISC", + "dependencies": { + "escape-string-regexp": "^4.0.0", + "rollup-plugin-node-polyfills": "^0.2.1" + }, + "peerDependencies": { + "esbuild": "*" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -47,7 +76,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -64,7 +92,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -81,7 +108,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -98,7 +124,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -115,7 +140,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -132,7 +156,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -149,7 +172,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -166,7 +188,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -183,7 +204,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -200,7 +220,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -217,7 +236,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -234,7 +252,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -251,7 +268,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -268,7 +284,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -285,7 +300,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -302,7 +316,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -319,7 +332,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -336,7 +348,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -353,7 +364,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -370,7 +380,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -387,7 +396,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -404,7 +412,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -421,7 +428,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -619,6 +625,50 @@ "node": ">= 8" } }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.18.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", @@ -850,6 +900,18 @@ "dev": true, "license": "MIT" }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, "node_modules/@tsconfig/node20": { "version": "20.1.4", "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.4.tgz", @@ -861,7 +923,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, "license": "MIT" }, "node_modules/@types/json-schema": { @@ -1301,6 +1362,18 @@ "dev": true, "license": "MIT" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -1391,6 +1464,26 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -1421,6 +1514,30 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1482,6 +1599,15 @@ "dev": true, "license": "MIT" }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1527,7 +1653,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" @@ -1574,6 +1699,28 @@ "node": ">=6.0.0" } }, + "node_modules/engine.io-client": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -1590,7 +1737,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -1629,7 +1775,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1841,6 +1986,24 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1915,6 +2078,23 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2112,6 +2292,26 @@ "he": "bin/he" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2165,7 +2365,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/is-extglob": { @@ -2325,6 +2524,15 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/memorystream": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", @@ -2378,7 +2586,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, "license": "MIT" }, "node_modules/muggle-string": { @@ -2388,6 +2595,45 @@ "dev": true, "license": "MIT" }, + "node_modules/music-metadata": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-7.14.0.tgz", + "integrity": "sha512-xrm3w7SV0Wk+OythZcSbaI8mcr/KHd0knJieu8bVpaPfMv/Agz5EooCAPz3OR5hbYMiUG6dgAPKZKnMzV+3amA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "content-type": "^1.0.5", + "debug": "^4.3.4", + "file-type": "^16.5.4", + "media-typer": "^1.1.0", + "strtok3": "^6.3.0", + "token-types": "^4.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/music-metadata-browser": { + "version": "2.5.10", + "resolved": "https://registry.npmjs.org/music-metadata-browser/-/music-metadata-browser-2.5.10.tgz", + "integrity": "sha512-03UnAmsSJoZZ5kK2BnEnd2zpH8LXRWQ6xlc7akKudhc2d9FT+yAiqapnmOzjW3g4cxxvIsSK5MVBO2Gi+Ymjfw==", + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "debug": "^4.3.4", + "music-metadata": "^7.13.3", + "readable-stream": "^4.3.0", + "readable-web-to-node-stream": "^3.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/musickit-typescript": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/musickit-typescript/-/musickit-typescript-1.2.4.tgz", @@ -2601,6 +2847,19 @@ "node": ">=8" } }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/picocolors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", @@ -2611,7 +2870,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -2737,6 +2995,15 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2782,6 +3049,52 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readable-web-to-node-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2824,7 +3137,7 @@ "version": "4.18.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.5" @@ -2856,6 +3169,57 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-inject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", + "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1", + "magic-string": "^0.25.3", + "rollup-pluginutils": "^2.8.1" + } + }, + "node_modules/rollup-plugin-inject/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "license": "MIT" + }, + "node_modules/rollup-plugin-inject/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/rollup-plugin-node-polyfills": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", + "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", + "license": "MIT", + "dependencies": { + "rollup-plugin-inject": "^3.0.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2880,6 +3244,26 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/semver": { "version": "7.6.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", @@ -2936,6 +3320,34 @@ "node": ">=8" } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -2945,6 +3357,22 @@ "node": ">=0.10.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2971,6 +3399,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -3004,6 +3449,23 @@ "node": ">=8.0" } }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -3078,7 +3540,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/vite": { @@ -3260,6 +3721,27 @@ "dev": true, "license": "ISC" }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", @@ -3270,6 +3752,14 @@ "node": ">=12" } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/MusicPlayerV2-GUI/package.json b/MusicPlayerV2-GUI/package.json index 8a62814..c230f0b 100644 --- a/MusicPlayerV2-GUI/package.json +++ b/MusicPlayerV2-GUI/package.json @@ -12,8 +12,15 @@ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" }, "dependencies": { + "@esbuild-plugins/node-globals-polyfill": "^0.2.3", + "@esbuild-plugins/node-modules-polyfill": "^0.2.2", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@rollup/plugin-inject": "^5.0.5", + "buffer": "^6.0.3", + "music-metadata-browser": "^2.5.10", "musickit-typescript": "^1.2.4", "pinia": "^2.1.7", + "socket.io-client": "^4.7.5", "vue": "^3.4.15", "vue-router": "^4.2.5" }, diff --git a/MusicPlayerV2-GUI/src/components/libraryView.vue b/MusicPlayerV2-GUI/src/components/libraryView.vue index d24678e..5c3018a 100644 --- a/MusicPlayerV2-GUI/src/components/libraryView.vue +++ b/MusicPlayerV2-GUI/src/components/libraryView.vue @@ -1,24 +1,35 @@ \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/components/notificationsModule.vue b/MusicPlayerV2-GUI/src/components/notificationsModule.vue new file mode 100644 index 0000000..fe336be --- /dev/null +++ b/MusicPlayerV2-GUI/src/components/notificationsModule.vue @@ -0,0 +1,383 @@ + + + + + + \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/components/playerView.vue b/MusicPlayerV2-GUI/src/components/playerView.vue index 2b74006..b495a1f 100644 --- a/MusicPlayerV2-GUI/src/components/playerView.vue +++ b/MusicPlayerV2-GUI/src/components/playerView.vue @@ -1,40 +1,54 @@ @@ -46,7 +60,9 @@ import playlistView from '@/components/playlistView.vue'; import MusicKitJSWrapper from '@/scripts/music-player'; import sliderView from './sliderView.vue'; - import type { Song } from '@/scripts/song'; + import type { ReadFile, Song, SongMove } from '@/scripts/song'; + import { parseBlob } from 'music-metadata-browser'; + import notificationsModule from './notificationsModule.vue'; const isPlaying = ref( false ); const repeatMode = ref( '' ); @@ -65,6 +81,7 @@ const currentlyPlayingSongArtist = ref( '' ); const pos = ref( 0 ); const duration = ref( 0 ); + const notifications = ref( notificationsModule ); const emits = defineEmits( [ 'playerStateChange' ] ); @@ -79,6 +96,10 @@ } } + const toggleRemaining = () => { + isShowingRemainingTime.value = !isShowingRemainingTime.value; + } + const control = ( action: string ) => { if ( action === 'pause' ) { isPlaying.value = false; @@ -103,9 +124,11 @@ if ( shuffleMode.value === '' ) { shuffleMode.value = '_on'; player.setShuffle( true ); + getDetails(); } else { shuffleMode.value = ''; player.setShuffle( false ); + getDetails(); } getDetails(); } else if ( action === 'forward' ) { @@ -177,13 +200,71 @@ } ); } + const selectCustomPlaylist = async ( pl: ReadFile[] ) => { + let n = notifications.value.createNotification( 'Analyzing playlist', 200, 'progress', 'normal' ); + playlist.value = []; + let plLoad: Song[] = []; + for ( let element in pl ) { + try { + plLoad.push( await fetchSongData( pl[ element ] ) ); + } catch ( e ) { + console.error( e ); + } + notifications.value.updateNotification( n, `Analyzing playlist (${element}/${pl.length})` ); + } + playlist.value = plLoad; + player.setPlaylist( playlist.value ); + player.prepare( 0 ); + isPlaying.value = true; + setTimeout( () => { + startProgressTracker(); + getDetails(); + }, 2000 ); + notifications.value.cancelNotification( n ); + notifications.value.createNotification( 'Playlist loaded', 10, 'ok', 'normal' ); + } + + const fetchSongData = ( songDetails: ReadFile ): Promise => { + return new Promise( ( resolve, reject ) => { + fetch( songDetails.url ).then( res => { + if ( res.status === 200 ) { + res.blob().then( blob => { + parseBlob( blob ).then( data => { + player.findSongOnAppleMusic( data.common.title ?? songDetails.filename.split( '.' )[ 0 ] ).then( d => { + let url = d.data.results.songs.data[ 0 ].attributes.artwork.url; + url = url.replace( '{w}', String( d.data.results.songs.data[ 0 ].attributes.artwork.width ) ); + url = url.replace( '{h}', String( d.data.results.songs.data[ 0 ].attributes.artwork.height ) ); + const song: Song = { + artist: data.common.artist ?? d.data.results.songs.data[ 0 ].attributes.artistName, + title: data.common.title ?? d.data.results.songs.data[ 0 ].attributes.name, + duration: data.format.duration ?? ( d.data.results.songs.data[ 0 ].attributes.durationInMillis / 1000 ), + id: songDetails.url, + origin: 'disk', + cover: url + } + resolve( song ); + } ).catch( e => { + reject( e ); + } ); + } ).catch( e => { + reject( e ); + } ); + } ).catch( e => { + reject( e ); + } ); + } + } ).catch( e => { + reject( e ); + } ); + } ); + } + const getDetails = () => { const details = player.getPlayingSong(); currentlyPlayingSongName.value = details.title; coverArt.value = details.cover; - currentlyPlayingSongIndex.value = player.getPlayingSongID(); + currentlyPlayingSongIndex.value = player.getQueueID(); playlist.value = player.getQueue(); - console.log( playlist.value ); currentlyPlayingSongArtist.value = details.artist; } @@ -207,7 +288,10 @@ let progressTracker = 0; + let hasReachedEnd = false; + let hasStarted = false; const startProgressTracker = () => { + hasReachedEnd = false; isPlaying.value = true; const playingSong = player.getPlayingSong(); duration.value = playingSong.duration; @@ -224,9 +308,18 @@ } progressTracker = setInterval( () => { pos.value = player.getPlaybackPos(); - if ( pos.value > playingSong.duration - 1 ) { - // TODO: repeat - control( 'next' ); + if ( pos.value > playingSong.duration - 1 && !hasReachedEnd ) { + stopProgressTracker(); + hasReachedEnd = true; + if ( repeatMode.value === '_one_on' ) { + player.goToPos( 0 ); + } else { + control( 'next' ); + } + } + + if ( pos.value > 0 && !hasStarted ) { + hasStarted = true; } const minuteCount = Math.floor( pos.value / 60 ); @@ -264,6 +357,49 @@ isPlaying.value = false; } + const moveSong = ( move: SongMove ) => { + player.moveSong( move ); + getDetails(); + } + + const addNewSongs = async ( songs: ReadFile[] ) => { + let n = notifications.value.createNotification( 'Analyzing new songs', 200, 'progress', 'normal' ); + playlist.value = player.getPlaylist(); + for ( let element in songs ) { + try { + playlist.value.push( await fetchSongData( songs[ element ] ) ); + } catch ( e ) { + console.error( e ); + } + notifications.value.updateNotification( n, `Analyzing new songs (${element}/${songs.length})` ); + } + player.setPlaylist( playlist.value ); + player.prepare( 0 ); + isPlaying.value = true; + setTimeout( () => { + startProgressTracker(); + getDetails(); + }, 2000 ); + notifications.value.cancelNotification( n ); + notifications.value.createNotification( 'New songs added', 10, 'ok', 'normal' ); + } + + emits( 'playerStateChange', isShowingFullScreenPlayer.value ? 'show' : 'hide' ); + + document.addEventListener( 'keydown', ( e ) => { + if ( e.key === ' ' ) { + // TODO: fix + e.preventDefault(); + playPause(); + } else if ( e.key === 'ArrowRight' ) { + e.preventDefault(); + control( 'next' ); + } else if ( e.key === 'ArrowLeft' ) { + e.preventDefault(); + control( 'previous' ); + } + } ); + defineExpose( { logIntoAppleMusic, getPlaylists, @@ -271,12 +407,12 @@ getAuth, skipLogin, selectPlaylist, + selectCustomPlaylist, } ); \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/components/playlistView.vue b/MusicPlayerV2-GUI/src/components/playlistView.vue index c67ec06..0cca5fd 100644 --- a/MusicPlayerV2-GUI/src/components/playlistView.vue +++ b/MusicPlayerV2-GUI/src/components/playlistView.vue @@ -1,7 +1,12 @@ \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/components/playlistsView.vue b/MusicPlayerV2-GUI/src/components/playlistsView.vue index 4d1da10..c17635a 100644 --- a/MusicPlayerV2-GUI/src/components/playlistsView.vue +++ b/MusicPlayerV2-GUI/src/components/playlistsView.vue @@ -1,10 +1,16 @@ + + \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/scripts/music-player.ts b/MusicPlayerV2-GUI/src/scripts/music-player.ts index 0ad2dd7..20f743b 100644 --- a/MusicPlayerV2-GUI/src/scripts/music-player.ts +++ b/MusicPlayerV2-GUI/src/scripts/music-player.ts @@ -1,4 +1,4 @@ -import type { Song } from "./song"; +import type { SearchResult, Song, SongMove } from "./song"; interface Config { devToken: string; @@ -20,6 +20,7 @@ class MusicKitJSWrapper { isShuffleEnabled: boolean; hasEncounteredAuthError: boolean; queuePos: number; + audioPlayer: HTMLAudioElement; constructor () { this.playingSongID = 0; @@ -35,6 +36,7 @@ class MusicKitJSWrapper { this.isLoggedIn = false; this.hasEncounteredAuthError = false; this.queuePos = 0; + this.audioPlayer = document.getElementById( 'local-audio' ) as HTMLAudioElement; const self = this; @@ -77,6 +79,7 @@ class MusicKitJSWrapper { 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, @@ -204,7 +207,11 @@ class MusicKitJSWrapper { console.log( err ); } ); } else { - // TODO: Implement + this.audioPlayer = document.getElementById( 'local-audio' ) as HTMLAudioElement; + this.audioPlayer.src = this.playlist[ this.playingSongID ].id; + setTimeout( () => { + this.control( 'play' ); + }, 500 ); } return true; } else { @@ -225,8 +232,8 @@ class MusicKitJSWrapper { this.musicKit.play(); return false; } else { + this.audioPlayer.play(); return false; - // TODO: Implement } } else { return false; @@ -237,8 +244,8 @@ class MusicKitJSWrapper { this.musicKit.pause(); return false; } else { + this.audioPlayer.pause(); return false; - // TODO: Implement } } else { return false; @@ -248,8 +255,8 @@ class MusicKitJSWrapper { 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; - // TODO: Implement } case "skip-10": if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { @@ -266,30 +273,30 @@ class MusicKitJSWrapper { } } } else { - // TODO: Finish - // if ( this.audioPlayer.currentTime < ( this.playlist[ this.playingSongID ].duration - 10 ) ) { - // this.audioPlayer.currentTime = this.audioPlayer.currentTime + 10; - // this.pos = this.audioPlayer.currentTime; - // this.sendUpdate( 'pos' ); - // } else { - // if ( this.repeatMode !== 'one' ) { - // this.control( 'next' ); - // } else { - // this.audioPlayer.currentTime = 0; - // this.pos = this.audioPlayer.currentTime; - // this.sendUpdate( 'pos' ); - // } - // } + if ( this.audioPlayer.currentTime < ( this.playlist[ this.playingSongID ].duration - 10 ) ) { + this.audioPlayer.currentTime = this.audioPlayer.currentTime + 10; + } else { + if ( this.repeatMode !== 'once' ) { + this.control( 'next' ); + } else { + this.audioPlayer.currentTime = 0; + } + } return false; } case "next": - if ( this.queuePos < this.queue.length ) { + if ( this.queuePos < this.queue.length - 1 ) { this.queuePos += 1; this.prepare( this.queue[ this.queuePos ] ); return true; } else { this.queuePos = 0; - this.control( 'pause' ); + if ( this.repeatMode !== 'all' ) { + this.control( 'pause' ); + } else { + this.playingSongID = this.queue[ this.queuePos ]; + this.prepare( this.queue[ this.queuePos ] ); + } return true; } case "previous": @@ -324,6 +331,13 @@ 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 ) { + this.queuePos = parseInt( el ); + break; + } + } } setRepeatMode ( mode: RepeatMode ) { @@ -334,10 +348,40 @@ class MusicKitJSWrapper { if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { this.musicKit.seekToTime( pos ); } else { - // TODO: Implement + this.audioPlayer.currentTime = pos; } } + + 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; + } + /** * Get the current position of the play heed. Will return in ms since start of the song * @returns {number} @@ -346,8 +390,7 @@ class MusicKitJSWrapper { if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { return this.musicKit.currentPlaybackTime; } else { - return 0; - // TODO: Implement + return this.audioPlayer.currentTime; } } @@ -367,6 +410,14 @@ class MusicKitJSWrapper { return this.playingSongID; } + /** + * Get the queue index of the currently playing song + * @returns {number} + */ + getQueueID (): number { + return this.queuePos; + } + /** * Get the full playlist, as it is set currently, not ordered by queue settings, but as passed in originally * @returns {Song[]} @@ -405,14 +456,25 @@ class MusicKitJSWrapper { if ( this.playlist[ this.playingSongID ].origin === 'apple-music' ) { return this.musicKit.isPlaying; } else { - // TODO: Implement - return false; + return !this.audioPlayer.paused; } } - // findSongOnAppleMusic ( searchTerm: string ): Song => { - // TODO: Implement - // } + findSongOnAppleMusic ( searchTerm: string ): Promise { + // TODO: Make storefront adjustable + return new Promise( ( resolve, reject ) => { + const queryParameters = { + term: ( searchTerm ), + types: [ 'songs' ], + }; + this.musicKit.api.music( `v1/catalog/ch/search`, queryParameters ).then( results => { + resolve( results ); + } ).catch( e => { + console.error( e ); + reject( e ); + } ); + } ); + } } export default MusicKitJSWrapper; \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts index fbf2a57..5b45f1c 100644 --- a/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts +++ b/MusicPlayerV2-GUI/src/scripts/notificationHandler.ts @@ -1,7 +1,44 @@ -const subscribe = ( handler: ( data: any ) => {} ): string => { - return ''; +/* +* 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" + +class NotificationHandler { + socket: Socket; + + constructor () { + this.socket = io(); + } + + /** + * Description + * @param {string} event The event to emit + * @param {any} data + * @returns {void} + */ + emit ( event: string, data: any ): void { + this.socket.emit( event, data ); + } + + registerListener ( event: string, cb: ( data: any ) => void ): void { + this.socket.on( event, cb ); + } + + disconnect (): void { + this.socket.disconnect(); + } + + joinRoom ( roomName: string ): void { + // this.socket. + } } -const unsubscribe = ( id: string ) => { - -} \ No newline at end of file +export default NotificationHandler; \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/scripts/song.d.ts b/MusicPlayerV2-GUI/src/scripts/song.d.ts index 4ddca1a..9fde4b5 100644 --- a/MusicPlayerV2-GUI/src/scripts/song.d.ts +++ b/MusicPlayerV2-GUI/src/scripts/song.d.ts @@ -40,4 +40,44 @@ export interface Song { * (OPTIONAL) This will be displayed in brackets on the showcase screens */ additionalInfo?: string; +} + + +export interface ReadFile { + url: string; + filename: string; +} + +export interface SearchResult { + 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 + }, + name: string; + genreNames: string[]; + durationInMillis: number; + } +} + +export interface SongMove { + songID: string; + newPos: number; } \ No newline at end of file diff --git a/MusicPlayerV2-GUI/src/views/AppView.vue b/MusicPlayerV2-GUI/src/views/AppView.vue index 84493d9..a1d6a15 100644 --- a/MusicPlayerV2-GUI/src/views/AppView.vue +++ b/MusicPlayerV2-GUI/src/views/AppView.vue @@ -1,14 +1,15 @@ @@ -17,8 +18,10 @@ import playerView from '@/components/playerView.vue'; import libraryView from '@/components/libraryView.vue'; import { ref } from 'vue'; +import type { ReadFile } from '@/scripts/song'; const isLoggedIntoAppleMusic = ref( false ); + const isReady = ref( false ); const isShowingFullScreenPlayer = ref( false ); const player = ref( playerView ); const playlists = ref( [] ); @@ -37,6 +40,7 @@ loginChecker = setInterval( () => { if ( player.value.getAuth()[ 0 ] ) { isLoggedIntoAppleMusic.value = true; + isReady.value = true; player.value.getPlaylists( ( data ) => { playlists.value = data.data.data; } ); @@ -49,13 +53,18 @@ } const skipLogin = () => { - isLoggedIntoAppleMusic.value = true; + isReady.value = true; + isLoggedIntoAppleMusic.value = false; player.value.skipLogin(); } const selectPlaylist = ( id: string ) => { player.value.selectPlaylist( id ); } + + const selectCustomPlaylist = ( playlist: ReadFile[] ) => { + player.value.selectCustomPlaylist( playlist ); + } \ No newline at end of file diff --git a/MusicPlayerV2-GUI/vite.config.ts b/MusicPlayerV2-GUI/vite.config.ts index 83badb7..28acfe6 100644 --- a/MusicPlayerV2-GUI/vite.config.ts +++ b/MusicPlayerV2-GUI/vite.config.ts @@ -2,6 +2,7 @@ import { fileURLToPath, URL } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' +import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill' // https://vitejs.dev/config/ export default defineConfig({ @@ -13,6 +14,18 @@ export default defineConfig({ '@': fileURLToPath(new URL('./src', import.meta.url)) } }, + optimizeDeps: { + esbuildOptions: { + define: { + global: 'globalThis', + }, + plugins: [ + NodeGlobalsPolyfillPlugin({ + buffer: true, + }), + ], + }, + }, server: { port: 8080 } diff --git a/backend/config/config b/backend/config/config deleted file mode 120000 index 4088526..0000000 --- a/backend/config/config +++ /dev/null @@ -1 +0,0 @@ -../config/ \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index eddb572..47e417a 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -18,7 +18,8 @@ "express": "^4.19.2", "jsonwebtoken": "^9.0.2", "node-mysql": "^0.4.2", - "oauth-janishutz-client-server": "file:../../oauth/client/server/dist" + "oauth-janishutz-client-server": "file:../../oauth/client/server/dist", + "socket.io": "^4.7.5" }, "devDependencies": { "typescript": "^5.4.5" @@ -40,6 +41,12 @@ "typescript": "^5.3.3" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -59,6 +66,12 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "license": "MIT" + }, "node_modules/@types/cors": { "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", @@ -174,6 +187,15 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/better-js-class": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/better-js-class/-/better-js-class-0.1.3.tgz", @@ -375,6 +397,68 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -1073,6 +1157,116 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, "node_modules/sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -1186,6 +1380,27 @@ "engines": { "node": ">= 0.8" } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/backend/package.json b/backend/package.json index 8849a82..27e8d85 100644 --- a/backend/package.json +++ b/backend/package.json @@ -29,6 +29,7 @@ "express": "^4.19.2", "jsonwebtoken": "^9.0.2", "node-mysql": "^0.4.2", - "oauth-janishutz-client-server": "file:../../oauth/client/server/dist" + "oauth-janishutz-client-server": "file:../../oauth/client/server/dist", + "socket.io": "^4.7.5" } } diff --git a/backend/src/storage/db.ts b/backend/src/storage/db.ts index d43d824..aecfa05 100644 --- a/backend/src/storage/db.ts +++ b/backend/src/storage/db.ts @@ -19,8 +19,8 @@ if ( typeof( __dirname ) === 'undefined' ) { } const dbRef = { - 'user': 'jh_store_users', - 'users': 'jh_store_users', + 'user': 'music_users', + 'users': 'music_users', }; diff --git a/backend/src/storage/mysqldb.ts b/backend/src/storage/mysqldb.ts index 32ae4e7..ba46fe0 100644 --- a/backend/src/storage/mysqldb.ts +++ b/backend/src/storage/mysqldb.ts @@ -60,7 +60,6 @@ class SQLDB { console.log( '[ SQL ] Connected to database successfully' ); self.sqlConnection.on( 'error', ( err ) => { if ( err.code === 'ECONNRESET' ) { - console.error( '[ SQL ] Reconnecting to database, because connection was reset!' ); self.isRecovering = true; setTimeout( () => { self.disconnect(); @@ -84,7 +83,7 @@ class SQLDB { if ( error ) throw error; if ( results[ 0 ][ '@@default_storage_engine' ] !== 'InnoDB' ) throw 'DB HAS TO USE InnoDB!'; } ); - this.sqlConnection.query( 'CREATE TABLE jh_store_users ( account_id INT ( 10 ) NOT NULL AUTO_INCREMENT, email TINYTEXT NOT NULL, data VARCHAR( 55000 ), uid TINYTEXT, lang TINYTEXT, username TINYTEXT, stripe_user_id TINYTEXT, settings VARCHAR( 5000 ), PRIMARY KEY ( account_id ) ) ENGINE=INNODB;', ( error ) => { + this.sqlConnection.query( 'CREATE TABLE music_users ( account_id INT ( 10 ) NOT NULL AUTO_INCREMENT, email TINYTEXT NOT NULL, uid TINYTEXT, lang TINYTEXT, username TINYTEXT, settings VARCHAR( 5000 ), PRIMARY KEY ( account_id ) ) ENGINE=INNODB;', ( error ) => { if ( error ) if ( error.code !== 'ER_TABLE_EXISTS_ERROR' ) throw error; return 'DONE'; } );