feat: Phase 1 handover - schema migration, correction mechanism, API fixes

Schema changes: dev.chunks->dev.chunk, remove old_chunk_id/chunk_index
Correction: asr-1.json format, generate/apply scripts
API: 37/37 endpoints fixed and tested
Docs: HANDOVER_V2.0.md for M4
This commit is contained in:
Accusys
2026-05-11 07:03:22 +08:00
parent ef894a44ad
commit 39ba5ddf76
147 changed files with 19843 additions and 3053 deletions
@@ -0,0 +1,214 @@
---
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Momentry Core Dev API 參考文件"
date: "2026-05-06"
version: "V1.1"
status: "deprecated"
owner: "Warren"
---
> ⚠️ **此文件為 V3.x 歷史參考,含已移除的路由。**
> 請改用 `API_DICTIONARY_V1.0.0.md`root)取得當前準確的 53 條 API 路由。
created_by: "OpenCode"
tags:
- "api"
- "reference"
- "dev"
- "v1.1"
- "restful"
related_documents:
- "MOMENTRY_CORE_API_V1.0.0.md"
- "RELEASE/RELEASE_API_REFERENCE_v1.0.0.md"
---
# Momentry Core Dev API 參考文件
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-05-06 |
| 文件版本 | V1.1 |
| Base URL | `http://localhost:3003` |
| 認證方式 | Header `X-API-Key`(部分端點需要) |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.1 | 2026-05-06 | 從程式碼實際路由重新產生 53 端點清單 | OpenCode |
| V1.0 | 2026-04-30 | 原始文件,含多個不存在之端點 | OpenCode |
---
## 認證
- **Header**: `X-API-Key: <your_api_key>`
- 目前 `/api/v1/auth/login` 回傳固定 demo Key: `muser_test_001`
- Protected routes 透過 `api_key_validation` middleware 驗證
- Public routes(免 Key: `/health`, `/health/detailed`, `/api/v1/auth/login`
---
## 端點列表
總計 **53 個註冊路由**(另有 1 個定義但未掛載)。
### 1. 系統與認證(System & Auth
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 1 | GET | `/health` | 基本健康檢查(回傳 status/version/uptime | ❌ |
| 2 | GET | `/health/detailed` | 詳細健康狀態(含 PG/Redis/Qdrant/MongoDB 各別延遲) | ❌ |
| 3 | POST | `/api/v1/auth/login` | 登入(固定 demo/demo,回傳 API Key | ❌ |
| 4 | POST | `/api/v1/auth/logout` | 登出 | ✅ |
### 2. 檔案管理(File Management
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 5 | GET | `/api/v1/files` | 檔案列表(支援分頁、status、q、uuid 過濾) | ✅ |
| 6 | GET | `/api/v1/file/:file_uuid` | 檔案詳細資訊(含 probe_json、metadata | ✅ |
| 7 | POST | `/api/v1/files/register` | 從磁碟註冊新檔案(支援 pattern 批次註冊) | ✅ |
| 8 | POST | `/api/v1/unregister` | 取消註冊檔案 | ✅ |
| 9 | GET | `/api/v1/files/scan` | 掃描 SFTPGo demo 目錄中的新檔案 | ✅ |
| 10 | GET | `/api/v1/file/:file_uuid/probe` | 取得/快取 ffprobe 資訊 | ✅ |
| 11 | POST | `/api/v1/file/:file_uuid/process` | 啟動處理 pipeline(建立 monitor job | ✅ |
| 12 | GET | `/api/v1/file/:file_uuid/chunks` | 列出 pre_chunks | ✅ |
| 13 | GET | `/api/v1/progress/:uuid` | 即時處理進度(來自 Redis PubSub | ✅ |
| 14 | GET | `/api/v1/jobs` | 任務列表(支援分頁、status 過濾) | ✅ |
### 3. 搜尋(Search
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 15 | POST | `/api/v1/search/visual` | 視覺搜尋 | ✅ |
| 16 | POST | `/api/v1/search/visual/class` | 依物件類別過濾搜尋 | ✅ |
| 17 | POST | `/api/v1/search/visual/density` | 依視覺密度搜尋 | ✅ |
| 18 | POST | `/api/v1/search/visual/stats` | 視覺統計資料 | ✅ |
| 19 | POST | `/api/v1/search/visual/combination` | 視覺組合搜尋(多條件) | ✅ |
| 20 | POST | `/api/v1/search/smart` | 智慧搜尋(語意向量) | ✅ |
| 21 | POST | `/api/v1/search/universal` | 通用搜尋 | ✅ |
| 22 | POST | `/api/v1/search/frames` | 影格搜尋 | ✅ |
### 4. 身份管理(Identity
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 23 | GET | `/api/v1/identities` | 身份列表 | ✅ |
| 24 | POST | `/api/v1/identity` | 建立身份(從 face.json 建立參考向量) | ✅ |
| 25 | GET | `/api/v1/identity/:identity_uuid` | 身份詳細資訊 | ✅ |
| 26 | DELETE | `/api/v1/identity/:identity_uuid` | 刪除身份 | ✅ |
| 27 | GET | `/api/v1/identity/:identity_uuid/files` | 該身份出現的所有檔案 | ✅ |
| 28 | GET | `/api/v1/identity/:identity_uuid/chunks` | 該身份的時間軸片段 | ✅ |
| 29 | POST | `/api/v1/identity/:identity_uuid/bind` | 綁定信號至身份 | ✅ |
| 30 | POST | `/api/v1/identity/:identity_uuid/unbind` | 解除綁定 | ✅ |
| 31 | POST | `/api/v1/identity/:from_uuid/mergeinto` | 合併身份(將 from 合併至目標) | ✅ |
### 5. 臉部(Face
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 32 | GET | `/api/v1/faces/candidates` | 臉部候選列表(未綁定者) | ✅ |
### 6. 媒體串流(Media
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 33 | GET | `/api/v1/file/:file_uuid/video` | 影片串流 | ✅ |
| 34 | GET | `/api/v1/file/:file_uuid/video/bbox` | 含 Bounding Box 的影片串流 | ✅ |
| 35 | GET | `/api/v1/file/:file_uuid/trace/:trace_id/video` | 特定 trace 的影片片段 | ✅ |
| 36 | GET | `/api/v1/file/:file_uuid/thumbnail` | 影片縮圖 | ✅ |
### 7. 檔案身份關聯(File-Identity
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 37 | GET | `/api/v1/file/:file_uuid/identities` | 該檔案的所有關聯身份 | ✅ |
### 8. Agent
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 38 | POST | `/api/v1/agents/translate` | 翻譯 Agent | ✅ |
| 39 | POST | `/api/v1/agents/identity/analyze` | 身份分析 Agent | ✅ |
| 40 | POST | `/api/v1/agents/identity/suggest` | 身份合併建議 | ✅ |
| 41 | GET | `/api/v1/agents/identity/status` | 身份 Agent 狀態 | ✅ |
| 42 | POST | `/api/v1/agents/suggest/clustering` | 聚類建議 | ✅ |
| 43 | POST | `/api/v1/agents/suggest/merge` | 合併建議 | ✅ |
| 44 | POST | `/api/v1/agents/5w1h/analyze` | 5W1H 分析 | ✅ |
| 45 | POST | `/api/v1/agents/5w1h/batch` | 5W1H 批量分析 | ✅ |
| 46 | GET | `/api/v1/agents/5w1h/status` | 5W1H 狀態 | ✅ |
### 9. 資源管理(Resource
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 47 | POST | `/api/v1/resource/register` | 註冊運算資源 | ✅ |
| 48 | POST | `/api/v1/resource/heartbeat` | 資源心跳回報 | ✅ |
| 49 | GET | `/api/v1/resources` | 資源列表 | ✅ |
### 10. 統計與設定(Stats & Config
| # | Method | Path | 說明 | 需 Key |
|---|--------|------|------|--------|
| 50 | GET | `/api/v1/stats/ingest` | 攝取統計(video/chunk 計數) | ✅ |
| 51 | GET | `/api/v1/stats/sftpgo` | SFTPGo 使用者狀態 | ✅ |
| 52 | GET | `/api/v1/stats/inference` | 推理叢集健康狀態 | ✅ |
| 53 | POST | `/api/v1/config/cache` | 切換快取開關 | ✅ |
---
## 未掛載的端點(定義了 handler 但未註冊路由)
| Handler | 位置 | 說明 |
|---------|------|------|
| `POST /api/v1/file/:file_uuid/face_trace/sortby` | `trace_agent_api.rs` | 定義了 `trace_agent_routes()` 但從未被 `server.rs` merge |
---
## 程式碼中存在 handler 但未註冊路由的端點
下列 handler 有實作但**沒有對應的 `.route()` 呼叫**,無法透過 HTTP 存取:
- `GET /api/v1/assets/:uuid/status``get_asset_status`
- `GET /api/v1/jobs/:job_id``get_job`
- `GET /api/v1/rules/:rule/status``get_rule_status`
- `GET /api/v1/videos/:uuid/details``video_details`
- `DELETE /api/v1/videos/:uuid``delete_video`
- `POST /api/v1/search``search`(語意搜尋)
- `POST /api/v1/search/hybrid``hybrid_search`
- `POST /api/v1/search/bm25``search_bm25`
- `GET /api/v1/lookup``lookup`
- `POST /api/v1/search/smart``search_smart`(server.rs 版,實際註冊的是 search.rs 版)
---
## 與 V1.0 文件的差異
V1.0 文件(`MOMENTRY_CORE_API_V1.0.0.md`)宣稱的端點中有以下**不存在於實際程式碼**:
| 文件宣稱 | 實際狀況 |
|----------|---------|
| `DELETE /api/v1/videos/:uuid` | handler 存在但未註冊路由 |
| `POST /api/v1/search` | handler 存在但未註冊路由 |
| `POST /api/v1/search/hybrid` | handler 存在但未註冊路由 |
| `POST /api/v1/assets/:uuid/process` | 實際是 `POST /api/v1/file/:file_uuid/process` |
| `GET /api/v1/files/:uuid/snapshots` | 不存在 |
| `POST /api/v1/files/:uuid/snapshots/migrate` | 不存在 |
| `GET /api/v1/face/list` | 不存在 |
| `POST /api/v1/face/recognize` | 不存在 |
---
## 路徑命名慣例
| 資源 | 路由格式 | 參數 |
|------|---------|------|
| 檔案 | `/api/v1/file/:file_uuid` | 32 碼 hex string |
| 身份 | `/api/v1/identity/:identity_uuid` | UUID v4 |
| 資源 | `/api/v1/resource/...` | - |
注意路徑使用**單數**`file`, `identity`),與 RELEASE 文件的 `files`, `identities` 不同。
@@ -0,0 +1,145 @@
# Physical Scene Analysis v1.0.0
將 CUT processor 從「場景切換偵測」升級為「場景物理特徵分析」。
## 流程
```
CUT (現有) Physical Analysis (新增)
┌──────────────┐ ┌──────────────────────┐
│ scenedetect │ ──→ │ ffmpeg signalstats │
│ frame_range │ │ ffmpeg ebur128 │
│ scene_050 │ │ ffmpeg tblend │
│ scene_051 │ │ 逐 scene 計算特徵 │
└──────────────┘ └──────────┬───────────┘
┌──────────────────┐
│ scene_050.json │
│ scene_051.json │ ← 原 JSON + 物理特徵
└──────────────────┘
```
## API
### POST /api/v1/file/:file_uuid/physical/analyze
對已註冊的影片執行物理特徵分析。
#### Request
```json
{
"features": ["luminance", "loudness", "silence", "motion", "color"],
"bin_scenes": true,
"time_range": [0, 5954]
}
```
| 參數 | 類型 | 預設 | 說明 |
|------|------|------|------|
| `features` | string[] | 全部 | 指定要分析的特徵 |
| `bin_scenes` | bool | true | 以 scene 為 bucketvs 固定時間間隔) |
| `time_range` | [float,float] | 全片 | 分析區間 |
#### Response
```json
{
"file_uuid": "3abeee81...",
"duration": 5954,
"feature_count": 1130,
"features": {
"luminance": {
"unit": "Y_channel_mean",
"global_avg": 45.2,
"global_min": 16.0,
"global_max": 128.0,
"data": [
{"scene": 1, "t_start": 0, "t_end": 34.68, "value": 51.3, "contrast": 23.7},
{"scene": 2, "t_start": 34.72, "t_end": 38.92, "value": 33.2, "contrast": 12.3}
]
},
"loudness": {
"unit": "LUFS",
"global_avg": -23.1,
"global_max": -10.3,
"data": [
{"scene": 1, "t_start": 0, "t_end": 34.68, "value": -28.5, "peak": -16.2},
{"scene": 2, "t_start": 34.72, "t_end": 38.92, "value": -18.5, "peak": -12.1}
]
},
"silence": {
"data": [
{"scene": 1, "count": 1, "total_duration": 29.9, "ratio": 0.86},
{"scene": 2, "count": 0, "total_duration": 0, "ratio": 0}
]
},
"motion": {
"unit": "frame_diff_mean",
"data": [
{"scene": 1, "value": 0.12},
{"scene": 2, "value": 0.45}
]
},
"color": {
"unit": "dominant_temp",
"data": [
{"scene": 1, "temp": 5600, "dominant": "warm"},
{"scene": 2, "temp": 3200, "dominant": "cool"}
]
}
},
"anomalies": [
{"scene": 1, "type": "extreme_silence", "value": 0.86, "description": "片頭靜音 86%"},
{"scene": 8, "type": "black_frame", "value": 16.0, "description": "fade-to-black 轉場"}
]
}
```
## 實作
### 單一 ffmpeg 命令(全片)
```bash
ffmpeg -i input.mp4 \
-vf "signalstats,select='gt(scene,0.3)',metadata=print" \
-af "ebur128=framelog=verbose" \
-f null - 2>&1 | python3 scripts/parse_physical_features.py
```
### 逐 scene 分析(搭配 CUT 輸出)
CUT 輸出已知 scene boundaries,可以只對關鍵幀算特徵:
```bash
# 對每個 scene 取 middle frame 算亮度
ffmpeg -i input.mp4 -vf "select='eq(n,1366)+eq(n,1607)'" \
-vsync 0 -f image2 /tmp/frames/%d.jpg
```
### Post-Processing Pipeline 整合
`processor.rs` 中新增一個 processor type `physical`
```rust
ProcessorType::Physical => {
let output = physical_analysis(uuid, &video_path).await?;
db.store_physical_features(uuid, &output).await?;
}
```
### DB Schema
```sql
CREATE TABLE dev.physical_features (
id BIGSERIAL PRIMARY KEY,
file_uuid VARCHAR(32) NOT NULL,
scene_number INT NOT NULL,
feature_type VARCHAR(20) NOT NULL, -- luminance | loudness | silence | motion | color
value FLOAT NOT NULL,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_physical_file ON dev.physical_features(file_uuid);
```