首页
/ Buzz 插件系统完全指南:内置插件、生命周期钩子与自定义插件开发

Buzz 插件系统完全指南:内置插件、生命周期钩子与自定义插件开发

2026-09-12 16:33:05作者:尤辰城Agatha

导读

本文是 Buzz 离线语音转写与翻译工具的插件(Plugin)使用与开发指南。插件自 Buzz 1.4.5 起引入,允许在不修改核心应用的前提下扩展转写流水线:既可以在转写前预处理音频(降噪、语言识别)、在转写后修改结果(字幕重排),也可以在转写保存后执行自定义动作(导出 DOCX、生成 AI 摘要、跳过重复任务)。读完本文,你将掌握插件的启用、排序与配置方式,理解 6 个内置插件的实现原理,并能够基于 BuzzPlugin 基类从零编写、打包和安装属于自己的插件。

插件机制概述:扩展点而非分支

Buzz 的插件体系将"转写流水线"分解为若干可插拔的时机点(hook)。根据 buzz/plugins/base.py 的文档字符串,插件是一个包含 plugin.py 模块的文件夹,该模块定义且仅定义一个 BuzzPlugin 子类;子类通过 metadata 类属性声明身份、配置模式与(可选的)pip 依赖,并覆写自己关心的生命周期钩子。

插件被刻意设计为"无需修改核心应用"的扩展:

  • 可在转写前处理音频,并返回一个新的文件路径交给后续转写;
  • 可修改或替换转写结果的分段列表;
  • 可在一条转写记录保存到数据库后执行副作用,例如导出额外格式或生成摘要。

生命周期钩子(Hook)一览

钩子方法 运行时机 线程 典型用途
before_transcription 转写开始前、语音提取之后 转写工作线程 音频预处理(降噪),返回新文件路径替换 task.file_path
check_skip 转写前 工作线程 返回分段列表则跳过转写;返回 None 则继续
after_transcription 转写完成后、落库之前 后台线程 修改/重排结果分段,返回值作为新的分段列表
on_complete 转写已保存到数据库之后 后台线程 副作用:写 Notes、导出文件、调用外部 API

PluginManager.process_completedbuzz/plugins/manager.py)为例,可以清楚看到后处理链的次序:先执行所有启用插件的 after_transcription,再把最终分段持久化到数据库(该调用被 marshaling 回主线程),最后执行 on_complete

线程安全契约(插件作者必读)

base.py 的模块注释明确了宿主强制的线程契约:

  • before_transcription 运行在转写工作线程上,可以读/改源音频文件并返回新路径,但不得触碰数据库、Qt 控件或任何主线程绑定的对象;
  • after_transcriptionon_complete 运行在后台线程,但通过 PluginContext.transcription_service 访问数据库是被允许的——宿主会用 MainThreadInvoker 把分段保存等调用转发回主线程(底层原因是 QSqlDatabase 默认连接是线程亲和的,见 buzz/plugins/post_processing.py);插件同样永远不要直接操作 Qt 控件。

钩子在队列工作线程中的实际调用点位于 buzz/file_transcriber_queue_worker.py:转写前先执行 run_before_transcription,随后执行 run_check_skip 决定是否跳过;process_completed 则由 buzz/widgets/main_window.py 在任务完成后触发。每个钩子都以 enabled_plugins_in_order() 的结果按序调用,且单个插件抛出的异常会被捕获记录,不会中断其他插件。

管理插件:启用、排序、配置与安装

在 Buzz 中打开 Help → Plugins 即可进入插件管理对话框(对应 buzz/widgets/plugins_dialog/plugins_dialog.py),支持以下操作:

  • 启用/禁用:使用每个插件旁边的开关切换;
  • 调整顺序:拖动插件改变执行顺序(顺序影响同一钩子的调用先后,见下文"执行顺序"说明);
  • 配置:点击插件旁的设置图标打开 plugin_settings_dialog.py,编辑各配置项;
  • 添加社区插件:点击 Add by URL...,粘贴一个 .zip 文件的链接即可安装;
  • 移除:选择插件后点击 Remove(会同时清理其密码字段在系统 keyring 中的密钥与存储的配置,见 PluginManager.remove)。

插件存储位置与加载

根据 buzz/plugins/loader.py

  • 插件目录为 <用户缓存目录>/Buzz/plugins/<plugin_id>/(通过 platformdirs.user_cache_dir("Buzz") 计算);
  • 第三方依赖统一安装到 <用户缓存目录>/Buzz/plugins_deps/,安装结果记录在 .installed.json 标记文件中,避免重复安装;
  • 首次启动时,源码树中内置的 6 个插件会被复制到用户目录;copy_bundled_plugins 采用内容比对(忽略 __pycache__*.pyc),仅当内置版本发生变化时才覆盖,不会动用户自行安装的插件;
  • discover_plugin_dirs 仅扫描包含 plugin.py 入口模块的子目录。

安装来自 zip 的插件(内部机制)

PluginManager.add_from_urlloader.download_and_extract 的安装流程包含多重保护(loader.py):

  1. 从 URL 下载 zip(60 秒超时);
  2. 解压前做 zip-slip 路径穿越防护,拒绝任何试图逃出目标目录的成员;
  3. 支持 zip 顶层直接放插件文件,或嵌套在单一包裹目录中(GitHub 风格 zip 的常见形态);
  4. 先加载验证、后提交安装:先用 load_plugin_from_dir 验证 zip 内容是一个合法插件(存在 plugin.py、恰好一个 BuzzPlugin 子类、有 metadata.id),验证通过才复制进插件目录;
  5. 安装后自动启用该插件。

安装后,插件声明的 pip_dependencies 会被 pip install --target plugins_deps_dir 到独立依赖目录并加入 sys.path,因此第三方依赖不会污染主应用环境。

启用状态与顺序的持久化

PluginManager 使用 QSettings 的 plugins 分组持久化 order(顺序列表)与 enabled(各插件开关状态)。_reconcile_order 会在初始化时清理已失效的插件 id,并把新发现的插件追加到顺序末尾。

执行顺序值得注意:同一钩子按顺序依次调用,前一个插件的结果会传递给后一个。例如 after_transcription 中后一个插件拿到的是前一个插件修改后的分段列表;before_transcription 中后一个插件看到的 task.file_path 是前一个插件返回的新路径;而 check_skip 则是第一个返回非 None 结果的插件生效run_check_skip 的短路语义)。

内置插件详解

内置插件由 loader.py 中的 BUNDLED_PLUGIN_IDS 定义:ai_summarytranscript_resizerexport_docxenhanced_language_detectionskip_already_transcribeddeep_filter_net,其源码即位于 buzz/plugins 目录,是编写自定义插件的最佳参考起点。

AI Summary:转录后自动生成摘要

ai_summary/plugin.pyon_complete 钩子中,把整段转录文本拼接后发送到 OpenAI 兼容的 chat completions API,并将生成的摘要写入转录记录的 Notes 字段和/或源音频旁的文本文件。

配置项(来自 metadata.config_fields):

配置键 类型 默认值 说明
api_url TEXT https://api.openai.com/v1 API 基础地址,可指向 OpenAI、Ollama 等兼容服务
api_key PASSWORD API 密钥,存入系统 keyring,不写入 QSettings
model TEXT gpt-4o-mini 使用的模型名
prompt TEXTAREA 内置提示词 摘要生成提示词
save_to_notes BOOL true 是否写入 Notes 字段
save_to_file BOOL false 是否写入文本文件
output_folder TEXT 文件输出目录,留空则保存在源文件旁

实现细节:该插件不声明 pip 依赖,因为 openai 包已随 Buzz 捆绑;请求超时 120 秒;文件输出命名为 {源文件名}.summary.txt。注意 PASSWORD 类型字段的读取与写入都走 keyring_store,密钥名称为 plugin:{plugin_id}:{field_key}(见 manager.py_secret_name),这是插件配置安全性的关键设计。

Enhanced Language Detection:转写前自动语言识别

enhanced_language_detection/plugin.pybefore_transcription 钩子中,针对"语言留空(自动检测)"的文件,先用本地 whisper.cpp 执行一次快速的 detect-language 预检测:

  • 优先使用本机已下载的最大 whisper.cpp 模型(排除 CUSTOM、LUMII 等特殊用途模型);
  • 若没有任何 whisper.cpp 模型,且配置项 download_tiny_if_missing(默认开启)为真,则自动下载 tiny 模型用于检测;
  • 检测到的语言写回 task.transcription_options.language,从而驱动实际转写、并渲染导出文件名中的 {{ language }} 占位符;同时通过 update_transcription_language 更新数据库记录,保证之后从转录查看器导出时语言一致;
  • 若用户已显式选择语言(代码中 "en" 被视为未选择的默认值,仍会触发检测),则跳过检测。

该插件的场景是批量转写语言未知的文件,用一次极快的 whisper.cpp 预检换取主转写的准确性与导出文件命名的正确性。

Export to DOCX:导出为 Word 文档

export_docx/plugin.pyon_complete 钩子中把转录导出为 Microsoft Word .docx 文件。亮点是零第三方依赖——它直接用标准库 zipfile 手写 OOXML 的三个必要部件([Content_Types].xml_rels/.relsword/document.xml)生成 docx,从而规避冻结版应用中 python-docx 依赖 lxml 二进制轮子无法从插件依赖缓存加载的问题。

配置项:

配置键 类型 默认值 说明
output_folder TEXT 输出目录,留空则保存在源文件旁
include_timestamps BOOL false 是否包含每段的时间戳

未开启时间戳时,文本按段落组织,分段规则与 TXT 导出器一致:相邻分段间隔达到 PARAGRAPH_SPLIT_TIME = 2000 毫秒即另起一段。文件名沿用源文件名({stem}.docx),标题取自文件名。

Resize Transcript:自动字幕重排

transcript_resizer/plugin.pyon_complete 钩子中,把**启用了词级时间戳(word-level timings)**的转录重新分组为适合字幕尺寸的分段,并替换原结果。它镜像了 transcription_resizer_widget.py 的 "Merge" 行为,但转为每次转写后自动执行。stable_whispersrt_equalizer 等重型导入被推迟到钩子内部,避免拖慢应用启动。

配置项:

配置键 类型 默认值 说明
merge_by_gap BOOL true 按静音间隙合并词
merge_gap_seconds TEXT 0.2 合并的间隙阈值(秒)
split_by_punctuation BOOL true 按标点拆分
punctuation TEXT .* /./. /。/?/? /?/!/! /!/,/, 拆分的标点规则串
split_by_max_length BOOL true 按最大长度拆分
max_length TEXT 42 最大字幕长度(字符)

实现要点:

  • 若转写未启用词级时间戳,插件直接跳过;
  • 内部把 Buzz 分段转换为 stable_whisper 期望的 get_transcript 数据源,以 regroup 规则字符串(形如 mg=0.2++42+1_sp=..._sl=42)驱动重排,并关闭 VAD 与静音抑制以保持时间轴一致;
  • 中文、日文、泰文等无空格语言NON_SPACE_LANGUAGES = {"zh", "ja", "th", "lo", "km", "my"})分词时不插入空格分隔符;
  • 规则字符串可用环境变量 BUZZ_MERGE_REGROUP_RULE 整体覆盖,便于高级用户微调;
  • 重排后通过 replace_transcription_segments 替换数据库中的分段。

DeepFilterNet Noise Reduction:转写前降噪

deep_filter_net/plugin.pybefore_transcription 钩子中用 DeepFilterNet3 模型移除背景噪声:对 task.file_path 指向的音频执行 init_df → load_audio → enhance → save_audio,输出带 _DeepFilterNet3.wav 后缀的新文件并返回该路径作为后续转写的音频源。插件声明依赖 deepfilternet>=0.5.6(这是唯一声明了 pip 依赖的内置插件,由 PluginManager._install_deps_if_needed 负责安装)。

配置项:

配置键 类型 默认值 说明
keep_denoised_file BOOL false 转写完成后是否保留降噪文件

默认情况下,降噪文件只是中间产物——on_complete 钩子会把它删除;开启"Keep denoised file after transcription"即可保留。若降噪阶段失败,插件记录错误并返回 None,转写继续使用原始音频,保证流水线不被单点故障阻断。

Skip Already Transcribed:跳过已转写文件

skip_already_transcribed/plugin.py 通过 check_skip 钩子实现"重复导入不重复转写"。支持两种检测方式(可同时启用):

  • Check for existing result files(默认开启):在音频同目录(以及任务的输出目录)查找同名的 .txt.srt.vtt 结果文件,若找到则解析其内容为分段并导入,任务标记为 Skipped,不再运行模型;TXT 直接作为整段文本,SRT/VTT 会解析时间轴并转换为 Segment
  • Check in transcription database(默认关闭):按文件名查询数据库中已有的已完成转写记录,命中则把旧分段复制到新记录,同样标记为 Skipped

该插件的典型场景是:重新导入一个"部分文件已转写"的文件夹,或对文件夹进行自动监听(folder watch)时,避免对已有结果的音频重复跑模型。注意实现中 _speech 后缀("Extract speech" 功能产生的临时文件)会被剥离后再匹配结果文件。

编写自己的插件:从模板到完整实现

内置插件源码是最佳起点。你也可以直接把 buzz/plugins 目录指给 AI 编码助手,请其按需生成插件。

插件骨架

一个插件文件夹必须包含 plugin.py,其中定义恰好一个 BuzzPlugin 子类。最小骨架如下(结构参照 tests/plugins/plugin_system_test.py 中的 VALID_PLUGIN):

from buzz.plugins.base import (
    BuzzPlugin, PluginMetadata, ConfigField, ConfigFieldType,
)

class MyPlugin(BuzzPlugin):
    metadata = PluginMetadata(
        id="my_plugin",                    # 唯一 id,用于目录命名与状态持久化
        name="My Plugin",                  # UI 中显示的名称
        description="...",                 # 可选
        version="1.0.0",                   # 可选
        pip_dependencies=[],               # 可选:需要额外安装的 PyPI 包
        config_fields=[                    # 可选:设置对话框中展示的配置项
            ConfigField(key="text", label="Text", default="hello"),
            ConfigField(key="flag", label="Flag",
                        type=ConfigFieldType.BOOL, default=True),
            ConfigField(key="secret", label="Secret",
                        type=ConfigFieldType.PASSWORD),  # 存入系统 keyring
        ],
    )

    def before_transcription(self, task, context):
        return task.file_path + ".processed"   # 返回新路径则替换音频源

    def after_transcription(self, task, segments, context):
        return segments                        # 返回修改后的分段列表

    def check_skip(self, task, context):
        return None                            # 返回分段列表则跳过转写

    def on_complete(self, transcription_id, task, segments, context):
        pass                                   # 转写落库后的副作用

关键 API 说明

  • ConfigFieldType 四选一:TEXT(单行输入)、TEXTAREA(多行输入)、BOOL(复选框)、PASSWORD(掩码输入,存系统 keyring);
  • PluginContext 提供 config(已解析的配置字典,键为 ConfigField.key)、transcription_service(数据库服务,后台线程调用会被自动 marshal 回主线程)、settingslog(专属 logger buzz.plugin.<id>);
  • 插件目录下的 locale/<locale>.json(如 lv_LV.json)可提供多语言文案,plugin_gettext(__file__) 会按当前 UI locale 加载翻译,缺失时回退原文(见 base.pyplugin_gettext)。

打包与安装

将插件文件夹(含 plugin.py 与可选 locale/ 目录)打包成 zip 后,在 Help → Plugins → Add by URL... 粘贴 zip 链接即可安装。zip 内可以是顶层插件文件,也可以是单个包裹目录(GitHub 仓库 zip 的常见形态);安装过程会自动校验合法性、安装声明的 pip 依赖,并默认启用。

质量保障:插件的测试视角

仓库为插件系统提供了较完整的测试覆盖,可作为自研插件的验收参照:

  • tests/plugins/plugin_system_test.py 覆盖:合法插件加载、缺少子类时报 PluginLoadError、管理器的发现与排序、配置持久化与 PASSWORD 字段密钥存储、启停/移动、zip 安装与移除等;
  • tests/plugins/loader_test.py 覆盖:内置插件复制与内容比对、zip-slip 防护、嵌套目录解包、download_and_extract 的校验逻辑等。

插件生态与社区

如果你构建了有用的插件,可以与 Buzz 社区分享,或提交合并请求申请将其收录为内置插件。内置插件的更新会通过 copy_bundled_plugins 的内容比对机制自动同步到用户目录;而用户自行安装的插件目录不会被内置更新覆盖,这一设计在"可升级性"与"用户自定义不被破坏"之间取得了平衡。

结语

Buzz 的插件体系围绕"四个生命周期钩子 + 声明式元数据 + 独立依赖目录"展开:核心应用只负责在正确的时机分发事件,而具体的音频预处理、结果改写与落库后动作全部交给插件自治。对于普通用户,六个内置插件已覆盖降噪、语言识别、字幕重排、DOCX 导出、AI 摘要与重复跳过等高频需求;对于开发者,同一套 BuzzPlugin 契约既可写出数十字的最小插件,也能承载依赖第三方库的复杂流水线——唯一的底线是遵守线程安全契约,不要在工作线程里碰数据库和 Qt 控件。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.79 K
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
936
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
863
1.36 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.1 K
1.03 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
400
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.09 K
540