commit 26f86e7e971d337c6c67f4aea3f6ccb023dd0fe4 Author: sim1222 Date: Tue Jun 25 11:18:16 2024 +0900 wip diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..c706c0f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,33 @@ +module.exports = { + "env": { + "es2021": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended" + ], + "overrides": [ + { + "env": { + "node": true + }, + "files": [ + ".eslintrc.{js,cjs}" + ], + "parserOptions": { + "sourceType": "script" + } + } + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..468f82a --- /dev/null +++ b/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8eefe4c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "printWidth": 120, + "tabWidth": 2, + "useTabs": true, + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "bracketSpacing": true, + "jsxBracketSameLine": false, + "arrowParens": "avoid" +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..a5c1d7e --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "nikokari-downloader-node", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node dist/index.js", + "build": "tsc src/index.ts --outDir dist", + "dev": "tsx watch src/index.ts", + "prettier": "prettier --write \"src/**/*.ts\"", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/node": "^20.14.8", + "@typescript-eslint/eslint-plugin": "^7.13.1", + "@typescript-eslint/parser": "^7.13.1", + "eslint": "^9.5.0", + "eslint-config-prettier": "^9.1.0", + "prettier": "^3.3.2", + "tsx": "^4.15.7" + }, + "dependencies": { + "dotenv": "^16.4.5", + "m3u8-parser": "^7.1.0", + "typescript": "^5.5.2" + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..ddf6a44 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1236 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + dotenv: + specifier: ^16.4.5 + version: 16.4.5 + m3u8-parser: + specifier: ^7.1.0 + version: 7.1.0 + typescript: + specifier: ^5.5.2 + version: 5.5.2 + +devDependencies: + '@types/node': + specifier: ^20.14.8 + version: 20.14.8 + '@typescript-eslint/eslint-plugin': + specifier: ^7.13.1 + version: 7.13.1(@typescript-eslint/parser@7.13.1)(eslint@9.5.0)(typescript@5.5.2) + '@typescript-eslint/parser': + specifier: ^7.13.1 + version: 7.13.1(eslint@9.5.0)(typescript@5.5.2) + eslint: + specifier: ^9.5.0 + version: 9.5.0 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@9.5.0) + prettier: + specifier: ^3.3.2 + version: 3.3.2 + tsx: + specifier: ^4.15.7 + version: 4.15.7 + +packages: + + /@babel/runtime@7.24.7: + resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: false + + /@esbuild/aix-ppc64@0.21.5: + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.21.5: + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.21.5: + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.21.5: + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.21.5: + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.21.5: + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.21.5: + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.21.5: + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.21.5: + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.21.5: + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.21.5: + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.21.5: + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.21.5: + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.21.5: + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.21.5: + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.21.5: + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.21.5: + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.21.5: + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.21.5: + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.21.5: + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.21.5: + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.21.5: + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.21.5: + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@9.5.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 9.5.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.1: + resolution: {integrity: sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/config-array@0.16.0: + resolution: {integrity: sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@eslint/object-schema': 2.1.4 + debug: 4.3.5 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/eslintrc@3.1.0: + resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.5 + espree: 10.1.0 + globals: 14.0.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@9.5.0: + resolution: {integrity: sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@eslint/object-schema@2.1.4: + resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/retry@0.3.0: + resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} + engines: {node: '>=18.18'} + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + dev: true + + /@types/node@20.14.8: + resolution: {integrity: sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1)(eslint@9.5.0)(typescript@5.5.2): + resolution: {integrity: sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.1 + '@typescript-eslint/parser': 7.13.1(eslint@9.5.0)(typescript@5.5.2) + '@typescript-eslint/scope-manager': 7.13.1 + '@typescript-eslint/type-utils': 7.13.1(eslint@9.5.0)(typescript@5.5.2) + '@typescript-eslint/utils': 7.13.1(eslint@9.5.0)(typescript@5.5.2) + '@typescript-eslint/visitor-keys': 7.13.1 + eslint: 9.5.0 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.2) + typescript: 5.5.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.5.2): + resolution: {integrity: sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 7.13.1 + '@typescript-eslint/types': 7.13.1 + '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.2) + '@typescript-eslint/visitor-keys': 7.13.1 + debug: 4.3.5 + eslint: 9.5.0 + typescript: 5.5.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@7.13.1: + resolution: {integrity: sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==} + engines: {node: ^18.18.0 || >=20.0.0} + dependencies: + '@typescript-eslint/types': 7.13.1 + '@typescript-eslint/visitor-keys': 7.13.1 + dev: true + + /@typescript-eslint/type-utils@7.13.1(eslint@9.5.0)(typescript@5.5.2): + resolution: {integrity: sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.2) + '@typescript-eslint/utils': 7.13.1(eslint@9.5.0)(typescript@5.5.2) + debug: 4.3.5 + eslint: 9.5.0 + ts-api-utils: 1.3.0(typescript@5.5.2) + typescript: 5.5.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@7.13.1: + resolution: {integrity: sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==} + engines: {node: ^18.18.0 || >=20.0.0} + dev: true + + /@typescript-eslint/typescript-estree@7.13.1(typescript@5.5.2): + resolution: {integrity: sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 7.13.1 + '@typescript-eslint/visitor-keys': 7.13.1 + debug: 4.3.5 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.4 + semver: 7.6.2 + ts-api-utils: 1.3.0(typescript@5.5.2) + typescript: 5.5.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@7.13.1(eslint@9.5.0)(typescript@5.5.2): + resolution: {integrity: sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0) + '@typescript-eslint/scope-manager': 7.13.1 + '@typescript-eslint/types': 7.13.1 + '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.2) + eslint: 9.5.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@7.13.1: + resolution: {integrity: sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==} + engines: {node: ^18.18.0 || >=20.0.0} + dependencies: + '@typescript-eslint/types': 7.13.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /@videojs/vhs-utils@3.0.5: + resolution: {integrity: sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==} + engines: {node: '>=8', npm: '>=5'} + dependencies: + '@babel/runtime': 7.24.7 + global: 4.4.0 + url-toolkit: 2.2.5 + dev: false + + /acorn-jsx@5.3.2(acorn@8.12.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.12.0 + dev: true + + /acorn@8.12.0: + resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.1.1 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dom-walk@0.1.2: + resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} + dev: false + + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + dev: false + + /esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /eslint-config-prettier@9.1.0(eslint@9.5.0): + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 9.5.0 + dev: true + + /eslint-scope@8.0.1: + resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint-visitor-keys@4.0.0: + resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /eslint@9.5.0: + resolution: {integrity: sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@9.5.0) + '@eslint-community/regexpp': 4.10.1 + '@eslint/config-array': 0.16.0 + '@eslint/eslintrc': 3.1.0 + '@eslint/js': 9.5.0 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.3.0 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.5 + escape-string-regexp: 4.0.0 + eslint-scope: 8.0.1 + eslint-visitor-keys: 4.0.0 + espree: 10.1.0 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.1 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@10.1.0: + resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + acorn: 8.12.0 + acorn-jsx: 5.3.2(acorn@8.12.0) + eslint-visitor-keys: 4.0.0 + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.7 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + dependencies: + flat-cache: 4.0.1 + dev: true + + /fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + dev: true + + /flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /get-tsconfig@4.7.5: + resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /global@4.4.0: + resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} + dependencies: + min-document: 2.19.0 + process: 0.11.10 + dev: false + + /globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.1 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /m3u8-parser@7.1.0: + resolution: {integrity: sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==} + dependencies: + '@babel/runtime': 7.24.7 + '@videojs/vhs-utils': 3.0.5 + global: 4.4.0 + dev: false + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + dev: true + + /min-document@2.19.0: + resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==} + dependencies: + dom-walk: 0.1.2 + dev: false + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@9.0.4: + resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier@3.3.2: + resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==} + engines: {node: '>=14'} + hasBin: true + dev: true + + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + dev: false + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: false + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-api-utils@1.3.0(typescript@5.5.2): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.5.2 + dev: true + + /tsx@4.15.7: + resolution: {integrity: sha512-u3H0iSFDZM3za+VxkZ1kywdCeHCn+8/qHQS1MNoO2sONDgD95HlWtt8aB23OzeTmFP9IU4/8bZUdg58Uu5J4cg==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.21.5 + get-tsconfig: 4.7.5 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /typescript@5.5.2: + resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==} + engines: {node: '>=14.17'} + hasBin: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.1 + dev: true + + /url-toolkit@2.2.5: + resolution: {integrity: sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==} + dev: false + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..bd22bba --- /dev/null +++ b/src/api.ts @@ -0,0 +1,333 @@ +import { UserAgent, frontendId, frontendVersion } from './constants'; +import { GenerateHlsSessionResponse, GetVideoInfoResponse } from './types/response'; +import { objToUrlParams } from './utils'; + +/** + * + * @param id sm11448603 + */ +export async function getVideoInfo(id: string) { + /* + { + "meta": { + "status": 200 + }, + "data": { + "client": { + "nicosid": "1719238556.1102796574", + "watchId": "sm11448603", + "watchTrackId": "YTLfJqsNf4_1719238557065" + }, + "genre": { + "key": "music_sound", + "label": "音楽・サウンド", + "isImmoral": false, + "isDisabled": false, + "isNotSet": false + }, + "media": { + "domand": { + "videos": [ + { + "id": "video-h264-720p", + "isAvailable": true, + "label": "720p", + "bitRate": 1471070, + "width": 960, + "height": 720, + "qualityLevel": 3, + "recommendedHighestAudioQualityLevel": 1 + }, + { + "id": "video-h264-480p", + "isAvailable": true, + "label": "480p", + "bitRate": 699517, + "width": 640, + "height": 480, + "qualityLevel": 2, + "recommendedHighestAudioQualityLevel": 1 + }, + { + "id": "video-h264-360p", + "isAvailable": true, + "label": "360p", + "bitRate": 555849, + "width": 480, + "height": 360, + "qualityLevel": 1, + "recommendedHighestAudioQualityLevel": 1 + }, + { + "id": "video-h264-360p-lowest", + "isAvailable": true, + "label": "低画質", + "bitRate": 301851, + "width": 480, + "height": 360, + "qualityLevel": 0, + "recommendedHighestAudioQualityLevel": 1 + } + ], + "audios": [ + { + "id": "audio-aac-192kbps", + "isAvailable": true, + "bitRate": 178506, + "samplingRate": 44100, + "integratedLoudness": -7.9000000000000003552713678800500929355621337890625, + "truePeak": 0.59999999999999997779553950749686919152736663818359375, + "qualityLevel": 1, + "loudnessCollection": [ + { + "type": "video", + "value": 0.44157044735331252294230353072634898126125335693359375 + }, + { + "type": "pureAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "nicoadVideoIntroduce", + "value": 0.89125093813374556273032567332847975194454193115234375 + }, + { + "type": "nicoadBillboard", + "value": 1 + }, + { + "type": "marquee", + "value": 1 + } + ] + }, + { + "id": "audio-aac-64kbps", + "isAvailable": true, + "bitRate": 92609, + "samplingRate": 44100, + "integratedLoudness": -7.9000000000000003552713678800500929355621337890625, + "truePeak": 0.59999999999999997779553950749686919152736663818359375, + "qualityLevel": 0, + "loudnessCollection": [ + { + "type": "video", + "value": 0.44157044735331252294230353072634898126125335693359375 + }, + { + "type": "pureAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "nicoadVideoIntroduce", + "value": 0.89125093813374556273032567332847975194454193115234375 + }, + { + "type": "nicoadBillboard", + "value": 1 + }, + { + "type": "marquee", + "value": 1 + } + ] + } + ], + "isStoryboardAvailable": false, + "accessRightKey": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJqdGkiOiI2Njc5N2Y5ZDE3MjMzIiwiZXhwIjoxNzE5MjM5MTU3LCJ0eXAiOiJBY2Nlc3MtUmlnaHQtS2V5IiwidmlkIjoic20xMTQ0ODYwMyIsInJpZCI6Im5pY292aWRlby1zbTExNDQ4NjAzIiwiZmlkIjo2LCJ1aWQiOiI2LVlUTGZKcXNOZjRfMTcxOTIzODU1NzA2NSIsImQiOjI4OCwidiI6WyJ2aWRlby1oMjY0LTcyMHAiLCJ2aWRlby1oMjY0LTQ4MHAiLCJ2aWRlby1oMjY0LTM2MHAiLCJ2aWRlby1oMjY0LTM2MHAtbG93ZXN0Il0sImEiOlsiYXVkaW8tYWFjLTE5MmticHMiLCJhdWRpby1hYWMtNjRrYnBzIl0sInMiOmZhbHNlLCJzaCI6ZmFsc2V9.lSUYPHzPGs7ChsfBHd0jY3_3zS2yGBLtk8M2qZNIXzfjMZzTPcnDMH02iedO5MagJYIuAmculBlkQI25h4VQzg" + }, + "delivery": null, + "deliveryLegacy": null + }, + "tag": { + "items": [ + { + "name": "VOCALOID", + "isLocked": true + }, + { + "name": "mztm", + "isLocked": true + }, + { + "name": "蛍草", + "isLocked": true + }, + { + "name": "ハロ/ハワユ", + "isLocked": true + }, + { + "name": "ナノウ", + "isLocked": true + }, + { + "name": "初音ミク", + "isLocked": false + }, + { + "name": "ミクオリジナル曲", + "isLocked": false + }, + { + "name": "ほえほえP", + "isLocked": false + }, + { + "name": "VOCALOID伝説入り", + "isLocked": false + }, + { + "name": "DAM&JOY配信中", + "isLocked": false + }, + { + "name": "初音ミク名曲リンク", + "isLocked": false + } + ] + }, + "video": { + "id": "sm11448603", + "title": "【初音ミクSoft】 ハロ/ハワユ 【オリジナル】", + "description": "詞&曲 : ナノウ   (mylist\/9634824)
絵   : mztm   (http:\/\/piapro.jp\/nico0410)
動画  : 蛍草   (mylist\/18321757)


※ニンテンドー3DS『初音ミク and Future Stars Project mirai』に、この曲が収録されました。
→ http:\/\/miku.sega.jp\/mirai\/

カラオケ → http:\/\/piapro.jp\/nir580


mylist\/34696796", + "count": { + "view": 3275900, + "comment": 43870, + "mylist": 72662, + "like": 3676 + }, + "duration": 288, + "thumbnail": { + "url": "https:\/\/nicovideo.cdn.nimg.jp\/thumbnails\/11448603\/11448603", + "middleUrl": null, + "largeUrl": null, + "player": "https:\/\/img.cdn.nimg.jp\/s\/nicovideo\/thumbnails\/11448603\/11448603.original\/a960x540l?key=b754efe8996ff38d0d93d7e620d33cefa4a9e0bd495f4356b5d6744f93f11d2a", + "ogp": "https:\/\/img.cdn.nimg.jp\/s\/nicovideo\/thumbnails\/11448603\/11448603.original\/r1280x720l?key=c0ee356e6d888a5351409da0d3216e1e22accc0aa56788c425f63c3397f3a223" + }, + "registeredAt": "2010-07-20T01:13:28+09:00" + }, + "ownerNickname": "ナノウ" + } +} + */ + + const url = 'https://www.nicovideo.jp/api/watch/tmp/'; + const params = { + _frontendId: frontendId, + _frontendVersion: frontendVersion, + }; + const res = await fetch(`${url}${id}?${objToUrlParams(params)}`, { + headers: { + 'User-Agent': UserAgent, + }, + }); + + const json: GetVideoInfoResponse = await res.json(); + + return json; +} + +export async function generateHlsSession(id: string, videoInfo: GetVideoInfoResponse) { + const params = { + actionTrackId: videoInfo.data.client.watchTrackId, + _frontendId: frontendId, + _frontendVersion: frontendVersion, + }; + + const videoAudioPairs = videoInfo.data.media.domand.videos.map(video => { + const audio = videoInfo.data.media.domand.audios.find( + audio => audio.qualityLevel === video.recommendedHighestAudioQualityLevel, + ); + if (!audio) { + throw new Error('Audio not found'); + } + return [video.id, audio.id]; + }); + + const res = await fetch(`https://nvapi.nicovideo.jp/v1/tmp/watch/${id}/access-rights/hls?${objToUrlParams(params)}`, { + method: 'POST', + body: JSON.stringify({ + outputs: videoAudioPairs, + }), + headers: { + 'Content-Type': 'application/json', + 'User-Agent': UserAgent, + 'X-Access-Right-Key': videoInfo.data.media.domand.accessRightKey, + 'X-Request-With': 'https://www.nicovideo.jp', + }, + }); + + const cookie = res.headers.get('set-cookie'); + + const json: GenerateHlsSessionResponse = await res.json(); + + return { + cookie, + data: json, + }; +} diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..4232025 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,18 @@ +import { RequestBase } from './types/request'; + +export const UserAgent = 'WindowsApplication'; + +export const CriUserAgent = 'CriHttpRequest'; + +export const GuestUser = { + loginId: '4a6utb743fkuk7d2', + password: '966n4jazzuai5e4x', +}; + +export const requestBase: RequestBase = { + charset: 'UTF-8', + format: 'json', +}; + +export const frontendId = 6; +export const frontendVersion = '0.0.0'; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..1a74771 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,47 @@ +import { generateHlsSession, getVideoInfo } from './api'; +import { UserAgent } from './constants'; +import { cookieStringToObj, downloadAesM3u8, getMediaPair, objToCookieString } from './utils'; +import fs from 'fs'; + +const videoId = 'sm11448603'; +(async () => { + const videoInfo = await getVideoInfo(videoId); + // console.log(videoInfo); + const videoSession = await generateHlsSession(videoId, videoInfo); + // console.log(videoSession); + + const expireTime = new Date(videoSession.data.data.expireTime); + const currentTime = new Date(); + if (expireTime < currentTime) { + console.error('Session expired'); + return; + } + + const highestVideo = videoInfo.data.media.domand.videos.sort((a, b) => b.qualityLevel - a.qualityLevel)[0]; + const highestAudio = videoInfo.data.media.domand.audios.sort((a, b) => b.qualityLevel - a.qualityLevel)[0]; + + console.log(videoSession.data.data.contentUrl); + const cookie = objToCookieString({ + nicosid: videoInfo.data.client.nicosid, + domand_bid: cookieStringToObj(videoSession.cookie).domand_bid, + }); + const m3u8 = await fetch(videoSession.data.data.contentUrl, { + headers: { + Cookie: cookie, + 'User-Agent': UserAgent, + }, + }).then(res => res.text()); + + // console.log(m3u8); + + const mediaPair = getMediaPair(highestVideo.id, highestAudio.id, m3u8); + // console.log(JSON.stringify(mediaPair, null, 2)); + + const video = await downloadAesM3u8(mediaPair.video?.uri, cookie); + fs.writeFileSync('video.mp4', Buffer.from(video)); + + // console.log(mediaPair.audio); + const audio = await downloadAesM3u8(mediaPair.audio['Main Audio'].uri, cookie); + fs.writeFileSync('audio.mp4', Buffer.from(audio)); + +})(); diff --git a/src/types/general.ts b/src/types/general.ts new file mode 100644 index 0000000..f50f8bb --- /dev/null +++ b/src/types/general.ts @@ -0,0 +1,10 @@ +export type APIUser = { + userCode: string; + authToken: string; +}; + +export enum SearchAutoCompleteType { + Keyword = 0, + Music = 1, + Artist = 2, +} diff --git a/src/types/request.ts b/src/types/request.ts new file mode 100644 index 0000000..59209a3 --- /dev/null +++ b/src/types/request.ts @@ -0,0 +1,140 @@ +import { APIUser, SearchAutoCompleteType } from './general'; + +export type RequestBase = { + charset: 'UTF-8'; + format: 'json'; +}; + +export type LoginRequest = RequestBase & { + loginId: string; + password: string; +}; + +export type LogoutRequest = RequestBase & + APIUser & { + compId: 1; + compAuthKey: '2/Qb9R@8s*'; + }; + +export type GetMusicDetailRequest = RequestBase & + APIUser & { + compId: 1; + compAuthKey: '2/Qb9R@8s*'; + deviceId: 22; + requestNo: string; + serviceId: 1; + contractId: 1; + }; + +export type GetMusicStreamingURLRequest = RequestBase & + APIUser & { + compId: 1; + compAuthKey: '2/Qb9R@8s*'; + deviceId: 22; + serviceId: 1; + requestNo: string; + }; + +export type GetMusicRankingTermRequest = RequestBase & + APIUser & { + compId: 1; + compAuthKey: '2/Qb9R@8s*'; + dispNumber: 1000; + pageNo: 1; + targetContractId: 0; + /** + * 202311 + * YYYYmm + */ + targetStartDateFrom: number; + targetStartDateTo: number; + targetEndDateFrom: number; + targetEndDateTo: number; + sort: 1; + }; + +export type GetMusicRankingRequest = RequestBase & + APIUser & { + compId: 1; + compAuthKey: '2/Qb9R@8s*'; + dispNumber: 30; + pageNo: number; + targetContractId: 1; + musicViewsRankingId: number; + serviceId: 1; + deviceId: 22; + getCount: 200; + thumbnailType: 1; + }; + +export type SearchAutoCompleteRequest = APIUser & { + authKey: '2/Qb9R@8s*'; + compId: 1; + keyword: string; + minseiModelNum: 'M1'; + modelTypeCode: 2; + responseInfoType: SearchAutoCompleteType; +}; + +export type GetNewReleaseMusicRequest = RequestBase & + APIUser & { + compId: 1; + compAuthKey: '2/Qb9R@8s*'; + deliveryRelativeWeek: 0; + dispNumber: 30; + pageNo: number; + serviceId: 1; + deviceId: 22; + targetContractId: 1; + thumbnailType: 1; + }; + +export type SearchVariousByKeywordRequest = APIUser & { + authKey: '2/Qb9R@8s*'; + compId: 1; + dispCount: 30; + keyword: string; + minseiModelNum: 'M1'; + modelPatternCode: 0; + modelTypeCode: 2; + pageNo: number; + shopType: '02'; + sort: 2; +}; + +export type SearchMusicByKeywordRequest = APIUser & { + authKey: '2/Qb9R@8s*'; + compId: 1; + dispCount: 30; + keyword: string; + minseiModelNum: 'M1'; + modelPatternCode: 1; + modelTypeCode: 2; + pageNo: number; + shopType: '02'; + sort: 1; +}; + +export type SearchArtistByKeywordRequest = APIUser & { + authKey: '2/Qb9R@8s*'; + compId: 1; + dispCount: 30; + keyword: string; + minseiModelNum: 'M1'; + modelPatternCode: 1; + modelTypeCode: 2; + pageNo: number; + shopType: '02'; + sort: 1; +}; + +export type GetMusicListByArtistRequest = APIUser & { + artistCode: number; + authKey: '2/Qb9R@8s*'; + compId: 1; + dispCount: 30; + minseiModelNum: 'M1'; + modelTypeCode: 2; + pageNo: number; + sort: 1; +}; diff --git a/src/types/response.ts b/src/types/response.ts new file mode 100644 index 0000000..766e10d --- /dev/null +++ b/src/types/response.ts @@ -0,0 +1,576 @@ +export type GetVideoInfoResponse = JsonAPIResponse<{ + /* + { + "meta": { + "status": 200 + }, + "data": { + "client": { + "nicosid": "1719238556.1102796574", + "watchId": "sm11448603", + "watchTrackId": "YTLfJqsNf4_1719238557065" + }, + "genre": { + "key": "music_sound", + "label": "音楽・サウンド", + "isImmoral": false, + "isDisabled": false, + "isNotSet": false + }, + "media": { + "domand": { + "videos": [ + { + "id": "video-h264-720p", + "isAvailable": true, + "label": "720p", + "bitRate": 1471070, + "width": 960, + "height": 720, + "qualityLevel": 3, + "recommendedHighestAudioQualityLevel": 1 + }, + { + "id": "video-h264-480p", + "isAvailable": true, + "label": "480p", + "bitRate": 699517, + "width": 640, + "height": 480, + "qualityLevel": 2, + "recommendedHighestAudioQualityLevel": 1 + }, + { + "id": "video-h264-360p", + "isAvailable": true, + "label": "360p", + "bitRate": 555849, + "width": 480, + "height": 360, + "qualityLevel": 1, + "recommendedHighestAudioQualityLevel": 1 + }, + { + "id": "video-h264-360p-lowest", + "isAvailable": true, + "label": "低画質", + "bitRate": 301851, + "width": 480, + "height": 360, + "qualityLevel": 0, + "recommendedHighestAudioQualityLevel": 1 + } + ], + "audios": [ + { + "id": "audio-aac-192kbps", + "isAvailable": true, + "bitRate": 178506, + "samplingRate": 44100, + "integratedLoudness": -7.9000000000000003552713678800500929355621337890625, + "truePeak": 0.59999999999999997779553950749686919152736663818359375, + "qualityLevel": 1, + "loudnessCollection": [ + { + "type": "video", + "value": 0.44157044735331252294230353072634898126125335693359375 + }, + { + "type": "pureAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "nicoadVideoIntroduce", + "value": 0.89125093813374556273032567332847975194454193115234375 + }, + { + "type": "nicoadBillboard", + "value": 1 + }, + { + "type": "marquee", + "value": 1 + } + ] + }, + { + "id": "audio-aac-64kbps", + "isAvailable": true, + "bitRate": 92609, + "samplingRate": 44100, + "integratedLoudness": -7.9000000000000003552713678800500929355621337890625, + "truePeak": 0.59999999999999997779553950749686919152736663818359375, + "qualityLevel": 0, + "loudnessCollection": [ + { + "type": "video", + "value": 0.44157044735331252294230353072634898126125335693359375 + }, + { + "type": "pureAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPreroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdMidroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "pureAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "houseAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "networkAdPostroll", + "value": 0.7079457843841379105498390345019288361072540283203125 + }, + { + "type": "nicoadVideoIntroduce", + "value": 0.89125093813374556273032567332847975194454193115234375 + }, + { + "type": "nicoadBillboard", + "value": 1 + }, + { + "type": "marquee", + "value": 1 + } + ] + } + ], + "isStoryboardAvailable": false, + "accessRightKey": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJqdGkiOiI2Njc5N2Y5ZDE3MjMzIiwiZXhwIjoxNzE5MjM5MTU3LCJ0eXAiOiJBY2Nlc3MtUmlnaHQtS2V5IiwidmlkIjoic20xMTQ0ODYwMyIsInJpZCI6Im5pY292aWRlby1zbTExNDQ4NjAzIiwiZmlkIjo2LCJ1aWQiOiI2LVlUTGZKcXNOZjRfMTcxOTIzODU1NzA2NSIsImQiOjI4OCwidiI6WyJ2aWRlby1oMjY0LTcyMHAiLCJ2aWRlby1oMjY0LTQ4MHAiLCJ2aWRlby1oMjY0LTM2MHAiLCJ2aWRlby1oMjY0LTM2MHAtbG93ZXN0Il0sImEiOlsiYXVkaW8tYWFjLTE5MmticHMiLCJhdWRpby1hYWMtNjRrYnBzIl0sInMiOmZhbHNlLCJzaCI6ZmFsc2V9.lSUYPHzPGs7ChsfBHd0jY3_3zS2yGBLtk8M2qZNIXzfjMZzTPcnDMH02iedO5MagJYIuAmculBlkQI25h4VQzg" + }, + "delivery": null, + "deliveryLegacy": null + }, + "tag": { + "items": [ + { + "name": "VOCALOID", + "isLocked": true + }, + { + "name": "mztm", + "isLocked": true + }, + { + "name": "蛍草", + "isLocked": true + }, + { + "name": "ハロ/ハワユ", + "isLocked": true + }, + { + "name": "ナノウ", + "isLocked": true + }, + { + "name": "初音ミク", + "isLocked": false + }, + { + "name": "ミクオリジナル曲", + "isLocked": false + }, + { + "name": "ほえほえP", + "isLocked": false + }, + { + "name": "VOCALOID伝説入り", + "isLocked": false + }, + { + "name": "DAM&JOY配信中", + "isLocked": false + }, + { + "name": "初音ミク名曲リンク", + "isLocked": false + } + ] + }, + "video": { + "id": "sm11448603", + "title": "【初音ミクSoft】 ハロ/ハワユ 【オリジナル】", + "description": "詞&曲 : ナノウ   (mylist\/9634824)
絵   : mztm   (http:\/\/piapro.jp\/nico0410)
動画  : 蛍草   (mylist\/18321757)


※ニンテンドー3DS『初音ミク and Future Stars Project mirai』に、この曲が収録されました。
→ http:\/\/miku.sega.jp\/mirai\/

カラオケ → http:\/\/piapro.jp\/nir580


mylist\/34696796", + "count": { + "view": 3275900, + "comment": 43870, + "mylist": 72662, + "like": 3676 + }, + "duration": 288, + "thumbnail": { + "url": "https:\/\/nicovideo.cdn.nimg.jp\/thumbnails\/11448603\/11448603", + "middleUrl": null, + "largeUrl": null, + "player": "https:\/\/img.cdn.nimg.jp\/s\/nicovideo\/thumbnails\/11448603\/11448603.original\/a960x540l?key=b754efe8996ff38d0d93d7e620d33cefa4a9e0bd495f4356b5d6744f93f11d2a", + "ogp": "https:\/\/img.cdn.nimg.jp\/s\/nicovideo\/thumbnails\/11448603\/11448603.original\/r1280x720l?key=c0ee356e6d888a5351409da0d3216e1e22accc0aa56788c425f63c3397f3a223" + }, + "registeredAt": "2010-07-20T01:13:28+09:00" + }, + "ownerNickname": "ナノウ" + } + } + */ + client: { + nicosid: string; + watchId: string; + watchTrackId: string; + }; + genre: { + key: string; + label: string; + isImmoral: boolean; + isDisabled: boolean; + isNotSet: boolean; + }; + media: { + domand: { + videos: { + id: string; + isAvailable: boolean; + label: string; + bitRate: number; + width: number; + height: number; + qualityLevel: number; + recommendedHighestAudioQualityLevel: number; + }[]; + audios: { + id: string; + isAvailable: boolean; + bitRate: number; + samplingRate: number; + integratedLoudnessintegratedLoudness: number; + truePeak: number; + qualityLevel: number; + loudnessCollection: { + type: string; + value: number; + }[]; + }[]; + isStoryboardAvailable: boolean | null; + accessRightKey: string; + }; + delivery: object | null; + deliveryLegacy: object | null; + }; + tag: { + items: { + name: string; + isLocked: boolean; + }[]; + }; + video: { + id: string; + title: string; + description: string; + count: { + view: number; + comment: number; + mylist: number; + like: number; + }; + duration: number; + thumbnail: { + url: string; + middleUrl: string | null; + largeUrl: string | null; + player: string; + ogp: string; + }; + registeredAt: string; + }; + ownerNickname: string; +}>; + +export type GenerateHlsSessionResponse = JsonAPIResponse<{ + /* + { + "meta": { + "status": 201 + }, + "data": { + "contentUrl": "https://delivery.domand.nicovideo.jp/hlsbid/65361c1070b2ba9a714e625a/playlists/variants/98242b0e8e144626.m3u8?session=54eec16681fdfd3f67508da9718b863498242b0e8e14462600000000667ad11d76f33be01c6b2915&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9kZWxpdmVyeS5kb21hbmQubmljb3ZpZGVvLmpwL2hsc2JpZC82NTM2MWMxMDcwYjJiYTlhNzE0ZTYyNWEvcGxheWxpc3RzL3ZhcmlhbnRzLzk4MjQyYjBlOGUxNDQ2MjYubTN1OFxcPyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE3MTkzMjQ5NTd9fX1dfQ__&Signature=iUfOfsePAAFzZSfiT4li2OXQHjSpyNqQ3kY3YwPNwKsHR3lmxDzLmcY5ZEWvAiI5bClGS0wtoxBl2XRkY-q0nnlA5uaYLe1pmdiTxyBvo~7TiqQ7493vJGI4avhiUp9uWf6Ow3ORi1sD3s~D0ExkU7lG-GcBIsNnL3SOwabS44UnlbyMyhTzdse9FZEYnZ-qHYZd-blaNnKGbP~MsohQUrptJ80-hKpJ2GDPjHS70TLEVVvrAw0Gg-29O4meQQHKHrUhrWRs8mo3yYPOw1RDVljs~GCtjwOyilPsKjyauSVqwWH79kuJQSQGGrNaarkHrmoDIndgG12n2MRsT8lB7g__&Key-Pair-Id=K11RB80NFXU134", + "createTime": "2024-06-24T23:15:57+09:00", + "expireTime": "2024-06-25T23:15:57+09:00" + } + } + */ + contentUrl: string; + createTime: string; // 2024-06-24T23:15:57+09:00 + expireTime: string; +}>; + +export type JsonAPIResponseBase = { + meta: { + status: number; + }; +}; +export type JsonAPIResponse = JsonAPIResponseBase & { + data: T; +}; + +export type JsonListAPIResponse = JsonAPIResponse & { + list: L[]; +}; + +export type PaginationData = { + dataCount: string; + hasNext: string; + hasPreview: string; + overFlg: string; + pageCount: string; + value: string; +}; + +export type LoginByDamtomoMemberIdResponse = { + authToken: string; + damtomoId: string; +}; + +export type GetMusicDetailResponse = { + artistCode: string; + artistName: string; + contentsId: string; + contentsYomi: string; + firstLine: string; + guideVocalList: { + contentsId: string; + duet: string; + playtime: string; + }[]; + musicTypeList: { + musicTypeCode: string; + musicTypeId: string; + musicTypeName: string; + }[]; + mylist: string; + requestNo: string; + songDifficulty: string; + songTechDifficulty: string; + thumbnailPathList: { + thumbnailPath: string; + thumbnailType: string; + }[]; + + value: string; +}; + +export type GetMusicStreamingURLResponseData = { + karaokeContentsId: string; +}; + +export type GetMusicStreamingURLResponseList = { + contentsId: string; + duet: string; + highBitrateUrl: string; + lowBitrateUrl: string; +}; + +export type GetMusicRankingTermResponseList = { + from: string; + to: string; + value: string; +}; + +export type GetMusicRankingResponseList = { + artistCd: string; + artistName: string; + contentsId: string; + contentsName: string; + female: string; + lyrics: string; + male: string; + mix: string; + normal: string; + ranking: string; + requestNo: string; + url: string; + viewable: string; + views: string; +}; + +export type SearchAutoCompleteResponse = { + keyword: string; +}; + +export type SearchAutoCompleteResponseList = { + targetWord: string; +}; + +export type GetNewReleaseMusicResponseList = { + artistCd: string; + artistName: string; + contentsId: string; + contentsName: string; + contentsYomi: string; + lyrics: string; + mylist: string; + requestNo: string; + streamKaraoke: string; + streamKaraokeList: { + contentsId: string; + contentsName: string; + contentsYomi: string; + duet: string; + karaokeContentsId: string; + playTime: string; + }; + url: string; +}; + +export type SearchMusicResponse = { + hasNext: string; + hasPreview: string; + keyword: string; + overFlag: string; + pageCount: number; + pageNo: number; + totalCount: number; +}; + +export type SearchVariousByKeywordResponseList = { + animeFlag: string; + artist: string; + artistCode: number; + artistYomi: string; + damTomoMovieFlag: string; + damTomoPublicMovieFlag: string; + damTomoPublicRecordingFlag: string; + damTomoPublicVocalFlag: string; + futureReleaseFlag: string; + guideVocalFlag: string; + honninFlag: string; + kidsFlag: string; + liveFlag: string; + myListFlag: string; + newArrivalsFlag: string; + newReleaseFlag: string; + releaseDate: string; + requestNo: string; + scoreFlag: string; + shift: string; + title: string; + titleYomi: string; +}; + +export type SearchMusicByKeywordResponseList = { + animeFlag: string; + artist: string; + artistCode: number; + artistYomi: string; + damTomoMovieFlag: string; + futureReleaseFlag: string; + guideVocalFlag: string; + honninFlag: string; + kidsFlag: string; + liveFlag: string; + myListFlag: string; + newArrivalsFlag: string; + newReleaseFlag: string; + releaseDate: string; + requestNo: string; + scoreFlag: string; + shift: string; + title: string; + titleYomi: string; +}; + +export type SearchArtistByKeywordResponseList = { + artist: string; + artistCode: 40651; + artistYomi: string; + holdMusicCount: 1; +}; + +export type GetMusicListByArtistResponse = { + artist: string; + artistCode: number; + artistYomi_Kana: string; + hasNext: string; + hasPreview: string; + overFlag: string; + pageCount: number; + pageNo: number; + totalCount: number; +}; + +export type GetMusicListByArtistResponseList = { + animeFlag: string; + artist: string; + artistYomi: string; + damTomoMovieFlag: string; + damTomoPublicMovieFlag: string; + damTomoPublicRecordingFlag: string; + damTomoPublicVocalFlag: string; + futureReleaseFlag: string; + guideVocalFlag: string; + honninFlag: string; + kidsFlag: string; + liveFlag: string; + myListFlag: string; + newArrivalsFlag: string; + newReleaseFlag: string; + releaseDate: string; + requestNo: string; + scoreFlag: string; + shift: string; + title: string; + titleYomi: string; +}; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..4f147df --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,190 @@ +import { CriUserAgent, UserAgent } from './constants'; +import { Parser } from 'm3u8-parser'; +import * as crypto from 'crypto'; +import fs from 'fs'; + +type Manifest = { + allowCache: boolean; + endList: boolean; + mediaSequence: number; + discontinuitySequence: number; + targetDuration: number; + discontinuityStarts: number[]; + segments: { + duration: number; + uri: string; + timeline: number; + key: { + method: string; + uri: string; + iv: Record; + }; + }[]; + mediaGroups: { + AUDIO: { + [key: string]: { + default: string; + autoselect: boolean; + uri: string; + }; + }; + VIDEO: { + [key: string]: { + default: string; + autoselect: boolean; + uri: string; + }; + }; + 'CLOSED-CAPTIONS': { + [key: string]: { + default: string; + autoselect: boolean; + uri: string; + }; + }; + SUBTITLES: { + [key: string]: { + default: string; + autoselect: boolean; + uri: string; + }; + }; + }; + playlists: { + attributes: { + AUDIO: string; + 'FRAME-RATE': number; + RESOLUTION: { + width: number; + height: number; + }; + CODECS: string; + 'AVERAGE-BANDWIDTH': string; + BANDWIDTH: number; + }; + uri: string; + timeline: number; + }[]; +}; + +export function getMediaPair(targetVideoId: string, targetAudioId: string, m3u8: string) { + const parser = new Parser(); + parser.push(m3u8); + parser.end(); + + const manifest: Manifest = parser.manifest; + const audio = manifest.mediaGroups.AUDIO[targetAudioId]; + const video = manifest.playlists.find(playlist => playlist.uri.includes(targetVideoId)); + + return { + audio, + video, + }; +} + +export const downloadAesM3u8 = async (url: string, cookie: string) => { + const m3u8 = await fetch(url, { + headers: { + Cookie: cookie, + 'User-Agent': UserAgent, + }, + }).then(res => res.text()); + + const parser = new Parser(); + parser.push(m3u8); + parser.end(); + + const manifest: Manifest = parser.manifest; + + // console.log(JSON.stringify(manifest, null, 2)); + + const initSegment = await fetch(manifest.segments[0].map.uri, { + headers: { + Cookie: cookie, + 'User-Agent': UserAgent, + }, + }).then(res => res.arrayBuffer()); + + const iv = Buffer.alloc(Object.keys(manifest.segments[0].key.iv).length * 4); + + for (const key in manifest.segments[0].key.iv) { + iv.writeUInt32BE(manifest.segments[0].key.iv[key], parseInt(key) * 4); + } + + const key = await fetch(manifest.segments[0].key.uri, { + headers: { + Cookie: cookie, + 'User-Agent': UserAgent, + }, + }).then(res => res.arrayBuffer()); + + const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key), iv); + + const segments: Buffer[] = []; + + segments.push(Buffer.from(initSegment)); + + let i = 0; + + for (const segment of manifest.segments) { + const res = await fetch(segment.uri, { + headers: { + Cookie: cookie, + 'User-Agent': UserAgent, + }, + }); + + const buffer = await res.arrayBuffer(); + + const arrayBufferView = new Uint8Array(buffer); + + let decrypted = decipher.update(arrayBufferView); + + if (i > 0) { + decrypted = decrypted.subarray(32, decrypted.length) + } + + fs.writeFileSync(`${i}.mp4`, Buffer.from(decrypted)); + + segments.push(decrypted); + + i++; + } + + segments.push(decipher.final()); + + const blob = new Blob(segments, { type: 'video/mp4' }); + + return blob.arrayBuffer(); +}; + +export function bufferToHexString(buffer: Buffer) { + return Array.prototype.map + .call(buffer, function (x: number) { + return ('00' + x.toString(16)).slice(-2); + }) + .join(''); +} + +export function objToUrlParams(obj: Record) { + return Object.keys(obj) + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`) + .join('&'); +} + +export function objToCookieString(obj: Record) { + return Object.keys(obj) + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`) + .join('; '); +} + +export function cookieStringToObj(cookieString: string) { + const obj: Record = {}; + + cookieString.split(';').forEach(cookie => { + const [key, value] = cookie.trim().split('='); + obj[key] = value; + }); + + return obj; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7512217 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + "rootDir": "src", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +}