<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Ajay Walia</title><link>https://curiousbit.netlify.app/</link><description>Digital workplace, artificial intelligence, cloud, security, automation, and enterprise technology notes by Ajay Walia.</description><language>en-au</language><managingEditor>Ajay Walia</managingEditor><webMaster>Ajay Walia</webMaster><copyright>Copyright 2026 Ajay Walia</copyright><lastBuildDate>Sun, 21 Jun 2026 05:46:10 +0000</lastBuildDate><atom:link href="https://curiousbit.netlify.app/tags/nodejs/index.xml" rel="self" type="application/rss+xml"/><image><url>https://curiousbit.netlify.app/images/og-default.png</url><title>Ajay Walia</title><link>https://curiousbit.netlify.app/</link></image><item><title>I Built My Own Video Downloader — No Ads, No Watermarks, Three Platforms</title><link>https://curiousbit.netlify.app/i-built-my-own-video-downloader-no-ads-no-watermarks-three-platforms/</link><guid isPermaLink="true">https://curiousbit.netlify.app/i-built-my-own-video-downloader-no-ads-no-watermarks-three-platforms/</guid><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><dc:creator>Ajay Walia</dc:creator><description>&lt;p&gt;Every video download site I tried felt like navigating a minefield — five ad clicks to reach a button that triggers another redirect. So I stopped using them and built a clean local tool that handles TikTok, Instagram, and X in one paste.&lt;/p&gt;</description><content:encoded>&lt;![CDATA[<img src="https://curiousbit.netlify.app/images/video-downloader-banner.jpg" alt="Nodejs" style="max-width:100%;height:auto;margin-bottom:1.5em;"/><p>Every video download site I tried felt like navigating a minefield — five ad clicks to reach a button that triggers another redirect. So I stopped using them and built a clean local tool that handles TikTok, Instagram, and X in one paste.</p><p><strong>No watermarks. No ads. No accounts. Just a URL.</strong></p><p><em>~6 min read · Node.js + React · conceptual deep-dive</em></p><style>
@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap');
.vd-article {
--vd-surface: #111827;
--vd-surface2: #1a2235;
--vd-border: #1f2d45;
--vd-text: #e2e8f0;
--vd-muted: #8b9ab3;
--vd-green: #00c853;
--vd-green-glow: rgba(0,200,83,0.15);
--vd-cyan: #22d3ee;
--vd-cyan-glow: rgba(34,211,238,0.12);
--vd-orange: #f59e0b;
color: var(--vd-text);
font-size: 1.05rem;
line-height: 1.85;
margin: 2.5rem 0;
}
.vd-article * { box-sizing: border-box; }
.vd-article h2 {
font-family: 'Space Grotesk', sans-serif;
font-size: clamp(1.5rem, 1.2rem + 1vw, 2.1rem);
font-weight: 700;
letter-spacing: -0.02em;
color: var(--vd-text);
margin: 2.6rem 0 0.9rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--vd-border);
}
.vd-article h3 {
font-family: 'Space Grotesk', sans-serif;
font-size: clamp(1.15rem, 1.05rem + 0.3vw, 1.45rem);
font-weight: 700;
color: var(--vd-green);
margin: 2rem 0 0.6rem;
}
.vd-article p { margin: 0 0 1.2rem; color: var(--vd-text); }
.vd-article strong { color: #fff; font-weight: 600; }
.vd-article a { color: var(--vd-green); text-decoration: underline; text-underline-offset: 3px; }
.vd-article code {
background: var(--vd-surface2);
padding: 0.1rem 0.4rem;
border-radius: 4px;
font-family: 'JetBrains Mono', monospace;
font-size: 0.88rem;
}
.vd-callout {
background: var(--vd-surface2);
border-left: 3px solid var(--vd-green);
border-radius: 0 10px 10px 0;
padding: 1.1rem 1.4rem;
margin: 1.6rem 0;
font-size: 1rem;
line-height: 1.7;
}
.vd-callout.warning { border-color: var(--vd-orange); background: rgba(245,158,11,0.06); }
.vd-callout.info { border-color: var(--vd-cyan); background: rgba(34,211,238,0.06); }
.vd-steps { display: flex; flex-direction: column; gap: 1rem; margin: 1.5rem 0 2rem; }
.vd-step-card {
background: var(--vd-surface);
border: 1px solid var(--vd-border);
border-radius: 12px;
padding: 1.2rem 1.4rem;
display: flex;
gap: 1.1rem;
align-items: flex-start;
}
.vd-step-num {
font-family: 'Space Grotesk', sans-serif;
font-size: 1.3rem; font-weight: 800;
color: var(--vd-green);
min-width: 2rem;
line-height: 1.3;
}
.vd-step-body h4 {
font-family: 'Space Grotesk', sans-serif;
font-weight: 700; font-size: 1rem;
margin: 0 0 0.3rem;
color: #fff;
}
.vd-step-body p { margin: 0; font-size: 0.92rem; color: var(--vd-muted); line-height: 1.6; }
.vd-platform-row { display: flex; gap: 0.75rem; flex-wrap: wrap; margin: 1.2rem 0 1.8rem; }
.vd-platform-badge {
display: flex; align-items: center; gap: 0.5rem;
background: var(--vd-surface);
border: 1px solid var(--vd-border);
border-radius: 10px;
padding: 0.6rem 1rem;
font-size: 0.88rem; font-weight: 600;
color: var(--vd-text);
}
.vd-platform-badge .vd-dot { width: 8px; height: 8px; border-radius: 50%; }
.vd-dot-tiktok { background: #ff0050; }
.vd-dot-insta { background: #e1306c; }
.vd-dot-x { background: #1d9bf0; }
.vd-post-img {
width: 100%; border-radius: 12px;
border: 1px solid var(--vd-border);
margin: 1.5rem 0 0.4rem;
display: block;
box-shadow: 0 4px 24px rgba(0,0,0,0.4);
}
.vd-img-caption {
text-align: center;
font-size: 0.8rem;
color: var(--vd-muted);
margin: 0 0 2rem;
font-style: italic;
}
.vd-flow {
background: var(--vd-surface);
border: 1px solid var(--vd-border);
border-radius: 14px;
padding: 1.8rem 1.4rem;
margin: 1.8rem 0;
display: flex; flex-direction: column; align-items: center;
}
.vd-flow-node {
background: var(--vd-surface2);
border: 1px solid var(--vd-border);
border-radius: 10px;
padding: 0.65rem 1.4rem;
font-size: 0.9rem;
font-weight: 600;
color: var(--vd-text);
text-align: center;
width: 100%;
max-width: 420px;
}
.vd-flow-node.green { border-color: rgba(0,200,83,0.4); background: var(--vd-green-glow); color: var(--vd-green); }
.vd-flow-node.orange { border-color: rgba(245,158,11,0.4); background: rgba(245,158,11,0.07); color: var(--vd-orange); }
.vd-flow-node.cyan { border-color: rgba(34,211,238,0.4); background: var(--vd-cyan-glow); color: var(--vd-cyan); }
.vd-flow-arrow { color: var(--vd-muted); font-size: 1.1rem; line-height: 1; padding: 0.35rem 0; }
.vd-results { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: 1.5rem 0 2rem; }
@media (max-width: 600px) { .vd-results { grid-template-columns: 1fr; } }
.vd-result-card {
background: var(--vd-surface);
border: 1px solid var(--vd-border);
border-radius: 12px;
padding: 1.2rem;
}
.vd-result-card .vd-rc-icon { font-size: 1.6rem; margin-bottom: 0.5rem; }
.vd-result-card h4 {
font-family: 'Space Grotesk', sans-serif;
font-size: 0.95rem; font-weight: 700;
color: #fff; margin: 0 0 0.3rem;
}
.vd-result-card p { font-size: 0.86rem; color: var(--vd-muted); margin: 0; line-height: 1.55; }
.vd-cta {
background: var(--vd-surface);
border: 1px solid rgba(0,200,83,0.25);
border-radius: 14px;
padding: 1.6rem 1.8rem;
margin: 2.5rem 0 0;
}
.vd-cta p { font-size: 0.95rem; color: var(--vd-text); margin: 0; line-height: 1.7; }
.vd-cta strong { color: var(--vd-green); }
.vd-divider { border: none; border-top: 1px solid var(--vd-border); margin: 2.2rem 0; }</style><div class="vd-article"><h2>The Problem With Every Downloader Site</h2><p>You find a TikTok video you want to keep. Maybe it's a tutorial, a recipe, a clip you want to share somewhere offline. You Google "download TikTok without watermark" and click the first result. What follows is a ritual:</p><div class="vd-callout warning">
Pop-up #1 appears. You close it. A second tab opens. You close that. You find the actual download button — but it's fake and triggers another ad. The real button is somewhere underneath a consent banner. You finally click download. It starts… and gives you a watermarked file anyway.</div><p>This happens on nearly every popular downloader site. They're monetised almost entirely through advertising, and the UX is designed to maximise your exposure to that advertising — not to help you download a video. The actual download logic underneath all that noise is usually a single API call.</p><p>So I asked myself: how hard would it actually be to build a clean version of this for personal use?</p><h2>What I Actually Wanted</h2><p>Three things, nothing more:</p><div class="vd-steps"><div class="vd-step-card"><div class="vd-step-num">1</div><div class="vd-step-body"><h4>No watermark, always</h4><p>The tool should try its hardest to get a watermark-free file. If it can't, it tells you — it doesn't quietly give you the watermarked version pretending it's clean.</p></div></div><div class="vd-step-card"><div class="vd-step-num">2</div><div class="vd-step-body"><h4>Support the platforms I actually use</h4><p>TikTok, Instagram, and X (Twitter). Paste any URL from any of these three and it should just work.</p></div></div><div class="vd-step-card"><div class="vd-step-num">3</div><div class="vd-step-body"><h4>Zero friction interface</h4><p>One input, one button. No accounts, no CAPTCHAs, no ads. It runs locally so there's nothing to sign up for.</p></div></div></div><h2>How It Works — The Conceptual Picture</h2><p>The tool is split into two halves: a lightweight React frontend and a Node.js backend. The frontend is just the URL input box. All the interesting logic lives in the backend.</p><p>Here are the five things that happen the moment you paste a URL and hit Download:</p><h3>Step 1 — Figure Out Which Platform You're On</h3><p>The very first thing the backend does is inspect the URL and decide: is this TikTok, Instagram, or X? Each platform has its own URL patterns — including short links, mobile URLs, and regional variants — and the tool checks all of them.</p><div class="vd-platform-row"><div class="vd-platform-badge"><span class="vd-dot vd-dot-tiktok"/> tiktok.com · vm.tiktok.com · vt.tiktok.com</div><div class="vd-platform-badge"><span class="vd-dot vd-dot-insta"/> instagram.com · www.instagram.com</div><div class="vd-platform-badge"><span class="vd-dot vd-dot-x"/> x.com · twitter.com · mobile.twitter.com</div></div><p>If the URL doesn't match any of these, the backend rejects it immediately with a clear error before wasting any time trying to fetch something it can't handle. No silent failures.</p><img src="/images/video-downloader-arch.jpg" alt="Anime engineer pointing at a holographic flowchart of the download architecture" class="vd-post-img"><p class="vd-img-caption">The backend runs through a short chain: detect → resolve → try providers → cache → serve</p><h3>Step 2 — Resolve Short Links</h3><p>TikTok in particular loves to generate short share links like<code>vm.tiktok.com/AbcXyz</code>. These redirect to the full video URL, but the download providers need the real URL to work with. So the backend follows up to five redirects to resolve the final destination before doing anything else.</p><h3>Step 3 — The Provider Chain</h3><p>This is the core of the tool. The backend doesn't rely on a single source for the download link — it tries multiple providers in order, and only moves to the next one if the previous failed.</p><div class="vd-flow"><div class="vd-flow-node cyan">Validated &amp; resolved URL</div><div class="vd-flow-arrow">↓</div><div class="vd-flow-node"><strong>Provider 1:</strong> TikWM API — fast, HD, usually no-watermark</div><div class="vd-flow-arrow">↓ if failed</div><div class="vd-flow-node"><strong>Provider 2:</strong> yt-dlp — catches what TikWM misses, supports all 3 platforms</div><div class="vd-flow-arrow">↓ if both fail on no-watermark</div><div class="vd-flow-node orange">Last resort: watermarked fallback (with a clear warning shown)</div><div class="vd-flow-arrow">↓</div><div class="vd-flow-node green">✓ Download link returned to frontend</div></div><p>The first provider, TikWM, is a public API that's fast and usually returns an HD, watermark-free file. But it occasionally struggles with newer videos or private content. That's when<strong>yt-dlp</strong> steps in — a powerful open-source tool that knows how to extract media from hundreds of platforms, and is updated constantly as platforms change their serving behaviour.</p><div class="vd-callout info"><strong>Why yt-dlp as a fallback and not primary?</strong> TikWM is faster and returns a clean pre-parsed result. yt-dlp is more capable but adds latency since it runs as a local process and parses raw platform data. Using TikWM first keeps the happy path quick.</div><h3>Step 4 — The Proxy Download</h3><p>Here's a detail that actually matters for reliability: the tool doesn't give your browser a direct link to TikTok's CDN or Instagram's servers. Instead, it registers a short-lived<strong>secure token</strong> that points back to the Node backend. When you click download, your browser hits the backend's own<code>/api/file</code> endpoint, which streams the video directly to you.</p><p>Why does this matter? Direct CDN links from social platforms often include authentication tokens or short expiry times. They also sometimes block downloads when accessed directly from a browser outside the platform. Running the stream through the backend sidesteps both issues — and means the download starts with a clean filename instead of a jumble of CDN parameters.</p><h3>Step 5 — Caching</h3><p>Once a video URL has been resolved and a download link extracted, the result is cached in memory for 30 minutes. If you (or someone else on the same local instance) pastes the same video URL again within that window, the backend returns the cached result instantly — no API calls, no yt-dlp process, just the stored answer.</p><h2>The Result</h2><p>What this adds up to in practice:</p><div class="vd-results"><div class="vd-result-card"><div class="vd-rc-icon">🎯</div><h4>No-watermark first, always</h4><p>The tool tries every avenue for a clean file before falling back. The fallback is clearly labelled.</p></div><div class="vd-result-card"><div class="vd-rc-icon">⚡</div><h4>Fast on repeat URLs</h4><p>Same video twice within 30 minutes? Instant response from the in-memory cache.</p></div><div class="vd-result-card"><div class="vd-rc-icon">🎵</div><h4>Audio extraction too</h4><p>The tool also surfaces the audio-only track when available — useful for saving music from TikTok.</p></div><div class="vd-result-card"><div class="vd-rc-icon">🔒</div><h4>No external accounts</h4><p>Runs entirely locally. Nothing to log into, nothing phoning home, no API keys required.</p></div></div><img src="/images/video-downloader-result.jpg" alt="Anime developer smiling at a clean minimal download interface" class="vd-post-img"><p class="vd-img-caption">One URL input, three download buttons, zero pop-ups. That's the whole interface.</p><h2>What's Next</h2><p>The tool is currently running locally but I'm planning to deploy it on<strong>curiousbit.netlify.app</strong> so anyone who wants a clean download experience can use it without having to run Node themselves. The architecture is already production-ready — it's just a matter of pointing it at a hosting environment and wiring up the environment variables.</p><p>A few things I'd like to add before making it fully public: rate limiting per IP (to avoid abuse), a simple download history in the UI, and potentially Instagram Stories support which currently needs a different extraction path.</p><div class="vd-callout">
The code is structured as a monorepo — backend and frontend live together, share URL validation logic, and build to a single deployable package. If you want to run it yourself locally, it's a single<code>npm install &amp;&amp; npm run dev</code> away.</div><hr class="vd-divider"><div class="vd-cta"><p><strong>Over to you:</strong> Have you ever got fed up enough with a broken web experience that you built your own alternative? I'd love to hear what you made — or whether you'd actually use a clean, ad-free downloader like this if it were publicly hosted. Drop your thoughts below or find me on LinkedIn.</p></div></div>
]]></content:encoded><media:content url="https://curiousbit.netlify.app/images/video-downloader-banner.jpg" medium="image"><media:title type="plain">Nodejs</media:title></media:content><category>automation</category><category>build-log</category><category>nodejs</category><category>Knowledge Base</category></item></channel></rss>