DEV Community

David
David

Posted on

Building a TikTok Downloader: Streaming, Expiring URLs, Slideshows and MP3

Recently, I built a small web service for downloading public TikTok videos, slideshows, and audio.

At first, a TikTok downloader sounds like a very simple project:

  1. Accept a TikTok URL
  2. Extract the media URL
  3. Download the file
  4. Send it to the user

But once you try to turn this into a real service that has to work reliably for many different TikTok posts, things become more interesting.

In this article, I want to share some of the technical decisions and problems I encountered while building it.

The obvious architecture

The most straightforward implementation looks like this:

TikTok
  ↓
My server
  ↓
Temporary MP4 file
  ↓
User
Enter fullscreen mode Exit fullscreen mode

The backend downloads the whole video, saves it somewhere on disk, and then returns the file to the user.

This works.

But for a public downloader, it also creates several unnecessary problems.

Every download means:

  • writing a potentially large file to disk;
  • waiting for the full file to download before processing it;
  • deleting temporary files afterward;
  • handling failed downloads and abandoned files;
  • generating a lot of disk I/O;
  • consuming more storage as concurrent downloads increase.

If 100 users are downloading videos at the same time, I don't really want 100 temporary MP4 files sitting on the server unless there is a good reason for them to be there.

Streaming instead of storing

For regular video downloads, there is usually no need to permanently store the file.

A better flow is closer to this:

TikTok CDN
     ↓
   Server
     ↓
 streaming response
     ↓
    User
Enter fullscreen mode Exit fullscreen mode

The server can read the media response in chunks and forward those chunks to the client.

Conceptually:

while (!feof($source)) {
    echo fread($source, 8192);
    flush();
}
Enter fullscreen mode Exit fullscreen mode

The real implementation needs proper HTTP headers, timeout handling, connection cleanup, error handling, and other details, but the important part is the architecture.

The entire video does not need to exist as a completed temporary file before the user can receive it.

This has a few advantages:

  • lower disk usage;
  • less disk I/O;
  • downloads can start earlier;
  • temporary-file cleanup becomes much simpler;
  • large files don't need to occupy local storage.

Of course, bandwidth still passes through the server if the media is proxied this way. Streaming removes the storage problem, not the bandwidth problem.

Why not re-encode every video?

Another tempting approach is to process every video with FFmpeg before sending it to the user.

For most normal downloads, I don't think that makes sense.

If TikTok already provides a usable MP4 file, re-encoding it would mean:

TikTok MP4
    ↓
Decode
    ↓
Encode again
    ↓
New MP4
Enter fullscreen mode Exit fullscreen mode

That adds CPU usage, increases processing time, and may reduce video quality.

So wherever possible, FromTik returns the available media without unnecessarily re-encoding it.

FFmpeg becomes useful when the output actually needs to be changed—for example, extracting MP3 audio or turning a slideshow into a video.

That is a very different use case from simply downloading an existing MP4.

Media URLs are temporary

One of the more important things to understand is that extracted CDN URLs should not be treated as permanent links.

TikTok media URLs can contain signatures and temporary parameters.

A URL that works now may stop working later.

That means a workflow like this is unreliable:

Extract media URL
      ↓
Store it in database
      ↓
Reuse it tomorrow
Enter fullscreen mode Exit fullscreen mode

Instead, media information should generally be treated as short-lived.

When a user submits a TikTok URL, the backend extracts fresh information for that request.

This also changes how caching should be designed.

Caching everything for a long time sounds attractive, but caching an expired CDN URL isn't very useful.

A TikTok post isn't always a video

Another thing I underestimated initially is that "TikTok downloader" does not mean only downloading MP4 files.

TikTok also has photo/slideshow posts.

So the service has to first identify what kind of post it is dealing with.

A simplified version looks like this:

TikTok URL
    ↓
Extract metadata
    ↓
┌───────────────┬─────────────────┐
│ Video post    │ Slideshow post  │
└───────────────┴─────────────────┘
Enter fullscreen mode Exit fullscreen mode

For a normal video, the result may contain several downloadable video variants.

For a slideshow, the user may want something completely different.

On FromTik, slideshow posts can be downloaded in different forms:

  • individual images;
  • all images inside a ZIP archive;
  • the slideshow rendered as a video with sound.

The last option requires actual media processing, because TikTok doesn't simply provide the slideshow as the exact MP4 output I want to give the user.

Turning a slideshow into a video

This is one of the cases where generating a new file makes sense.

The process is roughly:

Images + audio
      ↓
    FFmpeg
      ↓
Generated MP4
Enter fullscreen mode Exit fullscreen mode

Unlike a regular TikTok video, there isn't always an existing MP4 that can simply be forwarded.

So this is where CPU usage and temporary storage become part of the problem.

It also means the architecture should distinguish between two types of downloads.

Pass-through downloads

Existing media can be streamed:

CDN → Server → User
Enter fullscreen mode Exit fullscreen mode

Generated downloads

Media has to be created first:

Images / Audio
       ↓
    Processing
       ↓
 Temporary output
       ↓
      User
Enter fullscreen mode Exit fullscreen mode

Treating these two cases differently avoids doing expensive processing when it isn't needed.

Audio has the same distinction

The audio downloader follows a similar idea.

There are two useful outputs:

  • the original audio provided by the source;
  • an MP3 version.

If the original audio is already available, it can be returned without converting it.

If the user specifically wants MP3, conversion becomes necessary.

Again, the rule is simple:

Don't transform media unless the requested output actually requires transformation.

It saves CPU and keeps the original quality whenever possible.

HD is more complicated than a button

Another interesting problem is quality selection.

It is easy to create buttons labeled:

720p
1080p
HD
Full HD
Enter fullscreen mode Exit fullscreen mode

But a downloader cannot magically create a higher-quality source.

If TikTok only provides one useful version of a particular post, converting it into a larger resolution doesn't suddenly create more detail.

So I prefer to expose higher-quality options only when an appropriate source actually exists.

Otherwise, an "HD" button risks becoming little more than marketing.

Error handling matters more than expected

A media downloader depends heavily on another platform.

That means many things can go wrong even when your own application is working perfectly:

  • the post was deleted;
  • the post is private;
  • the URL is malformed;
  • TikTok changed something;
  • the media URL expired;
  • a CDN request failed;
  • extraction returned incomplete information;
  • the connection was interrupted midway through a download.

One of my goals with FromTik was therefore not just to make downloads work, but to make failures understandable.

A user should ideally see something meaningful instead of waiting for 30 seconds and eventually getting a generic 500 error.

This sounds obvious, but when testing other downloaders, I noticed that failed requests often result in exactly that kind of experience: a long loading state followed by an unexplained error.

Reliability is probably more important for this kind of service than adding another minor feature.

What I learned

The biggest lesson from building the project was that the downloading part itself isn't necessarily the most difficult part.

The interesting engineering decisions are around it:

  • when to stream and when to store;
  • when to use FFmpeg and when not to;
  • how to handle temporary CDN URLs;
  • how to support different types of posts;
  • how to avoid unnecessary processing;
  • how to recover gracefully when the upstream platform changes or fails.

The architecture I ended up with is therefore less like:

URL → download file
Enter fullscreen mode Exit fullscreen mode

and more like:

                  ┌─ Stream existing video
                  │
TikTok URL → Parse ├─ Return original audio
                  │
                  ├─ Convert audio to MP3
                  │
                  ├─ Download slideshow images
                  │
                  └─ Generate slideshow video
Enter fullscreen mode Exit fullscreen mode

The input looks simple, but the correct processing path depends on what the user actually requested.

The project

The service is available at:

https://fromtik.net/en

It currently supports TikTok videos, slideshows, and audio downloads.

I'm still improving it, especially around reliability and handling different types of TikTok content.

If you've built a similar media-processing service, I'd be interested to hear how you handle streaming, temporary files, and upstream platform changes.

Top comments (0)