65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
import * as slugify from 'slugify';
|
|
import { basename, join } from 'path';
|
|
import os from 'node:os';
|
|
import fs from 'node:fs';
|
|
import { createId } from '@paralleldrive/cuid2';
|
|
import { ua0 } from 'scout/ua.js';
|
|
import { Readable } from 'stream';
|
|
import { finished } from 'stream/promises';
|
|
import pRetry from 'p-retry';
|
|
|
|
export function fpSlugify(str: string): string {
|
|
return slugify(str, {
|
|
lower: true,
|
|
strict: true,
|
|
locale: 'en',
|
|
trim: true,
|
|
});
|
|
}
|
|
|
|
export function getTmpFile(str: string): string {
|
|
return join(os.tmpdir(), `${createId()}_${basename(str)}`);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {String} url
|
|
* @returns {String} filePath
|
|
*
|
|
* greetz https://stackoverflow.com/a/74722818/1004931
|
|
*/
|
|
export async function download({ url, filePath }: { url: string; filePath?: string }): Promise<string | null> {
|
|
if (!url) throw new Error(`second arg passed to download() must be a {string} url`);
|
|
const fileBaseName = basename(url);
|
|
filePath = filePath || join(os.tmpdir(), `${createId()}_${fileBaseName}`);
|
|
const stream = fs.createWriteStream(filePath);
|
|
|
|
const requestData = async () => {
|
|
const response = await fetch(url, {
|
|
headers: {
|
|
'user-agent': ua0,
|
|
},
|
|
});
|
|
|
|
const { body } = response;
|
|
await finished(Readable.fromWeb(body).pipe(stream));
|
|
|
|
// Abort retrying if the resource doesn't exist
|
|
if (response.status === 404) {
|
|
throw new AbortError(response.statusText);
|
|
}
|
|
|
|
return;
|
|
};
|
|
|
|
try {
|
|
await pRetry(requestData, { retries: 3 });
|
|
} catch (e) {
|
|
console.error(`utils.download failed to download ${url}`);
|
|
console.error(e);
|
|
return null;
|
|
}
|
|
return filePath;
|
|
}
|
|
|
|
export const tmpFileRegex = /^\/tmp\/.*\.jpg$/; |