Skip to content

feat: ASS 字幕格式支持#558

Open
rrrrrede1 wants to merge 8 commits into
Huanshere:mainfrom
rrrrrede1:feature/ass-support
Open

feat: ASS 字幕格式支持#558
rrrrrede1 wants to merge 8 commits into
Huanshere:mainfrom
rrrrrede1:feature/ass-support

Conversation

@rrrrrede1
Copy link
Copy Markdown

@rrrrrede1 rrrrrede1 commented May 16, 2026

声明:我对项目的完整架构没有 100% 的把握,这部分代码包括PR描述是在 AI 辅助下完成并且经过本地测试的。有任何不对的地方,非常愿意配合修改

概述

在原有 SRT 字幕流程基础上,新增 ASS (Advanced SubStation Alpha) 字幕格式支持。ASS 格式提供远超 SRT force_style 的样式控制能力,包括独立字体、颜色、描边、阴影、对齐等完整 ASS V4+ Style 属性,以及一键粘贴导入外部样式行。同时将 SRT 和 ASS 的样式配置分离为独立的配置节点,各自拥有专属的 UI 编辑器。

分支: feature/ass-support
涉及提交: 8 个(3 feat + 3 fix + 2 refactor)

提交列表(按时间顺序):

  1. 1726467 feat: add ASS subtitle format support with configurable styles
  2. 5563102 fix: add missing zh-CN translations for ASS subtitle UI strings
  3. 6993565 fix: correct ASS V4+ Style field order and add missing style fields
  4. b84cea2 feat: add ASS style import from pasted line and float outline/shadow support
  5. ac38dd9 refactor: remove unused audio ASS output and import
  6. 59d6cab fix: prevent selectbox data loss on imported values outside preset options
  7. 78bc07f refactor: separate SRT and ASS style configs into independent config nodes
  8. 1259e49 feat: add PlayRes conditional comment in config and separate SRT/ASS style configs

新增功能

1. 字幕格式选择

config.yaml 新增 subtitle.format 字段,取值 srtass(默认 srt)。

Streamlit 侧边栏 Subtitles Settings 区域新增下拉框,切换后立即生效并 rerun。

2. SRT/ASS 样式配置分离

SRT 和 ASS 的样式配置分为两个独立节点:

  • subtitle.srt_style:SRT force_style 参数,仅包含 ffmpeg force_style 支持的字段子集
  • subtitle.ass_style:ASS V4+ Style 完整属性,包含所有 23 字段

两者拥有不同的默认值(如 SRT source fontsize=15 vs ASS source fontsize=17)。

3. ASS 文件生成(_6_gen_sub

subtitle.formatass 时,步骤 6 在生成 SRT 的同时额外生成:

文件 内容
output/src.ass 仅源语言
output/trans.ass 仅译文
output/src_trans.ass 源语言在上,译文在下(\N 分隔)
output/trans_src.ass 译文在上,源语言在下

SRT 文件始终生成,不受格式选择影响。配音用 SRT(audio/src_subs_for_audio.srtaudio/trans_subs_for_audio.srt)也始终生成。

4. 烧录模式切换(_7_sub_into_vid

格式 烧录方式
srt ffmpeg subtitles=xxx.srt:force_style='...',force_style 参数从 subtitle.srt_style 读取
ass ffmpeg subtitles=xxx.ass,样式由 .ass 文件内的 Style 行定义

ASS 模式下不再走 force_style,所有样式由 ASS 文件中的 Style 行控制。

5. 配音烧录(_12_dub_to_vid

  • SRT 模式:从 subtitle.srt_style.translation 构建 force_style,烧录 dub.srt
  • ASS 模式:调用 _generate_dub_ass() 实时生成 dub.ass,烧录到配音视频中

_generate_dub_ass_8_1_AUDIO_TASKnew_sub_timeslines 数据构建 DataFrame,再调用 generate_ass 生成,与 _6 生成的显示用 ASS 文件完全独立。

6. 字幕下载 ZIP

下载 ZIP 时同时包含 .srt.ass 文件(若存在)。

7. 样式配置(config.yaml

subtitle:
  format: 'srt'
  # SRT style settings (only effective when format is 'srt')
  srt_style:
    source:
      fontname: 'Arial'
      fontsize: 15
      primary_color: '&HFFFFFF'
      outline_color: '&H000000'
      outline_width: 1.0
      shadow_color: '&H80000000'
      border_style: 1
      alignment: 8
      margin_v: 10
      margin_l: 10
      margin_r: 10
    translation:
      fontname: 'Arial'
      fontsize: 17
      primary_color: '&H00FFFF'
      outline_color: '&H000000'
      outline_width: 1.0
      back_color: '&H33000000'
      border_style: 4
      alignment: 2
      margin_v: 27
      margin_l: 10
      margin_r: 10
  # ASS style settings (only effective when format is 'ass')
  ass_style:
    scale_mode: 'absolute'
    # --- 仅 relative 模式生效 ---
    play_res_x: 1920
    play_res_y: 1080
    # ---------------------------
    source:
      fontname: 'Arial'
      fontsize: 17
      primary_color: '&HFFFFFF'
      secondary_color: '&HFFFFFF'
      outline_color: '&H000000'
      back_color: '&H000000'
      bold: 0
      italic: 0
      border_style: 1
      outline_width: 1.0
      shadow: 0.0
      shadow_color: '&H80000000'   # 冗余字段,ASS 不使用,SRT 从 srt_style 读取
      alignment: 8
      margin_l: 10
      margin_r: 10
      margin_v: 10
    translation:
      fontname: 'Arial'
      fontsize: 17
      primary_color: '&H00FFFF'
      secondary_color: '&H00FFFF'
      outline_color: '&H000000'
      back_color: '&H33000000'
      bold: 0
      italic: 0
      border_style: 4
      outline_width: 1.0
      shadow: 0.0
      alignment: 2
      margin_l: 10
      margin_r: 10
      margin_v: 27

缩放模式

模式 行为
absolute PlayResX/Y = 视频实际分辨率,fontsize 为绝对像素值
relative PlayResX/Y 由 config 指定,fontsize 相对于该分辨率缩放

8. Streamlit 样式编辑器

侧边栏根据格式选择条件显示不同的样式面板:

SRT 模式 → 显示 SRT Style Settings 折叠面板:

  • Source: Font Name、Font Size、Primary/Outline Color、Outline Width、Shadow Color、Border Style、Alignment、Margin V/L/R
  • Translation: Font Name、Font Size、Primary/Outline Color、Outline Width、Back Color、Border Style、Alignment、Margin V/L/R
  • Save SRT Style 按钮

ASS 模式 → 显示 ASS Style Settings 折叠面板:

  • Import ASS Style 折叠面板(一键导入)
  • Font Size Mode(Absolute Pixels / Relative Scaling)
  • PlayResX / PlayResY(仅 relative 模式)
  • Source: Font Name、Font Size、Primary/Secondary/Outline Color、Outline Width(支持小数)、Shadow Depth(支持小数)、Border Style、Alignment、Margin V
  • Translation: Font Name、Font Size、Primary/Secondary/Outline/Back Color、Outline Width(支持小数)、Shadow Depth(支持小数)、Border Style、Alignment、Margin V
  • Save ASS Style 按钮

9. 一键粘贴导入

在 ASS Style Settings 顶部新增 "Import ASS Style" 折叠面板:

  1. 粘贴任意 ASS Style 行,如:
    Style: Default,微软雅黑,54,&H00FFFFFF,&H00FFFFFF,&H19533B3B,&H910E0807,0,0,0,0,100.0,100.0,0.0,0.0,1,3.1,1.2,2,135,135,72,1
    
  2. 选择应用目标:Source / Translation / Both
  3. 点击 Import Style → 解析并写入 config → 页面自动刷新

解析函数 parse_ass_style_line() 支持:

  • Style: 前缀或不带
  • Bold/Italic 的 -1 值自动转为 1
  • float 类型的 Outline(如 3.1)和 Shadow(如 1.2
  • fontsize 等整数字段接受 54.0 形式

10. 完整 ASS V4+ Style 字段对齐

原始实现中 _build_style_line 的字段顺序与 ASS Format 严重错位(从 Bold 开始全部偏移)。现已修正为 23 字段标准输出:

Style: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,OutlineColour,BackColour,Bold,Italic,Underline,StrikeOut,ScaleX,ScaleY,Spacing,Angle,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,Encoding

新增了之前缺失的字段:SecondaryColour、BackColour(source)、Bold、Italic、Shadow(深度值,非颜色)、MarginL、MarginR。


已知局限

不支持从 UI 编辑的字段

以下 ASS Style 字段在 UI 和 config 中不可编辑,使用硬编码默认值:

字段 默认值 说明
Underline 0 ASS 下划线标注,极少用于字幕
StrikeOut 0 ASS 删除线标注,极少用于字幕
ScaleX 100 水平缩放比例,对字幕无实际意义
ScaleY 100 垂直缩放比例,对字幕无实际意义
Spacing 0 字符间距,中文字幕几乎不用
Angle 0 旋转角度,仅特殊场景使用

这些字段可以通过一键导入粘贴来设置(解析后存入 config),但不会在 UI 中单独暴露,因为对字幕场景几乎无用。一键导入后 ScaleX/ScaleY/Spacing/Angle 不会被保留(不在解析字段中),固定使用默认值。

Selectbox 动态选项

BorderStyle 和 Alignment 的 selectbox 选项是动态的:预设选项为基础(BorderStyle: 1/3/4,Alignment: 1-9),但当 config 中的值不在预设列表中时,该值会被自动追加到选项中,确保导入的值不会因 UI 下拉框不包含而丢失。

shadow_color 字段为冗余配置

ASS V4+ 格式中没有 ShadowColour 字段(ASS 只有数值类型的 Shadow 深度)。ass_style.source.shadow_color 目前存在于 config 中但不被任何代码路径使用——ASS 模式下不写入 Style 行的 ShadowColour,SRT 模式下从 srt_style 读取 ShadowColour。此字段为遗留冗余,未来可考虑移除。

字体可用性

ASS 文件中引用的字体名必须在运行 ffmpeg 的系统上已安装。Linux 默认回退到 NotoSansCJK-Regular,macOS 回退到 Arial Unicode MS。如果字体不可用,字幕会显示方框。

双语字幕的 Style 分配

src_trans.ass 中双语行使用 Source 的 Style(第一个 column 的 Style),两部分文本用 \N 连接。这意味着源语言和译文在同一 Style 下渲染,无法在双语行中分别控制源/译的字体和颜色——这是 ASS 格式本身的限制(一个 Dialogue 行只能指定一个 Style)。

SRT force_style 的局限

SRT 模式下通过 ffmpeg force_style 参数注入样式,但 force_style 只支持有限的 ASS 属性子集(FontName、FontSize、PrimaryColour、OutlineColour、OutlineWidth、ShadowColour/BackColour、BorderStyle、Alignment、MarginV/L/R)。这些参数可通过 srt_style 配置和 SRT Style 编辑器修改,并非固定写死;但受限于 ffmpeg force_style 本身支持的属性范围,不如 ASS 模式灵活。

模式切换后的遗留文件

若先以 ASS 格式运行一遍流水线(_6_gen_sub 生成 src.asstrans.asssrc_trans.asstrans_src.ass_12 可能生成 dub.ass),再将 subtitle.format 切回 SRT 重新运行,这些 .ass 文件既不会被删除也不会被覆盖——因为 SRT 模式下的 _6_12 不触碰任何 .ass 文件。这些遗留文件不影响 SRT 流程的运行,但如果需要纯净输出,需手动清理 output/ 下的 .ass 文件。

配音流程仅使用 SRT

步骤 8-11 的配音流程内部使用 SRT 文件做音频对齐,不受 subtitle.format 影响。_6_gen_sub 不会生成 audio/*_for_audio.ass 文件(配音不需要 ASS)。ASS 文件仅在烧录(_7_12)时使用。

为什么配音依赖 SRT 不会导致音画不同步

整个流水线中,决定时间轴的数据源只有一个:_2_asr 产出的词级时间戳(cleaned_chunks.xlsx)。无论 SRT 还是 ASS,时间戳都由同一个 align_timestamp() 函数生成,来自同一组 (start, end) 数据。

配音流程的时间轴链路:

_6_gen_sub 产出 SRT(固定步骤,不受 subtitle.format 影响)
      ↓
_8_1_audio_task 解析 SRT → 仅提取 start_time/end_time/text → tts_tasks.xlsx
      ↓
_8_2_dub_chunks 匹配文本 → 添加 cut_off/lines/src_lines 列
      ↓
_10_gen_audio 按 tts_tasks.xlsx 生成音频片段
      ↓
_11_merge_audio 按 new_sub_times 拼接音频 → dub.mp3 + dub.srt
      ↓
_12_dub_to_vid 用 dub.srt 或 dub.ass 烧录

关键点:

环节 数据来源 是否受 subtitle.format 影响
_8_1 解析 SRT audio/trans_subs_for_audio.srt — SRT 始终生成
_8_2 匹配文本 trans.srt + src.srt — SRT 始终生成
_11 输出 dub.srt new_sub_times(配音速度调整后的实际时间) — 与字幕格式无关
_12 生成 dub.ass new_sub_times 实时转换 — 与 _6 生成的 ASS 无关
  • _8_1 解析的是 audio/trans_subs_for_audio.srt,这是 _6_gen_sub 始终生成的文件
  • _8_2 匹配的 trans.srtsrc.srt 同样始终存在
  • _11 输出的 dub.srt 时间戳来自 new_sub_times(经过配音速度调整后的实际时间),与任何字幕格式无关
  • _12dub.ass 是在烧录时从 _8_1_AUDIO_TASKnew_sub_times 实时转换的(_generate_dub_ass),时间戳直接从配音数据取值,和 _6 生成的显示用 ASS 文件完全独立

因此,配音音频与字幕的时间轴始终一致,不会因字幕格式选择而产生音画不同步。

一键导入不覆盖的字段

一键导入只写入解析出来的字段。以下字段不会被导入写入(因为不属于 ASS Style 行),保留 config 中的原值:

  • shadow_color(冗余字段,ASS 不使用,SRT 从 srt_style 读取)
  • scale_modeplay_res_xplay_res_y(ASS 全局属性,不属于 Style 行)

修改文件清单

文件 改动
config.yaml 新增 subtitle.formatsubtitle.srt_stylesubtitle.ass_style 完整配置
core/utils/ass_utils.py 新增:generate_assparse_ass_style_line_build_style_line_get_default_style、时间转换函数
core/_6_gen_sub.py 条件生成 ASS 文件
core/_7_sub_into_vid.py ASS/SRT 双模式烧录,SRT force_style 从 srt_style 配置读取,新增 _platform_fontname_build_srt_force_style
core/_12_dub_to_vid.py ASS 模式自动生成 dub.ass_generate_dub_ass),SRT force_style 从 srt_style 读取,配音烧录双模式
core/st_utils/sidebar_setting.py 格式选择器、SRT Style 编辑器、ASS Style 编辑器、一键导入 UI
core/st_utils/imports_and_utils.py ZIP 下载同时包含 .srt.ass 文件
core/utils/models.py 新增 ASS 输出路径常量
translations/en.json 新增 ASS 及 SRT 样式相关翻译键
translations/zh-CN.json 新增 ASS 及 SRT 样式相关翻译键

测试清单

在 Window 10 的 WSL Fedora Linux 43 中启动 Streamlit 进行测试:

  • ✅ 前端 UI 正常
  • ✅ 粘贴导入正常
  • ✅ 切换字幕格式正常
  • ✅ 相对缩放烧录字幕正常
  • ✅ 绝对像素烧录字幕正常
  • ✅ 下载字幕文件正常

rrrrrede1 added 8 commits May 16, 2026 02:09
- Add core/utils/ass_utils.py for ASS file generation
  (PlayRes absolute/relative modes, style config driven)
- config.yaml: add subtitle.format and subtitle.ass_style
  (source/translation fonts, colors, borders, alignment)
- _6_gen_sub: generate ASS files alongside SRT when format=ass
- _7_sub_into_vid: remove hardcoded styles, read from config;
  ASS mode burns two .ass files, SRT mode uses config-driven
  force_style with platform font fallback
- _12_dub_to_vid: same pattern, generate dub.ass on-the-fly
- sidebar_setting: add Subtitle Format selector and ASS Style
  Settings expander with all style controls
- imports_and_utils: include .ass files in subtitle download zip
_build_style_line had severely misaligned field output vs ASS V4+ Format
(23 fields). Fields from Bold onward were shifted, causing Shadow depth
to be swallowed and later fields (Alignment, MarginV) in wrong
positions. Added missing fields: SecondaryColour, BackColour for
source, Bold, Italic, Shadow depth, MarginL, MarginR. Updated config,
UI, defaults, and translations accordingly.
…support

- Add parse_ass_style_line() to parse any ASS Style line into config dict
- Add import UI in sidebar: text_area + radio (source/translation/both)
- Change outline_width and shadow to float for fractional values (e.g. 3.1)
- Add translation keys for import feature (en + zh-CN)
- Remove ASS_AUDIO_OUTPUT_CONFIGS and its generation in _6_gen_sub
  (dubbing pipeline only consumes SRT, audio ASS files were never used)
…tions

BorderStyle and Alignment selectboxes now dynamically include the
current config value if it's not in the default preset list, so
imported values (e.g. BorderStyle=4 from paste) are never silently
overwritten by fallback.
…nodes

- Add subtitle.srt_style config node for SRT force_style parameters
  (FontName, FontSize, PrimaryColour, OutlineColour, OutlineWidth,
   ShadowColour/BackColour, BorderStyle, Alignment, MarginV/L/R)
- Keep subtitle.ass_style for full V4+ Style attributes independently
- _build_srt_force_style now reads from subtitle.srt_style instead of
  subtitle.ass_style
- _build_dub_force_style now reads from subtitle.srt_style.translation
- Add SRT Style Settings UI panel in sidebar (simplified editor with
  only force_style-supported fields)
- Add translations for SRT style UI strings
- Regenerate clean config.yaml from config_bak.yaml template
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant