diff --git a/package-lock.json b/package-lock.json index f87707af52b..b6c6641afcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@storybook/addon-knobs": "^6.1.20", "@storybook/preset-typescript": "^3.0.0", "@storybook/react": "^6.1.20", + "@types/cli-progress": "^3.9.1", "@types/jest": "^26.0.20", "@types/node": "^14.14.31", "@types/query-string": "^6.3.0", @@ -38,6 +39,7 @@ "@typescript-eslint/parser": "^4.15.2", "babel-loader": "^8.2.2", "babel-preset-react-app": "^10.0.0", + "cli-progress": "^3.9.0", "dotenv": "^8.2.0", "eslint": "^7.20.0", "eslint-config-prettier": "^8.1.0", @@ -4855,6 +4857,15 @@ "integrity": "sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw==", "dev": true }, + "node_modules/@types/cli-progress": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.9.1.tgz", + "integrity": "sha512-X/tKJv/GoYlCBS9wwJTLrVSxzIOI/Cj1cCatYOAAoQne3aT1QbHBptBS5+zLe2ToSljAijHU1N/ouBNFvZ2H/g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -7958,6 +7969,19 @@ "node": ">=8" } }, + "node_modules/cli-progress": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.0.tgz", + "integrity": "sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA==", + "dev": true, + "dependencies": { + "colors": "^1.1.2", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/cli-table3": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", @@ -8170,7 +8194,6 @@ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "optional": true, "engines": { "node": ">=0.1.90" } @@ -27979,6 +28002,15 @@ "integrity": "sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw==", "dev": true }, + "@types/cli-progress": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.9.1.tgz", + "integrity": "sha512-X/tKJv/GoYlCBS9wwJTLrVSxzIOI/Cj1cCatYOAAoQne3aT1QbHBptBS5+zLe2ToSljAijHU1N/ouBNFvZ2H/g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -30664,6 +30696,16 @@ "restore-cursor": "^3.1.0" } }, + "cli-progress": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.0.tgz", + "integrity": "sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "string-width": "^4.2.0" + } + }, "cli-table3": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.0.tgz", @@ -30847,8 +30889,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "optional": true + "dev": true }, "combined-stream": { "version": "1.0.8", diff --git a/package.json b/package.json index d20951c4ab3..95702e23aea 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "type-check-watch": "tsc --watch", "type-check-watch:server": "tsc -b tsconfig-server.json --watch", "generate-data": "NODE_PATH=./src ts-node --project tsconfig-server.json src/server/app.ts", + "generate-spotify": "NODE_PATH=./src ts-node --project tsconfig-server.json src/server/generate-spotify.ts", "lint": "eslint --ignore-path .gitignore . --ext ts,tsx,js,jsx", "lint:fix": "eslint --ignore-path .gitignore . --ext ts,tsx,js,jsx --fix", "format": "prettier 'src/**/*.{ts,tsx}' --write", @@ -59,6 +60,7 @@ "@storybook/addon-knobs": "^6.1.20", "@storybook/preset-typescript": "^3.0.0", "@storybook/react": "^6.1.20", + "@types/cli-progress": "^3.9.1", "@types/jest": "^26.0.20", "@types/node": "^14.14.31", "@types/query-string": "^6.3.0", @@ -70,6 +72,7 @@ "@typescript-eslint/parser": "^4.15.2", "babel-loader": "^8.2.2", "babel-preset-react-app": "^10.0.0", + "cli-progress": "^3.9.0", "dotenv": "^8.2.0", "eslint": "^7.20.0", "eslint-config-prettier": "^8.1.0", diff --git a/src/data/spotify.json b/src/data/spotify.json new file mode 100644 index 00000000000..1737526001d --- /dev/null +++ b/src/data/spotify.json @@ -0,0 +1,1256 @@ +{ + "albums": [ + { + "key": "bokuhabokuwosukininaru", + "title": "僕は僕を好きになる", + "id": "1ISWLvdWkjzWN1s0bs9FDG" + }, + { + "key": "hogoshoku", + "title": "しあわせの保護色", + "id": "56JUEkWVuVtK9mElB2kiYs" + }, + { + "key": "yoakemade", + "title": "夜明けまで強がらなくてもいい", + "id": "11hykNTgokpkxoM2ORt5B2" + }, + { + "key": "singout", + "title": "Sing Out!", + "id": "3ThChGpRAJdED7DpEkSFDg" + }, + { + "key": "kaerimichi", + "title": "帰り道は遠回りしたくなる", + "id": "6JRJ7rS8nlHA2ZNZgznlEE" + }, + { + "key": "jikochu", + "title": "ジコチューで行こう!", + "id": "0x9xjOtUFE908fC0a3pIja" + }, + { + "key": "synchronicity", + "title": "シンクロニシティ", + "id": "6kG2cUR1dGvardDnsY9Bpy" + }, + { + "key": "itsukadekiru", + "title": "いつかできるから今日できる", + "id": "4zOFS8kS3Ogn8pbIDpVPyc" + }, + { + "key": "nigemizu", + "title": "逃げ水", + "id": "5r0P9GNKDAhiJw8ahIFLWX" + }, + { + "key": "influencer", + "title": "インフルエンサー", + "id": "2DOlVrOCdY139TNstj7u2I" + }, + { + "key": "sayonaranoimi", + "title": "サヨナラの意味", + "id": "6h44qcqKe5voOXmriZOGNZ" + }, + { + "key": "hadasummer", + "title": "裸足でSummer", + "id": "49O4upPZzJqOkR7NToxWQy" + }, + { + "key": "harujion", + "title": "ハルジオンが咲く頃", + "id": "7Cap3VI6Qj8fdYWKe4iB3s" + }, + { + "key": "imahana", + "title": "今、話したい誰かがいる", + "id": "1uTxqOmZlAz0aYRYRn53co" + }, + { + "key": "taiyouknock", + "title": "太陽ノック", + "id": "6hk3lDpiR9AJlnr3Hfr0Si" + }, + { + "key": "inochihautsukushii", + "title": "命は美しい", + "id": "09k3ewXvAvaBPFapWC2DBe" + }, + { + "key": "nandomenoaozoraka", + "title": "何度目の青空か?", + "id": "4C5S4rMig6j7Hr5gntneml" + }, + { + "key": "natsunofreeeasy", + "title": "夏のFree&Easy", + "id": "4SW9lF2HJhfeaBW4FkRjCe" + }, + { + "key": "kiduitarakataomoi", + "title": "気づいたら片想い", + "id": "1ERBtyylbrzPU4rwZR8LRI" + }, + { + "key": "valletta", + "title": "バレッタ", + "id": "2NkG7Ei5PObrNHHcTgXLgF" + }, + { + "key": "girlsrule", + "title": "ガールズルール", + "id": "2sHnyQEh20qlAUutv9GhRt" + }, + { + "key": "kiminonahakibou", + "title": "君の名は希望", + "id": "7A3aZHZPTFtdljcMlOB2ca" + }, + { + "key": "seifukunomannequin", + "title": "制服のマネキン", + "id": "32MRkhxCzkEVxQqt1ZP5tw" + }, + { + "key": "hashirebicycle", + "title": "走れ!Bicycle", + "id": "5oQSm1mzDY83RxXPiNGDnI" + }, + { + "key": "oideshampo", + "title": "おいでシャンプー", + "id": "36XKV5MFmLauQ9daxq2rgk" + }, + { + "key": "gurugurucurtain", + "title": "ぐるぐるカーテン", + "id": "4TQD18Lcu9qi7bdEEHwqDx" + }, + { + "key": "imagaomoideninarumade", + "title": "今が思い出になるまで", + "id": "2hMlKl0uHwvJbVi5CDdWE0" + }, + { + "key": "bokudakenokimiunderbestalbum", + "title": "僕だけの君〜Under Super Best〜", + "id": "6YRzRI4ntxktnpEd7S7y5t" + }, + { + "key": "umaretekarahajimetemitayume", + "title": "生まれてから初めて見た夢", + "id": "6L1fT1qgkCZsuAtIMesmTf" + }, + { + "key": "sorezorenoisu", + "title": "それぞれの椅子", + "id": "11WqRoGaD4VOqMnHbU2qRd" + }, + { + "key": "toumeinairo", + "title": "透明な色", + "id": "113S2YpuGZsDyxALnzt8Ni" + }, + { + "key": "sekaichuunorinjinyo", + "title": "世界中の隣人よ", + "id": "1bk9wrORug1g1wqzXNXAkR" + }, + { + "key": "route246", + "title": "Route 246", + "id": "6vUqEPgE9CoFOL4I9Do7J2" + }, + { + "key": "123", + "title": "1・2・3", + "id": "53QcliU1a5fKzjc42cylAx" + } + ], + "songs": [ + { + "key": "gurugurucurtain", + "title": "ぐるぐるカーテン", + "id": "2KYGpwVMkiIDuMrglcsBUu" + }, + { + "key": "hidarimunenoyuuki", + "title": "左胸の勇気", + "id": "5WdN9uaPHloYhM5DIMRMOs" + }, + { + "key": "nogizakanouta", + "title": "乃木坂の詩", + "id": "2HqPpwLSb2GHYvS5HWGx6E" + }, + { + "key": "aitakattakamoshirenai", + "title": "会いたかったかもしれない", + "id": "4n1hmiV1acBqXrdRJNLFrX" + }, + { + "key": "ushinaitakunaikara", + "title": "失いたくないから", + "id": "24NQGra3FwYDL18jn16zyc" + }, + { + "key": "shiroikumoninotte", + "title": "白い雲にのって", + "id": "2KYGpwVMkiIDuMrglcsBUu" + }, + { + "key": "oideshampo", + "title": "おいでシャンプー", + "id": "7ybYoq7gscTWZ3xBMD56EH" + }, + { + "key": "ookaminikuchibuewo", + "title": "狼に口笛を", + "id": "4EhjVvzUMsHqb4o9M1jTHT" + }, + { + "key": "kokoronokusuri", + "title": "心の薬", + "id": "4MvKGlzuPDrMppbraalrkF" + }, + { + "key": "guuzenwoiiwakenishite", + "title": "偶然を言い訳にして", + "id": "2wxNEfvOfXHqtBSyLdkgs6" + }, + { + "key": "mizutamamoyou", + "title": "水玉模様", + "id": "3qcljexmOIjgnZsixBP2qB" + }, + { + "key": "house", + "title": "ハウス!", + "id": "7ybYoq7gscTWZ3xBMD56EH" + }, + { + "key": "hashirebicycle", + "title": "走れ!Bicycle", + "id": "6PHaSGdOpN6l3iOjVNTTxF" + }, + { + "key": "namidagamadakanashimi", + "title": "涙がまだ悲しみだった頃", + "id": "5AJiBI7tIJnmKjFO5DVE1c" + }, + { + "key": "sekkachinakatatsumuri", + "title": "せっかちなかたつむり", + "id": "0JEE8o1wfLRXYdc98jCeo4" + }, + { + "key": "hitohanazehashirunoka", + "title": "人はなぜ走るのか?", + "id": "6PHaSGdOpN6l3iOjVNTTxF" + }, + { + "key": "otogadenaiguitar", + "title": "音が出ないギター", + "id": "60Vsc24OaaiTKPiY2vqler" + }, + { + "key": "kairyuunoshimayo", + "title": "海流の島よ", + "id": "5Ta0UPB35YzO3kdANMXbnN" + }, + { + "key": "seifukunomannequin", + "title": "制服のマネキン", + "id": "3G1eBTZjG3IS0uijvLd61d" + }, + { + "key": "harunomelody", + "title": "春のメロディー", + "id": "7jAg7YXzdM62NhAJGiThnq" + }, + { + "key": "yubibouenkyou", + "title": "指望遠鏡", + "id": "3G1eBTZjG3IS0uijvLd61d" + }, + { + "key": "yasashisanaramaniatteiru", + "title": "やさしさなら間に合ってる", + "id": "2OpdwkRt9cakz99ptEAYaU" + }, + { + "key": "kokojanaidokoka", + "title": "ここじゃないどこか", + "id": "29uPyrWXwC5s2mjrvQ799G" + }, + { + "key": "shibuyablues", + "title": "渋谷ブルース", + "id": "1CHXuG4f3st8FPBZkLCR7H" + }, + { + "key": "kiminonahakibou", + "title": "君の名は希望", + "id": "4Sa8R8CJgXNJz6MsOrsSwj" + }, + { + "key": "13nichinokinyoubi", + "title": "13日の金曜日", + "id": "1i1aPkQl35PFBmAtLe1ylJ" + }, + { + "key": "shakism", + "title": "シャキイズム", + "id": "167Dw1F1uoV0vXQPe5iW5k" + }, + { + "key": "romanticikayaki", + "title": "ロマンティックいか焼き", + "id": "4Sa8R8CJgXNJz6MsOrsSwj" + }, + { + "key": "dekopin", + "title": "でこぴん", + "id": "5nyfV3iHR8j3JToPhrzY0u" + }, + { + "key": "psychokinesesnokanousei", + "title": "サイコキネシスの可能性", + "id": "5dxwnKeauoyJfwS2L9K4r6" + }, + { + "key": "girlsrule", + "title": "ガールズルール", + "id": "6fZnP5rYZ4vH2ZIz8wmY3V" + }, + { + "key": "senpuuki", + "title": "扇風機", + "id": "5omWDEV8lWWAXW2UqiJjCy" + }, + { + "key": "sekaideichibankodokuralover", + "title": "世界で一番 孤独なLover", + "id": "7vIHaZfoZKwTIpyVOyrOAl" + }, + { + "key": "koumoriyo", + "title": "コウモリよ", + "id": "5V1U72W8tPsK2vcUxB7Rkv" + }, + { + "key": "hokanohoshikara", + "title": "他の星から", + "id": "2WtzXgKsi1eIh8e498hjEm" + }, + { + "key": "ningentoiugakki", + "title": "人間という楽器", + "id": "3AeU1caJLSFn41QYM53Vp9" + }, + { + "key": "valletta", + "title": "バレッタ", + "id": "4bY2oZGA5Br3pTE1Jd1IfY" + }, + { + "key": "hatsukoinohitowo", + "title": "初恋の人を今でも", + "id": "20zXam3lhbDUz0LKamwbRg" + }, + { + "key": "tsukinoookisa", + "title": "月の大きさ", + "id": "4bY2oZGA5Br3pTE1Jd1IfY" + }, + { + "key": "watanotamenidarekanotameni", + "title": "私のために 誰かのために", + "id": "3uZyNjUQUZi8LchoPMpXbu" + }, + { + "key": "sonnabakana", + "title": "そんなバカな…", + "id": "3tlwsu4iIqb0AsPRNO9nVJ" + }, + { + "key": "yasashisatoha", + "title": "やさしさとは", + "id": "0Nfc6VaSM29s3NlqAFYNs8" + }, + { + "key": "kiduitarakataomoi", + "title": "気づいたら片想い", + "id": "2dM2gSRYArANuzXNVAKdSF" + }, + { + "key": "umaretamamade", + "title": "生まれたままで", + "id": "3aAwyLQCufcIO5PycybXDa" + }, + { + "key": "romancenostart", + "title": "ロマンスのスタート", + "id": "2dM2gSRYArANuzXNVAKdSF" + }, + { + "key": "toikimethod", + "title": "吐息のメソッド", + "id": "0ftbYKKBXa474dBswBvOKN" + }, + { + "key": "kodokukyoudai", + "title": "孤独兄弟", + "id": "7s9zSYY1tw8vtZSFJLXDg5" + }, + { + "key": "dankeschan", + "title": "ダンケシェーン", + "id": "1TTOSCx5QXuJMzAAuM4Iub" + }, + { + "key": "natsunofreeeasy", + "title": "夏のFree&Easy", + "id": "07rhQCCBrzRyG4x5mseaUj" + }, + { + "key": "kokoniiruriyuu", + "title": "ここにいる理由", + "id": "3WfZC7nM0z2E7ztnjYv6te" + }, + { + "key": "nanimodekizunisobaniiru", + "title": "何もできずにそばにいる", + "id": "07rhQCCBrzRyG4x5mseaUj" + }, + { + "key": "sonosakinodeguchi", + "title": "その先の出口!", + "id": "5uGfc4JnZjpj4S5isp5rnX" + }, + { + "key": "mukuchinalion", + "title": "無口なライオン", + "id": "4J2ndtZOpU5ufMPnKwBjzM" + }, + { + "key": "bokugaikanakyadaregaikunda", + "title": "僕が行かなきゃ誰が行くんだ?", + "id": "34EggPJUYZ980F3aUlHHnp" + }, + { + "key": "nandomenoaozoraka", + "title": "何度目の青空か?", + "id": "5BHSWjy2EUK0hyif8LvTgt" + }, + { + "key": "anohibokuha", + "title": "あの日 僕は咄嗟に嘘をついた", + "id": "31uqroHYLDL86v05Lzb2Bt" + }, + { + "key": "toomawarinoaijou", + "title": "遠回りの愛情", + "id": "6U0JjaWBwEKXmqhwOv7CWy" + }, + { + "key": "korogattakanewonarase", + "title": "転がった鐘を鳴らせ!", + "id": "0u3IypMX6E59xWB2Eeo5bz" + }, + { + "key": "watashiokiru", + "title": "私、起きる。", + "id": "7uv4UmkNrK60zBBGxPToXy" + }, + { + "key": "tenderdays", + "title": "Tender days", + "id": "6snDcXtATlGaUq74bL9D1r" + }, + { + "key": "inochihautsukushii", + "title": "命は美しい", + "id": "17MM4MKSYe5aUxgWjInR0a" + }, + { + "key": "kimihabokuto", + "title": "君は僕と会わない方がよかったのかな", + "id": "0eXA6K04zDFjfASRZ33e3W" + }, + { + "key": "arakajimekatarareruromance", + "title": "あらかじめ語られるロマンス", + "id": "7f5zSlRZMdRjRnWzSjYJeN" + }, + { + "key": "tachinaorichuu", + "title": "立ち直り中", + "id": "17MM4MKSYe5aUxgWjInR0a" + }, + { + "key": "gomennezutto", + "title": "ごめんね ずっと…", + "id": "4A4DYoICwsIrsd2DpJsqPB" + }, + { + "key": "border", + "title": "ボーダー", + "id": "2WhBfFwG3IJjDvuOVg2c1p" + }, + { + "key": "taiyouknock", + "title": "太陽ノック", + "id": "5eDvZAip7hyk4zfCx9VgdU" + }, + { + "key": "wakaregiwa", + "title": "別れ際、もっと好きになる", + "id": "0pF5NfdEi6tH4iB01tXrTH" + }, + { + "key": "mousukoshinoyume", + "title": "もう少しの夢", + "id": "761UxAw3jPm8A1VesXrUFA" + }, + { + "key": "sakanatachinolovesong", + "title": "魚たちのLOVE SONG", + "id": "25UAy0ZGpB9vYWPesCDFNT" + }, + { + "key": "muhyoujyou", + "title": "無表情", + "id": "2KFuAjeUSENhyGB7tRnXrq" + }, + { + "key": "hanenokioku", + "title": "羽根の記憶", + "id": "2KDDzuuP0Jii5D31rN4b49" + }, + { + "key": "seifukuwonuidesayonarawo", + "title": "制服を脱いでサヨナラを…", + "id": "7azGsAmxfXt1EoYIrdAtFH" + }, + { + "key": "imahana", + "title": "今、話したい誰かがいる", + "id": "4tB8CBS7A4LHwwtVr3gZvZ" + }, + { + "key": "shittonokenri", + "title": "嫉妬の権利", + "id": "5Az4TuiEzMcGa1anQ0BWig" + }, + { + "key": "popipappappa", + "title": "ポピパッパパー", + "id": "4tB8CBS7A4LHwwtVr3gZvZ" + }, + { + "key": "otonahenochikamichi", + "title": "大人への近道", + "id": "3VMZAipjYqCQBuLUAB0DbR" + }, + { + "key": "kanashiminowasurekata", + "title": "悲しみの忘れ方", + "id": "69EXE4CkLHvy69Fhy60uw6" + }, + { + "key": "sukima", + "title": "隙間", + "id": "7Hn5xIUv3ph7N64hoAC4K3" + }, + { + "key": "harujion", + "title": "ハルジオンが咲く頃", + "id": "3H2Wvju9hjz9Tobx5BqR8j" + }, + { + "key": "futoogo", + "title": "不等号", + "id": "6zu5TnWb5nSuS3nDhyrPwr" + }, + { + "key": "harukanarubhutan", + "title": "遥かなるブータン", + "id": "39aFcLysRVduA54gGzBNuh" + }, + { + "key": "tsuyogarutsubomi", + "title": "強がる蕾", + "id": "63VLw1ZUtfiLhWOsSv7SWB" + }, + { + "key": "kyuushamen", + "title": "急斜面", + "id": "2Rev4To5NySteYuO9mBYLP" + }, + { + "key": "tsuribori", + "title": "釣り堀", + "id": "67FzYb95kfMkXtvE08UGM7" + }, + { + "key": "yuuutsutofuusengamu", + "title": "憂鬱と風船ガム", + "id": "2mssm4KTiNv1UxbC9iPHBj" + }, + { + "key": "hadasummer", + "title": "裸足でSummer", + "id": "288SNOHYZizb3jZ6U6DByY" + }, + { + "key": "secretgravity", + "title": "シークレットグラフィティー", + "id": "5YruHLEm4wBPvDiiyPE535" + }, + { + "key": "bokudakenohikari", + "title": "僕だけの光", + "id": "4G1SfLHqH2LRRgZUPXyd1f" + }, + { + "key": "offshoregirl", + "title": "オフショアガール", + "id": "3P6eQT3gsJPngJZKnfMKRq" + }, + { + "key": "inochinoshinjitsu", + "title": "命の真実 ミュージカル「林檎売りとカメムシ」", + "id": "3LnlOWlTucksFfI8GLk8DY" + }, + { + "key": "hakumaisama", + "title": "白米様", + "id": "0irSjVd94xSXvgOPsX0zjv" + }, + { + "key": "ikuatenonaibokutachi", + "title": "行くあてのない僕たち", + "id": "1Tky8Rhg1rorqdl5ciICY8" + }, + { + "key": "sayonaranoimi", + "title": "サヨナラの意味", + "id": "0lU7b9JpuI9RMVqqcJc5kd" + }, + { + "key": "bulanko", + "title": "ブランコ", + "id": "3gAA0WJSgKuPzzzh7ctW0J" + }, + { + "key": "kodokunaaozora", + "title": "孤独な青空", + "id": "7gIbUkerBBBFXkLNgkatax" + }, + { + "key": "anokyoushitsu", + "title": "あの教室", + "id": "2Y1zmbZFWe1502zWB9kPIV" + }, + { + "key": "nidomenokisskara", + "title": "2度目のキスから", + "id": "2jy416ZaSveCTN2tIefrkO" + }, + { + "key": "kiminiokuruhanaganai", + "title": "君に贈る花がない", + "id": "1TXY3XKZTcH1WoJZzhLz1o" + }, + { + "key": "naimononedari", + "title": "ないものねだり", + "id": "0YrImmP8vWiOzhmXLmrPSZ" + }, + { + "key": "influencer", + "title": "インフルエンサー", + "id": "0PHEwJbhs3wte1vCsBI4LF" + }, + { + "key": "fusuenhaikiteiru", + "title": "風船は生きている", + "id": "2eKAV73LZoVUzfsUkRsJ5E" + }, + { + "key": "jinseiwokangaetakunaru", + "title": "人生を考えたくなる", + "id": "6NL0GcenRMDurXrBMP32d3" + }, + { + "key": "igaibreak", + "title": "意外BREAK", + "id": "6CgHMIz8xAGtMWN9a7gPB8" + }, + { + "key": "anotherghost", + "title": "Another Ghost", + "id": "3q5OeaArzZRQJdXyWiyK8s" + }, + { + "key": "sanbanmenokaze", + "title": "三番目の風", + "id": "6ArMFJgmzYe7v0oomxZWPa" + }, + { + "key": "atarisawarinonaihanashi", + "title": "当たり障りのない話", + "id": "6IoGtLneWHFg8xohLZGHXK" + }, + { + "key": "nigemizu", + "title": "逃げ水", + "id": "4BUOpUEPoIBZpq7YauH6Bs" + }, + { + "key": "under", + "title": "アンダー", + "id": "7LYDGshFLrpYubQKl0cnbG" + }, + { + "key": "onnahahitorija", + "title": "女は一人じゃ眠れない", + "id": "6EPyPNp1db6gQ0NefdL0Eh" + }, + { + "key": "hitonatsunonagasa", + "title": "ひと夏の長さより…", + "id": "4lL86KdpvgIYIi9k4hTcyi" + }, + { + "key": "liveshin", + "title": "ライブ神", + "id": "0JAmHfmvOMbastQeMC2Fp8" + }, + { + "key": "mirainokotae", + "title": "未来の答え", + "id": "3J6pnunAEOwMH0sk1KGCkG" + }, + { + "key": "naitatteiijanaika", + "title": "泣いたっていいじゃないか?", + "id": "0vROfsN8j8BfSHzBrL7IAD" + }, + { + "key": "itsukadekiru", + "title": "いつかできるから今日できる", + "id": "0kUjyLfXDaKZedEAruiqAw" + }, + { + "key": "myrule", + "title": "My rule", + "id": "1CqW8fybefVEVqoaPJRPRq" + }, + { + "key": "fumenshou", + "title": "不眠症", + "id": "342aVhw02OaBF9UEywfbGT" + }, + { + "key": "maaiika", + "title": "まあいいか?", + "id": "6bLt7GKS0WsgLJIXn9SmjL" + }, + { + "key": "shirenosoujinin", + "title": "失恋お掃除人", + "id": "0mJIAermoMh7OwHyPUOixK" + }, + { + "key": "bokunoshoudou", + "title": "僕の衝動", + "id": "2dSluryKdieg5HAcwPgdwb" + }, + { + "key": "atarashiikafun", + "title": "新しい花粉 〜ミュージカル「見知らぬ世界」より〜", + "id": "1ca4t27dPC0fSd4r57x5ur" + }, + { + "key": "synchronicity", + "title": "シンクロニシティ", + "id": "3cZwlMEtYsc2c3d6ABFyOz" + }, + { + "key": "atarashiisekai", + "title": "新しい世界", + "id": "4iAg9UTrGgugJImC7p9IrK" + }, + { + "key": "against", + "title": "Against", + "id": "6SptPJcmTkYFJOy75hD1Rl" + }, + { + "key": "kumoninarebaii", + "title": "雲になればいい", + "id": "7s4UaSa1r99mkcBnQeGn4h" + }, + { + "key": "scoutman", + "title": "スカウトマン", + "id": "7cJdxQFu9WxkutVDbanQlY" + }, + { + "key": "tokitokimekimeki", + "title": "トキトキメキメキ", + "id": "4J7PDtfmPoVDRufv2ch6Jh" + }, + { + "key": "kotodamahou", + "title": "言霊砲", + "id": "4ATXW5W9Q9f3MYDNkhsEcP" + }, + { + "key": "jikochu", + "title": "ジコチューで行こう!", + "id": "3Tt2l5PZQMonMesad0GDu3" + }, + { + "key": "sankakunoakichi", + "title": "三角の空き地", + "id": "6LI2ssXl4dHF1TzK0UDQ2M" + }, + { + "key": "soratobira", + "title": "空扉", + "id": "4Rbvlpm11tSQix847jn9VN" + }, + { + "key": "jibunjanaikanji", + "title": "自分じゃない感じ", + "id": "0z74l1FiNMYOlVJNcdLQll" + }, + { + "key": "kokoronomonolog", + "title": "心のモノローグ", + "id": "7DdBIYjmkR9Ztbue5cAME2" + }, + { + "key": "chikyuugamaruinara", + "title": "地球が丸いなら", + "id": "2QItMBwcXzZU2SRMWVAhmy" + }, + { + "key": "annarisukidatta", + "title": "あんなに好きだったのに...", + "id": "3hgmb4VRqF9HSDonZz4tFp" + }, + { + "key": "kaerimichi", + "title": "帰り道は遠回りしたくなる", + "id": "4RWJxm0Vn83uE0TIhwnABv" + }, + { + "key": "nichijou", + "title": "日常", + "id": "1Cbp51G3YL67nvFmYBpozR" + }, + { + "key": "caravan", + "title": "キャラバンは眠らない", + "id": "0exxxpT1EIBmbR5lByOZSN" + }, + { + "key": "tsuduku", + "title": "つづく", + "id": "1x8zWQYfhjEDJOkMFKFldj" + }, + { + "key": "kokuhakunojunban", + "title": "告白の順番", + "id": "6NZNue7dfaGDTpdrt6kIui" + }, + { + "key": "chopinnousotsuki", + "title": "ショパンの嘘つき", + "id": "46ah85z0PR7y0P90lZfZgW" + }, + { + "key": "shiritaikoto", + "title": "知りたいこと", + "id": "4niF2a63Vmq1ATzokilk5z" + }, + { + "key": "singout", + "title": "Sing Out!", + "id": "3sdIljU1Gej1Wry4dhqhEF" + }, + { + "key": "kassouro", + "title": "滑走路", + "id": "0f1K02YGAMuwl0mg2klOgG" + }, + { + "key": "noyounasonzai", + "title": "のような存在", + "id": "44cf9GtsX1zPUiJIsVgiwf" + }, + { + "key": "amiloving", + "title": "Am I Loving?", + "id": "6UHNo7NANyU7Hqyhb13sx7" + }, + { + "key": "heikousen", + "title": "平行線", + "id": "0J0mr41LztDnTi5UOJevIB" + }, + { + "key": "yonbanmenohikari", + "title": "4番目の光", + "id": "72gHiEGclooRMFt7HinWPk" + }, + { + "key": "aimai", + "title": "曖昧", + "id": "5cySeVYbitxf7X0gwxObUj" + }, + { + "key": "yoakemade", + "title": "夜明けまで強がらなくてもいい", + "id": "6fLq9e3IPdHUwIeSR49n5e" + }, + { + "key": "bokunokoto", + "title": "僕のこと、知ってる?", + "id": "39epbMXRznFcePJ7qjpdTO" + }, + { + "key": "romendensha", + "title": "路面電車の街", + "id": "4dcZ6hNfOt0AwAnmUXvWLz" + }, + { + "key": "toshoshitsu", + "title": "図書室の君へ", + "id": "3rm5vFfGPXxz8frQpHC6oJ" + }, + { + "key": "tokidokiomoidashite", + "title": "時々 思い出してください", + "id": "4caZsqrVQC34Hepr4YzR8i" + }, + { + "key": "domybest", + "title": "~Do my best~じゃ意味はない", + "id": "3fLHhgAcOiszGj4ja26F9V" + }, + { + "key": "bokunoomoikomi", + "title": "僕の思い込み", + "id": "2T5VEgkRTrWXb564mWMmRT" + }, + { + "key": "hogoshoku", + "title": "しあわせの保護色", + "id": "52SybEH2oGMMrvnoLBFCCf" + }, + { + "key": "staywithme", + "title": "サヨナラ Stay with me", + "id": "0YlWiVdHiRJi0ik9DoBMyf" + }, + { + "key": "jaane", + "title": "じゃあね。", + "id": "2tfWSFLLCbrLQTO2Bysx05" + }, + { + "key": "anastasia", + "title": "アナスターシャ", + "id": "1D4aIu3rfVDuIgamuiyqFr" + }, + { + "key": "brandnewday", + "title": "毎日がBrand new day", + "id": "3ZZc0JttCeRwWPb2U5H441" + }, + { + "key": "isee", + "title": "I see…", + "id": "32OuoReGRWwfbecOPvrwiu" + }, + { + "key": "fantastic", + "title": "ファンタスティック三色パン", + "id": null + }, + { + "key": "bokuhabokuwosukininaru", + "title": "僕は僕を好きになる", + "id": "7z6wfRDM09uY5x5HUoqSjV" + }, + { + "key": "tsumetaimizunonaka", + "title": "冷たい水の中", + "id": "1crlvs2Lcx1nWuytuREXls" + }, + { + "key": "outoftheblue", + "title": "Out of the blue", + "id": "7zOquHCkrtxvqsDXsTEKeC" + }, + { + "key": "kuchihodonimonaikiss", + "title": "口ほどにもないKISS", + "id": "696tGOLUPP1Pm2o3h8yFjG" + }, + { + "key": "ashitagaaruriyuu", + "title": "明日がある理由", + "id": "6wE6JlR2ZXOS478i7AocWT" + }, + { + "key": "wildernessworld", + "title": "Wilderness world", + "id": "0QUPgSnUrPquL8FzQ1qAHO" + }, + { + "key": "yuujoupierce", + "title": "友情ピアス", + "id": "16iSt4eD3gboQGYbt4o98V" + }, + { + "key": "darekahamikata", + "title": "誰かは味方", + "id": "4SHhFiwHvIImfdOcedzXpe" + }, + { + "key": "kakumeinouma", + "title": "革命の馬", + "id": "0scYh5dgeZIvnbFIMgXagP" + }, + { + "key": "bokugairubasho", + "title": "僕がいる場所", + "id": "14Iv7R2qZ6f9U0MdJf24UK" + }, + { + "key": "anatanotamenihikitai", + "title": "あなたのために弾きたい", + "id": "4QBWFxM2vULNYEVoJdXlBF" + }, + { + "key": "keishasuru", + "title": "傾斜する", + "id": "6tr3iX2T0yVGXSR1bxbiWp" + }, + { + "key": "nazonorakugaki", + "title": "なぞの落書き", + "id": "4uVWXIh5QmHA0B0YMExYxO" + }, + { + "key": "jiyuunokanata", + "title": "自由の彼方", + "id": "2zFpfoNzQCX8nF5wx5QRz9" + }, + { + "key": "hitoriyogari", + "title": "ひとりよがり", + "id": "01K100VZmxJLcK87H9UOrO" + }, + { + "key": "kikkake", + "title": "きっかけ", + "id": "0cJ8TAagHiLE5Hir5eTzO0" + }, + { + "key": "taiyounikudokarete", + "title": "太陽に口説かれて", + "id": "3EcQNCmWH7bFUt4bpA9jwA" + }, + { + "key": "yokuboureincarnation", + "title": "欲望のリインカーネーション", + "id": "2mJMGSEgzoeHO7mNeEAz0k" + }, + { + "key": "kuukikann", + "title": "空気感", + "id": "5DvPZtJncFCL5hYzIGWrwU" + }, + { + "key": "kougouseikibou", + "title": "光合成希望", + "id": "5SC8XFdVkbCY9OxSaxLVjU" + }, + { + "key": "threefoldchoice", + "title": "Threefold choice", + "id": "4I8qxjt4tGgx8sOWVpP0RE" + }, + { + "key": "teitaionnokiss", + "title": "低体温のキス", + "id": "1jMyMUuaCH5HXgHQHmJnd0" + }, + { + "key": "shitsurenshitarakaowoarae", + "title": "失恋したら、顔を洗え!", + "id": "2c27qiBQ4i6KDKHmrjJXDg" + }, + { + "key": "kakigoorinokataomoi", + "title": "かき氷の片想い", + "id": "6rXYzMClnUDGNM5JGOyFZh" + }, + { + "key": "kanjyourokugousen", + "title": "環状六号線", + "id": "3fJr2a12MyPt1PyL6xQibV" + }, + { + "key": "kuchiyakusoku", + "title": "口約束", + "id": "1XxJtK3zUnyKO2ieWXxtC2" + }, + { + "key": "skydiving", + "title": "スカイダイビング", + "id": "52pzDWwPLtDBUwJKYnEadp" + }, + { + "key": "kimigaaoidekureta", + "title": "君が扇いでくれた", + "id": "0anw2y6ifCI7QS8Cj5Ph7x" + }, + { + "key": "omoidefirst", + "title": "思い出ファースト", + "id": "0nyKonjsR9NyxRt8sLmXIW" + }, + { + "key": "setteiondo", + "title": "設定温度", + "id": "2AIuPQQffrwa69nWwhWtc0" + }, + { + "key": "rewindanohi", + "title": "Rewindあの日", + "id": "2F0BHgRpe2qHKo9TPYCdvY" + }, + { + "key": "gomennesmoothie", + "title": "ごめんね、スムージー", + "id": "67wQAw9IlNH8mZ62BZa7Pb" + }, + { + "key": "minikuiwatashi", + "title": "醜い私", + "id": "1qD8gMjI5kLoukd7abEikU" + }, + { + "key": "kataikaranoyounidakishimetai", + "title": "硬い殻のように抱きしめたい", + "id": "3dulIrWkmE0dkQXx3l4goU" + }, + { + "key": "mangetsugakieta", + "title": "満月が消えた", + "id": "5lGGpEdbUgrQeTWk4H46ws" + }, + { + "key": "watabokori", + "title": "ワタボコリ", + "id": "2vc94JB9Bu1tzWrMNN3C4E" + }, + { + "key": "ryuuseidiscotic", + "title": "流星ディスコティック", + "id": "4zXlRlay9e52MBsojv3Z9R" + }, + { + "key": "boukyakutobigaku", + "title": "忘却と美学", + "id": "0cmyH8Z46P96325dQF0Pde" + }, + { + "key": "jibunnokoto", + "title": "自分のこと", + "id": "1TNCxWTdtNmgPQmk18yiyg" + }, + { + "key": "unuborebeach", + "title": "自惚れビーチ", + "id": "32SSQ92kR3a2zixZeOXAW4" + }, + { + "key": "sonohito", + "title": "その女", + "id": "6HSwLsRg5yJw9U6YrxvslD" + }, + { + "key": "dareyorimosobaniitai", + "title": "誰よりそばにいたい", + "id": "3PCJ2r8e6Zf9Nfau0i09Vb" + }, + { + "key": "arigachinarenai", + "title": "ありがちな恋愛", + "id": "5WIczCcuSgjKPYQnezexg1" + }, + { + "key": "moshikimigainakereba", + "title": "もし君がいなければ", + "id": "62RUIMh289Uf7BTAE97WmF" + }, + { + "key": "kissnoshuriken", + "title": "キスの手裏剣", + "id": "6w2NYKv8EEE1pilZRiPJZP" + }, + { + "key": "hoozuewotsuitehanemurenai", + "title": "頬杖をついては眠れない", + "id": "4P3tZcOFjT4AbgWY6iyi36" + }, + { + "key": "bocchitou", + "title": "ぼっち党", + "id": "44a9nS4c72m1vn6bnRUg3B" + }, + { + "key": "sayuringoboshuuchuu", + "title": "さゆりんご募集中", + "id": "2ZvnjCyHDGAXObqI8fne6e" + }, + { + "key": "gorugonzora", + "title": "ゴルゴンゾーラ", + "id": "23sOsxn7BECJPt1jM6GZNF" + }, + { + "key": "mousuguzambidensetsu", + "title": "もうすぐ~ザンビ伝説~", + "id": "4QdSvekzs4vRYhxO2y8BLI" + }, + { + "key": "sekainorinjinyo", + "title": "世界中の隣人よ", + "id": "5CenLj8SbVZThUcZtuHpwi" + }, + { + "key": "route246", + "title": "Route 246", + "id": "5Zobr1aK1bEo7iUkI8Okxc" + }, + { + "key": "123", + "title": "1・2・3", + "id": "5eOz8xzZX9J5oSGTdifRgz" + } + ] +} \ No newline at end of file diff --git a/src/server/actors/Spotify/index.ts b/src/server/actors/Spotify/index.ts new file mode 100644 index 00000000000..0442f1a8f6b --- /dev/null +++ b/src/server/actors/Spotify/index.ts @@ -0,0 +1,207 @@ +import cliProgress from 'cli-progress'; +import { CdTitle } from 'server/actors/Discography/constants/cdTitle'; +import { DiscographyRawArray } from 'server/actors/Discography/models'; +import { + filterAlbums, + filterOtherCds, + filterSingles, +} from 'server/actors/Discography/utils'; +import { SongTitle } from 'server/actors/Songs/constants/songTitle'; +import { + convertSongAlbums, + convertSongOtherCds, + convertSongSingle, +} from 'server/actors/Songs/converters'; +import { SongsRawArray } from 'server/actors/Songs/models'; +import { + SpotifyAlbumId, + SpotifyIds, + SpotifySongId, +} from 'server/actors/Spotify/models'; +import * as SpotifyApi from 'server/libs/spotify'; +import { writeJSONFile } from 'server/utils/file'; +import { arrayToObject, batchArray } from 'utils/array'; + +const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.legacy); + +type FetchOptions = { + batchSize?: number; + timeout?: number; +}; + +const DEFAULT_BATCH_SIZE = 10; +const DEFAULT_TIMEOUT = 2000; + +function batchFetchPromises( + fetchPromises: (() => Promise)[], + batchSize: number, + timeout: number +) { + const fetchPromiseBatches = batchArray(fetchPromises, batchSize); + + return fetchPromiseBatches.map(batch => async () => { + const albums = await Promise.all(batch.map(batch => batch())); + return new Promise(resolve => { + setTimeout(resolve, timeout, albums); + }); + }); +} + +async function getSpotifyAlbumArray( + albumsRawArray: DiscographyRawArray, + options?: FetchOptions +): Promise { + console.log('Fetching albums'); + progressBar.start(albumsRawArray.length, 0); + + const batchSize = options?.batchSize ?? DEFAULT_BATCH_SIZE; + const timeout = options?.timeout ?? DEFAULT_TIMEOUT; + + const allFetchPromises = albumsRawArray.map(album => async () => { + const spotifyAlbum = await SpotifyApi.getSpotifyAlbum(album.title); + return { + key: album.key, + title: album.title, + id: spotifyAlbum?.id ?? null, + }; + }); + + const fetchPromisesWaitingChain = batchFetchPromises( + allFetchPromises, + batchSize, + timeout + ); + + const albumIds: SpotifyAlbumId[] = []; + + for (let i = 0; i < fetchPromisesWaitingChain.length; i++) { + const fetchBatch = fetchPromisesWaitingChain[i]; + const albumIdsBatch = await fetchBatch(); + albumIds.push(...albumIdsBatch); + progressBar.update(Math.min((i + 1) * batchSize, albumsRawArray.length)); + } + + progressBar.stop(); + + return albumIds; +} + +async function getSpotifySongArray(params: { + songsRawArray: SongsRawArray; + singlesRawArray: DiscographyRawArray; + otherCdsRawArray: DiscographyRawArray; + albumsRawArray: DiscographyRawArray; + spotifyAlbumArray: SpotifyAlbumId[]; + options?: FetchOptions; +}): Promise { + const { + songsRawArray, + singlesRawArray, + otherCdsRawArray, + albumsRawArray, + spotifyAlbumArray, + options, + } = params; + + console.log('Fetching songs'); + progressBar.start(songsRawArray.length, 0); + + const batchSize = options?.batchSize ?? DEFAULT_BATCH_SIZE; + const timeout = options?.timeout ?? DEFAULT_TIMEOUT; + + const songWithAlbumArray: { + key: string; + title: SongTitle; + album: CdTitle | ''; + }[] = songsRawArray.map(song => { + const songSingle = convertSongSingle({ + songTitle: song.title, + singlesRawArray, + }); + const songAlbum = convertSongAlbums({ + songTitle: song.title, + albumsRawArray, + })[0]; + const songOtherCd = convertSongOtherCds({ + songTitle: song.title, + otherCdsRawArray, + })[0]; + + return { + key: song.key, + title: song.title, + album: + [songSingle, songOtherCd, songAlbum].filter( + album => album !== undefined + )[0]?.title ?? '', + }; + }); + + const spotifyAlbumMap = arrayToObject(spotifyAlbumArray, 'title'); + + const allFetchPromises = songWithAlbumArray.map(song => async () => { + const spotifySong = await SpotifyApi.getSpotifySong( + song.title, + song.album !== '' + ? spotifyAlbumMap[song.album]?.id ?? undefined + : undefined + ); + + return { + key: song.key, + title: song.title, + id: spotifySong?.id ?? null, + }; + }); + + const fetchPromisesWaitingChain = batchFetchPromises( + allFetchPromises, + batchSize, + timeout + ); + + const songIds: SpotifySongId[] = []; + + for (let i = 0; i < fetchPromisesWaitingChain.length; i++) { + const fetchBatch = fetchPromisesWaitingChain[i]; + const songIdsBatch = await fetchBatch(); + songIds.push(...songIdsBatch); + progressBar.update(Math.min((i + 1) * batchSize, albumsRawArray.length)); + } + + progressBar.stop(); + + return songIds; +} + +async function getSpotifyIds( + albumsRawArray: DiscographyRawArray, + songsRawArray: SongsRawArray, + options?: FetchOptions +): Promise { + const spotifyAlbumArray = await getSpotifyAlbumArray(albumsRawArray, options); + const spotifySongArray = await getSpotifySongArray({ + songsRawArray, + singlesRawArray: filterSingles(albumsRawArray), + albumsRawArray: filterAlbums(albumsRawArray), + otherCdsRawArray: filterOtherCds(albumsRawArray), + spotifyAlbumArray, + options, + }); + + return { albums: spotifyAlbumArray, songs: spotifySongArray }; +} + +export async function generateSpotifyIds( + albumsRawArray: DiscographyRawArray, + songsRawArray: SongsRawArray, + options?: FetchOptions +) { + const spotifyIds = await getSpotifyIds( + albumsRawArray, + songsRawArray, + options + ); + + writeJSONFile('./src/data/spotify.json', spotifyIds); +} diff --git a/src/server/actors/Spotify/models.ts b/src/server/actors/Spotify/models.ts new file mode 100644 index 00000000000..4278619aa89 --- /dev/null +++ b/src/server/actors/Spotify/models.ts @@ -0,0 +1,18 @@ +import { CdTitle } from 'server/actors/Discography/constants/cdTitle'; +import { SongTitle } from 'server/actors/Songs/constants/songTitle'; + +export type SpotifyAlbumId = { + key: string; + title: CdTitle; + id: string | null; +}; +export type SpotifySongId = { + key: string; + title: SongTitle; + id: string | null; +}; + +export type SpotifyIds = { + albums: SpotifyAlbumId[]; + songs: SpotifySongId[]; +}; diff --git a/src/server/app.ts b/src/server/app.ts index 91b813e849c..b5f88ddf907 100644 --- a/src/server/app.ts +++ b/src/server/app.ts @@ -1,3 +1,5 @@ +// Server entry point + import { Members } from 'server/actors/Members'; import { membersRawArray } from 'server/actors/Members/raw'; import { Songs } from 'server/actors/Songs'; diff --git a/src/server/generate-spotify.ts b/src/server/generate-spotify.ts new file mode 100644 index 00000000000..b6affc5ae44 --- /dev/null +++ b/src/server/generate-spotify.ts @@ -0,0 +1,8 @@ +import { discographyRawArray } from 'server/actors/Discography/raw/editor'; +import { songsRawArray } from 'server/actors/Songs/raw'; +import { generateSpotifyIds } from 'server/actors/Spotify'; + +generateSpotifyIds(discographyRawArray, songsRawArray, { + batchSize: 20, + timeout: 2000, +}); diff --git a/src/utils/array.ts b/src/utils/array.ts index 82fe0e17a01..0b34129ac54 100644 --- a/src/utils/array.ts +++ b/src/utils/array.ts @@ -39,3 +39,14 @@ export const filterDuplicate = < return uniqueList; }; + +export function batchArray(array: T[], batchSize: number): T[][] { + const batchedArray: T[][] = []; + const arrayLength = array.length; + + for (let i = 0; i < arrayLength; i += batchSize) { + batchedArray.push(array.slice(i, i + batchSize)); + } + + return batchedArray; +}