2025-10-23 10:30:43 +08:00

91 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { promises as fs } from 'fs';
import os from 'os';
import path from 'path';
import { execFile } from 'child_process';
import { promisify } from 'util';
const execFileAsync = promisify(execFile);
export function pickBestPlayAddr(variants: PlayVariant[] | undefined | null) {
if (!variants?.length) return null;
const best = variants.reduce((best, cur) => {
const b1 = best?.bit_rate ?? -1;
const b2 = cur?.bit_rate ?? -1;
return b2 > b1 ? cur : best;
});
return best?.play_addr ?? null;
}
/**
* 使用 ffmpeg 从视频二进制中提取第一帧,返回 JPEG buffer
*/
export async function extractFirstFrame(videoBuffer: Buffer): Promise<{ buffer: Buffer; contentType: string; ext: string } | null> {
const ffmpegCmd = process.env.FFMPEG_PATH || 'ffmpeg';
const tmpDir = os.tmpdir();
const base = `dy_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
const inPath = path.join(tmpDir, `${base}.mp4`);
const outPath = path.join(tmpDir, `${base}.jpg`);
try {
await fs.writeFile(inPath, videoBuffer);
const args = [
'-hide_banner',
'-loglevel', 'error',
'-ss', '0',
'-i', inPath,
'-frames:v', '1',
'-q:v', '2',
'-f', 'image2',
'-y',
outPath,
];
await execFileAsync(ffmpegCmd, args, { windowsHide: true });
const img = await fs.readFile(outPath);
return { buffer: img, contentType: 'image/jpeg', ext: 'jpg' };
} catch (e: any) {
if (e && (e.code === 'ENOENT' || /not found|is not recognized/i.test(String(e.message)))) {
console.warn('系统未检测到 ffmpeg可安装并配置 PATH 或设置 FFMPEG_PATH 后启用封面提取。');
return null;
}
throw e;
} finally {
try { await fs.unlink(inPath); } catch { }
try { await fs.unlink(outPath); } catch { }
}
}
/**
* 使用 ffprobe 获取视频时长(毫秒)
*/
export async function getVideoDuration(videoBuffer: Buffer): Promise<number | null> {
const ffprobeCmd = process.env.FFPROBE_PATH || 'ffprobe';
const tmpDir = os.tmpdir();
const base = `dy_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
const inPath = path.join(tmpDir, `${base}.mp4`);
try {
await fs.writeFile(inPath, videoBuffer);
const args = [
'-v', 'error',
'-show_entries', 'format=duration',
'-of', 'default=noprint_wrappers=1:nokey=1',
inPath,
];
const { stdout } = await execFileAsync(ffprobeCmd, args, { windowsHide: true });
const durationSeconds = parseFloat(stdout.trim());
if (isNaN(durationSeconds)) return null;
return Math.round(durationSeconds * 1000); // 转换为毫秒
} catch (e: any) {
if (e && (e.code === 'ENOENT' || /not found|is not recognized/i.test(String(e.message)))) {
console.warn('系统未检测到 ffprobe可安装并配置 PATH 或设置 FFPROBE_PATH 后启用时长提取。');
return null;
}
console.warn(`获取视频时长失败: ${e?.message || e}`);
return null;
} finally {
try { await fs.unlink(inPath); } catch { }
}
}