Update AGENTS.md with complete documentation
This commit is contained in:
378
AGENTS.md
378
AGENTS.md
@@ -4,10 +4,12 @@
|
|||||||
|
|
||||||
MoMentry Playground 是一個整合式桌面應用程式,提供:
|
MoMentry Playground 是一個整合式桌面應用程式,提供:
|
||||||
- 多格式媒體播放(視頻、音頻、圖片)
|
- 多格式媒體播放(視頻、音頻、圖片)
|
||||||
- Markdown/PDF/HTML 檢視
|
|
||||||
- Frame-精確的視頻控制
|
- Frame-精確的視頻控制
|
||||||
|
- ASR 字幕顯示(可開關)
|
||||||
|
- YOLO 檢測框疊加(可開關)
|
||||||
|
- Chunk 導航標記(可開關)
|
||||||
- 與 momentry_core 處理模組的進度監控
|
- 與 momentry_core 處理模組的進度監控
|
||||||
- Database 查詢功能
|
- 自然語言搜尋
|
||||||
|
|
||||||
## 技術棧
|
## 技術棧
|
||||||
|
|
||||||
@@ -15,10 +17,11 @@ MoMentry Playground 是一個整合式桌面應用程式,提供:
|
|||||||
|------|------|
|
|------|------|
|
||||||
| 語言 | Rust 2021 |
|
| 語言 | Rust 2021 |
|
||||||
| 桌面框架 | tao + wry |
|
| 桌面框架 | tao + wry |
|
||||||
| 視頻播放 | SDL2 + FFmpeg-sidecar |
|
| 視頻播放 | SDL2 + FFmpeg |
|
||||||
| 前端 | HTML/CSS/JS (WebView) |
|
| 前端 | HTML/CSS/JS (WebView) |
|
||||||
| 後端接口 | HTTP API (momentry_core port 3002) |
|
| 後端接口 | HTTP API (momentry_core port 3002) |
|
||||||
| 數據庫 | PostgreSQL, MongoDB, Qdrant (via API) |
|
| 數據庫 | PostgreSQL, MongoDB, Qdrant (via API) |
|
||||||
|
| 緩存 | LRU (YOLO frame cache) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -33,10 +36,13 @@ cargo build
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
|
|
||||||
# 運行
|
# 運行
|
||||||
cargo run
|
cargo run -- --video "/path/to/video.mov"
|
||||||
|
|
||||||
# 指定功能運行
|
# 指定參數
|
||||||
cargo run --features "youtube,cloud"
|
cargo run --release -- \
|
||||||
|
--video "/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov" \
|
||||||
|
--asr "/Users/accusys/momentry_core_0.1/output/39567a0eb16f39fd.asr.json" \
|
||||||
|
--yolo "/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.yolo.json"
|
||||||
```
|
```
|
||||||
|
|
||||||
### 測試
|
### 測試
|
||||||
@@ -71,22 +77,10 @@ cargo clippy
|
|||||||
# 類型檢查
|
# 類型檢查
|
||||||
cargo check
|
cargo check
|
||||||
|
|
||||||
# 完整檢查(相當於所有以上)
|
# 完整檢查
|
||||||
cargo build --all-targets
|
cargo build --all-targets
|
||||||
```
|
```
|
||||||
|
|
||||||
### WASM 目標(如使用 Yew)
|
|
||||||
```bash
|
|
||||||
# 添加 WASM 目標
|
|
||||||
rustup target add wasm32-unknown-unknown
|
|
||||||
|
|
||||||
# 使用 Trunk 開發
|
|
||||||
trunk serve
|
|
||||||
|
|
||||||
# Production 構建
|
|
||||||
trunk build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 代碼風格指南
|
## 代碼風格指南
|
||||||
@@ -94,7 +88,6 @@ trunk build --release
|
|||||||
### 格式規範
|
### 格式規範
|
||||||
- **最大行長**: 100 字符
|
- **最大行長**: 100 字符
|
||||||
- **縮進**: 4 空格(使用 `rustfmt` 自動處理)
|
- **縮進**: 4 空格(使用 `rustfmt` 自動處理)
|
||||||
- **換行**: Unix 風格 (`\n`)
|
|
||||||
|
|
||||||
### 命名慣例
|
### 命名慣例
|
||||||
```rust
|
```rust
|
||||||
@@ -106,33 +99,21 @@ trait MediaViewer;
|
|||||||
// 函數/方法/變量: snake_case
|
// 函數/方法/變量: snake_case
|
||||||
fn play_video();
|
fn play_video();
|
||||||
let current_frame = 0;
|
let current_frame = 0;
|
||||||
let is_playing = true;
|
|
||||||
|
|
||||||
// 常量: SCREAMING_SNAKE_CASE
|
// 常量: SCREAMING_SNAKE_CASE
|
||||||
const MAX_FRAME_BUFFER: usize = 10;
|
const MAX_FRAME_BUFFER: usize = 10;
|
||||||
const SEEK_DELAY_MS: u64 = 500;
|
|
||||||
|
|
||||||
// 模塊: snake_case
|
|
||||||
mod video_player;
|
|
||||||
pub mod ffmpeg;
|
|
||||||
|
|
||||||
// 公開 API: 完整單詞
|
|
||||||
fn get_playback_position() -> Duration;
|
|
||||||
fn set_volume() -> Result<(), PlayerError>;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 錯誤處理
|
### 錯誤處理
|
||||||
```rust
|
```rust
|
||||||
// 使用 anyhow 進行應用級錯誤處理
|
|
||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{Context, Result, anyhow};
|
||||||
|
|
||||||
fn load_video(path: &str) -> Result<VideoFile> {
|
fn load_video(path: &str) -> Result<VideoFile> {
|
||||||
let file = File::open(path)
|
let file = File::open(path)
|
||||||
.with_context(|| format!("Failed to open video: {}", path))?;
|
.with_context(|| format!("Failed to open video: {}", path))?;
|
||||||
// ...
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定義專用錯誤枚舉
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum PlayerError {
|
pub enum PlayerError {
|
||||||
#[error("Codec not supported: {0}")]
|
#[error("Codec not supported: {0}")]
|
||||||
@@ -140,143 +121,175 @@ pub enum PlayerError {
|
|||||||
|
|
||||||
#[error("Seek failed: {0}")]
|
#[error("Seek failed: {0}")]
|
||||||
SeekFailed(String),
|
SeekFailed(String),
|
||||||
|
|
||||||
#[error("FFmpeg error: {stderr}")]
|
|
||||||
FFmpegError { stderr: String },
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 模塊結構
|
### 模塊結構
|
||||||
```rust
|
```rust
|
||||||
// src/lib.rs - 庫入口
|
// src/lib.rs
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod ui;
|
pub mod overlay;
|
||||||
pub mod viewer;
|
pub mod web;
|
||||||
pub mod api;
|
pub mod config;
|
||||||
|
|
||||||
// 每個模塊的組織
|
// src/player/mod.rs
|
||||||
mod player {
|
pub mod video;
|
||||||
pub mod video; // 公開子模塊
|
pub mod ffmpeg;
|
||||||
mod controls; // 私有實現
|
pub mod renderer;
|
||||||
|
pub mod state;
|
||||||
pub use video::{VideoPlayer, VideoEvent};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 並發安全
|
pub use video::Video;
|
||||||
```rust
|
pub use state::{PlayerState, PlaybackState};
|
||||||
// 使用 Arc<Mutex<T>> 共享狀態
|
|
||||||
let player = Arc::new(Mutex::new(VideoPlayer::new()));
|
|
||||||
|
|
||||||
// 在多線程間共享 Frame buffer
|
|
||||||
type FrameBuffer = Arc<Mutex<VecDeque<Frame>>>;
|
|
||||||
|
|
||||||
// Spawn 線程時克隆 Arc
|
|
||||||
let player_clone = Arc::clone(&player);
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### 文檔注釋
|
|
||||||
```rust
|
|
||||||
/// 視頻播放器核心結構
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```
|
|
||||||
/// let player = VideoPlayer::new();
|
|
||||||
/// player.open("video.mp4")?;
|
|
||||||
/// player.play()?;
|
|
||||||
/// ```
|
|
||||||
pub struct VideoPlayer {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 播放事件
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum PlayEvent {
|
|
||||||
/// 播放開始
|
|
||||||
Play,
|
|
||||||
/// 播放暫停
|
|
||||||
Pause,
|
|
||||||
/// 進度更新 (當前幀, 總幀數)
|
|
||||||
Progress(u64, u64),
|
|
||||||
/// 播放結束
|
|
||||||
End,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 依賴管理
|
|
||||||
```toml
|
|
||||||
# 明確版本範圍
|
|
||||||
[dependencies]
|
|
||||||
anyhow = "1.0"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
|
||||||
|
|
||||||
# 避免使用 * 導入
|
|
||||||
# 錯誤: use std::collections::*;
|
|
||||||
# 正確: use std::collections::{HashMap, HashSet};
|
|
||||||
```
|
|
||||||
|
|
||||||
### API 設計
|
|
||||||
```rust
|
|
||||||
// 使用 Result 明確錯誤
|
|
||||||
fn command(&self, cmd: &str) -> Result<String>;
|
|
||||||
|
|
||||||
// 公開方法要有文檔
|
|
||||||
/// 發送播放命令
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * `cmd` - 命令字串 ("play", "pause", "seek <time>")
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// 返回無效命令或播放錯誤
|
|
||||||
pub fn command(&self, cmd: &str) -> Result<String> {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## WebView 前端規範
|
## 快捷鍵定義
|
||||||
|
|
||||||
### HTML 結構
|
| 快捷鍵 | 功能 | Toggle |
|
||||||
```html
|
|--------|------|--------|
|
||||||
<div id="app">
|
| `Space` | 播放/暫停 | - |
|
||||||
<div id="toolbar"><!-- 控制工具欄 --></div>
|
| `←` | 上一幀 | - |
|
||||||
<div id="input-area"><!-- 命令輸入 --></div>
|
| `→` | 下一幀 | - |
|
||||||
<div id="media-viewport"><!-- 媒體顯示 --></div>
|
| `Shift+←` | 後退 1 秒 | - |
|
||||||
<div id="controls"><!-- 播放控制 --></div>
|
| `Shift+→` | 前進 1 秒 | - |
|
||||||
<div id="status-bar"><!-- 狀態信息 --></div>
|
| `S` | 字幕顯示 | ✅ |
|
||||||
<div id="output-log"><!-- 日誌輸出 --></div>
|
| `Y` | YOLO 疊加 | ✅ |
|
||||||
</div>
|
| `C` | Chunk 標記 | ✅ |
|
||||||
|
| `M` | 靜音 | ✅ |
|
||||||
|
| `+` / `=` | 放大 | - |
|
||||||
|
| `-` | 縮小 | - |
|
||||||
|
| `0` | 重置縮放 | - |
|
||||||
|
| `R` | 重置視圖 | - |
|
||||||
|
| `F` | 全屏 | - |
|
||||||
|
| `[` | 上一 Chunk | - |
|
||||||
|
| `]` | 下一 Chunk | - |
|
||||||
|
| `/` | 開啟搜尋 | - |
|
||||||
|
| `Esc` | 關閉面板 | - |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 命令列參數
|
||||||
|
|
||||||
|
```bash
|
||||||
|
momentry [OPTIONS]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-v, --video <PATH> Video file path
|
||||||
|
-a, --asr <PATH> ASR JSON file path
|
||||||
|
-y, --yolo <PATH> YOLO JSON file path
|
||||||
|
-w, --width <N> Window width (default: 1280)
|
||||||
|
-h, --height <N> Window height (default: 720)
|
||||||
|
--fullscreen Start in fullscreen mode
|
||||||
|
--locale <LANG> UI language (en, zh-TW)
|
||||||
|
--help Show help
|
||||||
```
|
```
|
||||||
|
|
||||||
### JS 命令接口
|
---
|
||||||
```javascript
|
|
||||||
// 發送命令到 Rust 後端
|
|
||||||
function sendCommand(cmd) {
|
|
||||||
window.momentry_command(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 接收後端事件
|
## 數據格式規範
|
||||||
window.momentry_on_event = function(event) {
|
|
||||||
console.log('Event:', event);
|
### ASR JSON
|
||||||
};
|
```json
|
||||||
|
{
|
||||||
|
"language": "en",
|
||||||
|
"language_probability": 0.99,
|
||||||
|
"segments": [
|
||||||
|
{ "start": 0.0, "end": 19.04, "text": "Hello and welcome..." }
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSS 樣式
|
### YOLO JSON
|
||||||
```css
|
```json
|
||||||
:root {
|
{
|
||||||
--bg-primary: #1e1e1e;
|
"metadata": { "fps": 59.94, "total_frames": 412343, "width": 1920, "height": 1080 },
|
||||||
--bg-secondary: #2d2d2d;
|
"frames": {
|
||||||
--text-primary: #ffffff;
|
"100": {
|
||||||
--accent: #007acc;
|
"frame_number": 100,
|
||||||
--error: #f44747;
|
"time_seconds": 1.668,
|
||||||
|
"detections": [
|
||||||
|
{ "class_name": "person", "confidence": 0.85, "x1": 150, "y1": 200, "x2": 400, "y2": 800 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
**座標系**: 絕對像素座標 (0 ~ width/height)
|
||||||
body { font-family: system-ui, sans-serif; }
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 服務連接配置
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
PostgreSQL:
|
||||||
|
host: localhost
|
||||||
|
port: 5432
|
||||||
|
database: momentry
|
||||||
|
user: accusys
|
||||||
|
password: Test3200
|
||||||
|
|
||||||
|
Redis:
|
||||||
|
host: localhost
|
||||||
|
port: 6379
|
||||||
|
password: accusys
|
||||||
|
|
||||||
|
Qdrant:
|
||||||
|
host: localhost
|
||||||
|
port: 6333
|
||||||
|
collection: AccusysDB
|
||||||
|
api_key: Test3200Test3200Test3200
|
||||||
|
|
||||||
|
API Server:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 3002
|
||||||
|
|
||||||
|
Ollama:
|
||||||
|
host: localhost
|
||||||
|
port: 11434
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 測試數據
|
||||||
|
|
||||||
|
```bash
|
||||||
|
VIDEO="/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.mov"
|
||||||
|
ASR="/Users/accusys/momentry_core_0.1/output/39567a0eb16f39fd.asr.json"
|
||||||
|
YOLO="/Users/accusys/test_video/Old_Time_Movie_Show_-_Charade_1963.HD.yolo.json"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 項目結構
|
||||||
|
|
||||||
|
```
|
||||||
|
momentry_playground/
|
||||||
|
├── Cargo.toml
|
||||||
|
├── src/
|
||||||
|
│ ├── main.rs # 入口
|
||||||
|
│ ├── lib.rs # 模組導出
|
||||||
|
│ ├── config.rs # 命令列解析
|
||||||
|
│ ├── player/ # 視頻播放
|
||||||
|
│ │ ├── mod.rs
|
||||||
|
│ │ ├── video.rs # 播放控制
|
||||||
|
│ │ ├── ffmpeg.rs # FFmpeg 解碼
|
||||||
|
│ │ ├── renderer.rs # SDL2 渲染
|
||||||
|
│ │ └── state.rs # 播放狀態
|
||||||
|
│ ├── overlay/ # 疊加層
|
||||||
|
│ │ ├── mod.rs
|
||||||
|
│ │ ├── asr.rs # ASR 載入
|
||||||
|
│ │ └── yolo.rs # YOLO 載入 (LRU cache)
|
||||||
|
│ └── web/ # WebView
|
||||||
|
│ ├── mod.rs
|
||||||
|
│ └── bridge.rs # JS <-> Rust IPC
|
||||||
|
├── web/
|
||||||
|
│ ├── index.html # 主 UI
|
||||||
|
│ ├── styles.css # 樣式
|
||||||
|
│ └── app.js # 前端邏輯
|
||||||
|
├── AGENTS.md
|
||||||
|
└── README.md
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -298,27 +311,8 @@ ffprobe -version
|
|||||||
|
|
||||||
### yt-dlp (YouTube 支援)
|
### yt-dlp (YouTube 支援)
|
||||||
```bash
|
```bash
|
||||||
brew install yt-dlp # macOS
|
brew install yt-dlp
|
||||||
pip install yt-dlp # pip
|
pip install yt-dlp
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 環境變量
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# API 服務地址
|
|
||||||
export MOMENTRY_API_URL=http://localhost:3002
|
|
||||||
|
|
||||||
# 數據庫連接
|
|
||||||
export DATABASE_URL=postgresql://user:pass@localhost:5432/momentry
|
|
||||||
export QDRANT_URL=http://localhost:6333
|
|
||||||
|
|
||||||
# 輸出目錄
|
|
||||||
export MOMENTRY_OUTPUT_DIR=./output
|
|
||||||
|
|
||||||
# 日誌級別
|
|
||||||
export RUST_LOG=debug
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -338,11 +332,10 @@ cargo fmt
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. 提交(使用Conventional Commits)
|
### 3. 提交
|
||||||
```bash
|
```bash
|
||||||
git commit -m "feat(player): add frame-precise seeking"
|
git commit -m "feat(player): add frame-precise seeking"
|
||||||
git commit -m "fix(controls): correct volume sync"
|
git commit -m "fix(controls): correct volume sync"
|
||||||
git commit -m "docs: update AGENTS.md"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 推送與 PR
|
### 4. 推送與 PR
|
||||||
@@ -356,18 +349,45 @@ git push -u origin feature/video-controls
|
|||||||
## 常見問題
|
## 常見問題
|
||||||
|
|
||||||
### Q: FFmpeg 路徑找不到
|
### Q: FFmpeg 路徑找不到
|
||||||
|
在 `src/player/ffmpeg.rs` 中指定路徑:
|
||||||
```rust
|
```rust
|
||||||
// 在代碼中指定路徑
|
Command::new("/opt/homebrew/bin/ffmpeg")
|
||||||
let ffmpeg_path = std::path::Path::new("/opt/homebrew/bin/ffmpeg");
|
|
||||||
VideoDecoder::new(ffmpeg_path)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Q: WebView 不顯示
|
### Q: YOLO 檔案太大 (483MB)
|
||||||
確保 `index.html` 在正確的資源路徑,或使用 `include_bytes!` 嵌入。
|
使用 LRU cache 緩存幀:
|
||||||
|
```rust
|
||||||
|
const CACHE_SIZE: usize = 60; // 只緩存前後 30 幀
|
||||||
|
cache: LruCache<u64, Vec<Detection>>
|
||||||
|
```
|
||||||
|
|
||||||
### Q: 編譯緩慢
|
### Q: 編譯緩慢
|
||||||
```bash
|
```bash
|
||||||
# 使用 sccache
|
|
||||||
cargo install sccache
|
cargo install sccache
|
||||||
export RUSTC_WRAPPER=sccache
|
export RUSTC_WRAPPER=sccache
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 多語言支援 (i18n)
|
||||||
|
|
||||||
|
目前界面語言為 English。擴展時:
|
||||||
|
1. 在 `web/` 添加 `locales/` 目錄
|
||||||
|
2. 建立 `en.json`, `zh-TW.json` 等檔案
|
||||||
|
3. 在 `app.js` 載入對應語言檔案
|
||||||
|
4. 使用 `data-i18n` 屬性標記翻譯元素
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 實施優先順序
|
||||||
|
|
||||||
|
| Phase | 功能 | 優先級 |
|
||||||
|
|-------|------|--------|
|
||||||
|
| 1 | 基礎播放器 | P0 |
|
||||||
|
| 2 | ASR 字幕 | P1 |
|
||||||
|
| 3 | YOLO 疊加 | P1 |
|
||||||
|
| 4 | Chunk 標記 | P2 |
|
||||||
|
| 5 | Zoom/Pan | P1 |
|
||||||
|
| 6 | Vector 搜尋 | P2 |
|
||||||
|
| 7 | 進度監控 | P3 |
|
||||||
|
| 8 | 雲端/YouTube | P3 |
|
||||||
|
|||||||
Reference in New Issue
Block a user