10 Commits
0.4.0 ... 0.8.0

Author SHA1 Message Date
bashonly
4fb477f4af Bump actions pins (#55)
* Bump actions/cache v5.0.1 → v5.0.3
* Bump actions/checkout v6.0.1 → v6.0.2
* Bump actions/download-artifact v7.0.0 → v8.0.1
* Bump actions/setup-node v6.1.0 → v6.3.0
* Bump actions/setup-python v6.1.0 → v6.2.0
* Bump actions/upload-artifact v6.0.0 → v7.0.0
* Bump astral-sh/ruff-action v3.5.1 → v3.6.1
* Bump oven-sh/setup-bun v2.0.2 → v2.2.0
* Bump pnpm/action-setup v4.2.0 → v5.0.0
* Bump zizmorcore/zizmor-action v0.3.0 → v0.5.2
* Bump actionlint v1.7.9 → v1.7.11
* Bump zizmor v1.19.0 → v1.23.1
2026-03-17 16:24:02 +00:00
bashonly
68448fa0ab Expose window global as self (#56)
Ref: https://github.com/yt-dlp/yt-dlp/issues/16256

Co-authored-by: Simon Sawicki <contact@grub4k.dev>
2026-03-17 16:12:08 +00:00
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
14 changed files with 440 additions and 772 deletions

View File

@@ -18,8 +18,8 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
ACTIONLINT_VERSION: "1.7.9"
ACTIONLINT_SHA256SUM: 233b280d05e100837f4af1433c7b40a5dcb306e3aa68fb4f17f8a7f45a7df7b4
ACTIONLINT_VERSION: "1.7.11"
ACTIONLINT_SHA256SUM: 900919a84f2229bac68ca9cd4103ea297abc35e9689ebb842c6e34a3d1b01b0a
ACTIONLINT_REPO: https://github.com/rhysd/actionlint
jobs:
@@ -29,10 +29,10 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.14"
- name: Install requirements
@@ -57,15 +57,15 @@ jobs:
actions: read # Needed by zizmorcore/zizmor-action if repository is private
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
with:
advanced-security: false
persona: pedantic
version: v1.19.0
version: v1.23.1
ruff_format:
name: Ruff format check
@@ -73,10 +73,10 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
- uses: astral-sh/ruff-action@4919ec5cf1f49eff0871dbcea0da843445b837e6 # v3.6.1
with:
args: "check --output-format github"
@@ -86,10 +86,10 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
- uses: astral-sh/ruff-action@4919ec5cf1f49eff0871dbcea0da843445b837e6 # v3.6.1
with:
args: "format --check --diff"
@@ -99,7 +99,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Deno v2.x (latest)
@@ -119,7 +119,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Deno v2.x (latest)
@@ -144,7 +144,7 @@ jobs:
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
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# required for hatch-vcs versioning
fetch-depth: 0
@@ -154,7 +154,7 @@ jobs:
with:
deno-version: v2.x
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: Build project
@@ -187,7 +187,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Deno v2.x (latest)
@@ -207,14 +207,14 @@ jobs:
sha256sum -- yt.solver.*.js | tee SHA2-256SUMS
popd
- name: Upload bundle hashes
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.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
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1
with:
@@ -232,7 +232,7 @@ jobs:
--allow-sys=uid \
src/yt/solver/test/download.ts
- name: Upload player JS artifact
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: player-js
path: |
@@ -246,16 +246,16 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
with:
version: 10
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
# minimum supported version
python-version: "3.10"
@@ -279,7 +279,7 @@ jobs:
run: |
pnpm run bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
name: bundle-hashes
@@ -294,10 +294,10 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
# minimum supported version
python-version: "3.10"
@@ -312,7 +312,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# required for hatch-vcs versioning
fetch-depth: 0
@@ -322,7 +322,7 @@ jobs:
with:
# minimum supported version
deno-version: "2.0.0"
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
# minimum supported version
python-version: "3.10"
@@ -346,7 +346,7 @@ jobs:
run: |
deno task bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
name: bundle-hashes
@@ -362,7 +362,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Deno
@@ -374,15 +374,17 @@ jobs:
run: |
deno install --frozen
- name: Download player JS artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: src/yt/solver/test/players
name: player-js
- name: Run Deno tests
run: |
deno test \
xargs -n 1 -P 10 deno test \
--no-prompt \
--allow-read=src/yt/solver/test/players/
--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
@@ -391,18 +393,18 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install Bun
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
# minimum supported version
bun-version: "1.0.31"
no-cache: true
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
# minimum supported version
python-version: "3.10"
@@ -426,7 +428,7 @@ jobs:
run: |
bun --bun run bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
name: bundle-hashes
@@ -442,11 +444,11 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Bun
uses: oven-sh/setup-bun@735343b667d3e6f658f44d0eca948eb6282f2b76 # v2.0.2
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
# XXX: We support 1.0.31, but test suite requires 1.2.11+
bun-version: "1.2.11"
@@ -455,7 +457,7 @@ jobs:
run: |
bun install --frozen-lockfile
- name: Download player JS artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: src/yt/solver/test/players
name: player-js
@@ -470,17 +472,17 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# required for hatch-vcs versioning
fetch-depth: 0
persist-credentials: false
- name: Install Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
# minimum supported version
node-version: "20.0"
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
# minimum supported version
python-version: "3.10"
@@ -504,7 +506,7 @@ jobs:
run: |
npm run bundle
- name: Download bundle hashes
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
name: bundle-hashes
@@ -520,11 +522,11 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Node
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
# XXX: We support 20.0, but test suite requires 22.18+
node-version: "22.18"
@@ -532,7 +534,7 @@ jobs:
run: |
npm ci
- name: Download player JS artifact
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: src/yt/solver/test/players
name: player-js

View File

@@ -13,7 +13,7 @@ jobs:
contents: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0 # Needed for hatch-vcs versioning
persist-credentials: false
@@ -21,7 +21,7 @@ jobs:
with:
deno-version: 3fbb1daddbc9333cddf0d8c0735811717dd70f7a # v2.6.3
cache: false
- uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.10"
- name: Install Python requirements
@@ -31,7 +31,7 @@ jobs:
run: |
python -m build
- name: Upload Python artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: artifacts-py
path: |
@@ -43,7 +43,7 @@ jobs:
deno install --frozen
deno task bundle
- name: Upload JavaScript artifacts
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: artifacts-js
path: |
@@ -58,7 +58,7 @@ jobs:
id-token: write # Needed for PyPI trusted publishing
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
name: artifacts-py
@@ -75,10 +75,10 @@ jobs:
contents: write # Needed by gh to publish release to Github
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: dist
pattern: artifacts-*

View File

@@ -3,8 +3,9 @@
"type": "module",
"scripts": {
"bundle": "rollup -c",
"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\"",
"prettier": "prettier",
"fmt": "prettier --write \"**/*.[jt]s\" \"package.json\"",
"fmt:check": "prettier --check \"**/*.[jt]s\" \"package.json\"",
"lint": "eslint src"
},
"dependencies": {

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>(
@@ -42,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,179 +0,0 @@
import { type ESTree } from "meriyah";
import { matchesStructure } from "../../utils.ts";
import { type DeepPartial } from "../../types.ts";
const identifier: DeepPartial<ESTree.Node> = {
or: [
{
type: "VariableDeclaration",
kind: "var",
declarations: {
anykey: [
{
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 = [
{
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") {
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);
}
}
}
return null;
}
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,9 +4,8 @@ export const setupNodes = parse(`
if (typeof globalThis.XMLHttpRequest === "undefined") {
globalThis.XMLHttpRequest = { prototype: {} };
}
const window = Object.create(null);
if (typeof URL === "undefined") {
window.location = {
globalThis.location = {
hash: "",
host: "www.youtube.com",
hostname: "www.youtube.com",
@@ -20,7 +19,7 @@ if (typeof URL === "undefined") {
username: "",
};
} else {
window.location = new URL("https://www.youtube.com/watch?v=yt-dlp-wins");
globalThis.location = new URL("https://www.youtube.com/watch?v=yt-dlp-wins");
}
if (typeof globalThis.document === "undefined") {
globalThis.document = Object.create(null);
@@ -31,4 +30,7 @@ if (typeof globalThis.navigator === "undefined") {
if (typeof globalThis.self === "undefined") {
globalThis.self = globalThis;
}
if (typeof globalThis.window === "undefined") {
globalThis.window = globalThis;
}
`).body;

View File

@@ -1,232 +0,0 @@
import { type ESTree } from "meriyah";
import { matchesStructure } from "../../utils.ts";
import { type DeepPartial } from "../../types.ts";
const nsigExpression: DeepPartial<ESTree.Statement> = {
type: "VariableDeclaration",
kind: "var",
declarations: [
{
type: "VariableDeclarator",
init: {
type: "CallExpression",
callee: {
type: "Identifier",
},
arguments: [
{
type: "Literal",
},
{
type: "CallExpression",
callee: {
type: "Identifier",
name: "decodeURIComponent",
},
},
],
},
},
],
};
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: DeepPartial<ESTree.Node> = {
or: [
{
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "Identifier",
},
right: {
type: "FunctionExpression",
params: [{}, {}, {}],
},
},
},
{
type: "FunctionDeclaration",
params: [{}, {}, {}],
},
{
type: "VariableDeclaration",
declarations: {
anykey: [
{
type: "VariableDeclarator",
init: {
type: "FunctionExpression",
params: [{}, {}, {}],
},
},
],
},
},
],
} as const;
export function extract(
node: ESTree.Node,
): ESTree.ArrowFunctionExpression | null {
if (!matchesStructure(node, identifier)) {
return null;
}
let block: ESTree.BlockStatement | undefined | null;
if (
node.type === "ExpressionStatement" &&
node.expression.type === "AssignmentExpression" &&
node.expression.right.type === "FunctionExpression"
) {
block = node.expression.right.body;
} else if (node.type === "VariableDeclaration") {
for (const decl of node.declarations) {
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);
let call: ESTree.CallExpression | null = null;
if (matchesStructure(relevantExpression!, logicalExpression)) {
if (
relevantExpression?.type !== "ExpressionStatement" ||
relevantExpression.expression.type !== "LogicalExpression" ||
relevantExpression.expression.right.type !== "SequenceExpression" ||
relevantExpression.expression.right.expressions[0].type !==
"AssignmentExpression" ||
relevantExpression.expression.right.expressions[0].right.type !==
"CallExpression"
) {
return null;
}
call = relevantExpression.expression.right.expressions[0].right;
} else if (
relevantExpression?.type === "IfStatement" &&
relevantExpression.consequent.type === "BlockStatement"
) {
for (const n of relevantExpression.consequent.body) {
if (!matchesStructure(n, nsigExpression)) {
continue;
}
if (
n.type !== "VariableDeclaration" ||
n.declarations[0].init?.type !== "CallExpression"
) {
continue;
}
call = n.declarations[0].init;
break;
}
}
if (call === null) {
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

@@ -10,293 +10,103 @@ export const tests: {
sig?: Step[];
}[] = [
{
player: "3d3ba064",
// 20522
player: "74edf1a3",
n: [
{ input: "ZdZIqFPQK-Ty8wId", expected: "qmtUsIz04xxiNW" },
{ input: "4GMrWHyKI5cEvhDO", expected: "N9gmEX7YhKTSmw" },
{ input: "IlLiA21ny7gqA2m4p37", expected: "9nRTxrbM1f0yHg" },
{ input: "eabGFpsUKuWHXGh6FR4", expected: "izmYqDEY6kl7Sg" },
{ input: "eabGF/ps%UK=uWHXGh6FR4", expected: "LACmqlhaBpiPlgE-a" },
],
sig: [
{
input:
"gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"ttJC2JfQdSswRAIgGBCxZyAfKyi0cjXCb3gqEctUw-NYdNmOEvaepit0zJAtIEsgOV2SXZjhSHMNy0NXNG_1kNyBf6HPuAuCduh-a7O",
"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: "5ec65609",
n: [{ input: "0eRGgQWJGfT5rFHFj", expected: "4SvMpDQH-vBJCw" }],
// 20523
player: "901741ab",
n: [{ input: "BQoJvGBkC2nj1ZZLK-", expected: "UMPovvBZRh-sjb" }],
sig: [
{
input:
"AAJAJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grH0rTMICA1mmDc0HoXgW3CAiAQQ4=CspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ=I",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"AJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grI0rTMICA1mmDc0HoXgW3CAiAQQ4HCspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ==",
"wgwCHlydB9Hg7PMegXoVzaoAXXB8woPSNZqRUC3Pe7vAEiApVSCMlhwmt5ON-8MB=5RPyyzdAM9MPM-kPfjgTxEK0IAhIgRwE0jiEJA",
},
],
},
{
player: "6742b2b9",
n: [
{ input: "_HPB-7GFg1VTkn9u", expected: "qUAsPryAO_ByYg" },
{ input: "K1t_fcB6phzuq2SF", expected: "Y7PcOt3VE62mog" },
],
// 20524
player: "e7573094",
n: [{ input: "IlLiA21ny7gqA2m4p37", expected: "3KuQ3235dojTSjo4" }],
sig: [
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"NJAJEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyyPRt=BM8-XO5tm5hlMCSVpAiEAv7eP3CURqZNSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=gwzz",
expected:
"AJfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYu7S6SHM8EjoCACIEQnz-nKN5RgG6iUTnNJC58csYPSrnS_SzricuUMJZGM",
"yEij0EwRgIhAI0KExTgjfPk-MPM9MAdzyNPRt=BM8-XO5tm5hlMCSVNAiEAvpeP3CURqZJSPow8BXXAoazVoXgeMP7gH9BdylHCwgw=g",
},
],
},
{
player: "23ccdd25",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "orSsTqUaUO-j" },
],
// 20525
player: "9fcf08e8",
n: [{ input: "4JRSxxN0E_fLVnxmd", expected: "xhAoMP8xPcR5wg" }],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a",
expected:
"ZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hAU6wbTvorvVVMgIARwsSdQfJAN",
"\x6a\x69\x68\x67\x66\x65\x64\x63\x62\x61\x60\x5f\x5e\x5d\x5c\x5b\x5a\x59\x58\x57\x56\x55\x54\x53\x52\x51\x50\x4f\x4e\x4d\x4c\x4b\x4a\x49\x48\x47\x46\x45\x44\x43\x42\x41\x40\x3f\x3e\x3d\x3c\x3b\x3a\x39\x38\x37\x36\x35\x34\x33\x32\x31\x30\x2f\x2e\x2d\x2c\x2b\x2a\x29\x28\x27\x26\x25\x24\x23\x22\x21\x20\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18\x17\x16\x15\x14\x13\x12\x11\x10\x0f\x0e\x0d\x0c\x0b\x03\x09\x08\x07\x06\x05\x04\x0a",
},
],
},
{
player: "3597727b",
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "PRwo5dDfisg0ejA2" },
],
// 20527
player: "21cd2156",
n: [{ input: "16EF3jx-Mr_TLuGH", expected: "OQJDc7IrlWCkfg" }],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a",
expected:
"AAJfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYuMS6SHM8Ej7CACIEQnz-nKN5RgG6iUTnNJC58csYPSroS_SzricuUMJZG",
"\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x00\x46\x47\x48\x49\x4a\x4b\x6a\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x4c",
},
],
},
{
// tce causes exception even in browser
player: "3752a005",
variants: ["main", "tcc", "es5", "es6", "tv", "tv_es6", "phone"],
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "j22ZtsqVsR0Dn" },
],
// 20528 (tv & tv_es6 needed self.location.origin fix)
player: "76ad2fe8",
n: [{ input: "V8pdMGD0Sz_M0DRT", expected: "rBnTGCWGFpwByA" }],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a",
expected:
"ZJM_ucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHG6S7uYq4TGjQXSD4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"\x46\x66\x65\x64\x63\x62\x61\x60\x5f\x5e\x67\x6a\x5b\x5a\x59\x58\x57\x56\x55\x54\x53\x52\x51\x50\x4f\x4e\x4d\x4c\x4b\x4a\x49\x48\x47\x2c\x45\x44\x43\x42\x41\x40\x3f\x3e\x3d\x3c\x3b\x3a\x39\x38\x13\x36\x35\x34\x33\x32\x31\x30\x2f\x2e\x2d\x5d\x2b\x2a\x29\x28\x27\x26\x25\x24\x23\x22\x21\x20\x1f\x1e\x1d\x1c\x1b\x1a\x19\x18\x17\x16\x15\x14\x0c\x12\x11\x10\x0f\x0e\x0d\x00\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x37",
},
],
},
{
// tce causes exception even in browser
player: "afc7785b",
variants: ["main", "tcc", "es5", "es6", "tv", "tv_es6", "phone"],
n: [
// Synthetic test
{ input: "0eRGgQWJGfT5rFHFj", expected: "j22ZtsqVsR0Dn" },
],
// 20529 (tv & tv_es6 needed self.location.origin fix)
player: "631d3938",
n: [{ input: "KBx1qz7jMhxELa8c", expected: "ttPvh7WIptsgSw" }],
sig: [
// Synthetic test
{
input:
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66",
expected:
"ZJM_ucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHG6S7uYq4TGjQXSD4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
},
],
},
{
// tce causes exception even in browser
player: "b9645327",
variants: ["main", "tcc", "es5", "es6", "tv", "tv_es6", "phone"],
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"],
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",
},
],
},
{
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",
},
],
},
{
// tce variant broke sig solving; n and other variants are added only for regression testing
player: "c1c87fb0",
n: [
// Synthetic test
{ input: "ZdZIqFPQK-Ty8wId", expected: "jCHBK5GuAFNa2" },
],
sig: [
// Synthetic test
{
input:
"gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt",
expected:
"ttJC2JfQdSswRAIgGBCxZyAfKyi0cjXCb3DqEctUw-NYdNmOEvaepit0zJAtIEsgOV2SXZjhSHMNy0NXNGa1kOyBf6HPuAuCduh-_",
"\x19\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x00\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63",
},
],
},
@@ -306,11 +116,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"],
["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);
}