687 lines
16 KiB
Markdown
687 lines
16 KiB
Markdown
# Momentry Core API 示範手冊
|
||
|
||
| 項目 | 內容 |
|
||
|------|------|
|
||
| 建立者 | OpenCode |
|
||
| 建立時間 | 2026-03-25 |
|
||
| 文件版本 | V1.0 |
|
||
|
||
---
|
||
|
||
## 版本歷史
|
||
|
||
| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|
||
|------|------|------|--------|-----------|
|
||
| V1.0 | 2026-03-25 | 創建示範手冊,包含 Demo API Key 與完整範例 | OpenCode | deepseek-reasoner |
|
||
|
||
---
|
||
|
||
**狀態**: 完成
|
||
|
||
---
|
||
|
||
## 快速開始
|
||
|
||
### Demo API Key
|
||
|
||
```
|
||
API Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
|
||
Key ID: muser_68600856036340bcafc01930eb4bd839
|
||
過期日: 2027-03-25
|
||
```
|
||
|
||
### 測試連線
|
||
|
||
```bash
|
||
curl http://localhost:3002/health
|
||
```
|
||
|
||
```json
|
||
{"status":"ok","version":"0.1.0","uptime_ms":456464}
|
||
```
|
||
|
||
### 測試認證
|
||
|
||
```bash
|
||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||
http://localhost:3002/api/v1/videos | jq '.videos | length'
|
||
```
|
||
|
||
```json
|
||
13
|
||
```
|
||
|
||
---
|
||
|
||
## 環境 URL
|
||
|
||
| 環境 | URL | 用途 |
|
||
|------|-----|------|
|
||
| **本地開發** | `http://localhost:3002` | 本機開發測試 |
|
||
| **外部訪問** | `https://api.momentry.ddns.net` | n8n/WordPress/curl 生產環境 |
|
||
|
||
---
|
||
|
||
## 端點總覽
|
||
|
||
| 方法 | 端點 | 說明 | 認證 |
|
||
|------|------|------|------|
|
||
| GET | `/health` | 健康檢查 | 公開 |
|
||
| GET | `/health/detailed` | 詳細健康檢查 | 公開 |
|
||
| POST | `/api/v1/register` | 註冊影片 | 需要 |
|
||
| POST | `/api/v1/probe` | 探測影片資訊 | 需要 |
|
||
| POST | `/api/v1/search` | 語意搜尋 | 需要 |
|
||
| POST | `/api/v1/n8n/search` | n8n 格式搜尋 | 需要 |
|
||
| POST | `/api/v1/search/hybrid` | 混合搜尋 | 需要 |
|
||
| GET | `/api/v1/videos` | 列出所有影片 | 需要 |
|
||
| GET | `/api/v1/lookup` | 查詢影片 UUID | 需要 |
|
||
| GET | `/api/v1/progress/:uuid` | 處理進度 | 需要 |
|
||
| GET | `/api/v1/jobs` | 任務列表 | 需要 |
|
||
| GET | `/api/v1/jobs/:uuid` | 任務詳情 | 需要 |
|
||
|
||
---
|
||
|
||
## 1. curl 範例
|
||
|
||
### 基本格式
|
||
|
||
```bash
|
||
curl -H "X-API-Key: YOUR_API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
URL
|
||
```
|
||
|
||
### 1.1 健康檢查(公開)
|
||
|
||
```bash
|
||
# 基本健康檢查
|
||
curl http://localhost:3002/health
|
||
|
||
# 詳細健康檢查(含服務狀態)
|
||
curl http://localhost:3002/health/detailed
|
||
```
|
||
|
||
### 1.2 列出影片
|
||
|
||
```bash
|
||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||
http://localhost:3002/api/v1/videos | jq '.'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"videos": [
|
||
{
|
||
"uuid": "952f5854b9febad1",
|
||
"file_name": "ExaSAN PCIe series - Director Ou Yu-Zhi Shares His Experience.mp4",
|
||
"duration": 159.637188,
|
||
"width": 640,
|
||
"height": 360
|
||
},
|
||
...
|
||
]
|
||
}
|
||
```
|
||
|
||
### 1.3 搜尋影片
|
||
|
||
```bash
|
||
curl -X POST \
|
||
-H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"query": "ExaSAN", "limit": 5}' \
|
||
http://localhost:3002/api/v1/search | jq '.'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"results": [
|
||
{
|
||
"uuid": "952f5854b9febad1",
|
||
"chunk_id": "...",
|
||
"text": "...",
|
||
"score": 0.85,
|
||
"start_time": 0.0,
|
||
"end_time": 5.0
|
||
}
|
||
],
|
||
"total": 1,
|
||
"query": "ExaSAN",
|
||
"took_ms": 123
|
||
}
|
||
```
|
||
|
||
### 1.4 查詢進度
|
||
|
||
```bash
|
||
curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
|
||
http://localhost:3002/api/v1/progress/952f5854b9febad1 | jq '.'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"uuid": "952f5854b9febad1",
|
||
"overall_progress": 67,
|
||
"current_processor": "yolo",
|
||
"processors": [
|
||
{"name": "asr", "status": "completed"},
|
||
{"name": "cut", "status": "completed"},
|
||
{"name": "yolo", "status": "running"}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. n8n 範例
|
||
|
||
### 2.1 HTTP Request 節點設定
|
||
|
||
```
|
||
Method: POST
|
||
URL: https://api.momentry.ddns.net/api/v1/search
|
||
Authentication: None (使用 Header)
|
||
|
||
Headers:
|
||
┌─────────────────────┬──────────────────────────────────────────────────┐
|
||
│ Name │ Value │
|
||
├─────────────────────┼──────────────────────────────────────────────────┤
|
||
│ X-API-Key │ muser_68600856036340bcafc01930eb4bd839_... │
|
||
│ Content-Type │ application/json │
|
||
└─────────────────────┴──────────────────────────────────────────────────┘
|
||
|
||
Body Content (JSON):
|
||
{
|
||
"query": "{{ $json.search_term }}",
|
||
"limit": 5
|
||
}
|
||
```
|
||
|
||
### 2.2 n8n 搜尋 Workflow
|
||
|
||
```json
|
||
{
|
||
"nodes": [
|
||
{
|
||
"name": "Manual Trigger",
|
||
"type": "n8n-nodes-base.manualTrigger",
|
||
"position": [250, 300]
|
||
},
|
||
{
|
||
"name": "Set Search Term",
|
||
"type": "n8n-nodes-base.set",
|
||
"parameters": {
|
||
"values": {
|
||
"json": {
|
||
"search_term": "ExaSAN"
|
||
}
|
||
}
|
||
},
|
||
"position": [450, 300]
|
||
},
|
||
{
|
||
"name": "Search Videos",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"parameters": {
|
||
"method": "POST",
|
||
"url": "https://api.momentry.ddns.net/api/v1/search",
|
||
"authentication": "genericCredentialType",
|
||
"genericAuthType": "httpHeaderAuth",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "X-API-Key",
|
||
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||
}
|
||
]
|
||
},
|
||
"sendBody": true,
|
||
"bodyContentType": "json",
|
||
"specifyBody": "json",
|
||
"jsonBody": "={{ { \"query\": $json.search_term, \"limit\": 5 } }}"
|
||
},
|
||
"position": [650, 300]
|
||
},
|
||
{
|
||
"name": "Process Results",
|
||
"type": "n8n-nodes-base.code",
|
||
"parameters": {
|
||
"jsCode": "// Extract video results\nconst results = $input.first().json.results;\nreturn results.map(r => ({\n uuid: r.uuid,\n text: r.text,\n score: r.score,\n time: `${r.start_time}s - ${r.end_time}s`\n}));"
|
||
},
|
||
"position": [850, 300]
|
||
}
|
||
],
|
||
"connections": {
|
||
"Manual Trigger": {
|
||
"main": [[{"node": "Set Search Term"}]]
|
||
},
|
||
"Set Search Term": {
|
||
"main": [[{"node": "Search Videos"}]]
|
||
},
|
||
"Search Videos": {
|
||
"main": [[{"node": "Process Results"}]]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2.3 n8n 列出影片 Workflow
|
||
|
||
```json
|
||
{
|
||
"nodes": [
|
||
{
|
||
"name": "Get Videos",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"parameters": {
|
||
"method": "GET",
|
||
"url": "https://api.momentry.ddns.net/api/v1/videos",
|
||
"sendHeaders": true,
|
||
"headerParameters": {
|
||
"parameters": [
|
||
{
|
||
"name": "X-API-Key",
|
||
"value": "muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"position": [450, 300]
|
||
},
|
||
{
|
||
"name": "Extract Video List",
|
||
"type": "n8n-nodes-base.code",
|
||
"parameters": {
|
||
"jsCode": "const videos = $input.first().json.videos;\nreturn videos.map(v => ({\n json: {\n uuid: v.uuid,\n name: v.file_name,\n duration: Math.round(v.duration) + 's',\n resolution: `${v.width}x${v.height}`\n }\n}));"
|
||
},
|
||
"position": [650, 300]
|
||
},
|
||
{
|
||
"name": "Slack Notification",
|
||
"type": "n8n-nodes-base.slack",
|
||
"parameters": {
|
||
"channel": "#momentry",
|
||
"text": "=Found {{ $json.length }} videos:\n{{ $json.map(v => `• ${v.name} (${v.duration})`).join(`\n`) }}"
|
||
},
|
||
"position": [850, 300]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 2.4 n8n 定時同步 Workflow
|
||
|
||
```json
|
||
{
|
||
"nodes": [
|
||
{
|
||
"name": "Schedule Trigger",
|
||
"type": "n8n-nodes-base.scheduleTrigger",
|
||
"parameters": {
|
||
"rule": {
|
||
"interval": [{"field": "hours", "hours": 1}]
|
||
}
|
||
},
|
||
"position": [250, 300]
|
||
},
|
||
{
|
||
"name": "Get Pending Videos",
|
||
"type": "n8n-nodes-base.httpRequest",
|
||
"parameters": {
|
||
"method": "GET",
|
||
"url": "https://api.momentry.ddns.net/api/v1/videos"
|
||
},
|
||
"position": [450, 300]
|
||
},
|
||
{
|
||
"name": "Filter Processing",
|
||
"type": "n8n-nodes-base.filter",
|
||
"parameters": {
|
||
"conditions": {
|
||
"options": {"caseSensitive": true},
|
||
"conditions": [
|
||
{"id": "status", "leftValue": "{{ $json.status }}", "rightValue": "processing"}
|
||
]
|
||
}
|
||
},
|
||
"position": [650, 300]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. WordPress 範例
|
||
|
||
### 3.1 PHP 函數庫
|
||
|
||
```php
|
||
<?php
|
||
/**
|
||
* Momentry API Client
|
||
*/
|
||
|
||
class Momentry_API {
|
||
private const API_URL = 'https://api.momentry.ddns.net';
|
||
private const API_KEY = 'muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69';
|
||
|
||
/**
|
||
* 發送 API 請求
|
||
*/
|
||
private function request(string $endpoint, array $data = [], string $method = 'GET'): array {
|
||
$url = self::API_URL . $endpoint;
|
||
|
||
$args = [
|
||
'headers' => [
|
||
'X-API-Key' => self::API_KEY,
|
||
'Content-Type' => 'application/json',
|
||
],
|
||
'timeout' => 30,
|
||
];
|
||
|
||
if ($method === 'POST') {
|
||
$args['method'] = 'POST';
|
||
$args['body'] = json_encode($data);
|
||
}
|
||
|
||
$response = wp_remote_request($url, $args);
|
||
|
||
if (is_wp_error($response)) {
|
||
throw new Exception($response->get_error_message());
|
||
}
|
||
|
||
return json_decode(wp_remote_retrieve_body($response), true);
|
||
}
|
||
|
||
/**
|
||
* 列出所有影片
|
||
*/
|
||
public function list_videos(): array {
|
||
return $this->request('/api/v1/videos');
|
||
}
|
||
|
||
/**
|
||
* 搜尋影片內容
|
||
*/
|
||
public function search(string $query, int $limit = 10): array {
|
||
return $this->request('/api/v1/search', [
|
||
'query' => $query,
|
||
'limit' => $limit,
|
||
], 'POST');
|
||
}
|
||
|
||
/**
|
||
* 取得影片進度
|
||
*/
|
||
public function get_progress(string $uuid): array {
|
||
return $this->request("/api/v1/progress/{$uuid}");
|
||
}
|
||
|
||
/**
|
||
* 檢查健康狀態
|
||
*/
|
||
public function health_check(): array {
|
||
return $this->request('/health');
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3.2 短代碼 (Shortcode)
|
||
|
||
```php
|
||
<?php
|
||
/**
|
||
* WordPress 短代碼範例
|
||
*/
|
||
|
||
// 註冊短代碼
|
||
add_shortcode('momentry_videos', function($atts) {
|
||
$atts = shortcode_atts([
|
||
'limit' => 10,
|
||
], $atts);
|
||
|
||
$api = new Momentry_API();
|
||
|
||
try {
|
||
$result = $api->list_videos();
|
||
$videos = array_slice($result['videos'], 0, $atts['limit']);
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="momentry-videos">
|
||
<h3>影片列表</h3>
|
||
<ul>
|
||
<?php foreach ($videos as $video): ?>
|
||
<li>
|
||
<strong><?= esc_html($video['file_name']) ?></strong>
|
||
<br>
|
||
<small>
|
||
UUID: <?= esc_html($video['uuid']) ?>
|
||
| 時長: <?= gmdate("H:i:s", $video['duration']) ?>
|
||
</small>
|
||
</li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
|
||
} catch (Exception $e) {
|
||
return '<p class="error">載入失敗: ' . esc_html($e->getMessage()) . '</p>';
|
||
}
|
||
});
|
||
|
||
// 搜尋短代碼
|
||
add_shortcode('momentry_search', function($atts, $content = '') {
|
||
$query = sanitize_text_field($content);
|
||
|
||
if (empty($query)) {
|
||
return '<p>請提供搜尋關鍵字</p>';
|
||
}
|
||
|
||
$api = new Momentry_API();
|
||
|
||
try {
|
||
$result = $api->search($query);
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="momentry-search-results">
|
||
<h3>「<?= esc_html($query) ?>」搜尋結果</h3>
|
||
<?php if (empty($result['results'])): ?>
|
||
<p>沒有找到相關結果</p>
|
||
<?php else: ?>
|
||
<ul>
|
||
<?php foreach ($result['results'] as $item): ?>
|
||
<li>
|
||
<a href="/video/<?= esc_attr($item['uuid']) ?>?t=<?= (int)$item['start_time'] ?>">
|
||
<?= esc_html($item['text']) ?>
|
||
</a>
|
||
<br>
|
||
<small>相似度: <?= round($item['score'] * 100) ?>%</small>
|
||
</li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
|
||
} catch (Exception $e) {
|
||
return '<p class="error">搜尋失敗: ' . esc_html($e->getMessage()) . '</p>';
|
||
}
|
||
});
|
||
```
|
||
|
||
### 3.3 使用方式
|
||
|
||
在 WordPress 頁面或文章中:
|
||
|
||
```
|
||
[momentry_videos limit="5"]
|
||
|
||
[momentry_search]ExaSAN[/momentry_search]
|
||
```
|
||
|
||
### 3.4 REST API 整合
|
||
|
||
```php
|
||
<?php
|
||
/**
|
||
* 註冊 WordPress REST API 端點
|
||
*/
|
||
|
||
add_action('rest_api_init', function() {
|
||
register_rest_route('momentry/v1', '/search', [
|
||
'methods' => 'GET',
|
||
'callback' => function(WP_REST_Request $request) {
|
||
$query = sanitize_text_field($request->get_param('q'));
|
||
|
||
if (empty($query)) {
|
||
return new WP_Error('missing_query', '需要搜尋關鍵字', ['status' => 400]);
|
||
}
|
||
|
||
$api = new Momentry_API();
|
||
$result = $api->search($query);
|
||
|
||
return new WP_REST_Response($result, 200);
|
||
},
|
||
'permission_callback' => '__return_true',
|
||
]);
|
||
});
|
||
|
||
// 使用方式: GET /wp-json/momentry/v1/search?q=ExaSAN
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 疑難排解
|
||
|
||
### 4.1 常見錯誤
|
||
|
||
| 錯誤 | 原因 | 解決方案 |
|
||
|------|------|----------|
|
||
| `401 Unauthorized` | API Key 無效或過期 | 檢查 API Key 是否正確 |
|
||
| `500 Internal Server Error` | 伺服器錯誤 | 檢查 `/health/detailed` 服務狀態 |
|
||
| `Connection Timeout` | 網路問題 | 確認 `api.momentry.ddns.net` 可達 |
|
||
|
||
### 4.2 測試腳本
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# test_api.sh - Momentry API 測試腳本
|
||
|
||
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||
BASE_URL="http://localhost:3002"
|
||
|
||
echo "=== 1. 健康檢查 ==="
|
||
curl -s "$BASE_URL/health" | jq .
|
||
echo ""
|
||
|
||
echo "=== 2. 列出影片 ==="
|
||
curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos" | jq '.videos | length'
|
||
echo ""
|
||
|
||
echo "=== 3. 搜尋測試 ==="
|
||
curl -s -X POST -H "X-API-Key: $API_KEY" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"query": "test", "limit": 3}' \
|
||
"$BASE_URL/api/v1/search" | jq '.results | length'
|
||
echo ""
|
||
|
||
echo "=== 完成 ==="
|
||
```
|
||
|
||
### 4.3 驗證腳本
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
# verify_auth.sh - 驗證 API Key
|
||
|
||
API_KEY="muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69"
|
||
BASE_URL="http://localhost:3002"
|
||
|
||
# 測試 1: 無 API Key
|
||
echo "測試 1: 無 API Key"
|
||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/v1/videos")
|
||
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401,實際 $RESULT"
|
||
|
||
# 測試 2: 有 API Key
|
||
echo "測試 2: 有 API Key"
|
||
RESULT=$(curl -s -H "X-API-Key: $API_KEY" "$BASE_URL/api/v1/videos")
|
||
echo "$RESULT" | jq -e '.videos' > /dev/null && echo "✅ 成功取得資料" || echo "❌ 取得資料失敗"
|
||
|
||
# 測試 3: 無效 API Key
|
||
echo "測試 3: 無效 API Key"
|
||
RESULT=$(curl -s -o /dev/null -w "%{http_code}" -H "X-API-Key: invalid_key" "$BASE_URL/api/v1/videos")
|
||
[ "$RESULT" = "401" ] && echo "✅ 正確拒絕 (401)" || echo "❌ 預期 401,實際 $RESULT"
|
||
```
|
||
|
||
---
|
||
|
||
## 5. API Key 管理
|
||
|
||
### 5.1 建立新 API Key
|
||
|
||
```bash
|
||
# 本地建立
|
||
./target/release/momentry api-key create "My App" --key-type user --ttl 90
|
||
```
|
||
|
||
### 5.2 列出 API Keys
|
||
|
||
```bash
|
||
./target/release/momentry api-key list
|
||
```
|
||
|
||
### 5.3 驗證 API Key
|
||
|
||
```bash
|
||
./target/release/momentry api-key validate --key "YOUR_API_KEY"
|
||
```
|
||
|
||
### 5.4 撤銷 API Key
|
||
|
||
```bash
|
||
./target/release/momentry api-key revoke --key "YOUR_API_KEY"
|
||
```
|
||
|
||
---
|
||
|
||
## 附錄
|
||
|
||
### A. 影片 UUID 說明
|
||
|
||
UUID 是基於檔案路徑的 SHA256 哈希前 16 位:
|
||
|
||
```
|
||
/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4
|
||
↓
|
||
SHA256 Hash
|
||
↓
|
||
9760d0820f0cf9a7
|
||
```
|
||
|
||
### B. 處理器狀態
|
||
|
||
| 狀態 | 說明 |
|
||
|------|------|
|
||
| `pending` | 等待處理 |
|
||
| `running` | 處理中 |
|
||
| `completed` | 已完成 |
|
||
| `failed` | 失敗 |
|
||
|
||
### C. 支援的處理器
|
||
|
||
- **ASR**: 語音識別
|
||
- **CUT**: 場景剪切
|
||
- **YOLO**: 物件偵測
|
||
|
||
### D. 聯絡支援
|
||
|
||
- Email: support@momentry.ddns.net
|
||
- 文件: https://docs.momentry.ddns.net
|
||
- GitHub: https://github.com/anomalyco/momentry
|