@mkterswingman/5mghost-yonder 0.0.33 → 0.0.35
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 +3 -3
- package/dist/cli/index.js +9 -1
- package/dist/cli/setupCookies.d.ts +2 -6
- package/dist/cli/setupCookies.js +4 -23
- package/dist/download/downloader.js +17 -4
- package/dist/utils/cookieRefresh.d.ts +4 -8
- package/dist/utils/cookieRefresh.js +10 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,8 +18,8 @@ The bootstrap installer:
|
|
|
18
18
|
- installs required runtimes (`playwright`, `yt-dlp`, `ffmpeg`)
|
|
19
19
|
- runs `yt-mcp setup`
|
|
20
20
|
- runs `yt-mcp smoke` to verify MCP startup and authenticated remote access
|
|
21
|
-
-
|
|
22
|
-
-
|
|
21
|
+
- opens a dedicated Playwright browser profile for YouTube login
|
|
22
|
+
- keeps cookie refresh inside the same dedicated profile (does not import system Chrome/Edge profiles)
|
|
23
23
|
- in unattended installs, defaults to OAuth auth mode and defaults to headed cookie setup after the prompt timeout
|
|
24
24
|
- in installer mode, OAuth waits up to `180s` and prints PAT fallback commands before waiting
|
|
25
25
|
- if auth is still incomplete after `setup`, the installer stops before smoke tests and YouTube cookie setup instead of pretending the install fully passed
|
|
@@ -57,7 +57,7 @@ Media download runtime expectations:
|
|
|
57
57
|
- `smoke` — run the installer smoke checks (`search_videos`, plus `validate_cookies` with `--subtitles`)
|
|
58
58
|
- `runtime` — install, update, or check required runtimes
|
|
59
59
|
- `check` — inspect shared auth, runtime status, and YouTube cookies
|
|
60
|
-
- `setup-cookies` —
|
|
60
|
+
- `setup-cookies` — open a dedicated Playwright browser profile for manual YouTube login and save cookies locally
|
|
61
61
|
- `uninstall` — remove MCP registrations and local `~/.yt-mcp` config
|
|
62
62
|
- `update` — update the main package and required runtimes
|
|
63
63
|
- `version` — print the installed version
|
package/dist/cli/index.js
CHANGED
|
@@ -92,7 +92,15 @@ async function main() {
|
|
|
92
92
|
}
|
|
93
93
|
case "setup-cookies": {
|
|
94
94
|
const { runSetupCookies, parseSetupCookiesArgs } = await import("./setupCookies.js");
|
|
95
|
-
|
|
95
|
+
try {
|
|
96
|
+
parseSetupCookiesArgs(process.argv.slice(3));
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
100
|
+
console.error(msg);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
await runSetupCookies({});
|
|
96
104
|
break;
|
|
97
105
|
}
|
|
98
106
|
case "uninstall": {
|
|
@@ -51,10 +51,6 @@ export interface SetupCookiesDeps {
|
|
|
51
51
|
runManualCookieSetup: typeof runManualCookieSetup;
|
|
52
52
|
log: (message: string) => void;
|
|
53
53
|
}
|
|
54
|
-
export interface SetupCookiesOptions {
|
|
55
|
-
importOnly?: boolean;
|
|
56
|
-
headed?: boolean;
|
|
57
|
-
}
|
|
58
54
|
export declare class BrowserProfileLockedError extends Error {
|
|
59
55
|
constructor(message?: string);
|
|
60
56
|
}
|
|
@@ -83,6 +79,6 @@ export declare function runManualCookieSetup(chromium: SetupCookiesChromium, dep
|
|
|
83
79
|
/**
|
|
84
80
|
* Interactive cookie setup — opens a visible browser for user to log in.
|
|
85
81
|
*/
|
|
86
|
-
export declare function parseSetupCookiesArgs(argv: string[]):
|
|
87
|
-
export declare function runSetupCookies(overrides?: Partial<SetupCookiesDeps
|
|
82
|
+
export declare function parseSetupCookiesArgs(argv: string[]): void;
|
|
83
|
+
export declare function runSetupCookies(overrides?: Partial<SetupCookiesDeps>): Promise<void>;
|
|
88
84
|
export {};
|
package/dist/cli/setupCookies.js
CHANGED
|
@@ -413,12 +413,11 @@ export async function runManualCookieSetup(chromium, deps) {
|
|
|
413
413
|
* Interactive cookie setup — opens a visible browser for user to log in.
|
|
414
414
|
*/
|
|
415
415
|
export function parseSetupCookiesArgs(argv) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
};
|
|
416
|
+
for (const arg of argv) {
|
|
417
|
+
throw new Error(`Unknown option for setup-cookies: ${arg}. setup-cookies accepts no options.`);
|
|
418
|
+
}
|
|
420
419
|
}
|
|
421
|
-
export async function runSetupCookies(overrides = {}
|
|
420
|
+
export async function runSetupCookies(overrides = {}) {
|
|
422
421
|
const deps = buildSetupCookiesDeps(overrides);
|
|
423
422
|
deps.log("\n🍪 YouTube Cookie Setup\n");
|
|
424
423
|
deps.ensureConfigDir();
|
|
@@ -429,23 +428,5 @@ export async function runSetupCookies(overrides = {}, options = {}) {
|
|
|
429
428
|
catch {
|
|
430
429
|
throw new Error("Playwright runtime is not installed.\nRun: yt-mcp runtime install");
|
|
431
430
|
}
|
|
432
|
-
if (!options.headed) {
|
|
433
|
-
let imported = false;
|
|
434
|
-
try {
|
|
435
|
-
imported = await deps.tryImportBrowserCookies(chromium, deps);
|
|
436
|
-
}
|
|
437
|
-
catch (err) {
|
|
438
|
-
if (!(err instanceof BrowserProfileLockedError) || options.importOnly) {
|
|
439
|
-
throw err;
|
|
440
|
-
}
|
|
441
|
-
deps.log(`${err.message}\n`);
|
|
442
|
-
}
|
|
443
|
-
if (imported) {
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
if (options.importOnly && !options.headed) {
|
|
448
|
-
throw new Error("No reusable YouTube session found in local Chrome/Edge profiles");
|
|
449
|
-
}
|
|
450
431
|
await deps.runManualCookieSetup(chromium, deps);
|
|
451
432
|
}
|
|
@@ -34,11 +34,24 @@ export async function downloadOneItem(input) {
|
|
|
34
34
|
await deps.rm(input.item.output_dir, { recursive: true, force: true });
|
|
35
35
|
await deps.mkdir(input.item.output_dir, { recursive: true });
|
|
36
36
|
await deps.mkdir(subtitlesDir, { recursive: true });
|
|
37
|
-
const
|
|
37
|
+
const metadataInput = {
|
|
38
38
|
sourceUrl: input.item.source_url,
|
|
39
39
|
videoId: input.item.video_id,
|
|
40
40
|
title: input.item.title,
|
|
41
|
-
}
|
|
41
|
+
};
|
|
42
|
+
let metadata;
|
|
43
|
+
try {
|
|
44
|
+
metadata = await deps.fetchMetadata(metadataInput);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const failureKind = classifyRefreshableFailure(error);
|
|
48
|
+
if (failureKind != null && failureKind !== "RATE_LIMITED" && await deps.refreshCookies()) {
|
|
49
|
+
metadata = await deps.fetchMetadata(metadataInput);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
42
55
|
const result = {
|
|
43
56
|
video_file: null,
|
|
44
57
|
metadata_file: metadataFile,
|
|
@@ -63,7 +76,7 @@ export async function downloadOneItem(input) {
|
|
|
63
76
|
});
|
|
64
77
|
}
|
|
65
78
|
catch (error) {
|
|
66
|
-
const failureKind =
|
|
79
|
+
const failureKind = classifyRefreshableFailure(error);
|
|
67
80
|
if (failureKind != null && failureKind !== "RATE_LIMITED" && await deps.refreshCookies()) {
|
|
68
81
|
result.video_file = await deps.downloadVideo({
|
|
69
82
|
sourceUrl: input.item.source_url,
|
|
@@ -311,7 +324,7 @@ function sortSubtitleFiles(files) {
|
|
|
311
324
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
312
325
|
.map(([format, paths]) => [format, [...paths].sort()]));
|
|
313
326
|
}
|
|
314
|
-
function
|
|
327
|
+
function classifyRefreshableFailure(error) {
|
|
315
328
|
const message = error instanceof Error ? error.message : String(error);
|
|
316
329
|
return classifyYtDlpFailure(message);
|
|
317
330
|
}
|
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cookie auto-refresh with
|
|
2
|
+
* Cookie auto-refresh with two-tier fallback.
|
|
3
3
|
*
|
|
4
4
|
* Tier 1: Headless Playwright refresh using existing browser-profile.
|
|
5
5
|
* If Google session is still valid, cookies are extracted automatically.
|
|
6
6
|
*
|
|
7
|
-
* Tier 2:
|
|
8
|
-
* Copies the real browser profile to a temp dir and reads cookies via CDP
|
|
9
|
-
* without making any network requests (avoids Google token rotation).
|
|
10
|
-
*
|
|
11
|
-
* Tier 3: Open a headed browser for manual user login.
|
|
7
|
+
* Tier 2: Open a headed browser for manual user login.
|
|
12
8
|
* Blocks the MCP tool call for up to ~2 minutes while the user logs in.
|
|
13
|
-
*
|
|
9
|
+
* Uses the dedicated yt-mcp profile so refresh never touches system profiles.
|
|
14
10
|
*/
|
|
15
11
|
/**
|
|
16
|
-
* Attempt to refresh YouTube cookies through
|
|
12
|
+
* Attempt to refresh YouTube cookies through the dedicated two-tier fallback chain.
|
|
17
13
|
*
|
|
18
14
|
* @returns true if cookies were refreshed, false if all tiers failed.
|
|
19
15
|
*/
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cookie auto-refresh with
|
|
2
|
+
* Cookie auto-refresh with two-tier fallback.
|
|
3
3
|
*
|
|
4
4
|
* Tier 1: Headless Playwright refresh using existing browser-profile.
|
|
5
5
|
* If Google session is still valid, cookies are extracted automatically.
|
|
6
6
|
*
|
|
7
|
-
* Tier 2:
|
|
8
|
-
* Copies the real browser profile to a temp dir and reads cookies via CDP
|
|
9
|
-
* without making any network requests (avoids Google token rotation).
|
|
10
|
-
*
|
|
11
|
-
* Tier 3: Open a headed browser for manual user login.
|
|
7
|
+
* Tier 2: Open a headed browser for manual user login.
|
|
12
8
|
* Blocks the MCP tool call for up to ~2 minutes while the user logs in.
|
|
13
|
-
*
|
|
9
|
+
* Uses the dedicated yt-mcp profile so refresh never touches system profiles.
|
|
14
10
|
*/
|
|
15
11
|
import { existsSync } from "node:fs";
|
|
16
12
|
import { PATHS, ensureConfigDir } from "./config.js";
|
|
@@ -18,7 +14,7 @@ import { detectBrowserChannel, hasYouTubeSession, saveCookiesToDisk, } from "../
|
|
|
18
14
|
import { hasSIDCookies } from "./cookies.js";
|
|
19
15
|
const HEADLESS_TIMEOUT_MS = 30_000;
|
|
20
16
|
/**
|
|
21
|
-
* Attempt to refresh YouTube cookies through
|
|
17
|
+
* Attempt to refresh YouTube cookies through the dedicated two-tier fallback chain.
|
|
22
18
|
*
|
|
23
19
|
* @returns true if cookies were refreshed, false if all tiers failed.
|
|
24
20
|
*/
|
|
@@ -30,13 +26,8 @@ export async function tryHeadlessRefresh() {
|
|
|
30
26
|
if (tier1)
|
|
31
27
|
return true;
|
|
32
28
|
}
|
|
33
|
-
// Tier 2:
|
|
34
|
-
|
|
35
|
-
if (tier2)
|
|
36
|
-
return true;
|
|
37
|
-
// Tier 3: Open headed browser for manual login
|
|
38
|
-
const tier3 = await tryHeadedManualLogin();
|
|
39
|
-
return tier3;
|
|
29
|
+
// Tier 2: Open headed browser for manual login
|
|
30
|
+
return tryHeadedManualLogin();
|
|
40
31
|
}
|
|
41
32
|
/**
|
|
42
33
|
* Tier 1: Use existing browser-profile with Playwright to refresh cookies.
|
|
@@ -84,49 +75,15 @@ async function tryPlaywrightRefresh() {
|
|
|
84
75
|
}
|
|
85
76
|
}
|
|
86
77
|
/**
|
|
87
|
-
* Tier 2:
|
|
88
|
-
* Uses headless Chrome with about:blank to avoid triggering Google token rotation.
|
|
89
|
-
*/
|
|
90
|
-
async function tryReimportFromSystemBrowser() {
|
|
91
|
-
try {
|
|
92
|
-
const pw = await import("playwright");
|
|
93
|
-
const { tryImportBrowserCookies, saveCookiesAndClose, readImportedBrowserCookies, } = await import("../cli/setupCookies.js");
|
|
94
|
-
const { findImportableBrowserProfileCandidates, prepareImportedBrowserWorkspace, cleanupImportedBrowserWorkspace, } = await import("./browserProfileImport.js");
|
|
95
|
-
const deps = {
|
|
96
|
-
ensureConfigDir,
|
|
97
|
-
loadChromium: async () => pw.chromium,
|
|
98
|
-
detectBrowserChannel,
|
|
99
|
-
hasYouTubeSession,
|
|
100
|
-
saveCookiesAndClose,
|
|
101
|
-
// Why: waitForLogin is unused in import path but required by SetupCookiesDeps type
|
|
102
|
-
waitForLogin: async () => null,
|
|
103
|
-
findImportableBrowserProfileCandidates,
|
|
104
|
-
prepareImportedBrowserWorkspace,
|
|
105
|
-
cleanupImportedBrowserWorkspace,
|
|
106
|
-
readImportedBrowserCookies,
|
|
107
|
-
tryImportBrowserCookies,
|
|
108
|
-
// Why: runManualCookieSetup is unused in import path but required by type
|
|
109
|
-
runManualCookieSetup: async () => { },
|
|
110
|
-
// Why: silent logging during auto-refresh — user doesn't see MCP server stdout
|
|
111
|
-
log: () => { },
|
|
112
|
-
};
|
|
113
|
-
return await tryImportBrowserCookies(pw.chromium, deps);
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
return false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Tier 3: Open a headed browser for the user to manually log in.
|
|
78
|
+
* Tier 2: Open a headed browser for the user to manually log in.
|
|
121
79
|
* Blocks the MCP tool call for up to ~2 minutes.
|
|
122
|
-
*
|
|
80
|
+
* Uses the dedicated yt-mcp browser-profile for future Tier 1 refreshes.
|
|
123
81
|
*/
|
|
124
82
|
async function tryHeadedManualLogin() {
|
|
125
83
|
try {
|
|
126
|
-
// Why:
|
|
127
|
-
// to manual Playwright login, which creates browser-profile for future Tier 1 use.
|
|
84
|
+
// Why: setup-cookies now only supports the dedicated manual Playwright login flow.
|
|
128
85
|
const { runSetupCookies } = await import("../cli/setupCookies.js");
|
|
129
|
-
await runSetupCookies({}
|
|
86
|
+
await runSetupCookies({});
|
|
130
87
|
return hasSIDCookies(PATHS.cookiesTxt);
|
|
131
88
|
}
|
|
132
89
|
catch {
|