fix(配置): 更新端口和缓存目录配置

修改端口配置以支持环境变量覆盖
简化缓存目录配置,默认使用项目内的.cache目录
```

```msg
perf(缩略图): 优化视频缩略图生成流程

使用ffmpeg生成PNG帧后再转换为WEBP格式
添加错误处理和日志输出
清理临时文件
更新缩略图元数据格式
This commit is contained in:
2026-01-07 19:14:56 +08:00
parent 34d1934bc6
commit c4561ab567
2 changed files with 15 additions and 6 deletions

View File

@@ -18,7 +18,7 @@ const EventEmitter = require('events');
// Configuration
const PORT = process.env.PORT || 9520;
const API_BASE = 'http://183.6.121.121:9558/api';
const CACHE_DIR = process.env.CACHE_DIR ? path.resolve(process.env.CACHE_DIR) : path.join(__dirname, '.cache');
const CACHE_DIR = path.join(__dirname, '.cache');
// Ensure cache directory exists
if (!fs.existsSync(CACHE_DIR)) {
@@ -177,12 +177,21 @@ async function generateThumbAndCache(reply, apiData, contentPath) {
if (contentType.includes('video/')) {
console.log('Generating video thumb:', srcPath);
const thumbTemp = path.join(dir, base.replace('.data', `.thumb.webp.tmp`));
const thumbFrameTemp = path.join(dir, base.replace('.data', `.thumb.frame.png.tmp`));
const { spawn } = require('child_process');
const args = ['-ss', '1', '-i', srcPath, '-frames:v', '1', '-vf', `scale=${width}:-2`, '-y', thumbTemp];
await new Promise((resolve, reject) => { const p = spawn('ffmpeg', args, { stdio: 'ignore' }); p.on('error', reject); p.on('close', c => c === 0 ? resolve() : reject(new Error(`ffmpeg exit ${c}`))); });
const args = ['-hide_banner', '-loglevel', 'error', '-nostdin', '-ss', '1', '-i', srcPath, '-frames:v', '1', '-vf', `scale=${width}:-2`, '-f', 'image2', '-vcodec', 'png', '-y', thumbFrameTemp];
await new Promise((resolve, reject) => {
let err = '';
const p = spawn('ffmpeg', args, { stdio: ['ignore', 'ignore', 'pipe'] });
if (p.stderr) p.stderr.on('data', d => { err += d.toString(); });
p.on('error', reject);
p.on('close', c => c === 0 ? resolve() : reject(new Error(`ffmpeg exit ${c}${err ? ': ' + err.trim() : ''}`)));
});
const thumbTemp = path.join(dir, base.replace('.data', `.thumb.tmp`));
await sharp(thumbFrameTemp).webp({ quality: 80 }).toFile(thumbTemp);
try { fs.renameSync(thumbTemp, thumbFinal); } catch (e) { if (fs.existsSync(thumbFinal)) { try { fs.unlinkSync(thumbFinal); } catch (_) { } fs.renameSync(thumbTemp, thumbFinal); } else { throw e; } }
await fs.promises.writeFile(metaThumbPath, JSON.stringify({ api: apiData, headers: apiData.data.headers || {}, srcSize: stat.size, inputFormat: 'video' }));
await fs.promises.writeFile(metaThumbPath, JSON.stringify({ api: apiData, headers: apiData.data.headers || {}, srcSize: stat.size, inputFormat: 'webp' }));
try { if (fs.existsSync(thumbFrameTemp)) fs.unlinkSync(thumbFrameTemp); } catch (_) { }
const tstat = fs.statSync(thumbFinal);
reply.headers({ 'Content-Type': 'image/webp', 'Content-Length': tstat.size, 'Accept-Ranges': 'bytes', 'Access-Control-Allow-Origin': '*' });
return fs.createReadStream(thumbFinal);

2
new.js
View File

@@ -7,7 +7,7 @@ const crypto = require('crypto');
const EventEmitter = require('events');
// Configuration
const PORT = 9520;
const PORT = process.env.PORT || 9520;
const API_BASE = 'http://127.0.0.1:9558/api';
const CACHE_DIR = process.env.CACHE_DIR ? path.resolve(process.env.CACHE_DIR) : path.join(__dirname, '.cache');