字幕文件解析并转换时间单位为微秒,输出指定格式的时间线数据

2026-04-07 12:48:52 8 分享链接 开发笔记 扣子工作流

扣子工作流低代码函数实现

以下是适配扣子工作流的低代码函数,实现字幕文件解析并转换时间单位为微秒,输出指定格式的时间线数据:

// 类型定义(扣子工作流中可省略,仅用于类型提示)
interface Args {
  params: {
    srt: string; // 输入的字幕文本(demo.srt.md内容)
  };
}

interface TimelineItem {
  start: number;
  end: number;
  text?: string;
}

interface Output {
  all_timelines: TimelineItem[];
  timelines: TimelineItem[];
}

async function main({ params }: Args): Promise<Output> {
  // 1. 解析输入的srt字幕文本
  const srtContent = params.srt;
  // 按行分割字幕内容,过滤空行
  const lines = srtContent.split('\n').filter(line => line.trim() !== '');
  
  // 存储解析后的时间线
  const timelines: TimelineItem[] = [];
  // 临时存储单条字幕数据
  let tempId = '';
  let tempTimeRange = '';
  let tempText = '';

  // 2. 时间格式转换工具函数:时分秒,毫秒 -> 微秒
  const timeToMicroseconds = (timeStr: string): number => {
    // 拆分时分秒和毫秒:00:00:00,000 -> [00,00,00,000]
    const [hms, ms] = timeStr.split(',');
    const [hours, minutes, seconds] = hms.split(':').map(Number);
    // 转换公式:小时*3600*1e9 + 分钟*60*1e9 + 秒*1e9 + 毫秒*1e6
    return (hours * 3600 + minutes * 60 + seconds) * 1000000 + Number(ms) * 1000;
  };

  // 3. 逐行解析字幕内容
  for (const line of lines) {
    const trimmedLine = line.trim();
    // 判断是否是序号行(纯数字)
    if (/^\d+$/.test(trimmedLine)) {
      // 如果已有未保存的临时数据,先保存
      if (tempTimeRange && tempText) {
        const [startStr, endStr] = tempTimeRange.split(' --> ').map(t => t.trim());
        const start = timeToMicroseconds(startStr);
        const end = timeToMicroseconds(endStr);
        timelines.push({ start, end, text: tempText.trim() });
      }
      // 重置临时变量
      tempId = trimmedLine;
      tempTimeRange = '';
      tempText = '';
    } 
    // 判断是否是时间范围行(包含 -->)
    else if (trimmedLine.includes('-->')) {
      tempTimeRange = trimmedLine;
    } 
    // 其余行是字幕文本
    else {
      tempText += trimmedLine;
    }
  }

  // 处理最后一条字幕
  if (tempTimeRange && tempText) {
    const [startStr, endStr] = tempTimeRange.split(' --> ').map(t => t.trim());
    const start = timeToMicroseconds(startStr);
    const end = timeToMicroseconds(endStr);
    timelines.push({ start, end, text: tempText.trim() });
  }

  // 4. 构建all_timelines(整体时间范围:第一条start到最后一条end)
  const all_timelines: TimelineItem[] = [];
  if (timelines.length > 0) {
    const firstStart = timelines[0].start;
    const lastEnd = timelines[timelines.length - 1].end;
    all_timelines.push({ start: firstStart, end: lastEnd });
  }

  // 5. 构建输出结果
  const ret: Output = {
    all_timelines,
    timelines
  };

  return ret;
}

函数使用说明

  1. 输入配置:在扣子工作流节点中,添加输入字段 srt,类型为文本,用于传入 demo.srt.md 的完整内容;
  2. 输出配置:添加两个输出字段

    • all_timelines:类型为数组,存储整体时间范围(第一条字幕开始到最后一条结束);
    • timelines:类型为数组,存储每条字幕的时间范围和文本;
  3. 核心逻辑说明

    • 时间转换:将 00:00:00,000 格式的时间拆分为时/分/秒/毫秒,转换为微秒(1秒=1e6微秒,1毫秒=1e3微秒);
    • 字幕解析:按行解析字幕序号、时间范围、文本内容,逐行拼接并生成时间线;
    • 边界处理:兼容最后一条字幕的解析,避免数据丢失;

测试验证

demo.srt.md 的内容传入 srt 输入字段后,输出结果示例:

{
  "all_timelines": [
    {
      "end": 24216000,
      "start": 0
    }
  ],
  "timelines": [
    {
      "end": 683000,
      "start": 0,
      "text": "那一日"
    },
    {
      "end": 1750000,
      "start": 683000,
      "text": "他亲眼看见"
    },
    {
      "end": 3383000,
      "start": 1883000,
      "text": "自家夫人披头散发"
    },
    // ... 中间省略 ...
    {
      "end": 24216000,
      "start": 23500000,
      "text": "将自己淹没"
    }
  ]
}

注意事项

  1. 确保输入的 srt 内容格式与 demo.srt.md 一致(序号+时间范围+文本);
  2. 时间范围行的分隔符必须是 --> (两侧有空格),若字幕文件格式不同需微调正则/分割逻辑;
  3. 扣子工作流中无需手动声明类型(interface),直接保留核心逻辑即可。

我已经按照你的要求修改了代码,输出改为三个字段

  • all_timelines:整体时间轴
  • timelines:只保留开始/结束时间(去掉text
  • texts:纯文本数组,只存放字幕内容

直接复制下面的完整代码到扣子低代码函数即可使用:

async function main({ params }: Args): Promise<Output> {
  // 1. 获取输入的字幕文本
  const srtContent = params.srt;
  const lines = srtContent.split('\n').filter(line => line.trim() !== '');

  // 存储解析结果
  const timelines = []; // 纯时间线
  const texts = [];    // 纯文本数组
  let tempId = '';
  let tempTimeRange = '';
  let tempText = '';

  // 时间转换:00:00:00,000 转为微秒
  const timeToMicroseconds = (timeStr) => {
    const [hms, ms] = timeStr.split(',');
    const [hours, minutes, seconds] = hms.split(':').map(Number);
    return (hours * 3600 + minutes * 60 + seconds) * 1000000 + Number(ms) * 1000;
  };

  // 2. 逐行解析SRT
  for (const line of lines) {
    const trimmedLine = line.trim();
    if (/^\d+$/.test(trimmedLine)) {
      // 保存上一条字幕数据
      if (tempTimeRange && tempText) {
        const [startStr, endStr] = tempTimeRange.split(' --> ').map(t => t.trim());
        const start = timeToMicroseconds(startStr);
        const end = timeToMicroseconds(endStr);
        
        timelines.push({ start, end });
        texts.push(tempText.trim());
      }
      // 重置
      tempId = trimmedLine;
      tempTimeRange = '';
      tempText = '';
    } 
    else if (trimmedLine.includes('-->')) {
      tempTimeRange = trimmedLine;
    } 
    else {
      tempText += trimmedLine;
    }
  }

  // 处理最后一条
  if (tempTimeRange && tempText) {
    const [startStr, endStr] = tempTimeRange.split(' --> ').map(t => t.trim());
    const start = timeToMicroseconds(startStr);
    const end = timeToMicroseconds(endStr);
    
    timelines.push({ start, end });
    texts.push(tempText.trim());
  }

  // 3. 生成整体时间轴
  const all_timelines = [];
  if (timelines.length > 0) {
    all_timelines.push({
      start: timelines[0].start,
      end: timelines[timelines.length - 1].end
    });
  }

  // 4. 输出三个字段
  return {
    all_timelines,
    timelines,
    texts
  };
}

输出格式(完全匹配你给的示例)

{
  "all_timelines": [
    {
      "end": 24216000,
      "start": 0
    }
  ],
  "timelines": [
    {
      "end": 683000,
      "start": 0
    },
    {
      "end": 1750000,
      "start": 683000
    },
    {
      "end": 3383000,
      "start": 1883000
    }
  ],
  "texts": [
    "那一日",
    "他亲眼看见",
    "自家夫人披头散发"
  ]
}

字段说明

  1. all_timelines:整个字幕的总开始/总结束时间
  2. timelines:每一条字幕的开始/结束时间(无文本
  3. texts:每一条字幕对应的纯文本内容(无时间

数组顺序一一对应,可直接在工作流中使用。

字幕文件解析并转换时间单位为微秒,输出指定格式的时间线数据