{ "kind": "open-flow-workflow", "version": 1, "name": "Extract a YouTube Video Transcript", "content": { "modelVersion": 14, "document": { "bindings": {}, "graph": { "nodes": { "n_e5cbc52b8858d9f4": { "name": "Receive a video", "kind": "webhook", "bodyFields": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "video" } ], "method": "POST" }, "n_7ca83d883b9981d9": { "inputs": { "body": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_e5cbc52b8858d9f4", "output": "body" } ] } }, "name": "Read the video ID", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "object", "additionalProperties": true }, "nullable": false, "handle": "body" }, { "jsonSchema": { "type": "string" }, "nullable": true, "handle": "video" } ], "outputs": [ { "jsonSchema": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "YouTube video URL (watch, youtu.be, Shorts or live) or the 11-character video ID." }, "nullable": false, "handle": "videoId" } ], "name": "Read the video ID", "moduleId": "n_002e4447dd8ee32a", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_f7842d4f8a69c875": { "inputs": { "video": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_7ca83d883b9981d9", "output": "videoId" } ] }, "timestamps": { "kind": "value", "value": false } }, "name": "Fetch the transcript", "kind": "task", "task": { "inputs": [ { "description": "YouTube video URL (watch, youtu.be, Shorts or live) or the 11-character video ID.", "jsonSchema": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "YouTube video URL (watch, youtu.be, Shorts or live) or the 11-character video ID." }, "nullable": false, "handle": "video" }, { "description": "Caption language code, for example en or es.", "jsonSchema": { "type": "string", "minLength": 1, "pattern": "\\S", "description": "Caption language code, for example en or es." }, "nullable": true, "handle": "language", "value": null }, { "description": "Whether to also return one segment per caption line with start and duration.", "jsonSchema": { "type": "boolean", "description": "Whether to also return one segment per caption line with start and duration." }, "nullable": true, "handle": "timestamps", "value": null } ], "outputs": [ { "description": "The 11-character YouTube video ID.", "jsonSchema": { "type": "string", "description": "The 11-character YouTube video ID." }, "nullable": false, "handle": "video_id" }, { "description": "Language code of the returned captions.", "jsonSchema": { "type": "string", "description": "Language code of the returned captions." }, "nullable": false, "handle": "language_code" }, { "description": "Video title.", "jsonSchema": { "type": "string", "description": "Video title." }, "nullable": false, "handle": "title" }, { "description": "Channel name.", "jsonSchema": { "type": "string", "description": "Channel name." }, "nullable": false, "handle": "author_name" }, { "description": "Channel URL.", "jsonSchema": { "type": "string", "description": "Channel URL." }, "nullable": false, "handle": "author_url" }, { "description": "Video thumbnail URL.", "jsonSchema": { "type": "string", "description": "Video thumbnail URL." }, "nullable": false, "handle": "thumbnail_url" }, { "description": "The full transcript as plain text.", "jsonSchema": { "type": "string", "description": "The full transcript as plain text." }, "nullable": false, "handle": "transcript" }, { "description": "Number of words in the transcript.", "jsonSchema": { "type": "number", "description": "Number of words in the transcript." }, "nullable": false, "handle": "word_count" }, { "description": "One entry per caption line. Present only when timestamps is true.", "jsonSchema": { "type": "array", "items": { "type": "object", "properties": { "start": { "type": "number", "description": "Start time in seconds." }, "duration": { "type": "number", "description": "Duration in seconds." }, "text": { "type": "string", "description": "Caption text." } }, "required": [ "start", "duration", "text" ], "additionalProperties": false, "description": "One caption line." }, "description": "One entry per caption line. Present only when timestamps is true." }, "nullable": true, "handle": "segments" } ], "name": "Fetch the transcript", "executor": { "kind": "connector", "action": "getyoutubetranscript.get_youtube_transcript" } } }, "n_338582b669cca886": { "inputs": { "requestedId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_7ca83d883b9981d9", "output": "videoId" } ] }, "videoId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f7842d4f8a69c875", "output": "video_id" } ] }, "transcript": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_f7842d4f8a69c875", "output": "transcript" } ] } }, "name": "Clean the transcript", "kind": "task", "task": { "inputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "requestedId" }, { "jsonSchema": { "type": "string", "description": "The 11-character YouTube video ID." }, "nullable": false, "handle": "videoId" }, { "jsonSchema": { "type": "string", "description": "The full transcript as plain text." }, "nullable": false, "handle": "transcript" } ], "outputs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "videoId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "transcript" } ], "name": "Clean the transcript", "moduleId": "n_55c26beaadf11fbe", "capabilities": [ { "kind": "connector", "mode": "independent", "actions": [] } ] } }, "n_e3a3cf6d0243a38e": { "name": "Transcript", "kind": "outputs", "outputDefs": [ { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "videoId" }, { "jsonSchema": { "type": "string" }, "nullable": false, "handle": "transcript" } ], "outputMappings": { "videoId": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_338582b669cca886", "output": "videoId" } ] }, "transcript": { "kind": "sources", "sources": [ { "kind": "node", "nodeId": "n_338582b669cca886", "output": "transcript" } ] } } } }, "edges": [ { "source": "n_e5cbc52b8858d9f4", "target": "n_7ca83d883b9981d9" }, { "source": "n_7ca83d883b9981d9", "target": "n_f7842d4f8a69c875" }, { "source": "n_f7842d4f8a69c875", "target": "n_338582b669cca886" }, { "source": "n_338582b669cca886", "target": "n_e3a3cf6d0243a38e" } ] } }, "modules": { "n_002e4447dd8ee32a": { "name": "Read the video ID", "imports": [], "source": "export default function main({video,body}) {\n video=video??body?.video;\n if(typeof video!=='string')throw Error('Provide a YouTube URL');\n const v=video.trim();let id;\n if(/^[A-Za-z0-9_-]{11}$/.test(v))id=v;\n else {let m=v.match(/^https?:\\/\\/(?:www\\.|m\\.)?youtube\\.com\\/watch\\?([^#]*)/i);if(m)id=m[1].split('&').find(x=>x.startsWith('v='))?.slice(2);else id=v.match(/^https?:\\/\\/(?:www\\.|m\\.)?youtube\\.com\\/(?:shorts|embed|live)\\/([A-Za-z0-9_-]{11})(?:[?#/]|$)/i)?.[1]??v.match(/^https?:\\/\\/(?:www\\.)?youtu\\.be\\/([A-Za-z0-9_-]{11})(?:[?#/]|$)/i)?.[1];}\n if(!/^[A-Za-z0-9_-]{11}$/.test(id??''))throw Error('Provide a supported YouTube video URL or ID');\n return {videoId:id};\n}\n" }, "n_55c26beaadf11fbe": { "name": "Clean the transcript", "imports": [], "source": "export default function main({requestedId,videoId,transcript}) {\n if(videoId!==requestedId)throw Error('Transcript belongs to another video');\n if(typeof transcript!=='string'||!transcript.trim())throw Error('No transcript available');\n return {videoId,transcript:transcript.replace(/\\s+/g,' ').replace(/\\s([.,!?])/g,'$1').trim()};\n}\n" } } }, "presentation": { "designer": { "version": 1, "flow": { "direction": "vertical" } } } }