Skip to content

Commit d5f5a61

Browse files
committed
fix(transcripts): fetch visitorData token to satisfy YouTube client validation in Workers
1 parent 6b0d646 commit d5f5a61

1 file changed

Lines changed: 29 additions & 4 deletions

File tree

‎apps/site/src/pages/api/transcript.ts‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,42 @@ async function fetchYouTubeCaptions(videoId: string): Promise<{
4444
const INNERTUBE_CLIENT_VERSION = "20.10.38";
4545

4646
try {
47+
// Acquire fresh visitorData token to bypass LOGIN_REQUIRED in Cloudflare datacenters
48+
let visitorData: string | undefined;
49+
try {
50+
const pageResp = await fetch(`https://www.youtube.com/watch?v=${videoId}`, {
51+
headers: {
52+
"User-Agent":
53+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
54+
"Accept-Language": "en-US,en;q=0.9",
55+
},
56+
});
57+
if (pageResp.ok) {
58+
const html = await pageResp.text();
59+
const match = html.match(/"visitorData":"([^"]+)"/);
60+
if (match) {
61+
visitorData = match[1];
62+
}
63+
}
64+
} catch {}
65+
66+
const requestHeaders: Record<string, string> = {
67+
"Content-Type": "application/json",
68+
"User-Agent": `com.google.android.youtube/${INNERTUBE_CLIENT_VERSION} (Linux; U; Android 14)`,
69+
};
70+
if (visitorData) {
71+
requestHeaders["X-Goog-Visitor-Id"] = visitorData;
72+
}
73+
4774
const response = await fetch(INNERTUBE_API_URL, {
4875
method: "POST",
49-
headers: {
50-
"Content-Type": "application/json",
51-
"User-Agent": `com.google.android.youtube/${INNERTUBE_CLIENT_VERSION} (Linux; U; Android 14)`,
52-
},
76+
headers: requestHeaders,
5377
body: JSON.stringify({
5478
context: {
5579
client: {
5680
clientName: "ANDROID",
5781
clientVersion: INNERTUBE_CLIENT_VERSION,
82+
...(visitorData ? { visitorData } : {}),
5883
},
5984
},
6085
videoId,

0 commit comments

Comments
 (0)