mcp-youtube-intelligence 0.2.4 → 0.2.5
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/build/analyze_channel.js +19 -12
- package/build/index.js +6 -1
- package/package.json +1 -1
package/build/analyze_channel.js
CHANGED
|
@@ -261,10 +261,10 @@ export async function runApifyTranscriptScraper(videoUrls, token) {
|
|
|
261
261
|
if (runResp === undefined) {
|
|
262
262
|
runResp = await startRun();
|
|
263
263
|
}
|
|
264
|
-
else if (!runResp.ok && runResp.status >= 500) {
|
|
265
|
-
// Retry once on a transient 5xx
|
|
266
|
-
//
|
|
267
|
-
//
|
|
264
|
+
else if (!runResp.ok && (runResp.status >= 500 || runResp.status === 429)) {
|
|
265
|
+
// Retry once on a transient 5xx, or a 429 — Apify returns 429 when the account's
|
|
266
|
+
// concurrent-actor-run limit is hit, which clears within seconds, unlike a bad
|
|
267
|
+
// token or malformed request. Same tolerance already given to Gemini below.
|
|
268
268
|
runResp = await startRun();
|
|
269
269
|
}
|
|
270
270
|
if (!runResp.ok) {
|
|
@@ -289,11 +289,13 @@ export async function runApifyTranscriptScraper(videoUrls, token) {
|
|
|
289
289
|
continue;
|
|
290
290
|
}
|
|
291
291
|
if (!statusResp.ok) {
|
|
292
|
-
// 4xx
|
|
293
|
-
|
|
292
|
+
// Most 4xx are permanent (invalid run ID, bad token) — fail fast instead of
|
|
293
|
+
// polling to timeout. 429 is the exception: Apify's own rate limit, which the
|
|
294
|
+
// next scheduled poll (APIFY_POLL_INTERVAL_MS later) already backs off past.
|
|
295
|
+
if (statusResp.status >= 400 && statusResp.status < 500 && statusResp.status !== 429) {
|
|
294
296
|
throw new Error(`Apify poll error ${statusResp.status} for run ${runId}: ${await statusResp.text()}`);
|
|
295
297
|
}
|
|
296
|
-
continue; // 5xx or network transient — keep polling
|
|
298
|
+
continue; // 5xx, 429, or network transient — keep polling
|
|
297
299
|
}
|
|
298
300
|
let statusBody;
|
|
299
301
|
try {
|
|
@@ -329,7 +331,7 @@ export async function runApifyTranscriptScraper(videoUrls, token) {
|
|
|
329
331
|
if (itemsResp === undefined) {
|
|
330
332
|
itemsResp = await fetchItems();
|
|
331
333
|
}
|
|
332
|
-
else if (!itemsResp.ok && itemsResp.status >= 500) {
|
|
334
|
+
else if (!itemsResp.ok && (itemsResp.status >= 500 || itemsResp.status === 429)) {
|
|
333
335
|
itemsResp = await fetchItems();
|
|
334
336
|
}
|
|
335
337
|
if (!itemsResp.ok)
|
|
@@ -488,7 +490,12 @@ ${truncated}`;
|
|
|
488
490
|
parsed = JSON.parse(text);
|
|
489
491
|
}
|
|
490
492
|
catch {
|
|
491
|
-
|
|
493
|
+
// Counts toward the same "every attempted video produced nothing usable"
|
|
494
|
+
// check below as an HTTP-level failure — an all-malformed-JSON response is
|
|
495
|
+
// just as much a Gemini outage as an all-5xx one, and previously only the
|
|
496
|
+
// latter was surfaced as a thrown error instead of a silent empty success.
|
|
497
|
+
lastRequestError = `Gemini returned unparseable JSON for video ${videoId}`;
|
|
498
|
+
console.warn(`[analyze_channel] ${lastRequestError}, skipping`);
|
|
492
499
|
continue;
|
|
493
500
|
}
|
|
494
501
|
results.push({
|
|
@@ -505,9 +512,9 @@ ${truncated}`;
|
|
|
505
512
|
console.warn(`[analyze_channel] Gemini request failed for video ${videoId}, skipping: ${lastRequestError}`);
|
|
506
513
|
}
|
|
507
514
|
}
|
|
508
|
-
// Every attempted video failed at the HTTP level
|
|
509
|
-
// failure so analyzeChannel's note reports a
|
|
510
|
-
// silently claiming success with zero structured topics.
|
|
515
|
+
// Every attempted video failed, whether at the HTTP level or via unparseable
|
|
516
|
+
// JSON — surface it as a total failure so analyzeChannel's note reports a
|
|
517
|
+
// Gemini outage instead of silently claiming success with zero structured topics.
|
|
511
518
|
if (attempted > 0 && results.length === 0 && lastRequestError) {
|
|
512
519
|
throw new Error(lastRequestError);
|
|
513
520
|
}
|
package/build/index.js
CHANGED
|
@@ -13,6 +13,10 @@ import { z } from "zod";
|
|
|
13
13
|
import { analyzeChannel } from "./analyze_channel.js";
|
|
14
14
|
const require = createRequire(import.meta.url);
|
|
15
15
|
const { version: packageVersion } = require("../package.json");
|
|
16
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
17
|
+
console.log(packageVersion);
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
16
20
|
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
17
21
|
console.log(`mcp-youtube-intelligence v${packageVersion} — MCP server for YouTube channel intelligence
|
|
18
22
|
|
|
@@ -20,7 +24,8 @@ Usage:
|
|
|
20
24
|
mcp-youtube-intelligence [options]
|
|
21
25
|
|
|
22
26
|
Options:
|
|
23
|
-
--help, -h
|
|
27
|
+
--help, -h Show this help message
|
|
28
|
+
--version, -v Print the installed version
|
|
24
29
|
|
|
25
30
|
Tools provided:
|
|
26
31
|
analyze_channel Extract transcripts, topics, and competitive signals from a YouTube channel`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-youtube-intelligence",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "MCP server for extracting structured intelligence from YouTube channels and videos — transcripts, topics, competitive signals",
|
|
5
5
|
"mcpName": "io.github.dmitriusan/mcp-youtube-intelligence",
|
|
6
6
|
"main": "build/index.js",
|