@mkterswingman/5mghost-yonder 0.0.6 → 0.0.7
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 +5 -0
- package/dist/cli/setupCookies.d.ts +18 -0
- package/dist/cli/setupCookies.js +40 -14
- package/dist/runtime/playwrightRuntime.d.ts +4 -0
- package/dist/runtime/playwrightRuntime.js +15 -3
- package/dist/utils/ytdlp.d.ts +4 -0
- package/dist/utils/ytdlp.js +10 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,3 +56,8 @@ Media download runtime expectations:
|
|
|
56
56
|
- `uninstall` — remove MCP registrations and local `~/.yt-mcp` config
|
|
57
57
|
- `update` — update the main package and required runtimes
|
|
58
58
|
- `version` — print the installed version
|
|
59
|
+
|
|
60
|
+
Runtime notes:
|
|
61
|
+
|
|
62
|
+
- Playwright browser installs are managed through `yt-mcp runtime install`, not a raw `npx playwright install ...` call
|
|
63
|
+
- `yt-dlp` is invoked with `--js-runtimes node`, so no extra Deno install is required
|
|
@@ -52,6 +52,24 @@ export interface SetupCookiesDeps {
|
|
|
52
52
|
log: (message: string) => void;
|
|
53
53
|
}
|
|
54
54
|
type SetupCookiesChromium = Awaited<ReturnType<SetupCookiesDeps["loadChromium"]>>;
|
|
55
|
+
interface CdpCookie {
|
|
56
|
+
name: string;
|
|
57
|
+
value: string;
|
|
58
|
+
domain: string;
|
|
59
|
+
path: string;
|
|
60
|
+
secure?: boolean;
|
|
61
|
+
httpOnly?: boolean;
|
|
62
|
+
expires?: number;
|
|
63
|
+
}
|
|
64
|
+
interface CdpCookieClient {
|
|
65
|
+
send(method: "Network.getAllCookies"): Promise<{
|
|
66
|
+
cookies?: CdpCookie[];
|
|
67
|
+
}>;
|
|
68
|
+
}
|
|
69
|
+
export declare function readCdpCookiesUntilSession(client: CdpCookieClient, deps: Pick<SetupCookiesDeps, "hasYouTubeSession">, options?: {
|
|
70
|
+
attempts?: number;
|
|
71
|
+
delayMs?: number;
|
|
72
|
+
}): Promise<PlaywrightCookie[] | null>;
|
|
55
73
|
export declare function tryImportBrowserCookies(chromium: SetupCookiesChromium, deps: SetupCookiesDeps): Promise<boolean>;
|
|
56
74
|
export declare function readImportedBrowserCookies(candidate: BrowserProfileCandidate, chromium: SetupCookiesChromium, deps: SetupCookiesDeps): Promise<PlaywrightCookie[] | null>;
|
|
57
75
|
export declare function runManualCookieSetup(chromium: SetupCookiesChromium, deps: SetupCookiesDeps): Promise<void>;
|
package/dist/cli/setupCookies.js
CHANGED
|
@@ -28,8 +28,17 @@ export const CHANNEL_LABELS = {
|
|
|
28
28
|
};
|
|
29
29
|
/** Check if YouTube SID cookies are present — the real signal of a logged-in session. */
|
|
30
30
|
export function hasYouTubeSession(cookies) {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const hasCookieTriplet = (domainFragment) => {
|
|
32
|
+
const names = new Set(cookies
|
|
33
|
+
.filter((cookie) => cookie.domain.includes(domainFragment))
|
|
34
|
+
.map((cookie) => cookie.name));
|
|
35
|
+
return names.has("SID") && names.has("HSID") && names.has("SSID");
|
|
36
|
+
};
|
|
37
|
+
if (hasCookieTriplet("youtube.com")) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
const hasYouTubeLoginInfo = cookies.some((cookie) => cookie.name === "LOGIN_INFO" && cookie.domain.includes("youtube.com"));
|
|
41
|
+
return hasYouTubeLoginInfo && hasCookieTriplet("google.");
|
|
33
42
|
}
|
|
34
43
|
/**
|
|
35
44
|
* Save cookies to Netscape format file and close the browser context.
|
|
@@ -146,6 +155,29 @@ async function waitForCdpVersion(port, timeoutMs = 15_000) {
|
|
|
146
155
|
}
|
|
147
156
|
return null;
|
|
148
157
|
}
|
|
158
|
+
export async function readCdpCookiesUntilSession(client, deps, options = {}) {
|
|
159
|
+
const attempts = options.attempts ?? 5;
|
|
160
|
+
const delayMs = options.delayMs ?? 1_000;
|
|
161
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
162
|
+
const result = await client.send("Network.getAllCookies");
|
|
163
|
+
const cookies = (result.cookies ?? []).map((cookie) => ({
|
|
164
|
+
name: cookie.name,
|
|
165
|
+
value: cookie.value,
|
|
166
|
+
domain: cookie.domain,
|
|
167
|
+
path: cookie.path,
|
|
168
|
+
secure: cookie.secure ?? false,
|
|
169
|
+
httpOnly: cookie.httpOnly ?? false,
|
|
170
|
+
expires: cookie.expires ?? -1,
|
|
171
|
+
}));
|
|
172
|
+
if (deps.hasYouTubeSession(cookies)) {
|
|
173
|
+
return cookies;
|
|
174
|
+
}
|
|
175
|
+
if (attempt < attempts - 1) {
|
|
176
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
149
181
|
async function terminateImportedBrowser(processHandle) {
|
|
150
182
|
if (processHandle.exitCode !== null || processHandle.signalCode !== null) {
|
|
151
183
|
return;
|
|
@@ -213,17 +245,11 @@ export async function readImportedBrowserCookies(candidate, chromium, deps) {
|
|
|
213
245
|
}
|
|
214
246
|
const page = context.pages()[0] ?? await context.newPage();
|
|
215
247
|
const client = await context.newCDPSession(page);
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
path: cookie.path,
|
|
222
|
-
secure: cookie.secure ?? false,
|
|
223
|
-
httpOnly: cookie.httpOnly ?? false,
|
|
224
|
-
expires: cookie.expires ?? -1,
|
|
225
|
-
}));
|
|
226
|
-
if (!deps.hasYouTubeSession(cookies)) {
|
|
248
|
+
const cookies = await readCdpCookiesUntilSession(client, deps, {
|
|
249
|
+
attempts: 6,
|
|
250
|
+
delayMs: 1_000,
|
|
251
|
+
});
|
|
252
|
+
if (!cookies) {
|
|
227
253
|
return null;
|
|
228
254
|
}
|
|
229
255
|
return cookies;
|
|
@@ -248,7 +274,7 @@ export async function runManualCookieSetup(chromium, deps) {
|
|
|
248
274
|
deps.log(`Using browser: ${CHANNEL_LABELS[channel] ?? channel}`);
|
|
249
275
|
if (channel === "chromium") {
|
|
250
276
|
deps.log("⚠️ No system Chrome or Edge found. Using bundled Chromium.\n" +
|
|
251
|
-
" If it fails, run:
|
|
277
|
+
" If it fails, run: yt-mcp runtime install\n");
|
|
252
278
|
}
|
|
253
279
|
let context;
|
|
254
280
|
try {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import type { RuntimeComponentState } from "./manifest.js";
|
|
2
|
+
export declare function getPlaywrightInstallInvocation(): {
|
|
3
|
+
command: string;
|
|
4
|
+
args: string[];
|
|
5
|
+
};
|
|
2
6
|
export declare function checkPlaywrightRuntime(): Promise<RuntimeComponentState & {
|
|
3
7
|
name: "playwright";
|
|
4
8
|
message?: string;
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
export function getPlaywrightInstallInvocation() {
|
|
7
|
+
const packageJsonPath = require.resolve("playwright/package.json");
|
|
8
|
+
const cliPath = join(dirname(packageJsonPath), "cli.js");
|
|
9
|
+
return {
|
|
10
|
+
command: process.execPath,
|
|
11
|
+
args: [cliPath, "install", "chromium"],
|
|
12
|
+
};
|
|
13
|
+
}
|
|
3
14
|
export async function checkPlaywrightRuntime() {
|
|
4
15
|
try {
|
|
5
16
|
const { chromium } = await import("playwright");
|
|
@@ -25,11 +36,12 @@ export async function checkPlaywrightRuntime() {
|
|
|
25
36
|
source: "runtime",
|
|
26
37
|
installed_at: null,
|
|
27
38
|
binary_path: null,
|
|
28
|
-
message: "
|
|
39
|
+
message: "yt-mcp runtime install",
|
|
29
40
|
};
|
|
30
41
|
}
|
|
31
42
|
export async function installPlaywrightRuntime() {
|
|
32
|
-
|
|
43
|
+
const invocation = getPlaywrightInstallInvocation();
|
|
44
|
+
execFileSync(invocation.command, invocation.args, { stdio: "inherit" });
|
|
33
45
|
return checkPlaywrightRuntime();
|
|
34
46
|
}
|
|
35
47
|
export async function updatePlaywrightRuntime() {
|
package/dist/utils/ytdlp.d.ts
CHANGED
|
@@ -12,6 +12,10 @@ export interface YtDlpStderrLineSplitter {
|
|
|
12
12
|
type SpawnSyncFn = typeof spawnSync;
|
|
13
13
|
export declare function createYtDlpStderrLineSplitter(onLine: (line: string) => void): YtDlpStderrLineSplitter;
|
|
14
14
|
export declare function hasYtDlp(runSpawnSync?: SpawnSyncFn): boolean;
|
|
15
|
+
export declare function buildYtDlpArgs(args: string[], options?: {
|
|
16
|
+
cookiesPath?: string;
|
|
17
|
+
cookiesExist?: boolean;
|
|
18
|
+
}): string[];
|
|
15
19
|
export declare function runYtDlp(args: string[], timeoutMs?: number, onStderrLine?: (line: string) => void): Promise<YtDlpResult>;
|
|
16
20
|
export declare function runYtDlpJson<T>(args: string[], timeoutMs?: number): Promise<{
|
|
17
21
|
ok: true;
|
package/dist/utils/ytdlp.js
CHANGED
|
@@ -57,13 +57,19 @@ export function hasYtDlp(runSpawnSync = spawnSync) {
|
|
|
57
57
|
});
|
|
58
58
|
return result.status === 0 && result.error == null;
|
|
59
59
|
}
|
|
60
|
+
export function buildYtDlpArgs(args, options = {}) {
|
|
61
|
+
const cookiesPath = options.cookiesPath ?? PATHS.cookiesTxt;
|
|
62
|
+
const cookiesExist = options.cookiesExist ?? existsSync(cookiesPath);
|
|
63
|
+
const finalArgs = ["--force-ipv4", "--no-warnings", "--js-runtimes", "node", ...args];
|
|
64
|
+
if (cookiesExist) {
|
|
65
|
+
finalArgs.push("--cookies", cookiesPath);
|
|
66
|
+
}
|
|
67
|
+
return finalArgs;
|
|
68
|
+
}
|
|
60
69
|
export function runYtDlp(args, timeoutMs = 45_000, onStderrLine) {
|
|
61
70
|
return new Promise((resolve, reject) => {
|
|
62
71
|
const start = Date.now();
|
|
63
|
-
const finalArgs =
|
|
64
|
-
if (existsSync(PATHS.cookiesTxt)) {
|
|
65
|
-
finalArgs.push("--cookies", PATHS.cookiesTxt);
|
|
66
|
-
}
|
|
72
|
+
const finalArgs = buildYtDlpArgs(args);
|
|
67
73
|
const proc = spawn(getYtDlpPath(), finalArgs, {
|
|
68
74
|
stdio: ["ignore", "pipe", "pipe"],
|
|
69
75
|
});
|