<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: CHANDAN</title>
    <description>The latest articles on DEV Community by CHANDAN (@chandan0000001).</description>
    <link>https://dev.to/chandan0000001</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4219729%2F13c1b4b7-211c-48a4-875c-8a755b011165.gif</url>
      <title>DEV Community: CHANDAN</title>
      <link>https://dev.to/chandan0000001</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chandan0000001"/>
    <language>en</language>
    <item>
      <title>Part 2: I Broke Out of YouTube My Bot Now Hits Instagram &amp; Twitter/X Too (3 Platforms, 3 Completely Different Protection Models)</title>
      <dc:creator>CHANDAN</dc:creator>
      <pubDate>Mon, 02 Nov 2026 06:20:04 +0000</pubDate>
      <link>https://dev.to/chandan0000001/part-2-i-broke-out-of-youtube-my-bot-now-hits-instagram-twitterx-too-3-platforms-3-23e9</link>
      <guid>https://dev.to/chandan0000001/part-2-i-broke-out-of-youtube-my-bot-now-hits-instagram-twitterx-too-3-platforms-3-23e9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Part 1 is here — read it first:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i"&gt;How I Built a Telegram Bot That Downloads YouTube Music &amp;amp; Videos (Up to 2 GB)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🤖 &lt;strong&gt;Try the live bot:&lt;/strong&gt; (&lt;a href="https://t.me/songsuperzxbot" rel="noopener noreferrer"&gt;https://t.me/songsuperzxbot&lt;/a&gt;)&lt;br&gt;
📦 &lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/chandan0000001/music-video_telegrambot" rel="noopener noreferrer"&gt;github.com/chandan0000001/music-video_telegrambot&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Someone sent me a message after Part 1 went live.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Can it do Instagram Reels?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Short answer at the time: no.&lt;/p&gt;

&lt;p&gt;Slightly longer answer after a few weeks of work: yes, and also Twitter/X, and also Instagram Stories, profile pictures, highlights, and posts. But getting there was a completely different problem from YouTube — and I mean &lt;em&gt;completely&lt;/em&gt; different. Three platforms. Three protection models. Three sets of rules about what you can fetch without authentication, what you can't, and what will quietly rate-limit you into oblivion if you're not careful.&lt;/p&gt;

&lt;p&gt;This is the story of how I expanded the bot, what I learned about how Instagram and Twitter/X actually protect their content, and the architectural changes that come with supporting multiple platforms in one bot.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Bot Already Does (Quick Recap)
&lt;/h2&gt;

&lt;p&gt;Before I get into the new stuff — if you haven't read Part 1, the short version:&lt;/p&gt;

&lt;p&gt;The bot is a production-grade Telegram bot built with &lt;strong&gt;Python 3.11, aiogram 3, FFmpeg, and SQLite WAL mode&lt;/strong&gt;. It searches YouTube, lets users pick from the top 5 results, and downloads audio in MP3/M4A/WAV up to 320kbps or video in MP4 up to 1080p. The clever parts are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;yt-dlp's Signature Cipher bypass&lt;/strong&gt; — reverse-engineers YouTube's JS player to reconstruct the real stream URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart 2 GB delivery&lt;/strong&gt; — files under 48 MB go via HTTP Bot API instantly; larger files use native MTProto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intel VAAPI hardware acceleration&lt;/strong&gt; — FFmpeg transcodes using the GPU render node, ~10x faster than CPU&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full admin panel&lt;/strong&gt; — stats, ban/unblock, broadcast, CSV export, live queue view&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;30 automated tests&lt;/strong&gt; — URL validation, FFmpeg pipeline, rate limiting, cancellation, database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That foundation is what Part 2 is built on. Now let's talk about why adding Instagram and Twitter/X was harder than just "swap yt-dlp's input URL."&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Three Platforms is an Architecture Problem, Not Just a Feature
&lt;/h2&gt;

&lt;p&gt;When I built the YouTube handler, it was a one-platform world. The whole flow assumed: user sends query or YouTube URL → validate → download → deliver.&lt;/p&gt;

&lt;p&gt;The moment you add Instagram and Twitter/X, that assumption breaks. You now need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A URL router&lt;/strong&gt; — detect which platform a URL belongs to before doing anything else&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform-specific authentication&lt;/strong&gt; — Instagram requires a session cookie; YouTube needs cookies for age-restricted content; Twitter/X can sometimes do without&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform-specific content types&lt;/strong&gt; — YouTube has audio or video. Instagram has reels, stories, highlights, posts, &lt;em&gt;and&lt;/em&gt; profile pictures. Twitter/X has videos, images, and posts with mixed media&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform-specific rate limiting&lt;/strong&gt; — YouTube is relatively forgiving. Instagram will silently downgrade your requests or temporarily block your IP if you hammer it. Twitter/X has changed its protection model more aggressively than any other platform in recent years&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's the new routing layer that runs before any download starts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;YOUTUBE&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;youtube&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;INSTAGRAM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instagram&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;TWITTER&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;twitter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;UNKNOWN&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;PLATFORM_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YOUTUBE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^https?://(www\.)?(youtube\.com/watch\?v=|youtu\.be/|youtube\.com/shorts/)[A-Za-z0-9_\-]{11}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INSTAGRAM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^https?://(www\.)?instagram\.com/(p|reel|stories|tv|s)/[A-Za-z0-9_\-]+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TWITTER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^https?://(www\.)?(twitter\.com|x\.com)/\w+/status/\d+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;detect_platform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# SSRF guard — always first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https?://(localhost|127\.|10\.|192\.168\.|169\.254\.)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;PLATFORM_PATTERNS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SSRF check runs before platform detection. An attacker sending &lt;code&gt;http://192.168.1.1/admin&lt;/code&gt; disguised as any platform URL gets rejected at this layer, not inside any platform-specific handler.&lt;/p&gt;




&lt;h2&gt;
  
  
  Platform 1: YouTube (The One We Already Solved)
&lt;/h2&gt;

&lt;p&gt;Covered in full in Part 1. The short summary of how YouTube protects streams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Signature Cipher&lt;/strong&gt; — stream URLs are scrambled via a JavaScript function that yt-dlp reverse-engineers from YouTube's player JS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;n-parameter throttle&lt;/strong&gt; — download speed is capped at ~50 KB/s unless you decrypt an "n" parameter from the same JS bundle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;po_token&lt;/strong&gt; — a newer proof-of-origin token embedded in browser sessions, requiring a valid &lt;code&gt;cookies.txt&lt;/code&gt; to bypass&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;yt-dlp handles all three. Keep it updated and refresh &lt;code&gt;cookies.txt&lt;/code&gt; every few weeks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Platform 2: Instagram — Sessions, Stories, and Aggressive Rate Limiting
&lt;/h2&gt;

&lt;p&gt;Instagram's protection model is fundamentally different from YouTube's. YouTube obfuscates stream URLs but serves them publicly. Instagram ties almost everything to authenticated sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's public vs what needs auth
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;No authentication needed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public post images and videos&lt;/li&gt;
&lt;li&gt;Public Reels&lt;/li&gt;
&lt;li&gt;Profile pictures (always public — served directly from Instagram's CDN)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Requires a valid Instagram session:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stories (expire in 24 hours; private to logged-in users)&lt;/li&gt;
&lt;li&gt;Highlights (archived stories; account-level access)&lt;/li&gt;
&lt;li&gt;Content from private accounts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How Instagram serves content
&lt;/h3&gt;

&lt;p&gt;When you watch a Reel in your browser, the actual video file is served from Instagram's CDN — URLs that look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://scontent-iad3-1.cdninstagram.com/v/t50.2886-16/...?_nc_ht=scontent...&amp;amp;_nc_cat=...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These CDN URLs are time-limited and session-tied. They're embedded inside a JSON blob in the page's JavaScript bundle or fetched via an internal GraphQL endpoint. yt-dlp's Instagram extractor reverse-engineers these endpoints to pull the direct CDN URL.&lt;/p&gt;

&lt;p&gt;For authenticated content (stories, highlights), the extractor needs a valid &lt;code&gt;sessionid&lt;/code&gt; cookie — the same session token your browser uses when you're logged into Instagram.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Instagram in the bot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_instagram_opts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;base_opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;outtmpl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;output_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quiet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cookiefile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;instagram_cookies.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# must contain valid sessionid
&lt;/span&gt;        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http_headers&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;User-Agent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;video&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;base_opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;story&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;base_opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;best&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;  &lt;span class="c1"&gt;# stories are single-stream
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile_picture&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;base_opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;best&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base_opts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The rate limiting trap
&lt;/h3&gt;

&lt;p&gt;Instagram rate-limits by IP and by session. If you download too many items in quick succession using the same session cookie, Instagram starts returning &lt;code&gt;401 Unauthorized&lt;/code&gt; errors — even for public content — or silently serves lower-quality versions.&lt;/p&gt;

&lt;p&gt;My fix: per-platform cooldown timers. Instagram downloads enforce a 15-second minimum gap between requests from the same session. This is longer than the YouTube cooldown (10 seconds) specifically because Instagram's rate limiting is less forgiving and harder to detect when it's happening.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;PLATFORM_COOLDOWNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;YOUTUBE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# seconds between requests
&lt;/span&gt;    &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INSTAGRAM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# Instagram rate-limits harder
&lt;/span&gt;    &lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TWITTER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Profile pictures — the easy one
&lt;/h3&gt;

&lt;p&gt;Profile pictures are the simplest Instagram download. They're always public, always served directly from the CDN, and don't require authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://instagram.com/{username}/?__a=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The JSON response contains &lt;code&gt;graphql.user.profile_pic_url_hd&lt;/code&gt; — a direct image URL. No cipher, no session required, just a clean HTTP GET.&lt;/p&gt;




&lt;h2&gt;
  
  
  Platform 3: Twitter/X — Bearer Tokens and a Platform That Keeps Moving
&lt;/h2&gt;

&lt;p&gt;Twitter's 2023 pivot to X — and the dramatic changes to API access that came with it — made this the most technically interesting platform to support.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Twitter/X protects video content
&lt;/h3&gt;

&lt;p&gt;Twitter/X video is delivered as &lt;strong&gt;HLS streams&lt;/strong&gt; (HTTP Live Streaming). When you watch a video in a tweet, the player fetches an M3U8 playlist that lists the actual video segments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=2176000,RESOLUTION=1280x720
https://video.twimg.com/ext_tw_video/.../720x1280/...m3u8
#EXT-X-STREAM-INF:BANDWIDTH=832000,RESOLUTION=640x360
https://video.twimg.com/ext_tw_video/.../360x640/...m3u8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The M3U8 playlist URL itself is embedded in the tweet's card data, which is fetched from an internal API endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.twitter.com/1.1/statuses/show.json?id=&amp;lt;tweet_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This endpoint requires a &lt;strong&gt;Bearer token&lt;/strong&gt; — an authentication token that Twitter's own web client sends with every request. yt-dlp extracts this bearer token from Twitter/X's JavaScript bundle (the same approach it uses for YouTube's cipher — find the token in the JS, use it for requests).&lt;/p&gt;

&lt;h3&gt;
  
  
  What happened in 2023
&lt;/h3&gt;

&lt;p&gt;When Elon Musk took over Twitter and rebranded it to X, free API tier access was almost entirely removed. Third-party apps that relied on Twitter's public API were killed. Nitter (the privacy-friendly Twitter frontend) started failing. Many download tools broke.&lt;/p&gt;

&lt;p&gt;yt-dlp adapted by switching to the "guest API" — an unauthenticated path that Twitter/X still exposes for public content, using a guest token that gets issued when you visit the site without being logged in. This guest token approach still works for public tweets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling tweets with multiple images
&lt;/h3&gt;

&lt;p&gt;A tweet can have 1-4 images. When a user sends a tweet URL with multiple images, the bot downloads all of them and sends them as a Telegram media group:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_twitter_images&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;yt_dlp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;YoutubeDL&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quiet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;skip_download&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ydl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ydl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extract_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;entries&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Multiple images — send as album
&lt;/span&gt;        &lt;span class="n"&gt;media_group&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;entries&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;img_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;img_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;media_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InputMediaPhoto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;img_url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;media_group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;answer_media_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;media_group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Single image or video
&lt;/span&gt;        &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Telegram supports sending up to 10 items in a media group, which covers Twitter/X's 4-image maximum comfortably.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Content-Type Detection System
&lt;/h2&gt;

&lt;p&gt;Instagram URLs carry information about content type in the path:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;URL pattern&lt;/th&gt;
&lt;th&gt;Content type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram.com/p/{id}/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Post (image or video)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram.com/reel/{id}/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reel (short video)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram.com/stories/{user}/{id}/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Story&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram.com/s/{highlight_id}/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Highlight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram.com/{username}/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Profile (→ profile picture)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram.com/tv/{id}/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;IGTV (long video)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;detect_instagram_content_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/reel/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;reel&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/stories/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;story&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/p/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;post&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/s/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;highlight&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/tv/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;video&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="c1"&gt;# If it's just instagram.com/{username}/ — profile picture
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https?://(www\.)?instagram\.com/[^/]+/?$&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile_picture&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bot surfaces a different keyboard to the user depending on content type. A Reel shows "Download Video" and "Download Audio". A Story just shows "Download". A profile URL shows "Download Profile Picture". No unnecessary choices for things that have only one format.&lt;/p&gt;




&lt;h2&gt;
  
  
  Updated Supported Platforms
&lt;/h2&gt;

&lt;p&gt;Here's where the bot stands now:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Content Types&lt;/th&gt;
&lt;th&gt;Auth Required&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;YouTube&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Music search, Audio (MP3/M4A/WAV), Video (144p–1080p)&lt;/td&gt;
&lt;td&gt;cookies.txt (age-restricted only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Instagram&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reel, Video, Post, Story, Highlight, Profile Picture&lt;/td&gt;
&lt;td&gt;sessionid cookie (stories/highlights)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Twitter / X&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Video, Post images (up to 4), GIF&lt;/td&gt;
&lt;td&gt;None (public tweets)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All three platforms route through the same pipeline:&lt;br&gt;
URL → platform detection → SSRF guard → content type detection → platform-specific yt-dlp config → FFmpeg → smart delivery (HTTP or MTProto) → cleanup.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned Building This
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Instagram is the hardest platform to stay compatible with.&lt;/strong&gt; YouTube breaks when they rotate the cipher — that's a known, predictable event that yt-dlp patches. Instagram breaks when they change their internal GraphQL schema, which happens without announcement and with no public documentation to reference. Two of my three "bot is down" incidents since adding Instagram support were Instagram schema changes, not YouTube.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twitter/X's guest API is fragile.&lt;/strong&gt; It works today. X has shown zero interest in maintaining compatibility with third-party tools, and the guest API could be deprecated at any point. I handle this by catching yt-dlp errors and returning a clear message to users — "this video may not be downloadable" — instead of silently failing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-platform rate limiting is non-negotiable.&lt;/strong&gt; A single rate limiter across all platforms was my first approach. It was wrong. Instagram needs longer cooldowns than YouTube. Twitter/X is somewhere in the middle. Platform-specific cooldown values aren't premature optimization — they're the difference between a bot that works reliably and one that gets session-banned after 20 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;yt-dlp is genuinely impressive.&lt;/strong&gt; One library, three completely different platform protection models, maintained by a community that ships patches sometimes within hours. The right posture when building on top of it: keep it updated, wrap all extractor calls in robust error handling, and never assume a config that works today will work in two weeks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The bot handles all three platforms now. Drop a YouTube link, an Instagram Reel, or a Twitter/X video URL and it figures out the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://t.me/songsuperzxbot" rel="noopener noreferrer"&gt;@songsuperzxbot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/chandan0000001/music-video_telegrambot" rel="noopener noreferrer"&gt;github.com/chandan0000001/music-video_telegrambot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1 (YouTube deep-dive):&lt;/strong&gt; &lt;a href="https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i"&gt;dev.to/chandan0000001/...&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Questions about Instagram's session handling, Twitter/X's HLS parsing, or anything else — drop them in the comments.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>opensource</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Updated feature Just added 
Instagram , X or twitter video Download</title>
      <dc:creator>CHANDAN</dc:creator>
      <pubDate>Mon, 02 Nov 2026 05:55:30 +0000</pubDate>
      <link>https://dev.to/chandan0000001/updated-feature-just-added-instagram-x-or-twitter-video-download-57kj</link>
      <guid>https://dev.to/chandan0000001/updated-feature-just-added-instagram-x-or-twitter-video-download-57kj</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i" class="crayons-story__hidden-navigation-link"&gt;How I Built a Telegram Bot That Downloads YouTube Music &amp;amp; Videos (Up to 2 GB) And How yt-dlp Actually Bypasses YouTube's Protection&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/chandan0000001" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4219729%2F13c1b4b7-211c-48a4-875c-8a755b011165.gif" alt="chandan0000001 profile" class="crayons-avatar__image" width="249" height="249"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/chandan0000001" class="crayons-story__secondary fw-medium m:hidden"&gt;
              CHANDAN
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                CHANDAN
                
                
              
              &lt;div id="story-author-preview-content-4955954" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/chandan0000001" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4219729%2F13c1b4b7-211c-48a4-875c-8a755b011165.gif" class="crayons-avatar__image" alt="" width="249" height="249"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;CHANDAN&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Nov 1&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i" id="article-link-4955954"&gt;
          How I Built a Telegram Bot That Downloads YouTube Music &amp;amp; Videos (Up to 2 GB) And How yt-dlp Actually Bypasses YouTube's Protection
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/python"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;python&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/telegram"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;telegram&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
          
          &lt;div class="only-sidebar-menu-item trusted-visible-block print-hidden align-center"&gt;
                
                    
                
          &lt;/div&gt;

        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>How I Built a Telegram Bot That Downloads YouTube Music &amp; Videos (Up to 2 GB) And How yt-dlp Actually Bypasses YouTube's Protection</title>
      <dc:creator>CHANDAN</dc:creator>
      <pubDate>Sun, 01 Nov 2026 05:19:08 +0000</pubDate>
      <link>https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i</link>
      <guid>https://dev.to/chandan0000001/how-i-built-a-telegram-bot-that-downloads-youtube-music-videos-up-to-2-gb-and-how-yt-dlp-431i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmjjlw4jthtgiuofm83ik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmjjlw4jthtgiuofm83ik.png" alt=" " width="382" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Live Bot: &lt;a href="https://t.me/songsuperzxbot" rel="noopener noreferrer"&gt;@songsuperzxbot&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Source Code: &lt;a href="https://github.com/chandan0000001/music-video_telegrambot" rel="noopener noreferrer"&gt;github.com/chandan0000001/music-video_telegrambot&lt;/a&gt;&lt;br&gt;&lt;br&gt;
HD Demo: &lt;a href="https://streamable.com/or78t5" rel="noopener noreferrer"&gt;Watch on Streamable&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I built a Telegram bot that downloads music and videos from YouTube and I want to walk you through exactly how it works, including the part that most tutorials skip: how yt-dlp actually gets past YouTube's stream protection.&lt;/p&gt;

&lt;p&gt;I built a Telegram bot that downloads music and videos from YouTube — and I want to walk you through exactly how it works, including the part that most tutorials skip: how yt-dlp actually gets past YouTube's stream protection.&lt;/p&gt;

&lt;p&gt;You can try the live bot here: @songsuperzxbot on Telegram&lt;br&gt;
Full source code: github.com/chandan0000001/music-video_telegrambot&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem With Every Other Telegram Music Bot
&lt;/h2&gt;

&lt;p&gt;Most Telegram music bots you find online have the same three problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They stop working after a few days because YouTube changes something&lt;/li&gt;
&lt;li&gt;They hit Telegram's 50 MB limit and either fail silently or send a compressed garbage file&lt;/li&gt;
&lt;li&gt;They run on a single thread, so if three people use it at once, everyone waits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted to fix all three. Here's how.&lt;/p&gt;


&lt;h2&gt;
  
  
  How YouTube Stream Extraction Actually Works (The "Bypass" Explained)
&lt;/h2&gt;

&lt;p&gt;This is the part nobody explains properly. When you open a YouTube video in your browser, the actual MP4/audio stream isn't directly accessible at a clean URL. YouTube obfuscates it in two ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Signature Cipher&lt;/strong&gt;&lt;br&gt;
YouTube wraps the stream URL inside a JavaScript function that scrambles the signature parameter. The raw URL looks something like this:&lt;br&gt;
(&lt;a href="https://rr3---sn-xxx.googlevideo.com/videoplayback?...&amp;amp;sig=SCRAMBLED_SIG" rel="noopener noreferrer"&gt;https://rr3---sn-xxx.googlevideo.com/videoplayback?...&amp;amp;sig=SCRAMBLED_SIG&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;The actual playable URL only works when you un-scramble that signature. YouTube's own player does this in the browser using a JavaScript function it downloads fresh every session. The function changes regularly to break scrapers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;yt-dlp's solution:&lt;/strong&gt; It downloads YouTube's player JS, extracts the current cipher function, reverse-engineers the transformation steps (they're always a combination of reverse, swap, and splice operations), and applies them to reconstruct the real signature. This is why yt-dlp updates so frequently — YouTube rotates this function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. n-parameter Throttling&lt;/strong&gt;&lt;br&gt;
Even with a valid signature, YouTube adds an &lt;code&gt;n&lt;/code&gt; parameter to the URL that limits download speed to about 50 KB/s if not decrypted. yt-dlp decrypts it using the same JS extraction approach, unlocking full speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In my bot&lt;/strong&gt;, I run yt-dlp with 8 parallel fragment streams to max out the download speed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ydl_opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;concurrent_fragment_downloads&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cookiefile&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cookies.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# handles age-restricted content
&lt;/span&gt;    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quiet&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;cookies.txt&lt;/code&gt; handles age-restricted videos. You export your browser cookies with the &lt;a href="https://chromewebstore.google.com/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc" rel="noopener noreferrer"&gt;Get cookies.txt extension&lt;/a&gt; and drop the file in the bot's folder.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Happens After Download: The FFmpeg Pipeline
&lt;/h2&gt;

&lt;p&gt;Once yt-dlp pulls the raw stream, FFmpeg takes over. For audio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Extract audio and convert to MP3 at chosen bitrate
&lt;/span&gt;&lt;span class="n"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webm&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;vn&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ab&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ar&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mp3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For video, I use Intel VAAPI hardware acceleration when available, which runs about 10x faster than CPU encoding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Hardware-accelerated H.264 encode
&lt;/span&gt;&lt;span class="n"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;vaapi_device&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;dri&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;renderD128&lt;/span&gt; \
       &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;webm&lt;/span&gt; \
       &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;vf&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;format=nv12,hwupload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; \
       &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="n"&gt;h264_vaapi&lt;/span&gt; \
       &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;aac&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mp4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On machines without VAAPI (Windows, Termux, most VPS), it falls back to software encoding automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 50 MB Telegram Limit — How I Send Up to 2 GB
&lt;/h2&gt;

&lt;p&gt;Standard Telegram bots can only send files under 50 MB. Most bots just compress the video into a potato-quality file. Mine doesn't.&lt;/p&gt;

&lt;p&gt;The routing logic works like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Under 48 MB&lt;/strong&gt; → Send directly via standard Bot API. Instant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over 48 MB, MTProto configured&lt;/strong&gt; → Use native MTProto client (Telethon) which supports up to 2 GB natively with your API ID and hash from my.telegram.org&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over 48 MB, no MTProto&lt;/strong&gt; → Split the file into 44 MB chunks using lossless stream copy (zero re-encoding, zero quality loss, runs at disk speed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lossless chunking command looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mp4&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;segment&lt;/span&gt; \
       &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;segment_size&lt;/span&gt; &lt;span class="mi"&gt;46137344&lt;/span&gt; &lt;span class="n"&gt;chunk_&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mp4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No quality loss. No waiting for re-encode. Just pure stream splitting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Database: Why SQLite Was the Right Choice
&lt;/h2&gt;

&lt;p&gt;I went with SQLite in WAL mode instead of PostgreSQL. For a single bot, this was the right call:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WAL mode means readers and writers don't block each other — critical when multiple users are downloading simultaneously&lt;/li&gt;
&lt;li&gt;Zero ops overhead — no separate database server to manage&lt;/li&gt;
&lt;li&gt;Atomic crash recovery built in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every connection opens with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PRAGMA journal_mode=WAL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PRAGMA foreign_keys=ON&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The schema tracks every user, every search, every download, and aggregated stats in real time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security: Blocking SSRF Attacks
&lt;/h2&gt;

&lt;p&gt;When users send URLs, a naive bot would just forward them to yt-dlp. That's dangerous — someone could send &lt;code&gt;http://192.168.1.1/admin&lt;/code&gt; and probe your internal network.&lt;/p&gt;

&lt;p&gt;My validator rejects anything that isn't a real YouTube URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;YOUTUBE_REGEX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^https?://(www\.)?(youtube\.com/watch\?v=|youtu\.be/|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;youtube\.com/shorts/)[A-Za-z0-9_\-]{11}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Block private IP ranges first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https?://(localhost|127\.|10\.|192\.168\.|169\.254\.)&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;YOUTUBE_REGEX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filenames are also sanitized before hitting the filesystem to prevent path traversal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;safe_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[^\w\-_\. ]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;_&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw_name&lt;/span&gt;&lt;span class="p"&gt;)[:&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Running 24/7 on Android (Yes, Really)
&lt;/h2&gt;

&lt;p&gt;One of the coolest deployment options is Termux on Android. No root required:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;python ffmpeg &lt;span class="nt"&gt;-y&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
termux-wake-lock
python bot.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;termux-wake-lock&lt;/code&gt; command prevents Android from killing the process when the screen turns off. I ran it for a week on an old Redmi phone and it handled hundreds of downloads without a single crash.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Test Suite: 30 Tests in 4 Seconds
&lt;/h2&gt;

&lt;p&gt;Everything is tested — URL validation, filename sanitization, FFmpeg processing, rate limiting, cancellation, and the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pytest tests/ &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# 30 passed in 4.01s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cancellation test is my favourite — it verifies that hitting the Cancel button actually kills both the yt-dlp subprocess AND the FFmpeg process immediately, then confirms the temp directory is cleaned up. No zombie processes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Live bot: &lt;a href="https://t.me/songsuperzxbot" rel="noopener noreferrer"&gt;https://t.me/songsuperzxbot&lt;/a&gt; on Telegram &lt;br&gt;
Source code + full setup guide: github.com/chandan0000001/music-video_telegrambot&lt;/p&gt;

&lt;p&gt;If you have questions about the yt-dlp cipher extraction, the MTProto upload integration, or running it on Termux, ask in the comments. Happy to go deeper on any part.&lt;/p&gt;

</description>
      <category>python</category>
      <category>telegram</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
