diff --git a/docs/CORE_API_REFERENCE.md b/docs/CORE_API_REFERENCE.md new file mode 100644 index 0000000..17d1da7 --- /dev/null +++ b/docs/CORE_API_REFERENCE.md @@ -0,0 +1,402 @@ +# 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, + tmdb_profile: Option, // 本地路徑 + created_at: String, +} + +// Face (from /faces endpoint) +struct FaceInfo { + id: i64, + file_uuid: String, + frame_number: i64, + timestamp_secs: f64, + face_id: Option, + bbox: Option, + 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, + file_uuid: String, + frame_number: i64, + confidence: f64, + bbox: Option, +} + +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, + file_uuid: Option, + trace_id: Option, + start_time: f64, + end_time: f64, + text_content: Option, +} +```