Files
momentry_core/docs/DEMO_MANUAL.md
accusys 383201cacd feat: Initial v0.9 release with API Key authentication
## v0.9.20260325_144654

### Features
- API Key Authentication System
- Job Worker System
- V2 Backup Versioning

### Bug Fixes
- get_processor_results_by_job column mapping

Co-authored-by: OpenCode
2026-03-25 14:53:41 +08:00

16 KiB
Raw Blame History

Momentry Core API 示範手冊

項目 內容
版本 V1.0
日期 2026-03-25
狀態 完成

快速開始

Demo API Key

API Key:  muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69
Key ID:   muser_68600856036340bcafc01930eb4bd839
過期日:   2027-03-25

測試連線

curl http://localhost:3002/health
{"status":"ok","version":"0.1.0","uptime_ms":456464}

測試認證

curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
  http://localhost:3002/api/v1/videos | jq '.videos | length'
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 範例

基本格式

curl -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  URL

1.1 健康檢查(公開)

# 基本健康檢查
curl http://localhost:3002/health

# 詳細健康檢查(含服務狀態)
curl http://localhost:3002/health/detailed

1.2 列出影片

curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
  http://localhost:3002/api/v1/videos | jq '.'
{
  "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 搜尋影片

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 '.'
{
  "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 查詢進度

curl -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \
  http://localhost:3002/api/v1/progress/952f5854b9febad1 | jq '.'
{
  "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

{
  "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

{
  "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

{
  "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
/**
 * 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
/**
 * 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
/**
 * 註冊 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 測試腳本

#!/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 驗證腳本

#!/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

# 本地建立
./target/release/momentry api-key create "My App" --key-type user --ttl 90

5.2 列出 API Keys

./target/release/momentry api-key list

5.3 驗證 API Key

./target/release/momentry api-key validate --key "YOUR_API_KEY"

5.4 撤銷 API Key

./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. 聯絡支援