docs: file_uuid generation rules for M4
This commit is contained in:
230
docs_v1.0/GUIDES/API_ACCESS.md
Normal file
230
docs_v1.0/GUIDES/API_ACCESS.md
Normal 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 個(句子 + 場景 + 時間)
|
||||
321
docs_v1.0/GUIDES/API_ENDPOINTS.md
Normal file
321
docs_v1.0/GUIDES/API_ENDPOINTS.md
Normal 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 範例
|
||||
106
docs_v1.0/GUIDES/API_ERROR_CODES.md
Normal file
106
docs_v1.0/GUIDES/API_ERROR_CODES.md
Normal 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
|
||||
129
docs_v1.0/GUIDES/API_INDEX.md
Normal file
129
docs_v1.0/GUIDES/API_INDEX.md
Normal 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 設計
|
||||
532
docs_v1.0/GUIDES/API_QUICK_REFERENCE.md
Normal file
532
docs_v1.0/GUIDES/API_QUICK_REFERENCE.md
Normal 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 工作流程整合
|
||||
310
docs_v1.0/GUIDES/API_REFERENCE.md
Normal file
310
docs_v1.0/GUIDES/API_REFERENCE.md
Normal 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` |
|
||||
427
docs_v1.0/GUIDES/API_TRAINING_MARCOM.md
Normal file
427
docs_v1.0/GUIDES/API_TRAINING_MARCOM.md
Normal 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 |
|
||||
159
docs_v1.0/GUIDES/DEMO_RUNNER_V1.0.0.md
Normal file
159
docs_v1.0/GUIDES/DEMO_RUNNER_V1.0.0.md
Normal 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.5–6 秒)
|
||||
- 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 內建 |
|
||||
| webbrowser(Python)| 開啟瀏覽器 | 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 渲染工具 |
|
||||
864
docs_v1.0/GUIDES/Demo_EndToEnd.md
Normal file
864
docs_v1.0/GUIDES/Demo_EndToEnd.md
Normal 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** | 1–4 | System check, scan, register, probe |
|
||||
| **處理中** | 5–6 | Submit job → Worker picks up → Each processor runs (pending→running→completed) |
|
||||
| **處理後** | 7–9 | 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"
|
||||
```
|
||||
493
docs_v1.0/GUIDES/M5API_Pipeline_Demo.md
Normal file
493
docs_v1.0/GUIDES/M5API_Pipeline_Demo.md
Normal 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"
|
||||
```
|
||||
412
docs_v1.0/GUIDES/PLAYGROUND_BINARY_IMPLEMENTATION.md
Normal file
412
docs_v1.0/GUIDES/PLAYGROUND_BINARY_IMPLEMENTATION.md
Normal 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 |
|
||||
418
docs_v1.0/GUIDES/PORTAL_API_DEMO_GUIDE.md
Normal file
418
docs_v1.0/GUIDES/PORTAL_API_DEMO_GUIDE.md
Normal 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
|
||||
122
docs_v1.0/GUIDES/PORTAL_DEVELOPMENT_PLAN.md
Normal file
122
docs_v1.0/GUIDES/PORTAL_DEVELOPMENT_PLAN.md
Normal 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. 是否需要匯出報表功能?
|
||||
472
docs_v1.0/GUIDES/Pipeline_API_Demo.md
Normal file
472
docs_v1.0/GUIDES/Pipeline_API_Demo.md
Normal 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"
|
||||
```
|
||||
499
docs_v1.0/GUIDES/USER_MANUAL.md
Normal file
499
docs_v1.0/GUIDES/USER_MANUAL.md
Normal 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` | 版本記錄 |
|
||||
Reference in New Issue
Block a user