cloakbrowser 0.3.31 → 0.4.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.
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Widevine CDM hint-file seeding for persistent contexts.
3
+ * Mirrors Python cloakbrowser/widevine.py.
4
+ *
5
+ * CloakBrowser's binary supports Widevine but ships no CDM (proprietary, can't
6
+ * redistribute). Users sideload it by copying a `WidevineCdm/` directory from a
7
+ * real Chrome install next to the binary (see issue #96). Chromium reads a
8
+ * "hint file" from the user-data-dir at early startup to register the CDM, but
9
+ * on a fresh profile it doesn't exist yet, and Playwright disables the component
10
+ * updater that would write it. This seeds the hint file before launch so a
11
+ * sideloaded CDM works on the first run. It never bundles, downloads, or copies
12
+ * the CDM — only writes the hint when a user-provided CDM is already present.
13
+ *
14
+ * Linux only: Chromium's hint-file mechanism is Linux/ChromeOS-specific.
15
+ */
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ const HINT_FILENAME = "latest-component-updated-widevine-cdm";
19
+ /** True if `file` exists and is a regular file (mirrors Python's Path.is_file()). */
20
+ function isFile(file) {
21
+ try {
22
+ return fs.statSync(file).isFile();
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ /** Absolute, symlink-resolved path (mirrors Python's Path.resolve()). */
29
+ function realPath(p) {
30
+ try {
31
+ return fs.realpathSync(p);
32
+ }
33
+ catch {
34
+ return path.resolve(p);
35
+ }
36
+ }
37
+ function seedingDisabled() {
38
+ const val = (process.env.CLOAKBROWSER_WIDEVINE ?? "").trim().toLowerCase();
39
+ return val === "0" || val === "false" || val === "off" || val === "no";
40
+ }
41
+ /**
42
+ * Locate a sideloaded Widevine CDM directory, or null if absent.
43
+ *
44
+ * Resolution:
45
+ * - If CLOAKBROWSER_WIDEVINE_CDM is set, it is used exclusively (overrides
46
+ * auto-detection). An invalid value (no `manifest.json`) skips seeding.
47
+ * - Otherwise, `<dir of the chrome binary>/WidevineCdm` — where a user naturally
48
+ * drops it, and where it lives for both downloaded and CLOAKBROWSER_BINARY_PATH binaries.
49
+ *
50
+ * A directory counts only if it contains `manifest.json`. The returned path is
51
+ * absolute and symlink-resolved (mirrors Python's Path.resolve()).
52
+ * @internal Exported for testing.
53
+ */
54
+ export function resolveWidevineCdmDir(binaryPath) {
55
+ const custom = process.env.CLOAKBROWSER_WIDEVINE_CDM;
56
+ // `!== undefined` (not truthiness): a present-but-empty env var is "set" and
57
+ // used exclusively — it resolves to an invalid path and skips seeding.
58
+ const cdmDir = custom !== undefined ? custom : path.join(path.dirname(binaryPath), "WidevineCdm");
59
+ return isFile(path.join(cdmDir, "manifest.json")) ? realPath(cdmDir) : null;
60
+ }
61
+ /**
62
+ * Write the Widevine CDM hint file into a persistent profile before launch.
63
+ * `binaryPath` is the resolved chrome executable; the CDM is looked for next to
64
+ * it. No-op on non-Linux, when disabled via CLOAKBROWSER_WIDEVINE, or when no
65
+ * sideloaded CDM is present. Never throws — a failure must not break launch.
66
+ */
67
+ export function seedWidevineHint(userDataDir, binaryPath) {
68
+ if (process.platform !== "linux")
69
+ return;
70
+ if (seedingDisabled())
71
+ return;
72
+ // Empty userDataDir = Playwright's ephemeral profile (its own temp dir);
73
+ // a persistent hint can't be placed there, and "" would pollute the CWD.
74
+ if (!userDataDir)
75
+ return;
76
+ // Everything below is best-effort and must never break the browser launch,
77
+ // so the whole body (resolution + write) is guarded.
78
+ try {
79
+ const cdmDir = resolveWidevineCdmDir(binaryPath);
80
+ if (cdmDir === null) {
81
+ if (process.env.CLOAKBROWSER_WIDEVINE_CDM !== undefined) {
82
+ console.warn("[cloakbrowser] CLOAKBROWSER_WIDEVINE_CDM is set but has no manifest.json; " +
83
+ "skipping Widevine hint seeding");
84
+ }
85
+ return;
86
+ }
87
+ const hintDir = path.join(userDataDir, "WidevineCdm");
88
+ fs.mkdirSync(hintDir, { recursive: true });
89
+ const hintFile = path.join(hintDir, HINT_FILENAME);
90
+ // cdmDir is already absolute/resolved.
91
+ const content = JSON.stringify({ Path: cdmDir });
92
+ try {
93
+ if (isFile(hintFile) && fs.readFileSync(hintFile, "utf-8") === content) {
94
+ return; // already seeded correctly
95
+ }
96
+ }
97
+ catch {
98
+ console.warn("[cloakbrowser] Existing Widevine hint unreadable; rewriting");
99
+ }
100
+ fs.writeFileSync(hintFile, content);
101
+ }
102
+ catch (e) {
103
+ // Best-effort: never break the launch, but surface the failure.
104
+ console.warn("[cloakbrowser] Failed to seed Widevine CDM hint file:", e);
105
+ }
106
+ }
107
+ //# sourceMappingURL=widevine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widevine.js","sourceRoot":"","sources":["../src/widevine.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloakbrowser",
3
- "version": "0.3.31",
3
+ "version": "0.4.0",
4
4
  "description": "Stealth Chromium that passes every bot detection test. Drop-in Playwright/Puppeteer replacement with source-level fingerprint patches.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",