mirror of
https://github.com/yt-dlp/ejs.git
synced 2026-06-23 23:09:12 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06d71457ef | ||
|
|
3e76dde153 | ||
|
|
4f1d91dbb1 | ||
|
|
4b4ac2b896 | ||
|
|
2655b1f55f | ||
|
|
877164a326 | ||
|
|
25b77b7310 | ||
|
|
57fe708cf4 | ||
|
|
508dddae12 | ||
|
|
32e6af5fb2 | ||
|
|
e0560ee403 | ||
|
|
b57ce18965 | ||
|
|
52a4f9d19a | ||
|
|
5d7bf090bb | ||
|
|
6a73fa37ba | ||
|
|
bf12d399b2 | ||
|
|
c9bbdcb445 | ||
|
|
4d0d39eca4 | ||
|
|
398f81bbed |
378
.github/workflows/ci.yml
vendored
Normal file
378
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,378 @@
|
|||||||
|
name: CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
paths-ignore:
|
||||||
|
- 'README.md'
|
||||||
|
- 'LICENSE'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'README.md'
|
||||||
|
- 'LICENSE'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ci-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ruff-format:
|
||||||
|
name: Ruff format check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: astral-sh/ruff-action@v3
|
||||||
|
with:
|
||||||
|
args: "check --output-format github"
|
||||||
|
|
||||||
|
ruff-lint:
|
||||||
|
name: Ruff linting check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: astral-sh/ruff-action@v3
|
||||||
|
with:
|
||||||
|
args: "format --check --diff"
|
||||||
|
|
||||||
|
prettier:
|
||||||
|
name: Prettier check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install Deno v2.x (latest)
|
||||||
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
- name: Install requirements
|
||||||
|
run: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Run Prettier check
|
||||||
|
run: |
|
||||||
|
python pnpm.py fmt:check
|
||||||
|
|
||||||
|
eslint:
|
||||||
|
name: ESLint check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install Deno v2.x (latest)
|
||||||
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
- name: Install requirements
|
||||||
|
run: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Run ESLint check
|
||||||
|
run: |
|
||||||
|
python pnpm.py lint
|
||||||
|
|
||||||
|
python_tests:
|
||||||
|
name: Python tests
|
||||||
|
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@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install Deno v2.x (latest)
|
||||||
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Build project
|
||||||
|
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'
|
||||||
|
run: |
|
||||||
|
unzip -u dist/yt_dlp_ejs-*.whl "yt_dlp_ejs/*"
|
||||||
|
- name: Unpack wheel (Windows)
|
||||||
|
if: matrix.runner == 'windows-latest'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Expand-Archive -Path dist/yt_dlp_ejs-*.whl -DestinationPath ./ -Force
|
||||||
|
- name: Run Python tests
|
||||||
|
timeout-minutes: 5
|
||||||
|
run: |
|
||||||
|
python -Werror -m unittest
|
||||||
|
|
||||||
|
prepare:
|
||||||
|
name: Prepare JS runtime tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- uses: pnpm/action-setup@v4 # respects packageManager version in package.json
|
||||||
|
- name: Install requirements
|
||||||
|
run: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Build control bundle
|
||||||
|
run: |
|
||||||
|
python pnpm.py run bundle
|
||||||
|
- name: Generate bundle hashes
|
||||||
|
run: |
|
||||||
|
pushd dist
|
||||||
|
sha256sum -- yt.solver.*.js | tee SHA2-256SUMS
|
||||||
|
popd
|
||||||
|
- name: Upload bundle hashes
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: bundle-hashes
|
||||||
|
path: |
|
||||||
|
dist/SHA2-256SUMS
|
||||||
|
compression-level: 0
|
||||||
|
- name: Cache player JS files
|
||||||
|
uses: actions/cache@v4
|
||||||
|
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: Install Deno v2.x (latest)
|
||||||
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: v2.x
|
||||||
|
- name: Download player JS files
|
||||||
|
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@v4
|
||||||
|
with:
|
||||||
|
name: player-js
|
||||||
|
path: |
|
||||||
|
src/yt/solver/test/players/*
|
||||||
|
compression-level: 0
|
||||||
|
|
||||||
|
deno_build:
|
||||||
|
name: Test Deno build
|
||||||
|
needs: [prepare]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install Deno
|
||||||
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: "2.0.0" # minimum supported version
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: "3.10" # minimum supported version
|
||||||
|
- name: Install Python requirements
|
||||||
|
run: |
|
||||||
|
python -m pip install -U build
|
||||||
|
- name: Test Deno build
|
||||||
|
run: |
|
||||||
|
python -m build
|
||||||
|
- name: Verify artifact contents
|
||||||
|
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: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Bundle with Deno
|
||||||
|
run: |
|
||||||
|
deno task bundle
|
||||||
|
- name: Download bundle hashes
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
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]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install Deno
|
||||||
|
uses: denoland/setup-deno@v2
|
||||||
|
with:
|
||||||
|
deno-version: "2.0.0" # minimum supported version
|
||||||
|
- name: Install Deno requirements
|
||||||
|
run: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Download player JS artifact
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: src/yt/solver/test/players
|
||||||
|
name: player-js
|
||||||
|
- name: Run Deno tests
|
||||||
|
run: |
|
||||||
|
deno test \
|
||||||
|
--no-prompt \
|
||||||
|
--allow-read=src/yt/solver/test/players/
|
||||||
|
|
||||||
|
bun_build:
|
||||||
|
name: Test Bun build
|
||||||
|
needs: [prepare]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: "1.0.31" # minimum supported version
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: "3.10" # minimum supported version
|
||||||
|
- name: Install Python requirements
|
||||||
|
run: |
|
||||||
|
python -m pip install -U build
|
||||||
|
- name: Test Bun build
|
||||||
|
run: |
|
||||||
|
python -m build
|
||||||
|
- name: Verify artifact contents
|
||||||
|
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: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Bundle with Bun
|
||||||
|
run: |
|
||||||
|
bun --bun run bundle
|
||||||
|
- name: Download bundle hashes
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
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]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install Bun
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: "1.2.11" # XXX: We support 1.0.31, but test suite requires 1.2.11+
|
||||||
|
- name: Install Bun requirements
|
||||||
|
run: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Download player JS artifact
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
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]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: "20.0" # minimum supported version
|
||||||
|
- uses: actions/setup-python@v6
|
||||||
|
with:
|
||||||
|
python-version: "3.10" # minimum supported version
|
||||||
|
- name: Install Python requirements
|
||||||
|
run: |
|
||||||
|
python -m pip install -U build
|
||||||
|
- name: Test Node build
|
||||||
|
run: |
|
||||||
|
python -m build
|
||||||
|
- name: Verify artifact contents
|
||||||
|
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: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Bundle with Node
|
||||||
|
run: |
|
||||||
|
npm run bundle
|
||||||
|
- name: Download bundle hashes
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
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]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@v6
|
||||||
|
with:
|
||||||
|
node-version: "22.18" # XXX: We support 20.0, but test suite requires 22.18+
|
||||||
|
- name: Install Node requirements
|
||||||
|
run: |
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
- name: Download player JS artifact
|
||||||
|
uses: actions/download-artifact@v5
|
||||||
|
with:
|
||||||
|
path: src/yt/solver/test/players
|
||||||
|
name: player-js
|
||||||
|
- name: Run Node tests
|
||||||
|
run: |
|
||||||
|
node --test
|
||||||
|
|
||||||
|
all_passed:
|
||||||
|
needs:
|
||||||
|
- ruff-format
|
||||||
|
- ruff-lint
|
||||||
|
- prettier
|
||||||
|
- eslint
|
||||||
|
- python_tests
|
||||||
|
- 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!"
|
||||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
name: Build artifacts
|
name: Build artifacts
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: denoland/setup-deno@v2
|
- uses: denoland/setup-deno@v2
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
compression-level: 0
|
compression-level: 0
|
||||||
- name: Build JavaScript artifacts
|
- name: Build JavaScript artifacts
|
||||||
run: |
|
run: |
|
||||||
deno install
|
python pnpm.py install --frozen-lockfile
|
||||||
deno task bundle
|
deno task bundle
|
||||||
- name: Upload JavaScript artifacts
|
- name: Upload JavaScript artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/download-artifact@v5
|
- uses: actions/download-artifact@v5
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
gh release create "${TAG}" \
|
gh release create "${TAG}" \
|
||||||
--title "yt-dlp-ejs ${TAG}" \
|
--title "yt-dlp-ejs ${TAG}" \
|
||||||
--notes-from-tag \
|
--generate-notes \
|
||||||
--verify-tag \
|
--verify-tag \
|
||||||
dist/yt.solver.*.js \
|
dist/yt.solver.*.js \
|
||||||
dist/yt_dlp_ejs-*.whl \
|
dist/yt_dlp_ejs-*.whl \
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,3 +6,4 @@
|
|||||||
/deno.lock
|
/deno.lock
|
||||||
/package-lock.json
|
/package-lock.json
|
||||||
/.idea
|
/.idea
|
||||||
|
/.venv
|
||||||
|
|||||||
59
README.md
59
README.md
@@ -1,24 +1,25 @@
|
|||||||
# yt-dlp-ejs
|
# yt-dlp-ejs
|
||||||
|
|
||||||
> [!CAUTION]
|
|
||||||
> This is currently in development
|
|
||||||
|
|
||||||
External JavaScript for yt-dlp supporting many runtimes
|
External JavaScript for yt-dlp supporting many runtimes
|
||||||
|
|
||||||
## Manual Installation
|
## 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
|
```console
|
||||||
pip install git+https://github.com/yt-dlp/ejs@main
|
pip install -U yt-dlp-ejs
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
While this project does pin its dependencies,
|
The project uses [`pnpm`](<https://github.com/pnpm/pnpm>) as a package manager with
|
||||||
it does not use lockfiles or enforce a particular package manager.
|
dependencies pinned through `pnpm-lock.yaml`.
|
||||||
You may install dependencies using any compatible package manager.
|
|
||||||
If you notice differences between different runtimes builds
|
If you only have Python and a JS runtime you may instead invoke `./pnpm.py`,
|
||||||
|
which will transparently invoke one of the supported JS runtimes to call `pnpm`.
|
||||||
|
|
||||||
|
This pure JavaScript approach should be runtime agnostic.
|
||||||
|
If you notice differences between different runtimes' builds
|
||||||
please open an issue [here](<https://github.com/yt-dlp/ejs/issues/new>).
|
please open an issue [here](<https://github.com/yt-dlp/ejs/issues/new>).
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
@@ -26,18 +27,50 @@ please open an issue [here](<https://github.com/yt-dlp/ejs/issues/new>).
|
|||||||
To build the Python package you need a PEP518 compatible builder.
|
To build the Python package you need a PEP518 compatible builder.
|
||||||
The build hook will automatically invoke `deno`, `bun` or `node` as required.
|
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 pnpm.py install --frozen-lockfile
|
||||||
|
python pnpm.py run bundle
|
||||||
|
```
|
||||||
|
|
||||||
|
This will automatically select an available runtime and invoke `pnpm` to build it.
|
||||||
|
|
||||||
### Tests
|
### 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:
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
deno run src/yt/solver/test/download.ts
|
||||||
|
|
||||||
|
# Bun:
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
bun --bun run src/yt/solver/test/download.ts
|
||||||
|
|
||||||
|
# Node 22.6+:
|
||||||
|
python pnpm.py install --frozen-lockfile
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
This code is licensed under [Unlicense](<https://unlicense.org/>).
|
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>),
|
[`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.
|
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.
|
||||||
|
|||||||
14
eslint.config.js
Normal file
14
eslint.config.js
Normal 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,
|
||||||
|
]);
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import os
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
|
|
||||||
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
raise RuntimeError(
|
|
||||||
"One of 'deno', 'bun', or 'npm' could not be found. "
|
|
||||||
"Please install one of them to proceed with the build.")
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
def clean(self, versions):
|
|
||||||
shutil.rmtree('node_modules', ignore_errors=True)
|
|
||||||
15
package.json
15
package.json
@@ -3,21 +3,28 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bundle": "rollup -c",
|
"bundle": "rollup -c",
|
||||||
"fmt": "prettier --write \"src/**.ts\" \"package.json\" \"rollup.config.js\" \"run.ts\""
|
"fmt": "prettier --write \"src/**.ts\" \"package.json\" \"rollup.config.js\" \"run.ts\" \"eslint.config.js\"",
|
||||||
|
"fmt:check": "prettier --check \"src/**.ts\" \"package.json\" \"rollup.config.js\" \"run.ts\" \"eslint.config.js\"",
|
||||||
|
"lint": "eslint src"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astring": "1.9.0",
|
"astring": "1.9.0",
|
||||||
"meriyah": "6.1.4"
|
"meriyah": "6.1.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/js": "9.38.0",
|
||||||
"@rollup/plugin-node-resolve": "16.0.3",
|
"@rollup/plugin-node-resolve": "16.0.3",
|
||||||
"@rollup/plugin-sucrase": "5.0.2",
|
"@rollup/plugin-sucrase": "5.0.2",
|
||||||
"@rollup/plugin-terser": "0.4.4",
|
"@rollup/plugin-terser": "0.4.4",
|
||||||
"rollup-plugin-license": "3.6.0",
|
|
||||||
"@types/bun": "1.3.0",
|
"@types/bun": "1.3.0",
|
||||||
"@types/deno": "2.5.0",
|
"@types/deno": "2.5.0",
|
||||||
"@types/node": "24.8.1",
|
"@types/node": "24.8.1",
|
||||||
|
"eslint": "9.38.0",
|
||||||
|
"globals": "16.4.0",
|
||||||
|
"prettier": "3.6.2",
|
||||||
"rollup": "4.52.5",
|
"rollup": "4.52.5",
|
||||||
"prettier": "3.6.2"
|
"rollup-plugin-license": "3.6.0",
|
||||||
}
|
"typescript-eslint": "8.46.2"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@10.24.0"
|
||||||
}
|
}
|
||||||
|
|||||||
1782
pnpm-lock.yaml
generated
Normal file
1782
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
90
pnpm.py
Executable file
90
pnpm.py
Executable file
@@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pathlib
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
try:
|
||||||
|
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
||||||
|
except ImportError:
|
||||||
|
BuildHookInterface = object
|
||||||
|
|
||||||
|
|
||||||
|
class CustomBuildHook(BuildHookInterface):
|
||||||
|
def initialize(self, version, build_data):
|
||||||
|
name, pnpm = build_pnpm()
|
||||||
|
if pnpm is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
"One of 'deno', 'bun', or 'npm' could not be found. "
|
||||||
|
"Please install one of them to proceed with the build."
|
||||||
|
)
|
||||||
|
print(f"Building with {name}...")
|
||||||
|
|
||||||
|
pnpm(["install", "--frozen-lockfile"])
|
||||||
|
pnpm(["run", "bundle"])
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
def build_pnpm():
|
||||||
|
package_json = pathlib.Path(__file__).with_name("package.json")
|
||||||
|
with package_json.open("rb") as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
package_manager = data["packageManager"]
|
||||||
|
env = os.environ.copy()
|
||||||
|
|
||||||
|
if pnpm := shutil.which("pnpm"):
|
||||||
|
name = "pnpm binary"
|
||||||
|
cmd = [pnpm]
|
||||||
|
|
||||||
|
elif deno := shutil.which("deno"):
|
||||||
|
name = "deno"
|
||||||
|
env["DENO_NO_UPDATE_CHECK"] = "1"
|
||||||
|
cmd = [
|
||||||
|
deno,
|
||||||
|
"run",
|
||||||
|
"--allow-all",
|
||||||
|
"--node-modules-dir=none",
|
||||||
|
f"npm:{package_manager}",
|
||||||
|
]
|
||||||
|
|
||||||
|
elif bun := shutil.which("bun"):
|
||||||
|
name = "bun"
|
||||||
|
cmd = [bun, "x", package_manager]
|
||||||
|
|
||||||
|
elif npm := shutil.which("npm"):
|
||||||
|
name = "npm (node)"
|
||||||
|
cmd = [npm, "exec", "--", package_manager]
|
||||||
|
|
||||||
|
else:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
def run_pnpm(args: list[str]):
|
||||||
|
return subprocess.check_call([*cmd, *args], env=env)
|
||||||
|
|
||||||
|
return name, run_pnpm
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
|
||||||
|
name, pnpm = build_pnpm()
|
||||||
|
if pnpm is None:
|
||||||
|
print("ERROR: No suitable JavaScript runtime found", file=sys.stderr)
|
||||||
|
sys.exit(128)
|
||||||
|
print(f"Calling {name}...", file=sys.stderr)
|
||||||
|
|
||||||
|
try:
|
||||||
|
pnpm(sys.argv[1:])
|
||||||
|
except subprocess.CalledProcessError as error:
|
||||||
|
sys.exit(error.returncode)
|
||||||
@@ -30,6 +30,11 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
dependencies = []
|
dependencies = []
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"ruff>=0.14.1",
|
||||||
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Documentation = "https://github.com/yt-dlp/ejs#readme"
|
Documentation = "https://github.com/yt-dlp/ejs#readme"
|
||||||
Issues = "https://github.com/yt-dlp/ejs/issues"
|
Issues = "https://github.com/yt-dlp/ejs/issues"
|
||||||
@@ -42,9 +47,44 @@ source = "vcs"
|
|||||||
exclude = [
|
exclude = [
|
||||||
"/.github/**",
|
"/.github/**",
|
||||||
"/src/yt/solver/test/players/*",
|
"/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]
|
[tool.hatch.build.hooks.vcs]
|
||||||
version-file = "yt_dlp_ejs/_version.py"
|
version-file = "yt_dlp_ejs/_version.py"
|
||||||
|
|
||||||
[tool.hatch.build.targets.wheel.hooks.custom]
|
[tool.hatch.build.targets.wheel.hooks.custom]
|
||||||
|
path = "pnpm.py"
|
||||||
|
|
||||||
|
[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"
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ function printHash() {
|
|||||||
for (const [fileName, assetInfo] of Object.entries(bundle)) {
|
for (const [fileName, assetInfo] of Object.entries(bundle)) {
|
||||||
if (assetInfo.code) {
|
if (assetInfo.code) {
|
||||||
try {
|
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}`);
|
console.log(`SHA3-512 for ${assetInfo.fileName}: ${digest}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Error hashing ${fileName}:`, err.message);
|
console.error(`Error hashing ${fileName}:`, err.message);
|
||||||
|
|||||||
21
src/types.ts
21
src/types.ts
@@ -1,7 +1,16 @@
|
|||||||
export type DeepPartial<T> = T extends object
|
type DP<T> = T extends (infer U)[]
|
||||||
? Or<{
|
? DeepPartial<U>[]
|
||||||
[P in keyof T]?: DeepPartial<T[P]>;
|
: T extends object
|
||||||
}>
|
? { [P in keyof T]?: DeepPartial<T[P]> }
|
||||||
: Or<T>;
|
: 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>>[] };
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ export function matchesStructure<T extends ESTree.Node>(
|
|||||||
// Handle `{ or: [a, b] }`
|
// Handle `{ or: [a, b] }`
|
||||||
return structure.or.some((node) => matchesStructure(obj, node));
|
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)) {
|
for (const [key, value] of Object.entries(structure)) {
|
||||||
if (!matchesStructure(obj[key as keyof typeof obj], value)) {
|
if (!matchesStructure(obj[key as keyof typeof obj], value)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -2,26 +2,50 @@ import { type ESTree } from "meriyah";
|
|||||||
import { matchesStructure } from "../../utils.ts";
|
import { matchesStructure } from "../../utils.ts";
|
||||||
import { type DeepPartial } from "../../types.ts";
|
import { type DeepPartial } from "../../types.ts";
|
||||||
|
|
||||||
const identifier: DeepPartial<ESTree.VariableDeclaration> = {
|
const identifier: DeepPartial<ESTree.Node> = {
|
||||||
type: "VariableDeclaration",
|
or: [
|
||||||
kind: "var",
|
|
||||||
declarations: [
|
|
||||||
{
|
{
|
||||||
type: "VariableDeclarator",
|
type: "VariableDeclaration",
|
||||||
id: {
|
kind: "var",
|
||||||
type: "Identifier",
|
declarations: {
|
||||||
},
|
anykey: [
|
||||||
init: {
|
|
||||||
type: "ArrayExpression",
|
|
||||||
elements: [
|
|
||||||
{
|
{
|
||||||
type: "Identifier",
|
type: "VariableDeclarator",
|
||||||
|
id: {
|
||||||
|
type: "Identifier",
|
||||||
|
},
|
||||||
|
init: {
|
||||||
|
type: "ArrayExpression",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "Identifier",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "ExpressionStatement",
|
||||||
|
expression: {
|
||||||
|
type: "AssignmentExpression",
|
||||||
|
left: {
|
||||||
|
type: "Identifier",
|
||||||
|
},
|
||||||
|
operator: "=",
|
||||||
|
right: {
|
||||||
|
type: "ArrayExpression",
|
||||||
|
elements: [
|
||||||
|
{
|
||||||
|
type: "Identifier",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
const catchBlockBody = [
|
const catchBlockBody = [
|
||||||
{
|
{
|
||||||
@@ -92,23 +116,37 @@ export function extract(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.type !== "VariableDeclaration") {
|
if (node.type === "VariableDeclaration") {
|
||||||
return null;
|
for (const declaration of node.declarations) {
|
||||||
|
if (
|
||||||
|
declaration.type !== "VariableDeclarator" ||
|
||||||
|
!declaration.init ||
|
||||||
|
declaration.init.type !== "ArrayExpression" ||
|
||||||
|
declaration.init.elements.length !== 1
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const [firstElement] = declaration.init.elements;
|
||||||
|
if (firstElement && firstElement.type === "Identifier") {
|
||||||
|
return makeSolverFuncFromName(firstElement.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (node.type === "ExpressionStatement") {
|
||||||
|
const expr = node.expression;
|
||||||
|
if (
|
||||||
|
expr.type === "AssignmentExpression" &&
|
||||||
|
expr.left.type === "Identifier" &&
|
||||||
|
expr.operator === "=" &&
|
||||||
|
expr.right.type === "ArrayExpression" &&
|
||||||
|
expr.right.elements.length === 1
|
||||||
|
) {
|
||||||
|
const [firstElement] = expr.right.elements;
|
||||||
|
if (firstElement && firstElement.type === "Identifier") {
|
||||||
|
return makeSolverFuncFromName(firstElement.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const declaration = node.declarations[0];
|
return null;
|
||||||
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 {
|
function makeSolverFuncFromName(name: string): ESTree.ArrowFunctionExpression {
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ export const setupNodes = parse(`
|
|||||||
if (typeof globalThis.XMLHttpRequest === "undefined") {
|
if (typeof globalThis.XMLHttpRequest === "undefined") {
|
||||||
globalThis.XMLHttpRequest = { prototype: {} };
|
globalThis.XMLHttpRequest = { prototype: {} };
|
||||||
}
|
}
|
||||||
if (typeof globalThis.window === "undefined") {
|
const window = Object.create(null);
|
||||||
globalThis.window = Object.create(null);
|
|
||||||
}
|
|
||||||
if (typeof URL === "undefined") {
|
if (typeof URL === "undefined") {
|
||||||
globalThis.window.location = {
|
window.location = {
|
||||||
hash: "",
|
hash: "",
|
||||||
host: "www.youtube.com",
|
host: "www.youtube.com",
|
||||||
hostname: "www.youtube.com",
|
hostname: "www.youtube.com",
|
||||||
@@ -22,7 +20,7 @@ if (typeof URL === "undefined") {
|
|||||||
username: "",
|
username: "",
|
||||||
};
|
};
|
||||||
} else {
|
} 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") {
|
if (typeof globalThis.document === "undefined") {
|
||||||
globalThis.document = Object.create(null);
|
globalThis.document = Object.create(null);
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ const logicalExpression: DeepPartial<ESTree.ExpressionStatement> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const identifier = {
|
const identifier: DeepPartial<ESTree.Node> = {
|
||||||
or: [
|
or: [
|
||||||
{
|
{
|
||||||
type: "ExpressionStatement",
|
type: "ExpressionStatement",
|
||||||
@@ -82,25 +82,50 @@ const identifier = {
|
|||||||
type: "FunctionDeclaration",
|
type: "FunctionDeclaration",
|
||||||
params: [{}, {}, {}],
|
params: [{}, {}, {}],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "VariableDeclaration",
|
||||||
|
declarations: {
|
||||||
|
anykey: [
|
||||||
|
{
|
||||||
|
type: "VariableDeclarator",
|
||||||
|
init: {
|
||||||
|
type: "FunctionExpression",
|
||||||
|
params: [{}, {}, {}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export function extract(
|
export function extract(
|
||||||
node: ESTree.Node,
|
node: ESTree.Node,
|
||||||
): ESTree.ArrowFunctionExpression | null {
|
): ESTree.ArrowFunctionExpression | null {
|
||||||
if (
|
if (!matchesStructure(node, identifier)) {
|
||||||
!matchesStructure(node, identifier as unknown as DeepPartial<ESTree.Node>)
|
|
||||||
) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const block =
|
let block: ESTree.BlockStatement | undefined | null;
|
||||||
node.type === "ExpressionStatement" &&
|
if (node.type === "ExpressionStatement" &&
|
||||||
node.expression.type === "AssignmentExpression" &&
|
node.expression.type === "AssignmentExpression" &&
|
||||||
node.expression.right.type === "FunctionExpression"
|
node.expression.right.type === "FunctionExpression") {
|
||||||
? node.expression.right.body
|
block = node.expression.right.body;
|
||||||
: node.type === "FunctionDeclaration"
|
} else if (node.type === "VariableDeclaration") {
|
||||||
? node.body
|
for (const decl of node.declarations) {
|
||||||
: null;
|
if (
|
||||||
|
decl.type === "VariableDeclarator" &&
|
||||||
|
decl.init?.type === "FunctionExpression" &&
|
||||||
|
decl.init?.params.length === 3
|
||||||
|
) {
|
||||||
|
block = decl.init.body;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (node.type === "FunctionDeclaration") {
|
||||||
|
block = node.body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const relevantExpression = block?.body.at(-2);
|
const relevantExpression = block?.body.at(-2);
|
||||||
if (!matchesStructure(relevantExpression!, logicalExpression)) {
|
if (!matchesStructure(relevantExpression!, logicalExpression)) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ export async function getIO(): Promise<IO> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Assume node compatibility
|
||||||
const { access, readFile } = await import("node:fs/promises");
|
const { access, readFile } = await import("node:fs/promises");
|
||||||
const { deepStrictEqual } = await import("node:assert");
|
const { deepStrictEqual } = await import("node:assert");
|
||||||
|
|||||||
@@ -251,6 +251,38 @@ export const tests: {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
player: "638ec5c6",
|
||||||
|
n: [
|
||||||
|
// Synthetic test
|
||||||
|
{ input: "ZdZIqFPQK-Ty8wId", expected: "1qov8-KM-yH" },
|
||||||
|
],
|
||||||
|
sig: [
|
||||||
|
// Synthetic test
|
||||||
|
{
|
||||||
|
input:
|
||||||
|
"gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt",
|
||||||
|
expected:
|
||||||
|
"MhudCuAuP-6fByOk1_GNXN7gNHHShjyXS2VOgsEItAJz0tipeav0OmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
player: "87644c66",
|
||||||
|
n: [
|
||||||
|
// Synthetic test
|
||||||
|
{ input: "ZdZIqFPQK-Ty8wId", expected: "iF5NxEm1BYk" },
|
||||||
|
],
|
||||||
|
sig: [
|
||||||
|
// Synthetic test
|
||||||
|
{
|
||||||
|
input:
|
||||||
|
"gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt",
|
||||||
|
expected:
|
||||||
|
"atJC2JfQdSswRAtgGBCxZyAfKyi0cjXCb3DqEctUw-NYdNmOEvIepit0zJAtIEsgOV2SXZjhSHMNy0NXNG_1kOyBf6HPuAuCduh-a7Ng",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const players = new Map([
|
export const players = new Map([
|
||||||
|
|||||||
0
test/__init__.py
Normal file
0
test/__init__.py
Normal file
24
test/test_modules.py
Normal file
24
test/test_modules.py
Normal 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()
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
from yt_dlp_ejs._version import version
|
from yt_dlp_ejs._version import version
|
||||||
from yt_dlp_ejs import yt
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = ["version"]
|
||||||
"version",
|
|
||||||
"yt"
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
from . import solver
|
|
||||||
|
|
||||||
__all__ = ["solver"]
|
|
||||||
@@ -2,15 +2,20 @@ import importlib.resources
|
|||||||
|
|
||||||
import yt_dlp_ejs.yt.solver
|
import yt_dlp_ejs.yt.solver
|
||||||
|
|
||||||
|
|
||||||
def core() -> str:
|
def core() -> str:
|
||||||
"""
|
"""
|
||||||
Read the contents of the JavaScript core solver bundle as string.
|
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:
|
def lib() -> str:
|
||||||
"""
|
"""
|
||||||
Read the contents of the JavaScript library solver bundle as string.
|
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"
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user