Last active
March 17, 2024 18:13
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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