Files
ImageApi/specs/001-video-wall/data-model.md
Cameron 19c099360e Add VideoWall feature: server-side preview clip generation and mobile grid view
Backend (Rust/Actix-web):
- Add video_preview_clips table and PreviewDao for tracking preview generation
- Add ffmpeg preview clip generator: 10 equally-spaced 1s segments at 480p with CUDA NVENC auto-detection
- Add PreviewClipGenerator actor with semaphore-limited concurrent processing
- Add GET /video/preview and POST /video/preview/status endpoints
- Extend file watcher to detect and queue previews for new videos
- Use relative paths consistently for DB storage (matching EXIF convention)

Frontend (React Native/Expo):
- Add VideoWall grid view with 2-3 column layout of looping preview clips
- Add VideoWallItem component with ActiveVideoPlayer sub-component for lifecycle management
- Add useVideoWall hook for batch status polling with 5s refresh
- Add navigation button in grid header (visible when videos exist)
- Use TextureView surface type to fix Android z-ordering issues
- Optimize memory: players only mount while visible via FlatList windowSize
- Configure ExoPlayer buffer options and caching for short clips
- Tap to toggle audio focus, long press to open in full viewer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 19:40:17 -05:00

63 lines
2.6 KiB
Markdown

# Data Model: VideoWall
## Entities
### VideoPreviewClip
Tracks the generation status and metadata of preview clips derived from source videos.
**Table**: `video_preview_clips`
| Field | Type | Constraints | Description |
|-------|------|-------------|-------------|
| id | INTEGER | PRIMARY KEY, AUTOINCREMENT | Unique identifier |
| file_path | TEXT | NOT NULL, UNIQUE | Relative path of the source video from BASE_PATH |
| status | TEXT | NOT NULL, DEFAULT 'pending' | Generation status: `pending`, `processing`, `complete`, `failed` |
| duration_seconds | REAL | NULLABLE | Duration of the generated preview clip (≤10s) |
| file_size_bytes | INTEGER | NULLABLE | Size of the generated MP4 file |
| error_message | TEXT | NULLABLE | Error details if status is `failed` |
| created_at | TEXT | NOT NULL | ISO 8601 timestamp when record was created |
| updated_at | TEXT | NOT NULL | ISO 8601 timestamp when record was last updated |
**Indexes**:
- `idx_preview_clips_file_path` on `file_path` (unique, used for lookups and batch queries)
- `idx_preview_clips_status` on `status` (used by file watcher to find pending/failed clips)
### Relationships
- **VideoPreviewClip → Source Video**: One-to-one via `file_path`. The preview clip file on disk is located at `{PREVIEW_CLIPS_DIRECTORY}/{file_path}.mp4`.
- **VideoPreviewClip → image_exif**: Implicit relationship via shared `file_path`. No foreign key needed — the EXIF table may not have an entry for every video.
## State Transitions
```
[new video detected] → pending
pending → processing (when generation starts)
processing → complete (when ffmpeg succeeds)
processing → failed (when ffmpeg fails or times out)
failed → pending (on retry / re-scan)
```
## Validation Rules
- `file_path` must be a valid relative path within BASE_PATH
- `status` must be one of: `pending`, `processing`, `complete`, `failed`
- `duration_seconds` must be > 0 and ≤ 10.0 when status is `complete`
- `file_size_bytes` must be > 0 when status is `complete`
- `error_message` should only be non-null when status is `failed`
## Storage Layout (Filesystem)
```
{PREVIEW_CLIPS_DIRECTORY}/
├── 2024/
│ ├── vacation/
│ │ ├── beach.mp4 # Preview for BASE_PATH/2024/vacation/beach.mov
│ │ └── sunset.mp4 # Preview for BASE_PATH/2024/vacation/sunset.mp4
│ └── birthday.mp4 # Preview for BASE_PATH/2024/birthday.avi
└── 2025/
└── trip.mp4 # Preview for BASE_PATH/2025/trip.mkv
```
All preview clips use `.mp4` extension regardless of source format.