diff --git a/.vscode/settings.json b/.github/.vscode/settings.json similarity index 100% rename from .vscode/settings.json rename to .github/.vscode/settings.json diff --git a/package.json b/package.json index 97941df0..1c69ba67 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "tslib": "^2.8.1", "typescript": "^5.7.3", "typescript-eslint": "^8.22.0", - "vite": "^6.1.6" + "vite": "^6.4.1" }, "dependencies": { "@fortawesome/fontawesome-free": "^6.7.2", @@ -68,6 +68,7 @@ "chart.js": "^4.4.7", "dompurify": "^3.2.4", "escape-goat": "^4.0.0", + "jszip": "^3.10.1", "object-to-formdata": "^4.5.1", "qrcode": "^1.5.4", "query-string": "^9.1.1", diff --git a/player-worker.sh b/player-worker.sh index 3804e508..3734bcdd 100644 --- a/player-worker.sh +++ b/player-worker.sh @@ -13,6 +13,7 @@ TAILWIND_CONFIG_INSERTION=" cd .. && git clone https://github.com/PhiZone/player.git && cd player && + npx -y @inlang/paraglide-js compile --project ./project.inlang --outdir ./src/lib/paraglide && find src -type f -exec sed -i 's/\$lib/\$lib\/player/g' {} + && find src -type f -exec sed -i 's/\${base}/\/player/g' {} + && find src -type f -exec sed -i 's/{base}/\/player/g' {} + && diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 81f269df..5aedc271 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 5.75.6(svelte@5.28.6) '@vercel/analytics': specifier: ^1.5.0 - version: 1.5.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6) + version: 1.5.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6) camelcase-keys: specifier: ^9.1.3 version: 9.1.3 @@ -32,6 +32,9 @@ importers: escape-goat: specifier: ^4.0.0 version: 4.0.0 + jszip: + specifier: ^3.10.1 + version: 3.10.1 object-to-formdata: specifier: ^4.5.1 version: 4.5.1 @@ -50,13 +53,13 @@ importers: devDependencies: '@sveltejs/adapter-node': specifier: ^5.2.12 - version: 5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))) + version: 5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))) '@sveltejs/kit': specifier: ^2.20.7 - version: 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + version: 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) '@sveltejs/vite-plugin-svelte': specifier: ^5.0.3 - version: 5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + version: 5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) '@sveltekit-i18n/base': specifier: ^1.3.7 version: 1.3.7(svelte@5.28.6) @@ -158,7 +161,7 @@ importers: version: 2.4.2(svelte@5.28.6) sveltekit-superforms: specifier: ^2.23.1 - version: 2.25.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(@types/json-schema@7.0.15)(svelte@5.28.6)(typescript@5.8.3) + version: 2.25.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(@types/json-schema@7.0.15)(svelte@5.28.6)(typescript@5.8.3) tailwindcss: specifier: ^3.4.17 version: 3.4.17 @@ -172,8 +175,8 @@ importers: specifier: ^8.22.0 version: 8.32.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) vite: - specifier: ^6.1.6 - version: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) + specifier: ^6.4.1 + version: 6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) packages: @@ -558,67 +561,56 @@ packages: resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.40.2': resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.40.2': resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.40.2': resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.40.2': resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.40.2': resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.40.2': resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.40.2': resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.40.2': resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.40.2': resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.40.2': resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} @@ -1047,6 +1039,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -1493,6 +1488,9 @@ packages: resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1547,6 +1545,9 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1585,6 +1586,9 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + katex@0.16.22: resolution: {integrity: sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==} hasBin: true @@ -1606,6 +1610,9 @@ packages: libphonenumber-js@1.12.8: resolution: {integrity: sha512-f1KakiQJa9tdc7w1phC2ST+DyxWimy9c3g3yeF+84QtEanJr2K77wAmBPP22riU05xldniHsvXuflnLZ4oysqA==} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lilconfig@2.1.0: resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} engines: {node: '>=10'} @@ -1867,6 +1874,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2031,6 +2041,9 @@ packages: engines: {node: '>=14'} hasBin: true + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + property-expr@2.0.6: resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} @@ -2082,6 +2095,9 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2129,6 +2145,9 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -2154,6 +2173,9 @@ packages: set-cookie-parser@2.7.1: resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -2216,6 +2238,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2472,8 +2497,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@6.3.5: - resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -2978,17 +3003,17 @@ snapshots: dependencies: acorn: 8.14.1 - '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))': + '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))': dependencies: '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.2) '@rollup/plugin-json': 6.1.0(rollup@4.40.2) '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.2) - '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) rollup: 4.40.2 - '@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))': + '@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -3001,27 +3026,27 @@ snapshots: set-cookie-parser: 2.7.1 sirv: 3.0.1 svelte: 5.28.6 - vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) + vite: 6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) debug: 4.4.0 svelte: 5.28.6 - vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) + vite: 6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))': + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 5.28.6 - vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) - vitefu: 1.0.6(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + vite: 6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) + vitefu: 1.0.6(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) transitivePeerDependencies: - supports-color @@ -3180,9 +3205,9 @@ snapshots: '@typescript-eslint/types': 8.32.1 eslint-visitor-keys: 4.2.0 - '@vercel/analytics@1.5.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)': + '@vercel/analytics@1.5.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)': optionalDependencies: - '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) svelte: 5.28.6 '@vinejs/compiler@3.0.0': @@ -3401,6 +3426,8 @@ snapshots: cookie@0.7.2: {} + core-util-is@1.0.3: {} + cors@2.8.5: dependencies: object-assign: 4.1.1 @@ -3904,6 +3931,8 @@ snapshots: ignore@7.0.4: {} + immediate@3.0.6: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -3947,6 +3976,8 @@ snapshots: dependencies: '@types/estree': 1.0.7 + isarray@1.0.0: {} + isexe@2.0.0: {} jackspeak@3.4.3: @@ -3988,6 +4019,13 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + katex@0.16.22: dependencies: commander: 8.3.0 @@ -4008,6 +4046,10 @@ snapshots: libphonenumber-js@1.12.8: optional: true + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lilconfig@2.1.0: {} lilconfig@3.1.3: {} @@ -4291,6 +4333,8 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -4407,6 +4451,8 @@ snapshots: prettier@3.5.3: {} + process-nextick-args@2.0.1: {} + property-expr@2.0.6: optional: true @@ -4459,6 +4505,16 @@ snapshots: dependencies: pify: 2.3.0 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -4525,6 +4581,8 @@ snapshots: dependencies: mri: 1.2.0 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safer-buffer@2.1.2: {} @@ -4560,6 +4618,8 @@ snapshots: set-cookie-parser@2.7.1: {} + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} shebang-command@2.0.0: @@ -4631,6 +4691,10 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -4729,9 +4793,9 @@ snapshots: '@sveltekit-i18n/parser-default': 1.1.1 svelte: 5.28.6 - sveltekit-superforms@2.25.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(@types/json-schema@7.0.15)(svelte@5.28.6)(typescript@5.8.3): + sveltekit-superforms@2.25.0(@sveltejs/kit@2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(@types/json-schema@7.0.15)(svelte@5.28.6)(typescript@5.8.3): dependencies: - '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) + '@sveltejs/kit': 2.20.8(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)))(svelte@5.28.6)(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)) devalue: 5.1.1 memoize-weak: 1.0.2 svelte: 5.28.6 @@ -4903,7 +4967,7 @@ snapshots: vary@1.1.2: {} - vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1): + vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1): dependencies: esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) @@ -4917,9 +4981,9 @@ snapshots: jiti: 2.4.2 yaml: 2.7.1 - vitefu@1.0.6(vite@6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)): + vitefu@1.0.6(vite@6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1)): optionalDependencies: - vite: 6.3.5(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) + vite: 6.4.1(@types/node@22.15.17)(jiti@2.4.2)(yaml@2.7.1) wavesurfer.js@7.9.5: {} diff --git a/src/lib/api/chart.ts b/src/lib/api/chart.ts index 9715e534..d2803197 100644 --- a/src/lib/api/chart.ts +++ b/src/lib/api/chart.ts @@ -16,6 +16,7 @@ export enum ChartFormat { Pec, PhiZone, Phigrim, + Milthm, Unsupported, } @@ -25,6 +26,10 @@ export enum ChartLevel { IN, AT, SP, + DZ, + SK, + CB, + CL, } export interface ChartDto { diff --git a/src/lib/components/SongSubmissionForm.svelte b/src/lib/components/SongSubmissionForm.svelte index 687b07d9..432c0c91 100644 --- a/src/lib/components/SongSubmissionForm.svelte +++ b/src/lib/components/SongSubmissionForm.svelte @@ -4,7 +4,7 @@ import RangeSlider from 'svelte-range-slider-pips'; import { superForm } from 'sveltekit-superforms'; - import type { Bpm, ChartBundle, RpeJson } from '$lib/types'; + import type { Bpm, ChartBundle, MilthmJson, RpeJson } from '$lib/types'; import { page } from '$app/state'; import Tag from '$lib/components/Tag.svelte'; @@ -75,32 +75,65 @@ $effect(() => { (async () => { - const chart = await bundle.resources.chart.text(); - const { BPMList, META }: RpeJson = JSON.parse(chart); + const chartText = await bundle.resources.chart.text(); + const chartJson = JSON.parse(chartText); $form.Title = bundle.metadata.title; $form.Illustrator = bundle.metadata.illustrator ?? ''; - $form.Offset = META.offset; - - bpmList = BPMList; - - let lastBpm = 0; - let lastBeat = 0; - let lastTimeSec = 0; - bpmList.forEach((bpm, i) => { - bpm.startBeat = toBeats(bpm.startTime); - bpm.startTimeSec = - i === 0 ? lastTimeSec : lastTimeSec + ((bpm.startBeat - lastBeat) / lastBpm) * 60; - lastBpm = bpm.bpm; - lastBeat = bpm.startBeat; - lastTimeSec = bpm.startTimeSec; - }); - - const bpmArr = bpmList.map((bpm) => bpm.bpm); - $form.MinBpm = Math.min(...bpmArr); - $form.MaxBpm = Math.max(...bpmArr); - if ($form.MinBpm === $form.MaxBpm) { - $form.Bpm = $form.MinBpm; + + if ('META' in chartJson && 'BPMList' in chartJson) { + const { BPMList, META } = chartJson as RpeJson; + $form.Offset = META.offset; + + bpmList = BPMList; + + let lastBpm = 0; + let lastBeat = 0; + let lastTimeSec = 0; + bpmList.forEach((bpm, i) => { + bpm.startBeat = toBeats(bpm.startTime); + bpm.startTimeSec = + i === 0 ? lastTimeSec : lastTimeSec + ((bpm.startBeat - lastBeat) / lastBpm) * 60; + lastBpm = bpm.bpm; + lastBeat = bpm.startBeat; + lastTimeSec = bpm.startTimeSec; + }); + + const bpmArr = bpmList.map((bpm) => bpm.bpm); + $form.MinBpm = Math.min(...bpmArr); + $form.MaxBpm = Math.max(...bpmArr); + if ($form.MinBpm === $form.MaxBpm) { + $form.Bpm = $form.MinBpm; + } + } else if ('lines' in chartJson && 'meta' in chartJson) { + const { bpms, meta } = chartJson as MilthmJson; + $form.Offset = Math.round(meta.offset * 100); + + bpmList = bpms.map((b) => ({ + bpm: b.bpm, + startTime: b.time, + startBeat: 0, + startTimeSec: 0, + })); + + let lastBpm = 0; + let lastBeat = 0; + let lastTimeSec = 0; + bpmList.forEach((bpm, i) => { + bpm.startBeat = toBeats(bpm.startTime); + bpm.startTimeSec = + i === 0 ? lastTimeSec : lastTimeSec + ((bpm.startBeat - lastBeat) / lastBpm) * 60; + lastBpm = bpm.bpm; + lastBeat = bpm.startBeat; + lastTimeSec = bpm.startTimeSec; + }); + + const bpmArr = bpmList.map((bpm) => bpm.bpm); + $form.MinBpm = Math.min(...bpmArr); + $form.MaxBpm = Math.max(...bpmArr); + if ($form.MinBpm === $form.MaxBpm) { + $form.Bpm = $form.MinBpm; + } } })(); }); diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 6bfa1dff..d96de302 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -15,7 +15,17 @@ export const USER_LEVELS = [ { exp: 100000, level: 10 }, ]; -export const LEVEL_TYPES = ['EZ', 'HD', 'IN', 'AT', 'SP']; +export const LEVEL_TYPES = [ + 'EZ', + 'HD', + 'IN', + 'AT', + 'SP', + 'DZ', + 'SK', + 'CB', + 'CL', +]; export const SONG_MATCH_SCORE_THRESHOLD = 8e3; diff --git a/src/lib/translations/en-US/common.json b/src/lib/translations/en-US/common.json index 198bf944..26992385 100644 --- a/src/lib/translations/en-US/common.json +++ b/src/lib/translations/en-US/common.json @@ -105,7 +105,7 @@ "song_preview": "Song Preview", "tips": { "audio": "MP3 / FLAC / WAV / OGG, no larger than 50 MB", - "chart": "Only charts made with PE / RPE are supported, no larger than 50 MB", + "chart": "Only charts created by PE / RPE / Rain Editor are supported, no larger than 50 MB", "chart_level": "EZ / HD / IN / AT / SP / FM / Custom / ……", "illustration": "JPG / JPEG / PNG / WEBP, no larger than 10 MB, 16:9, no smaller than 960×540", "image": "JPG / JPEG / PNG / WEBP, no larger than 10 MB", diff --git a/src/lib/translations/en-US/studio.json b/src/lib/translations/en-US/studio.json index 4bde9220..0283027f 100644 --- a/src/lib/translations/en-US/studio.json +++ b/src/lib/translations/en-US/studio.json @@ -6,7 +6,7 @@ "chart_submission": "Chart Submission", "chart_submissions": "Chart Submissions", "choose_chart": "Choose Chart", - "choose_chart_description": "Please select a compressed file (.pez or .zip) containing an RPE or PE chart. The file must include at least a chart file, an audio file, and an Illustration file.", + "choose_chart_description": "Only PE / RPE / Rain Editor created charts are supported, not larger than 50 MB", "collaboration": "Collaboration", "collaborations": "Collaborations", "collection_admission": "Collection Admission", @@ -30,6 +30,7 @@ }, "session": { "chart_confirmation_notice": "Please verify the chart's performance and offset using PhiZone Player for this step. Once confirmed, click the \"Export\" button.", + "milthm_preview_not_supported": "Milthm chart preview is not supported yet", "pci_description": "Your submission might be restricted or rejected due to copyright issues. Listed are resource records whose copyright may have been potentially infringed by your submission.", "potential_copyright_infringements": "Potential Infringing Songs", "potential_song_duplicates": "Potential Duplicate Songs", diff --git a/src/lib/translations/ja-JP/common.json b/src/lib/translations/ja-JP/common.json index 3fc995ee..38291a4e 100644 --- a/src/lib/translations/ja-JP/common.json +++ b/src/lib/translations/ja-JP/common.json @@ -105,7 +105,7 @@ "song_preview": "曲目预览", "tips": { "audio": "50 MB 又は更に小さい MP3 / FLAC / WAV / OGG", - "chart": "仅支持 PE / RPE 创作的谱面,不大于 50 MB", + "chart": "仅支持 PE / RPE / Rain Editor 创作的谱面,不大于 50 MB", "chart_level": "EZ / HD / IN / AT / SP / FM / Custom / ……", "illustration": "10 MB 又は更に小さい JPG / JPEG / PN / WEBP、比は 16:9、解像度は960x540 未満でなければなりません", "image": "10 MB 又は更に小さい JPG / JPEG / PNG / WEBP", diff --git a/src/lib/translations/ja-JP/studio.json b/src/lib/translations/ja-JP/studio.json index bbf1bbd5..2b6046a1 100644 --- a/src/lib/translations/ja-JP/studio.json +++ b/src/lib/translations/ja-JP/studio.json @@ -6,7 +6,7 @@ "chart_submission": "谱面稿件", "chart_submissions": "谱面稿件", "choose_chart": "选择谱面", - "choose_chart_description": "请在此处选择包含 RPE 或 PE 谱面的压缩包(.pez 或 .zip),并且须至少包含谱面、曲目、曲绘文件。", + "choose_chart_description": "PE / RPE / Rain Editor で作成された譜面のみ対応しています。50MB以下。", "collaboration": "合作邀请", "collaborations": "合作邀请", "collection_admission": "合集收录申请", @@ -30,6 +30,7 @@ }, "session": { "chart_confirmation_notice": "请在本步骤使用 PhiZone Player 确认谱面的表现情况与谱面延迟。如完成确认,请点击“导出”按钮。", + "milthm_preview_not_supported": "Milthmの譜面プレビューはまだサポートされていません", "pci_description": "你提交的稿件可能会因版权问题而受到限制或被退回。此处列出了你的稿件可能侵犯其版权的资源记录。", "potential_copyright_infringements": "疑似侵权的曲目", "potential_song_duplicates": "疑似重复的曲目", diff --git a/src/lib/translations/zh-CN/chart.json b/src/lib/translations/zh-CN/chart.json index 1b0fef08..c218730e 100644 --- a/src/lib/translations/zh-CN/chart.json +++ b/src/lib/translations/zh-CN/chart.json @@ -33,7 +33,8 @@ "1": "PhiEditer 谱面", "2": "PhiZone 谱面", "3": "Phigrim 谱面", - "4": "未受支持" + "4": "Milthm 谱面", + "5": "未受支持" }, "gameplay": "游玩体验", "highest_difficulty": "最高定数", @@ -56,7 +57,11 @@ "1": "HD", "2": "IN", "3": "AT", - "4": "SP" + "4": "SP", + "5": "DZ", + "6": "SK", + "7": "CB", + "8": "CL" }, "lowest_difficulty": "最低定数", "lowest_note_count": "最低物量", diff --git a/src/lib/translations/zh-CN/common.json b/src/lib/translations/zh-CN/common.json index f6d00f02..e52a1ca1 100644 --- a/src/lib/translations/zh-CN/common.json +++ b/src/lib/translations/zh-CN/common.json @@ -105,7 +105,7 @@ "song_preview": "曲目预览", "tips": { "audio": "50 MB 或更小的 MP3 / FLAC / WAV / OGG", - "chart": "仅支持 PE / RPE 创作的谱面,不大于 50 MB", + "chart": "仅支持 PE / RPE / Rain Editor 创作的谱面,不大于 50 MB", "chart_level": "EZ / HD / IN / AT / SP / FM / Custom / ……", "illustration": "10 MB 或更小的 JPG / JPEG / PNG / WEBP,长宽比须为 16:9,分辨率不得低于 960×540", "image": "10 MB 或更小的 JPG / JPEG / PNG / WEBP", diff --git a/src/lib/translations/zh-CN/studio.json b/src/lib/translations/zh-CN/studio.json index bbf1bbd5..b5c3cb06 100644 --- a/src/lib/translations/zh-CN/studio.json +++ b/src/lib/translations/zh-CN/studio.json @@ -6,7 +6,7 @@ "chart_submission": "谱面稿件", "chart_submissions": "谱面稿件", "choose_chart": "选择谱面", - "choose_chart_description": "请在此处选择包含 RPE 或 PE 谱面的压缩包(.pez 或 .zip),并且须至少包含谱面、曲目、曲绘文件。", + "choose_chart_description": "仅支持 PE / RPE / Rain Editor 创作的谱面,不大于 50 MB", "collaboration": "合作邀请", "collaborations": "合作邀请", "collection_admission": "合集收录申请", @@ -30,6 +30,7 @@ }, "session": { "chart_confirmation_notice": "请在本步骤使用 PhiZone Player 确认谱面的表现情况与谱面延迟。如完成确认,请点击“导出”按钮。", + "milthm_preview_not_supported": "暂不支持 Milthm 谱面预览", "pci_description": "你提交的稿件可能会因版权问题而受到限制或被退回。此处列出了你的稿件可能侵犯其版权的资源记录。", "potential_copyright_infringements": "疑似侵权的曲目", "potential_song_duplicates": "疑似重复的曲目", @@ -128,4 +129,4 @@ "upload_chart": "上传谱面", "upload_song": "上传曲目", "view_guide": "查看指南" -} +} \ No newline at end of file diff --git a/src/lib/translations/zh-TW/common.json b/src/lib/translations/zh-TW/common.json index 8d0ee178..7624eb84 100644 --- a/src/lib/translations/zh-TW/common.json +++ b/src/lib/translations/zh-TW/common.json @@ -105,7 +105,7 @@ "song_preview": "曲目預覽", "tips": { "audio": "50 MB 或更小的 MP3 / FLAC / WAV / OGG", - "chart": "僅支援 PE / RPE 創作的譜面,不大於 50 MB", + "chart": "僅支援 PE / RPE / Rain Editor 創作的譜面,不大於 50 MB", "chart_level": "EZ / HD / IN / AT / SP / FM / Custom / ……", "illustration": "10 MB 或更小的 JPG / JPEG / PNG / WEBP,長寬比必須為 16:9,解析度不得低於 960×540", "image": "10 MB 或更小的 JPG / JPEG / PNG / WEBP", diff --git a/src/lib/translations/zh-TW/studio.json b/src/lib/translations/zh-TW/studio.json index 0be46205..ab407cb2 100644 --- a/src/lib/translations/zh-TW/studio.json +++ b/src/lib/translations/zh-TW/studio.json @@ -6,7 +6,7 @@ "chart_submission": "譜面稿件", "chart_submissions": "譜面稿件", "choose_chart": "选择谱面", - "choose_chart_description": "请在此处选择包含 RPE 或 PE 谱面的压缩包(.pez 或 .zip),并且须至少包含谱面、曲目、曲绘文件。", + "choose_chart_description": "僅支持 PE / RPE / Rain Editor 創作的譜面,不大於 50 MB", "collaboration": "合作邀請", "collaborations": "合作邀請", "collection_admission": "合集收錄申請", @@ -30,6 +30,7 @@ }, "session": { "chart_confirmation_notice": "请在本步骤使用 PhiZone Player 确认谱面的表现情况与谱面延迟。如完成确认,请点击“导出”按钮。", + "milthm_preview_not_supported": "暫不支援 Milthm 譜面預覽", "pci_description": "你提交的稿件可能会因版权问题而受到限制或被退回。此处列出了你的稿件可能侵犯其版权的资源记录。", "potential_copyright_infringements": "疑似侵权的曲目", "potential_song_duplicates": "疑似重复的曲目", diff --git a/src/lib/types.ts b/src/lib/types.ts index 333aaff5..1add5e59 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -49,3 +49,18 @@ export interface RpeJson { BPMList: Bpm[]; META: RpeMeta; } + +export interface MilthmMeta { + offset: number; +} + +export interface MilthmBpm { + bpm: number; + time: [number, number, number]; +} + +export interface MilthmJson { + meta: MilthmMeta; + bpms: MilthmBpm[]; + lines: unknown[]; +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 87a01ca9..3d455284 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -227,6 +227,16 @@ export const getLevelColor = (type: number | undefined) => { return 'btn-primary'; case 3: return ''; + case 4: + return 'btn-accent'; + case 5: + return 'btn-success'; + case 6: + return 'btn-info'; + case 7: + return 'btn-error'; + case 8: + return ''; default: return 'btn-accent'; } diff --git a/src/routes/(app)/studio/chart-submissions/new/+page.svelte b/src/routes/(app)/studio/chart-submissions/new/+page.svelte index e569a3dd..cc913024 100644 --- a/src/routes/(app)/studio/chart-submissions/new/+page.svelte +++ b/src/routes/(app)/studio/chart-submissions/new/+page.svelte @@ -2,6 +2,7 @@ import { createQuery } from '@tanstack/svelte-query'; import { superForm } from 'sveltekit-superforms'; + import { browser } from '$app/environment'; import type { SongSubmissionDto } from '$lib/api'; import type { SongDto } from '$lib/api/song'; @@ -52,7 +53,7 @@ createQuery( api.song.submission.listAll( { order: ['dateCreated'], desc: [true] }, - { enabled: !songSwitch }, + { enabled: browser && !songSwitch }, ), ), ); @@ -194,7 +195,7 @@ type="file" id="file" name="File" - accept=".json, .pec" + accept=".json, .pec, .zip" class={`w-1/3 file:mr-4 file:py-2 file:border-0 file:btn ${ $errors.File ? 'input-error file:btn-error' diff --git a/src/routes/(app)/studio/new-submission/+page.server.ts b/src/routes/(app)/studio/new-submission/+page.server.ts index 0b6ce0ac..6a7fdccb 100644 --- a/src/routes/(app)/studio/new-submission/+page.server.ts +++ b/src/routes/(app)/studio/new-submission/+page.server.ts @@ -1,4 +1,4 @@ -/* eslint-disable prefer-const */ +import type { Actions, PageServerLoad } from './$types'; import { fail } from '@sveltejs/kit'; import { superValidate } from 'sveltekit-superforms'; import { zod } from 'sveltekit-superforms/adapters'; @@ -8,70 +8,75 @@ import API from '$lib/api'; import { ChartLevel } from '$lib/api/chart'; import { Accessibility, EditionType, ResponseDtoStatus } from '$lib/api/types'; import { TAG_JOINER } from '$lib/constants'; -import { t } from '$lib/translations/config'; +import { t, loadTranslations } from '$lib/translations/config'; -const songSchema = z - .object({ +const createSongSchema = (locale: string) => + z + .object({ + Id: z.string(), + Title: z.string().max(100, t.get('error.ValueTooLong', { locale } as any)), + EditionType: z.nativeEnum(EditionType), + Edition: z.string().optional(), + AuthorName: z.string(), + Illustrator: z.string(), + Description: z.string().optional(), + Accessibility: z.nativeEnum(Accessibility), + Bpm: z.number(), + MinBpm: z.number(), + MaxBpm: z.number(), + Lyrics: z.string().optional(), + License: z.custom().optional(), + OriginalityProof: z.custom().optional(), + Offset: z.number(), + PreviewStart: z.string(), + PreviewEnd: z.string(), + Tags: z.string(), + }) + .refine(({ EditionType, Edition }) => EditionType === 0 || Edition, { + message: t.get('error.FieldEmpty', { locale } as any), + path: ['Edition'], + }) + .refine(({ EditionType, License }) => EditionType !== 3 || License, { + message: t.get('error.FieldEmpty', { locale } as any), + path: ['License'], + }) + .refine(({ MinBpm, Bpm }) => MinBpm <= Bpm, { + message: t.get('studio.submission.min_bpm_error', { locale } as any), + path: ['MinBpm'], + }) + .refine(({ MaxBpm, Bpm }) => MaxBpm >= Bpm, { + message: t.get('studio.submission.max_bpm_error', { locale } as any), + path: ['MaxBpm'], + }); + +const createChartSchema = (locale: string) => + z.object({ Id: z.string(), - Title: z.string().max(100, t.get('error.ValueTooLong')), - EditionType: z.nativeEnum(EditionType), - Edition: z.string().optional(), + Title: z.string().max(100, t.get('error.ValueTooLong', { locale } as any)).optional(), + LevelType: z.nativeEnum(ChartLevel), + Level: z.string(), + Difficulty: z.number(), + File: z.custom(), AuthorName: z.string(), - Illustrator: z.string(), + Illustration: z.custom().optional(), + Illustrator: z.string().optional(), Description: z.string().optional(), Accessibility: z.nativeEnum(Accessibility), - Bpm: z.number(), - MinBpm: z.number(), - MaxBpm: z.number(), - Lyrics: z.string().optional(), - License: z.custom().optional(), - OriginalityProof: z.custom().optional(), - Offset: z.number(), - PreviewStart: z.string(), - PreviewEnd: z.string(), + IsRanked: z.boolean(), + SongId: z.string(), + SongSubmissionId: z.string(), Tags: z.string(), - }) - .refine(({ EditionType, Edition }) => EditionType === 0 || Edition, { - message: t.get('error.FieldEmpty'), - path: ['Edition'], - }) - .refine(({ EditionType, License }) => EditionType !== 3 || License, { - message: t.get('error.FieldEmpty'), - path: ['License'], - }) - .refine(({ MinBpm, Bpm }) => MinBpm <= Bpm, { - message: t.get('studio.submission.min_bpm_error'), - path: ['MinBpm'], - }) - .refine(({ MaxBpm, Bpm }) => MaxBpm >= Bpm, { - message: t.get('studio.submission.max_bpm_error'), - path: ['MaxBpm'], }); -const chartSchema = z.object({ - Id: z.string(), - Title: z.string().max(100, t.get('error.ValueTooLong')).optional(), - LevelType: z.nativeEnum(ChartLevel), - Level: z.string(), - Difficulty: z.number(), - File: z.custom(), - AuthorName: z.string(), - Illustration: z.custom().optional(), - Illustrator: z.string().optional(), - Description: z.string().optional(), - Accessibility: z.nativeEnum(Accessibility), - IsRanked: z.boolean(), - SongId: z.string(), - SongSubmissionId: z.string(), - Tags: z.string(), -}); - -type SongSchema = z.infer; -type ChartSchema = z.infer; +type SongSchema = z.infer>; +type ChartSchema = z.infer>; -export const load = async () => { - const songForm = await superValidate(zod(songSchema)); - const chartForm = await superValidate(zod(chartSchema)); +export const load: PageServerLoad = async ({ cookies }) => { + const locale = cookies.get('language') || 'zh-CN'; + await loadTranslations(locale, 'studio'); + await loadTranslations(locale, 'error'); + const songForm = await superValidate(zod(createSongSchema(locale))); + const chartForm = await superValidate(zod(createChartSchema(locale))); return { songForm, chartForm }; }; @@ -79,11 +84,14 @@ const parsePreviewTime = (time: string) => { return /^\d{1,2}:\d{1,2}.\d+$/g.test(time) ? `00:${time}` : time; }; -export const actions = { - song: async ({ request, locals, fetch }) => { +export const actions: Actions = { + song: async ({ request, locals, fetch, cookies }) => { + const locale = cookies.get('language') || 'zh-CN'; + await loadTranslations(locale, 'studio'); + await loadTranslations(locale, 'error'); const api = new API(fetch, locals.accessToken); const formData = await request.formData(); - const form = await superValidate(formData, zod(songSchema)); + const form = await superValidate(formData, zod(createSongSchema(locale))); if (!form.valid) { return fail(400, { form }); @@ -140,15 +148,15 @@ export const actions = { ); form.valid = false; if (error.status === ResponseDtoStatus.ErrorBrief) { - form.message = t.get(`error.${error.code}`); + form.message = t.get(`error.${error.code}`, { locale } as any); } else if (error.status === ResponseDtoStatus.ErrorWithMessage) { form.message = error.message; } else if (error.status === ResponseDtoStatus.ErrorDetailed) { - form.message = t.get(`error.${error.code}`); + form.message = t.get(`error.${error.code}`, { locale } as any); form.errors = {}; error.errors.forEach(({ field, errors }) => { form.errors[field as keyof SongSchema] = errors.map((value) => { - return t.get(`error.${value}`); + return t.get(`error.${value}`, { locale } as any); }); }); } @@ -164,10 +172,13 @@ export const actions = { } } }, - chart: async ({ request, locals, fetch }) => { + chart: async ({ request, locals, fetch, cookies }) => { + const locale = cookies.get('language') || 'zh-CN'; + await loadTranslations(locale, 'studio'); + await loadTranslations(locale, 'error'); const api = new API(fetch, locals.accessToken); const formData = await request.formData(); - const form = await superValidate(formData, zod(chartSchema)); + const form = await superValidate(formData, zod(createChartSchema(locale))); if (!form.valid) { return fail(400, { form }); @@ -189,15 +200,15 @@ export const actions = { ); form.valid = false; if (error.status === ResponseDtoStatus.ErrorBrief) { - form.message = t.get(`error.${error.code}`); + form.message = t.get(`error.${error.code}`, { locale } as any); } else if (error.status === ResponseDtoStatus.ErrorWithMessage) { form.message = error.message; } else if (error.status === ResponseDtoStatus.ErrorDetailed) { - form.message = t.get(`error.${error.code}`); + form.message = t.get(`error.${error.code}`, { locale } as any); form.errors = {}; error.errors.forEach(({ field, errors }) => { form.errors[field as keyof ChartSchema] = errors.map((value) => { - return t.get(`error.${value}`); + return t.get(`error.${value}`, { locale } as any); }); }); } diff --git a/src/routes/(app)/studio/new-submission/+page.svelte b/src/routes/(app)/studio/new-submission/+page.svelte index 76525097..04686e34 100644 --- a/src/routes/(app)/studio/new-submission/+page.svelte +++ b/src/routes/(app)/studio/new-submission/+page.svelte @@ -1,5 +1,6 @@