diff --git a/.gitignore b/.gitignore index de7f813..a80a560 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ # ignore node_modules -node_modules \ No newline at end of file +node_modules +*.secret.json +apple_private_key.p8 \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0c636f9..5632d6b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -13,6 +13,8 @@ "cors": "^2.8.5", "express-session": "^1.17.3", "music-metadata": "^7.13.0", + "node-fetch": "^2.7.0", + "node-musickit-api": "^2.1.1", "realtime-bpm-analyzer": "^3.2.1", "vue": "^3.2.13", "vue-router": "^4.0.3", @@ -3927,6 +3929,14 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, + "node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/babel-loader": { "version": "8.3.0", "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.3.0.tgz", @@ -4328,6 +4338,11 @@ "node": ">=0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -6193,6 +6208,14 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", @@ -7186,7 +7209,6 @@ "version": "1.15.2", "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz", "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true, "engines": { "node": ">=4.0" }, @@ -8673,6 +8695,35 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/jsprim": { "version": "1.4.2", "resolved": "https://registry.npmmirror.com/jsprim/-/jsprim-1.4.2.tgz", @@ -8759,6 +8810,25 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/keyv/-/keyv-3.1.0.tgz", @@ -8912,6 +8982,36 @@ "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", @@ -8936,6 +9036,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -9630,10 +9735,9 @@ "optional": true }, "node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "dev": true, + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -9658,6 +9762,18 @@ "node": ">= 6.13.0" } }, + "node_modules/node-musickit-api": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-musickit-api/-/node-musickit-api-2.1.1.tgz", + "integrity": "sha512-6dl6KbvSSXZyabxvFEYhW3yQbrqM0pn/8b0BNk2wHJfjIxWkgjMl+Uh0q/NkxdWIq/pY1jTsGuVEfraRDv5nQQ==", + "dependencies": { + "axios": "^0.21.4", + "jsonwebtoken": "^8.5.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/node-releases": { "version": "2.0.10", "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.10.tgz", @@ -12655,9 +12771,8 @@ }, "node_modules/tr46": { "version": "0.0.3", - "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", @@ -13887,9 +14002,8 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { "version": "5.83.1", @@ -14302,9 +14416,8 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -17569,6 +17682,14 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", "dev": true }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + }, "babel-loader": { "version": "8.3.0", "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.3.0.tgz", @@ -17908,6 +18029,11 @@ "integrity": "sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==", "dev": true }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -19401,6 +19527,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", @@ -20227,8 +20361,7 @@ "follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", - "dev": true + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" }, "forever-agent": { "version": "0.6.1", @@ -21391,6 +21524,30 @@ "universalify": "^2.0.0" } }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + } + } + }, "jsprim": { "version": "1.4.2", "resolved": "https://registry.npmmirror.com/jsprim/-/jsprim-1.4.2.tgz", @@ -21472,6 +21629,25 @@ } } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/keyv/-/keyv-3.1.0.tgz", @@ -21603,6 +21779,36 @@ "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.kebabcase": { "version": "4.1.1", "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", @@ -21627,6 +21833,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -22181,10 +22392,9 @@ "optional": true }, "node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "dev": true, + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "requires": { "whatwg-url": "^5.0.0" } @@ -22195,6 +22405,15 @@ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true }, + "node-musickit-api": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-musickit-api/-/node-musickit-api-2.1.1.tgz", + "integrity": "sha512-6dl6KbvSSXZyabxvFEYhW3yQbrqM0pn/8b0BNk2wHJfjIxWkgjMl+Uh0q/NkxdWIq/pY1jTsGuVEfraRDv5nQQ==", + "requires": { + "axios": "^0.21.4", + "jsonwebtoken": "^8.5.1" + } + }, "node-releases": { "version": "2.0.10", "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.10.tgz", @@ -24536,9 +24755,8 @@ }, "tr46": { "version": "0.0.3", - "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "truncate-utf8-bytes": { "version": "1.0.2", @@ -25549,9 +25767,8 @@ }, "webidl-conversions": { "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { "version": "5.83.1", @@ -25870,9 +26087,8 @@ }, "whatwg-url": { "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/frontend/package.json b/frontend/package.json index cb5af51..730ec48 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,6 +16,8 @@ "cors": "^2.8.5", "express-session": "^1.17.3", "music-metadata": "^7.13.0", + "node-fetch": "^2.7.0", + "node-musickit-api": "^2.1.1", "realtime-bpm-analyzer": "^3.2.1", "vue": "^3.2.13", "vue-router": "^4.0.3", diff --git a/frontend/src/app.js b/frontend/src/app.js index 36e8d4e..892a8b5 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -4,10 +4,10 @@ const path = require( 'path' ); const cors = require( 'cors' ); const fs = require( 'fs' ); const bodyParser = require( 'body-parser' ); -const musicMetadata = require( 'music-metadata' ); const dialog = require( 'electron' ).dialog; const session = require( 'express-session' ); + app.use( bodyParser.urlencoded( { extended: false } ) ); app.use( bodyParser.json() ); app.use( cors() ); @@ -17,9 +17,6 @@ app.use( session( { resave: false, } ) ); -let indexedData = {}; -let coverArtIndex = {}; -const allowedFileTypes = [ '.mp3', '.wav', '.flac' ]; let connectedClients = {}; let changedStatus = []; @@ -123,59 +120,11 @@ app.get( '/openSongs', ( req, res ) => { app.get( '/indexDirs', ( req, res ) => { if ( req.query.dir ) { + // TODO: Load from json file if ( indexedData[ req.query.dir ] ) { res.send( indexedData[ req.query.dir ] ); } else { - fs.readdir( req.query.dir, { encoding: 'utf-8' }, ( err, dat ) => { - if ( err ) { - res.status( 404 ).send( 'ERR_DIR_NOT_FOUND' ); - return; - }; - ( async() => { - // TODO: Check for songlist.csv or songlist.json file and use the data provided there for each song to override - // what was found automatically. If no song title was found in songlist or metadata, use filename - // TODO: Also save found information to those files and don't rerun checks if data is present - let files = {}; - for ( let file in dat ) { - if ( allowedFileTypes.includes( dat[ file ].slice( dat[ file ].indexOf( '.' ), dat[ file ].length ) ) ) { - try { - let metadata = await musicMetadata.parseFile( req.query.dir + '/' + dat[ file ] ); - files[ req.query.dir + '/' + dat[ file ] ] = { - 'artist': metadata[ 'common' ][ 'artist' ], - 'title': metadata[ 'common' ][ 'title' ], - 'year': metadata[ 'common' ][ 'year' ], - 'bpm': metadata[ 'common' ][ 'bpm' ], - 'genre': metadata[ 'common' ][ 'genre' ], - 'duration': Math.round( metadata[ 'format' ][ 'duration' ] ), - 'isLossless': metadata[ 'format' ][ 'lossless' ], - 'sampleRate': metadata[ 'format' ][ 'sampleRate' ], - 'bitrate': metadata[ 'format' ][ 'bitrate' ], - 'numberOfChannels': metadata[ 'format' ][ 'numberOfChannels' ], - 'container': metadata[ 'format' ][ 'container' ], - 'filename': req.query.dir + '/' + dat[ file ], - } - if ( metadata[ 'common' ][ 'picture' ] ) { - files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = true; - if ( req.query.coverart == 'true' ) { - coverArtIndex[ req.query.dir + '/' + dat[ file ] ] = metadata[ 'common' ][ 'picture' ] ? metadata[ 'common' ][ 'picture' ][ 0 ][ 'data' ] : undefined; - } - } else { - files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; - } - } catch ( err ) { - console.error( err ); - files[ req.query.dir + '/' + dat[ file ] ] = 'ERROR'; - } - } else if ( dat[ file ].slice( dat[ file ].indexOf( '.' ), dat[ file ].length ) === '.csv' ) { - - } else if ( dat[ file ].slice( dat[ file ].indexOf( '.' ), dat[ file ].length ) === '.json' ) { - - } - } - indexedData[ req.query.dir ] = files; - res.send( files ); - } )(); - } ); + res.send( files ); } } else { res.status( 400 ).send( 'ERR_REQ_INCOMPLETE' ); @@ -202,9 +151,6 @@ app.get( '/getSongFile', ( req, res ) => { } } ); -// TODO: Add get lyrics route later -// 'lyrics': metadata[ 'common' ][ 'lyrics' ], - app.use( ( request, response, next ) => { response.sendFile( path.join( __dirname + '' ) ) diff --git a/frontend/src/client/showcase.js b/frontend/src/client/showcase.js index 2ef9ed6..e8e089a 100644 --- a/frontend/src/client/showcase.js +++ b/frontend/src/client/showcase.js @@ -236,8 +236,6 @@ createApp( { analyser.getByteFrequencyData( dataArray ); let prevSpectrum = null; let threshold = 10; // Adjust as needed - // TODO: Make sure it works as it should - // TODO: Make pos update also occur on 10 sec time jump this.beatDetected = false; this.micAnalyzer = setInterval( () => { analyser.getByteFrequencyData( dataArray ); diff --git a/frontend/src/components/player.vue b/frontend/src/components/player.vue index e46749c..6d3cbbb 100644 --- a/frontend/src/components/player.vue +++ b/frontend/src/components/player.vue @@ -270,6 +270,8 @@ export default { } else if ( action === 'forward10' ) { if ( musicPlayer.currentTime < ( musicPlayer.duration - 10 ) ) { musicPlayer.currentTime = musicPlayer.currentTime + 10; + this.playbackPos = musicPlayer.currentTime; + this.sendUpdate( 'pos' ); } else { if ( this.repeatMode !== 'one' ) { this.control( 'next' ); @@ -283,10 +285,17 @@ export default { clearInterval( this.progressTracker ); this.playbackPos = 0; musicPlayer.currentTime = 0; + this.sendUpdate( 'pos' ); } else if ( action === 'next' ) { this.$emit( 'update', { 'type': 'next' } ); } else if ( action === 'previous' ) { - this.$emit( 'update', { 'type': 'previous' } ); + if ( this.playbackPos > 3 ) { + this.playbackPos = 0; + musicPlayer.currentTime = 0; + this.sendUpdate( 'pos' ); + } else { + this.$emit( 'update', { 'type': 'previous' } ); + } } else if ( action === 'shuffleOff' ) { this.$emit( 'update', { 'type': 'shuffleOff' } ); this.isShuffleEnabled = false; @@ -337,6 +346,26 @@ export default { this.$emit( 'update', { 'type': 'fancyView', 'status': true } ); this.isShowingFancyView = true; }, + }, + created() { + document.addEventListener( 'keydown', ( e ) => { + if ( e.key === ' ' ) { + e.preventDefault(); + if ( !this.isPlaying ) { + this.control( 'play' ); + } else { + this.control( 'pause' ); + } + } else if ( e.key === 'ArrowRight' ) { + e.preventDefault(); + this.control( 'next' ); + } else if ( e.key === 'ArrowLeft' ) { + e.preventDefault(); + this.control( 'previous' ); + } else { + console.log( e.key ); + } + } ); } } \ No newline at end of file diff --git a/frontend/src/config/apple-music-api.config.json b/frontend/src/config/apple-music-api.config.json new file mode 100644 index 0000000..ce385e8 --- /dev/null +++ b/frontend/src/config/apple-music-api.config.json @@ -0,0 +1,5 @@ +{ + "teamID": "", + "keyID": "", + "storefront": "us" +} \ No newline at end of file diff --git a/frontend/src/imageFetcher.js b/frontend/src/imageFetcher.js index 42873f0..981e3ad 100644 --- a/frontend/src/imageFetcher.js +++ b/frontend/src/imageFetcher.js @@ -7,6 +7,25 @@ * */ -module.exports.fetch = () => { - // TODO: Implement +const MusicKit = require( 'node-musickit-api' ); +const fs = require( 'fs' ); +const path = require( 'path' ); + +// TODO: deploy non-secret version +const settings = JSON.parse( fs.readFileSync( path.join( __dirname + '/config/apple-music-api.config.secret.json' ) ) ); + +const music = new MusicKit( { + key: fs.readFileSync( path.join( __dirname + '/config/apple_private_key.p8' ) ).toString(), + teamId: settings.teamID, // This is your developer account's team ID + keyId: settings.keyID // This is the keys ID +} ); + +module.exports.fetch = ( type, searchQuery, callback ) => { + music.search( settings.storefront, type, searchQuery, ( err, data ) => { + if ( err ) { + callback( err, null ); + return; + } + callback( null, data ); + } ); } \ No newline at end of file diff --git a/frontend/src/indexer.js b/frontend/src/indexer.js new file mode 100644 index 0000000..622e1e7 --- /dev/null +++ b/frontend/src/indexer.js @@ -0,0 +1,99 @@ +const fs = require( 'fs' ); +const imageFetcher = require( './imageFetcher.js' ); +const musicMetadata = require( 'music-metadata' ); +const allowedFileTypes = [ '.mp3', '.wav', '.flac' ]; + +let indexedData = {}; +let coverArtIndex = {}; + +module.exports.index = ( req ) => { + return new Promise( ( reject, resolve ) => { + fs.readdir( req.query.dir, { encoding: 'utf-8' }, ( err, dat ) => { + if ( err ) { + res.status( 404 ).send( 'ERR_DIR_NOT_FOUND' ); + return; + }; + ( async() => { + // TODO: Check for songlist.csv or songlist.json file and use the data provided there for each song to override + // what was found automatically. If no song title was found in songlist or metadata, use filename + // TODO: Also save found information to those files and don't rerun checks if data is present + if ( dat.includes( 'songlist.csv' ) || dat.includes( 'songlist.json' ) ) { + + } else { + + } + } )(); + } ); + } ); +} + +const parseExistingData = () => { + +} + +const parseDir = async ( dat, req ) => { + let files = {}; + for ( let file in dat ) { + if ( allowedFileTypes.includes( dat[ file ].slice( dat[ file ].indexOf( '.' ), dat[ file ].length ) ) ) { + try { + let metadata = await musicMetadata.parseFile( req.query.dir + '/' + dat[ file ] ); + files[ req.query.dir + '/' + dat[ file ] ] = { + 'artist': metadata[ 'common' ][ 'artist' ], + 'title': metadata[ 'common' ][ 'title' ], + 'year': metadata[ 'common' ][ 'year' ], + 'bpm': metadata[ 'common' ][ 'bpm' ], + 'genre': metadata[ 'common' ][ 'genre' ], + 'duration': Math.round( metadata[ 'format' ][ 'duration' ] ), + 'isLossless': metadata[ 'format' ][ 'lossless' ], + 'sampleRate': metadata[ 'format' ][ 'sampleRate' ], + 'bitrate': metadata[ 'format' ][ 'bitrate' ], + 'numberOfChannels': metadata[ 'format' ][ 'numberOfChannels' ], + 'container': metadata[ 'format' ][ 'container' ], + 'filename': req.query.dir + '/' + dat[ file ], + } + if ( metadata[ 'common' ][ 'picture' ] ) { + files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = true; + if ( req.query.coverart == 'true' ) { + coverArtIndex[ req.query.dir + '/' + dat[ file ] ] = metadata[ 'common' ][ 'picture' ] ? metadata[ 'common' ][ 'picture' ][ 0 ][ 'data' ] : undefined; + } + } else { + if ( req.query.coverart == 'true' ) { + imageFetcher.fetch( 'songs', metadata[ 'common' ][ 'artist' ] + ' ' + metadata[ 'common' ][ 'title' ], ( err, data ) => { + if ( err ) { + indexedData[ req.query.dir ][ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; + files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; + return; + } + if ( data.results.songs ) { + if ( data.results.songs.data ) { + let url = data.results.songs.data[ 0 ].attributes.artwork.url; + url = url.replace( '{w}', data.results.songs.data[ 0 ].attributes.artwork.width ); + url = url.replace( '{h}', data.results.songs.data[ 0 ].attributes.artwork.height ); + console.log( url ); + } else { + indexedData[ req.query.dir ][ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; + files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; + } + } else { + indexedData[ req.query.dir ][ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; + files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; + } + } ); + files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = true; + } else { + files[ req.query.dir + '/' + dat[ file ] ][ 'hasCoverArt' ] = false; + } + } + } catch ( err ) { + console.error( err ); + files[ req.query.dir + '/' + dat[ file ] ] = 'ERROR'; + } + } else if ( dat[ file ].slice( dat[ file ].indexOf( '.' ), dat[ file ].length ) === '.csv' ) { + + } else if ( dat[ file ].slice( dat[ file ].indexOf( '.' ), dat[ file ].length ) === '.json' ) { + + } + } + indexedData[ req.query.dir ] = files; + return files; +} \ No newline at end of file