32 Commits
0.1.0 ... 0.7.0

Author SHA1 Message Date
bashonly
cd4e87f52e Fix sig value encoding (#54)
Co-authored-by: Simon Sawicki <contact@grub4k.dev>
2026-03-13 07:23:10 +00:00
Simon Sawicki
41ff68e6f8 Solve new player variants (#53) 2026-03-13 01:05:48 +01:00
Simon Sawicki
5bc9811c7a Fix sig solving for tce and es6 player variants (#47) 2026-02-21 17:32:45 +01:00
toddynnn
1b648c34c1 Fix sig extraction in main variant of player 4e51e895 (#48) 2026-02-16 23:27:40 +01:00
Simon Sawicki
d13ca53401 Simplify package scripts 2026-02-08 21:58:03 +01:00
Simon Sawicki
a3095891a9 Use parallel testing in CI for deno 2026-02-08 21:58:03 +01:00
Simon Sawicki
c51d14fa61 Implement extract.ts helper for quick manual testing 2026-02-08 21:58:03 +01:00
Simon Sawicki
96c417f90a More robust solver extraction
- Do not hard fail if a single extraction fails
- Do not fail if multiple solutions exist but they are the same
2026-02-08 21:58:03 +01:00
Simon Sawicki
e91d03f58a Fix sig extraction in tce variant of player c1c87fb0 (#43) 2026-01-19 22:17:25 +00:00
bashonly
32e63d577f Harden CI/CD pipeline (#40)
* Add actionlint and zizmor CI jobs

* Pin all actions to commit hashes

* Pin Deno to hash in release workflow

* Explicitly declare workflow permissions

* Avoid using actions/cache in release workflow and whenever possible
2025-12-30 18:11:18 +01:00
Simon Sawicki
83777e845d Fix windows build with bun and wrapper files (#41) 2025-12-22 19:40:53 +01:00
bashonly
f4189efdc7 Documentation and CI improvements (#39)
* README and workflow cleanup
* Bump actions/cache → v5
* Bump actions/upload-artifact → v6
* Bump actions/download-artifact → v7
2025-12-14 00:20:04 +00:00
Simon Sawicki
a0faf4144a Remove build dependency on node; add lock for every package manager (#38) 2025-12-13 23:00:13 +00:00
bashonly
06d71457ef Bump actions/checkout to v6 (#36) 2025-12-07 22:37:46 +00:00
Simon Sawicki
3e76dde153 Use pnpm binary if available (#35) 2025-12-07 19:27:53 +01:00
Simon Sawicki
4f1d91dbb1 Switch to pnpm (#33)
Fixes #29
2025-12-03 23:00:59 +00:00
bashonly
4b4ac2b896 Include test data directory in sdist (#27) 2025-11-06 22:09:05 +00:00
sepro
2655b1f55f Fix es6 and tv_es6 n func extraction (#26) 2025-10-28 21:54:48 +01:00
Simon Sawicki
877164a326 Add build time lockfile for deno (#25) 2025-10-28 00:41:18 +01:00
bashonly
25b77b7310 Fix encoding for Windows (#22) 2025-10-24 13:33:13 +13:00
bashonly
57fe708cf4 Do not run CI on tag pushes (#21) 2025-10-23 22:18:55 +00:00
bashonly
508dddae12 Finalize for release (#20) 2025-10-23 21:29:29 +00:00
bashonly
32e6af5fb2 Cache player JS test data on GitHub Actions (#19) 2025-10-23 21:18:04 +00:00
Simon Sawicki
e0560ee403 Use importlib.resources.files() api (#15) 2025-10-23 21:32:43 +02:00
sepro
b57ce18965 Add formatting/linting CI (#18) 2025-10-23 21:24:31 +02:00
bashonly
52a4f9d19a Add Python module test (#17) 2025-10-23 17:59:59 +00:00
bashonly
5d7bf090bb CI, build and documentation fixes (#14) 2025-10-22 23:01:24 +00:00
sepro
6a73fa37ba Fix sig in player 638ec5c6 (#10) 2025-10-22 20:48:45 +02:00
bashonly
bf12d399b2 Add CI testing (#11)
Co-authored-by: sepro <sepro@sepr0.com>
2025-10-22 04:53:57 +00:00
bashonly
c9bbdcb445 Improve build and test instructions in README 2025-10-21 22:46:22 -05:00
sepro
4d0d39eca4 Fix build hook for Node and Bun 2025-10-21 22:46:22 -05:00
N/Ame
398f81bbed Use a local window variable (#9) 2025-10-22 01:14:14 +02:00
33 changed files with 7507 additions and 658 deletions

567
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,567 @@
name: CI
on:
push:
branches:
- '**'
paths-ignore:
- 'README.md'
- 'LICENSE'
pull_request:
paths-ignore:
- 'README.md'
- 'LICENSE'
permissions: {}
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
ACTIONLINT_VERSION: "1.7.9"
ACTIONLINT_SHA256SUM: 233b280d05e100837f4af1433c7b40a5dcb306e3aa68fb4f17f8a7f45a7df7b4
ACTIONLINT_REPO: https://github.com/rhysd/actionlint
jobs:
actionlint:
name: Lint workflows
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.14"
- name: Install requirements
env:
ACTIONLINT_TARBALL: ${{ format('actionlint_{0}_linux_amd64.tar.gz', env.ACTIONLINT_VERSION) }}
shell: bash
run: |
sudo apt -y install shellcheck
python -m pip install -U pyflakes
curl -LO "${ACTIONLINT_REPO}/releases/download/v${ACTIONLINT_VERSION}/${ACTIONLINT_TARBALL}"
printf '%s %s' "${ACTIONLINT_SHA256SUM}" "${ACTIONLINT_TARBALL}" | sha256sum -c -
tar xvzf "${ACTIONLINT_TARBALL}" actionlint
chmod +x actionlint
- name: Run actionlint
run: |
./actionlint -color
zizmor:
name: Audit workflows
permissions:
contents: read
actions: read # Needed by zizmorcore/zizmor-action if repository is private
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
with:
advanced-security: false
persona: pedantic
version: v1.19.0
ruff_format:
name: Ruff format check
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
with:
args: "check --output-format github"
ruff_lint:
name: Ruff linting check
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
with:
args: "format --check --diff"
prettier:
name: Prettier check
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Install Deno v2.x (latest)
uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
deno-version: v2.x
- name: Install Deno requirements
run: |
deno install --frozen
- name: Run Prettier check
run: |
deno task fmt:check
eslint:
name: ESLint check
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Install Deno v2.x (latest)
uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
deno-version: v2.x
- name: Install Deno requirements
run: |
deno install --frozen
- name: Run ESLint check
run: |
deno task lint
python_tests:
name: Python tests
permissions:
contents: read
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
runner: [ubuntu-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', pypy-3.11]
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install Deno v2.x (latest)
uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
deno-version: v2.x
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: ${{ matrix.python-version }}
- name: Build project
shell: bash
run: |
# `pip install -e` omits the force-included JS, so use `build` instead
python -m pip install -U build
python -m build
- name: Unpack wheel (Linux)
if: matrix.runner == 'ubuntu-latest'
shell: bash
run: |
unzip -u dist/yt_dlp_ejs-*.whl "yt_dlp_ejs/*"
- name: Unpack wheel (Windows)
if: matrix.runner == 'windows-latest'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
Expand-Archive -Path dist/yt_dlp_ejs-*.whl -DestinationPath ./ -Force
- name: Run Python tests
timeout-minutes: 5
shell: bash
run: |
python -Werror -m unittest
prepare:
name: Prepare JS runtime tests
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Install Deno v2.x (latest)
uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
deno-version: v2.x
- name: Install Deno requirements
run: |
deno install --frozen
- name: Build control bundle
run: |
deno task bundle
- name: Generate bundle hashes
shell: bash
run: |
pushd dist
sha256sum -- yt.solver.*.js | tee SHA2-256SUMS
popd
- name: Upload bundle hashes
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: bundle-hashes
path: |
dist/SHA2-256SUMS
compression-level: 0
- name: Cache player JS files
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
with:
path: |
src/yt/solver/test/players
key: test-player-js-${{ hashFiles('src/yt/solver/test/tests.ts') }}
- name: Download player JS files
timeout-minutes: 15
run: |
deno run \
--no-prompt \
--allow-read=src/yt/solver/test/players/ \
--allow-write=src/yt/solver/test/players/ \
--allow-net=www.youtube.com \
--allow-sys=uid \
src/yt/solver/test/download.ts
- name: Upload player JS artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: player-js
path: |
src/yt/solver/test/players/*
compression-level: 0
pnpm_build:
name: Test pnpm build
needs: [prepare]
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
with:
version: 10
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
# minimum supported version
python-version: "3.10"
- name: Install Python requirements
run: |
python -m pip install -U build
- name: Test pnpm build
run: |
python -m build
- name: Verify artifact contents
shell: bash
run: |
tar -tvzf dist/yt_dlp_ejs-*.tar.gz
unzip -l dist/yt_dlp_ejs-*.whl | tee .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/core\.min\.js' .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/lib\.min\.js' .wheel_contents
- name: Install pnpm requirements
run: |
pnpm install --frozen-lockfile
- name: Bundle with pnpm
run: |
pnpm run bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: dist
name: bundle-hashes
- name: Verify bundle hashes
run: |
cd dist
sha256sum -c SHA2-256SUMS
deno_lock_check:
name: Test Deno lockfile
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
# minimum supported version
python-version: "3.10"
- name: Verify lockfile
run: |
python ./check.py
deno_build:
name: Test Deno build
needs: [prepare]
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install Deno
uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
# minimum supported version
deno-version: "2.0.0"
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
# minimum supported version
python-version: "3.10"
- name: Install Python requirements
run: |
python -m pip install -U build
- name: Test Deno build
run: |
python -m build
- name: Verify artifact contents
shell: bash
run: |
tar -tvzf dist/yt_dlp_ejs-*.tar.gz
unzip -l dist/yt_dlp_ejs-*.whl | tee .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/core\.min\.js' .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/lib\.min\.js' .wheel_contents
- name: Install Deno requirements
run: |
deno install --frozen
- name: Bundle with Deno
run: |
deno task bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: dist
name: bundle-hashes
- name: Verify bundle hashes
run: |
cd dist
sha256sum -c SHA2-256SUMS
deno_tests:
name: Run Deno tests
needs: [prepare]
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Install Deno
uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
# minimum supported version
deno-version: "2.0.0"
- name: Install Deno requirements
run: |
deno install --frozen
- name: Download player JS artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: src/yt/solver/test/players
name: player-js
- name: Run Deno tests
run: |
xargs -n 1 -P 10 deno test \
--no-prompt \
--no-check \
--allow-read=src/yt/solver/test/players/ \
--filter <<<"$(printf -- '-%s-\n' main tcc tce es5 es6 tv tv_es6 phone es6_tcc es6_tce)"
bun_build:
name: Test Bun build
needs: [prepare]
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install Bun
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
with:
# minimum supported version
bun-version: "1.0.31"
no-cache: true
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
# minimum supported version
python-version: "3.10"
- name: Install Python requirements
run: |
python -m pip install -U build
- name: Test Bun build
run: |
python -m build
- name: Verify artifact contents
shell: bash
run: |
tar -tvzf dist/yt_dlp_ejs-*.tar.gz
unzip -l dist/yt_dlp_ejs-*.whl | tee .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/core\.min\.js' .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/lib\.min\.js' .wheel_contents
- name: Install Bun requirements
run: |
bun install --frozen-lockfile
- name: Bundle with Bun
run: |
bun --bun run bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: dist
name: bundle-hashes
- name: Verify bundle hashes
run: |
cd dist
sha256sum -c SHA2-256SUMS
bun_tests:
name: Run Bun tests
needs: [prepare]
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Install Bun
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
with:
# XXX: We support 1.0.31, but test suite requires 1.2.11+
bun-version: "1.2.11"
no-cache: true
- name: Install Bun requirements
run: |
bun install --frozen-lockfile
- name: Download player JS artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: src/yt/solver/test/players
name: player-js
- name: Run Bun tests
run: |
bun test
node_build:
name: Test Node build
needs: [prepare]
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
# minimum supported version
node-version: "20.0"
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
# minimum supported version
python-version: "3.10"
- name: Install Python requirements
run: |
python -m pip install -U build
- name: Test Node build
run: |
python -m build
- name: Verify artifact contents
shell: bash
run: |
tar -tvzf dist/yt_dlp_ejs-*.tar.gz
unzip -l dist/yt_dlp_ejs-*.whl | tee .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/core\.min\.js' .wheel_contents
grep -q 'yt_dlp_ejs/yt/solver/lib\.min\.js' .wheel_contents
- name: Install Node requirements
run: |
npm ci
- name: Bundle with Node
run: |
npm run bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: dist
name: bundle-hashes
- name: Verify bundle hashes
run: |
cd dist
sha256sum -c SHA2-256SUMS
node_tests:
name: Run Node tests
needs: [prepare]
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
with:
# XXX: We support 20.0, but test suite requires 22.18+
node-version: "22.18"
- name: Install Node requirements
run: |
npm ci
- name: Download player JS artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: src/yt/solver/test/players
name: player-js
- name: Run Node tests
run: |
node --test
all_passed:
name: all_passed
needs:
- actionlint
- zizmor
- ruff_format
- ruff_lint
- prettier
- eslint
- python_tests
- pnpm_build
- deno_lock_check
- deno_build
- deno_tests
- bun_build
- bun_tests
- node_build
- node_tests
runs-on: ubuntu-latest
steps:
- name: All checks passed
run: |
echo "All checks passed!"

View File

@@ -4,21 +4,24 @@ on:
tags:
- "*"
permissions:
contents: read
permissions: {}
jobs:
build:
name: Build artifacts
permissions:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- uses: denoland/setup-deno@v2
fetch-depth: 0 # Needed for hatch-vcs versioning
persist-credentials: false
- uses: denoland/setup-deno@e95548e56dfa95d4e1a28d6f422fafe75c4c26fb # v2.0.3
with:
deno-version: v2.x
- uses: actions/setup-python@v6
deno-version: 3fbb1daddbc9333cddf0d8c0735811717dd70f7a # v2.6.3
cache: false
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
with:
python-version: "3.10"
- name: Install Python requirements
@@ -28,7 +31,7 @@ jobs:
run: |
python -m build
- name: Upload Python artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: artifacts-py
path: |
@@ -37,10 +40,10 @@ jobs:
compression-level: 0
- name: Build JavaScript artifacts
run: |
deno install
deno install --frozen
deno task bundle
- name: Upload JavaScript artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: artifacts-js
path: |
@@ -51,17 +54,16 @@ jobs:
name: Publish to PyPI
needs: [build]
if: github.repository == 'yt-dlp/ejs'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # required for PyPI trusted publishing
id-token: write # Needed for PyPI trusted publishing
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: dist
name: artifacts-py
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
verbose: true
@@ -69,14 +71,14 @@ jobs:
name: Create GitHub release
needs: [build, publish_pypi]
if: always() && !failure() && !cancelled()
runs-on: ubuntu-latest
permissions:
contents: write
contents: write # Needed by gh to publish release to Github
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
fetch-depth: 0
- uses: actions/download-artifact@v5
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
path: dist
pattern: artifacts-*
@@ -88,7 +90,7 @@ jobs:
run: |
gh release create "${TAG}" \
--title "yt-dlp-ejs ${TAG}" \
--notes-from-tag \
--generate-notes \
--verify-tag \
dist/yt.solver.*.js \
dist/yt_dlp_ejs-*.whl \

8
.github/zizmor.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
rules:
concurrency-limits:
ignore:
- release.yml # Can only be triggered by a tag pushed by a maintainer
unpinned-uses:
config:
policies:
"*": hash-pin

4
.gitignore vendored
View File

@@ -2,7 +2,5 @@
*.py[cd]
/yt_dlp_ejs/_version.py
/node_modules
/bun.lock
/deno.lock
/package-lock.json
/.idea
/.venv

View File

@@ -1,24 +1,23 @@
# yt-dlp-ejs
> [!CAUTION]
> This is currently in development
External JavaScript for yt-dlp supporting many runtimes
## Manual Installation
In the yt-dlp repository, install the python package, either directly or from url:
Install ejs into the same environment as yt-dlp:
```console
pip install git+https://github.com/yt-dlp/ejs@main
pip install -U yt-dlp-ejs
```
## Development
While this project does pin its dependencies,
it does not use lockfiles or enforce a particular package manager.
You may install dependencies using any compatible package manager.
If you notice differences between different runtimes builds
The project provides lockfiles for every supported package manager.
If you only have Python and a JS runtime, then you may instead run `./hatch_build.py`.
This will transparently invoke one of the supported JS runtimes for the build.
If you notice differences between different runtimes' builds
please open an issue [here](<https://github.com/yt-dlp/ejs/issues/new>).
### Build
@@ -26,18 +25,75 @@ please open an issue [here](<https://github.com/yt-dlp/ejs/issues/new>).
To build the Python package you need a PEP518 compatible builder.
The build hook will automatically invoke `deno`, `bun` or `node` as required.
Alternatively, to only build the JavaScript files you can run the `bundle` script manually.
Alternatively, to only build the JavaScript files you can run the `bundle` script manually:
```bash
python hatch_build.py
```
This will automatically select an available runtime and build using it.
### Tests
First, to download the player files, run `src/yt/solver/test/download.ts`.
First, make sure the project's dependencies are installed and download the player JS files:
After running that once, use any of `deno test`, `bun test` or `node --test`.
```bash
# Deno:
deno install --frozen
deno run src/yt/solver/test/download.ts
# Bun:
bun install --frozen-lockfile
bun --bun run src/yt/solver/test/download.ts
# Node 22.6+:
npm ci
node --experimental-strip-types src/yt/solver/test/download.ts
```
Then the tests can be run:
```bash
# Deno
deno test
# Bun
bun test
# Node
node --test
```
## Upgrading packages
When upgrading packages in package.json, all lockfiles must be updated simultaneously.
To do this, run the following commands:
```bash
# Upgrade packages automatically (or manually adjust versions)
pnpm upgrade --latest
# Generate base `package-lock.json`
rm -rf node_modules
npm install
# Migrate to other package managers
pnpm import
bun pm migrate --force
# Make sure to use a deno with lockfile v4 (<2.3)
deno install --lockfile-only
# Ensure that `deno.json` is the same as `package-lock.json`.
# Note: you may need to manually update the `ADDITIONAL_PACKAGES_NODE`
# and/or `ADDITIONAL_PACKAGES_DENO` variables in `./check.py`.
python check.py
```
## Licensing
This code is licensed under [Unlicense](<https://unlicense.org/>).
An exception to this are the prebuilt wheels, which contain both
An exception to this is the prebuilt wheels, which contain both
[`meriyah`](<https://github.com/meriyah/meriyah>) and [`astring`](<https://github.com/davidbonnet/astring>),
licensed under [`ISC`](<https://github.com/meriyah/meriyah?tab=ISC-1-ov-file>) and [`MIT`](<https://github.com/davidbonnet/astring?tab=MIT-1-ov-file>), respectively.

450
bun.lock Normal file
View File

@@ -0,0 +1,450 @@
{
"lockfileVersion": 1,
"workspaces": {
"": {
"name": "ejs",
"dependencies": {
"astring": "1.9.0",
"meriyah": "6.1.4",
},
"devDependencies": {
"@eslint/js": "9.38.0",
"@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-sucrase": "5.0.2",
"@rollup/plugin-terser": "0.4.4",
"@types/bun": "1.3.0",
"@types/deno": "2.5.0",
"@types/node": "24.8.1",
"eslint": "9.38.0",
"globals": "16.4.0",
"prettier": "3.6.2",
"rollup": "4.52.5",
"rollup-plugin-license": "3.6.0",
"typescript-eslint": "8.46.2",
},
},
},
"packages": {
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
"@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
"@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="],
"@eslint/js": ["@eslint/js@9.38.0", "", {}, "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
"@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
"@jridgewell/source-map": ["@jridgewell/source-map@0.3.11", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA=="],
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" } }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="],
"@rollup/plugin-sucrase": ["@rollup/plugin-sucrase@5.0.2", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "sucrase": "^3.27.0" }, "peerDependencies": { "rollup": "^2.53.1||^3.0.0||^4.0.0" } }, "sha512-4MhIVH9Dy2Hwose1/x5QMs0XF7yn9jDd/yozHqzdIrMWIolgFpGnrnVhQkqTaK1RALY/fpyrEKmwH/04vr1THA=="],
"@rollup/plugin-terser": ["@rollup/plugin-terser@0.4.4", "", { "dependencies": { "serialize-javascript": "^6.0.1", "smob": "^1.0.0", "terser": "^5.17.4" }, "peerDependencies": { "rollup": "^2.0.0||^3.0.0||^4.0.0" } }, "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A=="],
"@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" } }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
"@types/bun": ["@types/bun@1.3.0", "", { "dependencies": { "bun-types": "1.3.0" } }, "sha512-+lAGCYjXjip2qY375xX/scJeVRmZ5cY0wyHYyCYxNcdEXrQ4AOe3gACgd4iQ8ksOslJtW4VNxBJ8llUwc3a6AA=="],
"@types/deno": ["@types/deno@2.5.0", "", {}, "sha512-g8JS38vmc0S87jKsFzre+0ZyMOUDHPVokEJymSCRlL57h6f/FdKPWBXgdFh3Z8Ees9sz11qt9VWELU9Y9ZkiVw=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@24.8.1", "", { "dependencies": { "undici-types": "~7.14.0" } }, "sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q=="],
"@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="],
"@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/type-utils": "8.46.2", "@typescript-eslint/utils": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.2", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.2", "@typescript-eslint/types": "^8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2" } }, "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.46.2", "", {}, "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.2", "@typescript-eslint/tsconfig-utils": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w=="],
"acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"array-find-index": ["array-find-index@1.0.2", "", {}, "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw=="],
"astring": ["astring@1.9.0", "", { "bin": "bin/astring" }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
"bun-types": ["bun-types@1.3.0", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-u8X0thhx+yJ0KmkxuEo9HAtdfgCBaM/aI9K90VQcQioAmkVp3SG3FkwWGibUFz3WdXAdcsqOcbU40lK7tbHdkQ=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
"commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
"commenting": ["commenting@1.1.0", "", {}, "sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.38.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.1", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.38.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": "bin/eslint.js" }, "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw=="],
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"meriyah": ["meriyah@6.1.4", "", {}, "sha512-Sz8FzjzI0kN13GK/6MVEsVzMZEPvOhnmmI1lU5+/1cGOiK3QUahntrNNtdVeihrO7t9JpoH75iMNXg6R6uWflQ=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"package-name-regex": ["package-name-regex@2.0.6", "", {}, "sha512-gFL35q7kbE/zBaPA3UKhp2vSzcPYx2ecbYuwv1ucE9Il6IIgBDweBlH8D68UFGZic2MkllKa2KHCfC1IQBQUYA=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
"pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"prettier": ["prettier@3.6.2", "", { "bin": "bin/prettier.cjs" }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="],
"resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
"rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
"rollup-plugin-license": ["rollup-plugin-license@3.6.0", "", { "dependencies": { "commenting": "~1.1.0", "fdir": "^6.4.3", "lodash": "~4.17.21", "magic-string": "~0.30.0", "moment": "~2.30.1", "package-name-regex": "~2.0.6", "spdx-expression-validate": "~2.0.0", "spdx-satisfies": "~5.0.1" }, "peerDependencies": { "rollup": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0" } }, "sha512-1ieLxTCaigI5xokIfszVDRoy6c/Wmlot1fDEnea7Q/WXSR8AqOjYljHDLObAx7nFxHC2mbxT3QnTSPhaic2IYw=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"smob": ["smob@1.5.0", "", {}, "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig=="],
"source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
"source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="],
"spdx-compare": ["spdx-compare@1.0.0", "", { "dependencies": { "array-find-index": "^1.0.2", "spdx-expression-parse": "^3.0.0", "spdx-ranges": "^2.0.0" } }, "sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A=="],
"spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="],
"spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="],
"spdx-expression-validate": ["spdx-expression-validate@2.0.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0" } }, "sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg=="],
"spdx-license-ids": ["spdx-license-ids@3.0.22", "", {}, "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ=="],
"spdx-ranges": ["spdx-ranges@2.1.1", "", {}, "sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA=="],
"spdx-satisfies": ["spdx-satisfies@5.0.1", "", { "dependencies": { "spdx-compare": "^1.0.0", "spdx-expression-parse": "^3.0.0", "spdx-ranges": "^2.0.0" } }, "sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw=="],
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="],
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
"terser": ["terser@5.44.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": "bin/terser" }, "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw=="],
"thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="],
"thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="],
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
"typescript-eslint": ["typescript-eslint@8.46.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.2", "@typescript-eslint/parser": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg=="],
"undici-types": ["undici-types@7.14.0", "", {}, "sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/config-helpers/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
}
}

104
check.py Executable file
View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python
from __future__ import annotations
import json
import pathlib
import sys
ADDITIONAL_PACKAGES_NODE = {}
ADDITIONAL_PACKAGES_DENO = {
"@types/node@22.5.4": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
"undici-types@6.19.8": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
}
BASE_PATH = pathlib.Path(__file__).parent
def parse_deno() -> dict[str, str]:
path = BASE_PATH / "deno.lock"
with path.open("rb") as file:
lockfile = json.load(file)
v = lockfile["version"]
if v not in ("4", "5"):
msg = f"Unsupported lockfile version: {v} (expected 4/5)"
raise ValueError(msg)
integrities = {}
for name, info in lockfile["npm"].items():
integrity = info["integrity"]
other = integrities.get(integrity)
if other and other != name:
msg = f"Duplicate integrity for {name} and {other}: {integrity}"
raise ValueError(msg)
integrities[integrity] = name
return integrities
def parse_node() -> dict[str, str]:
path = BASE_PATH / "package-lock.json"
with path.open("rb") as file:
lockfile = json.load(file)
v = lockfile["lockfileVersion"]
if v != 3:
msg = f"Unsupported lockfile version: {v} (expected 3)"
raise ValueError(msg)
integrities = {}
for path, info in lockfile["packages"].items():
if not path:
continue
_, _, mod_name = path.rpartition("node_modules/")
version = info["version"]
name = f"{mod_name}@{version}"
integrity = info["integrity"]
other = integrities.get(integrity)
if other and other != name:
msg = f"Duplicate integrity for {name} and {other}: {integrity}"
raise ValueError(msg)
integrities[integrity] = name
return integrities
def main():
try:
packages_deno = parse_deno()
except Exception as error:
print(f"ERROR: Could not read deno lockfile: {error}", file=sys.stderr)
sys.exit(1)
try:
packages_node = parse_node()
except Exception as error:
print(f"ERROR: Could not read npm lockfile: {error}", file=sys.stderr)
sys.exit(1)
packages_deno.update({v: k for k, v in ADDITIONAL_PACKAGES_NODE.items()})
packages_node.update({v: k for k, v in ADDITIONAL_PACKAGES_DENO.items()})
differences = packages_deno.keys() ^ packages_node.keys()
if diffs_deno := differences.intersection(packages_deno):
print(
"deno => npm:",
*(f"{packages_deno[h]} ({h})" for h in diffs_deno),
sep="\n\t",
)
if diffs_node := differences.intersection(packages_node):
print(
" npm => deno:",
*(f"{packages_node[h]} ({h})" for h in diffs_node),
sep="\n\t",
)
if differences:
sys.exit(1)
if __name__ == "__main__":
main()

1155
deno.lock generated Normal file

File diff suppressed because it is too large Load Diff

14
eslint.config.js Normal file
View File

@@ -0,0 +1,14 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: { globals: globals.node },
},
tseslint.configs.recommended,
]);

93
hatch_build.py Normal file → Executable file
View File

@@ -1,38 +1,79 @@
#!/usr/bin/env python
import os
import shutil
import subprocess
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
try:
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
except ImportError:
BuildHookInterface = object
class CustomBuildHook(BuildHookInterface):
def initialize(self, version, build_data):
if shutil.which("deno"):
print("Building with deno...", flush=True)
os.environ["DENO_NO_UPDATE_CHECK"] = "1"
subprocess.run(["deno", "install"], check=True)
subprocess.run(["deno", "task", "bundle"], check=True)
elif shutil.which("bun"):
print("Building with bun...", flush=True)
subprocess.run(["bun", "install", "--frozen-lockfile"], check=True)
subprocess.run(["bun", "run", "bundle"], check=True)
elif shutil.which("npm"):
print("Building with npm...", flush=True)
# npm is a batch file (`npm.cmd`) on windows, which requires `shell=True`
requires_shell = os.name == "nt"
subprocess.run(["npm", "ci"], check=True, shell=requires_shell)
subprocess.run(["npm", "run", "bundle"], check=True, shell=requires_shell)
else:
name, cmds, env = build_bundle_cmds()
if cmds is None:
raise RuntimeError(
"One of 'deno', 'bun', or 'npm' could not be found. "
"Please install one of them to proceed with the build.")
"One of 'pnpm', 'deno', 'bun', or 'npm' could not be found. "
"Please install one of them to proceed with the build."
)
print(f"Building with {name}...")
build_data["force_include"]["dist/yt.solver.core.min.js"] = "yt_dlp_ejs/yt/solver/core.min.js"
build_data["force_include"]["dist/yt.solver.lib.min.js"] = "yt_dlp_ejs/yt/solver/lib.min.js"
for cmd in cmds:
subprocess.run(cmd, env=env, check=True)
build_data["force_include"].update(
{
"dist/yt.solver.core.min.js": "yt_dlp_ejs/yt/solver/core.min.js",
"dist/yt.solver.lib.min.js": "yt_dlp_ejs/yt/solver/lib.min.js",
}
)
def clean(self, versions):
shutil.rmtree('node_modules', ignore_errors=True)
shutil.rmtree("node_modules", ignore_errors=True)
def build_bundle_cmds():
env = os.environ.copy()
if pnpm := shutil.which("pnpm"):
name = "pnpm"
install = [pnpm, "install", "--frozen-lockfile"]
bundle = [pnpm, "run", "bundle"]
elif deno := shutil.which("deno"):
name = "deno"
env["DENO_NO_UPDATE_CHECK"] = "1"
install = [deno, "install", "--frozen"]
bundle = [deno, "task", "bundle"]
elif bun := shutil.which("bun"):
name = "bun"
install = [bun, "install", "--frozen-lockfile"]
bundle = [bun, "--bun", "run", "bundle"]
elif npm := shutil.which("npm"):
name = "npm (node)"
install = [npm, "ci"]
bundle = [npm, "run", "bundle"]
else:
return None, None, None
return name, [install, bundle], env
if __name__ == "__main__":
import sys
name, cmds, env = build_bundle_cmds()
if cmds is None:
print("ERROR: No suitable JavaScript runtime found", file=sys.stderr)
sys.exit(128)
print(f"Bundling using {name}...", file=sys.stderr)
try:
for cmd in cmds:
subprocess.check_call(cmd, env=env)
except subprocess.CalledProcessError as error:
sys.exit(error.returncode)

2788
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,21 +3,28 @@
"type": "module",
"scripts": {
"bundle": "rollup -c",
"fmt": "prettier --write \"src/**.ts\" \"package.json\" \"rollup.config.js\" \"run.ts\""
"prettier": "prettier",
"fmt": "prettier --write \"**/*.[jt]s\" \"package.json\"",
"fmt:check": "prettier --check \"**/*.[jt]s\" \"package.json\"",
"lint": "eslint src"
},
"dependencies": {
"astring": "1.9.0",
"meriyah": "6.1.4"
},
"devDependencies": {
"@eslint/js": "9.38.0",
"@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-sucrase": "5.0.2",
"@rollup/plugin-terser": "0.4.4",
"rollup-plugin-license": "3.6.0",
"@types/bun": "1.3.0",
"@types/deno": "2.5.0",
"@types/node": "24.8.1",
"eslint": "9.38.0",
"globals": "16.4.0",
"prettier": "3.6.2",
"rollup": "4.52.5",
"prettier": "3.6.2"
"rollup-plugin-license": "3.6.0",
"typescript-eslint": "8.46.2"
}
}

1782
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,11 @@ classifiers = [
]
dependencies = []
[dependency-groups]
dev = [
"ruff>=0.14.1",
]
[project.urls]
Documentation = "https://github.com/yt-dlp/ejs#readme"
Issues = "https://github.com/yt-dlp/ejs/issues"
@@ -42,9 +47,43 @@ source = "vcs"
exclude = [
"/.github/**",
"/src/yt/solver/test/players/*",
"!/src/yt/solver/test/players/.gitignore",
]
[tool.hatch.build.targets.wheel]
packages = ["yt_dlp_ejs"]
[tool.hatch.build.hooks.vcs]
version-file = "yt_dlp_ejs/_version.py"
[tool.hatch.build.targets.wheel.hooks.custom]
[tool.ruff.lint]
select = [
"C4",
"E",
"F",
"I",
"PLC",
"PLE",
"PLW",
"PYI",
"RET",
"RUF",
"SIM",
"TD",
"TID",
"W",
]
ignore = [
"TD003",
"E402",
"E501",
"PLR09",
]
[tool.ruff.lint.isort]
force-single-line = true
[tool.ruff.lint.flake8-tidy-imports]
ban-relative-imports = "all"

View File

@@ -31,7 +31,9 @@ function printHash() {
for (const [fileName, assetInfo] of Object.entries(bundle)) {
if (assetInfo.code) {
try {
const digest = createHash("sha3-512").update(assetInfo.code).digest("hex");
const digest = createHash("sha3-512")
.update(assetInfo.code)
.digest("hex");
console.log(`SHA3-512 for ${assetInfo.fileName}: ${digest}`);
} catch (err) {
console.error(`Error hashing ${fileName}:`, err.message);

View File

@@ -1,7 +1,16 @@
export type DeepPartial<T> = T extends object
? Or<{
[P in keyof T]?: DeepPartial<T[P]>;
}>
: Or<T>;
type DP<T> = T extends (infer U)[]
? DeepPartial<U>[]
: T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;
type Or<T> = T | { or: T[] };
type ValueOf<T> = T extends (infer U)[]
? U
: T extends object
? T[keyof T]
: never;
export type DeepPartial<T> =
| DP<T>
| { or: DP<T>[] }
| { anykey: DP<ValueOf<T>>[] };

View File

@@ -1,4 +1,4 @@
import { type ESTree } from "meriyah";
import { parse, type ESTree } from "meriyah";
import { type DeepPartial } from "./types.ts";
export function matchesStructure<T extends ESTree.Node>(
@@ -22,6 +22,13 @@ export function matchesStructure<T extends ESTree.Node>(
// Handle `{ or: [a, b] }`
return structure.or.some((node) => matchesStructure(obj, node));
}
if ("anykey" in structure && Array.isArray(structure.anykey)) {
// Handle `{ anykey: [a, b] }`
const haystack = Array.isArray(obj) ? obj : Object.values(obj);
return structure.anykey.every((value) =>
haystack.some((el) => matchesStructure(el, value)),
);
}
for (const [key, value] of Object.entries(structure)) {
if (!matchesStructure(obj[key as keyof typeof obj], value)) {
return false;
@@ -35,3 +42,10 @@ export function matchesStructure<T extends ESTree.Node>(
export function isOneOf<T>(value: unknown, ...of: readonly T[]): value is T {
return of.includes(value as T);
}
export function generateArrowFunction(
data: string,
): ESTree.ArrowFunctionExpression {
return (parse(data).body[0] as ESTree.ExpressionStatement)
.expression as ESTree.ArrowFunctionExpression;
}

24
src/yt/solver/extract.ts Normal file
View File

@@ -0,0 +1,24 @@
import { parse } from "meriyah";
import { getIO } from "./test/io.ts";
import { downloadCached } from "./test/utils.ts";
import { argv } from "node:process";
import { getSolutions, modifyPlayer } from "./solvers.ts";
import { generate } from "astring";
const data = await (
argv.length > 3
? () => downloadCached(argv[2], argv[3])
: async () => {
const io = await getIO();
return await io.read(argv[2]);
}
)();
const program = parse(data);
const statements = modifyPlayer(program);
const solutionMap = getSolutions(statements);
for (const solutions of Object.values(solutionMap)) {
for (const solution of solutions) {
console.log(String.raw`${generate(solution)}`);
}
}

View File

@@ -1,141 +0,0 @@
import { type ESTree } from "meriyah";
import { matchesStructure } from "../../utils.ts";
import { type DeepPartial } from "../../types.ts";
const identifier: DeepPartial<ESTree.VariableDeclaration> = {
type: "VariableDeclaration",
kind: "var",
declarations: [
{
type: "VariableDeclarator",
id: {
type: "Identifier",
},
init: {
type: "ArrayExpression",
elements: [
{
type: "Identifier",
},
],
},
},
],
};
const catchBlockBody = [
{
type: "ReturnStatement",
argument: {
type: "BinaryExpression",
left: {
type: "MemberExpression",
object: {
type: "Identifier",
},
computed: true,
property: {
type: "Literal",
},
optional: false,
},
right: {
type: "Identifier",
},
operator: "+",
},
},
] as const;
export function extract(
node: ESTree.Node,
): ESTree.ArrowFunctionExpression | null {
if (!matchesStructure(node, identifier)) {
// Fallback search for try { } catch { return X[12] + Y }
let name: string | undefined | null = null;
let block: ESTree.BlockStatement | null | undefined = null;
switch (node.type) {
case "ExpressionStatement": {
if (
node.expression.type === "AssignmentExpression" &&
node.expression.left.type === "Identifier" &&
node.expression.right.type === "FunctionExpression" &&
node.expression.right.params.length === 1
) {
name = node.expression.left.name;
block = node.expression.right.body;
}
break;
}
case "FunctionDeclaration": {
if (node.params.length === 1) {
name = node.id?.name;
block = node.body;
}
break;
}
}
if (!block || !name) {
return null;
}
const tryNode = block.body.at(-2);
if (
tryNode?.type !== "TryStatement" ||
tryNode.handler?.type !== "CatchClause"
) {
return null;
}
const catchBody = tryNode.handler!.body.body;
if (matchesStructure(catchBody, catchBlockBody)) {
return makeSolverFuncFromName(name);
}
return null;
}
if (node.type !== "VariableDeclaration") {
return null;
}
const declaration = node.declarations[0];
if (
declaration.type !== "VariableDeclarator" ||
!declaration.init ||
declaration.init.type !== "ArrayExpression" ||
declaration.init.elements.length !== 1
) {
return null;
}
const [firstElement] = declaration.init.elements;
if (!firstElement || firstElement.type !== "Identifier") {
return null;
}
return makeSolverFuncFromName(firstElement.name);
}
function makeSolverFuncFromName(name: string): ESTree.ArrowFunctionExpression {
return {
type: "ArrowFunctionExpression",
params: [
{
type: "Identifier",
name: "n",
},
],
body: {
type: "CallExpression",
callee: {
type: "Identifier",
name: name,
},
arguments: [
{
type: "Identifier",
name: "n",
},
],
optional: false,
},
async: false,
expression: false,
generator: false,
};
}

143
src/yt/solver/nsig.ts Normal file
View File

@@ -0,0 +1,143 @@
import { type ESTree } from "meriyah";
import { generate } from "astring";
import { matchesStructure, generateArrowFunction } from "../../utils.ts";
import { type DeepPartial } from "../../types.ts";
const identifier: DeepPartial<ESTree.Node> = {
or: [
{
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
or: [{ type: "Identifier" }, { type: "MemberExpression" }],
},
right: {
type: "FunctionExpression",
async: false,
},
},
},
{
type: "FunctionDeclaration",
async: false,
id: { type: "Identifier" },
},
{
type: "VariableDeclaration",
declarations: {
anykey: [
{
type: "VariableDeclarator",
init: {
type: "FunctionExpression",
async: false,
},
},
],
},
},
],
} as const;
const asdasd: DeepPartial<ESTree.ExpressionStatement> = {
type: "ExpressionStatement",
expression: {
type: "CallExpression",
callee: {
type: "MemberExpression",
object: { type: "Identifier" },
property: {},
optional: false,
},
arguments: [
{
type: "Literal",
value: "alr",
},
{
type: "Literal",
value: "yes",
},
],
optional: false,
},
};
export function extract(
node: ESTree.Node,
): ESTree.ArrowFunctionExpression | null {
if (!matchesStructure(node, identifier)) {
return null;
}
const options: {
name: ESTree.Expression;
statements: ESTree.Statement[];
}[] = [];
if (node.type === "FunctionDeclaration") {
if (node.id && node.body?.body) {
options.push({
name: node.id,
statements: node.body?.body,
});
}
} else if (node.type === "ExpressionStatement") {
if (node.expression.type !== "AssignmentExpression") {
return null;
}
const name = node.expression.left;
const body = (node.expression.right as ESTree.FunctionExpression)?.body
?.body;
if (name && body) {
options.push({
name: name,
statements: body,
});
}
} else if (node.type === "VariableDeclaration") {
for (const declaration of node.declarations) {
const name = declaration.id;
const body = (declaration.init as ESTree.FunctionExpression)?.body?.body;
if (name && body) {
options.push({
name: name,
statements: body,
});
}
}
}
for (const { name, statements } of options) {
if (matchesStructure(statements, { anykey: [asdasd] })) {
return createSolver(name);
}
}
return null;
}
function createSolver(
expression: ESTree.Expression,
): ESTree.ArrowFunctionExpression {
return generateArrowFunction(`
({sig, n}) => {
const url = (${generate(expression)})("https://youtube.com/watch?v=yt-dlp-wins", "s", sig ? encodeURIComponent(sig) : undefined);
url.set("n", n);
const proto = Object.getPrototypeOf(url);
const keys = Object.keys(proto).concat(Object.getOwnPropertyNames(proto));
for (const key of keys) {
if (!["constructor", "set", "get", "clone"].includes(key)) {
url[key]();
break;
}
}
const s = url.get("s");
return {
sig: s ? decodeURIComponent(s) : null,
n: url.get("n") ?? null,
};
}
`);
}

View File

@@ -4,11 +4,9 @@ export const setupNodes = parse(`
if (typeof globalThis.XMLHttpRequest === "undefined") {
globalThis.XMLHttpRequest = { prototype: {} };
}
if (typeof globalThis.window === "undefined") {
globalThis.window = Object.create(null);
}
const window = Object.create(null);
if (typeof URL === "undefined") {
globalThis.window.location = {
window.location = {
hash: "",
host: "www.youtube.com",
hostname: "www.youtube.com",
@@ -22,7 +20,7 @@ if (typeof URL === "undefined") {
username: "",
};
} else {
globalThis.window.location = new URL("https://www.youtube.com/watch?v=yt-dlp-wins");
window.location = new URL("https://www.youtube.com/watch?v=yt-dlp-wins");
}
if (typeof globalThis.document === "undefined") {
globalThis.document = Object.create(null);

View File

@@ -1,157 +0,0 @@
import { type ESTree } from "meriyah";
import { matchesStructure } from "../../utils.ts";
import { type DeepPartial } from "../../types.ts";
const logicalExpression: DeepPartial<ESTree.ExpressionStatement> = {
type: "ExpressionStatement",
expression: {
type: "LogicalExpression",
left: {
type: "Identifier",
},
right: {
type: "SequenceExpression",
expressions: [
{
type: "AssignmentExpression",
left: {
type: "Identifier",
},
operator: "=",
right: {
type: "CallExpression",
callee: {
type: "Identifier",
},
arguments: {
or: [
[
{ type: "Literal" },
{
type: "CallExpression",
callee: {
type: "Identifier",
name: "decodeURIComponent",
},
arguments: [{ type: "Identifier" }],
optional: false,
},
],
[
{
type: "CallExpression",
callee: {
type: "Identifier",
name: "decodeURIComponent",
},
arguments: [{ type: "Identifier" }],
optional: false,
},
],
],
},
optional: false,
},
},
{
type: "CallExpression",
},
],
},
operator: "&&",
},
};
const identifier = {
or: [
{
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "Identifier",
},
right: {
type: "FunctionExpression",
params: [{}, {}, {}],
},
},
},
{
type: "FunctionDeclaration",
params: [{}, {}, {}],
},
],
} as const;
export function extract(
node: ESTree.Node,
): ESTree.ArrowFunctionExpression | null {
if (
!matchesStructure(node, identifier as unknown as DeepPartial<ESTree.Node>)
) {
return null;
}
const block =
node.type === "ExpressionStatement" &&
node.expression.type === "AssignmentExpression" &&
node.expression.right.type === "FunctionExpression"
? node.expression.right.body
: node.type === "FunctionDeclaration"
? node.body
: null;
const relevantExpression = block?.body.at(-2);
if (!matchesStructure(relevantExpression!, logicalExpression)) {
return null;
}
if (
relevantExpression?.type !== "ExpressionStatement" ||
relevantExpression.expression.type !== "LogicalExpression" ||
relevantExpression.expression.right.type !== "SequenceExpression" ||
relevantExpression.expression.right.expressions[0].type !==
"AssignmentExpression"
) {
return null;
}
const call = relevantExpression.expression.right.expressions[0].right;
if (call.type !== "CallExpression" || call.callee.type !== "Identifier") {
return null;
}
// TODO: verify identifiers here
return {
type: "ArrowFunctionExpression",
params: [
{
type: "Identifier",
name: "sig",
},
],
body: {
type: "CallExpression",
callee: {
type: "Identifier",
name: call.callee.name,
},
arguments:
call.arguments.length === 1
? [
{
type: "Identifier",
name: "sig",
},
]
: [
call.arguments[0],
{
type: "Identifier",
name: "sig",
},
],
optional: false,
},
async: false,
expression: false,
generator: false,
};
}

View File

@@ -8,7 +8,7 @@ const io = await getIO();
for (const test of tests) {
for (const variant of test.variants ?? players.keys()) {
const path = getCachePath(test.player, variant);
await io.test(`${test.player} ${variant}`, async (assert, subtest) => {
await io.test(`-${test.player}-${variant}-`, async (assert, subtest) => {
const content = await io.read(path);
const solvers = getFromPrepared(preprocessPlayer(content));
for (const mode of ["n", "sig"] as const) {

View File

@@ -1,14 +1,45 @@
import { type ESTree, parse } from "meriyah";
import { generate } from "astring";
import { extract as extractSig } from "./sig.ts";
import { extract as extractN } from "./n.ts";
import { extract } from "./nsig.ts";
import { setupNodes } from "./setup.ts";
import { generateArrowFunction } from "../../utils.ts";
export function preprocessPlayer(data: string): string {
const ast = parse(data);
const body = ast.body;
const program = parse(data);
const plainStatements = modifyPlayer(program);
const solutions = getSolutions(plainStatements);
for (const [name, options] of Object.entries(solutions)) {
plainStatements.push({
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "MemberExpression",
computed: false,
object: {
type: "Identifier",
name: "_result",
},
property: {
type: "Identifier",
name: name,
},
optional: false,
},
right: multiTry(options),
},
});
}
const block = (() => {
program.body.splice(0, 0, ...setupNodes);
return generate(program);
}
export function modifyPlayer(program: ESTree.Program) {
const body = program.body;
const block: ESTree.BlockStatement = (() => {
switch (body.length) {
case 1: {
const func = body[0];
@@ -40,19 +71,7 @@ export function preprocessPlayer(data: string): string {
throw "unexpected structure";
})();
const found = {
n: [] as ESTree.ArrowFunctionExpression[],
sig: [] as ESTree.ArrowFunctionExpression[],
};
const plainExpressions = block.body.filter((node: ESTree.Node) => {
const n = extractN(node);
if (n) {
found.n.push(n);
}
const sig = extractSig(node);
if (sig) {
found.sig.push(sig);
}
block.body = block.body.filter((node: ESTree.Statement) => {
if (node.type === "ExpressionStatement") {
if (node.expression.type === "AssignmentExpression") {
return true;
@@ -61,43 +80,75 @@ export function preprocessPlayer(data: string): string {
}
return true;
});
block.body = plainExpressions;
for (const [name, options] of Object.entries(found)) {
// TODO: this is cringe fix plz
const unique = new Set(options.map((x) => JSON.stringify(x)));
if (unique.size !== 1) {
const message = `found ${unique.size} ${name} function possibilities`;
throw (
message +
(unique.size ? `: ${options.map((x) => generate(x)).join(", ")}` : "")
return block.body;
}
export function getSolutions(
statements: ESTree.Statement[],
): Record<string, ESTree.ArrowFunctionExpression[]> {
const found = {
n: [] as ESTree.ArrowFunctionExpression[],
sig: [] as ESTree.ArrowFunctionExpression[],
};
for (const statement of statements) {
const result = extract(statement);
if (result) {
found.n.push(
makeSolver(result, {
type: "Identifier",
name: "n",
}),
);
found.sig.push(
makeSolver(result, {
type: "Identifier",
name: "sig",
}),
);
}
plainExpressions.push({
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "MemberExpression",
computed: false,
object: {
type: "Identifier",
name: "_result",
},
property: {
type: "Identifier",
name: name,
},
},
right: options[0],
},
});
}
return found;
}
ast.body.splice(0, 0, ...setupNodes);
return generate(ast);
function makeSolver(
result: ESTree.ArrowFunctionExpression,
ident: ESTree.Identifier,
): ESTree.ArrowFunctionExpression {
return {
type: "ArrowFunctionExpression",
params: [ident],
body: {
type: "MemberExpression",
object: {
type: "CallExpression",
callee: result,
arguments: [
{
type: "ObjectExpression",
properties: [
{
type: "Property",
key: ident,
value: ident,
kind: "init",
computed: false,
method: false,
shorthand: true,
},
],
},
],
optional: false,
},
computed: false,
property: ident,
optional: false,
},
async: false,
expression: true,
generator: false,
};
}
export function getFromPrepared(code: string): {
@@ -108,3 +159,31 @@ export function getFromPrepared(code: string): {
Function("_result", code)(resultObj);
return resultObj;
}
function multiTry(
generators: ESTree.ArrowFunctionExpression[],
): ESTree.ArrowFunctionExpression {
return generateArrowFunction(`
(_input) => {
const _results = new Set();
const errors = [];
for (const _generator of ${generate({
type: "ArrayExpression",
elements: generators,
} as ESTree.Node)}) {
try {
_results.add(_generator(_input));
} catch (e) {
errors.push(e);
}
}
if (!_results.size) {
throw \`no solutions: \${errors.join(", ")}\`;
}
if (_results.size !== 1) {
throw \`invalid solutions: \${[..._results].map(x => JSON.stringify(x)).join(", ")}\`;
}
return _results.values().next().value;
}
`);
}

View File

@@ -1,24 +1,13 @@
import { players, tests } from "./tests.ts";
import { getCachePath } from "./utils.ts";
import { getIO } from "./io.ts";
const io = await getIO();
import { downloadCached } from "./utils.ts";
for (const test of tests) {
const variants = test.variants ?? players.keys();
for (const variant of variants) {
const path = getCachePath(test.player, variant);
if (await io.exists(path)) {
continue;
try {
await downloadCached(test.player, variant);
} catch (e) {
console.error(e);
}
const playerPath = players.get(variant);
const url = `https://www.youtube.com/s/player/${test.player}/${playerPath}`;
console.log("Requesting", url);
const response = await fetch(url);
if (!response.ok) {
console.error(`Failed to request ${variant} player for ${test.player}`);
continue;
}
await io.write(path, response);
}
}

View File

@@ -23,7 +23,9 @@ export async function getIO(): Promise<IO> {
}
async function _getIO(): Promise<IO> {
if (globalThis.process?.release?.name === "node") {
// Old Deno requires casting to any as globalThis lacks an index signature
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if ((globalThis as any).process?.release?.name === "node") {
// Assume node compatibility
const { access, readFile } = await import("node:fs/promises");
const { deepStrictEqual } = await import("node:assert");

View File

@@ -10,244 +10,77 @@ export const tests: {
sig?: Step[];
}[] = [
{
player: "3d3ba064",
n: [
{ input: "ZdZIqFPQK-Ty8wId", expected: "qmtUsIz04xxiNW" },
{ input: "4GMrWHyKI5cEvhDO", expected: "N9gmEX7YhKTSmw" },
],
// 20518
player: "edc3ba07",
n: [{ input: "BQoJvGBkC2nj1ZZLK-", expected: "-m-se9fQVnvEofLx" }],
sig: [
{
input:
"gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"ttJC2JfQdSswRAIgGBCxZyAfKyi0cjXCb3gqEctUw-NYdNmOEvaepit0zJAtIEsgOV2SXZjhSHMNy0NXNG_1kNyBf6HPuAuCduh-a7O",
"zwg=wgwCHlydB9zg7PMegXoVzaoAXXB8woPSNZqRUC3Pe7vAEiApVSCMlh5mt5OX-8MB=tRPyyEdAM9MPM-kPfjgTxEK0IAhIgRwE0jiz",
},
],
},
{
player: "5ec65609",
n: [{ input: "0eRGgQWJGfT5rFHFj", expected: "4SvMpDQH-vBJCw" }],
// 20521
player: "316b61b4",
n: [{ input: "IlLiA21ny7gqA2m4p37", expected: "GchRcsUC_WmnhOUVGV" }],
sig: [
{
input:
"AAJAJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grH0rTMICA1mmDc0HoXgW3CAiAQQ4=CspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ=I",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"AJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grI0rTMICA1mmDc0HoXgW3CAiAQQ4HCspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ==",
"tJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRN=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwz",
},
],
},
{
player: "6742b2b9",
// 20522
player: "74edf1a3",
n: [
{ input: "_HPB-7GFg1VTkn9u", expected: "qUAsPryAO_ByYg" },
{ input: "K1t_fcB6phzuq2SF", expected: "Y7PcOt3VE62mog" },
{ input: "IlLiA21ny7gqA2m4p37", expected: "9nRTxrbM1f0yHg" },
{ input: "eabGFpsUKuWHXGh6FR4", expected: "izmYqDEY6kl7Sg" },
{ input: "eabGF/ps%UK=uWHXGh6FR4", expected: "LACmqlhaBpiPlgE-a" },
],
sig: [
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"AJfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYu7S6SHM8EjoCACIEQnz-nKN5RgG6iUTnNJC58csYPSrnS_SzricuUMJZGM",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hzMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzl",
},
{
input:
"\x00\x01\x02%\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49",
expected:
"\x00\x01\x02%\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x40\x41\x42\x49\x44\x45\x46\x47\x48\x43",
},
],
},
{
player: "23ccdd25",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "orSsTqUaUO-j" },
],
// 20523
player: "901741ab",
n: [{ input: "BQoJvGBkC2nj1ZZLK-", expected: "UMPovvBZRh-sjb" }],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"ZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hAU6wbTvorvVVMgIARwsSdQfJAN",
"wgwCHlydB9Hg7PMegXoVzaoAXXB8woPSNZqRUC3Pe7vAEiApVSCMlhwmt5ON-8MB=5RPyyzdAM9MPM-kPfjgTxEK0IAhIgRwE0jiEJA",
},
],
},
{
player: "3597727b",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "PRwo5dDfisg0ejA2" },
],
// 20524
player: "e7573094",
n: [{ input: "IlLiA21ny7gqA2m4p37", expected: "3KuQ3235dojTSjo4" }],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"AAJfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYuMS6SHM8Ej7CACIEQnz-nKN5RgG6iUTnNJC58csYPSroS_SzricuUMJZG",
},
],
},
{
// tce causes exception even in browser
player: "3752a005",
variants: ["main", "tcc", "es5", "es6", "tv", "tv_es6", "phone", "tablet"],
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "j22ZtsqVsR0Dn" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"ZJM_ucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHG6S7uYq4TGjQXSD4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
},
],
},
{
// tce causes exception even in browser
player: "afc7785b",
variants: ["main", "tcc", "es5", "es6", "tv", "tv_es6", "phone", "tablet"],
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "j22ZtsqVsR0Dn" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"ZJM_ucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHG6S7uYq4TGjQXSD4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
},
],
},
{
// tce causes exception even in browser
player: "b9645327",
variants: ["main", "tcc", "es5", "es6", "tv", "tv_es6", "phone", "tablet"],
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "j22ZtsqVsR0Dn" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"ZJM_ucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHG6S7uYq4TGjQXSD4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
},
],
},
{
// tce causes exception even in browser
player: "035b9195",
variants: ["main", "tcc", "es5", "es6", "tv", "tv_es6", "phone", "tablet"],
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "j22ZtsqVsR0Dn" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"ZJM_ucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHG6S7uYq4TGjQXSD4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
},
],
},
{
player: "6740c111",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "AVsXYE0uE1k8e" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"JfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYu7S6SHM8EjoCACIEQnz-MKN5RgG6iUTnNJC58csYPSrnS_SzricuUMJZGn",
},
],
},
{
player: "f6a4f3bc",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "H1NKYFbhlqZ" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"JfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYM7S6SHM8EjoCACIEQnz-nKM5RgG6iUTnNJC58cNYPSrnS_SzricuUMJZGu",
},
],
},
{
player: "b66835e2",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "H1NKYFbhlqZ" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"JfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYM7S6SHM8EjoCACIEQnz-nKM5RgG6iUTnNJC58cNYPSrnS_SzricuUMJZGu",
},
],
},
{
player: "4f8fa943",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "JWWr7hDSRpMq5" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"AAJfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYu7S6SHr8EjoCACIEQnz-nKN5RgG6iUTnNZC58csYPSMnS_SzricuUM",
},
],
},
{
player: "0004de42",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "OPd7UEsCDmCw4qD0" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
expected:
"ZJMUucirzS_SnrSPYsc85MJNnTUi6GgR5NCn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQ",
},
],
},
{
player: "2b83d2e0",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "euHbygrCMLksxd" },
],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJA",
expected:
"-MGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKnMznQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJ",
"yEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyNPRt=BM8-XO5tm5hlMCSVNAiEAvpeP3CURqZJSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=g",
},
],
},
@@ -257,12 +90,12 @@ export const players = new Map([
["main", "player_ias.vflset/en_US/base.js"],
["tcc", "player_ias_tcc.vflset/en_US/base.js"],
["tce", "player_ias_tce.vflset/en_US/base.js"],
["es5", "player_es5.vflset/en_US/base.js"],
["es6", "player_es6.vflset/en_US/base.js"],
["tv", "tv-player-ias.vflset/tv-player-ias.js"],
["tv_es6", "tv-player-es6.vflset/tv-player-es6.js"],
["phone", "player-plasma-ias-phone-en_US.vflset/base.js"],
["tablet", "player-plasma-ias-tablet-en_US.vflset/base.js"],
["es6_tcc", "player_es6_tcc.vflset/en_US/base.js"],
["es6_tce", "player_es6_tce.vflset/en_US/base.js"],
] as const);
export type Variant = typeof players extends Map<infer T, unknown> ? T : never;

View File

@@ -1,5 +1,26 @@
import { type Variant } from "./tests.ts";
import { getIO } from "./io.ts";
import { players, type Variant } from "./tests.ts";
export function getCachePath(player: string, variant: Variant) {
return `src/yt/solver/test/players/${player}-${variant}`;
}
export async function downloadCached(player: string, variant: string) {
const io = await getIO();
const playerPath = players.get(variant as Variant);
if (!playerPath) {
throw `Invalid player variant: ${variant}`;
}
const path = getCachePath(player, variant as Variant);
if (!(await io.exists(path))) {
const url = `https://www.youtube.com/s/player/${player}/${playerPath}`;
console.log("Requesting", url);
const response = await fetch(url);
if (!response.ok) {
throw `Failed to request ${variant} player for ${player}`;
}
await io.write(path, response);
}
return await io.read(path);
}

0
test/__init__.py Normal file
View File

24
test/test_modules.py Normal file
View File

@@ -0,0 +1,24 @@
import unittest
from pathlib import Path
import yt_dlp_ejs.yt.solver
BASE_PATH = Path(__file__).parent.parent
CORE_PATH = BASE_PATH / "yt_dlp_ejs/yt/solver/core.min.js"
LIB_PATH = BASE_PATH / "yt_dlp_ejs/yt/solver/lib.min.js"
class TestModules(unittest.TestCase):
def test_yt_solver(self):
self.assertEqual(
yt_dlp_ejs.yt.solver.core(),
CORE_PATH.read_text(encoding="utf-8"),
)
self.assertEqual(
yt_dlp_ejs.yt.solver.lib(),
LIB_PATH.read_text(encoding="utf-8"),
)
if __name__ == "__main__":
unittest.main()

View File

@@ -1,7 +1,3 @@
from yt_dlp_ejs._version import version
from yt_dlp_ejs import yt
__all__ = [
"version",
"yt"
]
__all__ = ["version"]

View File

@@ -1,3 +0,0 @@
from . import solver
__all__ = ["solver"]

View File

@@ -2,15 +2,20 @@ import importlib.resources
import yt_dlp_ejs.yt.solver
def core() -> str:
"""
Read the contents of the JavaScript core solver bundle as string.
"""
return importlib.resources.read_text(yt_dlp_ejs.yt.solver, "core.min.js")
return (importlib.resources.files(yt_dlp_ejs.yt.solver) / "core.min.js").read_text(
encoding="utf-8"
)
def lib() -> str:
"""
Read the contents of the JavaScript library solver bundle as string.
"""
return importlib.resources.read_text(yt_dlp_ejs.yt.solver, "lib.min.js")
return (importlib.resources.files(yt_dlp_ejs.yt.solver) / "lib.min.js").read_text(
encoding="utf-8"
)