---
document_type: "general_doc"
service: "MOMENTRY_CORE"
title: "時間格式統一計畫：秒.幀表示法"
date: "2026-03-26"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:

ai_query_hints:
  - "查詢 時間格式統一計畫：秒.幀表示法 的內容"
  - "時間格式統一計畫：秒.幀表示法 的主要目的是什麼？"
  - "如何操作或實施 時間格式統一計畫：秒.幀表示法？"
---

# 時間格式統一計畫：秒.幀表示法

| 項目 | 內容 |
|------|------|
| 建立者 | OpenCode |
| 建立時間 | 2026-03-26 |
| 文件版本 | V1.0 |

---

## 版本歷史

| 版本 | 日期 | 目的 | 操作人 | 工具/模型 |
|------|------|------|--------|-----------|
| V1.0 | 2026-03-26 | 創建時間格式統一計畫 | OpenCode | OpenCode / deepseek-reasoner |

---

## 概述

本文檔定義 Momentry Core v1.0 的時間表示法統一方案。目標是將現有的浮點秒數表示法，統一改為「秒.幀」格式（如 `123.60` 表示 123秒第60幀），以解決時間精度與顯示一致性的問題。

### 核心原則
1. **只存儲整數幀數** + **FPS**，不存浮點秒數
2. **計算時才轉換**：秒數 = 幀數 ÷ FPS
3. **顯示格式**：`秒.幀`（如 `123.60` = 123秒第60幀）

---

## 當前狀態分析

### 現有時間表示問題
| 問題點 | 當前實現 | 問題 |
|--------|----------|------|
| 存儲方式 | `start_time`/`end_time` (f64秒) + `start_frame`/`end_frame` (i64幀) | 數據冗餘，可能不一致 |
| 計算邏輯 | `start_frame = (start_time * fps) as i64` | 浮點轉整數可能丟失精度 |
| 顯示格式 | `format_time()`: `HH:MM:SS.mm`<br>`format_time_range()`: `MM:SS - MM:SS` | 格式不統一，未顯示幀數 |
| API 響應 | 返回浮點秒數 (`start_time: f64`) | 客戶端需自行轉換，易出錯 |

### 影響範圍
| 模組 | 受影響文件 | 修改內容 |
|------|------------|----------|
| 核心數據結構 | `src/core/chunk/types.rs` | 移除 `start_time`/`end_time` 字段 |
| 數據庫 | `src/core/db/postgres_db.rs` | 更新 schema 和查詢 |
| API 層 | `src/api/server.rs` | 更新所有響應結構 |
| 處理管道 | `src/core/chunk/splitter.rs`<br>`src/core/processor/asr.rs` | ASR → Chunk 轉換邏輯 |
| 顯示層 | `src/player/main.rs`<br>`src/player/chunk_selector.rs` | 時間格式化與顯示 |
| 向量存儲 | `src/core/db/qdrant_db.rs` | 更新 payload 結構 |

---

## 決策記錄

### 已確認決策
| 決策項目 | 決策內容 | 理由 |
|----------|----------|------|
| **FPS 精度** | 支持分數 FPS（如 29.97, 23.976） | 真實視頻常見分數幀率 |
| **顯示格式** | `秒.幀`（如 `123.60` = 123秒第60幀） | 直觀反映時間與幀數關係 |
| **遷移策略** | 重置數據庫（因只有兩個測試 video） | 簡單快速，無歷史數據包袱 |
| **向後兼容** | 立即移除舊字段，不保留兼容層 | POC 階段可接受破壞性變更 |
| **幀數位數** | 固定兩位顯示（不足補零） | 視覺一致性，如 `123.04` |

### 待決策事項
| 事項 | 選項 | 建議 |
|------|------|------|
| FPS 存儲類型 | `f64`（當前）vs `Rational`（分子/分母） | 保持 `f64`，簡單實用 |
| 時間格式化工具 | 新增 `src/core/time.rs` 模組 | 集中時間計算與格式化邏輯 |
| 數據驗證 | 添加 `assert_eq!(start_frame, (start_time * fps).round() as i64)` | 確保數據一致性 |

---

## 實施計畫（5階段）

### 階段 1：核心數據結構重構 (2天)
| 任務 | 描述 | 文件 | 輸出 |
|------|------|------|------|
| 1.1 | 修改 `Chunk` 結構，移除時間字段 | `src/core/chunk/types.rs` | 只存 `start_frame`/`end_frame` + `fps` |
| 1.2 | 新增時間計算與格式化方法 | `src/core/chunk/types.rs` | `.start_sec_frame()`, `.end_sec_frame()` |
| 1.3 | 更新數據庫 schema | `src/core/db/postgres_db.rs` | 移除 `start_time`/`end_time` 列 |
| 1.4 | 創建時間工具模組 | `src/core/time.rs`（新） | `format_sec_frame()`, `frame_to_seconds()` |

### 階段 2：API 層重構 (2天)
| 任務 | 描述 | 文件 | 輸出 |
|------|------|------|------|
| 2.1 | 更新 `SearchResult` 等響應結構 | `src/api/server.rs` | 新增 `start_sec_frame`/`end_sec_frame` 字符串字段 |
| 2.2 | 更新所有搜索端點 | `src/api/server.rs` | `/api/v1/search`, `/api/v1/n8n/search`, `/api/v1/search/hybrid` |
| 2.3 | 更新其他 API 端點 | `src/api/server.rs` | 所有使用時間字段的端點 |
| 2.4 | 更新 API 文檔 | `docs_v1.0/REFERENCE/API_REFERENCE.md` 等 | 反映新格式 |

### 階段 3：處理管道重構 (1天)
| 任務 | 描述 | 文件 | 輸出 |
|------|------|------|------|
| 3.1 | 修改 ASR → Chunk 轉換邏輯 | `src/core/chunk/splitter.rs` | 直接使用幀數計算 |
| 3.2 | 確保 FPS 傳遞 | `src/api/server.rs` (註冊) | Video 註冊時存儲 FPS 到 `videos` 表 |
| 3.3 | 更新其他處理器 | `src/core/processor/*.rs` | 所有使用時間的處理器 |

### 階段 4：顯示層更新 (1天)
| 任務 | 描述 | 文件 | 輸出 |
|------|------|------|------|
| 4.1 | 更新 TUI 時間顯示 | `src/player/chunk_selector.rs` | `format_time_range()` 顯示 `秒.幀` |
| 4.2 | 更新播放跳轉邏輯 | `src/player/main.rs` | 使用幀數計算 seek 位置 |
| 4.3 | 更新 ASR 疊加層 | `src/player/asr_overlay.rs` | 時間顯示格式 |

### 階段 5：數據遷移與測試 (0.5天)
| 任務 | 描述 | 命令 | 驗證 |
|------|------|------|------|
| 5.1 | 重置數據庫 | `psql -U accusys -h localhost -c "DROP DATABASE momentry; CREATE DATABASE momentry;"` | 乾淨的數據庫 |
| 5.2 | 重新註冊測試 video | `cargo run -- register ./demo/video1.mp4`<br>`cargo run -- register ./demo/video2.mp4` | 數據正確存儲 |
| 5.3 | 運行完整測試 | `cargo test --lib`<br>`cargo clippy --lib` | 確保所有功能正常 |

---

## 技術詳細設計

### 1. 核心數據結構變更
```rust
// 修改前 (src/core/chunk/types.rs)
pub struct Chunk {
    pub start_time: f64,      // ← 移除
    pub end_time: f64,        // ← 移除
    pub start_frame: i64,
    pub end_frame: i64,
    pub fps: f64,
}

// 修改後
pub struct Chunk {
    pub start_frame: i64,     // ← 主要存儲
    pub end_frame: i64,       // ← 主要存儲
    pub fps: f64,
}

impl Chunk {
    // 新增計算方法
    pub fn start_seconds(&self) -> f64 {
        self.start_frame as f64 / self.fps
    }
    
    pub fn start_sec_frame(&self) -> String {
        let seconds = self.start_seconds();
        let sec_int = seconds.floor() as i64;
        let frame_num = self.start_frame % self.fps as i64;
        format!("{}.{:02}", sec_int, frame_num)
    }
}
```

### 2. 數據庫 Schema 變更
```sql
-- 修改前
CREATE TABLE chunks (
    start_time DOUBLE PRECISION NOT NULL,
    end_time DOUBLE PRECISION NOT NULL,
    start_frame BIGINT DEFAULT 0,
    end_frame BIGINT DEFAULT 0,
    fps DOUBLE PRECISION DEFAULT 24.0,
);

-- 修改後
CREATE TABLE chunks (
    start_frame BIGINT NOT NULL,      -- ← 改為 NOT NULL
    end_frame BIGINT NOT NULL,        -- ← 改為 NOT NULL
    fps DOUBLE PRECISION DEFAULT 24.0,
    -- 移除 start_time, end_time
);
```

### 3. API 響應格式
```json
// 修改前
{
  "uuid": "a1b10138a6bbb0cd",
  "chunk_id": "sentence_0001",
  "start_time": 12.345,      // ← 浮點秒數
  "end_time": 15.678,
  "text": "Hello world"
}

// 修改後
{
  "uuid": "a1b10138a6bbb0cd",
  "chunk_id": "sentence_0001",
  "start_sec_frame": "12.10",  // ← 字符串：秒.幀
  "end_sec_frame": "15.20",
  "start_frame": 363,          // ← 原始幀數（供計算用）
  "end_frame": 456,
  "fps": 30.0,
  "text": "Hello world"
}
```

### 4. 時間格式化工具
```rust
// src/core/time.rs (新文件)
pub struct FrameTime {
    frame: i64,
    fps: f64,
}

impl FrameTime {
    pub fn new(frame: i64, fps: f64) -> Self {
        Self { frame, fps }
    }
    
    pub fn seconds(&self) -> f64 {
        self.frame as f64 / self.fps
    }
    
    pub fn format_sec_frame(&self) -> String {
        let sec_int = (self.frame as f64 / self.fps).floor() as i64;
        let frame_num = self.frame % self.fps as i64;
        format!("{}.{:02}", sec_int, frame_num)
    }
    
    pub fn from_seconds(seconds: f64, fps: f64) -> Self {
        let frame = (seconds * fps).round() as i64;
        Self::new(frame, fps)
    }
}
```

---

## 風險與緩解措施

| 風險 | 影響 | 緩解措施 |
|------|------|----------|
| 浮點精度損失 | 幀數計算不準確 | 使用 `round()` 而非 `as i64` 直接轉換 |
| FPS 不一致 | 不同視頻 FPS 不同 | Video 註冊時準確解析並存儲 FPS |
| 數據遷移失敗 | 現有數據丟失 | 方案 A：重置數據庫（推薦，因只有測試數據）<br>方案 B：先備份再遷移 |
| 客戶端兼容性 | 現有客戶端無法解析新格式 | POC 階段可接受，告知客戶端更新 |
| 性能影響 | 頻繁計算秒數可能影響性能 | 緩存計算結果，或提供可選的秒數字段 |

---

## 測試計畫

### 單元測試
```rust
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_frame_time_conversion() {
        let fps = 30.0;
        let frame = 1234;
        let ft = FrameTime::new(frame, fps);
        
        // 測試秒數計算
        assert!((ft.seconds() - 41.13333).abs() < 0.0001);
        
        // 測試格式化
        assert_eq!(ft.format_sec_frame(), "41.04");  // 1234 % 30 = 4
        
        // 測試可逆性
        let ft2 = FrameTime::from_seconds(ft.seconds(), fps);
        assert_eq!(ft.frame, ft2.frame);
    }
}
```

### 集成測試
1. **視頻註冊測試**：註冊視頻，驗證 FPS 正確解析並存儲
2. **ASR 處理測試**：運行 ASR，驗證 Chunk 正確創建
3. **搜索測試**：執行搜索，驗證返回格式正確
4. **播放測試**：使用 TUI player，驗證時間顯示與跳轉正常

---

## 附錄：相關文件

### 待修改文件清單
```
src/core/chunk/types.rs          # Chunk 結構定義
src/core/chunk/splitter.rs       # ASR 轉 Chunk 邏輯
src/core/db/postgres_db.rs       # 數據庫 schema 和查詢
src/api/server.rs               # API 響應結構
src/player/main.rs              # TUI 顯示邏輯
src/player/chunk_selector.rs    # 時間格式化
src/core/db/qdrant_db.rs        # 向量搜索 payload
src/core/db/mongodb_db.rs       # MongoDB 模型
src/core/processor/asr.rs       # ASR 處理器
src/core/db/sync_db.rs          # 數據同步
```

### 新創建文件
```
src/core/time.rs                # 時間格式化工具
docs_v1.0/ARCHITECTURE/TIME_FORMAT_ARCHITECTURE.md
docs_v1.0/IMPLEMENTATION/PHASE_1_CORE_STRUCTURES.md
```

---

## 時間軸與里程碑

| 里程碑 | 日期 | 完成標準 |
|--------|------|----------|
| 計畫確認 | 2026-03-26 | 本文檔審閱通過 |
| 階段 1 完成 | 2026-03-28 | Chunk 結構修改完成，通過單元測試 |
| 階段 2 完成 | 2026-03-30 | API 響應格式更新完成 |
| 階段 3 完成 | 2026-03-31 | 處理管道更新完成 |
| 階段 4 完成 | 2026-04-01 | 顯示層更新完成 |
| 階段 5 完成 | 2026-04-01 | 數據遷移完成，全系統測試通過 |

---

## 聯絡與支援

| 角色 | 聯絡人 | 職責 |
|------|--------|------|
| 計畫負責人 | OpenCode | 整體實施與協調 |
| 技術審查 | 團隊成員 | 代碼審查與測試 |
| 問題回報 | GitHub Issues | https://github.com/anomalyco/momentry/issues |

---

## 版本資訊

- **版本**: V1.0
- **建立日期**: 2026-03-26  
- **最後更新**: 2026-03-26
- **狀態**: 🔄 計畫階段

---

> **注意**: 本計畫為 POC (Proof of Concept) 階段實施方案，專注於解決時間格式統一的核心問題。其他功能（如用戶感知搜索、多模塊處理）將記錄於待辦事項，待本計畫完成後執行。
