{"slug": "how-to-build-a-voice-over-tool-for-youtube-videos", "title": "How to Build a Voice-Over Tool for YouTube Videos", "summary": "A developer published a walkthrough for building a lightweight voice-over tool that uses the ElevenLabs text-to-speech API to convert a script into narration audio and a synced SRT subtitle file. The Python script sends the script to a cloned voice ID with adjustable stability and similarity settings, then parses returned timestamps to generate subtitles, with a Node.js snippet for uploading video and captions via the YouTube Data API. The post also includes an affiliate link promoting ElevenLabs' free trial quota.", "body_md": "If you’ve ever tried to produce a YouTube video, you’ll know that the most time‑consuming part isn’t shooting footage or writing the script—it’s the audio. Recording a human voice, cleaning it up, and syncing it to the video can eat up hours of a creator’s day. That’s where text‑to‑speech (TTS) and voice‑cloning come in: they let you generate high‑quality narration with a few lines of code.\n\nIn this post I’ll walk you through building a lightweight voice‑over tool that takes a script, turns it into a natural‑sounding voice, and even produces a subtitle file that you can drop straight into your YouTube upload. We’ll use **ElevenLabs** as our TTS engine because it offers state‑of‑the‑art neural voices and an easy‑to‑use API.\n\n**Pro tip:** If you’re new to ElevenLabs or want a free trial, use this affiliate link: [https://try.elevenlabs.io/kr07zfuqn1bp](https://try.elevenlabs.io/kr07zfuqn1bp). It unlocks a generous quota of free synthesis hours.\n\nElevenLabs gives you access to a catalog of voices. You can:\n\nWhen you clone a voice, you’ll get a unique *voice ID* that you’ll reference later. Make sure the voice you choose matches the tone of your channel (e.g., friendly, professional, energetic).\n\nBelow is a minimal Python script that sends a script to ElevenLabs, receives back an MP3 file, and writes a subtitle file in SRT format. Feel free to adapt it to your preferred language.\n\n``` python\nimport requests\nimport json\nimport os\n\nELEVEN_API_URL = \"https://api.elevenlabs.io/v1/text-to-speech\"\nAPI_KEY = \"YOUR_ELEVEN_API_KEY\"\nVOICE_ID = \"YOUR_CLONED_VOICE_ID\"\n\n# 1️⃣ Read the script\nwith open(\"script.txt\", \"r\", encoding=\"utf-8\") as f:\n    script = f.read()\n\n# 2️⃣ Build the request payload\npayload = {\n    \"text\": script,\n    \"voice_settings\": {\n        \"stability\": 0.75,\n        \"similarity_boost\": 0.8\n    }\n}\n\nheaders = {\n    \"xi-api-key\": API_KEY,\n    \"Content-Type\": \"application/json\"\n}\n\n# 3️⃣ Make the request\nresponse = requests.post(\n    f\"{ELEVEN_API_URL}/{VOICE_ID}\",\n    headers=headers,\n    data=json.dumps(payload)\n)\n\n# 4️⃣ Save the MP3\nif response.status_code == 200:\n    with open(\"narration.mp3\", \"wb\") as out:\n        out.write(response.content)\n    print(\"✅ Audio generated!\")\nelse:\n    print(f\"❌ Error {response.status_code}: {response.text}\")\n```\n\n`stability`) and how closely the voice mimics the source (` similarity_boost`).\nYouTube accepts SRT files, and syncing subtitles is a breeze once you have timestamps. ElevenLabs can return timestamps if you set `output_format` to `json`. Let’s tweak our script to capture those timestamps:\n\n```\npayload[\"output_format\"] = \"json\"\n\nresponse = requests.post(\n    f\"{ELEVEN_API_URL}/{VOICE_ID}\",\n    headers=headers,\n    data=json.dumps(payload)\n)\n\n# Parse timestamps\nif response.status_code == 200:\n    data = response.json()\n    audio_bytes = requests.get(data[\"audio_url\"]).content\n    with open(\"narration.mp3\", \"wb\") as out:\n        out.write(audio_bytes)\n\n    # Build SRT\n    srt_lines = []\n    for i, segment in enumerate(data[\"segments\"]):\n        start = segment[\"start\"]\n        end = segment[\"end\"]\n        text = segment[\"text\"]\n        srt_lines.extend([f\"{i+1}\", f\"{format_time(start)} --> {format_time(end)}\", text, \"\"])\n\n    with open(\"subtitles.srt\", \"w\", encoding=\"utf-8\") as srt:\n        srt.write(\"\\n\".join(srt_lines))\n\n    print(\"✅ Audio + subtitles ready!\")\nelse:\n    print(f\"❌ Error {response.status_code}: {response.text}\")\n\ndef format_time(seconds: float) -> str:\n    \"\"\"Convert seconds to SRT timestamp format.\"\"\"\n    m, s = divmod(seconds, 60)\n    h, m = divmod(m, 60)\n    ms = int((s - int(s)) * 1000)\n    return f\"{int(h):02}:{int(m):02}:{int(s):02},{ms:03}\"\n```\n\n**Result:** \n\n`narration.mp3` – the spoken audio.\n`subtitles.srt` – a perfectly synced subtitle file.\nWhile you can still use the YouTube web UI, the YouTube Data API lets you push the video, audio, and subtitles in one go. Below is a quick Node.js snippet that uploads a video and attaches the SRT file:\n\n``` js\nconst fs = require('fs');\nconst { google } = require('googleapis');\nconst youtube = google.youtube('v3');\n\n// 1️⃣ Authenticate (OAuth2 flow required)\nconst auth = new google.auth.OAuth2(\n  'YOUR_CLIENT_ID',\n  'YOUR_CLIENT_SECRET',\n  'YOUR_REDIRECT_URI'\n);\n// Assume you have a valid refresh token\nauth.setCredentials({ refresh_token: 'YOUR_REFRESH_TOKEN' });\n\nasync function uploadVideo() {\n  const res = await youtube.videos.insert({\n    auth,\n    part: 'snippet,status',\n    requestBody: {\n      snippet: {\n        title: 'My AI‑Generated Voice‑Over',\n        description: 'Generated with ElevenLabs TTS',\n        tags: ['AI', 'TTS', 'ElevenLabs'],\n        categoryId: '22' // People & Blogs\n      },\n      status: { privacyStatus: 'public' }\n    },\n    media: {\n      body: fs.createReadStream('narration.mp3')\n    }\n  });\n\n  const videoId = res.data.id;\n\n  // Upload subtitles\n  await youtube.captions.insert({\n    auth,\n    part: 'snippet',\n    requestBody: {\n      snippet: {\n        videoId,\n        language: 'en',\n        name: 'English subtitles',\n        isDraft: false\n      }\n    },\n    media: {\n      body: fs.createReadStream('subtitles.srt')\n    }\n  });\n\n  console.log(`✅ Video uploaded: https://youtu.be/${videoId}`);\n}\n\nuploadVideo().catch(console.error);\n```\n\n**Heads‑up:** Setting up OAuth2 is a bit more involved. Check the Google API docs for a full walkthrough, but once you have the refresh token, the rest is just a few lines of code.\n\n| Task | Tool | Why it matters | \n|---|---|---|\n| Voice selection | ElevenLabs | Natural‑sounding, customizable | \n| Text formatting | Markdown or HTML | Keeps scripts readable | \n| Subtitle sync | ElevenLabs JSON output | Accurate timings | \n| Video upload | YouTube Data API | Fully automated pipeline | \n\nIf you find yourself generating a lot of videos, consider wrapping this entire flow in a Docker container or a serverless function. That way you can trigger a new voice‑over job from a webhook whenever a new script lands in a GitHub repo.\n\n| Issue | Fix | \n|---|---|\n| **Audio too short / missing words** | Use `output_format=json` to get timestamps; double‑check the script length. | \n| **Voice sounding robotic** | Increase `stability` and`similarity_boost` in the payload. | \n| **API rate limits** | Cache the generated MP3s; batch requests if you have many scripts. | \n| **Subtitle mis‑alignment** | Verify timestamps with a tool like `ffmpeg -i narration.mp3 -f ffmetadata` . | \n\nYou now have a fully automated pipeline:\n\nYou can extend this by adding background music, dynamic pacing, or even generating multiple language tracks.\n\nIf you haven’t tried ElevenLabs yet, jump on the affiliate link above: [https://try.elevenlabs.io/kr07zfuqn1bp](https://try.elevenlabs.io/kr07zfuqn1bp). It gives you free credits and a smooth onboarding process, so you can focus on creating content, not on the tech stack. Happy coding—and happy talking!", "url": "https://wpnews.pro/news/how-to-build-a-voice-over-tool-for-youtube-videos", "canonical_source": "https://dev.to/voice_developer/how-to-build-a-voice-over-tool-for-youtube-videos-1fjg", "published_at": "2026-10-27 22:56:55+00:00", "updated_at": "2026-10-27 23:11:53.431780+00:00", "lang": "en", "topics": ["ai-tools", "generative-ai", "natural-language-processing"], "entities": ["ElevenLabs", "YouTube", "Python", "Node.js"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/how-to-build-a-voice-over-tool-for-youtube-videos", "markdown": "https://wpnews.pro/news/how-to-build-a-voice-over-tool-for-youtube-videos.md", "text": "https://wpnews.pro/news/how-to-build-a-voice-over-tool-for-youtube-videos.txt", "jsonld": "https://wpnews.pro/news/how-to-build-a-voice-over-tool-for-youtube-videos.jsonld"}}