A small but easy-to-get-wrong part of building any "download this file" button: the filename the browser actually saves. I ran into a handful of edge cases while working on X Video Downloader, and none of them are specific to this tool — they apply to any client triggering a file save from a URL.
The naive approach breaks on real-world titles
The obvious approach is to build a filename from the post's text: take the first few words, slap on an extension, done. This breaks fast:
function naiveFilename(text, ext) {
return text.slice(0, 40) + '.' + ext;
}
Post text isn't filename-safe. Slashes turn into fake subdirectories on some platforms. Control characters and emoji can end up silently stripped or mangled depending on the OS and browser. And on Windows, a small set of reserved names (CON, PRN, NUL, COM1, …) will make the save fail outright if they land as the whole filename, case-insensitively.
A safer normalization pass
const RESERVED = new Set(['CON','PRN','AUX','NUL',
'COM1','COM2','COM3','COM4','COM5','COM6','COM7','COM8','COM9',
'LPT1','LPT2','LPT3','LPT4','LPT5','LPT6','LPT7','LPT8','LPT9']);
function safeFilename(raw, ext, fallback = 'download') {
let name = raw
.normalize('NFKC')
.replace(/[