openpuzzle-captcha/module/mimetype.js
2025-12-28 15:54:54 +03:00

129 lines
2.9 KiB
JavaScript

const fs = require("fs");
function readByBuffer(buffer) {
if (!Buffer.isBuffer(buffer) || buffer.length < 4) {
return { contentType: "application/octet-stream", extension: "" };
}
buffer = buffer.slice(0, 16);
// ---------- IMAGES ----------
// PNG
if (
buffer.length >= 8 &&
buffer[0] === 0x89 &&
buffer[1] === 0x50 &&
buffer[2] === 0x4e &&
buffer[3] === 0x47
) {
return { contentType: "image/png", extension: "png" };
}
// JPEG
if (buffer[0] === 0xff && buffer[1] === 0xd8) {
return { contentType: "image/jpeg", extension: "jpg" };
}
// GIF
if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46) {
return { contentType: "image/gif", extension: "gif" };
}
// WEBP
if (
buffer.length >= 12 &&
buffer[0] === 0x52 && // R
buffer[1] === 0x49 && // I
buffer[2] === 0x46 && // F
buffer[3] === 0x46 && // F
buffer[8] === 0x57 && // W
buffer[9] === 0x45 && // E
buffer[10] === 0x42 && // B
buffer[11] === 0x50 // P
) {
return { contentType: "image/webp", extension: "webp" };
}
// ---------- VIDEO / AUDIO ----------
// MP4 / M4V / MOV (ISO BMFF)
if (buffer.length >= 12 && buffer.slice(4, 8).toString() === "ftyp") {
const brand = buffer.slice(8, 12).toString();
if (["isom", "mp41", "mp42"].includes(brand)) {
return { contentType: "video/mp4", extension: "mp4" };
}
if (["M4V ", "M4VH"].includes(brand)) {
return { contentType: "video/x-m4v", extension: "m4v" };
}
if (brand === "qt ") {
return { contentType: "video/quicktime", extension: "mov" };
}
}
// MP3
if (
buffer.slice(0, 3).toString() === "ID3" ||
(buffer[0] === 0xff && (buffer[1] & 0xe0) === 0xe0)
) {
return { contentType: "audio/mpeg", extension: "mp3" };
}
// WAV
if (
buffer.slice(0, 4).toString() === "RIFF" &&
buffer.slice(8, 12).toString() === "WAVE"
) {
return { contentType: "audio/wav", extension: "wav" };
}
// OGG
if (buffer.slice(0, 4).toString() === "OggS") {
return { contentType: "audio/ogg", extension: "ogg" };
}
// ---------- DOCUMENTS ----------
// PDF
if (buffer.slice(0, 4).toString() === "%PDF") {
return { contentType: "application/pdf", extension: "pdf" };
}
// ZIP (docx, xlsx, apk, etc.)
if (buffer[0] === 0x50 && buffer[1] === 0x4b) {
return { contentType: "application/zip", extension: "zip" };
}
// ---------- TEXT ----------
// UTF-8 text (heuristic)
const text = buffer.toString("utf8");
if (/^[\x09\x0A\x0D\x20-\x7E]*$/.test(text)) {
return { contentType: "text/plain", extension: "txt" };
}
// ---------- FALLBACK ----------
return {
contentType: "application/octet-stream",
extension: "",
};
}
function readByPath(pathToFile) {
const size = 16;
const fd = fs.openSync(pathToFile, "r");
try {
const buffer = Buffer.alloc(size);
fs.readSync(fd, buffer, 0, size, 0);
return readByBuffer(buffer);
} catch (err) {
console.error(err);
} finally {
fs.closeSync(fd);
}
}
module.exports = { byPath: readByPath, byBuffer: readByBuffer };