Skip to content

Instantly share code, notes, and snippets.

@hippietrail
Last active March 17, 2024 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hippietrail/af26d363d7f6ac37565c5074f04e4f5e to your computer and use it in GitHub Desktop.
Save hippietrail/af26d363d7f6ac37565c5074f04e4f5e to your computer and use it in GitHub Desktop.
TypeScript code to fetch one or more YouTube transcripts as plain text without API key
import url from 'url';
interface Cue {
transcriptCueRenderer: {
cue: {
simpleText: string;
};
};
}
interface CueGroup {
transcriptCueGroupRenderer: {
cues: Cue[];
};
}
interface Response {
actions: {
updateEngagementPanelAction: {
content: {
transcriptRenderer: {
body: {
transcriptBodyRenderer: {
cueGroups: {
transcriptCueGroupRenderer: CueGroup;
},
},
},
},
},
};
}[];
}
async function getTranscriptJsonByVideoID(videoID: string): Promise<Response> {
const response = await fetch(url.format({
protocol: 'https',
hostname: 'www.youtube.com',
pathname: 'youtubei/v1/get_transcript',
query: { key: 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8' },
}), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
context: { client: { clientName: 'WEB', clientVersion: '2.9999099' } },
params: Buffer.from(`\n\x0b${videoID}`).toString('base64'),
}),
});
return (await response.json()) as Response;
}
async function main() {
const videoIDs = process.argv.slice(2);
const promiseArray = videoIDs.map(getTranscriptJsonByVideoID);
const settledPromises = await Promise.allSettled(promiseArray);
for (const settled of settledPromises) {
if (settled.status === 'fulfilled' && 'actions' in settled.value) {
const { actions } = settled.value;
const transcriptCues = actions
.find(({ updateEngagementPanelAction }) => updateEngagementPanelAction)!
.updateEngagementPanelAction.content.transcriptRenderer.body.transcriptBodyRenderer.cueGroups;
const cueGroupsArray = Object.values(transcriptCues);
const flattenedCues = cueGroupsArray.flatMap((cueGroup: CueGroup) => cueGroup.transcriptCueGroupRenderer.cues);
console.log(
flattenedCues.map((cue: Cue) => cue.transcriptCueRenderer.cue.simpleText).join('\n')
);
} else {
if (settled.status === 'rejected') {
console.error('rejected', settled.reason);
} else if (!('actions' in settled.value)) {
console.error('no actions');
}
}
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment