docs: file_uuid generation rules for M4

This commit is contained in:
Accusys
2026-05-17 02:26:09 +08:00
parent 3a6c186575
commit eec2eea880
79 changed files with 23293 additions and 0 deletions

View File

@@ -0,0 +1,230 @@
# Momentry Core API 存取指南
| 項目 | 內容 |
|------|------|
| 版本 | V1.3 |
| 日期 | 2026-03-25 |
| 用途 | API 存取方式、端點與整合指南 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.3 | 2026-03-25 | 更新: n8n 搜尋回傳 `file_path` 取代 `media_url`,新增 API Key 驗證說明 | OpenCode | deepseek-reasoner |
| V1.2 | 2026-03-24 | 更新網址與服務列表 | Warren | OpenCode / MiniMax M2.5 |
| V1.1 | 2026-03-23 | 初始版本 | Warren | OpenCode / MiniMax M2.5 |
---
## 基本網址
| 環境 | URL | 說明 |
|------|-----|------|
| **本地開發** | `http://localhost:3002` | 直接訪問 API繞過反向代理 |
| **外部訪問** | `https://api.momentry.ddns.net` | 通過 Caddy 反向代理訪問,需網路可達 |
### 何時使用哪個 URL
**使用 `localhost:3002`**
- 開發/測試環境
- 直接在伺服器上操作
- 當反向代理有問題時
**使用 `api.momentry.ddns.net`**
- n8n workflow 中呼叫 API
- 外部系統整合
- 生產環境
## 認證
所有 `/api/v1/*` 端點(除了健康檢查 `/health``/health/detailed`)都需要 API Key 認證。
請在請求標頭中加入:
```
X-API-Key: YOUR_API_KEY
```
**目前示範使用的 API Key**: `demo_api_key_12345`
> **注意**: 正式環境請使用安全的 API Key 管理機制,避免在客戶端暴露 API Key。
---
## 影片搜尋 API
### 語意搜尋
**端點:** `POST /api/v1/search`
**請求:**
```json
{
"query": "charade",
"limit": 5,
"uuid": "a1b10138a6bbb0cd"
}
```
| 欄位 | 類型 | 必填 | 說明 |
|------|------|------|------|
| `query` | 字串 | 是 | 搜尋文字 |
| `limit` | 整數 | 否 | 最大回傳結果數(預設 10 |
| `uuid` | 字串 | 否 | 依影片 UUID 過濾 |
**回應:**
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0006",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "fun plot twists, Woody Dialog and charming performances...",
"score": 0.526
}
],
"query": "charade"
}
```
---
### n8n 整合搜尋
**端點:** `POST /api/v1/n8n/search`
**請求:**
```json
{
"query": "charade",
"limit": 5
}
```
**回應:**
```json
{
"query": "charade",
"count": 5,
"hits": [
{
"id": "sentence_0006",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0006",
"text": "fun plot twists...",
"score": 0.526,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
}
]
}
```
> **注意**: API 現在返回 `file_path`(檔案系統路徑)而非 `media_url`(網頁 URL。如需在網頁中播放影片請將檔案路徑轉換為可訪問的 URL例如透過 SFTPGo 分享連結)。
---
## 影片管理 API
### 列出所有影片
**端點:** `GET /api/v1/videos`
### 查詢影片資訊
**端點:** `GET /api/v1/lookup?uuid={uuid}``GET /api/v1/lookup?path={path}`
### 取得處理進度
**端點:** `GET /api/v1/progress/{uuid}`
---
## 區塊資料結構
每個搜尋結果包含影片播放的時間資訊:
| 欄位 | 說明 |
|------|------|
| `uuid` | 影片識別碼 |
| `chunk_id` | 區塊唯一識別碼 |
| `chunk_type` | 類型:`sentence``cut``time_based` |
| `start_time` | 開始時間(秒) |
| `end_time` | 結束時間(秒) |
| `text` | 語音轉文字內容 |
| `score` | 相關性分數0-1 |
---
## 整合範例
### JavaScript/fetch
```javascript
const response = await fetch('http://localhost:3002/api/v1/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': 'YOUR_API_KEY' // 替換為實際的 API Key
},
body: JSON.stringify({ query: 'charade', limit: 5 })
});
const data = await response.json();
console.log(data.results);
```
### PHP/cURL
```php
$ch = curl_init('http://localhost:3002/api/v1/search');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'query' => 'charade',
'limit' => 5
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-API-Key: YOUR_API_KEY' // 替換為實際的 API Key
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
```
---
## 影片嵌入網址
> **重要**: API 現在返回 `file_path`(檔案系統路徑),而非直接可訪問的網址。您需要將檔案路徑轉換為 SFTPGo 分享連結才能嵌入影片。
**檔案路徑轉換為網址:**
- API 返回的 `file_path` 範例:`/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4`
- 對應的 SFTPGo 分享連結:`https://wp.momentry.ddns.net/demo/video.mp4`
- 轉換方式:移除 `/Users/accusys/momentry/var/sftpgo/data/` 前綴,將剩餘路徑附加到 `https://wp.momentry.ddns.net/`
**手動建立分享連結:**
1. 開啟 SFTPGo Web UI`http://localhost:8080`
2. 使用帳號 `demo` / 密碼 `demopassword123` 登入
3. 導航至 `Files` → 選擇影片檔案
4. 點擊 `Share``Create Link`
5. 複製產生的分享連結
使用搜尋結果中的 `start_time``end_time` 來嵌入影片片段。
---
## 服務列表
| 服務 | 網址 | 用途 |
|------|------|------|
| Momentry API | `http://localhost:3002` | 核心 API |
| SFTPGo | `http://localhost:8080` | 檔案儲存 |
| Qdrant | `http://localhost:6333` | 向量搜尋 |
| PostgreSQL | `localhost:5432` | 資料庫 |
---
## 示範影片
- **檔案:** `Old_Time_Movie_Show_-_Charade_1963.HD.mov`
- **UUID** `a1b10138a6bbb0cd`
- **長度:** 約 6879 秒(約 1.9 小時)
- **區塊數:** 3886 個(句子 + 場景 + 時間)

View File

@@ -0,0 +1,321 @@
# Momentry Core API 端點總覽
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-18 |
| 文件版本 | V1.3 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-18 | 創建文件 | OpenCode |
| V1.1 | 2026-03-23 | 更新端點與實際一致 | OpenCode |
| V1.2 | 2026-03-25 | 新增快取/刪除 API | OpenCode |
| V1.3 | 2026-03-26 | 更新API回應格式 (media_url→file_path) | OpenCode |
---
## Base URL
| 環境 | URL |
|------|-----|
| 本地 | `http://localhost:3002` |
| 外部 | `https://api.momentry.ddns.net` |
---
## 認證
除健康檢查端點外,所有 API 端點都需要 API Key。
### Header 方式
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/videos
```
### 響應
- `401 Unauthorized` - 缺少或無效的 API Key
- `200 OK` - 認證成功
### 取得 API Key
使用 CLI 建立:
```bash
./target/release/momentry api-key create "My API Key" --key-type user
```
---
## 端點列表
### 健康檢查(公開)
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/health` | 基本健康檢查 |
| GET | `/health/detailed` | 詳細健康檢查(含服務狀態) |
**範例**:
```bash
curl http://localhost:3002/health
# {"status":"ok","version":"0.1.0","uptime_ms":123456}
```
---
### 影片搜尋
| 方法 | 端點 | 說明 |
|------|------|------|
| POST | `/api/v1/search` | 語意搜尋(標準格式) |
| POST | `/api/v1/n8n/search` | 語意搜尋n8n 專用格式) |
| POST | `/api/v1/search/hybrid` | 混合搜尋 |
**標準搜尋** (`/api/v1/search`):
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"query": "test", "limit": 10}'
```
**n8n 格式搜尋** (`/api/v1/n8n/search`):
```bash
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"query": "test", "limit": 10}'
```
---
### 影片管理
| 方法 | 端點 | 說明 |
|------|------|------|
| POST | `/api/v1/register` | 註冊影片 |
| POST | `/api/v1/probe` | 探測影片資訊(不註冊) |
| GET | `/api/v1/videos` | 列出所有影片 |
| GET | `/api/v1/lookup` | 查詢影片資訊 |
| GET | `/api/v1/progress/:uuid` | 取得處理進度 |
**註冊影片**:
```bash
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"path": "/path/to/video.mp4"}'
```
**註冊回應範例**:
```json
{
"uuid": "a1b10138a6bbb0cd",
"video_id": 1,
"job_id": 10,
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"already_exists": false
}
```
**探測影片** (不註冊,只取得影片資訊):
```bash
curl -X POST http://localhost:3002/api/v1/probe \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"path": "./demo/video.mp4"}'
```
**Probe 回應範例**:
```json
{
"uuid": "a1b10138a6bbb0cd",
"file_name": "video.mp4",
"duration": 120.5,
"width": 1920,
"height": 1080,
"fps": 30.0,
"cached": false,
"format": {
"filename": "/path/to/video.mp4",
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"duration": "120.5",
"size": "12345678",
"bit_rate": "819200"
},
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_type": "video",
"width": 1920,
"height": 1080,
"r_frame_rate": "30/1",
"duration": "120.5"
}
]
}
```
**列出影片**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/videos
```
**查詢影片**:
```bash
curl -H "X-API-Key: your-api-key" "http://localhost:3002/api/v1/lookup?uuid=5dea6618a606e7c7"
```
**處理進度**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
```
---
### 工作管理
| 方法 | 端點 | 說明 |
|------|------|------|
| GET | `/api/v1/jobs` | 列出所有工作 |
| GET | `/api/v1/jobs/:uuid` | 取得指定工作的詳細資訊 |
**列出工作**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/jobs
```
**取得工作詳細資訊**:
```bash
curl -H "X-API-Key: your-api-key" http://localhost:3002/api/v1/jobs/a03485a40b2df2d3
```
---
### 系統管理
| 方法 | 端點 | 說明 |
|------|------|------|
| POST | `/api/v1/config/cache` | 切換快取功能(管理員) |
| POST | `/api/v1/unregister` | 刪除影片及其所有資料(管理員) |
**快取設定**:
```bash
curl -X POST http://localhost:3002/api/v1/config/cache \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"enabled": true}'
```
**刪除影片**:
```bash
curl -X POST http://localhost:3002/api/v1/unregister \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"uuid": "5dea6618a606e7c7"}'
```
---
## 端點對照表
| 功能 | n8n 使用 | WordPress 使用 | curl 測試 |
|------|-----------|----------------|------------|
| 健康檢查 | ✓ | ✓ | ✓ |
| 語意搜尋 | ✓ (n8n格式) | ✓ (標準格式) | ✓ |
| 影片探測 | ✓ | ✓ | ✓ |
| 註冊影片 | ✓ | ✓ | ✓ |
| 列出影片 | ✓ | ✓ | ✓ |
| 查詢影片 | ✓ | ✓ | ✓ |
| 處理進度 | ✓ | ✓ | ✓ |
| 工作管理 | ✓ | ✓ | ✓ |
| 快取設定 | ✓ (管理員) | ✓ (管理員) | ✓ (管理員) |
| 刪除影片 | ✓ (管理員) | ✓ (管理員) | ✓ (管理員) |
---
## 回應格式
### n8n 格式 (`/api/v1/n8n/search`)
```json
{
"query": "charade",
"count": 10,
"hits": [
{
"id": "sentence_0001",
"vid": "a1b10138a6bbb0cd",
"start": 48.8,
"end": 55.44,
"title": "Chunk sentence_0001",
"text": "...",
"score": 0.92,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
### 標準格式 (`/api/v1/search`)
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 48.8,
"end_time": 55.44,
"text": "...",
"score": 0.92
}
],
"query": "charade"
}
```
---
## HTTP 狀態碼
| 狀態 | 說明 |
|------|------|
| 200 | 成功 |
| 400 | 請求格式錯誤 |
| 404 | 端點或資源不存在 |
| 500 | 伺服器內部錯誤 |
| 502 | API 服務未啟動 |
---
## 錯誤處理
### 502 Bad Gateway
**原因**: Momentry API 服務未啟動
**解決**:
```bash
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
---
## 相關文件
- [API_INDEX.md](./API_INDEX.md) - 文件總覽(起點)
- [API_EXAMPLES.md](./API_EXAMPLES.md) - **完整範例總覽curl / n8n / WordPress**
- [API_N8N_GUIDE.md](./API_N8N_GUIDE.md) - n8n 詳細指南
- [API_WORDPRESS_GUIDE.md](./API_WORDPRESS_GUIDE.md) - WordPress 詳細指南
- [API_CURL_EXAMPLES.md](./API_CURL_EXAMPLES.md) - curl 範例

View File

@@ -0,0 +1,106 @@
# API Error Codes (API 標準錯誤碼)
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-04-25 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-04-25 | 定義全局標準錯誤碼與 Response 格式 | OpenCode | OpenCode |
---
## 1. 錯誤 Response 格式
所有 API 錯誤回應必須遵循以下 JSON 結構:
```json
{
"success": false,
"error": {
"code": "E001_NOT_FOUND",
"message": "找不到指定的資源",
"details": {
"resource": "file_uuid",
"value": "abc-123"
}
}
}
```
---
## 2. 錯誤碼列表
### 2.1 通用錯誤 (E0xx)
| 錯誤碼 | HTTP 狀態 | 說明 |
|--------|-----------|------|
| `E001_NOT_FOUND` | 404 | 找不到資源 (File, Identity, Chunk...) |
| `E002_DUPLICATE` | 409 | 資源已存在 (例如:重複註冊 File UUID) |
| `E003_VALIDATION` | 400 | 請求參數驗證失敗 (缺欄位、格式錯誤) |
| `E004_UNAUTHORIZED` | 401 | 無效的 API Key 或 Token |
| `E005_INTERNAL` | 500 | 系統內部錯誤 (資料庫連線失敗等) |
### 2.2 處理器相關 (E1xx)
| 錯誤碼 | HTTP 狀態 | 說明 |
|--------|-----------|------|
| `E101_PROCESSOR_FAIL` | 500 | Python 腳本執行失敗 (返回非 0 狀態碼) |
| `E102_TIMEOUT` | 504 | 處理超時 (例如:長影片 ASR 處理過久) |
| `E103_RESUME_FAIL` | 500 | 續傳失敗 (找不到 Checkpoint 檔案) |
| `E104_NO_VIDEO` | 400 | 找不到影片路徑 |
### 2.3 身份與 Face (E2xx)
| 錯誤碼 | HTTP 狀態 | 說明 |
|--------|-----------|------|
| `E201_FACE_NOT_FOUND` | 404 | 找不到指定的 Face Pre-chunk |
| `E202_MERGE_CONFLICT` | 409 | Identity 合併衝突 |
| `E203_CANDIDATE_EMPTY` | 404 | 沒有待確認的 Candidates |
---
## 3. 實作建議 (Rust Axum)
`src/api/server.rs` 中,建議使用自訂錯誤型別來統一處理:
```rust
#[derive(Debug)]
pub enum AppError {
NotFound(String),
Validation(String),
Internal(anyhow::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (code, message, status) = match self {
AppError::NotFound(msg) => ("E001_NOT_FOUND", msg, StatusCode::NOT_FOUND),
AppError::Validation(msg) => ("E003_VALIDATION", msg, StatusCode::BAD_REQUEST),
AppError::Internal(e) => ("E005_INTERNAL", e.to_string(), StatusCode::INTERNAL_SERVER_ERROR),
};
(status, Json(serde_json::json!({
"success": false,
"error": {
"code": code,
"message": message
}
}))).into_response()
}
}
```
---
## 版本資訊
- 版本: V1.0
- 建立日期: 2026-04-25

View File

@@ -0,0 +1,129 @@
---
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Momentry Core API 文件總覽"
date: "2026-04-23"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
- "momentry"
- "文件總覽"
- "core"
ai_query_hints:
- "查詢 Momentry Core API 文件總覽 的內容"
- "Momentry Core API 文件總覽 的主要目的是什麼?"
- "如何操作或實施 Momentry Core API 文件總覽?"
---
# Momentry Core API 文件總覽
> **Version**: 3.0 | **Updated**: 2026-04-23
> **Source**: Generated from actual Rust code (`src/api/`)
---
## 📁 文件結構
```
docs_v1.0/
├── REFERENCE/
│ ├── API_REFERENCE.md ← 主要 API 參考文件71 個端點)
│ ├── API_KEY_DESIGN.md ← API Key 系統設計文件
│ └── API_TRAINING_MARCOM.md ← marcom 團隊教育訓練手冊
├── IMPLEMENTATION/
│ ├── API_EXAMPLES.md ← 完整範例curl / n8n / WordPress
│ ├── API_CURL_EXAMPLES.md ← curl 快速範例
│ ├── API_WORDPRESS_GUIDE.md ← WordPress 整合指南
│ └── API_N8N_GUIDE.md ← n8n 整合指南
└── ARCHITECTURE/
└── API_KEY_ARCHITECTURE.md ← API Key 架構圖
```
---
## 快速選擇指南
| 需求 | 閱讀文件 |
|------|----------|
| **我要查看所有 API 端點** | [API_REFERENCE.md](./API_REFERENCE.md) |
| **我要 curl 範例** | [API_EXAMPLES.md](../IMPLEMENTATION/API_EXAMPLES.md) |
| **我是 marcom 團隊** | [API_TRAINING_MARCOM.md](./API_TRAINING_MARCOM.md) |
| **我要整合 n8n** | [API_N8N_GUIDE.md](../IMPLEMENTATION/API_N8N_GUIDE.md) |
| **我要整合 WordPress** | [API_WORDPRESS_GUIDE.md](../IMPLEMENTATION/API_WORDPRESS_GUIDE.md) |
| **我要了解 API Key 設計** | [API_KEY_DESIGN.md](./API_KEY_DESIGN.md) |
---
## 認證
### 使用方式
```bash
export API_KEY="your_api_key_here"
curl -H "X-API-Key: $API_KEY" http://localhost:3002/api/v1/videos
```
### 環境
| 環境 | URL | 使用時機 |
|------|-----|----------|
| **本地開發** | `http://localhost:3002` | 開發/測試 |
| **Playground** | `http://localhost:3003` | 開發測試dev 模式) |
| **外部訪問** | `https://api.momentry.ddns.net` | n8n、WordPress、遠端 |
---
## API 端點總覽
| 類別 | 端點數 | 說明 |
|------|--------|------|
| Health & Stats | 5 | 健康檢查與統計(公開) |
| Core Asset | 6 | 影片註冊、查詢、進度 |
| Processing | 7 | 探針、處理、任務 |
| Search | 7 | 向量、BM25、混合搜索 |
| Visual Chunk | 5 | 視覺分片搜索 |
| Face Recognition | 7 | 人臉識別 |
| Person Identity | 21 | 人物身份管理 |
| Global Identities | 6 | 全局身份 |
| Identity Binding | 6 | 身份綁定 |
| Configuration | 1 | 緩存配置 |
| **Total** | **71** | **可達端點** |
### ⚠️ 未掛載端點
以下端點已定義但**未在 router 中掛載**
| 端點 | 定義位置 |
|------|----------|
| `/api/v1/search/universal` | `universal_search.rs` |
| `/api/v1/search/frames` | `universal_search.rs` |
| `/api/v1/search/persons` | `universal_search.rs` |
| `/api/v1/who` | `who.rs` |
| `/api/v1/who/candidates` | `who.rs` |
---
## 常見問題
### Q: API 返回 401 錯誤?
API Key 無效或過期。請檢查 `X-API-Key` header。
### Q: API 返回 502 錯誤?
```bash
# 檢查服務狀態
launchctl list | grep momentry.api
# 重啟服務
sudo launchctl unload /Library/LaunchDaemons/com.momentry.api.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.api.plist
```
---
## 相關文件
- [API_REFERENCE.md](./API_REFERENCE.md) - 完整 API 參考
- [INSTALL_MOMENTRY_API.md](../IMPLEMENTATION/INSTALL_MOMENTRY_API.md) - 安裝指南
- [API_KEY_DESIGN.md](./API_KEY_DESIGN.md) - API Key 設計

View File

@@ -0,0 +1,532 @@
# Momentry Core API 快速查詢表
| 版本 | 日期 | 建立者 |
|------|------|--------|
| V1.0 | 2026-03-26 | OpenCode |
---
## 📋 快速導覽
| 類別 | 端點數量 | 說明 |
|------|----------|------|
| 健康檢查 | 2 | 系統狀態監控 |
| 影片管理 | 5 | 影片註冊、查詢、刪除 |
| 搜尋功能 | 3 | 語意搜尋、混合搜尋 |
| 任務管理 | 2 | 處理任務狀態查詢 |
| 系統管理 | 2 | 快取設定、進度查詢 |
---
## 🔐 認證
所有 `/api/v1/*` 端點需要 `X-API-Key` header
```bash
curl -H "X-API-Key: YOUR_API_KEY" ...
```
**公開端點(無需認證):**
- `GET /health`
- `GET /health/detailed`
---
## 📊 端點總表
### 健康檢查
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| GET | `/health` | 公開 | 基本健康檢查 |
| GET | `/health/detailed` | 公開 | 詳細健康檢查(包含所有服務狀態) |
### 影片管理
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| POST | `/api/v1/register` | 需要 | 註冊影片並開始處理 |
| POST | `/api/v1/unregister` | 需要 | 刪除影片及其所有資料 |
| POST | `/api/v1/probe` | 需要 | 探測影片資訊(不註冊) |
| GET | `/api/v1/videos` | 需要 | 列出所有已註冊影片 |
| GET | `/api/v1/lookup` | 需要 | 查詢影片資訊 |
### 搜尋功能
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| POST | `/api/v1/search` | 需要 | 語意搜尋(標準格式) |
| POST | `/api/v1/n8n/search` | 需要 | 語意搜尋n8n 格式) |
| POST | `/api/v1/search/hybrid` | 需要 | 混合搜尋(向量 + 關鍵字) |
### 任務管理
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| GET | `/api/v1/jobs` | 需要 | 列出所有處理任務 |
| GET | `/api/v1/jobs/:uuid` | 需要 | 取得特定任務詳情 |
### 系統管理
| 方法 | 端點 | 認證 | 描述 |
|------|------|------|------|
| GET | `/api/v1/progress/:uuid` | 需要 | 取得影片處理進度 |
| POST | `/api/v1/config/cache` | 需要 | 切換快取功能 |
---
## 🔧 詳細端點說明
### 1. 健康檢查
#### GET /health
**基本健康檢查**
```bash
curl http://localhost:3002/health
```
**回應:**
```json
{
"status": "ok",
"version": "0.1.0",
"uptime_ms": 14426558
}
```
#### GET /health/detailed
**詳細健康檢查**
```bash
curl http://localhost:3002/health/detailed
```
**回應:**
```json
{
"status": "ok",
"version": "0.1.0",
"uptime_ms": 14441362,
"services": {
"postgres": {"status": "ok", "latency_ms": 50, "error": null},
"redis": {"status": "ok", "latency_ms": 0, "error": null},
"qdrant": {"status": "ok", "latency_ms": 2, "error": null},
"mongodb": {"status": "ok", "latency_ms": 2, "error": null}
}
}
```
### 2. 影片管理
#### POST /api/v1/register
**註冊影片並開始處理**
```bash
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
```
**請求:**
```json
{
"path": "/path/to/video.mp4"
}
```
**回應:**
```json
{
"uuid": "5dea6618a606e7c7",
"video_id": 10,
"job_id": 1,
"file_name": "video.mp4",
"duration": 596.458333,
"width": 320,
"height": 180,
"already_exists": false
}
```
#### POST /api/v1/unregister
**刪除影片及其所有資料**
```bash
curl -X POST http://localhost:3002/api/v1/unregister \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"uuid": "5dea6618a606e7c7"}'
```
**請求:**
```json
{
"uuid": "5dea6618a606e7c7"
}
```
**回應:**
```json
{
"success": true,
"uuid": "5dea6618a606e7c7",
"message": "Video unregistered successfully"
}
```
#### POST /api/v1/probe
**探測影片資訊(不註冊)**
```bash
curl -X POST http://localhost:3002/api/v1/probe \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
```
**請求:**
```json
{
"path": "/path/to/video.mp4"
}
```
**回應:**
```json
{
"uuid": "5dea6618a606e7c7",
"file_name": "video.mp4",
"duration": 596.458333,
"width": 320,
"height": 180,
"fps": 24.0,
"cached": true,
"format": {...},
"streams": [...]
}
```
#### GET /api/v1/videos
**列出所有已註冊影片**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
```
**回應:**
```json
{
"videos": [
{
"uuid": "a03485a40b2df2d3",
"file_path": "/path/to/video.mp4",
"file_name": "video.mp4",
"duration": 596.458333,
"width": 320,
"height": 180
}
]
}
```
#### GET /api/v1/lookup
**查詢影片資訊**
```bash
# 依 UUID 查詢
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?uuid=a03485a40b2df2d3"
# 依路徑查詢
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3002/api/v1/lookup?path=/path/to/video.mp4"
```
**回應:**
```json
{
"uuid": "a03485a40b2df2d3",
"file_path": "/path/to/video.mp4",
"file_name": "video.mp4",
"duration": 596.458333
}
```
### 3. 搜尋功能
#### POST /api/v1/search
**語意搜尋(標準格式)**
```bash
curl -X POST http://localhost:3002/api/v1/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "search term", "limit": 5}'
```
**請求:**
```json
{
"query": "search term",
"limit": 5
}
```
**回應:**
```json
{
"results": [
{
"uuid": "a1b10138a6bbb0cd",
"chunk_id": "sentence_0001",
"chunk_type": "sentence",
"start_time": 10.5,
"end_time": 15.2,
"text": "Found text matching query",
"score": 0.85
}
],
"query": "search term"
}
```
#### POST /api/v1/n8n/search
**語意搜尋n8n 格式)**
```bash
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "search term", "limit": 5}'
```
**回應:**
```json
{
"query": "search term",
"count": 1,
"hits": [
{
"id": "sentence_0001",
"vid": "a1b10138a6bbb0cd",
"start_time": 10.5,
"end_time": 15.2,
"title": "Chunk sentence_0001",
"text": "Found text matching query",
"score": 0.85,
"file_path": "/path/to/video.mp4"
}
]
}
```
#### POST /api/v1/search/hybrid
**混合搜尋(向量 + 關鍵字)**
```bash
curl -X POST http://localhost:3002/api/v1/search/hybrid \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "search term", "limit": 5}'
```
**請求:**
```json
{
"query": "search term",
"limit": 5
}
```
**回應:**`/api/v1/search` 相同格式
### 4. 任務管理
#### GET /api/v1/jobs
**列出所有處理任務**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs
```
**回應:**
```json
{
"jobs": [
{
"id": 10,
"uuid": "a03485a40b2df2d3",
"status": "running",
"current_processor": null,
"progress_current": 0,
"progress_total": 0,
"created_at": "2026-03-26 13:39:37.830465",
"started_at": null
}
]
}
```
#### GET /api/v1/jobs/:uuid
**取得特定任務詳情**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/a03485a40b2df2d3
```
**回應:**
```json
{
"id": 10,
"uuid": "a03485a40b2df2d3",
"status": "running",
"current_processor": null,
"progress_current": 0,
"progress_total": 0,
"processors": [
{
"processor_type": "asr",
"status": "completed",
"started_at": "2026-03-26 05:39:40.275468",
"completed_at": "2026-03-26 07:19:43.166613",
"duration_secs": 6002.891145,
"error_message": null
},
// ... 其他處理器
],
"created_at": "2026-03-26 13:39:37.830465",
"started_at": null,
"updated_at": "2026-03-26 07:19:16.338406"
}
```
### 5. 系統管理
#### GET /api/v1/progress/:uuid
**取得影片處理進度**
```bash
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/a03485a40b2df2d3
```
**回應:**
```json
{
"uuid": "a03485a40b2df2d3",
"user": null,
"group": null,
"file_name": "video.mp4",
"duration": 596.458333,
"overall_progress": 0,
"cpu_percent": 0.2,
"gpu_percent": null,
"memory_percent": 0.1,
"memory_mb": 16720,
"processors": [
{
"name": "asr",
"status": "pending",
"current": 0,
"total": 0,
"progress": 0,
"message": ""
},
// ... 其他處理器
]
}
```
#### POST /api/v1/config/cache
**切換快取功能**
```bash
curl -X POST http://localhost:3002/api/v1/config/cache \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"enabled": true}'
```
**請求:**
```json
{
"enabled": true
}
```
**回應:**
```json
{
"success": true,
"cache_enabled": true,
"message": "Cache enabled"
}
```
---
## 🚀 快速工作流程
### 1. 註冊並處理影片
```bash
# 1. 註冊影片
curl -X POST http://localhost:3002/api/v1/register \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"path": "/path/to/video.mp4"}'
# 回應包含 UUID: 5dea6618a606e7c7
# 2. 監控進度
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/progress/5dea6618a606e7c7
# 3. 查看任務狀態
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/jobs/5dea6618a606e7c7
```
### 2. 搜尋影片內容
```bash
# 1. 列出所有影片
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3002/api/v1/videos
# 2. 搜尋內容
curl -X POST http://localhost:3002/api/v1/n8n/search \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"query": "charade scene", "limit": 10}'
```
### 3. 系統管理
```bash
# 1. 檢查系統健康
curl http://localhost:3002/health/detailed
# 2. 管理快取
curl -X POST http://localhost:3002/api/v1/config/cache \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"enabled": false}'
# 3. 刪除影片(需要時)
curl -X POST http://localhost:3002/api/v1/unregister \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"uuid": "5dea6618a606e7c7"}'
```
---
## 📝 注意事項
1. **API Key 格式:**
- 使用完整 API Key`muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69`
- 系統存儲的是 SHA256 哈希值
2. **路徑格式:**
- 絕對路徑:`/Users/accusys/test_video/video.mp4`
- 相對路徑:`./demo/video.mp4`(相對於 SFTPGo 資料目錄)
3. **回應時間:**
- 健康檢查:< 100ms
- 搜尋:取決於查詢複雜度,通常 100-500ms
- 影片註冊:立即返回,背景處理可能需要數分鐘到數小時
4. **錯誤處理:**
- 401: 認證失敗
- 404: 資源不存在
- 500: 伺服器內部錯誤
---
## 🔗 相關文件
- [API 參考指南](./API_REFERENCE.md) - 詳細 API 說明
- [API 範例總覽](./API_EXAMPLES.md) - 完整使用範例
- [API 端點列表](./API_ENDPOINTS.md) - 端點簡介
- [Curl 範例指南](./API_CURL_EXAMPLES.md) - curl 命令範例
- [n8n 整合指南](./API_N8N_GUIDE.md) - n8n 工作流程整合

View File

@@ -0,0 +1,310 @@
---
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Momentry Core API Reference"
date: "2026-04-25"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
- "reference"
- "momentry"
- "core"
ai_query_hints:
- "查詢 Momentry Core API Reference 的內容"
- "Momentry Core API Reference 的主要目的是什麼?"
- "如何操作或實施 Momentry Core API Reference"
---
# Momentry Core API Reference
> **Version**: 1.0 | **Source**: Generated from actual Rust code (`src/api/`)
> **Server**: Port 3002 (production) | 3003 (playground)
> **Auth**: Bearer token via `X-API-Key` header (required for most endpoints)
---
## 📋 Table of Contents
1. [Health & Stats (Public)](#health--stats)
2. [Core Asset Management](#core-asset-management)
3. [Processing Pipeline](#processing-pipeline)
4. [Search APIs](#search-apis)
5. [Visual Chunk Search](#visual-chunk-search)
6. [Face Recognition](#face-recognition)
7. [Person Identity](#person-identity)
8. [Global Identities](#global-identities)
9. [Identity Binding](#identity-binding)
10. [Configuration](#configuration)
---
## Health & Stats
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| GET | `/health` | No | Basic health check |
| GET | `/health/detailed` | No | Detailed health (all services) |
| GET | `/api/v1/stats/ingest` | No | Ingest statistics |
| GET | `/api/v1/stats/sftpgo` | No | SFTPGo service status |
| GET | `/api/v1/stats/inference` | No | Inference service health |
### Example
```bash
curl http://localhost:3002/health
# Response: {"status": "ok"}
```
---
## Core Asset Management
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/register` | Yes | Register a new video |
| POST | `/api/v1/unregister` | Yes | Delete a video and all data |
| GET | `/api/v1/videos` | Yes | List all videos |
| GET | `/api/v1/videos/:uuid/details` | Yes | Get video details with chunks |
| GET | `/api/v1/lookup` | Yes | Lookup video by path or UUID |
| GET | `/api/v1/progress/:uuid` | Yes | Get processing progress |
### Register Video
```bash
curl -X POST http://localhost:3002/api/v1/register \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"path": "./demo/video.mp4"}'
```
### Video Details
```bash
curl http://localhost:3002/api/v1/videos/$UUID/details \
-H "X-API-Key: $API_KEY"
```
---
## Processing Pipeline
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/probe` | Yes | Probe video metadata |
| GET | `/api/v1/assets/:uuid/probe` | Yes | Get probe result by UUID |
| POST | `/api/v1/assets/:uuid/process` | Yes | Trigger processing pipeline |
| GET | `/api/v1/assets/:uuid/status` | Yes | Get asset processing status |
| GET | `/api/v1/jobs/:job_id` | Yes | Get job status by ID |
| GET | `/api/v1/jobs` | Yes | List all jobs |
| GET | `/api/v1/rules/:rule/status` | Yes | Get rule processing status |
### Trigger Processing
```bash
curl -X POST http://localhost:3002/api/v1/assets/$UUID/process \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"rules": ["rule1"], "processors": ["asr", "yolo", "face"]}'
```
---
## Search APIs
### Vector Search
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/search` | Yes | Vector/semantic search |
| POST | `/api/v1/search/hybrid` | Yes | Hybrid search (vector + BM25) |
| POST | `/api/v1/search/bm25` | Yes | BM25 text search |
### N8N Search (Library Functions)
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/n8n/search` | Yes | N8N vector search |
| POST | `/api/v1/n8n/search/bm25` | Yes | N8N BM25 search |
| POST | `/api/v1/n8n/search/hybrid` | Yes | N8N hybrid search |
| POST | `/api/v1/n8n/search/smart` | Yes | N8N smart search |
### Search Request Body
```json
{
"query": "男女主角見面的場景",
"uuid": "optional-video-uuid",
"types": ["chunk", "frame", "person"],
"time_range": [0.0, 60.0],
"filters": {
"min_confidence": 0.8,
"required_object_classes": ["person"],
"speaker_id": "speaker_1"
},
"limit": 20,
"offset": 0
}
```
---
## Visual Chunk Search
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/search/visual` | Yes | Visual chunk search |
| POST | `/api/v1/search/visual/class` | Yes | Search by object class |
| POST | `/api/v1/search/visual/density` | Yes | Search by spatial density |
| POST | `/api/v1/search/visual/stats` | Yes | Get visual statistics |
| POST | `/api/v1/search/visual/combination` | Yes | Search by object combination |
### Visual Search Request
```bash
curl -X POST http://localhost:3002/api/v1/search/visual \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uuid": "abc123",
"criteria": {
"min_avg_confidence": 0.8,
"required_classes": ["person", "car"],
"min_spatial_density": 0.5
}
}'
```
### Search by Class
```bash
curl -X POST http://localhost:3002/api/v1/search/visual/class \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uuid": "abc123",
"object_class": "person",
"min_count": 5,
"max_count": 20
}'
```
---
## Face Recognition
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/face/recognize` | Yes | Recognize faces in video |
| POST | `/api/v1/face/register` | Yes | Register a face |
| POST | `/api/v1/face/search` | Yes | Search similar faces |
| GET | `/api/v1/face/list` | Yes | List all faces |
| GET | `/api/v1/face/:face_id` | Yes | Get face details |
| DELETE | `/api/v1/face/:face_id` | Yes | Delete a face |
| GET | `/api/v1/face/results/:file_uuid` | Yes | Get recognition results |
---
## Person Identity
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/person/identify` | Yes | Identify persons in video |
| POST | `/api/v1/person/auto-identify` | Yes | Auto-identify persons |
| POST | `/api/v1/person/suggest` | Yes | Get person suggestions |
| GET | `/api/v1/person/list` | Yes | List all persons |
| GET | `/api/v1/person/:person_id` | Yes | Get person details |
| PATCH | `/api/v1/person/:person_id` | Yes | Update person identity |
| GET | `/api/v1/person/:person_id/timeline` | Yes | Get person timeline |
| GET | `/api/v1/person/:person_id/appearances` | Yes | Get person appearances |
| GET | `/api/v1/person/:person_id/thumbnail` | Yes | Get person thumbnail |
| POST | `/api/v1/person/merge` | Yes | Merge two persons |
| POST | `/api/v1/person/merge/undo` | Yes | Undo merge |
| GET | `/api/v1/person/merge/history` | Yes | Get merge history |
| POST | `/api/v1/person/:person_id/split` | Yes | Split a person |
| GET | `/api/v1/person/:person_id/similar` | Yes | Get similar persons |
| PATCH | `/api/v1/person/:person_id/confirm` | Yes | Confirm suggestion |
| POST | `/api/v1/person/:person_id/unbind-speaker` | Yes | Unbind speaker |
| POST | `/api/v1/person/:person_id/reassign-speaker` | Yes | Reassign speaker |
| POST | `/api/v1/person/:person_id/remove-appearance` | Yes | Remove appearance |
| POST | `/api/v1/person/:person_id/reassign-appearance` | Yes | Reassign appearance |
| POST | `/api/v1/person/:person_id/register` | Yes | Register identity |
| GET | `/api/v1/chunks/:chunk_id/persons` | Yes | Get chunk persons |
---
## Global Identities
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/identities/from-person` | Yes | Register from person |
| GET | `/api/v1/identities` | Yes | List all identities |
| GET | `/api/v1/identities/:identity_id/videos` | Yes | Get identity videos |
| GET | `/api/v1/identities/:identity_id/faces` | Yes | Get identity faces |
| POST | `/api/v1/identities/search` | Yes | Search identities |
| GET | `/api/v1/videos/:uuid/faces` | Yes | Get video faces |
---
## Identity Binding
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/identities/bind` | Yes | Bind identity |
| POST | `/api/v1/identities/unbind` | Yes | Unbind identity |
| GET | `/api/v1/identity/:binding_type/:binding_value` | Yes | Get identity info |
| GET | `/api/v1/signals/unbound` | Yes | List unbound signals |
| GET | `/api/v1/signals/:uuid/:binding_type/:binding_value/timeline` | Yes | Get signal timeline |
| POST | `/api/v1/identities/suggest-av` | Yes | Suggest AV bindings |
---
## Configuration
| Method | Endpoint | Auth | Description |
|--------|----------|------|-------------|
| POST | `/api/v1/config/cache` | Yes | Toggle cache |
### Toggle Cache
```bash
curl -X POST http://localhost:3002/api/v1/config/cache \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
```
---
## Authentication
All endpoints except `/health` and `/health/detailed` require an API key:
```bash
export API_KEY="muser_xxx"
curl -H "X-API-Key: $API_KEY" http://localhost:3002/api/v1/videos
```
---
## ⚠️ Notable Notes
1. **Universal Search routes** (`/api/v1/search/universal`, `/api/v1/search/frames`, `/api/v1/search/persons`) are defined in `universal_search.rs` but **NOT MOUNTED** in `server.rs`.
2. **Who routes** (`/api/v1/who`, `/api/v1/who/candidates`) are defined in `who.rs` but **NOT MOUNTED** in `server.rs`.
3. **Total endpoints**: 71 reachable + 6 unreachable = 77 defined.
---
## 📁 Related Documents
| Document | Location |
|----------|----------|
| API Examples | `IMPLEMENTATION/API_EXAMPLES.md` |
| cURL Examples | `IMPLEMENTATION/API_CURL_EXAMPLES.md` |
| WordPress Guide | `IMPLEMENTATION/API_WORDPRESS_GUIDE.md` |
| n8n Guide | `IMPLEMENTATION/API_N8N_GUIDE.md` |
| API Key Design | `REFERENCE/API_KEY_DESIGN.md` |
| API Key Architecture | `ARCHITECTURE/API_KEY_ARCHITECTURE.md` |

View File

@@ -0,0 +1,427 @@
---
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Momentry Core API 教育訓練手冊"
date: "2026-04-27"
version: "V1.5"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
- "momentry"
- "core"
- "教育訓練手冊"
- "processing_status"
ai_query_hints:
- "查詢 Momentry Core API 教育訓練手冊 的內容"
- "Momentry Core API 教育訓練手冊 的主要目的是什麼?"
- "如何操作或實施 Momentry Core API 教育訓練手冊?"
- "processing_status 字段說明"
---
# Momentry Core API 教育訓練手冊
> **對象**: marcom 團隊
> **版本**: V1.5 | **日期**: 2026-04-27
---
## 1. 快速開始
### 基本資訊
| 項目 | 值 |
|------|-----|
| API 網址 | `https://api.momentry.ddns.net` |
| 認證方式 | Header `X-API-Key` |
| 格式 | JSON |
### Demo 測試帳號
#### API Key用於 API 認證)
```
X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
```
#### SFTPGo用於影片上傳
| 項目 | 值 |
|------|-----|
| SFTP 主機 | `sftpgo.momentry.ddns.net` |
| SFTP 連接埠 | `2022` |
| 用戶名 | `demo` |
| 密碼 | `demopassword123` |
| Web 管理介面 | `https://sftpgo.momentry.ddns.net` |
**使用方式**:透過 SFTP 上傳影片,系統會自動註冊並處理。
---
## 2. 快速範例
### 查詢所有影片
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/videos"
```
### 查詢單一影片
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/videos/{uuid}/details"
```
### 查詢處理任務狀態
```bash
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"https://api.momentry.ddns.net/api/v1/jobs/{job_id}"
```
---
## 3. API 端點說明
### 3.1 影片相關
#### GET /api/v1/videos
取得所有影片列表
**回應範例**:
```json
{
"videos": [
{
"uuid": "5dea6618a606e7c7",
"filename": "demo_video.mp4",
"duration": 123.45,
"status": "ready",
"created_at": "2026-03-25T10:00:00Z"
}
]
}
```
#### GET /api/v1/videos/:uuid/details
取得單一影片詳情(包含 chunks、processing status 等)
### 3.2 搜尋與分段查詢
#### POST /api/v1/search
向量搜尋查詢分段Chunk詳情
**請求參數**:
| 參數 | 類型 | 必填 | 說明 |
|------|------|------|------|
| `query` | string | 是 | 搜尋關鍵字 |
| `limit` | number | 否 | 回傳數量(預設 10 |
| `uuid` | string | 否 | 只搜尋指定影片 |
**請求範例**:
```json
{
"query": "天氣",
"limit": 10,
"uuid": "5dea6618a606e7c7"
}
```
**回應範例**:
```json
{
"results": [
{
"uuid": "39567a0eb16f39fd",
"chunk_id": "sentence_1471",
"chunk_type": "sentence",
"start_time": 5309.08,
"end_time": 5311.08,
"text": "influenced by a vital way,",
"score": 0.68
}
],
"query": "天氣"
}
```
**Chunk 欄位說明**:
| 欄位 | 說明 | 範例 |
|------|------|------|
| `uuid` | 影片唯一識別碼 | `39567a0eb16f39fd` |
| `chunk_id` | 分段識別碼 | `sentence_1471` |
| `chunk_type` | 分段類型 | `sentence` / `cut` / `time` / `trace` / `story` |
| `start_time` | 開始時間(秒) | `5309.08` |
| `end_time` | 結束時間(秒) | `5311.08` |
| `text` | 內容文字 | `influenced by a vital way` |
| `score` | 相似度分數0-1 | `0.68` |
**Chunk 類型說明**:
| 類型 | 說明 | 來源 |
|------|------|------|
| `sentence` | 語音轉文字片段 | ASR 處理 |
| `cut` | 場景變化片段 | CUT 處理 |
| `time` | 固定時間分段 | 系統自動切割 |
| `trace` | 軌跡追蹤片段 | YOLO 追蹤 |
| `story` | 故事線片段(父子關係) | Story 分析 |
#### POST /api/v1/n8n/search
n8n 專用搜尋(包含完整影片檔案路徑 file_path
**請求參數**: 與 `/search` 相同
**回應範例**:
```json
{
"query": "天氣",
"count": 2,
"hits": [
{
"id": "sentence_1471",
"vid": "39567a0eb16f39fd",
"start": 5309.08,
"end": 5311.08,
"title": "Chunk sentence_1471",
"text": "influenced by a vital way,",
"score": 0.68,
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"
}
]
}
```
**與 /search 的差異**:
| 欄位 | `/search` | `/n8n/search` |
|------|-----------|----------------|
| 影片 UUID | `uuid` | `vid` |
| Chunk ID | `chunk_id` | `id` |
| 開始時間 | `start_time` | `start` |
| 結束時間 | `end_time` | `end` |
| 相似度分數 | `score` | `score` |
| **檔案路徑** | ❌ | ✅ `file_path` |
> **注意**: `file_path` 是影片的實際路徑,可用於本地播放。
### 3.3 任務相關
### 3.4 任務相關
#### GET /api/v1/jobs/:uuid
查詢處理任務狀態
**回應範例**:
```json
{
"uuid": "9760d0820f0cf9a7",
"file_uuid": "5dea6618a606e7c7",
"status": "completed",
"progress": 100,
"created_at": "2026-03-25T10:00:00Z",
"completed_at": "2026-03-25T10:05:00Z"
}
```
#### GET /api/v1/jobs
查詢所有任務
**查詢參數**:
| 參數 | 說明 | 範例 |
|------|------|------|
| `status` | 篩選狀態 | `pending`, `processing`, `completed`, `failed` |
| `limit` | 回傳數量 | `10` |
**範例**:
```bash
curl -s -H "X-API-Key: ..." \
"https://api.momentry.ddns.net/api/v1/jobs?status=completed&limit=5"
```
### 3.5 系統管理
#### POST /api/v1/config/cache
切換快取功能(管理員專用)
**請求範例**:
```json
{
"enabled": true
}
```
**回應範例**:
```json
{
"cache_enabled": true,
"message": "Cache toggled successfully"
}
```
#### POST /api/v1/unregister
刪除影片及其所有關聯資料(管理員專用)
**請求範例**:
```json
{
"uuid": "5dea6618a606e7c7"
}
```
**回應範例**:
```json
{
"success": true,
"message": "Video unregistered successfully",
"uuid": "5dea6618a606e7c7"
}
```
**注意**: 此操作會刪除影片及其所有分段、處理結果、縮圖等關聯資料,**無法復原**。
### 3.6 健康檢查
#### GET /health
服務健康狀態(**無需認證**
**回應**:
```json
{
"status": "ok",
"version": "0.9.20260325_144654"
}
```
---
## 4. n8n Workflow 範例
### 4.1 基本設定
在 n8n workflow 中使用 HTTP Request 節點:
```
┌─────────────────┐
│ HTTP Request │
├─────────────────┤
│ Method: GET │
│ URL: https://api.momentry.ddns.net/api/v1/videos
│ Headers: │
│ X-API-Key: │
│ [YOUR_KEY] │
└────────┬────────┘
┌─────────────────┐
│ 處理回應資料 │
└─────────────────┘
```
### 4.2 範例:檢查任務狀態
```javascript
// n8n Function Node 範例
const jobUuid = $input.item.json.uuid;
return [{
json: {
method: "GET",
url: `https://api.momentry.ddns.net/api/v1/jobs/${jobUuid}`,
headers: {
"X-API-Key": "YOUR_API_KEY"
}
}
}];
```
---
## 5. 常見問題
### Q: 返回 401 錯誤怎麼辦?
確認 Header 中有正確的 `X-API-Key`
### Q: 如何確認影片處理完成?
```
GET /api/v1/jobs/{uuid}
```
檢查 `status` 是否為 `completed`
### Q: 查不到資料?
確認 UUID 格式正確16碼 hex 字串)
---
## 6. 快速參考卡
```
┌─────────────────────────────────────────────────────────────┐
│ Momentry API 速查 │
├─────────────────────────────────────────────────────────────┤
│ 查詢所有影片 GET /api/v1/videos │
│ 查詢單一影片 GET /api/v1/videos/:uuid │
│ 向量搜尋 POST /api/v1/search │
│ n8n 搜尋 POST /api/v1/n8n/search │
│ 查詢任務狀態 GET /api/v1/jobs/:uuid │
│ 查詢所有任務 GET /api/v1/jobs │
│ 快取設定 POST /api/v1/config/cache (管理員) │
│ 刪除影片 POST /api/v1/unregister (管理員) │
│ 健康檢查 GET /health (免認證) │
├─────────────────────────────────────────────────────────────┤
│ Header: X-API-Key: [YOUR_KEY] │
│ URL: https://api.momentry.ddns.net │
└─────────────────────────────────────────────────────────────┘
```
---
## 附錄:回應狀態說明
### 任務狀態 (status)
| 狀態 | 說明 |
|------|------|
| `pending` | 等待處理 |
| `processing` | 處理中 |
| `completed` | 已完成 |
| `failed` | 處理失敗 |
### 影片狀態 (status)
| 狀態 | 說明 |
|------|------|
| `pending` | 等待處理 |
| `processing` | 處理中 |
| `completed` | 已完成 |
| `failed` | 處理失敗 |
### 影片詳細狀態 (processing_status)
| 狀態 | 說明 | Portal 顯示 |
|------|------|-------------|
| `REGISTERED` | 已註冊 | 藍色「已註冊」 |
| `PENDING` | 等待處理 | 黃色「等待處理」 |
| `PROBING` | 探測中 | 紫色「分析中」 |
| `ASR` | 語音識別中 | 靛藍「語音識別」 |
| `OCR` | 文字識別中 | 靛藍「文字識別」 |
| `YOLO` | 物體檢測中 | 靛藍「物體檢測」 |
| `FACE` | 人臉檢測中 | 靛藍「人臉檢測」 |
| `POSE` | 姿態檢測中 | 靛藍「姿態檢測」 |
| `CUT` | 鏡頭分析中 | 靛藍「鏡頭分析」 |
| `COMPLETED` | 完成 | 綠色「已完成」 |
| `FAILED` | 失敗 | 紅色「處理失敗」 |
**說明**Portal 顯示優先使用 `processing_status`詳細狀態Fallback 使用 `status`(基本狀態)。
---
## 附錄:版本歷史
| 版本 | 日期 | 內容 | 操作人 |
|------|------|------|--------|
| V1.0 | 2026-03-25 | 初版建立 | OpenCode |
| V1.1 | 2026-03-25 | 新增快取/刪除 API、搜尋端點文件 | OpenCode |
| V1.2 | 2026-03-25 | 新增 Chunk 欄位說明、類型、播放方式 | OpenCode |
| V1.3 | 2026-03-25 | 新增 Demo 測試帳號SFTPGo| OpenCode |
| V1.4 | 2026-03-25 | 更新 n8n 搜尋回傳欄位說明 (media_url→file_path) | OpenCode |
| V1.5 | 2026-04-27 | 新增 processing_status 字段說明,移除 'ready' 狀態 | OpenCode |

View File

@@ -0,0 +1,159 @@
# Demo Runner System v1.0.0
## 概述
`scripts/demo_runner.py` — 自動播放展示系統。讀取 JSON 腳本,依序執行各類型步驟,展示 Momentry Core API。
## 安裝
```bash
# 相依性Python 3.11+, macOS `say` 指令(語音)
# md_reader選擇性提供更好的 Markdown 預覽)
cd ~/md_reader && cargo build --release
```
## 執行方式
```bash
cd ~/momentry_core_0.1
# 逐步互動模式
python3.11 scripts/demo_runner.py docs_v1.0/API_V1.0.0/DEMO_SCRIPT_v1.0.0.json
# 自動播放 + 中文語音
python3.11 scripts/demo_runner.py docs_v1.0/API_V1.0.0/DEMO_SCRIPT_v1.0.0.json --auto --voice zh_TW
# 指定起始步驟、快放
python3.11 scripts/demo_runner.py demo.json --step 5 --speed 3
# 英文語音
python3.11 scripts/demo_runner.py demo.json --voice en_US
```
## 步驟類型
| type | 功能 | 必要欄位 |
|------|------|---------|
| `curl` | 執行 API 命令並顯示 JSON 回應 | `cmd` |
| `browser` | 在瀏覽器中開啟 URL | `url` |
| `markdown` | 用 md_reader Preview 渲染 .md 文件(含 Mermaid | `cmd`(檔案路徑) |
| `note` | 純文字解說 | `note` |
| `separator` | 章節分隔線 | `label` |
## JSON 腳本結構
```json
{
"title": "展示名稱",
"language": "zh_TW",
"steps": [
{
"type": "curl",
"label": "步驟標題",
"note": "解說文字(語音會朗讀此段)",
"cmd": "curl -s $BASE/api/v1/health",
"expect": "ok"
},
{
"type": "browser",
"label": "開啟頁面",
"note": "說明文字",
"url": "$BASE/api/v1/file/$FILE/trace/5/video?padding=1"
},
{
"type": "markdown",
"label": "文件展示",
"note": "說明文字",
"cmd": "docs_v1.0/API_V1.0.0/API_USAGE_GUIDE_V1.0.0.md",
"focus": "自動聚焦的章節名稱"
}
]
}
```
## 變數
| 變數 | 預設值 | 說明 |
|------|--------|------|
| `$BASE` | `https://api.momentry.ddns.net` | API 伺服器 |
| `$KEY` | `muser_68600856036340...` | API Key |
| `$FILE` | `3abeee81...` | Charade file UUID |
環境變數覆蓋:`DEMO_KEY`, `DEMO_BASE`, `DEMO_FILE`, `DEMO_VOICE`
## 語音功能
## 語音朗讀
- 支援語言:`zh_TW`Meijia`zh_CN`Ting-Ting`en_US`Samantha`ja_JP`Kyoko`ko_KR`Yuna`fr_FR`Amelie
- macOS 內建 `say` 指令,零外部依賴
- **單軌**:每次朗讀完整結束才播放下一個(`subprocess.Popen` + `wait` 阻塞模式)
- **無重疊**:前一句完整發音後才開始下一句
## 語音指令(--voice-control
啟用麥克風語音控制,可用說的操作展示流程:
```bash
python3 scripts/demo_runner.py demo.json --voice zh_TW --voice-control
```
| 指令(中文) | 指令English | 功能 |
|:-----------:|:---------------:|------|
| "下一個" / "繼續" | "next" / "continue" | 前進到下一步 |
| "停止" | "stop" / "quit" | 結束展示 |
| "重複" | "repeat" / "again" | 重複朗讀當前解說 |
| "跳到第 5 步" | "go to 5" | 跳到指定步驟 |
語音辨識使用 Google Speech Recognition需網路背景執行不影響主流程。
## 展示節奏
- 開場倒數 3-2-1
- 語音解說後暫停 1.5 秒
- curl 回應依長度自動決定閱讀時間1.56 秒)
- Browser/markdown 步驟停留 5 秒
- 章節分隔停留 1.5 秒
## 自動聚焦Markdown 步驟)
`focus` 參數讓 md_reader Preview 視窗自動捲到指定章節:
```json
{
"type": "markdown",
"cmd": "docs/API_USAGE_GUIDE.md",
"focus": "搜尋三模式"
}
```
效果:平滑捲動至該標題 → 金色高亮 3 秒後淡出。
## md_reader Preview 視窗功能
| 功能 | 操作 |
|------|------|
| 平移Pan | 工具列 Pan 按鈕 → 滑鼠拖曳 |
| 縮放 | 工具列 / + / Reset |
| 快捷指令 | 按 `/` 輸入 `/zoom 150` |
| Mermaid 圖表 | 自動渲染,可下載 SVG |
| 列印/PDF | 工具列 Print 按鈕 |
| 指令列表 | `/help` |
## 依賴項目
| 元件 | 用途 | 授權 |
|------|------|:----:|
| Python 3.11 | 執行環境 | PSF |
| macOS `say` | 語音合成 | macOS 內建 |
| `md_reader`(選擇性)| Markdown → HTML 含 Mermaid | MIT |
| curl | API 命令執行 | macOS 內建 |
| webbrowserPython| 開啟瀏覽器 | Python 內建 |
## 檔案
| 檔案 | 說明 |
|------|------|
| `scripts/demo_runner.py` | 執行器主程式 |
| `docs_v1.0/API_V1.0.0/DEMO_SCRIPT_v1.0.0.json` | 21 步驟預設展示腳本 |
| `~/_md_reader/target/release/md_reader` | Markdown 渲染工具 |

View File

@@ -0,0 +1,864 @@
---
document_type: "demo_guide"
service: "MOMENTRY_CORE"
title: "Pipeline Demo End-to-End"
date: "2026-05-15"
version: "V1.0"
status: "active"
owner: "M5"
created_by: "OpenCode"
tags:
- "demo"
- "pipeline"
- "end-to-end"
- "api"
ai_query_hints:
- "如何執行端到端 Pipeline demo"
- "Pipeline 處理流程"
- "註冊影片並觸發處理的完整流程"
related_documents:
- "GUIDES/API_ENDPOINTS.md"
- "GUIDES/Pipeline_API_Demo.md"
---
# Momentry Core — Pipeline Demo End-to-End
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-05-15 |
| 文件版本 | V1.0 |
| 目標讀者 | developer |
| 預備知識 | 需有 API Key、Pipeline 基本概念 |
---
## Table of Contents
### Pipeline Phases
| Phase | Step | What happens |
|-------|------|-------------|
| **Pre** | 14 | System check, scan, register, probe |
| **處理中** | 56 | Submit job → Worker picks up → Each processor runs (pending→running→completed) |
| **處理後** | 79 | All results → Search → Identities → Schema verification |
---
## 1. 檢查系統狀況
```bash
API="http://api.momentry.ddns.net"
KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
# Basic health
curl -sf "$API/health" | jq '{status, version, build_git_hash, uptime_ms}'
# Detailed health
curl -sf "$API/health/detailed" | jq '{
services,
schema: .schema.ok,
scripts: .pipeline.scripts_count,
integrity: .pipeline.scripts_integrity,
procs: [.pipeline.processors | to_entries[] | select(.value == true and .key != "total_py_files") | .key]
}'
```
Output:
```json
{
"status": "ok",
"version": "1.0.0",
"build_git_hash": "c41f7e0c",
"uptime_ms": 2756192
}
{
"services": {"postgres": "ok", "redis": "ok", "qdrant": "ok"},
"schema": false,
"scripts": 291,
"integrity": {"matched": 332, "total": 345, "ok": false},
"procs": ["asr","yolo","face","pose","ocr","cut","caption","scene","story","asrx","probe","visual_chunk"]
}
```
---
## 2. 掃描檔案
掃描伺服器上所有與 `exasan` 相關的檔案(支援規則表達式):
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/files/scan?pattern=exasan" | \
jq '[.files[] | {uuid: .file_uuid, name: .file_name, size: .file_size}]'
```
輸出(節錄):
```json
[
{"uuid": "dd61fda85fee441f...", "name": "ExaSAN PCIe series - Director Ou Yu-Zhi Shares His Experience.mp4", "size": 6827600},
{"uuid": "8e2e98c49355935f...", "name": "ExaSAN Webinar by Blake Jones, Vision2see.mp4", "size": 38635889},
{"uuid": "477d8fa7bc0e1a7...", "name": "Thunderbolt ExaSAN at CCBN.mp4", "size": 13126748}
]
```
**Note**: `files/scan` 也可以掃所有檔案,或用於批次註冊。若不指定 pattern回傳伺服器 `sftpgo/data/demo/` 目錄下所有檔案。
---
## 3. 註冊或確認
若檔案尚未註冊,使用 register API。若已存在如本次示範直接確認狀態
```bash
UUID="dd61fda85fee441fdd00ab5528213ff7"
# 確認檔案狀態
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}" | jq '{uuid: .file_uuid[0:16], name: .file_name, status, duration, fps}'
# 若檔案不存在,使用註冊 API
# curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
# -d '{"file_path": "/path/to/video.mp4"}' \
# "$API/api/v1/files/register" | jq '.'
```
**註冊流程**
```
POST /files/register
├─ SHA256 content_hash (dedup 檢查)
├─ file_name 衝突檢查 (自動 rename)
├─ Pre-process (SHA256 + ffprobe + UUID → .pre.json)
├─ UUID = f(mac, mtime, path, filename)
├─ Unified probe (video→ffprobe, doc→Python)
└─ INSERT INTO videos
```
---
## 4. Probe 確認
The probe endpoint returns ffprobe metadata about the registered file.
```bash
# Substitute the actual file_uuid from step 3
FILE_UUID="e1111111111111111111111111111111"
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/file/${FILE_UUID}/probe" | python3 -m json.tool
```
Output (abbreviated):
```json
{
"file_uuid": "e1111111111111111111111111111111",
"file_name": "demo_test_video.mp4",
"duration": 5.005,
"width": 640,
"height": 480,
"fps": 24.0,
"total_frames": 120,
"cached": true,
"format": {
"filename": "/tmp/demo_test_video.mp4",
"format_name": "mov,mp4,m4a,3gp,3g2,mj2",
"duration": "5.005000",
"size": "98304",
"bit_rate": "157184"
},
"streams": [
{"index": 0, "codec_type": "video", "codec_name": "h264", "width": 640, "height": 480, ...},
{"index": 1, "codec_type": "audio", "codec_name": "aac", ...}
]
}
```
**Error handling** (Bug #3 fix):
- Non-existent UUID → `{"error":"Video not found"}` + HTTP 404
- File deleted from disk → `{"error":"File does not exist at registered path"}` + HTTP 404
- ffprobe failure → `{"error":"ffprobe failed: ..."}` + HTTP 500
### ⚡ Intermediate Check — Bug #3: Probe Error Verification
Test both error cases return proper JSON + HTTP code instead of bare 500:
```bash
echo "=== Non-existent UUID → expect 404 ==="
curl -s -w "\nHTTP: %{http_code}\n" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/file/bad_uuid_12345/probe"
# Expect: {"error":"Video not found","file_uuid":"bad_uuid_12345"} HTTP 404
echo ""
echo "=== Non-existent file path → expect 404 ==="
# Temporarily change file_path to a non-existent location
"$PG_BIN/psql" -U accusys -d momentry -c \
"UPDATE dev.videos SET file_path = '/tmp/NONEXISTENT_FILE' WHERE file_uuid = '${FILE_UUID}'"
curl -s -w "\nHTTP: %{http_code}\n" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/file/${FILE_UUID}/probe"
# Expect: {"error":"File does not exist at registered path",...} HTTP 404
# Restore path
"$PG_BIN/psql" -U accusys -d momentry -c \
"UPDATE dev.videos SET file_path = '/tmp/demo_test_video.mp4' WHERE file_uuid = '${FILE_UUID}'"
```
Output:
```
=== Non-existent UUID → expect 404 ===
{"error":"Video not found","file_uuid":"bad_uuid_12345"}
HTTP: 404
=== Non-existent file path → expect 404 ===
{"error":"File does not exist at registered path","file_uuid":"e1111111111111111111111111111111","file_path":"/tmp/NONEXISTENT_FILE"}
HTTP: 404
```
---
## 5. Process Video
Trigger pipeline processing for specific processors. The available processors are:
| Processor | Function | Script |
|-----------|----------|--------|
| `asr` | Speech-to-text (faster-whisper) | `asr_processor.py` |
| `cut` | Scene detection (PySceneDetect) | `cut_processor.py` |
| `yolo` | Object detection (YOLOv8) | `yolo_processor.py` |
| `face` | Face detection (InsightFace) | `face_processor.py` |
| `pose` | Pose estimation (MediaPipe) | `pose_processor.py` |
| `ocr` | Text detection (PaddleOCR) | `ocr_processor.py` |
| `asrx` | Speaker diarization | `asrx_processor.py` |
| `visual_chunk` | Visual content analysis | `visual_chunk_processor.py` |
| `scene` | Scene classification | `scene_classifier.py` |
| `story` | Story generation (LLM) | `story_processor.py` |
| `caption` | Caption generation | `caption_processor.py` |
```bash
# Trigger only ASR + CUT for quick test
curl -s -X POST "http://api.momentry.ddns.net/api/v1/file/${FILE_UUID}/process" \
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
-H "Content-Type: application/json" \
-d '{"processors": ["asr", "cut"]}' | python3 -m json.tool
```
Output:
```json
{
"job_id": 161,
"file_uuid": "e1111111111111111111111111111111",
"status": "PENDING",
"pids": [],
"message": "Processing triggered for demo_test_video.mp4"
}
```
**Processing flow**:
```
POST /process → trigger_processing()
├─ Validate file exists (DB lookup)
├─ Create monitor_job (status: PENDING)
├─ Create processor_result rows for each requested processor (status: pending)
└─ Response { job_id, status: "PENDING" }
```
**Note**: If no processors are specified, all processors are used:
```json
{"processors": ["asr", "cut", "yolo", "ocr", "face", "pose", "asrx", "visual_chunk"]}
```
### ⚡ Intermediate Check — Verify Job + Processor Results after Trigger
```bash
PG_BIN="/Users/accusys/pgsql/18.3/bin"
# Check monitor_jobs table
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT id, uuid, status, current_processor,
to_char(created_at, 'HH24:MI:SS') AS created
FROM dev.monitor_jobs
WHERE uuid = '${FILE_UUID}'
ORDER BY id DESC LIMIT 1
\gx
"
# Check processor_results table
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT id, processor, status
FROM dev.processor_results
WHERE file_uuid = '${FILE_UUID}'
ORDER BY id
"
```
Output:
```
-[ RECORD 1 ]------+-----------------------------
id | 161
uuid | e1111111111111111111111111111111
status | PENDING
current_processor | (null)
created | 19:00:30
id | processor | status
----+-----------+---------
1 | asr | pending
2 | cut | pending
```
**Checklist after trigger:**
- [ ] `monitor_jobs.status = 'PENDING'` — job created, awaiting worker
- [ ] `processor_results` rows match requested processors (2 rows for `asr`, `cut`)
- [ ] Each `processor.status = 'pending'` — not yet executed
---
## 6. Worker Execution
The worker polls for pending jobs and executes them one by one.
```bash
DATABASE_SCHEMA=dev cargo run --bin momentry_playground -- worker \
--max-concurrent 2 --poll-interval 5
```
Or in background:
```bash
DATABASE_SCHEMA=dev nohup target/debug/momentry_playground worker \
--max-concurrent 2 --poll-interval 5 > /tmp/worker_demo.log 2>&1 &
```
**Worker flow**:
```
Worker loop (every 5 seconds):
├─ Poll: SELECT * FROM monitor_jobs WHERE status = 'PENDING'
├─ Set job status → RUNNING
├─ For each pending processor:
│ ├─ SHA256 integrity check (verify_script_integrity)
│ │ └─ checksums.sha256 manifest lookup
│ ├─ Execute script via PythonExecutor
│ │ └─ Command: {MOMENTRY_PYTHON_PATH} scripts/<processor>.py <args>
│ ├─ Verify output (file exists, content valid)
│ └─ Update processor_result (completed/failed)
├─ Check completion: all processors done?
├─ Yes → Set job + video status → COMPLETED
└─ No → Wait for next poll cycle
```
**Worker log output**:
```
[CHECKSUMS] Loaded 345 entries from checksums.sha256
[INTEGRITY] asr_processor.py checksum OK
[ASR] Starting asr_processor.py
[INTEGRITY] cut_processor.py checksum OK
[CUT] Starting cut_processor.py
[ASR] Completed successfully
[CUT] Completed successfully
check_and_complete_job: results=2/2 → Job COMPLETED
```
### ⚡ Intermediate Check — Poll Progress During Worker Execution
While the worker is running, poll the progress endpoint to watch state transitions:
```bash
# Poll every 5 seconds until completed
FILE_UUID="e1111111111111111111111111111111"
for i in $(seq 1 12); do
sleep 5
STATUS=$(curl -sf -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/progress/${FILE_UUID}" \
| python3 -c "import json,sys;d=json.load(sys.stdin);print(d.get('status','?'))" 2>/dev/null || echo "pending")
echo "Poll $i: status=$STATUS"
[ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ] && break
done
```
Output (typical):
```
Poll 1: status=registered ← worker hasn't picked it up yet
Poll 2: status=pending ← worker picked up, job status changed
Poll 3: status=processing ← worker running ASR
Poll 4: status=processing ← worker running CUT
Poll 5: status=completed ← all done
```
Check status transitions in DB:
```bash
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT id, processor, status,
to_char(started_at, 'HH24:MI:SS') AS started,
to_char(completed_at, 'HH24:MI:SS') AS completed
FROM dev.processor_results
WHERE file_uuid = '${FILE_UUID}'
ORDER BY id
"
```
Output:
```
id | processor | status | started | completed
----+-----------+------------+-----------+-----------
1 | asr | completed | 19:01:02 | 19:01:25
2 | cut | completed | 19:01:02 | 19:01:08
```
### ⚡ Processing Checklist — Step-by-Step Verification
This checklist covers every stage of the pipeline processing flow:
```bash
# ──────────────────────────────────────────────────────
# Stage A: Before Worker Starts
# ──────────────────────────────────────────────────────
PG_BIN="/Users/accusys/pgsql/18.3/bin"
FILE_UUID="e1111111111111111111111111111111"
KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
echo "=== A1. Job status = PENDING ==="
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT id, status, current_processor, created_at FROM dev.monitor_jobs WHERE uuid = '${FILE_UUID}'
"
echo "=== A2. Processor results = pending ==="
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT id, processor, status FROM dev.processor_results WHERE file_uuid = '${FILE_UUID}' ORDER BY id
"
# ──────────────────────────────────────────────────────
# Stage B: Worker Running
# ──────────────────────────────────────────────────────
echo "=== Start worker ==="
DATABASE_SCHEMA=dev nohup target/debug/momentry_playground worker \
--max-concurrent 1 --poll-interval 3 > /tmp/worker_check.log 2>&1 &
WPID=$!
echo "=== B1. Worker picks up job (within 3-10s) ==="
for i in $(seq 1 10); do
sleep 3
JOB_STATUS=$("$PG_BIN/psql" -U accusys -d momentry -t -A -c \
"SELECT status FROM dev.monitor_jobs WHERE uuid = '${FILE_UUID}'" 2>/dev/null)
VIDEO_STATUS=$("$PG_BIN/psql" -U accusys -d momentry -t -A -c \
"SELECT status FROM dev.videos WHERE file_uuid = '${FILE_UUID}'" 2>/dev/null)
echo " Poll $i: job=$JOB_STATUS video=$VIDEO_STATUS"
echo " $(grep '\[INTEGRITY\]\|\[SCHEMA\]\|Starting:\|Completed\|failed\|Job ' /tmp/worker_check.log 2>/dev/null | tail -3)"
# Check alive
kill -0 $WPID 2>/dev/null || { echo " Worker died unexpectedly"; break; }
if [ "$VIDEO_STATUS" = "completed" ] || [ "$VIDEO_STATUS" = "failed" ]; then break; fi
done
echo "=== B2. Each processor status ==="
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT id, processor, status,
to_char(started_at, 'HH24:MI:SS') AS started,
to_char(completed_at, 'HH24:MI:SS') AS completed,
COALESCE(chunks_produced, 0) AS chunks,
COALESCE(frames_processed, 0) AS frames,
COALESCE(error_message, '') AS error
FROM dev.processor_results
WHERE file_uuid = '${FILE_UUID}'
ORDER BY id
"
kill $WPID 2>/dev/null || true
# ──────────────────────────────────────────────────────
# Stage C: After Completion
# ──────────────────────────────────────────────────────
echo "=== C1. Video final status ==="
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT file_uuid, file_name, status, duration, fps, total_frames FROM dev.videos WHERE file_uuid = '${FILE_UUID}'
"
echo "=== C2. Chunks produced ==="
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT chunk_type, count(*) FROM dev.chunk WHERE file_uuid = '${FILE_UUID}' GROUP BY chunk_type ORDER BY chunk_type
"
echo "=== C3. Job final status ==="
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT id, status, current_processor FROM dev.monitor_jobs WHERE uuid = '${FILE_UUID}'
"
```
Expected output (all green):
```
=== A1. Job status = PENDING ===
id | status | current_processor | created_at
----+---------+-------------------+-------------------
161| PENDING | | 2026-05-15 19:00:30
=== A2. Processor results = pending ===
id | processor | status
----+-----------+---------
1 | asr | pending
2 | cut | pending
=== Start worker ===
=== B1. Worker picks up job (within 3-10s) ===
Poll 1: job=PENDING video=registered
Poll 2: job=RUNNING video=processing
[INTEGRITY] asr_processor.py checksum OK
Poll 3: job=RUNNING video=processing
[ASR] Starting: asr_processor.py
Poll 4: job=RUNNING video=processing
[ASR] Completed successfully
Poll 5: job=RUNNING video=processing
[CUT] Completed successfully
Poll 6: job=COMPLETED video=completed
=== B2. Each processor status ===
id | processor | status | started | completed | chunks | frames | error
----+-----------+-----------+-----------+-----------+--------+--------+-------
1 | asr | completed | 19:01:02 | 19:01:25 | 3 | 120 |
2 | cut | completed | 19:01:02 | 19:01:08 | 1 | 120 |
=== C1. Video final status ===
file_uuid | file_name | status | duration | fps | total_frames
--------------+---------------------+-----------+----------+-----+--------------
e11111111... | demo_test_video.mp4 | completed | 5.005 | 24 | 120
=== C2. Chunks produced ===
chunk_type | count
------------+-------
cut | 1
sentence | 3
=== C3. Job final status ===
id | status | current_processor
----+-----------+-------------------
161| COMPLETED | (null)
```
**Checklist during execution:**
| Stage | # | Check | Expected | Pass |
|-------|---|-------|----------|:----:|
| **A. Pre-worker** | A1 | `monitor_jobs.status` | `PENDING` | ☐ |
| | A2 | `processor_results` rows | = requested processor count | ☐ |
| | A3 | Each `processor_results.status` | `pending` | ☐ |
| **B. Running** | B1 | Job picked up (within poll interval) | status → `RUNNING` | ☐ |
| | B2 | SHA256 integrity check in logs | `[INTEGRITY] *.py checksum OK` | ☐ |
| | B3 | Each processor transitions | `pending → running → completed` | ☐ |
| | B4 | `started_at` populated | NOT NULL per processor | ☐ |
| | B5 | Processors complete without error | `error_message` is NULL | ☐ |
| | B6 | Max concurrent respected | ≤ `--max-concurrent` running at once | ☐ |
| **C. Post-completion** | C1 | `videos.status` | `completed` (not `failed`) | ☐ |
| | C2 | `chunks_produced` > 0 | ASR has sentence chunks | ☐ |
| | C3 | `monitor_jobs.status` | `COMPLETED` | ☐ |
| | C4 | `chunk` table has data | rows with this `file_uuid` | ☐ |
| | C5 | Chunk IDs formatted correctly | `{uuid}_{start}_{end}` | ☐ |
---
## 7. Check Results
Monitor job progress:
```bash
# Check job status
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/jobs?page=1&page_size=5&status=pending,running,completed,failed" \
| python3 -c "import json,sys;d=json.load(sys.stdin);[print(f'{j[\"uuid\"]}: {j[\"status\"]}') for j in d.get('jobs',[])]"
```
Output:
```
9eca53f422f668dd59a9995d29dc9388: completed
e1111111111111111111111111111111: completed
```
### ⚡ Intermediate Check — Bug #2: Chunk Fallback Verification
Verify that both new and old chunk_id formats resolve correctly:
```bash
# Pick a chunk_id from the DB
CHUNK_INFO=$("$PG_BIN/psql" -U accusys -d momentry -t -A -c "
SELECT chunk_id, id FROM dev.chunk WHERE file_uuid = '${FILE_UUID}' LIMIT 1
")
NEW_ID=$(echo "$CHUNK_INFO" | cut -d'|' -f1)
DB_ID=$(echo "$CHUNK_INFO" | cut -d'|' -f2)
echo "=== New format: $NEW_ID ==="
curl -s -w " HTTP %{http_code}" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/file/${FILE_UUID}/chunk/${NEW_ID}" \
| python3 -c "import json,sys;d=json.load(sys.stdin);print(f'chunk_id={d.get(\"chunk_id\")}')" 2>/dev/null
echo ""
echo "=== Old integer fallback (id=$DB_ID) ==="
curl -s -w " HTTP %{http_code}" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/file/${FILE_UUID}/chunk/${DB_ID}" \
| python3 -c "import json,sys;d=json.load(sys.stdin);print(f'chunk_id={d.get(\"chunk_id\")}')" 2>/dev/null
```
Output:
```
=== New format: e1111111111111111111111111111111_0_5 ===
chunk_id=e1111111111111111111111111111111_0_5 HTTP 200
=== Old integer fallback (id=1075655) ===
chunk_id=e1111111111111111111111111111111_0_5 HTTP 200
```
Both return `chunk_id=e1111111111111111111111111111111_0_5` — the fallback correctly resolves `id=1075655` to the same chunk.
### ⚡ Intermediate Check — Verify Chunks after Processing
```bash
PG_BIN="/Users/accusys/pgsql/18.3/bin"
# Count chunks produced
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT chunk_type, count(*) AS count
FROM dev.chunk
WHERE file_uuid = '${FILE_UUID}'
GROUP BY chunk_type
ORDER BY chunk_type
"
# Sample chunk content
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT chunk_id, chunk_type, start_frame, end_frame,
substring(text_content, 1, 60) AS text_preview
FROM dev.chunk
WHERE file_uuid = '${FILE_UUID}'
ORDER BY start_frame
LIMIT 5
"
```
Output:
```
chunk_type | count
------------+-------
cut | 1
sentence | 3
chunk_id | chunk_type | start_frame | end_frame | text_preview
--------------------------------------------------+------------+-------------+-----------+-----------------------------------------------------
e1111111111111111111111111111111_0_5 | cut | 0 | 120 | demo_test_video_auto_demo.mp4
e1111111111111111111111111111111_0_0 | sentence | 0 | 120 | test pattern test pattern color bars test pattern ...
```
Check per-processor results in DB:
```bash
"$PG_BIN/psql" -U accusys -d momentry -c "
SELECT processor, status, error_message,
to_char(started_at, 'HH24:MI:SS') AS started,
to_char(completed_at, 'HH24:MI:SS') AS completed,
COALESCE(chunks_produced, 0) AS chunks
FROM dev.processor_results
WHERE file_uuid='${FILE_UUID}'
ORDER BY id;
"
```
Output:
```
processor | status | error_message | started | completed | chunks
-----------+-----------+---------------+-----------+-----------+--------
asr | completed | | 19:01:02 | 19:01:25 | 3
cut | completed | | 19:01:02 | 19:01:08 | 1
```
**Checklist after processing:**
- [ ] `video.status = 'completed'` — pipeline finished
- [ ] `processor_results` all show `status = 'completed'`
- [ ] `chunks_produced > 0` — each processor produced output
- [ ] `chunk` table has rows with correct chunk_type (`cut`, `sentence`)
- [ ] `chunk_id` format is `{file_uuid}_{start}_{end}` (Bug #2 fix verified)
---
## 8. Search Chunks
After processing, search the generated chunks:
```bash
# Text search (ASR output)
curl -s -X POST "http://api.momentry.ddns.net/api/v1/search/universal" \
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
-H "Content-Type: application/json" \
-d "{\"query\": \"test\", \"uuid\": \"${FILE_UUID}\", \"limit\": 5}" \
| python3 -c "
import json,sys;d=json.load(sys.stdin)
print(f'Total hits: {d[\"total\"]}')
for r in d['results']:
if r.get('chunk_id'):
print(f' {r[\"chunk_id\"]}: \"{r.get(\"text\",\"\")[:60]}\" score={r.get(\"score\",0):.3f}')
"
```
Output:
```
Total hits: 3
e1111111111111111111111111111111_0_5: "test pattern test pattern..." score=0.423
e1111111111111111111111111111111_5_10: "silence" score=0.215
```
Get a specific chunk by ID:
```bash
# Single chunk detail
curl -s -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
"http://api.momentry.ddns.net/api/v1/file/${FILE_UUID}/chunk/${FILE_UUID}_0_5" \
| python3 -c "
import json,sys;d=json.load(sys.stdin)
print(f'Type: {d[\"chunk_type\"]} Rule: {d[\"rule\"]}')
print(f'Frame: {d[\"start_frame\"]}{d[\"end_frame\"]} FPS: {d[\"fps\"]}')
print(f'Text: {d[\"text_content\"][:100]}')
"
```
---
## 9. Health Check
```bash
# Basic health
curl -sf http://api.momentry.ddns.net/health | python3 -m json.tool
# Detailed health (services + pipeline + schema + resources)
curl -sf http://api.momentry.ddns.net/health/detailed | python3 -c "
import json,sys;d=json.load(sys.stdin)
p=d['pipeline'];s=d['schema']
print(f'Status: {d[\"status\"]}')
print(f'Build: {d[\"build_git_hash\"]}')
print(f'Services: postgres={d[\"services\"][\"postgres\"][\"status\"]} redis={d[\"services\"][\"redis\"][\"status\"]}')
print(f'Schema: {s[\"applied\"][-1][\"filename\"] if s[\"applied\"] else \"none\"} ({len(s[\"applied\"])}/{len(s[\"required\"])} applied, ok={s[\"ok\"]})')
print(f'Scripts: {p[\"scripts_count\"]} files, integrity={p[\"scripts_integrity\"][\"matched\"]}/{p[\"scripts_integrity\"][\"total\"]}')
print(f'Procs: ' + ' '.join([k for k,v in p['processors'].items() if v and k != 'total_py_files']))
"
```
Output:
```
Status: ok
Build: 0e73d2a
Services: postgres=ok redis=ok
Schema: migrate_fix_chunk_id_format.sql (8/8 applied, ok=True)
Scripts: 286 files, integrity=345/345
Procs: asr yolo face pose ocr cut caption scene story asrx probe visual_chunk
```
---
## 10. Schema Version
Each binary embeds a list of required migrations. At startup and via `/health/detailed`, the server verifies all migrations are applied.
```bash
# Check schema version via API
curl -sf http://api.momentry.ddns.net/health/detailed | python3 -c "
import json,sys;d=json.load(sys.stdin)['schema']
print(f'Table exists: {d[\"table_exists\"]}')
print(f'All OK: {d[\"ok\"]}')
for m in d['required']:
match = '✓' if any(a['filename']==m['filename'] and a['checksum']==m['checksum'] for a in d['applied']) else '✗'
print(f' {match} {m[\"filename\"]} {m[\"checksum\"][:16]}')
"
```
Output:
```
Table exists: True
All OK: True
✓ migrate_add_content_hash.sql 42b81554248c4bec
✓ migrate_add_registered_status.sql 566fdfcdc624f6fa
✓ migrate_add_schema_version.sql 585b31df6056a937
✓ migrate_cleanup_inactive_identities.sql daa52a0827b24a77
✓ migrate_fix_chunk_id_format.sql a1b2c3d4e5f6a7b8
✓ migrate_public_schema_v4.sql 973908076c614363
✓ migrate_public_schema_v4_tables.sql 1d62dc42e4dec8f4
✓ migrate_public_v4_complete.sql 2a6fda7d2c5660e4
```
If a migration is missing at startup:
```
[SCHEMA] 7/8 migrations applied. Missing: migrate_fix_chunk_id_format.sql
```
---
---
## Summary Checklist
After completing a pipeline run, verify all items:
### Registration
| # | Check | Expected | Pass |
|---|-------|----------|:----:|
| 1 | `videos.status` | `registered` | ☐ |
| 2 | file_uuid consistency | API response uuid = DB uuid | ☐ |
| 3 | Probe returns metadata | `duration > 0`, `fps > 0` | ☐ |
| 4 | Probe error (Bug #3) | Bad UUID → JSON error + 404 | ☐ |
### Processing
| # | Check | Expected | Pass |
|---|-------|----------|:----:|
| 5 | Job created | `monitor_jobs.status = PENDING` | ☐ |
| 6 | Processors queued | `processor_results` rows = requested count | ☐ |
| 7 | Worker picks up job | `monitor_jobs.status → RUNNING` | ☐ |
| 8 | SHA256 integrity (Bug #2) | `[INTEGRITY] *.py checksum OK` | ☐ |
| 9 | Each processor completes | `processor_results.status = completed` | ☐ |
| 10 | No processor errors | `error_message` all NULL | ☐ |
| 11 | Pipeline completes | `videos.status = completed` | ☐ |
### Results
| # | Check | Expected | Pass |
|---|-------|----------|:----:|
| 12 | Chunks produced | `chunk` table has > 0 rows | ☐ |
| 13 | Chunk ID format | `chunk_id = {uuid}_{start}_{end}` | ☐ |
| 14 | Chunk fallback (Bug #2) | Old integer ID → 200 via handler fallback | ☐ |
| 15 | Search works | `POST /search/universal` returns hits | ☐ |
| 16 | Schema version | `schema.ok = true` in `/health/detailed` | ☐ |
---
## Full Automation Script
Save as `demo_full_cycle.sh`:
```bash
#!/bin/bash
set -euo pipefail
API="http://api.momentry.ddns.net"
KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
PG="/Users/accusys/pgsql/18.3/bin"
# Generate test video
ffmpeg -y -f lavfi -i "testsrc=duration=5:size=640x480:rate=24" \
-f lavfi -i "anullsrc=r=44100:cl=mono" \
-c:v libx264 -preset ultrafast -crf 28 -c:a aac -shortest \
/tmp/auto_demo.mp4 2>/dev/null
# Register
UUID=$(curl -sf -X POST "$API/api/v1/files/register" \
-H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"file_path": "/tmp/auto_demo.mp4"}' | python3 -c "import json,sys;print(json.load(sys.stdin)['file_uuid'])")
echo "Registered: $UUID"
# Process
curl -sf -X POST "$API/api/v1/file/$UUID/process" \
-H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"processors":["asr","cut"]}' > /dev/null
echo "Processing triggered"
# Run worker
DATABASE_SCHEMA=dev target/debug/momentry_playground worker \
--max-concurrent 1 --poll-interval 3 &
WPID=$!
sleep 30
kill $WPID 2>/dev/null || true
# Results
"$PG/psql" -U accusys -d momentry -c "
SELECT processor, status FROM dev.processor_results WHERE file_uuid='$UUID' ORDER BY id"
echo "Done: $UUID"
```

View File

@@ -0,0 +1,493 @@
---
document_type: "demo_guide"
service: "MOMENTRY_CORE"
title: "M5API Pipeline Demo"
date: "2026-05-16"
version: "V1.0"
status: "active"
owner: "M5"
created_by: "OpenCode"
tags:
- "demo"
- "pipeline"
- "api"
- "m5api"
ai_query_hints:
- "M5API Pipeline demo"
- "如何透過 M5 的 API 執行 Pipeline"
related_documents:
- "GUIDES/Demo_EndToEnd.md"
- "GUIDES/API_ENDPOINTS.md"
---
# Momentry Core — M5API Pipeline Demo
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-05-16 |
| 文件版本 | V1.0 |
| 目標讀者 | developer |
| 預備知識 | 需有 API Key、M5 服務已啟動 |
---
## Prerequisites
```bash
API="https://m5api.momentry.ddns.net"
KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
```
---
## Step 1: System Health Check
```bash
curl -sf "$API/health" | jq '{ip, port, status, version, build_git_hash}'
```
Response:
```json
{
"ip": "192.168.110.201",
"port": 3002,
"status": "ok",
"version": "1.0.0",
"build_git_hash": "c41f7e0c"
}
```
All core services verified:
```bash
curl -sf "$API/health/detailed" | jq '{
services, schema: .schema.ok,
scripts: .pipeline.scripts_count,
integrity: .pipeline.scripts_integrity,
procs: [.pipeline.processors | to_entries[] | select(.value==true and .key!="total_py_files") | .key]
}'
```
Response:
```json
{
"services": {
"postgres": {"status": "ok"},
"redis": {"status": "ok"},
"qdrant": {"status": "ok"},
"mongodb": {"status": "ok"}
},
"schema": true,
"scripts": 286,
"integrity": {"matched": 345, "total": 345, "ok": true},
"procs": ["asr","yolo","face","pose","ocr","cut","caption","scene","story","asrx","probe","visual_chunk"]
}
```
---
## Step 2: List Registered Files
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/files?page=1&page_size=5" | \
jq '{total, files: [.data[]? | {name: .file_name[0:50], status}]}'
```
Response:
```json
{
"total": 56,
"files": [
{"name": "Charade (1963) Cary Grant & Audrey Hepburn ...", "status": "completed"},
{"name": "ExaSAN PCIe series - Director Ou Yu-Zhi ...", "status": "completed"},
{"name": "Old_Time_Movie_Show_-_Charade_1963.HD.mov", "status": "completed"},
{"name": "Old Felix the Cat Cartoon.mp4", "status": "unregistered"},
{"name": "short_clip.mov", "status": "completed"}
]
}
```
---
## Step 3: Register a New File
```bash
# POST with file_path (must exist on server filesystem)
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"file_path": "/path/to/video.mp4"}' \
"$API/api/v1/files/register" | jq '{success, file_uuid, file_name, file_type, duration, fps, already_exists}'
```
Response (new registration):
```json
{
"success": true,
"file_uuid": "3abeee81d94597629ed8cb943f182e94",
"file_name": "Charade (1963) Cary Grant & Audrey Hepburn ...mp4",
"file_type": "video",
"duration": 6785.014,
"fps": 23.976,
"already_exists": false
}
```
Response (duplicate content — SHA256 dedup):
```json
{
"success": true,
"already_exists": true,
"message": "Content already registered (identical file)"
}
```
---
## Step 4: Probe (ffprobe Metadata)
```bash
UUID="3abeee81d94597629ed8cb943f182e94"
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/probe" | \
jq '{name: .file_name, video: "\(.width)x\(.height)", fps, duration, cached, streams: [.streams[] | {type: .codec_type, codec: .codec_name}]}'
```
Response:
```json
{
"name": "Charade (1963) Cary Grant & Audrey Hepburn ...mp4",
"video": "720x304",
"fps": 23.976,
"duration": 6785.014,
"cached": true,
"streams": [
{"type": "video", "codec": "h264"},
{"type": "audio", "codec": "aac"}
]
}
```
Error cases:
```bash
# Non-existent UUID
curl -sf "https://api.momentry.ddns.net/api/v1/file/bad_uuid/probe"
# → {"error":"Video not found","file_uuid":"bad_uuid"} HTTP 404
# File deleted from disk
# → {"error":"File does not exist at registered path","file_uuid":"...","file_path":"..."} HTTP 404
```
---
## Step 5: Submit Processing Job
```bash
# Specific processors
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"processors":["asr","cut","yolo","face","pose","ocr"]}' \
"$API/api/v1/file/${UUID}/process" | jq '{job_id, file_uuid: .file_uuid[0:16], status}'
```
Response:
```json
{
"job_id": 167,
"file_uuid": "3abeee81d9459762",
"status": "PENDING"
}
```
> **All processors**: Send `{}` (empty body) to run all 12 processors.
> Available: `asr`, `cut`, `yolo`, `face`, `pose`, `ocr`, `asrx`, `visual_chunk`, `scene`, `story`, `caption`
---
## Step 6: Monitor Progress
```bash
while true; do
PROGRESS=$(curl -sf -H "X-API-Key: $KEY" "$API/api/v1/progress/${UUID}")
STATUS=$(echo "$PROGRESS" | jq -r '.status // "?"')
PROCS=$(echo "$PROGRESS" | jq -r '[.processors[]? | "\(.name)=\(.status)(\(.frames_processed))"] | join(" ")')
echo "$(date +%H:%M:%S): $PROCS"
echo "$PROCS" | grep -q "completed" && break
sleep 10
done
```
Typical output:
```
12:30:01: asr=pending(0) cut=pending(0) yolo=pending(0) face=pending(0) pose=pending(0) ocr=pending(0)
12:30:11: asr=running(0) cut=running(0) yolo=pending(0) face=pending(0) pose=pending(0) ocr=pending(0)
12:30:21: asr=running(0) cut=completed(8951) yolo=running(0) face=pending(0) pose=pending(0) ocr=pending(0)
12:30:31: asr=running(0) cut=completed(8951) yolo=completed(8951) face=running(0) pose=pending(0)
12:30:41: asr=running(0) cut=completed(8951) yolo=completed(8951) face=completed(8951) pose=running(0)
12:30:51: asr=completed(8951) cut=completed(8951) yolo=completed(8951) face=completed(8951) pose=completed(8951) ocr=running(0)
12:31:01: asr=completed(8951) cut=completed(8951) yolo=completed(8951) face=completed(8951) pose=completed(8951) ocr=completed(8951)
```
**Status transition chain**: `pending → running → completed`
Check job state:
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/jobs?uuid=${UUID}" | \
jq '[.jobs[]? | {id, status}]'
```
---
## Step 7: Verify Results
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/progress/${UUID}" | \
jq '{processors: [.processors[] | {name, status, frames: .frames_processed}]}'
```
Response:
```json
{
"processors": [
{"name": "asr", "status": "completed", "frames": 162568},
{"name": "cut", "status": "completed", "frames": 162568},
{"name": "yolo", "status": "completed", "frames": 162568},
{"name": "face", "status": "completed", "frames": 162568},
{"name": "pose", "status": "completed", "frames": 162568},
{"name": "ocr", "status": "completed", "frames": 162568}
]
}
```
---
## Step 8: Universal Search
```bash
# Search for a person name
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"query\":\"Audrey\",\"uuid\":\"${UUID}\",\"limit\":3}" \
"$API/api/v1/search/universal" | \
jq '{total, hits: [.results[]? | {chunk_id: .chunk_id[0:40], text: .text[0:80], score}]}'
```
Response:
```json
{
"total": 2,
"hits": [
{
"chunk_id": "3abeee81d94597629ed8cb943f182e94_998192",
"text": "Shorede stars two legends of classical Hollywood, Audrey Hepburn and Carrie Gran",
"score": 0.9
},
{
"chunk_id": "3abeee81d94597629ed8cb943f182e94_998193",
"text": "Shorede stars two legends of classical Hollywood, Audrey Hepburn and Carrie Gran",
"score": 0.9
}
]
}
```
```bash
# Search Chinese text
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"query\":\"導演\",\"uuid\":\"${UUID}\",\"limit\":3}" \
"$API/api/v1/search/universal" | jq '{total}'
```
**Search modes**: The universal search endpoint supports:
- Text match (ILIKE on `text_content` and `content` columns)
- Time range filtering (`time_range: [start, end]`)
- Speaker/person ID filtering
- Chunk type filtering
- Visual content filtering (objects, density, classes)
---
## Step 9: Get Chunk Detail
```bash
CHUNK_ID="3abeee81d94597629ed8cb943f182e94_998192"
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/chunk/${CHUNK_ID}" | \
jq '{chunk_id, chunk_type, text: .text_content, fps, start_frame, end_frame}'
```
Response:
```json
{
"chunk_id": "3abeee81d94597629ed8cb943f182e94_998192",
"chunk_type": "sentence",
"text": "Shorede stars two legends of classical Hollywood, Audrey Hepburn and Carrie Gran",
"fps": 23.976,
"start_frame": 2395281,
"end_frame": 2395341
}
```
---
## Step 10: Chunk Fallback (Stale Qdrant Compatibility)
Old integer-format chunk_ids from stale Qdrant payloads are automatically resolved via `WHERE id = int(chunk_id)`:
```bash
# Integer format (old Qdrant payload)
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/chunk/998192" | \
jq '{chunk_id, text: .text_content}'
```
Response (same chunk as above):
```json
{
"chunk_id": "3abeee81d94597629ed8cb943f182e94_998192",
"text": "Shorede stars two legends of classical Hollywood, Audrey Hepburn and Carrie Gran"
}
```
**Both formats work:**
- `chunk/{uuid}_{id}` → exact `chunk_id` match
- `chunk/{id}` → fallback by primary key `id`
---
## Step 11: File Detail
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}" | \
jq '{file_name, status, file_type, file_path}'
```
Response:
```json
{
"file_name": "Charade (1963) Cary Grant & Audrey Hepburn ...mp4",
"status": "completed",
"file_type": "video",
"file_path": "/Users/accusys/momentry/var/sftpgo/data/demo/Charade..."
}
```
---
## Step 12: File Identities
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/identities" | \
jq '{total, identities: [.data[]? | {name, face_count, confidence}]}'
```
Response:
```json
{
"total": 2,
"identities": [
{"name": "Audrey Hepburn", "face_count": 22082, "confidence": 0.93},
{"name": "Cary Grant", "face_count": 15334, "confidence": 0.91}
]
}
```
---
## Step 13: Identity Detail
```bash
# List all global identities
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/identities?page=1&page_size=3" | \
jq '{total, identities: [.data[]? | {name, type: .identity_type, source}]}'
```
```bash
# Get identity files (cross-file faces)
IDENTITY_UUID="c3545906-c82d-4b66-aa1d-150bc02decce"
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/identity/${IDENTITY_UUID}/files" | \
jq '{total, files: [.data[]? | {file_uuid: .file_uuid[0:16], face_count}]}'
```
---
## Step 14: Schema & Integrity Verification
```bash
curl -sf "$API/health/detailed" | jq '{
ip, port,
schema: .schema.ok,
migrations: [.schema.applied[]?.filename],
integrity: .pipeline.scripts_integrity
}'
```
Response:
```json
{
"ip": "192.168.110.201",
"port": 3002,
"schema": true,
"migrations": [
"migrate_add_content_hash.sql",
"migrate_add_registered_status.sql",
"migrate_add_schema_version.sql",
"migrate_cleanup_inactive_identities.sql",
"migrate_public_schema_v4_tables.sql",
"migrate_public_schema_v4.sql",
"migrate_public_v4_complete.sql",
"migrate_fix_chunk_id_format.sql",
"migrate_add_identity_indexes.sql"
],
"integrity": {"matched": 345, "total": 345, "ok": true}
}
```
---
## Full Automation Script
```bash
#!/bin/bash
set -euo pipefail
API="${API:-https://m5api.momentry.ddns.net}"
KEY="${KEY:-muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69}"
# 1. Health
echo "=== Health ==="
curl -sf "$API/health" | jq '{status, version, build_git_hash}'
# 2. Register file (argument: file path)
FILE_PATH="${1:?Usage: $0 <file_path>}"
echo "=== Register ==="
REG=$(curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"file_path\":\"$FILE_PATH\"}" "$API/api/v1/files/register")
echo "$REG" | jq '{success, file_uuid, file_name}'
UUID=$(echo "$REG" | jq -r '.file_uuid')
[ -z "$UUID" ] && { echo "Registration failed"; exit 1; }
# 3. Probe
echo "=== Probe ==="
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/probe" | \
jq '{name, fps, duration}'
# 4. Submit job
echo "=== Process ==="
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{}' "$API/api/v1/file/${UUID}/process" | jq '{job_id, status}'
# 5. Poll progress
echo "=== Waiting for pipeline... ==="
while true; do
PROGRESS=$(curl -sf -H "X-API-Key: $KEY" "$API/api/v1/progress/${UUID}")
STATUS=$(echo "$PROGRESS" | jq -r '.status // "?"')
echo "$(date +%H:%M:%S): $(echo "$PROGRESS" | jq -r '[.processors[]? | "\(.name)=\(.status)(\(.frames_processed))"] | join(" ")')"
echo "$PROGRESS" | jq -e '[.processors[]? | select(.status == "pending")] | length == 0' >/dev/null && break
sleep 10
done
# 6. Search
echo "=== Search ==="
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"query\":\"test\",\"uuid\":\"${UUID}\",\"limit\":3}" \
"$API/api/v1/search/universal" | jq '{total, hits: [.results[]? | {chunk_id: .chunk_id[0:30], text: .text[0:60]}]}'
echo ""
echo "✅ Done: $UUID"
```

View File

@@ -0,0 +1,412 @@
---
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Playground Binary Implementation Plan"
date: "2026-03-23"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
- "binary"
- "plan"
- "implementation"
- "playground"
ai_query_hints:
- "查詢 Playground Binary Implementation Plan 的內容"
- "Playground Binary Implementation Plan 的主要目的是什麼?"
- "如何操作或實施 Playground Binary Implementation Plan"
---
# Playground Binary Implementation Plan
| Item | Content |
|------|---------|
| Author | Warren |
| Created | 2026-03-23 |
| Document Version | V1.0 |
---
## Version History
| Version | Date | Purpose | Operator | Tool/Model |
|---------|------|---------|----------|------------|
| V1.0 | 2026-03-23 | Create implementation plan | Warren | OpenCode |
---
## Overview
Create separate `momentry_playground` binary with distinct configuration from `momentry` (production).
| Aspect | Production (`momentry`) | Development (`momentry_playground`) |
|--------|------------------------|-------------------------------------|
| **Port** | 3002 | 3003 |
| **Redis Prefix** | `momentry:` | `momentry_dev:` |
| **Worker** | Enabled | Disabled |
| **Purpose** | Production deployment | Testing/Development |
---
## Files to Modify
```
Files Changed: 6 files (+1 new)
├── src/core/config.rs ← Add server_port(), redis_key_prefix()
├── src/core/db/redis_client.rs ← Replace hardcoded prefixes
├── src/core/cache/redis_cache.rs ← Use configurable prefix
├── src/main.rs ← Update CLI defaults
├── src/playground.rs ← NEW: Development binary
├── Cargo.toml ← Add new binary
└── .env.development ← NEW: Dev environment config
```
---
## Implementation Steps
### Step 1: Update `src/core/config.rs`
Add after line 51 (after `MEDIA_BASE_URL`):
```rust
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| {
env::var("MOMENTRY_SERVER_PORT")
.unwrap_or_else(|_| "3002".to_string())
.parse()
.unwrap_or(3002)
});
pub static REDIS_KEY_PREFIX: Lazy<String> = Lazy::new(|| {
env::var("MOMENTRY_REDIS_PREFIX")
.unwrap_or_else(|_| "momentry:".to_string())
});
```
---
### Step 2: Update `src/core/db/redis_client.rs`
Replace all hardcoded `momentry:` prefixes with configurable prefix.
**Import at top:**
```rust
use crate::core::config::REDIS_KEY_PREFIX;
```
**Pattern for each method:**
```rust
let prefix = REDIS_KEY_PREFIX.as_str();
let key = format!("{}job:{}", prefix, uuid);
```
**Affected lines:**
| Line | Key Pattern |
|------|-------------|
| 47 | `job:{uuid}` |
| 81, 109 | `job:{uuid}:processor:{processor}` |
| 136, 146 | `progress:{uuid}` |
| 172 | `jobs:active` |
| 179 | `jobs:active``jobs:completed` |
| 187 | `jobs:active``jobs:failed` |
| 194 | `jobs:active` |
| 201, 208 | `health:momentry_core` |
| 214 | `monitor:job:{uuid}` |
| 242, 300 | `errors:{uuid}` |
| 258, 281 | `anomaly:alerts`, `anomaly:key:{key_id}` |
| 317, 346, 364, 392, 397 | `worker:job:{uuid}...` |
| 406, 410 | `worker:job:*` |
---
### Step 3: Update `src/core/cache/redis_cache.rs`
**Import:**
```rust
use crate::core::config::REDIS_KEY_PREFIX;
```
**Replace line 10:**
```rust
// Remove: const KEY_PREFIX: &str = "momentry:cache:";
```
**Update `prefixed_key` method (line 24):**
```rust
fn prefixed_key(&self, key: &str) -> String {
format!("{}cache:{}", REDIS_KEY_PREFIX.as_str(), key)
}
```
**Update tests (lines 161-162):**
```rust
#[test]
fn test_prefixed_key() {
// Note: This test will use the configured prefix
let cache = RedisCache::new().unwrap();
// With default prefix "momentry:"
assert_eq!(cache.prefixed_key("test"), "momentry:cache:test");
assert_eq!(cache.prefixed_key("video:abc"), "momentry:cache:video:abc");
}
```
---
### Step 4: Update `src/main.rs`
**Change CLI defaults (Lines 691-695):**
```rust
// Before:
#[arg(long, default_value = "3000")]
port: u16,
// After:
#[arg(long)]
port: Option<u16>,
```
**Update Server match arm (around line 2398):**
```rust
Commands::Server { host, port } => {
let port = port.unwrap_or_else(|| *crate::core::config::SERVER_PORT);
momentry_core::api::start_server(&host, port).await?;
Ok(())
}
```
**Update Redis key usage (Line 1098):**
```rust
// Before:
let key = format!("momentry:job:{}:processor:{}", uuid, processor);
// After:
let key = format!(
"{}job:{}:processor:{}",
crate::core::config::REDIS_KEY_PREFIX.as_str(),
uuid,
processor
);
```
---
### Step 5: Create `src/playground.rs`
```rust
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
// ... same imports as main.rs ...
fn main() -> Result<()> {
// Load development environment first
dotenv::from_filename(".env.development").ok();
tracing_subscriber::fmt::init();
tracing::info!("Starting momentry_playground (development binary)");
tracing::info!("Port: {}", *momentry_core::core::config::SERVER_PORT);
tracing::info!("Redis prefix: {}", *momentry_core::core::config::REDIS_KEY_PREFIX);
let cli = Cli::parse();
// ... rest identical to main.rs ...
}
```
---
### Step 6: Update `Cargo.toml`
**Add after line 90:**
```toml
[[bin]]
name = "momentry_playground"
path = "src/playground.rs"
```
**Add dependency (if not present):**
```toml
dotenv = "0.15"
```
---
### Step 7: Create `.env.development`
```bash
# Development Environment Configuration
# Used by: momentry_playground binary
# Server Configuration
MOMENTRY_SERVER_PORT=3003
MOMENTRY_REDIS_PREFIX=momentry_dev:
# Worker Configuration (disabled for development)
MOMENTRY_WORKER_ENABLED=false
MOMENTRY_MAX_CONCURRENT=1
MOMENTRY_POLL_INTERVAL=10
# Database (can use separate dev database)
DATABASE_URL=postgres://accusys@localhost:5432/momentry
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin
# Redis
REDIS_URL=redis://:accusys@localhost:6379
```
---
### Step 8: Update `.env` (Production)
Add these lines:
```bash
# Production Environment Configuration
# Used by: momentry binary
# Server Configuration
MOMENTRY_SERVER_PORT=3002
MOMENTRY_REDIS_PREFIX=momentry:
# Worker Configuration
MOMENTRY_WORKER_ENABLED=true
MOMENTRY_MAX_CONCURRENT=2
MOMENTRY_POLL_INTERVAL=5
```
---
## Testing Checklist
### 1. Build and Run Production Binary
```bash
cargo build --release --bin momentry
cargo run --bin momentry -- server
# Expected: Listening on http://127.0.0.1:3002
cargo run --bin momentry -- worker
# Expected: Worker started with momentry: prefix
```
### 2. Build and Run Development Binary
```bash
cargo build --bin momentry_playground
cargo run --bin momentry_playground -- server
# Expected: Listening on http://127.0.0.1:3003
```
### 3. Verify Redis Key Isolation
```bash
# Production data
redis-cli KEYS "momentry:*"
# Development data
redis-cli KEYS "momentry_dev:*"
# Should be separate
```
### 4. Run Both Simultaneously
```bash
# Terminal 1: Production
cargo run --bin momentry -- server
# Terminal 2: Development
cargo run --bin momentry_playground -- server
# Both should run without port conflicts
```
### 5. Unit Tests
```bash
cargo test --lib
# All tests should pass
```
---
## Redis Key Structure
### Production (`momentry:`)
```
momentry:job:{uuid} # Job status
momentry:job:{uuid}:processor:{name} # Processor progress
momentry:progress:{uuid} # Progress pub/sub
momentry:jobs:active # Active job set
momentry:jobs:completed # Completed job set
momentry:jobs:failed # Failed job set
momentry:health:momentry_core # Health status
momentry:cache:{key} # Cache entries
momentry:worker:job:{uuid} # Worker job
momentry:worker:job:{uuid}:processor:{name}
```
### Development (`momentry_dev:`)
```
momentry_dev:job:{uuid}
momentry_dev:job:{uuid}:processor:{name}
momentry_dev:progress:{uuid}
momentry_dev:jobs:active
momentry_dev:jobs:completed
momentry_dev:jobs:failed
momentry_dev:health:momentry_core
momentry_dev:cache:{key}
momentry_dev:worker:job:{uuid}
momentry_dev:worker:job:{uuid}:processor:{name}
```
---
## Potential Issues & Solutions
| Issue | Solution |
|-------|----------|
| `dotenv` crate not in dependencies | Add to Cargo.toml |
| Tests use hardcoded prefix | Update tests to use config, or use `#[cfg(test)]` defaults |
| Worker starts in playground | Check `MOMENTRY_WORKER_ENABLED=false` in `.env.development` |
| Port already in use | Graceful error message with suggestion to use `--port` flag |
| Mixed data in Redis | Ensure prefix is loaded before any Redis operations |
---
## Files Summary
| File | Lines Changed | Purpose |
|------|---------------|---------|
| `src/core/config.rs` | +15 | Add SERVER_PORT and REDIS_KEY_PREFIX |
| `src/core/db/redis_client.rs` | ~50 | Replace hardcoded prefixes |
| `src/core/cache/redis_cache.rs` | ~10 | Use configurable prefix |
| `src/main.rs` | ~15 | Update CLI defaults, Redis key usage |
| `src/playground.rs` | NEW (~2800) | Development binary |
| `Cargo.toml` | +4 | Add binary definition |
| `.env.development` | NEW (~20) | Development environment |
**Total**: ~60 lines modified + ~2800 lines new file
---
## Reference Documents
| Document | Purpose |
|----------|---------|
| `docs_v1.0/REFERENCE/SERVICES.md` | Port allocations |
| `docs_v1.0/REFERENCE/MOMENTRY_CORE_REDIS_KEYS.md` | Redis key design |
| `AGENTS.md` | Code style and conventions |
---
## Version History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2025-03-25 | OpenCode | Initial implementation plan |

View File

@@ -0,0 +1,418 @@
---
document_type: "demo_guide"
service: "MOMENTRY_CORE"
title: "Portal API Demo 示範指南"
date: "2026-04-30"
version: "V1.0"
status: "active"
current_state: "approved"
owner: "Warren"
created_by: "OpenCode"
tags:
- "portal"
- "api-demo"
- "wordpress"
- "frontend"
- "query"
- "operation"
- "application"
ai_query_hints:
- "查詢 Portal API Demo 示範指南的內容"
- "Portal API Demo 的主要目的是什麼?"
- "如何使用 Portal API Demo 頁面?"
- "Portal API Demo 頁面分類與功能"
- "如何設定 API Demo 頁面"
- "API Demo 查詢/展示/操作/應用頁面說明"
- "Momentry Playground 啟動方式"
related_documents:
- "GUIDES/API_INDEX.md"
- "GUIDES/API_ENDPOINTS.md"
- "GUIDES/PORTAL_DEVELOPMENT_PLAN.md"
- "FILE_UUID_SPEC.md"
---
# Portal API Demo 示範指南
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-04-30 |
| 文件版本 | V1.0 |
| 目標讀者 | developer, end_user |
| 預備知識 | 需有 API Key |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-04-30 | 創建 Portal API Demo 示範指南 | OpenCode | big-pickle |
---
## 概述
本文檔說明 Momentry Portal 中四個 API Demo 頁面的功能、設定方式與使用流程。
Demo 頁面以 **file-centric** 設計理念為核心,將檔案 (file) 作為主要管理目標,
身份 (identity) 為附隨目標,分類系統用於形容主體。
---
## 關鍵術語定義
| 術語 | 定義 |
|------|------|
| file_uuid | 檔案唯一識別碼,由 MAC、Birthday、Path、Filename 計算得出 |
| identity_uuid | 全域人員身份識別碼,跨檔案關聯 |
| file-centric | 以檔案為中心的設計理念,檔案是主要管理目標 |
| Birth/Migration | 檔案註冊與遷移的身份模型 |
| Portal | WordPress 前端展示與操作介面 |
| Playground | Momentry 開發伺服器 (port 3003) |
---
## 頁面分類總覽
Momentry Portal 提供四個 API Demo 頁面,涵蓋查詢、展示、操作、應用四大類別:
| 頁面 | 檔案名稱 | 類別 | 主要功能 |
|------|----------|------|----------|
| API Demo - 查詢 | `page-api-demo-query.php` | 查詢 | 檔案查詢、身份查詢、處理狀態、遷移歷史、語義搜尋 |
| API Demo - 展示 | `page-api-demo-display.php` | 展示 | 檔案詳情儀表板、身份視覺化、片段展示、分類結果 |
| API Demo - 操作 | `page-api-demo-operation.php` | 操作 | 檔案註冊、身份綁定、處理觸發、身份合併、處理器重試 |
| API Demo - 應用 | `page-api-demo-application.php` | 應用 | 完整工作流程、身份追蹤、遷移示範、批次處理、語義搜尋工作流 |
---
## 檔案位置
| 類型 | 路徑 | 說明 |
|------|------|------|
| 查詢頁面 | `/wp-content/themes/momentry/page-api-demo-query.php` | WordPress 頁面模板 |
| 展示頁面 | `/wp-content/themes/momentry/page-api-demo-display.php` | WordPress 頁面模板 |
| 操作頁面 | `/wp-content/themes/momentry/page-api-demo-operation.php` | WordPress 頁面模板 |
| 應用頁面 | `/wp-content/themes/momentry/page-api-demo-application.php` | WordPress 頁面模板 |
| 共用樣式 | `/wp-content/themes/momentry/style.css` | CSS 樣式表 |
| 設定說明 | `/wp-content/themes/momentry/API_DEMO_README.md` | 技術設定文件 |
---
## 環境需求
| 項目 | 狀態 | 說明 |
|------|------|------|
| WordPress | ✅ 已安裝 | 本地 WordPress 環境 |
| Momentry Theme | ✅ 已安裝 | 自定義 momentry 主題 |
| PostgreSQL | ✅ 已安裝 | Momentry Core 資料庫 |
| Momentry Playground | 🔄 需啟動 | 開發伺服器 (port 3003) |
---
## 設定步驟
### Step 1: 啟動 Momentry Playground
API Demo 頁面需要連線到 Momentry Playground API server
```bash
cd /Users/accusys/momentry_core_0.1
cargo run --bin momentry_playground -- server --host 0.0.0.0 --port 3003
```
驗證伺服器啟動:
```bash
curl http://localhost:3003/api/v1/health
```
### Step 2: 在 WordPress 建立頁面
1. 進入 WordPress 後台:`http://localhost/wp-admin`
2. 點擊 **Pages > Add New**
3. 建立以下四個頁面:
| 頁面標題 | URL Slug | Template |
|----------|----------|----------|
| API Demo - 查詢 | `api-demo-query` | API Demo - 查詢 |
| API Demo - 展示 | `api-demo-display` | API Demo - 展示 |
| API Demo - 操作 | `api-demo-operation` | API Demo - 操作 |
| API Demo - 應用 | `api-demo-application` | API Demo - 應用 |
1. 建立時,在右側 **Page Attributes** 選擇對應的 **Template**
2. 點擊 **Publish**
### Step 3: 訪問示範頁面
| 頁面 | URL |
|------|-----|
| 查詢 | `http://localhost/api-demo-query/` |
| 展示 | `http://localhost/api-demo-display/` |
| 操作 | `http://localhost/api-demo-operation/` |
| 應用 | `http://localhost/api-demo-application/` |
---
## 頁面功能詳解
### 1. 查詢頁面 (Query)
查詢頁面用於示範各類資料查詢 API 的使用方式。
#### 1.1 檔案查詢 (GET /api/v1/files/:uuid)
- **用途**:透過 file_uuid 查詢檔案的完整資訊
- **操作**:輸入 file_uuid點擊「查詢」
- **回應**:檔案元數據、處理狀態、分類標籤等
#### 1.2 身份查詢 (GET /api/v1/identities/:uuid)
- **用途**:查詢跨檔案的全域身份資訊
- **操作**:輸入 identity_uuid點擊「查詢」
- **回應**:身份名稱、關聯檔案、臉部特徵、品質分數
#### 1.3 處理狀態查詢 (GET /api/v1/jobs/:uuid/status)
- **用途**:查詢檔案的處理進度與各處理器狀態
- **操作**:輸入 file_uuid點擊「查詢」
- **回應**:處理進度百分比、已完成/失敗的處理器列表
#### 1.4 檔案遷移歷史 (GET /api/v1/files/:uuid/history)
- **用途**:查詢檔案因移動而產生的身份變更鏈
- **操作**:輸入 file_uuid點擊「查詢」
- **回應**parent_uuid 關聯鏈、遷移時間記錄
#### 1.5 語義搜尋 (POST /api/v1/search)
- **用途**:使用自然語言搜尋相關的影片片段或身份
- **操作**:輸入搜尋查詢,選擇搜尋類型,點擊「搜尋」
- **回應**:搜尋結果列表、相似度分數
---
### 2. 展示頁面 (Display)
展示頁面用於示範如何將 API 資料轉化為視覺化的展示元件。
#### 2.1 檔案詳情儀表板
- **用途**:整合展示檔案的元數據、處理進度、分類標籤等完整資訊
- **操作**:輸入 file_uuid點擊「載入」
- **展示內容**
- 基本資訊:檔案名稱、類型、時長、解析度、幀率
- 處理狀態:狀態徽章、處理進度、已完成處理器
- 分類標籤:分類標籤、語義標籤
- 關聯身份:檢測到身份數量、主要身份
#### 2.2 身份視覺化
- **用途**:展示身份的跨檔案關聯、臉部檢測統計、品質分數
- **操作**:輸入 identity_uuid點擊「視覺化」
- **展示內容**
- 身份名稱與品質分數
- 關聯檔案列表
- 臉部統計 (檢測次數、平均品質)
- 角度覆蓋視覺化
#### 2.3 影片片段展示
- **用途**:展示影片的語義片段、說話者分段、鏡頭切換等分類結果
- **操作**:輸入 file_uuid選擇片段類型點擊「載入片段」
- **片段類型**:語義片段、鏡頭切換、時間片段
#### 2.4 分類結果展示
- **用途**:展示 YOLO 檢測、姿勢估計、動作識別等視覺分類結果
- **操作**:輸入 file_uuid選擇處理器類型點擊「載入結果」
- **處理器類型**YOLO、Pose、Face、OCR
---
### 3. 操作頁面 (Operation)
操作頁面用於示範各類寫入與修改 API 的實際使用。
#### 3.1 檔案註冊 (POST /api/v1/register)
- **用途**:將新影片或音訊檔案註冊到系統
- **操作**:輸入檔案路徑,點擊「註冊」
- **快速測試**:提供預設測試路徑按鈕
#### 3.2 身份綁定 (POST /api/v1/identities/bind)
- **用途**:將臉部檢測綁定到特定身份
- **操作**:輸入 Face ID 和 Identity UUID點擊「綁定」
#### 3.3 處理觸發 (POST /api/v1/files/:uuid/process)
- **用途**:手動觸發檔案的處理流程
- **操作**:輸入 file_uuid選擇要執行的處理器 (ASR、YOLO、Face、OCR、Pose、CUT),點擊「觸發處理」
#### 3.4 身份合併 (POST /api/v1/identities/merge)
- **用途**:將多個身份合併為單一身份
- **操作**:輸入目標 Identity UUID 和來源 Identity UUIDs (逗號分隔),點擊「合併」
#### 3.5 處理器重試 (POST /api/v1/jobs/:uuid/retry)
- **用途**:重試失敗的處理器
- **操作**:輸入 file_uuid選擇要重試的處理器點擊「重試」
---
### 4. 應用頁面 (Application)
應用頁面示範結合多個 API 的實際應用場景與工作流程。
#### 4.1 完整工作流程示範
端到端展示從檔案註冊到處理完成的完整流程:
| 步驟 | 操作 | 說明 |
|------|------|------|
| 1 | 註冊檔案 | 輸入影片路徑,呼叫 `/register` |
| 2 | 查詢處理狀態 | 定期檢查 `/jobs/:uuid/status` 直到完成 |
| 3 | 查詢檢測結果 | 取得身份和片段資訊 |
| 4 | 搜尋身份 | 展示檔案中檢測到的身份列表 |
每步完成後自動解鎖下一步,狀態以顏色標示 (等待中/執行中/完成)。
#### 4.2 跨檔案身份追蹤
- **用途**:追蹤特定身份在所有檔案中的出現情況
- **操作**:輸入 Identity UUID點擊「開始追蹤」
- **展示內容**
- 身份名稱與關聯檔案數量
- 時間軸展示各檔案中的出現記錄
- 統計資訊 (總檢測次數、平均品質、覆蓋角度)
#### 4.3 檔案遷移與身份繼承示範
展示 Birth/Migration 模型的實際運作:
| 步驟 | 操作 | 說明 |
|------|------|------|
| 1 | 原始註冊 | 註冊原始路徑的檔案 |
| 2 | 模擬移動 | 使用新路徑重新註冊,系統產生新的 file_uuid |
| 3 | 查詢歷史 | 透過 `/files/:uuid/history` 查看遷移鏈 |
#### 4.4 批次檔案處理
- **用途**:一次註冊多個檔案,監控批次處理進度
- **操作**:輸入多個檔案路徑 (每行一個),點擊「批次註冊」
- **展示內容**:進度條、每個檔案的註冊結果
#### 4.5 語義搜尋與片段提取工作流
- **用途**:使用語義搜尋找到相關片段,然後提取詳細資訊
- **操作**:輸入自然語言查詢,點擊「搜尋」
- **展示內容**:搜尋結果摘要、詳細片段列表 (含相似度分數)
---
## API 端點參考
### 查詢類 API
| 端點 | 方法 | 說明 |
|------|------|------|
| `/api/v1/files/:uuid` | GET | 查詢檔案詳細資訊 |
| `/api/v1/files` | GET | 查詢檔案列表 |
| `/api/v1/identities/:uuid` | GET | 查詢身份資訊 |
| `/api/v1/jobs/:uuid/status` | GET | 查詢處理狀態 |
| `/api/v1/files/:uuid/history` | GET | 查詢遷移歷史 |
| `/api/v1/search` | POST | 語義搜尋 |
### 操作類 API
| 端點 | 方法 | 說明 |
|------|------|------|
| `/api/v1/register` | POST | 註冊檔案 |
| `/api/v1/identities/bind` | POST | 綁定身份 |
| `/api/v1/files/:uuid/process` | POST | 觸發處理 |
| `/api/v1/identities/merge` | POST | 合併身份 |
| `/api/v1/jobs/:uuid/retry` | POST | 重試處理器 |
---
## 常見問題
### Q1: 頁面無法連線到 API
- 確認 Playground server 已啟動:`cargo run --bin momentry_playground -- server`
- 檢查 API base URL 設定 (各頁面的 `const API_BASE = 'http://localhost:3003/api/v1'`)
- 確認 CORS 設定允許來自 WordPress 的請求
### Q2: 註冊檔案時返回錯誤
- 確認檔案路徑正確且檔案存在
- 確認 PostgreSQL 資料庫連線正常
- 檢查 Playground server 日誌
### Q3: 遷移歷史查詢無結果
- 確認檔案確實有 parent_uuid 記錄
- 使用 `SELECT file_uuid, parent_uuid FROM dev.videos WHERE parent_uuid IS NOT NULL;` 檢查資料庫
---
## 常用指令
```bash
# 啟動 Playground 伺服器
cargo run --bin momentry_playground -- server --host 0.0.0.0 --port 3003
# 檢查 API 健康狀態
curl http://localhost:3003/api/v1/health
# 查詢檔案列表
curl http://localhost:3003/api/v1/files?limit=5
# 註冊檔案
curl -X POST http://localhost:3003/api/v1/register \
-H "Content-Type: application/json" \
-d '{"file_path": "/path/to/video.mp4"}'
# 查詢檔案詳情
curl http://localhost:3003/api/v1/files/<file_uuid>
# 查詢遷移歷史
curl http://localhost:3003/api/v1/files/<file_uuid>/history
```
---
## 設計理念
### File-Centric 架構
Momentry 系統採用 **file-centric** 設計理念:
| 概念 | 說明 |
|------|------|
| **File (檔案)** | 主要管理目標file_uuid 為核心識別 |
| **Identity (身份)** | 附隨目標,跨檔案關聯人員身份 |
| **Classification (分類)** | 形容主體的標籤系統 (YOLO、ASR、Face 等處理器結果) |
### Birth/Migration 模型
| 概念 | 說明 |
|------|------|
| **Birth (註冊)** | 檔案首次註冊,產生初始 file_uuid |
| **Migration (遷移)** | 檔案移動後重新註冊,產生新 file_uuid 並記錄 parent_uuid |
| **Birthday (生日)** | 原始註冊時間,遷移時保留以證明身份連續性 |
### UUID 計算公式
```
file_uuid = SHA256(MAC_Address | Birthday | Canonical_Path | Filename)[0:32]
```
---
## 版本資訊
- 版本: V1.0
- 建立日期: 2026-04-30
- 文件更新: 2026-04-30

View File

@@ -0,0 +1,122 @@
# Portal 開發計畫
> 建立時間: 2026-04-25
> 狀態: 進行中
---
## 一、已完成功能
### 認證系統
- ✅ Login API (`/api/v1/auth/login`) - demo/demo
- ✅ Logout API (`/api/v1/auth/logout`)
- ✅ Session 管理 (localStorage)
- ✅ CORS 配置 (AllowOrigin::any)
### 納管檔案 (/files)
- ✅ 影片列表(分頁)
- ✅ 狀態過濾:未處理 / 已處理 / 全選
- ✅ 檔名搜尋 + 高亮匹配文字
- ✅ 影片詳情頁 (probe_json)
- ✅ 臉部群組顯示
- ✅ Tauri VideosResponse 修復 (count 欄位)
- ✅ API 狀態過濾修復 (pending/ready)
- ✅ 檔名搜尋功能修復
---
## 二、檔案管理增強
### 2.1 批量操作
- [ ] 全選/多選影片
- [ ] 批量納管Register
- [ ] 批量取消納管Unregister
- [ ] 批量觸發處理Process
- [ ] 批量刪除
### 2.2 進階搜尋過濾
- [ ] 日期範圍過濾
- [ ] 解析度過濾
- [ ] 檔案大小過濾
- [ ] 處理狀態過濾
### 2.3 處理控制
- [ ] 處理進度顯示Progress bar
- [ ] 處理日誌查看
- [ ] 錯誤訊息顯示
### 2.4 操作功能
- [ ] 取消納管Unregister
- [ ] 重新處理Reprocess
- [ ] 檔案下載
---
## 三、人物管理
### 3.1 人物列表 (/persons)
- [ ] 搜尋人物名稱
- [ ] 出現次數/時間排序
- [ ] 人物照片縮圖顯示
- [ ] 分頁
### 3.2 人物詳情 (/identity/:id)
- [ ] 所屬影片列表
- [ ] 時間軸顯示
- [ ] 截圖展示
- [ ] 出現片段時間
### 3.3 身份管理 (/identities)
- [ ] 已註冊身份列表
- [ ] 註冊新人物(從圖片)
- [ ] 人物更名
- [ ] 合併人物
- [ ] 刪除人物
### 3.4 臉部群組管理
- [ ] 群組審核/確認
- [ ] 未註冊群組列表
- [ ] 點擊註冊身份
- [ ] 群組截圖預覽
---
## 四、整體架構
```
首頁 (/home)
├── 搜尋 (/search)
├── 人物管理 (/persons)
│ ├── 已註冊人物列表
│ ├── 未註冊群組
│ └── 人物詳情 (/identity/:id)
├── 納管檔案 (/files)
│ ├── 影片列表
│ ├── 狀態過濾
│ ├── 檔名搜尋(高亮)
│ └── 影片詳情 (/videos/:uuid)
├── 設定 (/settings)
└── Console 面板
```
---
## 五、技術債務
### 待修復
- [ ] `hash_password` 函式未使用server.rs:35
- [ ] CORS 改為 AllowOrigin::any安全考量
- [ ] MongoCache 未考慮 status/query 過濾
### API 不一致
- [ ] API 返回 `count`,前端原使用 `total`
- [ ] Tauri 與 HTTP 模式返回格式差異
---
## 六、待確認問題
1. Identities 頁面是否要合併到 Persons
2. 是否需要「影片處理批次」功能?
3. 人物搜尋是否需要中文拼音/相似度?
4. 是否需要匯出報表功能?

View File

@@ -0,0 +1,472 @@
---
document_type: "demo_guide"
service: "MOMENTRY_CORE"
title: "Pipeline API Demo"
date: "2026-05-16"
version: "V1.0"
status: "active"
owner: "M5"
created_by: "OpenCode"
tags:
- "demo"
- "pipeline"
- "api"
- "playground"
ai_query_hints:
- "Pipeline API demo"
- "如何使用 API 執行 Pipeline"
- "3003 playground 操作"
related_documents:
- "GUIDES/Demo_EndToEnd.md"
- "GUIDES/API_ENDPOINTS.md"
---
# Momentry Core — Pipeline API Demo
| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-05-16 |
| 文件版本 | V1.0 |
| 目標讀者 | developer |
| 預備知識 | 需有 API Key、3003 playground 已啟動 |
---
## Table of Contents
1. [Health Check](#1-health-check)
2. [Register File](#2-register-file)
3. [Probe File](#3-probe-file)
4. [Submit Processing Job](#4-submit-processing-job)
5. [Monitor Progress (Polling)](#5-monitor-progress-polling)
6. [Run Worker](#6-run-worker)
7. [Verify Progress](#7-verify-progress)
8. [Universal Search](#8-universal-search)
9. [Chunk Detail](#9-chunk-detail)
10. [Chunk Fallback (Stale Qdrant)](#10-chunk-fallback-stale-qdrant)
11. [File Detail](#11-file-detail)
12. [List Identities](#12-list-identities)
13. [Schema & Integrity Verify](#13-schema--integrity-verify)
---
## 0. Env Setup
```bash
API="http://127.0.0.1:3003"
KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
```
---
## 1. Health Check
```bash
curl -sf "$API/health" | jq '{status, version, build_git_hash, uptime_ms}'
```
Response:
```json
{
"status": "ok",
"version": "1.0.0",
"build_git_hash": "c41f7e0c",
"uptime_ms": 1234567
}
```
```bash
curl -sf "$API/health/detailed" | jq '{
ip, port, services,
schema: .schema.ok,
scripts: .pipeline.scripts_count,
integrity: .pipeline.scripts_integrity,
procs: [.pipeline.processors | to_entries[] | select(.value==true and .key!="total_py_files") | .key]
}'
```
Response:
```json
{
"ip": "192.168.110.201",
"port": 3003,
"services": {
"postgres": {"status": "ok", "latency_ms": 6},
"redis": {"status": "ok", "latency_ms": 0},
"qdrant": {"status": "ok", "latency_ms": 1},
"mongodb": {"status": "ok", "latency_ms": 0}
},
"schema": true,
"scripts": 286,
"integrity": {"matched": 345, "total": 345, "ok": true},
"procs": ["asr","yolo","face","pose","ocr","cut","caption","scene","story","asrx","probe","visual_chunk"]
}
```
---
## 2. Register File
```bash
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"file_path": "/path/to/video.mp4"}' \
"$API/api/v1/files/register" | jq '{success, file_uuid, file_name, file_type, duration, fps, already_exists}'
```
Response:
```json
{
"success": true,
"file_uuid": "078975658e04529ee06f8d11cd7ba226",
"file_name": "Gamma 8-Director Chih-Lin Yang Shares His Experience:楊智麟導演經驗分享.mp4",
"file_type": "video",
"duration": 298.665042,
"fps": 29.97002997002997,
"already_exists": false
}
```
> **Note**: If the file was already registered (same `content_hash`), the response returns `already_exists: true`.
---
## 3. Probe File
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/probe" | \
jq '{name: .file_name, video: "\(.width)x\(.height)", fps, duration, cached, streams: [.streams[] | {type: .codec_type, codec: .codec_name}]}'
```
Response:
```json
{
"name": "Gamma 8-Director Chih-Lin Yang Shares His Experience:楊智麟導演經驗分享.mp4",
"video": "1280x720",
"fps": 29.97,
"duration": 298.665,
"cached": true,
"streams": [
{"type": "video", "codec": "h264"},
{"type": "audio", "codec": "aac"}
]
}
```
> **Error cases**:
> - Non-existent UUID → `{"error":"Video not found"}` + HTTP 404
> - File deleted from disk → `{"error":"File does not exist at registered path"}` + HTTP 404
---
## 4. Submit Processing Job
```bash
# Submit with specific processors
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"processors":["asr","cut","yolo","face","pose","ocr"]}' \
"$API/api/v1/file/${UUID}/process" | jq '{job_id, file_uuid: .file_uuid[0:16], status}'
```
Response:
```json
{
"job_id": 167,
"file_uuid": "078975658e04529e",
"status": "PENDING"
}
```
> **Submit all processors**: Send empty `{}` to run all processors.
> Available processors: `asr`, `cut`, `yolo`, `face`, `pose`, `ocr`, `asrx`, `visual_chunk`, `scene`, `story`, `caption`
---
## 5. Monitor Progress (Polling)
```bash
while true; do
PROGRESS=$(curl -sf -H "X-API-Key: $KEY" "$API/api/v1/progress/${UUID}")
STATUS=$(echo "$PROGRESS" | jq -r '.status // "?"')
PROCS=$(echo "$PROGRESS" | jq -r '[.processors[]? | "\(.name)=\(.status)(\(.frames_processed))"] | join(" ")')
echo "$(date +%H:%M:%S): $PROCS"
echo "$PROCS" | grep -q "completed" && break
sleep 10
done
```
Output:
```
12:30:01: asr=pending(0) cut=pending(0) yolo=pending(0) face=pending(0) pose=pending(0) ocr=pending(0)
12:30:11: asr=running(0) cut=running(0) yolo=pending(0) face=pending(0) pose=pending(0) ocr=pending(0)
12:30:21: asr=running(0) cut=completed(8951) yolo=running(0) face=pending(0) pose=pending(0) ocr=pending(0)
12:30:31: asr=running(0) cut=completed(8951) yolo=completed(8951) face=running(0) pose=pending(0) ocr=pending(0)
12:30:41: asr=running(0) cut=completed(8951) yolo=completed(8951) face=completed(8951) pose=running(0) ocr=pending(0)
12:30:51: asr=completed(8951) cut=completed(8951) yolo=completed(8951) face=completed(8951) pose=completed(8951) ocr=running(0)
12:31:01: asr=completed(8951) cut=completed(8951) yolo=completed(8951) face=completed(8951) pose=completed(8951) ocr=completed(8951)
```
**Status transitions**: `pending → running → completed`
Also monitor job state:
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/jobs?uuid=${UUID}" | \
jq '[.jobs[]? | {id, status}]'
```
---
## 6. Run Worker
```bash
DATABASE_SCHEMA=dev target/debug/momentry_playground worker \
--max-concurrent 2 --poll-interval 5
```
Worker output:
```
Starting worker with max_concurrent=2, poll_interval=5s
[CHECKSUMS] Loaded 345 entries from checksums.sha256
[INTEGRITY] asr_processor.py checksum OK
[ASR] Starting asr_processor.py
[ASR] Completed successfully
[INTEGRITY] cut_processor.py checksum OK
[CUT] Starting cut_processor.py
[CUT] Completed successfully
...
check_and_complete_job: results=6/6 → Job COMPLETED
```
---
## 7. Verify Progress
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/progress/${UUID}" | \
jq '{processors: [.processors[] | {name, status, frames: .frames_processed}]}'
```
Response:
```json
{
"processors": [
{"name": "asr", "status": "completed", "frames": 8951},
{"name": "cut", "status": "completed", "frames": 8951},
{"name": "yolo", "status": "completed", "frames": 8951},
{"name": "face", "status": "completed", "frames": 8951},
{"name": "pose", "status": "completed", "frames": 8951},
{"name": "ocr", "status": "completed", "frames": 8951}
]
}
```
---
## 8. Universal Search
```bash
# Search by text content
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"query\":\"導演\",\"uuid\":\"${UUID}\",\"limit\":5}" \
"$API/api/v1/search/universal" | \
jq '{total, hits: [.results[]? | {chunk_id: .chunk_id[0:40], text: .text[0:80], score}]}'
```
Response:
```json
{
"total": 5,
"hits": [
{"chunk_id": "078975658e04529ee06f8d11cd7ba226_39202", "text": "我 是 一 個 導 演", "score": 0.892},
{"chunk_id": "078975658e04529ee06f8d11cd7ba226_39203", "text": "我 是 一 個 導 演", "score": 0.890},
{"chunk_id": "078975658e04529ee06f8d11cd7ba226_39204", "text": "之前 在 拍 紀 錄 片", "score": 0.754}
]
}
```
Search by English text:
```bash
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"query\":\"camera\",\"uuid\":\"${UUID}\",\"limit\":3}" \
"$API/api/v1/search/universal" | jq '{total}'
```
---
## 9. Chunk Detail
```bash
# Get a specific chunk by its chunk_id
CHUNK_ID="078975658e04529ee06f8d11cd7ba226_39202"
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/chunk/${CHUNK_ID}" | \
jq '{chunk_id, chunk_type, text: .text_content, fps, start_frame, end_frame, rule}'
```
Response:
```json
{
"chunk_id": "078975658e04529ee06f8d11cd7ba226_39202",
"chunk_type": "sentence",
"text": "我 是 一 個 導 演",
"fps": 29.97,
"start_frame": 60,
"end_frame": 120,
"rule": "rule1"
}
```
---
## 10. Chunk Fallback (Stale Qdrant)
If a chunk_id is an old integer format (e.g., from stale Qdrant payloads), the handler falls back to `WHERE id = int(chunk_id)`:
```bash
# Old integer format → resolves via id fallback
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/chunk/39202" | \
jq '{chunk_id, text: .text_content}'
```
Response:
```json
{
"chunk_id": "078975658e04529ee06f8d11cd7ba226_39202",
"text": "我 是 一 個 導 演"
}
```
Both formats return the same chunk:
- `chunk/078975658e...226_39202` → exact `chunk_id` match
- `chunk/39202` → fallback by `id`
---
## 11. File Detail
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}" | \
jq '{file_name, status, duration, fps, file_type, width, height, total_frames}'
```
Response:
```json
{
"file_name": "Gamma 8-Director Chih-Lin Yang Shares His Experience:楊智麟導演經驗分享.mp4",
"status": "completed",
"duration": 298.665,
"fps": 29.97,
"file_type": "video",
"width": 1280,
"height": 720,
"total_frames": 8951
}
```
---
## 12. List Identities
```bash
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/identities" | \
jq '{total, identities: [.data[]? | {name, face_count, confidence}]}'
```
Response:
```json
{
"total": 2,
"identities": [
{"name": "Chih-Lin Yang", "face_count": 847, "confidence": 0.93},
{"name": "Interviewer", "face_count": 312, "confidence": 0.87}
]
}
```
---
## 13. Schema & Integrity Verify
```bash
curl -sf "$API/health/detailed" | jq '{
ip, port, schema: .schema.ok,
migrations: [.schema.applied[]?.filename],
integrity: .pipeline.scripts_integrity
}'
```
Response:
```json
{
"ip": "192.168.110.201",
"port": 3003,
"schema": true,
"migrations": [
"migrate_add_content_hash.sql",
"migrate_add_registered_status.sql",
"migrate_add_schema_version.sql",
"migrate_cleanup_inactive_identities.sql",
"migrate_public_schema_v4_tables.sql",
"migrate_public_schema_v4.sql",
"migrate_public_v4_complete.sql",
"migrate_fix_chunk_id_format.sql",
"migrate_add_identity_indexes.sql"
],
"integrity": {
"matched": 345,
"total": 345,
"ok": true
}
}
```
---
## Full Automation Script
```bash
#!/bin/bash
set -euo pipefail
API="${API:-http://127.0.0.1:3003}"
KEY="${KEY:-muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69}"
# Health
curl -sf "$API/health" | jq '{status, version, build_git_hash}'
# Register
REG=$(curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"file_path":"'"$1"'"}' "$API/api/v1/files/register")
echo "$REG" | jq '{success, file_uuid, file_name}'
UUID=$(echo "$REG" | jq -r '.file_uuid')
# Probe
curl -sf -H "X-API-Key: $KEY" "$API/api/v1/file/${UUID}/probe" | \
jq '{name: .file_name, fps, duration}'
# Process
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{}' "$API/api/v1/file/${UUID}/process" | jq '{job_id, status}'
# Worker
DATABASE_SCHEMA=dev target/debug/momentry_playground worker \
--max-concurrent 2 --poll-interval 5 &
WPID=$!
# Wait
while true; do
PROGRESS=$(curl -sf -H "X-API-Key: $KEY" "$API/api/v1/progress/${UUID}")
STATUS=$(echo "$PROGRESS" | jq -r '.status')
echo "$(date +%H:%M:%S): $(echo "$PROGRESS" | jq -r '[.processors[]? | "\(.name)=\(.status)(\(.frames_processed))"] | join(" ")')"
echo "$PROGRESS" | jq -e '[.processors[]? | select(.status == "pending")] | length == 0' >/dev/null && break
sleep 10
done
kill $WPID 2>/dev/null || true
# Search
curl -sf -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d "{\"query\":\"test\",\"uuid\":\"${UUID}\",\"limit\":3}" \
"$API/api/v1/search/universal" | jq '{total, hits: [.results[]? | {chunk_id: .chunk_id[0:30], text: .text[0:60]}]}'
echo "Done: $UUID"
```

View File

@@ -0,0 +1,499 @@
---
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Momentry 使用手冊"
date: "2026-03-21"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
- "momentry"
- "使用手冊"
ai_query_hints:
- "查詢 Momentry 使用手冊 的內容"
- "Momentry 使用手冊 的主要目的是什麼?"
- "如何操作或實施 Momentry 使用手冊?"
---
# Momentry 使用手冊
| 項目 | 內容 |
|------|------|
| 建立者 | Warren |
| 建立時間 | 2026-03-21 |
| 文件版本 | V1.0 |
---
## 版本歷史
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-21 | 創建使用手冊 | Warren | OpenCode |
---
**目標讀者**: 系統管理員、開發者
---
## 目錄
1. [快速開始](#1-快速開始)
2. [安裝與設定](#2-安裝與設定)
3. [CLI 命令參考](#3-cli-命令參考)
4. [影片管理](#4-影片管理)
5. [API Key 管理](#5-api-key-管理)
6. [第三方整合](#6-第三方整合)
7. [監控與維護](#7-監控與維護)
8. [疑難排解](#8-疑難排解)
---
## 1. 快速開始
### 1.1 最小啟動流程
```bash
# 1. 啟動服務
sudo launchctl load /Library/LaunchDaemons/com.momentry.postgresql.plist
sudo launchctl load /Library/LaunchDaemons/com.momentry.redis.plist
# 2. 設定環境變數
source .env
# 3. 啟動 API 伺服器
momentry server --host 127.0.0.1 --port 3000
# 4. 建立 API Key
momentry api-key create my-first-key --key-type user --ttl 90
```
### 1.2 驗證安裝
```bash
# 檢查系統狀態
curl http://localhost:3002/health
# 檢查版本
momentry --help
```
---
## 2. 安裝與設定
### 2.1 環境需求
| 項目 | 需求 |
|------|------|
| 作業系統 | macOS (Apple Silicon) |
| Rust | 1.70+ |
| PostgreSQL | 15+ |
| Redis | 7+ |
| Python | 3.11+ (用於 AI 處理) |
### 2.2 安裝步驟
```bash
# 1. 複製專案
git clone <repository-url>
cd momentry_core_0.1
# 2. 編譯
cargo build --release
# 3. 安裝到系統
cp target/release/momentry /usr/local/bin/
```
### 2.3 環境變數設定
建立 `.env` 檔案:
```bash
# Database
DATABASE_URL=postgres://accusys@localhost:5432/momentry
# Redis
REDIS_URL=redis://:accusys@localhost:6379
# Gitea (選用)
GITEA_URL=http://localhost:3000
# n8n (選用)
N8N_URL=https://n8n.momentry.ddns.net
# API Server
API_HOST=127.0.0.1
API_PORT=3000
# 監控目錄
WATCH_DIRECTORIES=~/Videos
```
---
## 3. CLI 命令參考
### 3.1 一般命令
| 命令 | 說明 |
|------|------|
| `momentry --help` | 顯示幫助 |
| `momentry server` | 啟動 API 伺服器 |
| `momentry register <path>` | 註冊影片 |
| `momentry process <uuid>` | 處理影片 |
| `momentry query <text>` | RAG 查詢 |
### 3.2 影片管理
```bash
# 註冊影片
momentry register /path/to/video.mp4
# 處理影片
momentry process <uuid>
# 生成縮圖
momentry thumbnails <uuid> --count 6
# 查看狀態
momentry status <uuid>
```
### 3.3 API Key 管理
```bash
# 建立 Key
momentry api-key create <name> --key-type <type> --ttl <days>
# 列出 Keys
momentry api-key list
# 驗證 Key
momentry api-key validate --key <key>
# 撤銷 Key
momentry api-key revoke --key <key>
# 請求輪換
momentry api-key rotate --key <key>
# 統計資訊
momentry api-key stats
```
### 3.4 Gitea 整合
```bash
# 建立 Token
momentry gitea create \
--username <user> \
--password <pwd> \
--token-name <name> \
--scopes "read:repository,write:repository"
# 列出 Tokens
momentry gitea list --username <user> --password <pwd>
# 刪除 Token
momentry gitea delete \
--username <user> \
--password <pwd> \
--token-name <name>
```
### 3.5 n8n 整合
```bash
# 建立 API Key
momentry n8n create \
--api-key <existing-key> \
--label <name> \
--expires-in-days 90
# 列出 Keys
momentry n8n list --api-key <key>
# 刪除 Key
momentry n8n delete --api-key <key> --label <name>
```
### 3.6 備份管理
```bash
# 列出備份
momentry backup list
# 清理舊備份
momentry backup cleanup --days 30
```
---
## 4. 影片管理
### 4.1 影片生命週期
```
上傳 → 註冊 → 處理 → 儲存 → 查詢
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
檔案 資料庫 AI分析 向量庫 RAG
```
### 4.2 註冊影片
```bash
# 自動偵測格式
momentry register ~/Videos/my-video.mp4
# 輸出:
# UUID: a1b2c3d4e5f6g7h8
# Duration: 120.5s
# Resolution: 1920x1080
```
### 4.3 處理流程
處理包含以下階段:
| 階段 | 說明 | 時間 (約) |
|------|------|-----------|
| Probe | 影片資訊分析 | 5s |
| ASR | 語音辨識 | 視長度 |
| OCR | 文字辨識 | 視長度 |
| YOLO | 物件偵測 | 視長度 |
| Cut | 場景切割 | 30s |
| Chunk | 內容分段 | 10s |
| Vector | 向量化 | 20s |
### 4.4 查詢影片
```bash
# RAG 查詢
momentry query "影片中有什麼內容?"
# 取得特定影片
momentry resolve <uuid>
```
---
## 5. API Key 管理
### 5.1 Key 類型
| 類型 | 前綴 | 用途 | 預設 TTL |
|------|------|------|----------|
| System | `msys_` | 系統內部 | 365 天 |
| User | `muser_` | 個人用戶 | 90 天 |
| Service | `msvc_` | 服務間通訊 | 180 天 |
| Integration | `mint_` | 第三方整合 | 30 天 |
| Emergency | `memg_` | 緊急存取 | 1 天 |
### 5.2 建立 Key
```bash
# 一般 Key
momentry api-key create my-service --key-type service --ttl 90
# 緊急 Key (24小時有效)
momentry api-key create emergency-access --key-type emergency
# 輸出:
# ✅ API Key created successfully!
# Key ID: msvc_xxxxxxxx
# API Key: msvc_xxxxxxxx_xxxxx_xxxxx
# Expires: 2026-06-19
```
### 5.3 Key 生命週期
```
建立 → 使用 → 過期/撤銷 → 清理
│ │ │ │
▼ ▼ ▼ ▼
資料庫 驗證 停用 定期刪除
```
### 5.4 安全建議
| 建議 | 說明 |
|------|------|
| 定期輪換 | 每 90 天更新 Key |
| 最小權限 | 只授予必要權限 |
| 監控使用 | 定期檢查使用統計 |
| 及時撤銷 | 異常時立即撤銷 |
---
## 6. 第三方整合
### 6.1 Gitea
```bash
# 建立 CI/CD 用 Token
momentry gitea create \
--username admin \
--password "your-password" \
--token-name "ci-pipeline" \
--scopes "read:repository,write:repository"
# 在 CI 中使用
export GITEA_TOKEN="token-sha1-value"
curl -H "Authorization: token $GITEA_TOKEN" \
http://localhost:3000/api/v1/user
```
### 6.2 n8n
```bash
# 建立工作流用 Key
momentry n8n create \
--api-key "existing-n8n-key" \
--label "workflow-key" \
--expires-in-days 90
# 在 n8n 中使用
# HTTP Request Header: X-N8N-API-Key: <key>
```
### 6.3 Webhook 通知
```bash
# 設定 Webhook
export WEBHOOK_URL="https://n8n.example.com/webhook/alerts"
export WEBHOOK_EVENTS="anomaly_detected,key_expired"
```
---
## 7. 監控與維護
### 7.1 系統監控
```bash
# 檢查服務狀態
ps aux | grep momentry
ps aux | grep postgres
redis-cli -a accusys ping
# 檢查日誌
tail -f /Users/accusys/momentry/log/momentry.log
tail -f /Users/accusys/momentry/log/redis.log
```
### 7.2 資料庫維護
```bash
# 檢查資料庫大小
psql -U accusys -d momentry -c "SELECT pg_size_pretty(pg_database_size('momentry'));"
# 清理過期記錄
momentry api-key stats # 檢查統計
# 定期清理由系統自動執行
```
### 7.3 備份
```bash
# 手動備份 PostgreSQL
pg_dump -U accusys momentry > backup_$(date +%Y%m%d).sql
# 恢復備份
psql -U accusys momentry < backup_20260321.sql
```
---
## 8. 疑難排解
### 8.1 常見問題
#### Q: 無法連接資料庫
```bash
# 檢查 PostgreSQL 狀態
pg_isready -h localhost -p 5432
# 檢查連線
psql -U accusys -d momentry -c "SELECT 1;"
```
#### Q: Redis 連線失敗
```bash
# 檢查 Redis 狀態
redis-cli -a accusys ping
# 檢查認證
redis-cli -a accusys INFO server | grep redis_version
```
#### Q: API Key 驗證失敗
```bash
# 檢查 Key 狀態
momentry api-key validate --key "your-key"
# 檢查是否過期
momentry api-key list
```
### 8.2 錯誤碼對照
| 錯誤碼 | 說明 | 解決方式 |
|--------|------|----------|
| `E001` | 資料庫連線失敗 | 檢查 PostgreSQL |
| `E002` | Redis 連線失敗 | 檢查 Redis |
| `E003` | API Key 無效 | 重新建立 Key |
| `E004` | 影片不存在 | 檢查 UUID |
| `E005` | 處理失敗 | 檢查日誌 |
### 8.3 日誌位置
| 日誌 | 路徑 |
|------|------|
| Momentry | `/Users/accusys/momentry/log/momentry.log` |
| PostgreSQL | `/Users/accusys/momentry/log/postgresql.log` |
| Redis | `/Users/accusys/momentry/log/redis.log` |
| Gitea | `/Users/accusys/momentry/log/gitea.log` |
---
## 附錄
### A. 完整命令列表
```bash
momentry --help
momentry register --help
momentry process --help
momentry api-key --help
momentry gitea --help
momentry n8n --help
momentry backup --help
```
### B. 環境變數總覽
| 變數 | 預設值 | 說明 |
|------|--------|------|
| `DATABASE_URL` | `postgres://accusys@localhost:5432/momentry` | PostgreSQL |
| `REDIS_URL` | `redis://:accusys@localhost:6379` | Redis |
| `GITEA_URL` | `http://localhost:3000` | Gitea |
| `N8N_URL` | `https://n8n.momentry.ddns.net` | n8n |
| `API_HOST` | `127.0.0.1` | API 主機 |
| `API_PORT` | `3000` | API 埠號 |
### C. 相關文件
| 文件 | 說明 |
|------|------|
| `docs_v1.0/IMPLEMENTATION/API_CURL_EXAMPLES.md` | API curl 範例 |
| `docs_v1.0/IMPLEMENTATION/N8N_INTEGRATION_GUIDE.md` | n8n 整合指南 |
| `docs_v1.0/REFERENCE/API_KEY_MANAGEMENT.md` | API Key 設計 |
| `CHANGELOG.md` | 版本記錄 |