HEX
Server: nginx/1.24.0
System: Linux nowruzgan 6.8.0-57-generic #59-Ubuntu SMP PREEMPT_DYNAMIC Sat Mar 15 17:40:59 UTC 2025 x86_64
User: babak (1000)
PHP: 8.3.6
Disabled: NONE
Upload Files
File: //var/dev/shahnamag/back-end/node_modules/@xhmikosr/downloader/index.js
import events from 'node:events';
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import contentDisposition from 'content-disposition';
import archiveType from '@xhmikosr/archive-type';
import decompress from '@xhmikosr/decompress';
import defaults from 'defaults';
import extName from 'ext-name';
import {fileTypeFromBuffer} from 'file-type';
import filenamify from 'filenamify';
import getStream from 'get-stream';
import got from 'got';

const defaultOptions = {
	got: {
		responseType: 'buffer',
		https: {
			rejectUnauthorized: process.env.npm_config_strict_ssl !== 'false',
		},
	},
	decompress: {},
};

const getExtFromMime = response => {
	const header = response.headers['content-type'];

	if (!header) {
		return null;
	}

	const exts = extName.mime(header);

	return exts.length === 1 ? exts[0].ext : null;
};

const getFilename = async (response, data) => {
	const header = response.headers['content-disposition'];

	if (header) {
		const parsed = contentDisposition.parse(header);

		if (parsed.parameters?.filename) {
			return parsed.parameters.filename;
		}
	}

	let filename = path.basename(new URL(response.requestUrl).pathname);

	if (!path.extname(filename)) {
		const fileType = await fileTypeFromBuffer(data);
		const ext = fileType?.ext || getExtFromMime(response);

		if (ext) {
			filename = `${filename}.${ext}`;
		}
	}

	return filename;
};

const filterEvents = async (name, listener) => {
	for await (const [message] of events.on(name, listener)) {
		if (message) {
			return message;
		}
	}
};

const download = (uri, output, options) => {
	if (typeof output === 'object') {
		options = output;
		output = null;
	}

	options = defaults(options, defaultOptions);

	const stream = got.stream(uri, options.got);

	const promise = filterEvents(stream, 'response')
		.then(response => {
			const encoding = options.got.responseType === 'buffer' ? 'buffer' : options.got.encoding;
			return Promise.all([getStream(stream, {encoding}), response]);
		})
		.then(async ([data, response]) => {
			const hasArchiveData = options.extract && await archiveType(data);

			if (!output) {
				return hasArchiveData ? decompress(data, options.decompress) : data;
			}

			const filename = options.filename || filenamify(await getFilename(response, data));
			const outputFilepath = path.join(output, filename);

			if (hasArchiveData) {
				return decompress(data, path.dirname(outputFilepath), options.decompress);
			}

			return fs
				.mkdir(path.dirname(outputFilepath), {recursive: true})
				.then(() => fs.writeFile(outputFilepath, data))
				.then(() => data);
		});

	// eslint-disable-next-line unicorn/no-thenable
	stream.then = promise.then.bind(promise);
	stream.catch = promise.catch.bind(promise);

	return stream;
};

export default download;