@mks2508/bundlp 0.1.12 → 0.1.13
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/dist/index.mjs +62 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -206,7 +206,7 @@ function isRecoverable(code) {
|
|
|
206
206
|
|
|
207
207
|
//#endregion
|
|
208
208
|
//#region src/http/client.ts
|
|
209
|
-
const log$
|
|
209
|
+
const log$8 = logger.scope("HttpClient");
|
|
210
210
|
function parseCookies$2(setCookieHeaders) {
|
|
211
211
|
return setCookieHeaders.map((header) => {
|
|
212
212
|
const [nameValue, ...attributes] = header.split(";").map((p) => p.trim());
|
|
@@ -248,15 +248,15 @@ var HttpClient = class {
|
|
|
248
248
|
const baseHeaders = new Headers(fetchOptions.headers);
|
|
249
249
|
if (cookieHeader) {
|
|
250
250
|
baseHeaders.set("Cookie", cookieHeader);
|
|
251
|
-
log$
|
|
251
|
+
log$8.info(`Attaching Cookie header (${cookieHeader.length} chars)`);
|
|
252
252
|
}
|
|
253
|
-
log$
|
|
254
|
-
log$
|
|
255
|
-
log$
|
|
256
|
-
log$
|
|
253
|
+
log$8.debug("=== HTTP REQUEST ===");
|
|
254
|
+
log$8.debug(`URL: ${url}`);
|
|
255
|
+
log$8.debug(`Method: ${fetchOptions.method || "GET"}`);
|
|
256
|
+
log$8.debug(`Headers: ${JSON.stringify(Object.fromEntries(baseHeaders.entries()), null, 2)}`);
|
|
257
257
|
if (fetchOptions.body) {
|
|
258
258
|
const bodyStr = typeof fetchOptions.body === "string" ? fetchOptions.body : "[binary]";
|
|
259
|
-
log$
|
|
259
|
+
log$8.debug(`Body (first 1000 chars): ${bodyStr.substring(0, 1e3)}`);
|
|
260
260
|
}
|
|
261
261
|
if (!manualRedirects) try {
|
|
262
262
|
const response = await fetch(url, {
|
|
@@ -264,9 +264,9 @@ var HttpClient = class {
|
|
|
264
264
|
headers: baseHeaders,
|
|
265
265
|
signal: timeout ? AbortSignal.timeout(timeout) : void 0
|
|
266
266
|
});
|
|
267
|
-
log$
|
|
268
|
-
log$
|
|
269
|
-
log$
|
|
267
|
+
log$8.debug("=== HTTP RESPONSE ===");
|
|
268
|
+
log$8.debug(`Status: ${response.status} ${response.statusText}`);
|
|
269
|
+
log$8.debug(`Headers: ${JSON.stringify(Object.fromEntries(response.headers.entries()), null, 2)}`);
|
|
270
270
|
return ok(response);
|
|
271
271
|
} catch (error) {
|
|
272
272
|
return err(createError("NETWORK_ERROR", `Request to ${url} failed: ${error.message}`, error));
|
|
@@ -337,7 +337,7 @@ var HttpClient = class {
|
|
|
337
337
|
const domain = c.domain?.toLowerCase() || "";
|
|
338
338
|
return domain.includes("youtube") || domain.includes("google") || domain.includes("googlevideo") || !c.domain;
|
|
339
339
|
});
|
|
340
|
-
log$
|
|
340
|
+
log$8.info(`Parsed ${cookies.length} cookies (format: ${format}), ${youtubeCookies.length} YouTube/Google related`);
|
|
341
341
|
for (const c of youtubeCookies) this.setCookie({
|
|
342
342
|
name: c.name,
|
|
343
343
|
value: c.value,
|
|
@@ -347,10 +347,10 @@ var HttpClient = class {
|
|
|
347
347
|
httpOnly: c.httpOnly,
|
|
348
348
|
expires: c.expires
|
|
349
349
|
});
|
|
350
|
-
log$
|
|
350
|
+
log$8.success(`Loaded ${youtubeCookies.length} YouTube/Google cookies`);
|
|
351
351
|
return ok(youtubeCookies.length);
|
|
352
352
|
} catch (error) {
|
|
353
|
-
log$
|
|
353
|
+
log$8.error("Failed to parse cookies:", error);
|
|
354
354
|
return err(createError("COOKIE_PARSE_ERROR", `Failed to parse cookies: ${error.message}`, error));
|
|
355
355
|
}
|
|
356
356
|
}
|
|
@@ -529,7 +529,7 @@ function isDrmOnly(formats) {
|
|
|
529
529
|
|
|
530
530
|
//#endregion
|
|
531
531
|
//#region src/innertube/client.ts
|
|
532
|
-
const log$
|
|
532
|
+
const log$7 = logger.scope("InnerTube");
|
|
533
533
|
/**
|
|
534
534
|
* Client for interacting with YouTube's InnerTube API.
|
|
535
535
|
*/
|
|
@@ -593,7 +593,7 @@ var InnerTubeClient = class {
|
|
|
593
593
|
*/
|
|
594
594
|
async fetchPlayer(videoId, options = {}) {
|
|
595
595
|
const config = INNERTUBE_CLIENTS[this.currentClient];
|
|
596
|
-
log$
|
|
596
|
+
log$7.debug(`Fetching player for ${videoId} with client ${this.currentClient}`);
|
|
597
597
|
const payload = {
|
|
598
598
|
context: this.buildContext(),
|
|
599
599
|
videoId,
|
|
@@ -604,57 +604,57 @@ var InnerTubeClient = class {
|
|
|
604
604
|
if (options.poToken) {
|
|
605
605
|
if (!payload.serviceIntegrityDimensions) payload.serviceIntegrityDimensions = {};
|
|
606
606
|
payload.serviceIntegrityDimensions.poToken = options.poToken;
|
|
607
|
-
log$
|
|
607
|
+
log$7.debug(`Using po_token (length: ${options.poToken.length})`);
|
|
608
608
|
}
|
|
609
609
|
const url = `${INNER_TUBE_API_URL}/player?key=${config.API_KEY || ""}`;
|
|
610
|
-
log$
|
|
611
|
-
log$
|
|
612
|
-
log$
|
|
613
|
-
log$
|
|
614
|
-
log$
|
|
615
|
-
log$
|
|
610
|
+
log$7.debug("=== INNERTUBE REQUEST ===");
|
|
611
|
+
log$7.debug(`Client: ${this.currentClient}`);
|
|
612
|
+
log$7.debug(`API Key: ${config.API_KEY || "none"}`);
|
|
613
|
+
log$7.debug(`URL: ${url}`);
|
|
614
|
+
log$7.debug(`Payload: ${JSON.stringify(payload, null, 2)}`);
|
|
615
|
+
log$7.debug(`Headers: ${JSON.stringify(this.getHeaders(), null, 2)}`);
|
|
616
616
|
const responseResult = await httpClient.post(url, payload, { headers: this.getHeaders() });
|
|
617
617
|
if (isErr(responseResult)) {
|
|
618
|
-
log$
|
|
618
|
+
log$7.error(`HTTP request failed for client ${this.currentClient}:`, responseResult.error.message);
|
|
619
619
|
return responseResult;
|
|
620
620
|
}
|
|
621
621
|
const response = responseResult.value;
|
|
622
622
|
if (!response.ok) {
|
|
623
|
-
log$
|
|
623
|
+
log$7.error(`Player request failed for ${this.currentClient}: HTTP ${response.status}`);
|
|
624
624
|
return err(createError("INNERTUBE_ERROR", `Player request failed with status ${response.status}`));
|
|
625
625
|
}
|
|
626
626
|
try {
|
|
627
627
|
const data = await response.json();
|
|
628
|
-
log$
|
|
629
|
-
log$
|
|
630
|
-
log$
|
|
631
|
-
log$
|
|
632
|
-
if (data.streamingData?.adaptiveFormats?.[0]) log$
|
|
628
|
+
log$7.debug("=== INNERTUBE RESPONSE ===");
|
|
629
|
+
log$7.debug(`playabilityStatus: ${JSON.stringify(data.playabilityStatus, null, 2)}`);
|
|
630
|
+
log$7.debug(`streamingData formats: ${data.streamingData?.formats?.length || 0}`);
|
|
631
|
+
log$7.debug(`streamingData adaptiveFormats: ${data.streamingData?.adaptiveFormats?.length || 0}`);
|
|
632
|
+
if (data.streamingData?.adaptiveFormats?.[0]) log$7.debug(`Sample format: ${JSON.stringify(data.streamingData.adaptiveFormats[0], null, 2)}`);
|
|
633
633
|
const validated = PlayerResponseSchema(data);
|
|
634
634
|
if (validated instanceof type.errors) {
|
|
635
|
-
log$
|
|
635
|
+
log$7.error(`Parse error for ${this.currentClient}:`, validated.summary);
|
|
636
636
|
return err(createError("PARSE_ERROR", `Invalid player response: ${validated.summary}`));
|
|
637
637
|
}
|
|
638
638
|
const status = validated.playabilityStatus.status;
|
|
639
639
|
const reason = validated.playabilityStatus.reason || "No reason";
|
|
640
|
-
log$
|
|
640
|
+
log$7.debug(`Client ${this.currentClient} response: status=${status}, reason=${reason}`);
|
|
641
641
|
if (status === "ERROR") {
|
|
642
|
-
log$
|
|
642
|
+
log$7.warn(`Client ${this.currentClient} returned ERROR: ${reason}`);
|
|
643
643
|
return err(createError("VIDEO_UNAVAILABLE", reason));
|
|
644
644
|
}
|
|
645
645
|
if (status === "LOGIN_REQUIRED") {
|
|
646
|
-
log$
|
|
646
|
+
log$7.warn(`Client ${this.currentClient} requires login: ${reason}`);
|
|
647
647
|
return err(createError("VIDEO_UNAVAILABLE", `Login required: ${reason}`));
|
|
648
648
|
}
|
|
649
649
|
if (status === "UNPLAYABLE") {
|
|
650
|
-
log$
|
|
650
|
+
log$7.warn(`Client ${this.currentClient} unplayable: ${reason}`);
|
|
651
651
|
return err(createError("VIDEO_UNAVAILABLE", `Unplayable: ${reason}`));
|
|
652
652
|
}
|
|
653
653
|
const formatsCount = (validated.streamingData?.formats?.length || 0) + (validated.streamingData?.adaptiveFormats?.length || 0);
|
|
654
|
-
log$
|
|
654
|
+
log$7.info(`Client ${this.currentClient} success: ${formatsCount} formats`);
|
|
655
655
|
return ok(validated);
|
|
656
656
|
} catch (error) {
|
|
657
|
-
log$
|
|
657
|
+
log$7.error(`Parse exception for ${this.currentClient}:`, error);
|
|
658
658
|
return err(createError("PARSE_ERROR", "Failed to parse InnerTube response", error));
|
|
659
659
|
}
|
|
660
660
|
}
|
|
@@ -667,35 +667,35 @@ var InnerTubeClient = class {
|
|
|
667
667
|
*/
|
|
668
668
|
async fetchPlayerWithFallback(videoId, options = {}) {
|
|
669
669
|
const errors = [];
|
|
670
|
-
log$
|
|
671
|
-
log$
|
|
670
|
+
log$7.info(`Starting fallback extraction for ${videoId}`);
|
|
671
|
+
log$7.info(`Fallback order: ${this.FALLBACK_ORDER.join(" → ")}`);
|
|
672
672
|
for (const clientName of this.FALLBACK_ORDER) {
|
|
673
|
-
log$
|
|
673
|
+
log$7.info(`Trying client: ${clientName}`);
|
|
674
674
|
if (isErr(this.setClient(clientName))) {
|
|
675
|
-
log$
|
|
675
|
+
log$7.warn(`Failed to set client ${clientName}`);
|
|
676
676
|
continue;
|
|
677
677
|
}
|
|
678
678
|
const result = await this.fetchPlayer(videoId, options);
|
|
679
679
|
if (isOk(result)) {
|
|
680
680
|
const validationResult = this.isValidResponse(result.value);
|
|
681
681
|
if (validationResult.valid) {
|
|
682
|
-
log$
|
|
682
|
+
log$7.success(`Client ${clientName} returned valid response`);
|
|
683
683
|
return result;
|
|
684
684
|
}
|
|
685
|
-
log$
|
|
685
|
+
log$7.warn(`Client ${clientName} response invalid: ${validationResult.reason}`);
|
|
686
686
|
errors.push({
|
|
687
687
|
client: clientName,
|
|
688
688
|
error: createError("VIDEO_UNAVAILABLE", validationResult.reason)
|
|
689
689
|
});
|
|
690
690
|
} else {
|
|
691
|
-
log$
|
|
691
|
+
log$7.warn(`Client ${clientName} failed: ${result.error.message}`);
|
|
692
692
|
errors.push({
|
|
693
693
|
client: clientName,
|
|
694
694
|
error: result.error
|
|
695
695
|
});
|
|
696
696
|
}
|
|
697
697
|
}
|
|
698
|
-
log$
|
|
698
|
+
log$7.error(`All clients failed for ${videoId}. Errors:`, errors.map((e) => ({
|
|
699
699
|
client: e.client,
|
|
700
700
|
error: e.error.message
|
|
701
701
|
})));
|
|
@@ -1412,6 +1412,7 @@ var PlayerCache = class {
|
|
|
1412
1412
|
|
|
1413
1413
|
//#endregion
|
|
1414
1414
|
//#region src/player/player.ts
|
|
1415
|
+
const log$6 = logger.scope("Player");
|
|
1415
1416
|
/**
|
|
1416
1417
|
* Handles YouTube player.js downloading, parsing, and cipher function extraction.
|
|
1417
1418
|
* Uses AST-based extraction for robustness against YouTube's obfuscation changes.
|
|
@@ -1441,11 +1442,25 @@ var Player = class {
|
|
|
1441
1442
|
if (isOk(latestResult) && latestResult.value) return this.restoreFromCache(latestResult.value);
|
|
1442
1443
|
}
|
|
1443
1444
|
if (!playerUrl) {
|
|
1445
|
+
log$6.info(`Fetching YouTube homepage to find player URL: ${YOUTUBE_BASE_URL}`);
|
|
1444
1446
|
const htmlResult = await httpClient.get(YOUTUBE_BASE_URL);
|
|
1445
|
-
if (!isOk(htmlResult))
|
|
1446
|
-
|
|
1447
|
-
|
|
1447
|
+
if (!isOk(htmlResult)) {
|
|
1448
|
+
log$6.error("Failed to fetch YouTube homepage:", htmlResult.error.message);
|
|
1449
|
+
return err(htmlResult.error);
|
|
1450
|
+
}
|
|
1451
|
+
const html = await htmlResult.value.text();
|
|
1452
|
+
log$6.info(`YouTube homepage response: ${html.length} chars`);
|
|
1453
|
+
log$6.info(`First 500 chars: ${html.substring(0, 500)}`);
|
|
1454
|
+
if (html.includes("Sign in to confirm")) log$6.error("BOT DETECTION: YouTube returned \"Sign in to confirm\" page");
|
|
1455
|
+
if (html.includes("robot") || html.includes("captcha")) log$6.error("BOT DETECTION: YouTube returned robot/captcha page");
|
|
1456
|
+
const match$1 = html.match(/\/s\/player\/[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+\/base\.js/);
|
|
1457
|
+
if (!match$1) {
|
|
1458
|
+
log$6.error("Player URL pattern not found in HTML");
|
|
1459
|
+
log$6.error(`HTML title: ${html.match(/<title>([^<]*)<\/title>/)?.[1] || "No title"}`);
|
|
1460
|
+
return err(createError("PLAYER_FETCH_FAILED", "Could not determine player URL"));
|
|
1461
|
+
}
|
|
1448
1462
|
playerUrl = YOUTUBE_BASE_URL + match$1[0];
|
|
1463
|
+
log$6.success(`Found player URL: ${playerUrl}`);
|
|
1449
1464
|
}
|
|
1450
1465
|
const playerId = this.extractPlayerId(playerUrl);
|
|
1451
1466
|
if (this.cache && playerId) {
|