Files
momentry_studio/docs/PEOPLE_DESIGN.md

7.3 KiB
Raw Blame History

People 頁面設計文件

1. 分層架構

層級 負責
Rust (Backend) API 代理、檔案系統存取、API key 管理
Vue (Frontend) UI 渲染、狀態管理、使用者交互

2. Core API 端點(已實測驗證)

詳見 CORE_API_REFERENCE.md。以下摘要 People 頁面使用的端點:

功能 Method 端點 Response Key 備註
列出人物 GET /api/v1/identities identities 含 metadata.starred
人物詳情 GET /api/v1/identity/{uuid} 頂層 含 tmdb_profile 路徑
搜尋人物 GET /api/v1/identities/search?q= results 欄位不同於 list
大頭照 本地檔案讀取 非 Core API
人臉列表 GET /api/v1/identity/{uuid}/faces data 不是 faces
追蹤列表 GET /api/v1/identity/{uuid}/traces traces
改名 PATCH /api/v1/identity/{uuid} 頂層 Body: {"name": "..."}
刪除 DELETE /api/v1/identity/{uuid} 204/404
建立 POST /api/v1/identity 需要 face_json_path
合併 POST /api/v1/identity/{uuid}/mergeinto Body: {"into_uuid": "..."}
綁定 POST /api/v1/identity/{uuid}/bind Body: {"face_id": "string", "file_uuid": "..."}
解綁 POST /api/v1/identity/{uuid}/unbind Body: 同上
候選人臉 GET /api/v1/faces/candidates candidates
縮圖 GET /api/v1/file/{uuid}/thumbnail Binary
影片 GET /api/v1/file/{uuid}/video Binary

3. Rust Structs 定義

struct PersonInfo {
    identity_uuid: String,
    name: String,
    starred: bool,  // 從 metadata.starred 提取
}

struct FaceInfo {
    id: i64,
    file_uuid: String,
    frame_number: i64,
    timestamp_secs: f64,
    face_id: Option<String>,
    confidence: f64,
}

struct TraceInfo {
    trace_id: i64,
    file_uuid: String,
    frame_count: i64,
    first_sec: f64,
    last_sec: f64,
    avg_confidence: f64,
}

struct FaceCandidate {
    id: i64,
    face_id: Option<String>,
    file_uuid: String,
    frame_number: i64,
    confidence: f64,
}

struct SearchIdentityResult {
    identity_id: i64,
    name: String,
    source: String,
    tmdb_id: Option<i64>,
    file_uuid: Option<String>,
    start_time: f64,
    end_time: f64,
    text_content: Option<String>,
}

4. Rust Commands 清單

4.1 現有(需修改)

Command 修改內容
get_people PersonInfo 加 starred: bool,解析 metadata.starred
get_faces 解析 data key 而非 facesFaceInfo 加 timestamp_secs
get_video_stream 重構:寫入 temp file → 回傳 file:// URL

4.2 現有(不變)

Command 行為
get_traces GET /identity/{uuid}/traces
get_identity_profile 讀本地 profile.jpg → base64
update_identity_name PATCH /identity/{uuid}
delete_identity DELETE /identity/{uuid}
get_thumbnail 抓縮圖 → base64

4.3 新增

Command Core API Body/Params 回傳
search_identities GET /identities/search?q=... query: String, limit: usize Vec<SearchIdentityResult>
merge_identities POST /identity/{uuid}/mergeinto uuid: String, into_uuid: String ()
bind_face POST /identity/{uuid}/bind uuid, face_id: String, file_uuid: String ()
unbind_face POST /identity/{uuid}/unbind uuid, face_id: String, file_uuid: String ()
get_face_candidates GET /faces/candidates page: usize, per_page: usize Vec<FaceCandidate>

注意create_identity 需要 face_json_path不在 People 頁面直接使用。


5. Vue 前端元件樹

PeopleView.vue
├── Toolbar
│   ├── h1 "People"
│   ├── 搜尋框 → debounce 300ms → invoke('search_identities')
│   └── 建立按鈕暫留create 需要 face_json_path
│
├── PeopleGrid
│   └── PersonCard × N (v-for filteredPeople)
│       ├── 大頭照 → invoke('get_identity_profile') cache
│       ├── 名稱
│       ├── ⭐ 星號 (starred)
│       └── @click → selectPerson()
│
├── DetailModal
│   ├── Header: 大頭照 + 可編輯名稱 + UUID
│   ├── FacesTab (橫向捲軸)
│   │   └── face × N → confidence %
│   ├── TracesTab (縱向列表)
│   │   └── trace × N → 時間 + 信心% → @click → VideoPlayer
│   └── ActionsTab
│       ├── Delete → confirm + invoke('delete_identity')
│       ├── Merge → 搜尋目標 + invoke('merge_identities')
│       ├── Bind Face → 從 Candidates 選取 → invoke('bind_face')
│       └── Unbind Face → invoke('unbind_face')
│
├── CandidatesModal
│   └── FaceCandidate × N → @click → bind_face()
│
└── VideoPlayer (共用元件)
    └── <video :src="videoSrc"> (file:// URL from temp file)

6. CSS 設計系統

色彩

用途 色碼
Primary #2563eb
Secondary #64748b
Success #22c55e
Warning #f59e0b
Error #ef4444
Card BG #ffffff
Text Primary #1e293b
Text Secondary #64748b

元件樣式

元件 樣式
Grid repeat(auto-fill, minmax(100px, 1fr)) gap 16px
Card bg #fff, border 1px #e5e7eb, radius 12px, padding 16px, hover border #4f46e5
Avatar 56px 圓形, bg #eef2ff, color #4f46e5
Modal fixed inset:0, bg rgba(0,0,0,.6), centered, max-w 700px, max-h 80vh
VideoPlayer fixed inset:0, bg rgba(0,0,0,.85), video max-w 80vw max-h 60vh, radius 16px

7. 資料流

事件                    Vue 呼叫                          Rust Command
─────────────────      ──────────────                      ────────────
頁面載入               invoke('get_people')                get_people()
點擊人物               invoke('get_faces')                 get_faces()
                      invoke('get_traces')                 get_traces()
                      invoke('get_identity_profile')       get_identity_profile()
搜尋輸入(debounce)     invoke('search_identities')         search_identities()
編輯名稱               invoke('update_identity_name')      update_identity_name()
刪除人物               invoke('delete_identity')           delete_identity()
合併人物               invoke('merge_identities')          merge_identities()
綁定人臉               invoke('bind_face')                 bind_face()
解綁人臉               invoke('unbind_face')               unbind_face()
載入候選人臉           invoke('get_face_candidates')       get_face_candidates()
播放影片               invoke('get_video_stream')          get_video_stream() → temp file

8. 實作順序

  1. Phase 1: 修改 PersonInfo + FaceInfo struct + 重構 get_video_stream (temp file)
  2. Phase 2: 新增 search_identities + get_face_candidates
  3. Phase 3: 新增 merge_identities + bind_face + unbind_face
  4. Phase 4: Vue PeopleView 完整 UIgrid + modal + tabs + actions + 搜尋 + 合併 + 綁定)
  5. Phase 5: 更新 AGENTS.md + 提交