## v0.9.20260325_144654 ### Features - API Key Authentication System - Job Worker System - V2 Backup Versioning ### Bug Fixes - get_processor_results_by_job column mapping Co-authored-by: OpenCode
13 KiB
13 KiB
Momentry JSON 輸出檔案規範
| 項目 | 內容 |
|---|---|
| 建立者 | Warren |
| 建立時間 | 2026-03-16 |
| 文件版本 | V1.0 |
版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|---|---|---|---|---|
| V1.0 | 2026-03-16 | 創建文件 | Warren | OpenCode / MiniMax M2.5 |
本文檔定義 Momentry Core 系統中所有 JSON 輸出檔案的結構、命名規範與儲存位置。
1. 輸出檔案總覽
1.1 檔案類型
| 類型 | 前綴 | 說明 | 狀態 |
|---|---|---|---|
| Probe | {uuid}.probe.json |
影片元數據 | ✅ 已實作 |
| ASR | {uuid}.asr.json |
語音識別結果 | ✅ 已實作 |
| ASRx | {uuid}.asrx.json |
說話者分離 | 🔜 規劃中 |
| OCR | {uuid}.ocr.json |
文字辨識結果 | 🔜 規劃中 |
| YOLO | {uuid}.yolo.json |
物件偵測結果 | 🔜 規劃中 |
| Face | {uuid}.face.json |
人臉偵測結果 | 🔜 規劃中 |
| Pose | {uuid}.pose.json |
姿態估計結果 | 🔜 規劃中 |
| Thumbnail | {uuid}/thumb_XXX.jpg |
縮圖檔案 | ✅ 已實作 |
1.2 命名規範
{UUID}.{類型}.json
範例:
1636719dc31f78ac.probe.json - 影片探測結果
1636719dc31f78ac.asr.json - 語音識別結果
1636719dc31f78ac.ocr.json - 文字辨識結果
- UUID: 16 字元,基於檔案路徑計算
- 類型: 小寫 snake_case
- 副檔名:
.json
2. 輸出目錄結構
2.1 預設輸出位置
momentry_core_0.1/
├── {uuid}.probe.json # 影片探測
├── {uuid}.asr.json # 語音識別
├── {uuid}.asrx.json # 說話者分離
├── {uuid}.ocr.json # 文字辨識
├── {uuid}.yolo.json # 物件偵測
├── {uuid}.face.json # 人臉偵測
├── {uuid}.pose.json # 姿態估計
└── thumbnails/
└── {uuid}/
├── thumb_000.jpg
├── thumb_001.jpg
└── ...
2.2 儲存策略
| 資料類型 | 儲存位置 | 說明 |
|---|---|---|
| JSON 檔案 | 專案根目錄 | 方便快速存取 |
| 縮圖 | thumbnails/{uuid}/ | 分離儲存 |
| 資料庫 | PostgreSQL | 長期儲存 |
3. JSON 結構定義
3.1 Probe (影片探測)
檔案: {uuid}.probe.json
{
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_type": "video",
"width": 1920,
"height": 1080,
"r_frame_rate": "60000/1001",
"duration": "6879.329524",
"sample_rate": null,
"channels": null
},
{
"index": 1,
"codec_name": "aac",
"codec_type": "audio",
"width": null,
"height": null,
"r_frame_rate": "0/0",
"duration": "6879.245333",
"sample_rate": "48000",
"channels": 2
}
],
"format": {
"filename": "/path/to/video.mov",
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"duration": "6879.329524",
"size": "2361629896",
"bit_rate": "2748000"
}
}
欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
streams |
Array | 媒體串流陣列 |
streams[].index |
Integer | 串流索引 |
streams[].codec_name |
String | 編碼名稱 |
streams[].codec_type |
String | 串流類型 (video/audio) |
streams[].width |
Integer | 寬度 (video) |
streams[].height |
Integer | 高度 (video) |
streams[].r_frame_rate |
String | 幀率 |
streams[].duration |
String | 持續時間 (秒) |
streams[].sample_rate |
String | 採樣率 (audio) |
streams[].channels |
Integer | 聲道數 (audio) |
format |
Object | 檔案格式資訊 |
format.filename |
String | 原始檔案路徑 |
format.format_name |
String | 格式名稱 |
format.duration |
String | 總時長 (秒) |
format.size |
String | 檔案大小 (bytes) |
format.bit_rate |
String | 位元率 |
3.2 ASR (語音識別)
檔案: {uuid}.asr.json
{
"language": "en",
"language_probability": 0.9945855736732483,
"segments": [
{
"start": 0.0,
"end": 19.04,
"text": "Hello and welcome to the old-time movie show."
},
{
"start": 19.04,
"end": 25.44,
"text": "Today we are featuring the 1963 comedy mystery film Charade."
}
]
}
欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
language |
String | 偵測語言代碼 (ISO 639-1) |
language_probability |
Float | 語言偵測機率 (0-1) |
segments |
Array | 語音分段陣列 |
segments[].start |
Float | 開始時間 (秒) |
segments[].end |
Float | 結束時間 (秒) |
segments[].text |
String | 識別文字 |
3.3 ASRx (說話者分離)
檔案: {uuid}.asrx.json
{
"language": "en",
"language_probability": 0.95,
"segments": [
{
"start": 0.0,
"end": 19.04,
"text": "Hello and welcome to the old-time movie show.",
"speaker_id": "SPEAKER_00",
"speaker_embedding": [0.123, -0.456, ...]
},
{
"start": 19.04,
"end": 25.44,
"text": "Today we are featuring the 1963 comedy mystery film Charade.",
"speaker_id": "SPEAKER_01",
"speaker_embedding": [0.789, -0.123, ...]
}
]
}
欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
language |
String | 偵測語言代碼 |
language_probability |
Float | 語言偵測機率 |
segments |
Array | 語音分段陣列 |
segments[].start |
Float | 開始時間 (秒) |
segments[].end |
Float | 結束時間 (秒) |
segments[].text |
String | 識別文字 |
segments[].speaker_id |
String | 說話者 ID |
segments[].speaker_embedding |
Array | 說話者嵌入向量 (可選) |
3.4 OCR (文字辨識)
檔案: {uuid}.ocr.json
{
"segments": [
{
"start": 10.5,
"end": 12.3,
"text": "EXAMPLE TEXT",
"boxes": [
{
"x1": 100,
"y1": 50,
"x2": 400,
"y2": 100
}
],
"confidence": 0.95
}
]
}
欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
segments |
Array | OCR 分段陣列 |
segments[].start |
Float | 開始時間 (秒) |
segments[].end |
Float | 結束時間 (秒) |
segments[].text |
String | 辨識文字 |
segments[].boxes |
Array | 文字邊界框陣列 |
segments[].boxes[].x1 |
Integer | 左上 X 座標 |
segments[].boxes[].y1 |
Integer | 左上 Y 座標 |
segments[].boxes[].x2 |
Integer | 右下 X 座標 |
segments[].boxes[].y2 |
Integer | 右下 Y 座標 |
segments[].confidence |
Float | 辨識信心度 |
3.5 YOLO (物件偵測)
檔案: {uuid}.yolo.json
{
"segments": [
{
"start": 0.0,
"end": 1.0,
"objects": [
{
"class": "person",
"confidence": 0.92,
"box": {
"x1": 150,
"y1": 200,
"x2": 400,
"y2": 800
}
},
{
"class": "car",
"confidence": 0.87,
"box": {
"x1": 800,
"y1": 400,
"x2": 1200,
"y2": 700
}
}
]
}
]
}
欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
segments |
Array | 時間分段陣列 |
segments[].start |
Float | 開始時間 (秒) |
segments[].end |
Float | 結束時間 (秒) |
segments[].objects |
Array | 偵測物件陣列 |
segments[].objects[].class |
String | 物件類別 |
segments[].objects[].confidence |
Float | 偵測信心度 |
segments[].objects[].box |
Object | 邊界框 |
3.6 Face (人臉偵測)
檔案: {uuid}.face.json
{
"segments": [
{
"start": 0.0,
"end": 1.0,
"faces": [
{
"face_id": "face_001",
"box": {
"x1": 100,
"y1": 50,
"x2": 300,
"y2": 350
},
"embedding": [0.123, -0.456, ...],
"emotion": "happy",
"age": 35,
"gender": "female"
}
]
}
]
}
欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
segments |
Array | 時間分段陣列 |
segments[].start |
Float | 開始時間 (秒) |
segments[].end |
Float | 結束時間 (秒) |
segments[].faces |
Array | 人臉陣列 |
segments[].faces[].face_id |
String | 人臉 ID |
segments[].faces[].box |
Object | 邊界框 |
segments[].faces[].embedding |
Array | 人臉嵌入向量 |
segments[].faces[].emotion |
String | 情緒分類 (可選) |
segments[].faces[].age |
Integer | 年齡估計 (可選) |
segments[].faces[].gender |
String | 性別估計 (可選) |
3.7 Pose (姿態估計)
檔案: {uuid}.pose.json
{
"segments": [
{
"start": 0.0,
"end": 1.0,
"poses": [
{
"person_id": "person_001",
"keypoints": {
"nose": {"x": 320, "y": 120, "confidence": 0.98},
"left_eye": {"x": 335, "y": 110, "confidence": 0.95},
"right_eye": {"x": 305, "y": 110, "confidence": 0.93},
"left_shoulder": {"x": 280, "y": 180, "confidence": 0.91},
"right_shoulder": {"x": 360, "y": 180, "confidence": 0.89}
},
"confidence": 0.92
}
]
}
]
}
欄位說明:
| 欄位 | 類型 | 說明 |
|---|---|---|
segments |
Array | 時間分段陣列 |
segments[].start |
Float | 開始時間 (秒) |
segments[].end |
Float | 結束時間 (秒) |
segments[].poses |
Array | 姿態陣列 |
segments[].poses[].person_id |
String | 人員 ID |
segments[].poses[].keypoints |
Object | 關鍵點 |
segments[].poses[].keypoints.{name} |
Object | 各關鍵點 |
segments[].poses[].keypoints.{name}.x |
Integer | X 座標 |
segments[].poses[].keypoints.{name}.y |
Integer | Y 座標 |
segments[].poses[].keypoints.{name}.confidence |
Float | 信心度 |
segments[].poses[].confidence |
Float | 整體信心度 |
4. 處理流程
4.1 處理管線
影片檔案
│
▼
┌─────────────────┐
│ 1. Register │ 建立 UUID,註冊影片
└────────┬────────┘
│
┌────▼────┐
│ 2. Probe │ ffprobe 擷取元數據
└────┬────┘
│ {uuid}.probe.json
┌────▼─────┐
│ 3. ASR │ faster-whisper 語音識別
└────┬─────┘
│ {uuid}.asr.json
┌────▼──────┐
│ 4. ASRx │ 說話者分離 (pyannote)
└────┬──────┘
│ {uuid}.asrx.json
┌────▼────┐
│ 5. OCR │ Tesseract 文字辨識
└────┬────┘
│ {uuid}.ocr.json
┌────▼────┐
│ 6. YOLO │ 物件偵測
└────┬────┘
│ {uuid}.yolo.json
┌────▼────┐
│ 7. Face │ 人臉偵測
└────┬────┘
│ {uuid}.face.json
┌────▼────┐
│ 8. Pose │ 姿態估計
└────┬────┘
│ {uuid}.pose.json
┌────▼──────┐
│ 9. Chunk │ 轉換為資料庫 chunks
└───────────┘
4.2 失敗處理
| 階段 | 失敗時 | 處理 |
|---|---|---|
| Probe | 無法讀取影片 | 終止流程,輸出錯誤 |
| ASR | 無音軌 | 產生空 segments,繼續流程 |
| OCR/YOLO/Face/Pose | 處理失敗 | 跳過該階段,記錄日誌 |
5. 資料庫儲存
5.1 Chunk 結構
CREATE TABLE chunks (
id BIGSERIAL PRIMARY KEY,
uuid VARCHAR(16) NOT NULL,
chunk_id VARCHAR(64) NOT NULL,
chunk_index INTEGER NOT NULL,
chunk_type VARCHAR(32) NOT NULL,
start_time DOUBLE PRECISION NOT NULL,
end_time DOUBLE PRECISION NOT NULL,
content JSONB NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(uuid, chunk_id)
);
5.2 轉換範例
// ASR → Chunk (Sentence)
for (i, seg) in asr_result.segments.iter().enumerate() {
let chunk = Chunk::new(
uuid.clone(),
i as u32,
ChunkType::Sentence,
seg.start,
seg.end,
serde_json::json!({"text": seg.text}),
);
db.store_chunk(&chunk).await?;
}
6. 版本歷史
| 版本 | 日期 | 變更 |
|---|---|---|
| 1.0.0 | 2026-03-16 | 初始版本 |
7. 相關文件
- RUST_DEVELOPMENT.md - Rust 開發規範
- AGENTS.md - 開發規範
- monitor_config.yaml - 監控配置