From c90394897daa0488495194ea289e743110fb54d0 Mon Sep 17 00:00:00 2001 From: Accusys Date: Thu, 14 May 2026 15:12:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20trace=20debug=20=E2=80=94=20show=20Stran?= =?UTF-8?q?ger=5FNNN=20for=20unnamed=20traces=20instead=20of=20unknown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_v1.0/API_V1.0.0/API_REFERENCE_V1.0.0.md | 26 +- docs_v1.0/API_V1.0.0/API_REFERENCE_v1.0.0.md | 26 +- .../INTERNAL/API_DICTIONARY_V1.0.0.md | 2 +- .../RELEASE/RELEASE_API_REFERENCE_V1.0.0.md | 2 +- .../RELEASE/RELEASE_API_REFERENCE_v1.0.0.md | 2 +- .../REFERENCE/API_3002_VS_3003_COMPARISON.md | 205 +-- portal/.env.development | 2 +- portal/.gitignore | 22 - portal/VIDEO_DETAIL_UPDATE.md | 89 -- portal/momentry-portal@0.1.0 | 0 portal/package-lock.json | 291 ++++ portal/package.json | 3 + portal/src/App.vue | 20 +- portal/src/api/client.ts | 43 +- portal/src/api/search.rs:3:5 | 0 portal/src/components/TranslatableText.vue | 6 +- portal/src/main.rs:14:18 | 0 portal/src/router.ts | 41 +- portal/src/views/ChunkDetailView.vue | 40 +- portal/src/views/FaceCandidatesView.vue | 343 +++-- portal/src/views/FilesView.vue | 59 +- portal/src/views/HomeView.vue | 68 +- portal/src/views/IdentityDetailView.vue | 56 +- portal/src/views/LoginView.vue | 27 +- portal/src/views/PersonsView.vue | 163 +-- portal/src/views/SearchView.vue | 157 ++- portal/src/views/SettingsView.vue | 369 +++--- portal/src/views/VideoDetailView.vue | 101 +- scripts/swift_processors/.build/.lock | 2 +- scripts/swift_processors/.build/build.db | Bin 319488 -> 421888 bytes .../swift_processors/.build/plugin-tools.yaml | 1169 +++++++---------- .../plugins/cache/GenerateDoccReference | Bin 110328 -> 110328 bytes .../cache/GenerateDoccReference-state.json | 4 +- .../Resources/DWARF/GenerateDoccReference | Bin 1642739 -> 1642739 bytes .../.build/plugins/cache/GenerateManual | Bin 109472 -> 109472 bytes .../plugins/cache/GenerateManual-state.json | 4 +- .../Contents/Resources/DWARF/GenerateManual | Bin 1641833 -> 1641833 bytes scripts/swift_processors/swift_face.swift | 96 +- scripts/yolo_processor.py | 10 +- src/api/media_api.rs | 2 + src/api/middleware.rs | 60 +- yolov5nu.mlpackage/Manifest.json | 18 - 42 files changed, 1806 insertions(+), 1722 deletions(-) delete mode 100644 portal/.gitignore delete mode 100644 portal/VIDEO_DETAIL_UPDATE.md delete mode 100644 portal/momentry-portal@0.1.0 delete mode 100644 portal/src/api/search.rs:3:5 delete mode 100644 portal/src/main.rs:14:18 delete mode 100644 yolov5nu.mlpackage/Manifest.json diff --git a/docs_v1.0/API_V1.0.0/API_REFERENCE_V1.0.0.md b/docs_v1.0/API_V1.0.0/API_REFERENCE_V1.0.0.md index daaddea..1a2bbb2 100644 --- a/docs_v1.0/API_V1.0.0/API_REFERENCE_V1.0.0.md +++ b/docs_v1.0/API_V1.0.0/API_REFERENCE_V1.0.0.md @@ -20,6 +20,8 @@ owner: "Warren" | Development | `http://localhost:3003` | | Auth | Header `X-API-Key: ` (login endpoint unprotected) | +> **Note**: All examples below use production port 3002. For dev testing, replace `3002` with `3003`. + --- ## 1. System @@ -55,22 +57,32 @@ curl http://localhost:3002/health | 13 | GET | `/api/v1/file/:file_uuid` | Single file detail | | 14 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata | | 15 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline | -| 16 | GET | `/api/v1/file/:file_uuid/chunks` | List pre-chunks | +| 16 | GET | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) | | 17 | GET | `/api/v1/progress/:file_uuid` | Processing progress | | 18 | GET | `/api/v1/jobs` | Monitor jobs (filterable) | ```bash -curl -X POST http://localhost:3002/api/v1/files/register -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_path":"/sftpgo/data/demo/video.mp4"}' +curl -X POST http://localhost:3002/api/v1/files/register -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_path":"/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"}' ``` ```json {"success":true,"file_uuid":"3abeee81d94597629ed8cb943f182e94","duration":5954.0} ``` +```bash +curl -X POST http://localhost:3002/api/v1/unregister \ + -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \ + -H "Content-Type: application/json" \ + -d '{"uuid":"53e3a229bf68878b7a799e811e097f9c"}' +``` +```json +{"success":true,"uuid":"53e3a229bf68878b7a799e811e097f9c","message":"File unregistered successfully"} +``` + ```bash curl "http://localhost:3002/api/v1/files?page=1&page_size=2" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" ``` ```json -{"files":[{"file_name":"Charade (1963)..."}],"total":37} +{"success":true,"data":[{"file_uuid":"aeed7134...","file_name":"Charade (1963)...","status":"ready"}],"total":0,"page":1,"page_size":2} ``` --- @@ -92,14 +104,14 @@ curl "http://localhost:3002/api/v1/files?page=1&page_size=2" -H "X-API-Key: muse curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"query":"name","limit":2,"mode":"bm25","uuid":"3abeee81d94597629ed8cb943f182e94"}' ``` ```json -{"count":1,"results":[{"text":"What's your name?","score":0.90}]} +{"query":"name","results":[{"chunk_id":"100","text":"What's your name?","start_time":258.68,"score":0.90}],"total":5,"page":1,"page_size":20,"took_ms":42} ``` ```bash curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"query":"friends","limit":2,"mode":"bm25","uuid":"3abeee81d94597629ed8cb943f182e94"}' ``` ```json -{"count":1,"results":[{"text":"You won't find it difficult to make some new friends.","score":0.90}]} +{"query":"friends","results":[{"chunk_id":"104","text":"You won't find it difficult to make some new friends.","start_time":272.38,"score":0.90}],"total":3,"page":1,"page_size":20,"took_ms":38} ``` --- @@ -183,7 +195,7 @@ Returns MP4 video binary (3.0MB) with bbox overlay. curl "http://localhost:3002/api/v1/identities?page=1&page_size=3" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" ``` ```json -{"identities":[ +{"count":3,"page":1,"page_size":3,"identities":[ {"name":"Cary Grant","tmdb_id":2102}, {"name":"Audrey Hepburn","tmdb_id":187}, {"name":"Walter Matthau","tmdb_id":2091} @@ -228,7 +240,7 @@ curl -X POST "http://localhost:3002/api/v1/identity/a9a90105-6d6b-46ff-92da-0c3c curl "http://localhost:3002/api/v1/resources" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" ``` ```json -{"resources":[{"resource_id":"mxbai-embed-large-v1","resource_type":"embedding_model"}]} +{"success":true,"data":[{"resource_id":"mxbai-embed-large-v1","resource_type":"embedding_model"}],"message":"OK"} ``` --- diff --git a/docs_v1.0/API_V1.0.0/API_REFERENCE_v1.0.0.md b/docs_v1.0/API_V1.0.0/API_REFERENCE_v1.0.0.md index daaddea..1a2bbb2 100644 --- a/docs_v1.0/API_V1.0.0/API_REFERENCE_v1.0.0.md +++ b/docs_v1.0/API_V1.0.0/API_REFERENCE_v1.0.0.md @@ -20,6 +20,8 @@ owner: "Warren" | Development | `http://localhost:3003` | | Auth | Header `X-API-Key: ` (login endpoint unprotected) | +> **Note**: All examples below use production port 3002. For dev testing, replace `3002` with `3003`. + --- ## 1. System @@ -55,22 +57,32 @@ curl http://localhost:3002/health | 13 | GET | `/api/v1/file/:file_uuid` | Single file detail | | 14 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata | | 15 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline | -| 16 | GET | `/api/v1/file/:file_uuid/chunks` | List pre-chunks | +| 16 | GET | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) | | 17 | GET | `/api/v1/progress/:file_uuid` | Processing progress | | 18 | GET | `/api/v1/jobs` | Monitor jobs (filterable) | ```bash -curl -X POST http://localhost:3002/api/v1/files/register -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_path":"/sftpgo/data/demo/video.mp4"}' +curl -X POST http://localhost:3002/api/v1/files/register -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"file_path":"/Users/accusys/momentry/var/sftpgo/data/demo/video.mp4"}' ``` ```json {"success":true,"file_uuid":"3abeee81d94597629ed8cb943f182e94","duration":5954.0} ``` +```bash +curl -X POST http://localhost:3002/api/v1/unregister \ + -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" \ + -H "Content-Type: application/json" \ + -d '{"uuid":"53e3a229bf68878b7a799e811e097f9c"}' +``` +```json +{"success":true,"uuid":"53e3a229bf68878b7a799e811e097f9c","message":"File unregistered successfully"} +``` + ```bash curl "http://localhost:3002/api/v1/files?page=1&page_size=2" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" ``` ```json -{"files":[{"file_name":"Charade (1963)..."}],"total":37} +{"success":true,"data":[{"file_uuid":"aeed7134...","file_name":"Charade (1963)...","status":"ready"}],"total":0,"page":1,"page_size":2} ``` --- @@ -92,14 +104,14 @@ curl "http://localhost:3002/api/v1/files?page=1&page_size=2" -H "X-API-Key: muse curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"query":"name","limit":2,"mode":"bm25","uuid":"3abeee81d94597629ed8cb943f182e94"}' ``` ```json -{"count":1,"results":[{"text":"What's your name?","score":0.90}]} +{"query":"name","results":[{"chunk_id":"100","text":"What's your name?","start_time":258.68,"score":0.90}],"total":5,"page":1,"page_size":20,"took_ms":42} ``` ```bash curl -X POST http://localhost:3002/api/v1/search/universal -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" -H "Content-Type: application/json" -d '{"query":"friends","limit":2,"mode":"bm25","uuid":"3abeee81d94597629ed8cb943f182e94"}' ``` ```json -{"count":1,"results":[{"text":"You won't find it difficult to make some new friends.","score":0.90}]} +{"query":"friends","results":[{"chunk_id":"104","text":"You won't find it difficult to make some new friends.","start_time":272.38,"score":0.90}],"total":3,"page":1,"page_size":20,"took_ms":38} ``` --- @@ -183,7 +195,7 @@ Returns MP4 video binary (3.0MB) with bbox overlay. curl "http://localhost:3002/api/v1/identities?page=1&page_size=3" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" ``` ```json -{"identities":[ +{"count":3,"page":1,"page_size":3,"identities":[ {"name":"Cary Grant","tmdb_id":2102}, {"name":"Audrey Hepburn","tmdb_id":187}, {"name":"Walter Matthau","tmdb_id":2091} @@ -228,7 +240,7 @@ curl -X POST "http://localhost:3002/api/v1/identity/a9a90105-6d6b-46ff-92da-0c3c curl "http://localhost:3002/api/v1/resources" -H "X-API-Key: muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69" ``` ```json -{"resources":[{"resource_id":"mxbai-embed-large-v1","resource_type":"embedding_model"}]} +{"success":true,"data":[{"resource_id":"mxbai-embed-large-v1","resource_type":"embedding_model"}],"message":"OK"} ``` --- diff --git a/docs_v1.0/API_V1.0.0/INTERNAL/API_DICTIONARY_V1.0.0.md b/docs_v1.0/API_V1.0.0/INTERNAL/API_DICTIONARY_V1.0.0.md index ab20311..9e41deb 100644 --- a/docs_v1.0/API_V1.0.0/INTERNAL/API_DICTIONARY_V1.0.0.md +++ b/docs_v1.0/API_V1.0.0/INTERNAL/API_DICTIONARY_V1.0.0.md @@ -84,7 +84,7 @@ related_documents: | `GET` | `/api/v1/files` | Public | | `GET` | `/api/v1/files/scan` | Public | | `POST` | `/api/v1/files/register` | Public | -| `POST` | `/api/v1/files/unregister` | Public | +| `POST` | `/api/v1/unregister` | Public | | `GET` | `/api/v1/file/:file_uuid` | Public | | `GET` | `/api/v1/file/:file_uuid/probe` | Public | | `POST` | `/api/v1/file/:file_uuid/process` | Public | diff --git a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md index 1a943af..f857758 100644 --- a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md +++ b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_V1.0.0.md @@ -63,7 +63,7 @@ curl $BASE/health | 13 | GET | `/api/v1/file/:file_uuid` | Single file detail | | 14 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata | | 15 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline | -| 16 | GET | `/api/v1/file/:file_uuid/chunks` | List pre-chunks | +| 16 | GET | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) | | 17 | GET | `/api/v1/progress/:file_uuid` | Processing progress | | 18 | GET | `/api/v1/jobs` | Monitor jobs | diff --git a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md index 1a943af..f857758 100644 --- a/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md +++ b/docs_v1.0/API_V1.0.0/RELEASE/RELEASE_API_REFERENCE_v1.0.0.md @@ -63,7 +63,7 @@ curl $BASE/health | 13 | GET | `/api/v1/file/:file_uuid` | Single file detail | | 14 | GET | `/api/v1/file/:file_uuid/probe` | ffprobe metadata | | 15 | POST | `/api/v1/file/:file_uuid/process` | Start pipeline | -| 16 | GET | `/api/v1/file/:file_uuid/chunks` | List pre-chunks | +| 16 | GET | `/api/v1/file/:file_uuid/chunk/:chunk_id` | Single chunk detail (V1.0.2+) | | 17 | GET | `/api/v1/progress/:file_uuid` | Processing progress | | 18 | GET | `/api/v1/jobs` | Monitor jobs | diff --git a/docs_v1.0/REFERENCE/API_3002_VS_3003_COMPARISON.md b/docs_v1.0/REFERENCE/API_3002_VS_3003_COMPARISON.md index a78cc53..98f522d 100644 --- a/docs_v1.0/REFERENCE/API_3002_VS_3003_COMPARISON.md +++ b/docs_v1.0/REFERENCE/API_3002_VS_3003_COMPARISON.md @@ -1,6 +1,6 @@ # 3002 vs 3003 API 比較報告 -> **日期**: 2026-04-14 | **目的**: 分析正式版與開發版 API 差異 +**日期**: 2026-05-13 | **目的**: 正式版與開發版 API 差異(Release 前) --- @@ -8,191 +8,50 @@ | 項目 | 3002 (正式版) | 3003 (開發版) | |------|:---:|:---:| -| **Build 日期** | 2026-04-13 00:29:25 | 2026-04-14 23:01:47 | -| **程式碼狀態** | ❌ 舊版,缺少新 API | ✅ 最新版,包含所有新功能 | -| **需要重新編譯** | ✅ 是 | ❌ 否 | +| **Build 日期** | 2026-05-13 21:07 | 2026-05-13 (持續開發) | +| **Version** | v1.0.0 | v1.0.0 | +| **Build Git Hash** | `d34bcae+` | `d34bcae+` | +| **程式碼狀態** | ✅ 同步(從 dev release binary) | ✅ 最新 | +| **Schema** | `public`(需執行 `chunks→chunk` RENAME) | `dev` | --- -## 2. API 端點可用性比較 +## 2. 共同端點(JSON 結構一致) -### 2.1 完全相同的端點(JSON 結構一致) - -| 端點 | 3002 | 3003 | JSON 結構 | -|------|:---:|:---:|:---:| -| `GET /health` | ✅ 200 | ✅ 200 | ✅ 一致 | -| `GET /api/v1/person/:id` | ✅ 200 | ✅ 200 | ✅ 一致 | -| `GET /api/v1/chunks/:id/persons` | ✅ 200 | ✅ 200 | ✅ 一致 | -| `GET /api/v1/face/list` | ✅ 200 | ✅ 200 | ✅ 一致 | -| `GET /api/v1/face/:id` | ✅ 200 | ✅ 200 | ✅ 一致 | -| `POST /api/v1/face/search` | ✅ 200 | ✅ 200 | ✅ 一致 | - -### 2.2 3002 缺少的端點 - -| 端點 | 3002 | 3003 | 新增日期 | -|------|:---:|:---:|:---:| -| `GET /api/v1/person/list` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/auto-identify` | ❌ 405 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/suggest` | ❌ 405 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/merge` | ❌ 405 | ✅ 200/400 | 2026-04-14 | -| `POST /api/v1/person/merge/undo` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `GET /api/v1/person/merge/history` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `GET /api/v1/person/:id/similar` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `PATCH /api/v1/person/:id/confirm` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/:id/unbind-speaker` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/:id/reassign-speaker` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/:id/remove-appearance` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/:id/reassign-appearance` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/person/:id/split` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/search/universal` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `POST /api/v1/search/frames` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `GET /api/v1/search/persons` | ❌ 404 | ✅ 200 | 2026-04-14 | -| `GET /api/v1/person/:id/thumbnail` | ❌ 404 | ✅ 200 | 2026-04-14 | +| 端點 | 3002 | 3003 | 備註 | +|------|:---:|:---:|------| +| `GET /health` | ✅ | ✅ | 含 `version` + `build_git_hash` | +| `GET /health/detailed` | ✅ | ✅ | 同上 | +| `GET /api/v1/files` | ✅ | ✅ | `total` 從 DB 讀取(不再寫死 0) | +| `GET /api/v1/files/scan` | ✅ | ✅ | 含 `.jpg/.png` 掃描(不再限 mp4) | +| `GET /api/v1/file/:uuid/process` | ✅ | ✅ | | +| `GET /api/v1/file/:uuid/chunk/:id` | ✅ | ✅ | | +| `GET /api/v1/identities` | ✅ | ✅ | 含分頁 | +| `GET /api/v1/identities/:id` | ✅ | ✅ | | +| `GET /api/v1/identity_bindings` | ✅ | ✅ | | +| `POST /api/v1/search/universal` | ✅ | ✅ | | +| `GET /api/v1/resources` | ✅ | ✅ | | +| `GET /api/v1/traces/:tid/faces` | ✅ | ✅ | | +| `GET /api/v1/traces/:tid/video` | ✅ | ✅ | | --- -## 3. JSON 回應結構比較 +## 3. 差異 -### 3.1 GET /api/v1/person/:id (兩者一致) - -```json -{ - "success": "bool", - "person_id": "str", - "name": "str", - "face_identity_id": "null", - "speaker_id": "str|null", - "confidence": "float", - "appearance_count": "int", - "total_appearance_duration": "float", - "first_appearance_time": "float", - "last_appearance_time": "float", - "is_confirmed": "bool", - "created_at": "str", - "updated_at": "str" -} -``` - -### 3.2 3003 獨有的 JSON 結構 - -#### POST /api/v1/person/auto-identify -```json -{ - "success": "bool", - "message": "str", - "total_persons": "int", - "matched_speakers": "int", - "persons": [{ - "person_id": "str", - "name": "str|null", - "speaker_id": "str|null", - "appearance_count": "int", - "total_appearance_duration": "float", - "first_appearance_time": "float|null", - "last_appearance_time": "float|null", - "is_confirmed": "bool", - "speaker_confidence": "float|null" - }] -} -``` - -#### POST /api/v1/person/suggest -```json -{ - "success": "bool", - "naming_suggestions": [{ - "person_id": "str", - "current_name": "str|null", - "suggested_name": "str", - "confidence": "float", - "sources": [{"type": "str", "detail": "str", "weight": "float"}], - "action": "str (auto_apply|needs_review)" - }], - "merge_suggestions": [{ - "person_id": "str", - "merge_with": ["str"], - "confidence": "float", - "reasons": ["str"], - "action": "str" - }], - "total_naming": "int", - "total_merge": "int" -} -``` - -#### POST /api/v1/search/universal -```json -{ - "query": "str", - "results": [{ - "type": "str (chunk|frame|person)", - // chunk 類型 - "chunk_id": "str", - "chunk_type": "str", - "start_time": "float", - "end_time": "float", - "score": "float", - "text": "str|null", - "speaker_id": "str|null", - "metadata": "object" - // 或 frame 類型 - // 或 person 類型 - }], - "total": "int", - "took_ms": "int" -} -``` - ---- - -## 4. 資料來源隔離 - -| 層級 | 3002 | 3003 | +| 項目 | 3002 | 3003 | |------|------|------| -| PostgreSQL Schema | `public` | `dev` | +| PostgreSQL Schema | `public`(需 rename `chunks→chunk`) | `dev`(已為 `chunk`) | | MongoDB Database | `momentry` | `momentry_dev` | | Redis Prefix | `momentry:` | `momentry_dev:` | -| Qdrant Collection | `momentry_rule1` | `momentry_dev_rule1` | +| Qdrant Collection Prefix | `momentry_` | `momentry_dev_` | | Output Dir | `/Users/accusys/momentry/output` | `/Users/accusys/momentry/output_dev` | +| `.env` | `.env`(`DATABASE_SCHEMA=public`) | `.env.development`(`DATABASE_SCHEMA=dev`) | --- -## 5. 部署建議 +## 4. Release 必要步驟 -### 3002 需要更新 - -3002 運行的是 **2026-04-13** 的舊版程式碼,缺少 **17 個新 API 端點**。 - -**更新步驟**: -```bash -# 1. 確認程式碼已是最新 -git status - -# 2. 重新編譯 -cargo build --release - -# 3. 停止舊版 -kill $(lsof -ti:3002) - -# 4. 啟動新版 -nohup cargo run --release --bin momentry -- server > /tmp/momentry_prod.log 2>&1 & - -# 5. 驗證 -curl http://localhost:3002/test-schema -# 應回傳: SCHEMA=public - -curl http://localhost:3002/api/v1/person/list?limit=1 -# 應回傳: {"success":true,"persons":[...], "total":N} -``` - ---- - -## 6. 總結 - -| 類別 | 狀態 | -|------|------| -| **程式碼版本** | 3002 落後 1 天 (2026-04-13 vs 2026-04-14) | -| **API 端點** | 3002 缺少 17 個新端點 | -| **JSON 結構** | 相同端點的 JSON 結構完全一致 | -| **資料隔離** | ✅ 完全獨立 (public vs dev) | -| **需要行動** | ✅ 3002 需要重新編譯部署 | +1. **Binary**:使用 M5 交付的 `momentry_v1.0.0` 取代 port 3002 binary +2. **Schema**:`ALTER TABLE public.chunks RENAME TO chunk;` +3. **Deploy**:`bash deploy.sh`(9 步驟,含 vec0.dylib) +4. **Identity**:保留 15 TMDB + merge dev data(`file_uuid` 欄位輔助) diff --git a/portal/.env.development b/portal/.env.development index 576c3df..c6991b3 100644 --- a/portal/.env.development +++ b/portal/.env.development @@ -1,4 +1,4 @@ # Portal Development Environment VITE_APP_TITLE=Momentry Portal (Development) VITE_API_BASE_URL=http://127.0.0.1:3003 -VITE_API_KEY=muser_test_001 +VITE_API_KEY=muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69 diff --git a/portal/.gitignore b/portal/.gitignore deleted file mode 100644 index a12ae11..0000000 --- a/portal/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -# Dependencies -node_modules/ -target/ -dist/ - -# Tauri -src-tauri/icons/ -src-tauri/target/ - -# Environment -.env -.env.local - -# OS -.DS_Store -Thumbs.db - -# IDE -.vscode/ -.idea/ -*.swp -*.swo diff --git a/portal/VIDEO_DETAIL_UPDATE.md b/portal/VIDEO_DETAIL_UPDATE.md deleted file mode 100644 index 53bd9ed..0000000 --- a/portal/VIDEO_DETAIL_UPDATE.md +++ /dev/null @@ -1,89 +0,0 @@ -# VideoDetailView.vue 更新摘要 - -## 更新日期 -2026-04-27 - -## 更新內容 - -### 1. 跳回按鈕優化 -- **原版**: 純文字按鈕,樣式簡單 -- **新版**: - - 使用 Tailwind CSS 樣式化按鈕 - - `bg-gray-800 hover:bg-gray-700 rounded-lg transition` - - 清晰的文字:"返回納管檔案列表" - - 位於頁面左側,標題位於右側 - -### 2. Probe 消息展示優化 -- **原版**: 直接展示所有信息 -- **新版**: - - **基本信息 Grid**: Duration, Resolution, Frame Rate, Codec - - **影片串流**: 折疊式展示(
標籤) - - 提示文字:"click to expand" - - 最大高度限制:`max-h-64` - - **音訊串流**: 折疊式展示 - - 顯示數量標記:"音訊串流 (N)" - - 每個串流獨立折疊 - - **完整 Probe JSON**: - - 折疊式展示 - - 藍色標記:"詳細" - - 最大高度限制:`max-h-96` - -### 3. 新增 Status / Processing Status 展示 -- **狀態區塊**: 新增獨立的"處理狀態"區塊 - - UUID (truncate) - - Status (帶顏色標籤) - - completed: 綠色 - - processing: 藍色 - - pending: 灰色 - - failed: 紅色 - - 註冊時間 - -- **Processing Status Details**: - - **階段 (Phase)**: PROCESSING / COMPLETED 等 - - **處理器列表**: active_processors (藍色標籤) - - **進度條**: - - 每個處理器獨立進度條 - - 動態顏色(根據status) - - 顯示百分比和帧數 - - **Agent 狀態**: - - 顯示各Agent狀態(five_w1h, identity等) - - 進度百分比和完成數量 - -### 4. 新增輔助函數 -```typescript -// 狀態顏色函數 -getStatusColor(status: string): string -getProgressColor(status: string): string -getAgentStatusColor(status: string): string - -// Processing Status解析 -if (typeof v.processing_status === 'string') { - video.value.processing_status = JSON.parse(v.processing_status) -} -``` - -## 檔案大小 -- **原版**: 227 行 -- **新版**: 371 行(增加 144 行) - -## 編譯結果 -✅ `npm run build` 成功 -- VideoDetailView-CP9GNQZ0.js: 10.56 kB (gzip: 3.46 kB) - -## 測試建議 -1. 啟動 portal dev server: `cd portal && npm run dev` -2. 確保 API server 在 port 3003 -3. 登入 portal -4. 進入 "/files" 頁面 -5. 點擊任意影片查看詳情 - -## API 端點需求 -- `/api/v1/videos?uuid={uuid}` - 返回 video 物件(包含 processing_status) -- `/api/v1/videos/{uuid}/faces` - 返回 face clusters - -## 未來改進建議 -1. 添加 chunk 搜尋功能(在詳情頁搜尋該影片的 chunks) -2. 添加 processing_status 更新按鈕(重新載入狀態) -3. 添加處理器重新執行功能 -4. 添加時間軸視覺化(timeline visualization) - diff --git a/portal/momentry-portal@0.1.0 b/portal/momentry-portal@0.1.0 deleted file mode 100644 index e69de29..0000000 diff --git a/portal/package-lock.json b/portal/package-lock.json index f1ca5ce..adc0d31 100644 --- a/portal/package-lock.json +++ b/portal/package-lock.json @@ -12,12 +12,15 @@ "@tauri-apps/plugin-fs": "^2.5.0", "@tauri-apps/plugin-http": "^2.5.8", "@tauri-apps/plugin-shell": "^2.3.5", + "@types/three": "^0.184.1", "axios": "^1.6.5", "pinia": "^2.1.7", + "three": "^0.184.0", "vue": "^3.4.0", "vue-router": "^4.2.5" }, "devDependencies": { + "@tauri-apps/cli": "^2.10.1", "@vitejs/plugin-vue": "^5.0.0", "autoprefixer": "^10.4.17", "postcss": "^8.4.33", @@ -86,6 +89,12 @@ "node": ">=6.9.0" } }, + "node_modules/@dimforge/rapier3d-compat": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", + "license": "Apache-2.0" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -913,6 +922,238 @@ "url": "https://opencollective.com/tauri" } }, + "node_modules/@tauri-apps/cli": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.1.tgz", + "integrity": "sha512-rpEbaJ/HzNb6fwsquwoAbq29/Vt4gADhS423A8fdkwL4edJ0wZmoB8ar7O6JPDL834MUKOCm/rrJ7c9oAaEaYQ==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.11.1", + "@tauri-apps/cli-darwin-x64": "2.11.1", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.1", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.1", + "@tauri-apps/cli-linux-arm64-musl": "2.11.1", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.1", + "@tauri-apps/cli-linux-x64-gnu": "2.11.1", + "@tauri-apps/cli-linux-x64-musl": "2.11.1", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.1", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.1", + "@tauri-apps/cli-win32-x64-msvc": "2.11.1" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.1.tgz", + "integrity": "sha512-6eEKMBXsQPCuM1EmvrjT2+aBuxWQuFdKdW8pzNuNQtpq45nEEpBlD5gr8pUeAyOU1DQKlkFaEc/MPBxb/Pfjtg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.1.tgz", + "integrity": "sha512-LQUO7exfRWjWALNhetph5guWpMeHphRpokOLk0OIbTTExaNwJNFu3I4vb+CCM/4G/QGoZe/5XikZOJdNEFP1ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.1.tgz", + "integrity": "sha512-5i/awiBCRRhOUG8yjn0fMHXIWD5Ez8eEk5LtvOxyQrKuJkRaZDvnbIjZbE183blAwkoA4xN3aO/prJiqscl02Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.1.tgz", + "integrity": "sha512-9LrwDw3S9Fygtw/Q6WDhOP+3svJRGAsejeE+GKrc0eO1ThMVhwi2LL6hw4dlKw93IfS7VY1G19sWGxJ/NcU4nA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.1.tgz", + "integrity": "sha512-mNA5dbbqPqDUdTIwdUYYuhO2GvIe9UnB2r0VU2njxBOS3Opbx4gKNC5yP0Iu4rYmEmqdlwry9VzGZQ3wq9dyFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.1.tgz", + "integrity": "sha512-fZj3Gwq+6fUs305T5WQiD5iSGJw+j/4w/HGmk4sHDAcy+rp9zU5eaxB7nOyz5/I/nkNAuKPqfp6uIbiUBXkBCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.1.tgz", + "integrity": "sha512-XFxGxOvHM7jjeD6ozCKdGfhzJ7lERYDGZl1/Kb4fsvchaJsfLJ981TlyTG8Qy/gFq+f5GitH3bfrX9JAkjPEyw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.1.tgz", + "integrity": "sha512-d5C2/Zm+68v7R9wTuTCjRQEVrWjcdMkJBZ1+rXse+QdMMlTB9+u9PDNDLw9PQflWxYLaYZ7tjxxL9Nb9II6PbA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.1.tgz", + "integrity": "sha512-YdeVWFAR1pTXzUU6NLstPq4G6OLxuDrXCXEBdmBH+5EZIDXUx0D2kJlz3+YjpazkKvAzYpgziTsyRagls0OfRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.1.tgz", + "integrity": "sha512-VBGkuH0eB9K9LLSMv361Gzr5Ou72sCS4+ztpmkWEQ+wd/amhcYOsf3X6qn1RJZDzIhiOYHJEOysZUC3baD01rA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.1.tgz", + "integrity": "sha512-b3ORhIAKgp9ZYY+zBt7b7r0kLU2kjvyGF0+MS2SBym3emsweGPybEqocJcmtMuxyBhkOKHP4CiuEJEDuAlTx6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@tauri-apps/plugin-fs": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-fs/-/plugin-fs-2.5.0.tgz", @@ -940,6 +1181,12 @@ "@tauri-apps/api": "^2.10.1" } }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -947,6 +1194,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.184.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.1.tgz", + "integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==", + "license": "MIT", + "dependencies": { + "@dimforge/rapier3d-compat": "~0.12.0", + "@tweenjs/tween.js": "~23.1.3", + "@types/stats.js": "*", + "@types/webxr": ">=0.5.17", + "fflate": "~0.8.2", + "meshoptimizer": "~1.1.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", @@ -1637,6 +1910,12 @@ "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -1955,6 +2234,12 @@ "node": ">= 8" } }, + "node_modules/meshoptimizer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz", + "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==", + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -2578,6 +2863,12 @@ "node": ">=0.8" } }, + "node_modules/three": { + "version": "0.184.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz", + "integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==", + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", diff --git a/portal/package.json b/portal/package.json index 6c2ee17..ce31ef4 100644 --- a/portal/package.json +++ b/portal/package.json @@ -13,12 +13,15 @@ "@tauri-apps/plugin-fs": "^2.5.0", "@tauri-apps/plugin-http": "^2.5.8", "@tauri-apps/plugin-shell": "^2.3.5", + "@types/three": "^0.184.1", "axios": "^1.6.5", "pinia": "^2.1.7", + "three": "^0.184.0", "vue": "^3.4.0", "vue-router": "^4.2.5" }, "devDependencies": { + "@tauri-apps/cli": "^2.10.1", "@vitejs/plugin-vue": "^5.0.0", "autoprefixer": "^10.4.17", "postcss": "^8.4.33", diff --git a/portal/src/App.vue b/portal/src/App.vue index 177163a..f00bcd1 100644 --- a/portal/src/App.vue +++ b/portal/src/App.vue @@ -9,11 +9,15 @@ 首頁 搜尋 人物管理 - Face Candidates + Face Traces 納管檔案 設定 + Pipeline - + + @@ -24,15 +28,15 @@ - -
+ +
\ No newline at end of file diff --git a/portal/src/views/FilesView.vue b/portal/src/views/FilesView.vue index 68f5088..2eab355 100644 --- a/portal/src/views/FilesView.vue +++ b/portal/src/views/FilesView.vue @@ -42,6 +42,13 @@ 已完成 + + @@ -174,24 +181,36 @@ function setStatusFilter(status: string) { async function fetchFiles() { loading.value = true - error.value = null try { const config = getCurrentConfig() - // Call the scan endpoint to get list of files with status - // Note: /api/v1/files/scan returns unregistered files. - // We might need a combined list or call /api/v1/files for registered ones. - // For now, let's assume /api/v1/files/scan returns all files with is_registered flag. - const response: any = await httpFetch(`${config.api_base_url}/api/v1/files/scan`) - - // Map scan response to our expected format - // Scan returns: { files: [ { file_uuid, file_name, is_registered, status... } ] } - files.value = response.files?.map((f: any) => ({ + const scanResp = await httpFetch(`${config.api_base_url}/api/v1/files/scan`) + const scanFiles: any[] = (scanResp?.files || []).map((f: any) => ({ ...f, - status: f.status || (f.is_registered ? 'pending' : 'unregistered') - })) || [] - - // To get actual processing status, we might need to cross reference with /api/v1/files - // But for Demo, let's rely on the scan endpoint providing status. + status: f.is_registered ? 'registered_scan' : 'unregistered' + })) + + // Get registered files with real processing status + let regFiles: any[] = [] + try { + const regResp = await httpFetch(`${config.api_base_url}/api/v1/files?page=1&page_size=100`) + regFiles = (regResp?.files || regResp?.data || []).map((f: any) => ({ + ...f, + status: f.status || 'pending' + })) + } catch { + // Registered files API may not be available; use scan data only + } + + // Merge: scan results first, then overlay with registered statuses + const merged = new Map() + for (const f of scanFiles) { + merged.set(f.file_path, f) + } + for (const f of regFiles) { + merged.set(f.file_path, f) + } + + files.value = Array.from(merged.values()) } catch (e) { console.error('Failed to fetch files:', e) error.value = String(e) @@ -201,6 +220,7 @@ async function fetchFiles() { } async function registerFile(filePath: string) { + if (!filePath) { alert('無法註冊:缺少檔案路徑'); return } try { await registerVideo(filePath) // Refresh list @@ -212,7 +232,9 @@ async function registerFile(filePath: string) { } async function unregisterFile(fileUuid: string, fileName: string) { - if (!confirm(`確定要取消註冊 "${fileName}" 嗎?這將刪除資料庫中的相關記錄。`)) { + if (!fileUuid) { alert('無法取消註冊:缺少 UUID'); return } + const displayName = fileName || '未知檔案' + if (!confirm(`確定要取消註冊 "${displayName}" 嗎?這將刪除資料庫中的相關記錄。`)) { return } @@ -226,6 +248,7 @@ async function unregisterFile(fileUuid: string, fileName: string) { } async function startProcessing(fileUuid: string) { + if (!fileUuid) { alert('無法處理:缺少 UUID'); return } if (!confirm('確定要開始分析處理此檔案嗎?')) return try { @@ -245,8 +268,8 @@ async function startProcessing(fileUuid: string) { } function enterWorkbench(fileUuid: string) { - // Navigate to the new Face Workbench view - router.push(`/video-detail/${fileUuid}`) + if (!fileUuid) { alert('無法開啟工作台:缺少 UUID'); return } + router.push(`/file/${fileUuid}`) } onMounted(fetchFiles) diff --git a/portal/src/views/HomeView.vue b/portal/src/views/HomeView.vue index 062016f..c76dea5 100644 --- a/portal/src/views/HomeView.vue +++ b/portal/src/views/HomeView.vue @@ -112,6 +112,12 @@

SFTPGo 狀態

+ +
+ {{ statusMsg.text }} + +
@@ -343,11 +349,7 @@ - - diff --git a/portal/src/views/IdentityDetailView.vue b/portal/src/views/IdentityDetailView.vue index 3ea8897..42da4c7 100644 --- a/portal/src/views/IdentityDetailView.vue +++ b/portal/src/views/IdentityDetailView.vue @@ -60,7 +60,6 @@ 影片名稱 - 區域 ID 出現次數 首次出現 @@ -68,7 +67,6 @@ {{ video.file_name }} - {{ video.person_id }} {{ video.appearance_count }} {{ video.first_appearance?.toFixed(2) || '-' }}s @@ -76,12 +74,25 @@
+ + +
+

3D 臉部

+

立體臉部網格(MediaPipe Face Mesh,468 landmarks)

+
+ +
+ {{ faceLoading ? '正在取得臉部資料...' : '尚無臉部資料' }} +
+
+
diff --git a/portal/src/views/LoginView.vue b/portal/src/views/LoginView.vue index d87ef45..efa1d03 100644 --- a/portal/src/views/LoginView.vue +++ b/portal/src/views/LoginView.vue @@ -54,9 +54,9 @@ - -
-

API 範例

+ +
+

API 範例 (dev)

# Login @@ -76,18 +76,26 @@ \ No newline at end of file +onMounted(() => { loadPersons() }) + diff --git a/portal/src/views/SearchView.vue b/portal/src/views/SearchView.vue index 9d05c26..9ee9175 100644 --- a/portal/src/views/SearchView.vue +++ b/portal/src/views/SearchView.vue @@ -20,26 +20,52 @@ {{ loading ? '搜尋中...' : '搜尋' }}
- -
- 搜尋模式: - +
+ +
+ 搜尋類型: + +
+ +
+ 搜尋模式: + +
+ +
+ 搜尋檔案: + +
{{ modeDescription }}
- -
+ +

搜尋結果 ({{ results.length }})

- -
+ +
找不到符合的結果
+ + +
+

Trace 搜尋結果 ({{ traceResults.length }})

+
+
+
+ Trace +
+
+
Trace #{{ trace.trace_id }}
+
{{ trace.face_count }} faces, {{ trace.duration_sec?.toFixed(1) || '?' }}s
+
+
+
+
+ + +
+ 找不到符合的 Trace +
@@ -113,6 +170,7 @@