mcp-scraper 0.3.46 → 0.3.47

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.
@@ -10288,8 +10288,8 @@ var init_rates = __esm({
10288
10288
  label: "Video breakdown (frame-by-frame + transcript)",
10289
10289
  aliases: ["video_frame_analysis", "video breakdown", "video analysis", "analyze video"],
10290
10290
  credits: mcToCredits(MC_COSTS.video_analysis),
10291
- unit: "per video",
10292
- notes: "Full multi-lens video breakdown: samples up to 120 frames across a video (to 30 min), analyzes each with vision AI, transcribes the audio, then produces summary, pacing/energy, words-per-minute, topic outline, key points, hook analysis, visual style, and a how-to-replicate recipe with a quality-control pass. Flat $1 per video; refunded if the run fails."
10291
+ unit: "per 120 frames (max 480)",
10292
+ notes: "Full multi-lens video breakdown: samples frames across a video (up to 30 minutes), analyzes each with vision AI, transcribes the audio, then produces summary, pacing/energy, words-per-minute, topic outline, key points, hook analysis, visual style, and a how-to-replicate recipe with a quality-control pass. $1 per 120 frames requested (max 480 = $4); billed down automatically if the video cannot use the requested frames, and refunded fully if the run fails."
10293
10293
  }
10294
10294
  ];
10295
10295
  CONCURRENCY_PRICE_ID = "price_1Ta1NRS8aAcsk3TGwsRnYbix";
@@ -10365,6 +10365,7 @@ var init_rates = __esm({
10365
10365
  REDDIT_THREAD_REFUND: "reddit_thread_refund",
10366
10366
  VIDEO_ANALYSIS: "video_analysis",
10367
10367
  VIDEO_ANALYSIS_REFUND: "video_analysis_refund",
10368
+ VIDEO_ANALYSIS_ADJUST: "video_analysis_adjust",
10368
10369
  MEMORY_AI: "memory_ai",
10369
10370
  MEMORY_AI_REFUND: "memory_ai_refund"
10370
10371
  };
@@ -16657,6 +16658,9 @@ var init_memory = __esm({
16657
16658
  function invalidRequest5(message) {
16658
16659
  return { error_code: "invalid_request", message };
16659
16660
  }
16661
+ function tiersFor(frames) {
16662
+ return Math.ceil(Math.min(Math.max(frames, 1), 480) / 120);
16663
+ }
16660
16664
  var import_hono9, import_zod20, videoApp, AnalyzeBodySchema;
16661
16665
  var init_video_routes = __esm({
16662
16666
  "src/api/video-routes.ts"() {
@@ -16671,7 +16675,7 @@ var init_video_routes = __esm({
16671
16675
  AnalyzeBodySchema = import_zod20.z.object({
16672
16676
  sourceUrl: import_zod20.z.string().trim().url("sourceUrl must be a direct video file URL"),
16673
16677
  intervalS: import_zod20.z.number().min(1).max(30).optional(),
16674
- maxFrames: import_zod20.z.number().int().min(1).max(120).optional(),
16678
+ maxFrames: import_zod20.z.number().int().min(1).max(480).optional(),
16675
16679
  detail: import_zod20.z.enum(["fast", "standard", "deep"]).optional(),
16676
16680
  vault: import_zod20.z.string().trim().min(1).optional()
16677
16681
  });
@@ -16682,21 +16686,24 @@ var init_video_routes = __esm({
16682
16686
  const user = c.get("user");
16683
16687
  const { key: memKey, error: memErr } = await getOrCreateUserMemoryKey(user);
16684
16688
  if (!memKey) return c.json({ error_code: "memory_unavailable", message: memErr ?? "memory unavailable" }, 502);
16685
- const { ok, balance_mc } = await debitMc(user.id, MC_COSTS.video_analysis, LedgerOperation.VIDEO_ANALYSIS, body.sourceUrl);
16686
- if (!ok) return c.json(insufficientBalanceResponse(balance_mc, MC_COSTS.video_analysis), 402);
16689
+ const requestedFrames = Math.min(Math.max(body.maxFrames ?? 120, 1), 480);
16690
+ const tiers = tiersFor(requestedFrames);
16691
+ const holdMc = tiers * MC_COSTS.video_analysis;
16692
+ const { ok, balance_mc } = await debitMc(user.id, holdMc, LedgerOperation.VIDEO_ANALYSIS, body.sourceUrl);
16693
+ if (!ok) return c.json(insufficientBalanceResponse(balance_mc, holdMc), 402);
16687
16694
  const started = await memoryCall(
16688
16695
  "videoAnalyzeStartTool",
16689
16696
  {
16690
16697
  sourceUrl: body.sourceUrl,
16691
16698
  intervalS: body.intervalS ?? 2,
16692
- maxFrames: body.maxFrames ?? 120,
16699
+ maxFrames: requestedFrames,
16693
16700
  detail: body.detail ?? "standard",
16694
16701
  vault: body.vault ?? "Library"
16695
16702
  },
16696
16703
  memKey
16697
16704
  );
16698
16705
  if (!started.ok || !started.runId) {
16699
- await creditMc(user.id, MC_COSTS.video_analysis, LedgerOperation.VIDEO_ANALYSIS_REFUND, `start-failed:${body.sourceUrl}`);
16706
+ await creditMc(user.id, holdMc, LedgerOperation.VIDEO_ANALYSIS_REFUND, `start-failed:${body.sourceUrl}`);
16700
16707
  return c.json({ error_code: "video_start_failed", message: started.error ?? "could not start analysis" }, 502);
16701
16708
  }
16702
16709
  return c.json({
@@ -16715,11 +16722,30 @@ var init_video_routes = __esm({
16715
16722
  if (!memKey) return c.json({ error_code: "memory_unavailable", message: memErr ?? "memory unavailable" }, 502);
16716
16723
  const st = await memoryCall("videoAnalyzeStatusTool", { runId }, memKey);
16717
16724
  if (!st.ok) return c.json({ error_code: "not_found", message: st.error ?? "run not found" }, 404);
16725
+ const requestedTiers = tiersFor(st.maxFrames ?? 120);
16726
+ let reconciliation = null;
16718
16727
  if (st.status === "failed") {
16728
+ const refundMc = requestedTiers * MC_COSTS.video_analysis;
16719
16729
  const refundOp = `${LedgerOperation.VIDEO_ANALYSIS_REFUND}:${runId}`;
16720
16730
  if (!await ledgerExistsForOperation(user.id, refundOp)) {
16721
- await creditMc(user.id, MC_COSTS.video_analysis, refundOp, `run-failed:${runId}`);
16731
+ await creditMc(user.id, refundMc, refundOp, `run-failed:${runId}`);
16732
+ }
16733
+ reconciliation = { billedMc: requestedTiers * MC_COSTS.video_analysis, refundedMc: refundMc, effectiveFrames: null };
16734
+ }
16735
+ if (st.status === "done") {
16736
+ const effectiveTiers = tiersFor(st.frameCount ?? 120);
16737
+ const diff = requestedTiers - effectiveTiers;
16738
+ if (diff > 0) {
16739
+ const adjustOp = `${LedgerOperation.VIDEO_ANALYSIS_ADJUST}:${runId}`;
16740
+ if (!await ledgerExistsForOperation(user.id, adjustOp)) {
16741
+ await creditMc(user.id, diff * MC_COSTS.video_analysis, adjustOp, `tier-reconcile:${runId}`);
16742
+ }
16722
16743
  }
16744
+ reconciliation = {
16745
+ billedMc: requestedTiers * MC_COSTS.video_analysis,
16746
+ refundedMc: Math.max(diff, 0) * MC_COSTS.video_analysis,
16747
+ effectiveFrames: st.frameCount ?? null
16748
+ };
16723
16749
  }
16724
16750
  return c.json({
16725
16751
  ok: true,
@@ -16728,7 +16754,8 @@ var init_video_routes = __esm({
16728
16754
  progress: st.progress ?? null,
16729
16755
  frameCount: st.frameCount ?? null,
16730
16756
  ...st.status === "done" ? { artifactPath: st.artifactPath ?? null, report: st.report ?? null } : {},
16731
- ...st.error ? { error: st.error } : {}
16757
+ ...st.error ? { error: st.error } : {},
16758
+ ...reconciliation ? { reconciliation } : {}
16732
16759
  });
16733
16760
  });
16734
16761
  }
@@ -26003,7 +26030,7 @@ var PACKAGE_VERSION;
26003
26030
  var init_version = __esm({
26004
26031
  "src/version.ts"() {
26005
26032
  "use strict";
26006
- PACKAGE_VERSION = "0.3.46";
26033
+ PACKAGE_VERSION = "0.3.47";
26007
26034
  }
26008
26035
  });
26009
26036
 
@@ -26199,7 +26226,7 @@ var init_mcp_tool_schemas = __esm({
26199
26226
  VideoFrameAnalysisInputSchema = {
26200
26227
  sourceUrl: import_zod31.z.string().min(1).describe("A DIRECT video file URL (.mp4/.webm/.mov). Not a YouTube/Facebook/Instagram page URL \u2014 resolve those to a direct media URL first. Videos up to 30 minutes are supported."),
26201
26228
  intervalS: import_zod31.z.number().min(1).max(30).optional().describe("Preferred seconds between sampled frames (1-30, default 2). Automatically widened for long videos so the whole duration is covered within the frame budget."),
26202
- maxFrames: import_zod31.z.number().int().min(1).max(120).optional().describe("Max frames analyzed (<=120, default 120). Frames are spread evenly across the whole video."),
26229
+ maxFrames: import_zod31.z.number().int().min(1).max(480).optional().describe("Max frames analyzed (<=480, default 120). $1 per 120 frames requested \u2014 120=$1 \u2026 480=$4 \u2014 automatically refunded down if the video cannot use them (minimum 1s between frames). Frames are spread evenly across the whole video."),
26203
26230
  detail: import_zod31.z.enum(["fast", "standard", "deep"]).optional().describe("Analysis depth. Default standard."),
26204
26231
  vault: import_zod31.z.string().min(1).optional().describe('Memory vault to save the finished breakdown into. Default "Library".')
26205
26232
  };
@@ -26662,7 +26689,12 @@ var init_mcp_tool_schemas = __esm({
26662
26689
  frameCount: import_zod31.z.number().int().nullable().optional(),
26663
26690
  artifactPath: NullableString,
26664
26691
  report: NullableString,
26665
- error: NullableString
26692
+ error: NullableString,
26693
+ reconciliation: import_zod31.z.object({
26694
+ billedMc: import_zod31.z.number().int(),
26695
+ refundedMc: import_zod31.z.number().int(),
26696
+ effectiveFrames: import_zod31.z.number().int().nullable()
26697
+ }).nullable().optional()
26666
26698
  };
26667
26699
  RedditThreadOutputSchema = {
26668
26700
  sourceUrl: NullableString,
@@ -27555,14 +27587,14 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
27555
27587
  }, async (input) => formatRedditThread(await executor.redditThread(input), input));
27556
27588
  server.registerTool("video_frame_analysis", {
27557
27589
  title: "Video Breakdown (frame-by-frame + transcript)",
27558
- description: "Produce a deep frame-by-frame + transcript breakdown of a video \u2014 pacing, hook, visual style, and how to replicate it. Pass a DIRECT video file URL (.mp4/.webm/.mov), not a page URL. Runs asynchronously and costs a flat $1 (refunded on failure): returns a runId immediately; poll video_frame_analysis_status until done.",
27590
+ description: "Produce a deep frame-by-frame + transcript breakdown of a video \u2014 pacing, hook, visual style, and how to replicate it. Pass a DIRECT video file URL (.mp4/.webm/.mov), not a page URL. Costs $1 per 120 frames requested (max 480 = $4; refunded down if the video can't use them; refunded fully on failure): returns a runId immediately; poll video_frame_analysis_status until done. Videos up to 30 minutes.",
27559
27591
  inputSchema: VideoFrameAnalysisInputSchema,
27560
27592
  outputSchema: recordOutputSchema("video_frame_analysis", VideoFrameAnalysisOutputSchema),
27561
27593
  annotations: { title: "Video Breakdown", readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
27562
27594
  }, async (input) => executor.videoFrameAnalysis(input));
27563
27595
  server.registerTool("video_frame_analysis_status", {
27564
27596
  title: "Video Breakdown Status",
27565
- description: 'Check progress of a video breakdown started with video_frame_analysis, using its runId. Free to call. When status is "done" it returns the full report and vault path; stop polling on "done" or "failed".',
27597
+ description: 'Check progress of a video breakdown started with video_frame_analysis, using its runId. Free to call. When status is "done" it returns the full report and vault path; stop polling on "done" or "failed". Reports the billed tier reconciliation when done.',
27566
27598
  inputSchema: VideoFrameAnalysisStatusInputSchema,
27567
27599
  outputSchema: recordOutputSchema("video_frame_analysis_status", VideoFrameAnalysisStatusOutputSchema),
27568
27600
  annotations: { title: "Video Breakdown Status", readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }