I built savenowx.com., a free tool that resolves a public X (Twitter) post into a downloadable video — no account, no watermark, no history. The interesting part isn't the UI, it's the constraint I set at the start: nothing touches disk on the server, ever. No stored video files, no download history, nothing to clean up or leak.
That one constraint forced a handful of design decisions that turned out to matter more than I expected. Here's how it actually works.
The resolve step never touches video bytes
GET /api/resolve?url= is the first thing that runs when someone pastes a link. It has two paths:
- X's public syndication endpoint — tried first, because it's fast and doesn't need to spin up yt-dlp at all.
- yt-dlp — used only as a fallback, when the syndication endpoint fails or the post needs it.
The key property: this endpoint returns metadata and CDN URLs. It never downloads or proxies a video byte. That distinction is what makes the rest of the architecture possible — the resolve step stays cheap and fast even under load, because it's doing an API call, not a file transfer.
Results are cached by tweet ID (Redis if REDIS_URL is set, an in-process dict otherwise), but with two different TTLs:
• 1 hour for a successful resolve
• 5 minutes for a genuine "no video" / "not found"
The second one matters more than it looks. Without it, a bad or mistyped link pasted repeatedly would keep re-hitting the syndication endpoint and the yt-dlp fallback on every single request — a cheap way for one confused user (or a bot) to generate real backend load for nothing. Five minutes is enough to absorb repeated hits without caching a real failure for so long that a since-fixed post stays wrongly marked broken.
Downloading a direct MP4: the backend isn't involved at all
Once a post resolves, most of the time the result includes a direct MP4 URL from X's own CDN (video.twimg.com). The naive approach is to have your backend fetch that URL and stream it back to the user. I don't do that.
Instead, the download button points at a small Cloudflare Worker sitting in front of video.twimg.com. The Worker's entire job is to set one header:
Content-Disposition: attachment
Why a whole edge Worker for one header? Because browsers ignore the HTML download attribute on cross-origin links. If I just linked straight to video.twimg.com/...mp4 with download="video.mp4", most browsers would navigate to the video instead of downloading it. The Worker exists purely to fix that, at the edge, with zero involvement from my own server.
The result: for the common case, the video goes directly from X's CDN to the user's device. My FastAPI backend never sees a single byte of it. It resolved the URL and then got out of the way.
The part that can't be a plain link: HLS-only posts
Here's the wrinkle that makes "just redirect to a URL" insufficient as a complete solution: a real share of X videos have no progressive MP4 at all — they're HLS-only (a .m3u8 playlist plus a bunch of small .ts segments). You can't turn that into a single downloadable file with a redirect. Something has to actually fetch the segments and remux them.
For those, and as a general fallback for any variant when no edge Worker is configured (e.g. local dev), there's a small job system:
POST /api/download/url-start → GET /status/{id} → GET /file/{id}
The backend downloads and remuxes via yt-dlp/ffmpeg, reports real progress the frontend polls, streams the finished file back, and deletes the temp file the moment it's sent. Nothing produced by this path outlives the request that asked for it.
So the actual rule is: plain link when possible, job system when not — and the job system's temp-file lifecycle is what keeps the "nothing stored" promise true even for the harder case.
MP3 extraction: streamed, not written
GET /api/mp3?id= takes the lowest-bitrate audio-bearing variant and pipes it through ffmpeg straight to the client. No intermediate file — ffmpeg's output goes directly into the HTTP response stream. Concurrent conversions are capped, since this is the one endpoint doing real CPU work on the server rather than just proxying bytes.
Why this constraint was worth the extra design work
"No server-side storage" sounds like a privacy bullet point for a landing page, but treating it as a hard architectural constraint (not a policy on top of an easier design) is what actually produced the interesting decisions here: syndication-before-yt-dlp for speed, two cache TTLs instead of one, an edge Worker instead of a backend proxy, and a job system whose temp files can't outlive a single request. None of those are exotic individually, but they only exist because "store nothing, ever" was a constraint from day one instead of an afterthought.
The whole thing is a fairly small Next.js + FastAPI + Cloudflare Worker stack. If you're curious, the tool itself is at savenowx.com.
Top comments (0)