0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Checking If a Media URL Is Still Valid Before You Try to Download It

0
Posted at

Checking If a Media URL Is Still Valid Before You Try to Download It

I maintain a small tool called Twitter Video Download that lets people save X (Twitter) videos, pull the audio out as MP3, and download recorded Spaces. One recurring headache with any "download this media URL" tool is that the URL you get back from a page is often not the final, stable file location — it's a redirect, a signed link with an expiry, or a CDN URL that returns a different status depending on region or headers. Here's a small pattern I use to validate a media URL before committing to a full download.

The naive approach (and why it breaks)

The obvious first attempt is just:

const res = await fetch(mediaUrl);
if (res.ok) {
  // stream it to disk / to the client
}

This works until it doesn't:

  • Some CDNs reject a plain fetch without a Range header and return 416 or 403.
  • Signed URLs can be time-limited; by the time a user clicks "download" the link may have expired.
  • A 200 response doesn't guarantee the body is actually media — some endpoints return an HTML error page with a 200 status (looking at you, a few ad-supported hosts).

A more defensive check

async function isDownloadable(url) {
  try {
    const head = await fetch(url, { method: 'HEAD', redirect: 'follow' });
    if (head.ok) {
      const type = head.headers.get('content-type') || '';
      return type.startsWith('video/') || type.startsWith('audio/');
    }
  } catch {
    // some servers don't implement HEAD at all — fall through to a ranged GET
  }

  // Fallback: ask for just the first byte instead of the whole file
  const partial = await fetch(url, {
    method: 'GET',
    headers: { Range: 'bytes=0-0' },
  });
  return partial.ok || partial.status === 206;
}

A few things this buys you:

  1. HEAD first, GET as fallback. Plenty of media servers don't support HEAD cleanly, so treat a thrown error or a non-2xx as "try the ranged GET" rather than an immediate failure.
  2. Content-Type as a sanity check, not proof. It's cheap to check and catches the "this is actually an HTML error page" case, but don't rely on it exclusively — some servers mislabel it.
  3. A 1-byte range request is enough to confirm the server will actually serve the file without pulling the whole thing over the wire, which matters when you're validating a batch of links before offering them to a user.

Why this matters for a downloader tool specifically

When the whole point of the product is "paste a link, get a file," the worst failure mode is a user waiting on a spinner for a link that was already dead. Validating up front — even with a cheap HEAD/ranged-GET check — turns a silent timeout into an immediate, honest "this link isn't available anymore, try re-copying it from the original post" message. That single check has probably saved more support emails than any other few lines of code in the project.

Nothing here is specific to X/Twitter — the same pattern applies to any tool that consumes a media URL from a third party rather than hosting the file itself.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?