You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Whisper API分片音频转录:修复SRT序号与时间戳问题

解决Whisper API分片转录SRT的拼接问题

针对长音频分片转录后SRT序号重置、时间戳错误、空行冗余的问题,直接给你可落地的Node.js/Express实现方案:

核心处理步骤

  1. 提前记录每个音频分片的时长:用工具获取每个分片的精确时长,后续用来修正时间戳
  2. 解析单分片的SRT内容:把每个分片的SRT拆成独立的字幕条目,提取序号、时间区间、文本
  3. 修正序号与时间戳:
    • 序号:累加前面所有分片的字幕总条数,作为当前分片每条字幕的起始序号
    • 时间戳:把当前分片每条字幕的时间都加上前面所有分片的总时长
  4. 重新生成标准SRT格式:统一用单空行分隔字幕条目,避免双空行

具体代码实现

1. 工具函数:获取音频分片时长

用fluent-ffmpeg库获取音频时长(先安装:npm install fluent-ffmpeg)

const ffmpeg = require('fluent-ffmpeg');

async function getAudioDuration(filePath) {
  return new Promise((resolve, reject) => {
    ffmpeg.ffprobe(filePath, (err, metadata) => {
      if (err) reject(err);
      // 转换为毫秒
      resolve(Math.floor(metadata.format.duration * 1000));
    });
  });
}

2. 工具函数:解析SRT为字幕条目数组

function parseSrt(srtContent) {
  const entries = [];
  // 按多换行分割字幕条目,处理Whisper返回的格式
  const rawEntries = srtContent.trim().split(/\n\n+/);
  
  rawEntries.forEach(entry => {
    const lines = entry.trim().split('\n');
    if (lines.length < 3) return;
    
    const index = parseInt(lines[0].trim(), 10);
    const [startStr, endStr] = lines[1].trim().split(' --> ');
    
    entries.push({
      index,
      start: srtTimeToMs(startStr),
      end: srtTimeToMs(endStr),
      text: lines.slice(2).join('\n').trim()
    });
  });
  
  return entries;
}

// SRT时间格式转毫秒
function srtTimeToMs(timeStr) {
  const [hours, minutes, seconds] = timeStr.split(':');
  const [sec, ms] = seconds.split(',');
  return parseInt(hours) * 3600000 + parseInt(minutes) * 60000 + parseInt(sec) * 1000 + parseInt(ms);
}

// 毫秒转SRT时间格式
function msToSrtTime(ms) {
  const hours = Math.floor(ms / 3600000);
  const minutes = Math.floor((ms % 3600000) / 60000);
  const seconds = Math.floor((ms % 60000) / 1000);
  const remainingMs = ms % 1000;
  return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')},${remainingMs.toString().padStart(3, '0')}`;
}

3. 拼接分片SRT的主函数

async function mergeChunkedSrts(chunkDataList) {
  let totalIndex = 0;
  let totalDuration = 0;
  const mergedEntries = [];
  
  for (const chunkData of chunkDataList) {
    const { srtContent, audioPath } = chunkData;
    // 获取当前分片时长
    const chunkDuration = await getAudioDuration(audioPath);
    // 解析当前分片的SRT
    const chunkEntries = parseSrt(srtContent);
    
    // 修正每个条目的序号和时间戳
    chunkEntries.forEach(entry => {
      mergedEntries.push({
        index: totalIndex + entry.index,
        start: entry.start + totalDuration,
        end: entry.end + totalDuration,
        text: entry.text
      });
    });
    
    // 更新累计序号和时长
    totalIndex += chunkEntries.length;
    totalDuration += chunkDuration;
  }
  
  // 生成最终的SRT内容
  return mergedEntries.map(entry => {
    return `${entry.index}\n${msToSrtTime(entry.start)} --> ${msToSrtTime(entry.end)}\n${entry.text}`;
  }).join('\n\n');
}

4. 使用示例

// 假设你已经有了分片的SRT内容和对应的音频文件路径
const chunkDataList = [
  {
    srtContent: '你的第一个分片SRT内容',
    audioPath: './audio/chunk1.mp3'
  },
  {
    srtContent: '你的第二个分片SRT内容',
    audioPath: './audio/chunk2.mp3'
  }
];

// 合并SRT
mergeChunkedSrts(chunkDataList).then(finalSrt => {
  console.log(finalSrt);
  // 这里可以把finalSrt写入文件或者返回给前端
}).catch(err => {
  console.error('合并失败:', err);
});

额外说明

  • 双空行问题:通过parseSrt函数里的split(/\n\n+/)合并多空行,最后用join('\n\n')统一生成单空行分隔的标准SRT格式
  • Whisper API返回的SRT可能有格式差异,比如有些换行不标准,parseSrt里的逻辑可以根据实际返回的格式微调
  • 音频分片要保证连续无重叠,否则时间戳会出错

内容的提问来源于stack exchange,提问作者mheavers

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 21:47:37