mirror of
https://github.com/yt-dlp/ejs.git
synced 2026-06-13 00:32:11 +00:00
Initial POC
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/dist
|
||||
*.py[cd]
|
||||
/yt_dlp_jsc_deno/dist
|
||||
/yt_dlp_jsc_deno/__about__.py
|
||||
9
README.md
Normal file
9
README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# yt-dlp-jsc-deno
|
||||
Deno backend of builtin JavaScript Challenge Provider for yt-dlp
|
||||
|
||||
## Installation
|
||||
In the yt-dlp repository, install the """python""" package, either by doing
|
||||
```console
|
||||
pip install ../yt-dlp-jsp-deno
|
||||
```
|
||||
or from the url.
|
||||
12
bundle.ts
Normal file
12
bundle.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import * as esbuild from "npm:esbuild@0.25.5";
|
||||
import { denoPlugins } from "jsr:@luca/esbuild-deno-loader@0.11.1";
|
||||
|
||||
await esbuild.build({
|
||||
plugins: [...denoPlugins()],
|
||||
entryPoints: ["./src/main.ts"],
|
||||
outfile: "./dist/jsc-deno.js",
|
||||
bundle: true,
|
||||
format: "esm",
|
||||
sourcemap: false,
|
||||
});
|
||||
esbuild.stop();
|
||||
10
deno.jsonc
Normal file
10
deno.jsonc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"test": {
|
||||
"exclude": ["./dist"]
|
||||
},
|
||||
"tasks": {
|
||||
"download": "deno run --allow-read --allow-write --allow-net=www.youtube.com tests/download.ts",
|
||||
"test": "deno test --allow-read --allow-env=BABEL_TYPES_8_BREAKING --location 'https://www.youtube.com/watch?v=yt-dlp-wins'",
|
||||
"bundle": "deno run --allow-env --allow-read --allow-run --allow-write bundle.ts"
|
||||
}
|
||||
}
|
||||
263
deno.lock
generated
Normal file
263
deno.lock
generated
Normal file
@@ -0,0 +1,263 @@
|
||||
{
|
||||
"version": "5",
|
||||
"specifiers": {
|
||||
"jsr:@luca/esbuild-deno-loader@0.11.1": "0.11.1",
|
||||
"jsr:@std/assert@1": "1.0.14",
|
||||
"jsr:@std/bytes@^1.0.2": "1.0.6",
|
||||
"jsr:@std/encoding@^1.0.5": "1.0.10",
|
||||
"jsr:@std/fs@*": "1.0.19",
|
||||
"jsr:@std/internal@^1.0.10": "1.0.10",
|
||||
"jsr:@std/path@^1.0.6": "1.1.2",
|
||||
"npm:@babel/generator@7.28.3": "7.28.3",
|
||||
"npm:@babel/parser@7.28.3": "7.28.3",
|
||||
"npm:@babel/types@7.28.2": "7.28.2",
|
||||
"npm:esbuild@0.25.5": "0.25.5"
|
||||
},
|
||||
"jsr": {
|
||||
"@luca/esbuild-deno-loader@0.11.1": {
|
||||
"integrity": "dc020d16d75b591f679f6b9288b10f38bdb4f24345edb2f5732affa1d9885267",
|
||||
"dependencies": [
|
||||
"jsr:@std/bytes",
|
||||
"jsr:@std/encoding",
|
||||
"jsr:@std/path"
|
||||
]
|
||||
},
|
||||
"@std/assert@1.0.14": {
|
||||
"integrity": "68d0d4a43b365abc927f45a9b85c639ea18a9fab96ad92281e493e4ed84abaa4",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal"
|
||||
]
|
||||
},
|
||||
"@std/bytes@1.0.6": {
|
||||
"integrity": "f6ac6adbd8ccd99314045f5703e23af0a68d7f7e58364b47d2c7f408aeb5820a"
|
||||
},
|
||||
"@std/encoding@1.0.10": {
|
||||
"integrity": "8783c6384a2d13abd5e9e87a7ae0520a30e9f56aeeaa3bdf910a3eaaf5c811a1"
|
||||
},
|
||||
"@std/fs@1.0.19": {
|
||||
"integrity": "051968c2b1eae4d2ea9f79a08a3845740ef6af10356aff43d3e2ef11ed09fb06"
|
||||
},
|
||||
"@std/internal@1.0.10": {
|
||||
"integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7"
|
||||
},
|
||||
"@std/path@1.1.2": {
|
||||
"integrity": "c0b13b97dfe06546d5e16bf3966b1cadf92e1cc83e56ba5476ad8b498d9e3038",
|
||||
"dependencies": [
|
||||
"jsr:@std/internal"
|
||||
]
|
||||
}
|
||||
},
|
||||
"npm": {
|
||||
"@babel/generator@7.28.3": {
|
||||
"integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
|
||||
"dependencies": [
|
||||
"@babel/parser",
|
||||
"@babel/types",
|
||||
"@jridgewell/gen-mapping",
|
||||
"@jridgewell/trace-mapping",
|
||||
"jsesc"
|
||||
]
|
||||
},
|
||||
"@babel/helper-string-parser@7.27.1": {
|
||||
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="
|
||||
},
|
||||
"@babel/helper-validator-identifier@7.27.1": {
|
||||
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="
|
||||
},
|
||||
"@babel/parser@7.28.3": {
|
||||
"integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
|
||||
"dependencies": [
|
||||
"@babel/types"
|
||||
],
|
||||
"bin": true
|
||||
},
|
||||
"@babel/types@7.28.2": {
|
||||
"integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
|
||||
"dependencies": [
|
||||
"@babel/helper-string-parser",
|
||||
"@babel/helper-validator-identifier"
|
||||
]
|
||||
},
|
||||
"@esbuild/aix-ppc64@0.25.5": {
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"os": ["aix"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@esbuild/android-arm64@0.25.5": {
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/android-arm@0.25.5": {
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"os": ["android"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@esbuild/android-x64@0.25.5": {
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"os": ["android"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/darwin-arm64@0.25.5": {
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/darwin-x64@0.25.5": {
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/freebsd-arm64@0.25.5": {
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/freebsd-x64@0.25.5": {
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"os": ["freebsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/linux-arm64@0.25.5": {
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/linux-arm@0.25.5": {
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm"]
|
||||
},
|
||||
"@esbuild/linux-ia32@0.25.5": {
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@esbuild/linux-loong64@0.25.5": {
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["loong64"]
|
||||
},
|
||||
"@esbuild/linux-mips64el@0.25.5": {
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["mips64el"]
|
||||
},
|
||||
"@esbuild/linux-ppc64@0.25.5": {
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["ppc64"]
|
||||
},
|
||||
"@esbuild/linux-riscv64@0.25.5": {
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["riscv64"]
|
||||
},
|
||||
"@esbuild/linux-s390x@0.25.5": {
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["s390x"]
|
||||
},
|
||||
"@esbuild/linux-x64@0.25.5": {
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/netbsd-arm64@0.25.5": {
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"os": ["netbsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/netbsd-x64@0.25.5": {
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"os": ["netbsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/openbsd-arm64@0.25.5": {
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"os": ["openbsd"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/openbsd-x64@0.25.5": {
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"os": ["openbsd"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/sunos-x64@0.25.5": {
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"os": ["sunos"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@esbuild/win32-arm64@0.25.5": {
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["arm64"]
|
||||
},
|
||||
"@esbuild/win32-ia32@0.25.5": {
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["ia32"]
|
||||
},
|
||||
"@esbuild/win32-x64@0.25.5": {
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"]
|
||||
},
|
||||
"@jridgewell/gen-mapping@0.3.13": {
|
||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||
"dependencies": [
|
||||
"@jridgewell/sourcemap-codec",
|
||||
"@jridgewell/trace-mapping"
|
||||
]
|
||||
},
|
||||
"@jridgewell/resolve-uri@3.1.2": {
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
|
||||
},
|
||||
"@jridgewell/sourcemap-codec@1.5.5": {
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="
|
||||
},
|
||||
"@jridgewell/trace-mapping@0.3.30": {
|
||||
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
|
||||
"dependencies": [
|
||||
"@jridgewell/resolve-uri",
|
||||
"@jridgewell/sourcemap-codec"
|
||||
]
|
||||
},
|
||||
"esbuild@0.25.5": {
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"optionalDependencies": [
|
||||
"@esbuild/aix-ppc64",
|
||||
"@esbuild/android-arm",
|
||||
"@esbuild/android-arm64",
|
||||
"@esbuild/android-x64",
|
||||
"@esbuild/darwin-arm64",
|
||||
"@esbuild/darwin-x64",
|
||||
"@esbuild/freebsd-arm64",
|
||||
"@esbuild/freebsd-x64",
|
||||
"@esbuild/linux-arm",
|
||||
"@esbuild/linux-arm64",
|
||||
"@esbuild/linux-ia32",
|
||||
"@esbuild/linux-loong64",
|
||||
"@esbuild/linux-mips64el",
|
||||
"@esbuild/linux-ppc64",
|
||||
"@esbuild/linux-riscv64",
|
||||
"@esbuild/linux-s390x",
|
||||
"@esbuild/linux-x64",
|
||||
"@esbuild/netbsd-arm64",
|
||||
"@esbuild/netbsd-x64",
|
||||
"@esbuild/openbsd-arm64",
|
||||
"@esbuild/openbsd-x64",
|
||||
"@esbuild/sunos-x64",
|
||||
"@esbuild/win32-arm64",
|
||||
"@esbuild/win32-ia32",
|
||||
"@esbuild/win32-x64"
|
||||
],
|
||||
"scripts": true,
|
||||
"bin": true
|
||||
},
|
||||
"jsesc@3.1.0": {
|
||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||
"bin": true
|
||||
}
|
||||
}
|
||||
}
|
||||
43
pyproject.toml
Normal file
43
pyproject.toml
Normal file
@@ -0,0 +1,43 @@
|
||||
[build-system]
|
||||
requires = ["hatchling", "hatch-build-scripts"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "yt-dlp-jsc-deno"
|
||||
version = "0.0.1"
|
||||
description = "JavaScript Challenge Provider for yt-dlp using Deno"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
license = "Unlicense"
|
||||
keywords = []
|
||||
authors = [
|
||||
{ name = "Simon Sawicki", email = "contact@grub4k.dev" },
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
[project.urls]
|
||||
Documentation = "https://github.com/yt-dlp/yt-dlp-jsc-deno#readme"
|
||||
Issues = "https://github.com/yt-dlp/yt-dlp-jsc-deno/issues"
|
||||
Source = "https://github.com/yt-dlp/yt-dlp-jsc-deno"
|
||||
|
||||
[[tool.hatch.build.hooks.build-scripts.scripts]]
|
||||
out_dir = "yt_dlp_jsc_deno"
|
||||
commands = [
|
||||
"deno task bundle",
|
||||
]
|
||||
artifacts = [
|
||||
"dist/jsc-deno.js",
|
||||
]
|
||||
44
src/main.ts
Normal file
44
src/main.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { getSolvers } from "./solvers.ts";
|
||||
import { isOneOf } from "./utils.ts";
|
||||
|
||||
if (Deno.stdin.isTerminal()) {
|
||||
console.error("Expected player content on stdin");
|
||||
Deno.exit(9);
|
||||
}
|
||||
const stdin = await new Response(Deno.stdin.readable).text();
|
||||
if (!stdin) {
|
||||
console.error("Expected player content on stdin");
|
||||
Deno.exit(9);
|
||||
}
|
||||
if (Deno.args.length < 1) {
|
||||
console.error("Expected one argument, `solver nsig:... nsig:... sig:...`");
|
||||
Deno.exit(9);
|
||||
}
|
||||
|
||||
const solveList: {
|
||||
mode: "nsig" | "sig";
|
||||
value: string;
|
||||
}[] = [];
|
||||
for (const arg of Deno.args) {
|
||||
const split = arg.split(":", 2);
|
||||
if (split.length === 1) {
|
||||
console.error(`Missing mode: ${arg}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
const [mode, value] = split;
|
||||
if (!isOneOf(mode, "sig", "nsig")) {
|
||||
console.error(`Invalid mode, expected "nsig:..." or "sig:...": ${mode}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
solveList.push({ mode, value });
|
||||
}
|
||||
|
||||
const solvers = getSolvers(stdin);
|
||||
for (const solve of solveList) {
|
||||
const solver = solvers[solve.mode];
|
||||
if (!solver) {
|
||||
console.error(`Did not set ${solve.mode} function`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
console.log(solver(solve.value));
|
||||
}
|
||||
113
src/nsig.ts
Normal file
113
src/nsig.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import {
|
||||
type ArrowFunctionExpression,
|
||||
type Node,
|
||||
} from "npm:@babel/types@7.28.2";
|
||||
import { matchesStructure } from "./utils.ts";
|
||||
import { type DeepPartial } from "./types.ts";
|
||||
|
||||
const identifier: DeepPartial<Node> = {
|
||||
type: "AssignmentExpression",
|
||||
operator: "=",
|
||||
left: {
|
||||
type: "Identifier",
|
||||
},
|
||||
right: {
|
||||
type: "FunctionExpression",
|
||||
params: [{}],
|
||||
body: {
|
||||
type: "BlockStatement",
|
||||
body: [
|
||||
{
|
||||
type: "ReturnStatement",
|
||||
// {
|
||||
argument:
|
||||
// or: [
|
||||
{
|
||||
type: "CallExpression",
|
||||
callee: {
|
||||
type: "MemberExpression",
|
||||
object: {
|
||||
type: "Identifier",
|
||||
// XXX: get switch function identifier, use here
|
||||
// name: "Lb",
|
||||
},
|
||||
property: {
|
||||
type: "MemberExpression",
|
||||
object: {
|
||||
type: "Identifier",
|
||||
// XXX: get global string store identifier, use here
|
||||
// name: "Y",
|
||||
},
|
||||
property: {
|
||||
type: "NumericLiteral",
|
||||
},
|
||||
},
|
||||
},
|
||||
arguments: [
|
||||
{ type: "ThisExpression" },
|
||||
{ type: "NumericLiteral" },
|
||||
{
|
||||
type: "Identifier",
|
||||
// XXX: get parameter name, use here
|
||||
// name: "Y",
|
||||
},
|
||||
],
|
||||
},
|
||||
// XXX: possible to be a direct call, ignore that for now
|
||||
// {
|
||||
// type: "CallExpression",
|
||||
// callee: {
|
||||
// type: "Identifier",
|
||||
// // XXX: get global string store identifier, use here
|
||||
// // name: "Y",
|
||||
// },
|
||||
// arguments: [
|
||||
// { type: "NumericLiteral" },
|
||||
// {
|
||||
// type: "Identifier",
|
||||
// // XXX: get parameter name, use here
|
||||
// // name: "Y",
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function extract(node: Node): ArrowFunctionExpression | null {
|
||||
if (!matchesStructure(node, identifier)) {
|
||||
return null;
|
||||
}
|
||||
if (node.type !== "AssignmentExpression" || node.left.type !== "Identifier") {
|
||||
return null;
|
||||
}
|
||||
// TODO: verify identifiers here
|
||||
return {
|
||||
type: "ArrowFunctionExpression",
|
||||
params: [
|
||||
{
|
||||
type: "Identifier",
|
||||
name: "nsig",
|
||||
},
|
||||
],
|
||||
async: false,
|
||||
expression: true,
|
||||
body: {
|
||||
type: "CallExpression",
|
||||
callee: {
|
||||
type: "Identifier",
|
||||
name: node.left.name,
|
||||
},
|
||||
arguments: [
|
||||
{
|
||||
type: "Identifier",
|
||||
name: "nsig",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
122
src/sig.ts
Normal file
122
src/sig.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import {
|
||||
type ArrowFunctionExpression,
|
||||
type Node,
|
||||
} from "npm:@babel/types@7.28.2";
|
||||
import { matchesStructure } from "./utils.ts";
|
||||
import { type DeepPartial } from "./types.ts";
|
||||
|
||||
const identifier: DeepPartial<Node> = {
|
||||
type: "AssignmentExpression",
|
||||
operator: "=",
|
||||
left: {
|
||||
type: "Identifier",
|
||||
},
|
||||
right: {
|
||||
type: "FunctionExpression",
|
||||
params: [{}, {}, {}],
|
||||
body: {
|
||||
type: "BlockStatement",
|
||||
body: [
|
||||
{ type: "ExpressionStatement" },
|
||||
{ type: "ExpressionStatement" },
|
||||
{ type: "ExpressionStatement" },
|
||||
{ type: "ExpressionStatement" },
|
||||
{
|
||||
type: "ExpressionStatement",
|
||||
expression: {
|
||||
type: "LogicalExpression",
|
||||
left: {
|
||||
type: "Identifier",
|
||||
// name: "M",
|
||||
},
|
||||
operator: "&&",
|
||||
right: {
|
||||
type: "SequenceExpression",
|
||||
expressions: [
|
||||
{
|
||||
type: "AssignmentExpression",
|
||||
operator: "=",
|
||||
left: {
|
||||
type: "Identifier",
|
||||
},
|
||||
right: {
|
||||
type: "CallExpression",
|
||||
callee: {
|
||||
type: "Identifier",
|
||||
},
|
||||
arguments: [
|
||||
{ type: "NumericLiteral" },
|
||||
{
|
||||
type: "CallExpression",
|
||||
callee: {
|
||||
type: "Identifier",
|
||||
name: "decodeURIComponent",
|
||||
},
|
||||
arguments: [{ type: "Identifier" }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "CallExpression",
|
||||
},
|
||||
],
|
||||
extra: {
|
||||
parenthesized: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ type: "ReturnStatement" },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function extract(node: Node): ArrowFunctionExpression | null {
|
||||
if (!matchesStructure(node, identifier)) {
|
||||
return null;
|
||||
}
|
||||
// shut the type checker up
|
||||
if (
|
||||
node.type !== "AssignmentExpression" ||
|
||||
node.right.type !== "FunctionExpression" ||
|
||||
node.right.body.body[4].type !== "ExpressionStatement" ||
|
||||
node.right.body.body[4].expression.type !== "LogicalExpression" ||
|
||||
node.right.body.body[4].expression.right.type !== "SequenceExpression" ||
|
||||
node.right.body.body[4].expression.right.expressions[0].type !==
|
||||
"AssignmentExpression"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const call = node.right.body.body[4].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",
|
||||
},
|
||||
],
|
||||
async: false,
|
||||
expression: true,
|
||||
body: {
|
||||
type: "CallExpression",
|
||||
callee: {
|
||||
type: "Identifier",
|
||||
name: call.callee.name,
|
||||
},
|
||||
arguments: [
|
||||
call.arguments[0],
|
||||
{
|
||||
type: "Identifier",
|
||||
name: "sig",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
103
src/solvers.ts
Executable file
103
src/solvers.ts
Executable file
@@ -0,0 +1,103 @@
|
||||
import { parse } from "npm:@babel/parser@7.28.3";
|
||||
import { generate } from "npm:@babel/generator@7.28.3";
|
||||
import { type ArrowFunctionExpression } from "npm:@babel/types@7.28.2";
|
||||
import { getFunctionNodes } from "./utils.ts";
|
||||
import { extract as extractSig } from "./sig.ts";
|
||||
import { extract as extractNsig } from "./nsig.ts";
|
||||
|
||||
function setup() {
|
||||
// @ts-ignore: This is used in the babel generated js
|
||||
globalThis.XMLHttpRequest = { prototype: {} };
|
||||
// deno-lint-ignore no-unused-vars
|
||||
const window = Object.assign(Object.create(null), globalThis);
|
||||
// deno-lint-ignore no-unused-vars
|
||||
const document = {};
|
||||
}
|
||||
|
||||
// helper functions
|
||||
export function getSolvers(data: string): {
|
||||
nsig: ((val: string) => string) | null;
|
||||
sig: ((val: string) => string) | null;
|
||||
} {
|
||||
const ast = parse(data, {
|
||||
attachComment: false,
|
||||
});
|
||||
const body = ast.program.body;
|
||||
if (body.length !== 2 || body[1].type !== "ExpressionStatement") {
|
||||
throw "unexpected structure";
|
||||
}
|
||||
const func = body[1];
|
||||
if (
|
||||
func.expression.type !== "CallExpression" ||
|
||||
func.expression.callee.type !== "FunctionExpression"
|
||||
) {
|
||||
throw "unexpected structure";
|
||||
}
|
||||
const found = {
|
||||
nsig: [] as ArrowFunctionExpression[],
|
||||
sig: [] as ArrowFunctionExpression[],
|
||||
};
|
||||
const plainExpressions = func.expression.callee.body.body.filter(
|
||||
(node, idx) => {
|
||||
if (idx === 0) {
|
||||
// Ignore `var window = this;`
|
||||
return false;
|
||||
}
|
||||
if (node.type === "ExpressionStatement") {
|
||||
if (node.expression.type === "AssignmentExpression") {
|
||||
const nsig = extractNsig(node.expression);
|
||||
if (nsig) {
|
||||
found.nsig.push(nsig);
|
||||
}
|
||||
const sig = extractSig(node.expression);
|
||||
if (sig) {
|
||||
found.sig.push(sig);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return node.expression.type === "StringLiteral";
|
||||
}
|
||||
return true;
|
||||
},
|
||||
);
|
||||
func.expression.callee.body.body = plainExpressions;
|
||||
|
||||
for (const [name, options] of Object.entries(found)) {
|
||||
if (options.length !== 1) {
|
||||
continue;
|
||||
}
|
||||
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],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
ast.program.body.splice(0, 0, ...getFunctionNodes(setup));
|
||||
|
||||
const { code } = generate(ast, {
|
||||
comments: false,
|
||||
compact: false,
|
||||
concise: false,
|
||||
});
|
||||
|
||||
// evil eval!!?!
|
||||
const resultObj = { nsig: null, sig: null };
|
||||
Function("_result", code)(resultObj);
|
||||
return resultObj;
|
||||
}
|
||||
22
src/solvers_test.ts
Normal file
22
src/solvers_test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { assertStrictEquals } from "jsr:@std/assert@1";
|
||||
import { getSolvers } from "./solvers.ts";
|
||||
import { players, tests } from "../tests/tests.ts";
|
||||
import { getCachePath } from "../tests/utils.ts";
|
||||
|
||||
for (const test of tests) {
|
||||
for (const variant of test.variants ?? players.keys()) {
|
||||
const path = getCachePath(test.player, variant);
|
||||
Deno.test(`${test.player} ${variant}`, async (t) => {
|
||||
const content = await Deno.readTextFile(path);
|
||||
const solvers = getSolvers(content);
|
||||
for (const mode of ["nsig", "sig"] as const) {
|
||||
for (const step of test[mode] || []) {
|
||||
await t.step(`${step.input} (${mode})`, () => {
|
||||
const got = solvers[mode]?.(step.input);
|
||||
assertStrictEquals(got, step.expected);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
4
src/types.ts
Normal file
4
src/types.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type DeepPartial<T> = T extends object ? {
|
||||
[P in keyof T]?: DeepPartial<T[P]>;
|
||||
}
|
||||
: T;
|
||||
49
src/utils.ts
Normal file
49
src/utils.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { parse } from "npm:@babel/parser@7.28.3";
|
||||
import { type Node, type Statement } from "npm:@babel/types@7.28.2";
|
||||
import { type DeepPartial } from "./types.ts";
|
||||
|
||||
export function matchesStructure<T extends Node>(
|
||||
obj: Node | Node[],
|
||||
structure: DeepPartial<T> | readonly DeepPartial<T>[],
|
||||
): obj is T {
|
||||
if (Array.isArray(structure)) {
|
||||
if (!Array.isArray(obj)) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
structure.length === obj.length &&
|
||||
structure.every((value, index) => matchesStructure(obj[index], value))
|
||||
);
|
||||
}
|
||||
if (typeof structure === "object") {
|
||||
if (!obj) {
|
||||
return !structure;
|
||||
}
|
||||
if ("or" in structure) {
|
||||
// Allow `{ or: [a, b] }` so we can handle some special cases
|
||||
return (structure.or! as DeepPartial<Node>[]).some((node) =>
|
||||
matchesStructure(obj, node)
|
||||
);
|
||||
}
|
||||
for (const [key, value] of Object.entries(structure)) {
|
||||
if (!matchesStructure(obj[key as keyof typeof obj], value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return structure === obj;
|
||||
}
|
||||
|
||||
export function getFunctionNodes(f: (...a: unknown[]) => void): Statement[] {
|
||||
const func = parse(f.toString()).program.body[0];
|
||||
if (func.type === "FunctionDeclaration") {
|
||||
return func.body.body;
|
||||
}
|
||||
console.error("failed to parse function into nodes");
|
||||
return [];
|
||||
}
|
||||
|
||||
export function isOneOf<T>(value: unknown, ...of: readonly T[]): value is T {
|
||||
return of.includes(value as T);
|
||||
}
|
||||
27
tests/download.ts
Normal file
27
tests/download.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { exists } from "jsr:@std/fs/exists";
|
||||
|
||||
import { tests, players } from "./tests.ts";
|
||||
import { getCachePath } 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 exists(path)) {
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
const file = await Deno.open(path, {
|
||||
createNew: true,
|
||||
write: true,
|
||||
});
|
||||
response.body!.pipeTo(file.writable);
|
||||
}
|
||||
}
|
||||
2
tests/players/.gitignore
vendored
Normal file
2
tests/players/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!/.gitignore
|
||||
71
tests/tests.ts
Normal file
71
tests/tests.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
type Step = {
|
||||
input: string;
|
||||
expected: string;
|
||||
};
|
||||
|
||||
export const tests: {
|
||||
player: string;
|
||||
variants?: Variant[];
|
||||
nsig?: Step[];
|
||||
sig?: Step[];
|
||||
}[] = [
|
||||
{
|
||||
player: "3d3ba064",
|
||||
variants: ["tce"],
|
||||
nsig: [
|
||||
{ input: "ZdZIqFPQK-Ty8wId", expected: "qmtUsIz04xxiNW" },
|
||||
{ input: "4GMrWHyKI5cEvhDO", expected: "N9gmEX7YhKTSmw" },
|
||||
],
|
||||
sig: [
|
||||
{
|
||||
input:
|
||||
"gN7a-hudCuAuPH6fByOk1_GNXN0yNMHShjZXS2VOgsEItAJz0tipeavEOmNdYN-wUtcEqD3bCXjc0iyKfAyZxCBGgIARwsSdQfJ2CJtt",
|
||||
expected:
|
||||
"ttJC2JfQdSswRAIgGBCxZyAfKyi0cjXCb3gqEctUw-NYdNmOEvaepit0zJAtIEsgOV2SXZjhSHMNy0NXNG_1kNyBf6HPuAuCduh-a7O",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
player: "5ec65609",
|
||||
variants: ["tce"],
|
||||
nsig: [{ input: "0eRGgQWJGfT5rFHFj", expected: "4SvMpDQH-vBJCw" }],
|
||||
sig: [
|
||||
{
|
||||
input:
|
||||
"AAJAJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grH0rTMICA1mmDc0HoXgW3CAiAQQ4=CspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ=I",
|
||||
expected:
|
||||
"AJfQdSswRQIhAMG5SN7-cAFChdrE7tLA6grI0rTMICA1mmDc0HoXgW3CAiAQQ4HCspfaF_vt82XH5yewvqcuEkvzeTsbRuHssRMyJQ==",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
player: "6742b2b9",
|
||||
variants: ["tce"],
|
||||
nsig: [
|
||||
{ input: "_HPB-7GFg1VTkn9u", expected: "qUAsPryAO_ByYg" },
|
||||
{ input: "K1t_fcB6phzuq2SF", expected: "Y7PcOt3VE62mog" },
|
||||
],
|
||||
sig: [
|
||||
{
|
||||
input:
|
||||
"MMGZJMUucirzS_SnrSPYsc85CJNnTUi6GgR5NKn-znQEICACojE8MHS6S7uYq4TGjQX_D4aPk99hNU6wbTvorvVVMgIARwsSdQfJAA",
|
||||
expected:
|
||||
"AJfQdSswRAIgMVVvrovTbw6UNh99kPa4D_XQjGT4qYu7S6SHM8EjoCACIEQnz-nKN5RgG6iUTnNJC58csYPSrnS_SzricuUMJZGM",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
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"],
|
||||
] as const);
|
||||
|
||||
export type Variant = (typeof players) extends Map<infer T, unknown> ? T : never;
|
||||
5
tests/utils.ts
Normal file
5
tests/utils.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { type Variant } from "./tests.ts";
|
||||
|
||||
export function getCachePath(player: string, variant: Variant) {
|
||||
return `tests/players/${player}_${variant}.js`;
|
||||
}
|
||||
20
yt_dlp_jsc_deno/__init__.py
Normal file
20
yt_dlp_jsc_deno/__init__.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import importlib.resources
|
||||
import importlib.metadata
|
||||
|
||||
import yt_dlp_jsc_deno
|
||||
|
||||
_name = "dist/jsc-deno.js"
|
||||
|
||||
version = importlib.metadata.version(yt_dlp_jsc_deno.__name__)
|
||||
|
||||
|
||||
def exists() -> bool:
|
||||
return importlib.resources.is_resource(yt_dlp_jsc_deno, _name)
|
||||
|
||||
|
||||
def read() -> str:
|
||||
return importlib.resources.read_text(yt_dlp_jsc_deno, _name)
|
||||
|
||||
|
||||
def path():
|
||||
return importlib.resources.path(yt_dlp_jsc_deno, _name)
|
||||
Reference in New Issue
Block a user