403 lines
8.1 KiB
Markdown
403 lines
8.1 KiB
Markdown
# Core API 完整測試報告
|
||
|
||
> 測試時間:2026-06-14
|
||
> Base URL:`http://localhost:3002`
|
||
> 所有端點均需附 `?api_key=KEY`
|
||
|
||
---
|
||
|
||
## 1. Files API
|
||
|
||
### GET /api/v1/files/scan
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `page_size` (int), `page` (int), `pattern` (string), `sort_by` (string), `sort_order` (string) |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | `files` (array) |
|
||
|
||
**欄位:**
|
||
```
|
||
file_name, relative_path, file_path, file_size, modified_time,
|
||
is_registered, file_uuid, status, registration_time, job_id
|
||
```
|
||
|
||
**注意:**
|
||
- 未註冊檔案 `file_uuid` 為 `null`(Rust 需 fallback 到 `file_path`)
|
||
- `status`:`"completed"` 或 `"unregistered"`
|
||
|
||
---
|
||
|
||
## 2. Search API
|
||
|
||
### POST /api/v1/search/llm-smart
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | POST |
|
||
| **Body** | `{"query": "string", "limit": int}` |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | `results` (array) |
|
||
|
||
**欄位:**
|
||
```
|
||
id, file_uuid, parent_id, scene_order, start_frame, end_frame, fps,
|
||
start_time, end_time, raw_text, summary, metadata, similarity,
|
||
file_name, serve_url, thumbnail_url
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Identities API
|
||
|
||
### GET /api/v1/identities (list)
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `page` (int), `per_page` (int) |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | `identities` (array) |
|
||
|
||
**欄位:**
|
||
```
|
||
id, identity_uuid, name, metadata
|
||
```
|
||
|
||
**metadata 欄位:**
|
||
```
|
||
role, aliases, starred, tmdb_gender, tmdb_aliases, tmdb_imdb_id,
|
||
tmdb_birthday, tmdb_deathday, tmdb_homepage, tmdb_movie_id,
|
||
tmdb_biography, tmdb_character, tmdb_cast_order, tmdb_department,
|
||
tmdb_popularity, tmdb_movie_title, tmdb_place_of_birth
|
||
```
|
||
|
||
**注意:** `starred` 在 metadata 內
|
||
|
||
### GET /api/v1/identities/search
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `q` (string) |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | `results` (array) |
|
||
|
||
**欄位(search result 不同於 list):**
|
||
```
|
||
identity_id, name, source, tmdb_id, file_uuid, trace_id, chunk_id,
|
||
start_frame, end_frame, fps, start_time, end_time, text_content
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Identity Detail API
|
||
|
||
### GET /api/v1/identity/{uuid}
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | 頂層物件 |
|
||
|
||
**欄位:**
|
||
```
|
||
success, identity_uuid, name, identity_type, source, status,
|
||
metadata, reference_data, tmdb_id, tmdb_profile, created_at, updated_at
|
||
```
|
||
|
||
**注意:** `tmdb_profile` 為本地絕對路徑(如 `/Users/accusys/momentry/output/identities/{uuid}/profile.jpg`)
|
||
|
||
### PATCH /api/v1/identity/{uuid} (update)
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | PATCH |
|
||
| **Body** | `{"name": "string"}` 或其他可更新欄位 |
|
||
| **HTTP** | 200 |
|
||
|
||
**Response:**
|
||
```json
|
||
{"success": true, "identity_uuid": "...", "updated_fields": ["name"]}
|
||
```
|
||
|
||
### DELETE /api/v1/identity/{uuid}
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | DELETE |
|
||
| **HTTP** | 204 (成功) / 404 (找不到) |
|
||
|
||
### POST /api/v1/identity (create)
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | POST |
|
||
| **Body** | 需要 `face_json_path` + `identity_name` 等欄位 |
|
||
| **HTTP** | 500 (缺少必要欄位時) |
|
||
|
||
**注意:** 此 API 需要 face JSON 路徑,不是單純建立空人物。需有 face data 才能建立。
|
||
|
||
---
|
||
|
||
## 5. Identity Faces API
|
||
|
||
### GET /api/v1/identity/{uuid}/faces
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `page_size` (int) |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | `data` (array) **不是** `faces` |
|
||
|
||
**欄位(data 內):**
|
||
```
|
||
id, file_uuid, frame_number, timestamp_secs, face_id, bbox, confidence
|
||
```
|
||
|
||
**Response 結構:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"identity_uuid": "...",
|
||
"name": "...",
|
||
"total": int,
|
||
"page": int,
|
||
"page_size": int,
|
||
"data": [...]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Identity Traces API
|
||
|
||
### GET /api/v1/identity/{uuid}/traces
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `page_size` (int) |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | `traces` (array) |
|
||
|
||
**欄位:**
|
||
```
|
||
file_uuid, trace_id, frame_count, first_frame, last_frame,
|
||
first_sec, last_sec, avg_confidence
|
||
```
|
||
|
||
**Response 結構:**
|
||
```json
|
||
{
|
||
"success": true,
|
||
"identity_uuid": "...",
|
||
"name": "...",
|
||
"total": int,
|
||
"page": int,
|
||
"page_size": int,
|
||
"total_faces": int,
|
||
"traces": [...]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Face Candidates API
|
||
|
||
### GET /api/v1/faces/candidates
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `page` (int), `page_size` (int) |
|
||
| **HTTP** | 200 |
|
||
| **Response Key** | `candidates` (array) |
|
||
|
||
**欄位:**
|
||
```
|
||
id, face_id, file_uuid, frame_number, confidence, bbox, attributes
|
||
```
|
||
|
||
**Response 結構:**
|
||
```json
|
||
{
|
||
"candidates": [...],
|
||
"total": int,
|
||
"page": int,
|
||
"page_size": int
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Bind / Unbind / Merge APIs
|
||
|
||
### POST /api/v1/identity/{uuid}/bind
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | POST |
|
||
| **Body** | `{"face_id": "string", "file_uuid": "string"}` |
|
||
| **HTTP** | 200 |
|
||
|
||
**Response:**
|
||
```json
|
||
{"success": true, "message": "Bound face ... to ...", "data": {"rows_affected": 0}}
|
||
```
|
||
|
||
**注意:** `face_id` 必須是 **string**,不能是 integer
|
||
|
||
### POST /api/v1/identity/{uuid}/unbind
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | POST |
|
||
| **Body** | `{"face_id": "string", "file_uuid": "string"}` |
|
||
| **HTTP** | 200 |
|
||
|
||
**Response:**
|
||
```json
|
||
{"success": true, "message": "Unbound face ... from ..."}
|
||
```
|
||
|
||
### POST /api/v1/identity/{uuid}/mergeinto
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | POST |
|
||
| **Body** | `{"into_uuid": "string"}` |
|
||
| **HTTP** | 200 (成功) / 404 (目標不存在) |
|
||
|
||
**注意:** URL 中的 `{uuid}` 是 **來源** identity,body 中的 `into_uuid` 是 **目標** identity
|
||
|
||
---
|
||
|
||
## 9. Media APIs
|
||
|
||
### GET /api/v1/file/{uuid}/thumbnail
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `frame` (int, default 30) |
|
||
| **HTTP** | 200 |
|
||
| **Content-Type** | `image/jpeg` |
|
||
| **Response** | Binary image data |
|
||
|
||
### GET /api/v1/file/{uuid}/video
|
||
|
||
| 項目 | 內容 |
|
||
|---|---|
|
||
| **Method** | GET |
|
||
| **Params** | `start_time` (float), `end_time` (float), `start_frame` (int), `end_frame` (int) |
|
||
| **HTTP** | 200 |
|
||
| **Content-Type** | `video/mp4` |
|
||
| **Response** | Binary MP4 data |
|
||
| **格式** | ISO Media, MP4 Base Media v1 |
|
||
|
||
---
|
||
|
||
## API Response Key 摘要
|
||
|
||
| 端點 | Response Key | 備註 |
|
||
|---|---|---|
|
||
| `/files/scan` | `files` | |
|
||
| `/search/llm-smart` | `results` | |
|
||
| `/identities` (list) | `identities` | |
|
||
| `/identities/search` | `results` | 欄位不同於 list |
|
||
| `/identity/{uuid}` | 頂層物件 | 包含 `success` |
|
||
| `/identity/{uuid}/faces` | `data` | **不是** `faces` |
|
||
| `/identity/{uuid}/traces` | `traces` | |
|
||
| `/faces/candidates` | `candidates` | |
|
||
|
||
---
|
||
|
||
## Rust Struct 映射建議
|
||
|
||
```rust
|
||
// Files
|
||
struct FileInfo {
|
||
file_uuid: String, // null 時 fallback file_path
|
||
file_name: String,
|
||
file_size: i64,
|
||
modified_time: String,
|
||
is_registered: bool,
|
||
}
|
||
|
||
// Identities (list)
|
||
struct PersonInfo {
|
||
identity_uuid: String,
|
||
name: String,
|
||
starred: bool, // 從 metadata.starred 提取
|
||
}
|
||
|
||
// Identity detail
|
||
struct IdentityDetail {
|
||
identity_uuid: String,
|
||
name: String,
|
||
identity_type: String,
|
||
source: String,
|
||
status: String,
|
||
tmdb_id: Option<i64>,
|
||
tmdb_profile: Option<String>, // 本地路徑
|
||
created_at: String,
|
||
}
|
||
|
||
// Face (from /faces endpoint)
|
||
struct FaceInfo {
|
||
id: i64,
|
||
file_uuid: String,
|
||
frame_number: i64,
|
||
timestamp_secs: f64,
|
||
face_id: Option<String>,
|
||
bbox: Option<BBox>,
|
||
confidence: f64,
|
||
}
|
||
|
||
// Trace
|
||
struct TraceInfo {
|
||
trace_id: i64,
|
||
file_uuid: String,
|
||
frame_count: i64,
|
||
first_frame: i64,
|
||
last_frame: i64,
|
||
first_sec: f64,
|
||
last_sec: f64,
|
||
avg_confidence: f64,
|
||
}
|
||
|
||
// Face Candidate
|
||
struct FaceCandidate {
|
||
id: i64,
|
||
face_id: Option<String>,
|
||
file_uuid: String,
|
||
frame_number: i64,
|
||
confidence: f64,
|
||
bbox: Option<BBox>,
|
||
}
|
||
|
||
struct BBox {
|
||
x: f64,
|
||
y: f64,
|
||
width: f64,
|
||
height: f64,
|
||
}
|
||
|
||
// Search result (不同於 list)
|
||
struct SearchIdentityResult {
|
||
identity_id: i64,
|
||
name: String,
|
||
source: String,
|
||
tmdb_id: Option<i64>,
|
||
file_uuid: Option<String>,
|
||
trace_id: Option<i64>,
|
||
start_time: f64,
|
||
end_time: f64,
|
||
text_content: Option<String>,
|
||
}
|
||
```
|