clearcote 0.47.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -14
- package/dist/cloud.d.ts.map +1 -1
- package/dist/cloud.js +5 -0
- package/dist/cloud.js.map +1 -1
- package/dist/docker.d.ts.map +1 -1
- package/dist/docker.js +5 -1
- package/dist/docker.js.map +1 -1
- package/dist/download.d.ts +2 -0
- package/dist/download.d.ts.map +1 -1
- package/dist/download.js +2 -1
- package/dist/download.js.map +1 -1
- package/dist/forms.d.ts +198 -0
- package/dist/forms.d.ts.map +1 -0
- package/dist/forms.js +632 -0
- package/dist/forms.js.map +1 -0
- package/dist/humanize.d.ts +37 -0
- package/dist/humanize.d.ts.map +1 -1
- package/dist/humanize.js +1207 -323
- package/dist/humanize.js.map +1 -1
- package/dist/index.d.ts +19 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +26 -12
- package/dist/index.js.map +1 -1
- package/dist/isolated.d.ts +71 -3
- package/dist/isolated.d.ts.map +1 -1
- package/dist/isolated.js +226 -26
- package/dist/isolated.js.map +1 -1
- package/dist/widevine.d.ts +90 -5
- package/dist/widevine.d.ts.map +1 -1
- package/dist/widevine.js +391 -40
- package/dist/widevine.js.map +1 -1
- package/package.json +1 -1
package/dist/isolated.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"isolated.js","sourceRoot":"","sources":["../src/isolated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,OAAO,aAAa;
|
|
1
|
+
{"version":3,"file":"isolated.js","sourceRoot":"","sources":["../src/isolated.ts"],"names":[],"mappings":"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"}
|
package/dist/widevine.d.ts
CHANGED
|
@@ -1,12 +1,40 @@
|
|
|
1
|
+
/** 1/true/yes/on forces Widevine on, 0/false/no/off off; an explicit `widevine` option wins over it. */
|
|
2
|
+
export declare const WIDEVINE_ENV = "CLEARCOTE_WIDEVINE";
|
|
3
|
+
/** How long a DEFAULT (not requested) launch waits for a first download, in seconds (clamped to 1-600). */
|
|
4
|
+
export declare const TIMEOUT_ENV = "CLEARCOTE_WIDEVINE_TIMEOUT_SECONDS";
|
|
5
|
+
export declare const DEFAULT_TIMEOUT_SECONDS = 15;
|
|
6
|
+
export declare const MIN_TIMEOUT_SECONDS = 1;
|
|
7
|
+
export declare const MAX_TIMEOUT_SECONDS = 600;
|
|
8
|
+
/** Ask Google for a newer CDM at most once a day. */
|
|
9
|
+
export declare const CHECK_INTERVAL_MS: number;
|
|
10
|
+
/** A failed first download is not retried for 10 minutes in this process. */
|
|
11
|
+
export declare const RETRY_AFTER_MS = 600000;
|
|
12
|
+
/** How long a background refresh waits for Google's update check (it may hold a finishing process that long). */
|
|
13
|
+
export declare const REFRESH_CHECK_TIMEOUT_MS = 10000;
|
|
14
|
+
/** In the cache root: its mtime is the last update check (a failed one counts). */
|
|
15
|
+
export declare const STAMP_FILE = ".last-check";
|
|
16
|
+
/** What a CDM version looks like; anything else never becomes a path. */
|
|
17
|
+
export declare const VERSION_RE: RegExp;
|
|
18
|
+
/** How long a requested install waits for another process installing the CDM. */
|
|
19
|
+
export declare const LOCK_WAIT_MS = 300000;
|
|
20
|
+
export declare const WARN_CODE = "widevine-default";
|
|
21
|
+
/** What one launch does about Widevine: requested, the licensed default, or nothing. */
|
|
22
|
+
export type WidevineMode = "explicit" | "default" | "off";
|
|
1
23
|
/** Chrome's component-updater app id for the Widevine CDM + Google's Omaha JSON endpoint. */
|
|
2
24
|
export declare const WIDEVINE_APP_ID = "oimompecagnajdejgnnjijobebaeigek";
|
|
3
25
|
export declare const OMAHA_URL = "https://update.googleapis.com/service/update2/json";
|
|
4
26
|
export declare const HINT_FILE = "latest-component-updated-widevine-cdm";
|
|
5
27
|
export interface WidevineOptions {
|
|
6
|
-
/** Destination root (default ~/.clearcote/WidevineCdm). */
|
|
28
|
+
/** Destination root (default CLEARCOTE_WIDEVINE_DIR, else ~/.clearcote/WidevineCdm). */
|
|
7
29
|
dest?: string;
|
|
8
30
|
/** Suppress progress logging. */
|
|
9
31
|
quiet?: boolean;
|
|
32
|
+
/** Abort the update check and the download (the default launch path bounds its wait with it). */
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
/** Abort only the update check (a background refresh bounds it). */
|
|
35
|
+
checkSignal?: AbortSignal;
|
|
36
|
+
/** How long to wait for another process installing the CDM (default 5 minutes). */
|
|
37
|
+
lockTimeoutMs?: number;
|
|
10
38
|
}
|
|
11
39
|
/** Minimal Omaha v3.1 update check for the current-OS x64 Widevine CDM (version 0.0.0.0 -> latest). */
|
|
12
40
|
export declare function omahaRequestBody(): unknown;
|
|
@@ -14,15 +42,60 @@ export declare function omahaRequestBody(): unknown;
|
|
|
14
42
|
export declare function parseUpdate(resp: any): [string, string, string];
|
|
15
43
|
/** A CRX3 file is 'Cr24' + u32 version + u32 headerLen + header + zip. Return the zip bytes. */
|
|
16
44
|
export declare function crx3ToZip(buf: Buffer): Buffer;
|
|
45
|
+
/** The newest complete CDM in the cache (`root`, default the cache root), or undefined. No network. */
|
|
46
|
+
export declare function cachedCdm(root?: string): string | undefined;
|
|
17
47
|
/**
|
|
18
|
-
* Download + verify the current-OS x64 Widevine CDM into `dest` (default
|
|
19
|
-
* <version
|
|
20
|
-
*
|
|
48
|
+
* Download + verify the current-OS x64 Widevine CDM into `dest` (default CLEARCOTE_WIDEVINE_DIR, else
|
|
49
|
+
* ~/.clearcote/WidevineCdm) as <version>/. Returns the versioned CDM directory (manifest.json +
|
|
50
|
+
* _platform_specific/<subdir>/<filename>).
|
|
51
|
+
*
|
|
52
|
+
* Always asks Google's update server for the latest version; the download is skipped when that version is
|
|
53
|
+
* already installed. Installs take the install lock and land with a rename. A version that does not look
|
|
54
|
+
* like one is refused.
|
|
21
55
|
*/
|
|
22
56
|
export declare function fetchWidevine(opts?: WidevineOptions): Promise<string>;
|
|
57
|
+
/**
|
|
58
|
+
* The CDM directory a launch should use, network only when needed: the cached CDM when Google was asked
|
|
59
|
+
* within the last day (no network), otherwise {@link fetchWidevine}. When that fails (offline, a firewall,
|
|
60
|
+
* Google refusing the request) the CDM already on disk still works and is used, and the failed check is
|
|
61
|
+
* recorded (the stamp is touched), so no launch asks again for a day. Rejects only when there is no CDM.
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveCdm(opts?: WidevineOptions): Promise<string>;
|
|
64
|
+
/**
|
|
65
|
+
* `"explicit"`, `"default"` or `"off"` for a launch. `option` (the `widevine` launch option) wins: true ->
|
|
66
|
+
* explicit, false -> off. Unset follows CLEARCOTE_WIDEVINE (1/true/yes/on -> explicit, 0/false/no/off ->
|
|
67
|
+
* off). Otherwise a licensed launch (a licence key resolved, which selects the PRO build) gets the default
|
|
68
|
+
* and a free launch stays off. Same rules in the Python and .NET SDKs.
|
|
69
|
+
*/
|
|
70
|
+
export declare function widevineMode(option: boolean | undefined | null, licensed: boolean, env?: NodeJS.ProcessEnv): WidevineMode;
|
|
71
|
+
/** How long a default launch waits for a first download: CLEARCOTE_WIDEVINE_TIMEOUT_SECONDS clamped to 1-600, else 15
|
|
72
|
+
* (an unparsable or non-positive value too). */
|
|
73
|
+
export declare function timeoutSeconds(env?: NodeJS.ProcessEnv): number;
|
|
74
|
+
/** Tests: forget the in-flight fetch, the refresh and the last failure (and stop what is running). */
|
|
75
|
+
export declare function resetWidevineDefaultState(): void;
|
|
76
|
+
/** Tests: the background refresh in flight, if any. */
|
|
77
|
+
export declare function widevineRefreshInFlight(): Promise<void> | undefined;
|
|
78
|
+
/** Tests: pretend the last failure was this long ago. */
|
|
79
|
+
export declare function ageLastWidevineFailure(byMs: number): void;
|
|
80
|
+
/**
|
|
81
|
+
* The CDM for a launch that did not ask for Widevine (the licensed default), or undefined.
|
|
82
|
+
*
|
|
83
|
+
* A CDM in the cache is used AT ONCE, with no network on the launch path; when its last update check is over a
|
|
84
|
+
* day old, a background refresh asks Google for a newer one (used from the next launch on). Only with nothing
|
|
85
|
+
* cached does the launch wait for a download, at most `timeoutMs` (default {@link timeoutSeconds}). FAIL-SOFT:
|
|
86
|
+
* never rejects. When there is no CDM to use (the download failed or did not finish in time) it warns once per
|
|
87
|
+
* process (unless quiet) and answers undefined, so the launch goes on without DRM; a download still running at
|
|
88
|
+
* the deadline is cancelled (it cannot keep the process alive), and a failed one is not retried for
|
|
89
|
+
* RETRY_AFTER_MS in this process. The fetches here are quiet: the default path prints nothing else.
|
|
90
|
+
*/
|
|
91
|
+
export declare function defaultCdm(opts?: {
|
|
92
|
+
quiet?: boolean;
|
|
93
|
+
timeoutMs?: number;
|
|
94
|
+
}): Promise<string | undefined>;
|
|
23
95
|
/**
|
|
24
96
|
* Make a persistent profile load the fetched CDM: copy it under <userDataDir>/WidevineCdm/<version>/
|
|
25
|
-
* and write the component hint file the engine reads. Fetches the CDM first if needed
|
|
97
|
+
* and write the component hint file the engine reads. Fetches the CDM first if needed
|
|
98
|
+
* ({@link resolveCdm}: the cached one when it is current).
|
|
26
99
|
*/
|
|
27
100
|
export declare function seedWidevine(userDataDir: string, opts?: WidevineOptions): Promise<string>;
|
|
28
101
|
/**
|
|
@@ -51,4 +124,16 @@ export declare function widevineArgs(ignoreDefaultArgs: string[] | boolean | und
|
|
|
51
124
|
export declare function widevineCdmArgs(exe: string | undefined, opts?: {
|
|
52
125
|
quiet?: boolean;
|
|
53
126
|
}): Promise<string[]>;
|
|
127
|
+
/**
|
|
128
|
+
* The Widevine switch for one launch, by {@link widevineMode}. explicit: {@link widevineCdmArgs} (the
|
|
129
|
+
* behaviour `widevine: true` always had). default: the same switch, only on an engine that implements it
|
|
130
|
+
* (r32+; an older engine keeps the old default, off), with the CDM from {@link defaultCdm} (bounded,
|
|
131
|
+
* fail-soft, warns once). off: nothing. A `--widevine-cdm-path` the caller put in `args` is theirs:
|
|
132
|
+
* nothing is added then. Works for every launch path: such an engine loads the CDM without a profile,
|
|
133
|
+
* incognito included.
|
|
134
|
+
*/
|
|
135
|
+
export declare function widevineLaunchArgs(exe: string | undefined, option: boolean | undefined | null, licensed: boolean, userArgs: readonly string[] | undefined, opts?: {
|
|
136
|
+
quiet?: boolean;
|
|
137
|
+
timeoutMs?: number;
|
|
138
|
+
}): Promise<string[]>;
|
|
54
139
|
//# sourceMappingURL=widevine.d.ts.map
|
package/dist/widevine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widevine.d.ts","sourceRoot":"","sources":["../src/widevine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"widevine.d.ts","sourceRoot":"","sources":["../src/widevine.ts"],"names":[],"mappings":"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"}
|
package/dist/widevine.js
CHANGED
|
@@ -1,21 +1,63 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Widevine CDM fetch + persistent-profile seeding (Windows + Linux).
|
|
2
2
|
//
|
|
3
3
|
// clearcote is a 100%-open-source build: it ships the EME/Widevine *plumbing* (enable_widevine=true)
|
|
4
|
-
// but NOT Google's proprietary CDM binary (that blob can't live in a FOSS package). This module
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// but NOT Google's proprietary CDM binary (that blob can't live in a FOSS package). This module fetches
|
|
5
|
+
// the CDM from Google's component server at runtime — exactly how a real Chrome receives it via the
|
|
6
|
+
// component updater — so requestMediaKeySystemAccess('com.widevine.alpha') resolves (DRM plays, and the
|
|
7
|
+
// EME surface matches a real Chrome instead of a no-Widevine tell).
|
|
8
8
|
//
|
|
9
9
|
// import { fetchWidevine, launchPersistentContext } from "clearcote";
|
|
10
10
|
// await fetchWidevine(); // download + verify into ~/.clearcote
|
|
11
11
|
// const ctx = await launchPersistentContext("profile", { widevine: true }); // seeds + enables it
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
//
|
|
13
|
+
// WHEN IT IS ON (since 0.48.0). A licensed launch (a licence key resolved: the launch that selects the
|
|
14
|
+
// PRO build, which claims the Google Chrome brand) on an engine with --widevine-cdm-path (r32+) loads the
|
|
15
|
+
// CDM by default; a free launch keeps it opt-in. `widevine: false` or CLEARCOTE_WIDEVINE=0 turns it off,
|
|
16
|
+
// `widevine: true` or CLEARCOTE_WIDEVINE=1 on for any launch; an explicit option wins over the
|
|
17
|
+
// environment (see widevineMode). The default is FAIL-SOFT and never waits on the network when a CDM is
|
|
18
|
+
// cached: a cached CDM is used at once (a stale one is refreshed in the background). Only the very first
|
|
19
|
+
// launch, with nothing cached, waits for the download, at most CLEARCOTE_WIDEVINE_TIMEOUT_SECONDS (default
|
|
20
|
+
// 15, clamped to 1-600); when that fails the launch goes on without Widevine and says so once per process.
|
|
21
|
+
// The default path prints nothing else. A launch that ASKED for Widevine keeps its old behaviour (a note,
|
|
22
|
+
// unless quiet, and no DRM).
|
|
23
|
+
//
|
|
24
|
+
// The CDM is cached (CLEARCOTE_WIDEVINE_DIR, else ~/.clearcote/WidevineCdm); Google is asked for a newer
|
|
25
|
+
// one at most once a day, and a failed check counts as one (the cached CDM keeps working). Installs take
|
|
26
|
+
// the SDK's install lock in the cache directory and land with download.ts's rename (retried on Windows
|
|
27
|
+
// while a scanner holds the fresh tree), so concurrent first launches never see a half-extracted CDM. The
|
|
28
|
+
// version Google names must look like one before it is used as a directory name. Same rules, names and
|
|
29
|
+
// warning as the Python and .NET SDKs.
|
|
30
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
31
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
15
32
|
import os from "node:os";
|
|
16
33
|
import path from "node:path";
|
|
34
|
+
import { performance } from "node:perf_hooks";
|
|
17
35
|
import extract from "extract-zip";
|
|
36
|
+
import { acquireInstallLock, renameDir } from "./download.js";
|
|
18
37
|
import { engineSupportsSwitch } from "./launchopts.js";
|
|
38
|
+
import { warnOnce } from "./warnings.js";
|
|
39
|
+
/** 1/true/yes/on forces Widevine on, 0/false/no/off off; an explicit `widevine` option wins over it. */
|
|
40
|
+
export const WIDEVINE_ENV = "CLEARCOTE_WIDEVINE";
|
|
41
|
+
/** How long a DEFAULT (not requested) launch waits for a first download, in seconds (clamped to 1-600). */
|
|
42
|
+
export const TIMEOUT_ENV = "CLEARCOTE_WIDEVINE_TIMEOUT_SECONDS";
|
|
43
|
+
export const DEFAULT_TIMEOUT_SECONDS = 15;
|
|
44
|
+
export const MIN_TIMEOUT_SECONDS = 1;
|
|
45
|
+
export const MAX_TIMEOUT_SECONDS = 600;
|
|
46
|
+
/** Ask Google for a newer CDM at most once a day. */
|
|
47
|
+
export const CHECK_INTERVAL_MS = 24 * 3600_000;
|
|
48
|
+
/** A failed first download is not retried for 10 minutes in this process. */
|
|
49
|
+
export const RETRY_AFTER_MS = 600_000;
|
|
50
|
+
/** How long a background refresh waits for Google's update check (it may hold a finishing process that long). */
|
|
51
|
+
export const REFRESH_CHECK_TIMEOUT_MS = 10_000;
|
|
52
|
+
/** In the cache root: its mtime is the last update check (a failed one counts). */
|
|
53
|
+
export const STAMP_FILE = ".last-check";
|
|
54
|
+
/** What a CDM version looks like; anything else never becomes a path. */
|
|
55
|
+
export const VERSION_RE = /^[0-9]+(\.[0-9]+){1,3}$/;
|
|
56
|
+
/** How long a requested install waits for another process installing the CDM. */
|
|
57
|
+
export const LOCK_WAIT_MS = 300_000;
|
|
58
|
+
export const WARN_CODE = "widevine-default";
|
|
59
|
+
const TRUTHY = ["1", "true", "yes", "on"];
|
|
60
|
+
const FALSY = ["0", "false", "no", "off"];
|
|
19
61
|
/** Chrome's component-updater app id for the Widevine CDM + Google's Omaha JSON endpoint. */
|
|
20
62
|
export const WIDEVINE_APP_ID = "oimompecagnajdejgnnjijobebaeigek";
|
|
21
63
|
export const OMAHA_URL = "https://update.googleapis.com/service/update2/json";
|
|
@@ -59,13 +101,28 @@ export function omahaRequestBody() {
|
|
|
59
101
|
},
|
|
60
102
|
};
|
|
61
103
|
}
|
|
62
|
-
|
|
104
|
+
/** `AbortSignal.timeout(ms)`, also aborted with `signal` (AbortSignal.any is Node 20.3+). */
|
|
105
|
+
function withTimeout(ms, signal) {
|
|
106
|
+
const timeout = AbortSignal.timeout(ms);
|
|
107
|
+
if (!signal)
|
|
108
|
+
return timeout;
|
|
109
|
+
const both = new AbortController();
|
|
110
|
+
const stop = (s) => () => both.abort(s.reason);
|
|
111
|
+
if (signal.aborted)
|
|
112
|
+
both.abort(signal.reason);
|
|
113
|
+
else {
|
|
114
|
+
signal.addEventListener("abort", stop(signal), { once: true });
|
|
115
|
+
timeout.addEventListener("abort", stop(timeout), { once: true });
|
|
116
|
+
}
|
|
117
|
+
return both.signal;
|
|
118
|
+
}
|
|
119
|
+
async function postJson(url, body, signal) {
|
|
63
120
|
const res = await fetch(url, {
|
|
64
121
|
method: "POST",
|
|
65
122
|
headers: { "Content-Type": "application/json", "User-Agent": UA },
|
|
66
123
|
body: JSON.stringify(body),
|
|
67
124
|
redirect: "follow",
|
|
68
|
-
signal:
|
|
125
|
+
signal: withTimeout(60_000, signal),
|
|
69
126
|
});
|
|
70
127
|
if (!res.ok)
|
|
71
128
|
throw new Error(`Widevine update check HTTP ${res.status}`);
|
|
@@ -105,54 +162,326 @@ export function crx3ToZip(buf) {
|
|
|
105
162
|
throw new Error("malformed CRX3 (header overruns buffer)");
|
|
106
163
|
return buf.subarray(12 + headerLen);
|
|
107
164
|
}
|
|
165
|
+
function cdmBinary(verDir) {
|
|
166
|
+
const { subdir, filename } = cdmPlatform();
|
|
167
|
+
return path.join(verDir, "_platform_specific", subdir, filename);
|
|
168
|
+
}
|
|
169
|
+
/** A complete CDM directory: its manifest and this OS's CDM binary are both there. */
|
|
170
|
+
function validCdm(verDir) {
|
|
171
|
+
return !!verDir && existsSync(cdmBinary(verDir)) && existsSync(path.join(verDir, "manifest.json"));
|
|
172
|
+
}
|
|
173
|
+
function versionKey(name) {
|
|
174
|
+
// "current" (no version in the update response) sorts below any real version
|
|
175
|
+
return /^\d+(\.\d+)*$/.test(name) ? [1, ...name.split(".").map(Number)] : [0];
|
|
176
|
+
}
|
|
177
|
+
function compareVersions(a, b) {
|
|
178
|
+
const ka = versionKey(a);
|
|
179
|
+
const kb = versionKey(b);
|
|
180
|
+
for (let i = 0; i < Math.max(ka.length, kb.length); i++) {
|
|
181
|
+
const d = (ka[i] ?? -1) - (kb[i] ?? -1);
|
|
182
|
+
if (d)
|
|
183
|
+
return d;
|
|
184
|
+
}
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
/** The newest complete CDM in the cache (`root`, default the cache root), or undefined. No network. */
|
|
188
|
+
export function cachedCdm(root = cacheRoot()) {
|
|
189
|
+
let names;
|
|
190
|
+
try {
|
|
191
|
+
names = readdirSync(root).filter((n) => !n.startsWith("."));
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
for (const name of names.sort(compareVersions).reverse()) {
|
|
197
|
+
const dir = path.join(root, name);
|
|
198
|
+
if (validCdm(dir))
|
|
199
|
+
return dir;
|
|
200
|
+
}
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
/** Whether Google was asked for a newer CDM less than CHECK_INTERVAL_MS ago. */
|
|
204
|
+
function stampFresh(root) {
|
|
205
|
+
try {
|
|
206
|
+
const age = Date.now() - statSync(path.join(root, STAMP_FILE)).mtimeMs;
|
|
207
|
+
return age > -3600_000 && age < CHECK_INTERVAL_MS; // a stamp from the future (clock moved back) is not fresh
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function touchStamp(root) {
|
|
214
|
+
try {
|
|
215
|
+
writeFileSync(path.join(root, STAMP_FILE), String(Math.floor(Date.now() / 1000)));
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
/* the next launch asks again */
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/** Remove a tree, never throwing (a Windows scanner can hold a file in it for a moment; the next install sweeps it). */
|
|
222
|
+
function removeQuietly(p) {
|
|
223
|
+
try {
|
|
224
|
+
rmSync(p, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
/* swept by the next install */
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** Remove what an interrupted install left in the cache root (only under the install lock). */
|
|
231
|
+
function sweepLeftovers(root) {
|
|
232
|
+
let names = [];
|
|
233
|
+
try {
|
|
234
|
+
names = readdirSync(root);
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
for (const n of names)
|
|
240
|
+
if (n.startsWith(".tmp-") || n.startsWith(".trash-"))
|
|
241
|
+
removeQuietly(path.join(root, n));
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* `root`/<version>, once `version` looks like a CDM version (`4.10.2830.0`). The version comes from Google's
|
|
245
|
+
* update response: anything else (`../victim`, an absolute path) is refused before it can name, or delete, a
|
|
246
|
+
* directory. No version at all means `current`.
|
|
247
|
+
*/
|
|
248
|
+
function versionDir(root, version) {
|
|
249
|
+
if (!version)
|
|
250
|
+
return path.join(root, "current");
|
|
251
|
+
if (!VERSION_RE.test(String(version))) {
|
|
252
|
+
throw new Error(`Widevine update response named an unexpected version ${JSON.stringify(String(version).slice(0, 80))} — refusing to use it`);
|
|
253
|
+
}
|
|
254
|
+
return path.join(root, version);
|
|
255
|
+
}
|
|
108
256
|
/**
|
|
109
|
-
* Download
|
|
110
|
-
*
|
|
111
|
-
*
|
|
257
|
+
* Download, verify and install one CDM version into `root`/<version>. Holds the SDK's install lock
|
|
258
|
+
* (`root`/.install-lock, the protocol in download.ts) so concurrent first launches take turns, extracts
|
|
259
|
+
* into `root`/.tmp-<id> and renames the finished tree into place: a reader never sees a half-extracted CDM.
|
|
260
|
+
* The renames are download.ts's, which retry while a Windows scanner holds a file in the fresh tree.
|
|
261
|
+
*/
|
|
262
|
+
async function install(root, url, sha256, version, opts) {
|
|
263
|
+
const verDir = versionDir(root, version);
|
|
264
|
+
mkdirSync(root, { recursive: true });
|
|
265
|
+
const lock = await acquireInstallLock(root, { timeoutMs: opts.lockTimeoutMs ?? LOCK_WAIT_MS, quiet: true });
|
|
266
|
+
try {
|
|
267
|
+
if (validCdm(verDir))
|
|
268
|
+
return verDir; // another process installed it while we waited
|
|
269
|
+
sweepLeftovers(root);
|
|
270
|
+
log(opts.quiet, `fetching CDM ${version || "(latest)"}`);
|
|
271
|
+
const res = await fetch(url, { headers: { "User-Agent": UA }, redirect: "follow", signal: withTimeout(120_000, opts.signal) });
|
|
272
|
+
if (!res.ok)
|
|
273
|
+
throw new Error(`Widevine CDM download HTTP ${res.status}`);
|
|
274
|
+
const blob = Buffer.from(await res.arrayBuffer());
|
|
275
|
+
if (createHash("sha256").update(blob).digest("hex") !== sha256.toLowerCase()) {
|
|
276
|
+
throw new Error("Widevine CDM sha256 mismatch — refusing to install");
|
|
277
|
+
}
|
|
278
|
+
const tmp = path.join(root, `.tmp-${randomBytes(8).toString("hex")}`);
|
|
279
|
+
try {
|
|
280
|
+
mkdirSync(tmp);
|
|
281
|
+
const zipPath = path.join(tmp, ".cdm.zip");
|
|
282
|
+
writeFileSync(zipPath, crx3ToZip(blob));
|
|
283
|
+
await extract(zipPath, { dir: tmp });
|
|
284
|
+
rmSync(zipPath, { force: true });
|
|
285
|
+
// some CRX layouts nest the platform dir differently; surface a clear error
|
|
286
|
+
if (!validCdm(tmp))
|
|
287
|
+
throw new Error(`extracted CDM but ${cdmPlatform().filename} not at ${cdmBinary(verDir)}`);
|
|
288
|
+
if (existsSync(verDir)) { // an incomplete tree (an older SDK extracted in place): move it aside
|
|
289
|
+
const trash = path.join(root, `.trash-${randomBytes(8).toString("hex")}`);
|
|
290
|
+
renameDir(verDir, trash);
|
|
291
|
+
removeQuietly(trash);
|
|
292
|
+
}
|
|
293
|
+
renameDir(tmp, verDir);
|
|
294
|
+
}
|
|
295
|
+
finally {
|
|
296
|
+
removeQuietly(tmp);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
finally {
|
|
300
|
+
await lock.release();
|
|
301
|
+
}
|
|
302
|
+
log(opts.quiet, `installed: ${verDir}`);
|
|
303
|
+
return verDir;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Download + verify the current-OS x64 Widevine CDM into `dest` (default CLEARCOTE_WIDEVINE_DIR, else
|
|
307
|
+
* ~/.clearcote/WidevineCdm) as <version>/. Returns the versioned CDM directory (manifest.json +
|
|
308
|
+
* _platform_specific/<subdir>/<filename>).
|
|
309
|
+
*
|
|
310
|
+
* Always asks Google's update server for the latest version; the download is skipped when that version is
|
|
311
|
+
* already installed. Installs take the install lock and land with a rename. A version that does not look
|
|
312
|
+
* like one is refused.
|
|
112
313
|
*/
|
|
113
314
|
export async function fetchWidevine(opts = {}) {
|
|
114
|
-
const [url, sha256, version] = parseUpdate(await postJson(OMAHA_URL, omahaRequestBody()));
|
|
315
|
+
const [url, sha256, version] = parseUpdate(await postJson(OMAHA_URL, omahaRequestBody(), opts.checkSignal ?? opts.signal));
|
|
115
316
|
const root = opts.dest || cacheRoot();
|
|
116
|
-
const verDir =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (existsSync(dll) && existsSync(path.join(verDir, "manifest.json"))) {
|
|
317
|
+
const verDir = versionDir(root, version);
|
|
318
|
+
if (validCdm(verDir)) {
|
|
319
|
+
touchStamp(root);
|
|
120
320
|
log(opts.quiet, `already present: ${verDir}`);
|
|
121
321
|
return verDir;
|
|
122
322
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (!res.ok)
|
|
126
|
-
throw new Error(`Widevine CDM download HTTP ${res.status}`);
|
|
127
|
-
const blob = Buffer.from(await res.arrayBuffer());
|
|
128
|
-
// The CDM is a NATIVE DLL loaded into the browser process — never install it unverified. A
|
|
129
|
-
// missing hash in the update response is a hard failure, not a skip.
|
|
323
|
+
// The CDM is a NATIVE library loaded into the browser process — never install it unverified. A
|
|
324
|
+
// missing hash in the update response is a hard failure, not a skip (checked before downloading).
|
|
130
325
|
if (!sha256)
|
|
131
326
|
throw new Error("Widevine update response had no sha256 — refusing to install an unverified CDM");
|
|
132
|
-
|
|
133
|
-
|
|
327
|
+
const dir = await install(root, url, sha256, version, opts);
|
|
328
|
+
touchStamp(root);
|
|
329
|
+
return dir;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* The CDM directory a launch should use, network only when needed: the cached CDM when Google was asked
|
|
333
|
+
* within the last day (no network), otherwise {@link fetchWidevine}. When that fails (offline, a firewall,
|
|
334
|
+
* Google refusing the request) the CDM already on disk still works and is used, and the failed check is
|
|
335
|
+
* recorded (the stamp is touched), so no launch asks again for a day. Rejects only when there is no CDM.
|
|
336
|
+
*/
|
|
337
|
+
export async function resolveCdm(opts = {}) {
|
|
338
|
+
const root = opts.dest || cacheRoot();
|
|
339
|
+
const cached = cachedCdm(root);
|
|
340
|
+
if (cached && stampFresh(root))
|
|
341
|
+
return cached;
|
|
342
|
+
try {
|
|
343
|
+
return await fetchWidevine({ ...opts, dest: root });
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
if (cached) {
|
|
347
|
+
touchStamp(root);
|
|
348
|
+
return cached;
|
|
349
|
+
}
|
|
350
|
+
throw e;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* `"explicit"`, `"default"` or `"off"` for a launch. `option` (the `widevine` launch option) wins: true ->
|
|
355
|
+
* explicit, false -> off. Unset follows CLEARCOTE_WIDEVINE (1/true/yes/on -> explicit, 0/false/no/off ->
|
|
356
|
+
* off). Otherwise a licensed launch (a licence key resolved, which selects the PRO build) gets the default
|
|
357
|
+
* and a free launch stays off. Same rules in the Python and .NET SDKs.
|
|
358
|
+
*/
|
|
359
|
+
export function widevineMode(option, licensed, env = process.env) {
|
|
360
|
+
if (option !== undefined && option !== null)
|
|
361
|
+
return option ? "explicit" : "off";
|
|
362
|
+
const value = String(env[WIDEVINE_ENV] ?? "").trim().toLowerCase();
|
|
363
|
+
if (TRUTHY.includes(value))
|
|
364
|
+
return "explicit";
|
|
365
|
+
if (FALSY.includes(value))
|
|
366
|
+
return "off";
|
|
367
|
+
return licensed ? "default" : "off";
|
|
368
|
+
}
|
|
369
|
+
/** How long a default launch waits for a first download: CLEARCOTE_WIDEVINE_TIMEOUT_SECONDS clamped to 1-600, else 15
|
|
370
|
+
* (an unparsable or non-positive value too). */
|
|
371
|
+
export function timeoutSeconds(env = process.env) {
|
|
372
|
+
const raw = String(env[TIMEOUT_ENV] ?? "").trim();
|
|
373
|
+
const value = raw === "" ? NaN : Number(raw);
|
|
374
|
+
if (!(value > 0))
|
|
375
|
+
return DEFAULT_TIMEOUT_SECONDS; // also NaN
|
|
376
|
+
return Math.min(Math.max(value, MIN_TIMEOUT_SECONDS), MAX_TIMEOUT_SECONDS);
|
|
377
|
+
}
|
|
378
|
+
// The default path's process state: the first download in flight (one at a time), the background refresh of
|
|
379
|
+
// a stale cached CDM (one at a time), and when a first download last failed (a monotonic clock).
|
|
380
|
+
let defaultJob;
|
|
381
|
+
let refreshJob;
|
|
382
|
+
let defaultFailedAt;
|
|
383
|
+
/** Tests: forget the in-flight fetch, the refresh and the last failure (and stop what is running). */
|
|
384
|
+
export function resetWidevineDefaultState() {
|
|
385
|
+
defaultJob?.controller.abort();
|
|
386
|
+
refreshJob?.controller.abort();
|
|
387
|
+
defaultJob = undefined;
|
|
388
|
+
refreshJob = undefined;
|
|
389
|
+
defaultFailedAt = undefined;
|
|
390
|
+
}
|
|
391
|
+
/** Tests: the background refresh in flight, if any. */
|
|
392
|
+
export function widevineRefreshInFlight() {
|
|
393
|
+
return refreshJob?.promise;
|
|
394
|
+
}
|
|
395
|
+
/** Tests: pretend the last failure was this long ago. */
|
|
396
|
+
export function ageLastWidevineFailure(byMs) {
|
|
397
|
+
if (defaultFailedAt !== undefined)
|
|
398
|
+
defaultFailedAt -= byMs;
|
|
399
|
+
}
|
|
400
|
+
function shortReason(e) {
|
|
401
|
+
const err = e;
|
|
402
|
+
const text = String(err?.message ?? e ?? "").trim().split("\n")[0].slice(0, 160);
|
|
403
|
+
const name = err?.name ?? "Error";
|
|
404
|
+
return text ? `${name}: ${text}` : name;
|
|
405
|
+
}
|
|
406
|
+
function warnDefault(reason, quiet) {
|
|
407
|
+
warnOnce(WARN_CODE, `Widevine is on by default for licensed launches, but the CDM could not be loaded (${reason}); ` +
|
|
408
|
+
"launching without it. Set CLEARCOTE_WIDEVINE=0 (or pass widevine: false) to stop trying.", quiet);
|
|
409
|
+
}
|
|
410
|
+
/** Ask Google whether a newer CDM exists, with nobody waiting: one at a time, quiet. The update check is bounded
|
|
411
|
+
* (REFRESH_CHECK_TIMEOUT_MS), so a blackholed network holds a finishing process that long at most, once a day:
|
|
412
|
+
* {@link resolveCdm} records a failed check. */
|
|
413
|
+
function refreshInBackground() {
|
|
414
|
+
if (refreshJob)
|
|
415
|
+
return;
|
|
416
|
+
const controller = new AbortController();
|
|
417
|
+
const job = {
|
|
418
|
+
controller,
|
|
419
|
+
promise: resolveCdm({ quiet: true, signal: controller.signal, checkSignal: withTimeout(REFRESH_CHECK_TIMEOUT_MS, controller.signal) })
|
|
420
|
+
.then(() => undefined, () => undefined)
|
|
421
|
+
.finally(() => { if (refreshJob === job)
|
|
422
|
+
refreshJob = undefined; }),
|
|
423
|
+
};
|
|
424
|
+
refreshJob = job;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* The CDM for a launch that did not ask for Widevine (the licensed default), or undefined.
|
|
428
|
+
*
|
|
429
|
+
* A CDM in the cache is used AT ONCE, with no network on the launch path; when its last update check is over a
|
|
430
|
+
* day old, a background refresh asks Google for a newer one (used from the next launch on). Only with nothing
|
|
431
|
+
* cached does the launch wait for a download, at most `timeoutMs` (default {@link timeoutSeconds}). FAIL-SOFT:
|
|
432
|
+
* never rejects. When there is no CDM to use (the download failed or did not finish in time) it warns once per
|
|
433
|
+
* process (unless quiet) and answers undefined, so the launch goes on without DRM; a download still running at
|
|
434
|
+
* the deadline is cancelled (it cannot keep the process alive), and a failed one is not retried for
|
|
435
|
+
* RETRY_AFTER_MS in this process. The fetches here are quiet: the default path prints nothing else.
|
|
436
|
+
*/
|
|
437
|
+
export async function defaultCdm(opts = {}) {
|
|
438
|
+
const root = cacheRoot();
|
|
439
|
+
const cached = cachedCdm(root);
|
|
440
|
+
if (cached) {
|
|
441
|
+
if (!stampFresh(root))
|
|
442
|
+
refreshInBackground();
|
|
443
|
+
return cached;
|
|
134
444
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
445
|
+
if (defaultFailedAt !== undefined && performance.now() - defaultFailedAt < RETRY_AFTER_MS)
|
|
446
|
+
return undefined;
|
|
447
|
+
const waitMs = opts.timeoutMs ?? timeoutSeconds() * 1000;
|
|
448
|
+
if (!defaultJob || defaultJob.done) {
|
|
449
|
+
const controller = new AbortController();
|
|
450
|
+
const started = { controller, done: false, promise: Promise.resolve({ err: undefined }) };
|
|
451
|
+
started.promise = resolveCdm({ quiet: true, signal: controller.signal, lockTimeoutMs: waitMs })
|
|
452
|
+
.then((ok) => ({ ok }), (err) => ({ err }))
|
|
453
|
+
.finally(() => { started.done = true; });
|
|
454
|
+
defaultJob = started;
|
|
455
|
+
}
|
|
456
|
+
const job = defaultJob;
|
|
457
|
+
let timer;
|
|
458
|
+
const deadline = new Promise((resolve) => { timer = setTimeout(() => resolve("timeout"), waitMs); });
|
|
138
459
|
try {
|
|
139
|
-
|
|
140
|
-
|
|
460
|
+
const r = await Promise.race([job.promise, deadline]);
|
|
461
|
+
if (r === "timeout") {
|
|
462
|
+
defaultFailedAt = performance.now();
|
|
463
|
+
job.controller.abort(); // never keep the process alive for a launch that went on without it
|
|
464
|
+
warnDefault(`not ready within ${waitMs / 1000} s`, opts.quiet);
|
|
465
|
+
return undefined;
|
|
466
|
+
}
|
|
467
|
+
if ("err" in r) {
|
|
468
|
+
defaultFailedAt = performance.now();
|
|
469
|
+
warnDefault(shortReason(r.err), opts.quiet);
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
return r.ok;
|
|
141
473
|
}
|
|
142
474
|
finally {
|
|
143
|
-
|
|
475
|
+
clearTimeout(timer);
|
|
144
476
|
}
|
|
145
|
-
if (!existsSync(dll))
|
|
146
|
-
throw new Error(`extracted CDM but ${filename} not at ${dll}`);
|
|
147
|
-
log(opts.quiet, `installed: ${verDir}`);
|
|
148
|
-
return verDir;
|
|
149
477
|
}
|
|
150
478
|
/**
|
|
151
479
|
* Make a persistent profile load the fetched CDM: copy it under <userDataDir>/WidevineCdm/<version>/
|
|
152
|
-
* and write the component hint file the engine reads. Fetches the CDM first if needed
|
|
480
|
+
* and write the component hint file the engine reads. Fetches the CDM first if needed
|
|
481
|
+
* ({@link resolveCdm}: the cached one when it is current).
|
|
153
482
|
*/
|
|
154
483
|
export async function seedWidevine(userDataDir, opts = {}) {
|
|
155
|
-
const src = await
|
|
484
|
+
const src = await resolveCdm(opts);
|
|
156
485
|
const version = path.basename(src);
|
|
157
486
|
const wvRoot = path.join(userDataDir, "WidevineCdm");
|
|
158
487
|
const target = path.join(wvRoot, version);
|
|
@@ -209,10 +538,32 @@ export async function widevineCdmArgs(exe, opts = {}) {
|
|
|
209
538
|
try {
|
|
210
539
|
if (!engineSupportsSwitch(exe, "widevine-cdm-path"))
|
|
211
540
|
return [];
|
|
212
|
-
return [`--widevine-cdm-path=${await
|
|
541
|
+
return [`--widevine-cdm-path=${await resolveCdm({ quiet: opts.quiet })}`];
|
|
213
542
|
}
|
|
214
543
|
catch {
|
|
215
544
|
return [];
|
|
216
545
|
}
|
|
217
546
|
}
|
|
547
|
+
function callerSetCdmPath(userArgs) {
|
|
548
|
+
return (userArgs ?? []).some((a) => String(a).split("=", 1)[0] === "--widevine-cdm-path");
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* The Widevine switch for one launch, by {@link widevineMode}. explicit: {@link widevineCdmArgs} (the
|
|
552
|
+
* behaviour `widevine: true` always had). default: the same switch, only on an engine that implements it
|
|
553
|
+
* (r32+; an older engine keeps the old default, off), with the CDM from {@link defaultCdm} (bounded,
|
|
554
|
+
* fail-soft, warns once). off: nothing. A `--widevine-cdm-path` the caller put in `args` is theirs:
|
|
555
|
+
* nothing is added then. Works for every launch path: such an engine loads the CDM without a profile,
|
|
556
|
+
* incognito included.
|
|
557
|
+
*/
|
|
558
|
+
export async function widevineLaunchArgs(exe, option, licensed, userArgs, opts = {}) {
|
|
559
|
+
const mode = widevineMode(option, licensed);
|
|
560
|
+
if (mode === "off" || callerSetCdmPath(userArgs))
|
|
561
|
+
return [];
|
|
562
|
+
if (mode === "explicit")
|
|
563
|
+
return widevineCdmArgs(exe, { quiet: opts.quiet });
|
|
564
|
+
if (!engineSupportsSwitch(exe, "widevine-cdm-path"))
|
|
565
|
+
return [];
|
|
566
|
+
const cdm = await defaultCdm(opts);
|
|
567
|
+
return cdm ? [`--widevine-cdm-path=${cdm}`] : [];
|
|
568
|
+
}
|
|
218
569
|
//# sourceMappingURL=widevine.js.map
|