小奥录音 OddMinutes - 您的音频会议小助手
开源的智能音频会议系统,支持实时录音、AI 转录、智能摘要生成和音字联动功能,也支持导入已有的音频进行转录、总结。
使用步骤
- 配置你自己的ASR和LLM服务器,只要支持OpenAI接口的都行。ASR推荐用我的 OddAsr私有部署,LLM推荐用Ollama私有部署或者是任意公有云的。
- 导入已有的会议音频,进行转写,并生成会议纪要、会议总结、会议待办事项。
- Windows系统上,支持系统托盘,方便快速管理和操作小奥录音
功能特性
- 🎙️ 麦克风 + 系统音频双轨录制 - 支持同时录制麦克风和系统音频,带自动增益控制(AGC)
- 📝 AI 实时转录 - 对接 OddASR 服务,支持字级时间戳输出
- 🤖 智能摘要生成 - 支持 Ollama/OpenAI/Claude/Groq 多种 LLM 提供商
- 🔗 音字联动 - 点击文字跳转到音频对应位置,播放时高亮当前文字
- 📤 多格式导出 - 支持 TXT、Markdown、JSON 格式导出
- 🔍 全文搜索 - 支持转录内容搜索
- ⏱️ 录音暂停/恢复 - 支持录音过程中暂停和恢复
- 🎨 多主题支持 - 提供现代亮色、科技暗色、温暖办公、专注暗色四种主题
- 🔔 通知系统 - 支持桌面通知和通知历史管理
- 📋 系统托盘 - Windows 系统托盘集成,支持快捷操作
快速安装
# 基础安装
pip install OddMinutes
# Windows 用户(包含系统托盘和快捷键支持)
pip install OddMinutes[windows]
# 启动程序
OddMinutes
首次运行时,程序会自动初始化数据库并创建用户数据目录。
技术架构
┌─────────────────────────────────────────────────────────────┐
│ 前端 (HTML/CSS/JS) │
│ - 会议列表管理 │
│ - 录音控制面板(电平监测) │
│ - 转录内容展示(音字联动) │
│ - AI 摘要展示 │
│ - 笔记编辑 │
│ - 主题切换 │
│ - 通知系统 │
└──────────────────────┬──────────────────────────────────────┘
│ HTTP / WebSocket
┌──────────────────────▼──────────────────────────────────────┐
│ Django 后端 │
│ ┌─────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐ │
│ │ core │ │ meetings │ │ recording │ │summary │ │
│ │ 首页/ │ │ 会议CRUD │ │ 音频录制 │ │ AI摘要 │ │
│ │ 路由 │ │ 搜索/导出 │ │ 电平监测 │ │ 模板配置 │ │
│ └─────────┘ └──────────┘ └────────────┘ └──────────┘ │
│ ┌──────────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ transcription│ │ system_app│ │ Channels │ │
│ │ ASR转录 │ │ 系统设置 │ │ WebSocket │ │
│ │ 音频上传 │ │ 通知/更新 │ │ 实时电平推送 │ │
│ └──────────────┘ └───────────┘ └──────────────┘ │
└──────────────────────┬──────────────────────────────────────┘
│ HTTP API
┌──────────────────────▼──────────────────────────────────────┐
│ 外部服务依赖 │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ OddASR │ │ Ollama/OpenAI │ │
│ │ (语音识别) │ │ (LLM 摘要) │ │
│ │ 127.0.0.1:9002│ │ localhost:11434 │ │
│ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
项目结构
OddMinutes/
├── OddMinutes/ # Python 包入口(用于 PyPI 安装)
│ ├── __init__.py # 包初始化
│ ├── launcher.py # 命令行启动器入口
│ ├── manage.py # Django 管理命令(引导用)
│ └── .env.example # 环境变量示例(引导用)
├── core/ # 核心模块(首页、基础模型)
│ ├── models.py # BaseModel 抽象基类
│ ├── views.py # 首页和会议详情视图
│ ├── urls.py # 路由配置
│ ├── audio_utils.py # 音频转换工具(WAV→MP3)
│ └── management/commands/init_defaults.py # 初始化命令
├── meetings/ # 会议管理模块
│ ├── models.py # Meeting、TranscriptSegment、SummaryProcess、MeetingExport
│ ├── views.py # 会议 CRUD、搜索、导出、清理
│ ├── urls.py # 路由配置
│ └── serializers.py # DRF 序列化器
├── recording/ # 录音模块
│ ├── models.py # RecordingSession、RecordingCheckpoint、AudioDevice
│ ├── views.py # 录音控制 API
│ ├── urls.py # 路由配置
│ ├── consumers.py # WebSocket 电平推送消费者
│ └── routing.py # WebSocket 路由
├── transcription/ # 转录模块
│ ├── models.py # TranscriptionJob、TranscriptionConfig
│ ├── views.py # 转录 API(文件转录、重新转录、上传)
│ ├── urls.py # 路由配置
│ ├── oddasr_client.py # OddASR OpenAI 兼容客户端
│ ├── consumers.py # WebSocket 实时转录消费者
│ └── routing.py # WebSocket 路由
├── summary/ # AI 摘要模块
│ ├── models.py # SummaryTemplate、AIModelConfig、SummaryLanguagePreference
│ ├── views.py # 摘要生成 API
│ ├── urls.py # 路由配置
│ └── ai_processor.py # AI 摘要处理器(支持多提供商、chunk 分段处理)
├── system_app/ # 系统设置模块
│ ├── models.py # SystemSettings、UpdateInfo、NotificationLog
│ ├── views.py # 系统设置、通知、更新检查 API
│ ├── urls.py # 路由配置
│ ├── tray_icon.py # 系统托盘图标
│ └── shortcuts.py # 快捷键支持
├── meeting_minutes/ # Django 项目配置
│ ├── settings.py # 项目配置
│ ├── urls.py # 全局路由
│ ├── asgi.py # ASGI 入口(支持 WebSocket)
│ ├── static/ # 静态资源
│ │ ├── css/style.css # 样式文件
│ │ ├── js/app.js # 前端逻辑
│ │ ├── images/ # 图片资源
│ │ └── favicon.ico # 网站图标
│ └── templates/ # HTML 模板
│ └── core/
│ ├── index.html # 主页面
│ └── meeting_detail.html # 会议详情页
├── media/ # 媒体文件存储(音频文件)
├── logs/ # 日志文件
├── docs/ # 文档目录
├── .env # 环境变量配置
├── .env.example # 环境变量示例
├── requirements.txt # Python 依赖
├── launcher.py # 根目录启动脚本
├── launcher_packaged.py # 打包后使用的启动脚本
├── manage.py # Django 管理命令
├── oddminutes_config.py # 全局配置加载模块
├── pyproject.toml # PyPI 打包配置
├── OddMinutes.spec # PyInstaller 打包配置
└── pack.bat / pack_whl.bat # 打包脚本
外部服务依赖
1. OddASR 语音识别服务
安装 OddASR 服务
pip install oddasr
运行 OddASR 服务
oddasr-server
必需 - 用于音频转录,提供 OpenAI 兼容的 /v1/audio/transcriptions 接口。
- 默认地址:
http://127.0.0.1:9002 - 配置文件:
.env中的ODDASR_HOST和ODDASR_PORT - 支持
verbose_json响应格式,返回段级和字级时间戳
2. Ollama / LLM 服务
可选 - 用于 AI 摘要生成。
- 默认地址:
http://localhost:11434(Ollama) - 支持提供商:Ollama(本地)、OpenAI、Claude、Groq
- 通过
pydantic-ai-slim库统一调用
3. FFmpeg
必需 - pydub 依赖 FFmpeg 进行音频格式转换。
安装与运行
环境要求
- Python 3.12+
- FFmpeg(需添加到系统 PATH)
- OddASR 服务(运行中)
- Ollama(可选,用于本地 LLM 摘要)
PyPI 安装(推荐)
# 基础安装
pip install OddMinutes
# Windows 用户(包含系统托盘和快捷键支持)
pip install OddMinutes[windows]
启动程序
OddMinutes
首次运行时,程序会自动完成以下操作:
- 自动安装缺失的依赖
- 初始化数据库(自动执行 migrate)
- 创建用户数据目录(位于
%APPDATA%\Local\OddMeta\OddMinutes或~/.local/share/OddMeta/OddMinutes) - 复制必要的配置文件
启动参数
# 指定端口
OddMinutes --port 9012
# 不自动打开浏览器
OddMinutes --no-browser
# 显示控制台窗口(调试模式)
OddMinutes --debug
开发安装(从源码)
git clone https://github.com/oddmeta/OddMinutes.git
cd OddMinutes
# 安装开发依赖
pip install -r requirements.txt
# 安装 Windows 额外依赖(Windows 用户)
pip install pywin32 keyboard pynput
# 配置环境变量
copy .env.example .env
编辑 .env 文件,设置关键配置:
# Django 配置
DJANGO_SECRET_KEY=your-secret-key-here
DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1
# OddASR 配置(必需)
ODDASR_HOST=127.0.0.1
ODDASR_PORT=9002
# Ollama 配置(可选,用于本地 LLM)
OLLAMA_HOST=http://localhost:11434
# 音频配置
AUDIO_SAMPLE_RATE=16000
AUDIO_CHANNELS=1
AUDIO_CHUNK_DURATION=5
# 会议设置
AUTO_CLEANUP_DAYS=7
MAX_AUDIO_FILE_SIZE=500
# 开发模式:指定数据目录
ODDMINUTES_DATA_DIR=.
初始化数据库
python manage.py migrate
python manage.py init_defaults
启动服务
# 使用启动脚本(推荐,自动处理依赖和配置)
python launcher.py
# 或使用调试模式
python launcher.py --debug
# 或直接运行开发服务器
python manage.py runserver 127.0.0.1:9011
服务启动后访问:http://127.0.0.1:9011
用户数据目录
通过 PyPI 安装后,用户数据和配置文件存储在以下位置:
- Windows:
%APPDATA%\Local\OddMeta\OddMinutes或C:\Users\<用户名>\AppData\Local\OddMeta\OddMinutes - Linux:
~/.local/share/OddMeta/OddMinutes - macOS:
~/Library/Application Support/OddMeta/OddMinutes
该目录包含:
.env- 环境变量配置文件db.sqlite3- SQLite 数据库文件media/- 音频文件存储目录logs/- 日志文件目录
配置说明
| 环境变量 | 默认值 | 使用模块 | 说明 |
|---|---|---|---|
DJANGO_SECRET_KEY |
django-insecure-change-me | 全局 | Django 安全密钥 |
DEBUG |
True | 全局 | 调试模式 |
ALLOWED_HOSTS |
localhost,127.0.0.1 | 全局 | 允许的主机 |
ODDASR_HOST |
127.0.0.1 | transcription | OddASR 服务主机 |
ODDASR_PORT |
9002 | transcription | OddASR 服务端口 |
OLLAMA_HOST |
http://localhost:11434 | summary | Ollama 服务地址 |
AUDIO_SAMPLE_RATE |
16000 | recording | 音频采样率 |
AUDIO_CHANNELS |
1 | recording | 音频通道数 |
AUDIO_CHUNK_DURATION |
5 | recording | 音频块持续时间(秒) |
AUTO_CLEANUP_DAYS |
7 | meetings | 自动清理天数 |
MAX_AUDIO_FILE_SIZE |
500 | 全局 | 最大音频文件大小(MB) |
ODDMINUTES_DATA_DIR |
自动检测 | 全局 | 开发模式数据目录 |
API 接口
会议管理 /api/meetings/
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/meetings/ |
获取会议列表 |
| POST | /api/meetings/ |
创建新会议 |
| GET | /api/meetings/{id}/ |
获取会议详情 |
| PUT/PATCH | /api/meetings/{id}/ |
更新会议信息 |
| DELETE | /api/meetings/{id}/ |
删除会议 |
| POST | /api/meetings/{id}/export/ |
导出会议(支持 txt/md/json) |
| POST | /api/meetings/search/ |
搜索转录内容 |
| POST | /api/meetings/cleanup/ |
清理旧会议 |
| PATCH | /api/meetings/transcript-segments/{id}/ |
更新转录片段 |
录音控制 /api/recording/
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/recording/start/ |
开始录音 |
| POST | /api/recording/pause/ |
暂停录音 |
| POST | /api/recording/resume/ |
恢复录音 |
| POST | /api/recording/stop/ |
停止录音 |
| GET | /api/recording/status/ |
获取录音状态 |
| POST | /api/recording/upload-chunk/ |
上传音频块 |
转录服务 /api/transcription/
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/transcription/transcribe/ |
转录音频文件 |
| POST | /api/transcription/retranscribe/ |
重新转录 |
| POST | /api/transcription/upload/ |
上传音频并转录 |
| GET | /api/transcription/status/{id}/ |
获取转录任务状态 |
| GET/POST | /api/transcription/config/ |
获取/设置转录配置 |
| GET | /api/transcription/oddasr-status/ |
检查 OddASR 服务状态 |
AI 摘要 /api/summary/
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/summary/generate/ |
生成会议摘要 |
| GET | /api/summary/status/{id}/ |
获取摘要状态 |
| POST | /api/summary/regenerate/ |
重新生成摘要 |
| GET/POST | /api/summary/templates/ |
获取/创建摘要模板 |
| GET/POST/PUT/DELETE | /api/summary/model-config/ |
AI 模型配置管理 |
| GET/POST | /api/summary/language-preferences/ |
语言偏好设置 |
| POST | /api/summary/stream/ |
流式生成摘要(SSE) |
系统设置 /api/system/
| 方法 | 路径 | 说明 |
|---|---|---|
| GET/POST | /api/system/settings/ |
获取/更新系统设置 |
| GET | /api/system/check-updates/ |
检查更新 |
| GET | /api/system/notifications/ |
获取通知列表 |
| POST | /api/system/notifications/read/ |
标记通知为已读 |
| POST | /api/system/notifications/send/ |
发送系统通知 |
| POST | /api/system/onboarding/complete/ |
完成首次引导 |
| GET | /api/system/info/ |
获取系统信息 |
WebSocket 接口
音频电平推送
- 路径:
ws://host:port/ws/recording/level/ - 用途: 实时推送麦克风和系统音频电平数据
- 推送频率: 10 次/秒
推送数据格式:
{
"mic_level": 0.1234,
"system_level": 0.0567,
"mixed_level": 0.1234,
"peak_mic": 0.2345,
"peak_system": 0.0890,
"status": "recording",
"duration": 45.5,
"agc": {
"enabled": true,
"mic_gain": 1.5,
"system_gain": 1.0,
"target_rms": 8000
}
}
实时转录
- 路径:
ws://host:port/ws/transcription/realtime/ - 用途: 实时音频转录
数据流详解
完整流程示例
-
创建会议 → POST
/api/meetings/→ 创建 Meeting 记录(status=recording) -
开始录音 → POST
/api/recording/start/- 选择麦克风设备和可选的系统音频设备
- 前端使用 MediaRecorder API 采集音频(webm/opus 格式)
- 实时计算音频电平,通过 WebSocket 推送到前端
-
录音过程
- 前端麦克风采集(48kHz, 单声道),使用 webm/opus 格式
- 音频块通过 POST
/api/recording/upload-chunk/上传到后端 - 后端处理 webm 分片合并(跳过重复的 EBML 头)
- 录音状态存储在数据库(RecordingSession 模型)
-
停止录音 → POST
/api/recording/stop/- 进入 "stopping" 宽限期(6秒),处理迟到的音频分片
- 宽限期结束后,合并缓冲区数据
- 保存到
media/audio/meetings/{date}/{filename}.webm - 自动转换为 MP3 格式(
audio_utils.convert_audio_to_mp3) - 自动触发 ASR 转录
- 更新 Meeting 状态为 completed
-
转录音频 → POST
/api/transcription/transcribe/- 创建 TranscriptionJob(status=processing)
- 后台线程调用 OddASR 客户端
- 请求格式:
model=oddasr2_paraformer,response_format=verbose_json,timestamp_granularities[]=word - OddASR 返回包含
segments和words的 JSON 数据
-
保存转录结果
- 解析 segments,每个 segment 包含 text、speaker、start、end、words
- 创建 TranscriptSegment 记录,words 字段存储字级时间戳
- 更新 Meeting 状态为 completed
-
生成摘要 → POST
/api/summary/generate/- 创建 SummaryProcess(status=processing)
- 合并所有转录文本
- 按 chunk_size=5000、overlap=1000 分段
- 每段调用 LLM(默认 Ollama/Qwen3-8B)生成结构化摘要
- 聚合所有 chunk 的摘要结果
-
结构化摘要格式
{ "MeetingName": "会议名称", "People": {"title": "参会人员", "blocks": [...]}, "SessionSummary": {"title": "会议概要", "blocks": [...]}, "CriticalDeadlines": {"title": "关键截止日期", "blocks": [...]}, "KeyItemsDecisions": {"title": "关键事项与决策", "blocks": [...]}, "ImmediateActionItems": {"title": "立即行动项", "blocks": [...]}, "NextSteps": {"title": "后续步骤", "blocks": [...]}, "MeetingNotes": {"meeting_name": "...", "sections": [...]} }
-
前端渲染
- 转录内容按 speaker 合并显示
- 每个 word 带有 data-start/data-end 属性(音字联动)
- 音频播放时自动高亮当前 word 和 segment
- 点击 word 跳转到对应时间点
数据库模型
核心模型关系
Meeting (会议)
├── transcriptions → TranscriptSegment[] (转录片段)
├── recording → RecordingSession (录音会话)
├── transcription_jobs → TranscriptionJob[] (转录任务)
├── summary_process → SummaryProcess (摘要处理)
└── exports → MeetingExport[] (导出记录)
TranscriptSegment (转录片段)
├── meeting → Meeting (所属会议)
└── words → JSON (字级时间戳数据)
SummaryProcess (摘要处理)
└── meeting → Meeting (所属会议)
RecordingSession (录音会话)
├── meeting → Meeting (所属会议)
└── checkpoints → RecordingCheckpoint[] (检查点)
关键字段说明
Meeting 模型
| 字段 | 类型 | 说明 |
|---|---|---|
| title | CharField | 会议标题 |
| status | CharField | 状态:recording/paused/completed/processing |
| audio_file | FileField | 音频文件路径 |
| audio_duration | FloatField | 音频时长(秒) |
| summary | JSONField | AI 摘要结果 |
| notes | TextField | 用户笔记 |
| language | CharField | 语言:auto/zh/en |
TranscriptSegment 模型
| 字段 | 类型 | 说明 |
|---|---|---|
| meeting | ForeignKey | 关联会议 |
| text | TextField | 转录文本 |
| speaker | CharField | 发言人 |
| audio_start_time | FloatField | 音频开始时间(秒) |
| audio_end_time | FloatField | 音频结束时间(秒) |
| duration | FloatField | 片段时长(秒) |
| is_final | BooleanField | 是否最终结果 |
| words | JSONField | 字级时间戳(OddASR verbose_json 返回) |
words 字段结构(字级时间戳)
[
{"word": "你好", "start": 0.5, "end": 0.8, "probability": 0.98},
{"word": "世界", "start": 0.9, "end": 1.2, "probability": 0.95}
]
前端功能
音字联动
前端实现了音频播放与文字的双向联动:
- 播放时高亮:音频播放到某个时间点,自动高亮对应的 word 和 segment
- 点击跳转:点击某个 word,音频跳转到对应时间点并开始播放
- 拖动同步:拖动音频进度条,自动滚动到对应的文字位置
转录编辑模式
支持对转录内容进行编辑:
- 编辑转录文本
- 修改发言人名称
- 批量保存修改
实时电平监测
通过 WebSocket 实时显示麦克风和系统音频的电平:
- 实时电平条显示
- AGC 增益信息提示
- 峰值监测
搜索功能
支持在所有会议的转录内容中进行全文搜索,返回匹配的会议列表和上下文。
主题系统
提供四种主题风格:
- modern-light - 现代亮色主题
- tech-dark - 科技暗色主题
- warm-office - 温暖办公主题
- focus-dark - 专注暗色主题
通知系统
- 桌面通知(页面不可见时)
- 通知历史记录(最多保存 200 条)
- 未读通知计数
- 标记已读/全部已读
技术栈
| 分类 | 技术 |
|---|---|
| 后端框架 | Django 5.0.7 |
| API 框架 | Django REST Framework 3.15.2 |
| WebSocket | Django Channels 4.1.0 |
| ASGI 服务器 | Daphne 4.1.2 |
| 数据库 | SQLite(默认) |
| 音频录制 | 前端 MediaRecorder API(webm/opus) |
| 音频处理 | pydub、numpy、scipy、ffmpeg-python |
| 语音识别 | OddASR(OpenAI 兼容) |
| AI 摘要 | pydantic-ai-slim(Ollama/OpenAI/Claude/Groq) |
| 前端 | HTML5、CSS3、JavaScript(原生) |
| 系统托盘 | pystray + pywin32(Windows) |
开发命令
# 启动开发服务器(使用 daphne,推荐)
python launcher.py
# 启动调试模式(显示控制台)
python launcher.py --debug
# 创建数据库迁移
python manage.py makemigrations
# 执行数据库迁移
python manage.py migrate
# 初始化默认数据(必需)
python manage.py init_defaults
# 创建超级用户
python manage.py createsuperuser
# 进入 Django Shell
python manage.py shell
# 运行测试
python manage.py test
系统托盘功能
Windows 系统托盘支持以下操作:
- 显示窗口 - 打开浏览器访问应用
- 新建会议 - 创建新的会议记录
- 开始录音 - 启动录音(自动创建会议)
- 暂停录音 - 暂停当前录音
- 恢复录音 - 恢复暂停的录音
- 停止录音 - 停止录音并处理音频
- 退出 - 停止服务并退出
注意事项
- OddASR 服务:使用前请确保 OddASR 服务已启动并正常运行
- FFmpeg:安装 pydub 前需确保 FFmpeg 已安装并添加到系统 PATH
- Ollama:使用本地 LLM 生成摘要时,需先
ollama pull qwen3:8b下载模型 - 音频设备权限:首次使用录音功能时需授予麦克风权限
- 系统音频录制:Windows 下录制系统音频需要启用立体声混音设备或使用虚拟音频线缆
- 数据目录:默认数据存储在用户目录,开发时可设置
ODDMINUTES_DATA_DIR指定项目根目录 - 开发模式:使用
python manage.py runserver时建议添加--noreload参数避免多进程状态问题
License
MIT License
Metadata
Release files for OddMinutes 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| oddminutes-0.1.6.tar.gz | 647.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| oddminutes-0.1.6-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.3 MB
Release files / oddminutes-0.1.6.tar.gz
| Download URL | oddminutes-0.1.6.tar.gz |
|---|---|
| Size | 647.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
188909534bd9411c16b5730ce79e91fa787a4aa515663e1a13b8989f8336f17c
|
|
BLAKE2b-256 checksum How to use checksums |
3df8964ad4bbd519e663b0aa607ec08bb4c6541cc65ebe2b43cd0e473fcfdfee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|
Release files / oddminutes-0.1.6-py3-none-any.whl
| Download URL | oddminutes-0.1.6-py3-none-any.whl |
|---|---|
| Size | 656.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
665ae71ad95141b6730023d63e797ed304e28c53a57f48aebde129a815cc0bbd
|
|
BLAKE2b-256 checksum How to use checksums |
b6a64910c050170371adb4f007ed6bec10fd98c8216e14a46e9f8788ecb9ea61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|