mcp-scraper 0.4.13 → 0.5.0
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/bin/api-server.cjs +63 -157
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +269 -3
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +3 -2
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- package/dist/chunk-3RTPEQJC.js +7 -0
- package/dist/chunk-3RTPEQJC.js.map +1 -0
- package/dist/{chunk-SIPXEFTR.js → chunk-APJO2XV5.js} +6 -6
- package/dist/chunk-APJO2XV5.js.map +1 -0
- package/dist/{chunk-VQV7MB2S.js → chunk-BL7BBSYF.js} +2 -2
- package/dist/{chunk-EA2AANFI.js → chunk-J4T5OSCF.js} +7 -4
- package/dist/chunk-J4T5OSCF.js.map +1 -0
- package/dist/{server-HNZG36IP.js → server-Z4MEISH5.js} +59 -154
- package/dist/server-Z4MEISH5.js.map +1 -0
- package/dist/{site-extract-repository-6X2C7RLF.js → site-extract-repository-U476J44K.js} +3 -3
- package/dist/{worker-WCSKUAOT.js → worker-KJZ3ZN2N.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-EA2AANFI.js.map +0 -1
- package/dist/chunk-O2D5HGKF.js +0 -7
- package/dist/chunk-O2D5HGKF.js.map +0 -1
- package/dist/chunk-SIPXEFTR.js.map +0 -1
- package/dist/server-HNZG36IP.js.map +0 -1
- /package/dist/{chunk-VQV7MB2S.js.map → chunk-BL7BBSYF.js.map} +0 -0
- /package/dist/{site-extract-repository-6X2C7RLF.js.map → site-extract-repository-U476J44K.js.map} +0 -0
- /package/dist/{worker-WCSKUAOT.js.map → worker-KJZ3ZN2N.js.map} +0 -0
package/dist/bin/api-server.cjs
CHANGED
|
@@ -8013,16 +8013,16 @@ var init_rates = __esm({
|
|
|
8013
8013
|
browser_minute: 12e3,
|
|
8014
8014
|
reddit_thread: 3e3,
|
|
8015
8015
|
reddit_comment: 200,
|
|
8016
|
-
video_analysis:
|
|
8016
|
+
video_analysis: 666700,
|
|
8017
8017
|
trustpilot_reviews: 500,
|
|
8018
8018
|
trustpilot_review: 100,
|
|
8019
8019
|
g2_reviews: 500,
|
|
8020
|
-
g2_review:
|
|
8020
|
+
g2_review: 200,
|
|
8021
8021
|
diff_page: 100
|
|
8022
8022
|
};
|
|
8023
8023
|
MC_PER_BROWSER_MS = MC_COSTS.browser_minute / 6e4;
|
|
8024
8024
|
BROWSER_OPEN_MIN_BALANCE_MC = 1e3;
|
|
8025
|
-
MC_PER_CREDIT =
|
|
8025
|
+
MC_PER_CREDIT = 100;
|
|
8026
8026
|
CREDIT_COST_CATALOG = [
|
|
8027
8027
|
{
|
|
8028
8028
|
key: "serp",
|
|
@@ -8242,8 +8242,8 @@ var init_rates = __esm({
|
|
|
8242
8242
|
CONCURRENCY_PRICE_ID = "price_1Ta1NRS8aAcsk3TGwsRnYbix";
|
|
8243
8243
|
SUBSCRIPTION_TIERS = {
|
|
8244
8244
|
"price_1TmiHRS8aAcsk3TGwmSNfNIa": { tier: "starter", label: "Starter", price_id: "price_1TmiHRS8aAcsk3TGwmSNfNIa", monthly_usd: 12, credits_mc: 8e6, concurrency: 3, intro_coupon: "mcp-starter-1dollar-intro-12" },
|
|
8245
|
-
"
|
|
8246
|
-
"
|
|
8245
|
+
"price_1TrgihS8aAcsk3TG5cglrq4D": { tier: "growth", label: "Growth", price_id: "price_1TrgihS8aAcsk3TG5cglrq4D", monthly_usd: 40, credits_mc: 26666700, concurrency: 10, intro_coupon: null },
|
|
8246
|
+
"price_1TrgihS8aAcsk3TG4HnG4gbY": { tier: "scale", label: "Scale", price_id: "price_1TrgihS8aAcsk3TG4HnG4gbY", monthly_usd: 100, credits_mc: 8e7, concurrency: 20, intro_coupon: null }
|
|
8247
8247
|
};
|
|
8248
8248
|
SUBSCRIPTION_TIER_BY_KEY = Object.fromEntries(
|
|
8249
8249
|
Object.values(SUBSCRIPTION_TIERS).map((t) => [t.tier, t])
|
|
@@ -13705,13 +13705,12 @@ function parseTimedtextXml(xml) {
|
|
|
13705
13705
|
}
|
|
13706
13706
|
return results;
|
|
13707
13707
|
}
|
|
13708
|
-
function isBotBlockStatus(status) {
|
|
13709
|
-
return status === "LOGIN_REQUIRED" || status === "AGE_VERIFICATION_REQUIRED";
|
|
13710
|
-
}
|
|
13711
13708
|
async function probePlayer(page, videoId) {
|
|
13712
13709
|
return page.evaluate(
|
|
13713
13710
|
async ({ vid, clients }) => {
|
|
13714
13711
|
let lastStatus = null;
|
|
13712
|
+
let sawBotBlock = false;
|
|
13713
|
+
let sawAccessible = false;
|
|
13715
13714
|
for (const client2 of clients) {
|
|
13716
13715
|
const resp = await fetch("/youtubei/v1/player?prettyPrint=false", {
|
|
13717
13716
|
method: "POST",
|
|
@@ -13727,97 +13726,24 @@ async function probePlayer(page, videoId) {
|
|
|
13727
13726
|
}
|
|
13728
13727
|
const data = await resp.json();
|
|
13729
13728
|
lastStatus = data?.playabilityStatus?.status ?? null;
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
let captions = null;
|
|
13729
|
+
if (lastStatus === "LOGIN_REQUIRED" || lastStatus === "AGE_VERIFICATION_REQUIRED") sawBotBlock = true;
|
|
13730
|
+
else if (lastStatus === "OK") sawAccessible = true;
|
|
13733
13731
|
const tracks = data?.captions?.playerCaptionsTracklistRenderer?.captionTracks ?? [];
|
|
13734
13732
|
if (tracks.length > 0) {
|
|
13735
13733
|
const track = tracks.find((t) => t.languageCode === "en") ?? tracks[0];
|
|
13736
13734
|
const sep = track.baseUrl.includes("?") ? "&" : "?";
|
|
13737
13735
|
const xmlResp = await fetch(`${track.baseUrl}${sep}fmt=json3`).catch(() => null);
|
|
13738
|
-
if (xmlResp?.ok)
|
|
13736
|
+
if (xmlResp?.ok) {
|
|
13737
|
+
const captions = await xmlResp.text();
|
|
13738
|
+
if (captions) return { captions, status: lastStatus, escalate: false };
|
|
13739
|
+
}
|
|
13739
13740
|
}
|
|
13740
|
-
if (!captions && !pick) continue;
|
|
13741
|
-
return {
|
|
13742
|
-
captions,
|
|
13743
|
-
status: lastStatus,
|
|
13744
|
-
audio: pick ? {
|
|
13745
|
-
url: pick.url,
|
|
13746
|
-
mimeType: pick.mimeType,
|
|
13747
|
-
bitrate: pick.bitrate ?? null,
|
|
13748
|
-
contentLength: pick.contentLength ? Number(pick.contentLength) || null : null,
|
|
13749
|
-
approxDurationMs: pick.approxDurationMs ? Number(pick.approxDurationMs) || null : null
|
|
13750
|
-
} : null
|
|
13751
|
-
};
|
|
13752
13741
|
}
|
|
13753
|
-
return { captions: null,
|
|
13742
|
+
return { captions: null, status: lastStatus, escalate: sawBotBlock && !sawAccessible };
|
|
13754
13743
|
},
|
|
13755
13744
|
{ vid: videoId, clients: INNERTUBE_CLIENTS }
|
|
13756
13745
|
).catch(() => null);
|
|
13757
13746
|
}
|
|
13758
|
-
async function downloadWholeAudioStreamInOneRequestBecauseYouTubeAllowsOnlyOnePerIp(page, audio) {
|
|
13759
|
-
if (audio.contentLength && audio.contentLength > MAX_AUDIO_BYTES) {
|
|
13760
|
-
console.warn(`[CaptionFetcher] audio stream ${audio.contentLength} bytes exceeds ${MAX_AUDIO_BYTES} byte cap`);
|
|
13761
|
-
return null;
|
|
13762
|
-
}
|
|
13763
|
-
const sep = audio.url.includes("?") ? "&" : "?";
|
|
13764
|
-
const url = audio.contentLength ? `${audio.url}${sep}range=0-${audio.contentLength - 1}` : audio.url;
|
|
13765
|
-
const result = await page.evaluate(async (u) => {
|
|
13766
|
-
try {
|
|
13767
|
-
const resp = await fetch(u);
|
|
13768
|
-
if (!resp.ok) return { ok: false, error: `http_${resp.status}` };
|
|
13769
|
-
const blob = await resp.blob();
|
|
13770
|
-
const dataUrl = await new Promise((resolve, reject) => {
|
|
13771
|
-
const reader = new FileReader();
|
|
13772
|
-
reader.onload = () => resolve(reader.result);
|
|
13773
|
-
reader.onerror = () => reject(reader.error);
|
|
13774
|
-
reader.readAsDataURL(blob);
|
|
13775
|
-
});
|
|
13776
|
-
return { ok: true, base64: dataUrl.slice(dataUrl.indexOf(",") + 1) };
|
|
13777
|
-
} catch (e) {
|
|
13778
|
-
return { ok: false, error: String(e) };
|
|
13779
|
-
}
|
|
13780
|
-
}, url).catch((e) => ({ ok: false, error: String(e) }));
|
|
13781
|
-
if (!result.ok || !result.base64) {
|
|
13782
|
-
console.warn(`[CaptionFetcher] audio fetch failed (${result.error ?? "unknown"}) for a ${audio.contentLength ?? 0} byte stream`);
|
|
13783
|
-
return null;
|
|
13784
|
-
}
|
|
13785
|
-
const bytes = Buffer.from(result.base64, "base64");
|
|
13786
|
-
if (audio.contentLength && bytes.length < audio.contentLength) {
|
|
13787
|
-
console.warn(`[CaptionFetcher] partial audio: ${bytes.length} of ${audio.contentLength} bytes`);
|
|
13788
|
-
return null;
|
|
13789
|
-
}
|
|
13790
|
-
return { bytes, mimeType: audio.mimeType.split(";")[0].trim() };
|
|
13791
|
-
}
|
|
13792
|
-
async function transcribeWithWizper(videoId, audio, startMs) {
|
|
13793
|
-
const falKey = process.env.FAL_KEY;
|
|
13794
|
-
if (!falKey) return null;
|
|
13795
|
-
try {
|
|
13796
|
-
import_client4.fal.config({ credentials: falKey });
|
|
13797
|
-
const ext = audio.mimeType.includes("webm") ? "webm" : audio.mimeType.includes("mp4") ? "m4a" : "audio";
|
|
13798
|
-
const audioFile = new File([new Uint8Array(audio.bytes)], `audio.${ext}`, { type: audio.mimeType });
|
|
13799
|
-
const uploadedUrl = await import_client4.fal.storage.upload(audioFile);
|
|
13800
|
-
const subscribeStartMs = Date.now();
|
|
13801
|
-
const result = await import_client4.fal.subscribe("fal-ai/wizper", {
|
|
13802
|
-
input: { audio_url: uploadedUrl, task: "transcribe", language: "en", chunk_level: "segment" },
|
|
13803
|
-
logs: false,
|
|
13804
|
-
pollInterval: 3e3
|
|
13805
|
-
});
|
|
13806
|
-
const subscribeDurationMs = Date.now() - subscribeStartMs;
|
|
13807
|
-
const data = result.data;
|
|
13808
|
-
const chunks = (data.chunks ?? []).map((c) => ({
|
|
13809
|
-
timestamp: c.timestamp,
|
|
13810
|
-
text: c.text.trim()
|
|
13811
|
-
}));
|
|
13812
|
-
void recordVendorUsage({ vendor: "fal_wizper", model: "fal-ai/wizper", units: subscribeDurationMs / 1e3, unitType: "compute_sec" });
|
|
13813
|
-
const text = data.text ?? chunks.map((c) => c.text).join(" ");
|
|
13814
|
-
if (!text) return null;
|
|
13815
|
-
return { videoId, text, chunks, durationMs: Date.now() - startMs, method: "browser-whisper" };
|
|
13816
|
-
} catch (err) {
|
|
13817
|
-
console.warn(`[CaptionFetcher] wizper transcription failed for ${videoId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13818
|
-
return null;
|
|
13819
|
-
}
|
|
13820
|
-
}
|
|
13821
13747
|
async function resolveResidentialProxy(kernelApiKey) {
|
|
13822
13748
|
try {
|
|
13823
13749
|
const resolved = await resolveKernelProxyId({
|
|
@@ -13833,14 +13759,13 @@ async function resolveResidentialProxy(kernelApiKey) {
|
|
|
13833
13759
|
return {};
|
|
13834
13760
|
}
|
|
13835
13761
|
}
|
|
13836
|
-
async function
|
|
13762
|
+
async function probeCaptions(videoId, kernelProxyIdOverride) {
|
|
13837
13763
|
const kernelApiKey = browserServiceApiKey();
|
|
13838
13764
|
if (!kernelApiKey) return null;
|
|
13839
|
-
const kernelProxyId =
|
|
13765
|
+
const kernelProxyId = kernelProxyIdOverride ?? browserServiceProxyId();
|
|
13840
13766
|
const driver = new BrowserDriver();
|
|
13841
13767
|
return runWithCostContext({ ...currentCostContext(), op: "yt_transcription", subOp: "browser-innertube" }, async () => {
|
|
13842
13768
|
let entries = [];
|
|
13843
|
-
let audio = null;
|
|
13844
13769
|
let retryWithFreshIp = false;
|
|
13845
13770
|
try {
|
|
13846
13771
|
await driver.launch({ kernelApiKey, kernelProxyId, headless: true, headlessMode: "headless", viewport: { width: 1280, height: 800 }, locale: "en-US" });
|
|
@@ -13852,69 +13777,51 @@ async function probeAndExtract(videoId, opts) {
|
|
|
13852
13777
|
);
|
|
13853
13778
|
const probe = await probePlayer(page, videoId);
|
|
13854
13779
|
if (!probe) return null;
|
|
13855
|
-
if (probe.captions
|
|
13780
|
+
if (probe.captions) {
|
|
13856
13781
|
entries = parseTimedtextJson3(probe.captions);
|
|
13857
13782
|
if (entries.length === 0) entries = parseTimedtextXml(probe.captions);
|
|
13858
13783
|
}
|
|
13859
13784
|
if (entries.length === 0) {
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
console.warn(`[CaptionFetcher] no captions and no audio stream for ${videoId} (playability=${probe.status ?? "unknown"}, proxied=${Boolean(kernelProxyId)})`);
|
|
13863
|
-
return { entries, audio: null, retryWithFreshIp };
|
|
13864
|
-
}
|
|
13865
|
-
audio = await downloadWholeAudioStreamInOneRequestBecauseYouTubeAllowsOnlyOnePerIp(page, probe.audio);
|
|
13866
|
-
if (!audio) return { entries, audio: null, retryWithFreshIp: true };
|
|
13785
|
+
retryWithFreshIp = probe.escalate;
|
|
13786
|
+
console.warn(`[CaptionFetcher] no caption track for ${videoId} (playability=${probe.status ?? "unknown"}, proxied=${Boolean(kernelProxyId)}, escalate=${probe.escalate})`);
|
|
13867
13787
|
}
|
|
13868
13788
|
} catch (err) {
|
|
13869
13789
|
console.warn(`[CaptionFetcher] kernel session failed for ${videoId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13870
13790
|
return null;
|
|
13871
13791
|
} finally {
|
|
13872
|
-
|
|
13873
|
-
await runWithCostContext({ ...currentCostContext(), op: "yt_transcription", subOp: servedSubOp }, () => driver.close());
|
|
13792
|
+
await driver.close();
|
|
13874
13793
|
}
|
|
13875
|
-
return { entries,
|
|
13794
|
+
return { entries, retryWithFreshIp };
|
|
13876
13795
|
});
|
|
13877
13796
|
}
|
|
13878
|
-
async function extractWithProxyEscalation(videoId, opts) {
|
|
13879
|
-
const direct = await probeAndExtract(videoId, opts);
|
|
13880
|
-
if (direct && (direct.entries.length > 0 || direct.audio)) return direct;
|
|
13881
|
-
const shouldEscalate = direct === null || direct.retryWithFreshIp;
|
|
13882
|
-
if (!shouldEscalate) return direct;
|
|
13883
|
-
const kernelApiKey = browserServiceApiKey();
|
|
13884
|
-
if (!kernelApiKey) return direct;
|
|
13885
|
-
const { kernelProxyId, disposableProxyId } = await resolveResidentialProxy(kernelApiKey);
|
|
13886
|
-
if (!kernelProxyId) return direct;
|
|
13887
|
-
try {
|
|
13888
|
-
console.warn(`[CaptionFetcher] ${videoId}: retrying through residential proxy after bot block`);
|
|
13889
|
-
const viaProxy = await probeAndExtract(videoId, { ...opts, kernelProxyId });
|
|
13890
|
-
return viaProxy ?? direct;
|
|
13891
|
-
} finally {
|
|
13892
|
-
if (disposableProxyId) {
|
|
13893
|
-
await deleteKernelProxyId(kernelApiKey, disposableProxyId).catch(
|
|
13894
|
-
(err) => console.warn(`[CaptionFetcher] failed to delete disposable proxy: ${err instanceof Error ? err.message : String(err)}`)
|
|
13895
|
-
);
|
|
13896
|
-
}
|
|
13897
|
-
}
|
|
13898
|
-
}
|
|
13899
13797
|
async function fetchViaKernelSession(videoId) {
|
|
13900
13798
|
const start = Date.now();
|
|
13901
|
-
|
|
13902
|
-
if (!outcome)
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
|
|
13912
|
-
|
|
13913
|
-
|
|
13914
|
-
|
|
13915
|
-
|
|
13799
|
+
let outcome = await probeCaptions(videoId);
|
|
13800
|
+
if (!outcome || outcome.entries.length === 0 && outcome.retryWithFreshIp) {
|
|
13801
|
+
const kernelApiKey = browserServiceApiKey();
|
|
13802
|
+
if (kernelApiKey) {
|
|
13803
|
+
const { kernelProxyId, disposableProxyId } = await resolveResidentialProxy(kernelApiKey);
|
|
13804
|
+
if (kernelProxyId) {
|
|
13805
|
+
try {
|
|
13806
|
+
console.warn(`[CaptionFetcher] ${videoId}: retrying captions through residential proxy after bot block`);
|
|
13807
|
+
outcome = await probeCaptions(videoId, kernelProxyId) ?? outcome;
|
|
13808
|
+
} finally {
|
|
13809
|
+
if (disposableProxyId) {
|
|
13810
|
+
await deleteKernelProxyId(kernelApiKey, disposableProxyId).catch(
|
|
13811
|
+
(err) => console.warn(`[CaptionFetcher] failed to delete disposable proxy: ${err instanceof Error ? err.message : String(err)}`)
|
|
13812
|
+
);
|
|
13813
|
+
}
|
|
13814
|
+
}
|
|
13815
|
+
}
|
|
13816
|
+
}
|
|
13916
13817
|
}
|
|
13917
|
-
return null;
|
|
13818
|
+
if (!outcome || outcome.entries.length === 0) return null;
|
|
13819
|
+
const chunks = outcome.entries.map((e) => ({
|
|
13820
|
+
timestamp: [e.start / 1e3, (e.start + e.dur) / 1e3],
|
|
13821
|
+
text: e.text
|
|
13822
|
+
}));
|
|
13823
|
+
const text = chunks.map((c) => c.text).join(" ");
|
|
13824
|
+
return { videoId, text, chunks, durationMs: Date.now() - start, method: "browser-innertube" };
|
|
13918
13825
|
}
|
|
13919
13826
|
async function fetchViaResolvedAudio(videoId) {
|
|
13920
13827
|
if (!mediaResolverConfigured()) return null;
|
|
@@ -13964,7 +13871,7 @@ async function fetchCaptions(videoId) {
|
|
|
13964
13871
|
`Could not transcribe ${videoId}: it has no caption track and its audio could not be retrieved.`
|
|
13965
13872
|
);
|
|
13966
13873
|
}
|
|
13967
|
-
var
|
|
13874
|
+
var INNERTUBE_CLIENTS, YT_RESIDENTIAL_LOCATION;
|
|
13968
13875
|
var init_CaptionFetcher = __esm({
|
|
13969
13876
|
"src/youtube/CaptionFetcher.ts"() {
|
|
13970
13877
|
"use strict";
|
|
@@ -13973,7 +13880,6 @@ var init_CaptionFetcher = __esm({
|
|
|
13973
13880
|
init_kernel_proxy_resolver();
|
|
13974
13881
|
init_audio_resolver();
|
|
13975
13882
|
init_media_transcription();
|
|
13976
|
-
import_client4 = require("@fal-ai/client");
|
|
13977
13883
|
init_cost_telemetry();
|
|
13978
13884
|
init_cost_context();
|
|
13979
13885
|
INNERTUBE_CLIENTS = [
|
|
@@ -13981,7 +13887,6 @@ var init_CaptionFetcher = __esm({
|
|
|
13981
13887
|
{ name: "IOS", version: "20.10.4", userAgent: "com.google.ios.youtube/20.10.4 (iPhone16,2; U; CPU iOS 18_3_2 like Mac OS X;)" },
|
|
13982
13888
|
{ name: "ANDROID_VR", version: "1.62.27", userAgent: "com.google.android.apps.youtube.vr.oculus/1.62.27 (Linux; U; Android 12L) gzip" }
|
|
13983
13889
|
];
|
|
13984
|
-
MAX_AUDIO_BYTES = 96 * 1024 * 1024;
|
|
13985
13890
|
YT_RESIDENTIAL_LOCATION = process.env.YOUTUBE_PROXY_LOCATION ?? "Austin, Texas";
|
|
13986
13891
|
}
|
|
13987
13892
|
});
|
|
@@ -15560,7 +15465,7 @@ async function kernelLaunchOptsResidential() {
|
|
|
15560
15465
|
}
|
|
15561
15466
|
return { headless: true, kernelApiKey: browserServiceApiKey(), kernelProxyId: proxyId, viewport: { width: 1280, height: 900 }, locale: "en-US" };
|
|
15562
15467
|
}
|
|
15563
|
-
var import_hono5, import_zod16,
|
|
15468
|
+
var import_hono5, import_zod16, import_client4, FacebookAdBodySchema, FacebookPageIntelBodySchema, FacebookTranscribeBodySchema, FacebookVideoTranscribeBodySchema, FacebookSearchBodySchema, FacebookMediaBodySchema, facebookAdApp, ALLOWED_MEDIA_HOSTS;
|
|
15564
15469
|
var init_facebook_ad_routes = __esm({
|
|
15565
15470
|
"src/api/facebook-ad-routes.ts"() {
|
|
15566
15471
|
"use strict";
|
|
@@ -15576,7 +15481,7 @@ var init_facebook_ad_routes = __esm({
|
|
|
15576
15481
|
init_FacebookOrganicVideoExtractor();
|
|
15577
15482
|
init_kernel_proxy_resolver();
|
|
15578
15483
|
init_schemas3();
|
|
15579
|
-
|
|
15484
|
+
import_client4 = require("@fal-ai/client");
|
|
15580
15485
|
init_api_auth();
|
|
15581
15486
|
init_url_utils();
|
|
15582
15487
|
init_concurrency_gates();
|
|
@@ -15719,7 +15624,7 @@ var init_facebook_ad_routes = __esm({
|
|
|
15719
15624
|
metadata: { videoUrl }
|
|
15720
15625
|
});
|
|
15721
15626
|
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
15722
|
-
|
|
15627
|
+
import_client4.fal.config({ credentials: process.env.FAL_KEY });
|
|
15723
15628
|
const holdMc = MEDIA_TRANSCRIBE_HOLD_MC;
|
|
15724
15629
|
let debited = false;
|
|
15725
15630
|
try {
|
|
@@ -15763,7 +15668,7 @@ var init_facebook_ad_routes = __esm({
|
|
|
15763
15668
|
metadata: { url: sourceUrl.href }
|
|
15764
15669
|
});
|
|
15765
15670
|
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
15766
|
-
|
|
15671
|
+
import_client4.fal.config({ credentials: process.env.FAL_KEY });
|
|
15767
15672
|
const driver = new BrowserDriver();
|
|
15768
15673
|
const holdMc = MEDIA_TRANSCRIBE_HOLD_MC;
|
|
15769
15674
|
let debited = false;
|
|
@@ -16144,7 +16049,7 @@ async function kernelLaunchOptsResidential2() {
|
|
|
16144
16049
|
function isBlockedMessage(msg) {
|
|
16145
16050
|
return msg.toLowerCase().includes("blocked") || msg.toLowerCase().includes("captcha");
|
|
16146
16051
|
}
|
|
16147
|
-
var import_hono6, import_zod17,
|
|
16052
|
+
var import_hono6, import_zod17, import_client5, GoogleAdsSearchBodySchema, GoogleAdsPageIntelBodySchema, GoogleAdsTranscribeBodySchema, googleAdsApp;
|
|
16148
16053
|
var init_google_ads_routes = __esm({
|
|
16149
16054
|
"src/api/google-ads-routes.ts"() {
|
|
16150
16055
|
"use strict";
|
|
@@ -16157,7 +16062,7 @@ var init_google_ads_routes = __esm({
|
|
|
16157
16062
|
init_GoogleAdsExtractor();
|
|
16158
16063
|
init_kernel_proxy_resolver();
|
|
16159
16064
|
init_schemas3();
|
|
16160
|
-
|
|
16065
|
+
import_client5 = require("@fal-ai/client");
|
|
16161
16066
|
init_api_auth();
|
|
16162
16067
|
init_url_utils();
|
|
16163
16068
|
init_concurrency_gates();
|
|
@@ -16268,7 +16173,7 @@ var init_google_ads_routes = __esm({
|
|
|
16268
16173
|
const user = c.get("user");
|
|
16269
16174
|
const gate = await acquireConcurrencyGate(user, "google_ads_transcribe", { reuseLockId: c.req.header("x-mcp-scraper-concurrency-lock"), metadata: { videoUrl } });
|
|
16270
16175
|
if (!gate.ok) return c.json(concurrencyLimitExceededResponse(gate), 429, { "Retry-After": String(gate.retryAfterSeconds) });
|
|
16271
|
-
|
|
16176
|
+
import_client5.fal.config({ credentials: process.env.FAL_KEY });
|
|
16272
16177
|
const holdMc = MEDIA_TRANSCRIBE_HOLD_MC;
|
|
16273
16178
|
let debited = false;
|
|
16274
16179
|
try {
|
|
@@ -20614,7 +20519,7 @@ function formatCreditsInfo(raw, input) {
|
|
|
20614
20519
|
return `| ${c.label} | ${c.credits} | ${c.unit}${notes} |`;
|
|
20615
20520
|
}).join("\n");
|
|
20616
20521
|
const ledgerRows = ledger.map((row) => {
|
|
20617
|
-
const credits = row.amount_mc /
|
|
20522
|
+
const credits = row.amount_mc / MC_PER_CREDIT;
|
|
20618
20523
|
return `| ${row.created_at} | ${row.operation} | ${credits} | ${row.description ?? ""} |`;
|
|
20619
20524
|
}).join("\n");
|
|
20620
20525
|
const matchedSection = matched ? `
|
|
@@ -20664,7 +20569,7 @@ ${ledgerRows}` : ""
|
|
|
20664
20569
|
ledger: ledger.map((row) => ({
|
|
20665
20570
|
createdAt: String(row.created_at ?? ""),
|
|
20666
20571
|
operation: String(row.operation ?? ""),
|
|
20667
|
-
credits: row.amount_mc /
|
|
20572
|
+
credits: row.amount_mc / MC_PER_CREDIT,
|
|
20668
20573
|
description: row.description ?? null
|
|
20669
20574
|
})),
|
|
20670
20575
|
concurrency: concurrencyRaw && upgradeRaw ? {
|
|
@@ -21463,6 +21368,7 @@ var init_mcp_response_formatter = __esm({
|
|
|
21463
21368
|
init_seo_issues();
|
|
21464
21369
|
init_image_audit();
|
|
21465
21370
|
init_report_artifact_offload();
|
|
21371
|
+
init_rates();
|
|
21466
21372
|
INLINE_BUDGET_BYTES = Number(process.env.MCP_SCRAPER_INLINE_BUDGET ?? 5e4);
|
|
21467
21373
|
STRUCTURED_ARRAY_CAP = 25;
|
|
21468
21374
|
reportSavingEnabled = true;
|
|
@@ -27297,7 +27203,7 @@ var init_serp_intelligence_routes = __esm({
|
|
|
27297
27203
|
kernelApiKey: browserServiceApiKey(),
|
|
27298
27204
|
kernelProxyId: browserServiceProxyId(),
|
|
27299
27205
|
signal: c.req.raw.signal,
|
|
27300
|
-
billing: { creditsUsed: cost /
|
|
27206
|
+
billing: { creditsUsed: cost / MC_PER_CREDIT }
|
|
27301
27207
|
});
|
|
27302
27208
|
await logRequestEvent({
|
|
27303
27209
|
userId: user.id,
|
|
@@ -27399,7 +27305,7 @@ var PACKAGE_VERSION;
|
|
|
27399
27305
|
var init_version = __esm({
|
|
27400
27306
|
"src/version.ts"() {
|
|
27401
27307
|
"use strict";
|
|
27402
|
-
PACKAGE_VERSION = "0.
|
|
27308
|
+
PACKAGE_VERSION = "0.5.0";
|
|
27403
27309
|
}
|
|
27404
27310
|
});
|
|
27405
27311
|
|
|
@@ -38839,7 +38745,7 @@ var init_server = __esm({
|
|
|
38839
38745
|
api_key: user.key_active ? user.api_key : null,
|
|
38840
38746
|
created_at: user.created_at,
|
|
38841
38747
|
balance_mc: user.balance_mc,
|
|
38842
|
-
balance_credits: user.balance_mc /
|
|
38748
|
+
balance_credits: user.balance_mc / MC_PER_CREDIT,
|
|
38843
38749
|
extra_concurrency_slots: user.extra_concurrency_slots,
|
|
38844
38750
|
concurrency_limit: concurrencyLimitForUser(user),
|
|
38845
38751
|
has_concurrency_sub: !!user.concurrency_stripe_sub_id,
|
|
@@ -39951,7 +39857,7 @@ var init_server = __esm({
|
|
|
39951
39857
|
tier: tier.tier,
|
|
39952
39858
|
label: tier.label,
|
|
39953
39859
|
monthly_usd: tier.monthly_usd,
|
|
39954
|
-
credits_per_month: tier.credits_mc /
|
|
39860
|
+
credits_per_month: tier.credits_mc / MC_PER_CREDIT,
|
|
39955
39861
|
concurrency: tier.concurrency,
|
|
39956
39862
|
intro: tier.intro_coupon ? "$1 first month" : null,
|
|
39957
39863
|
next_step: "Open checkout_url in a browser to complete payment."
|
|
@@ -39981,7 +39887,7 @@ var init_server = __esm({
|
|
|
39981
39887
|
const freeCredits = await getFreeCreditBreakdown(user.id);
|
|
39982
39888
|
return c.json({
|
|
39983
39889
|
balance_mc: balanceMc,
|
|
39984
|
-
balance_credits: balanceMc /
|
|
39890
|
+
balance_credits: balanceMc / MC_PER_CREDIT,
|
|
39985
39891
|
free_credits: freeCredits,
|
|
39986
39892
|
ledger
|
|
39987
39893
|
});
|
|
@@ -40003,7 +39909,7 @@ var init_server = __esm({
|
|
|
40003
39909
|
})) : void 0;
|
|
40004
39910
|
return c.json({
|
|
40005
39911
|
balance_mc: balanceMc,
|
|
40006
|
-
balance_credits: balanceMc /
|
|
39912
|
+
balance_credits: balanceMc / MC_PER_CREDIT,
|
|
40007
39913
|
item: body.item ?? null,
|
|
40008
39914
|
matched_cost: matchedCost ? (({ aliases, ...cost }) => cost)(matchedCost) : null,
|
|
40009
39915
|
costs,
|