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>
This commit is contained in:
79
specs/001-video-wall/plan.md
Normal file
79
specs/001-video-wall/plan.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Implementation Plan: VideoWall
|
||||
|
||||
**Branch**: `001-video-wall` | **Date**: 2026-02-25 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/001-video-wall/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Add a VideoWall feature spanning the Rust API backend and React Native mobile app. The backend generates 480p MP4 preview clips (up to 10 seconds, composed of 10 equally spaced 1-second segments) using ffmpeg, extending the existing `OverviewVideo` pattern in `src/video/ffmpeg.rs`. The mobile app adds a VideoWall view using `expo-video` and FlatList to display a responsive 2-3 column grid of simultaneously looping, muted preview clips with audio-on-long-press. Preview clips are cached on disk, served via new API endpoints, and generated proactively by the file watcher.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: Rust (stable, Cargo) for backend API; TypeScript / React Native (Expo SDK 52) for mobile app
|
||||
**Primary Dependencies**: actix-web 4, Diesel 2.2 (SQLite), ffmpeg/ffprobe (CLI), expo-video 3.0, expo-router 6.0, react-native-reanimated 4.1
|
||||
**Storage**: SQLite (preview clip status tracking), filesystem (MP4 preview clips in `PREVIEW_CLIPS_DIRECTORY`)
|
||||
**Testing**: `cargo test` for backend; manual testing for mobile app
|
||||
**Target Platform**: Linux server (API), iOS/Android (mobile app via Expo)
|
||||
**Project Type**: Mobile app + REST API (two separate repositories)
|
||||
**Performance Goals**: <3s VideoWall load for 50 pre-generated previews; <30s per clip generation; <5MB per clip; smooth simultaneous playback of 6-12 clips
|
||||
**Constraints**: Semaphore-limited concurrent ffmpeg processes (existing pattern); 480p resolution to keep bandwidth/CPU manageable; audio track preserved but muted by default
|
||||
**Scale/Scope**: Hundreds to low thousands of videos per library; single user at a time
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
Constitution is an unfilled template — no project-specific gates defined. **PASS** (no violations possible).
|
||||
|
||||
Post-Phase 1 re-check: Still PASS — no gates to evaluate.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/001-video-wall/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0 output
|
||||
├── data-model.md # Phase 1 output
|
||||
├── quickstart.md # Phase 1 output
|
||||
├── contracts/ # Phase 1 output
|
||||
│ └── api-endpoints.md
|
||||
└── tasks.md # Phase 2 output (/speckit.tasks command)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
# Backend (ImageApi - Rust)
|
||||
src/
|
||||
├── video/
|
||||
│ ├── ffmpeg.rs # Add generate_preview_clip() using existing pattern
|
||||
│ ├── actors.rs # Add PreviewClipGenerator actor (semaphore-limited)
|
||||
│ └── mod.rs # Add generate_preview_clips() batch function
|
||||
├── main.rs # Add GET /video/preview, POST /video/preview/status endpoints
|
||||
│ # Extend file watcher to trigger preview generation
|
||||
├── database/
|
||||
│ ├── schema.rs # Add video_preview_clips table
|
||||
│ └── models.rs # Add VideoPreviewClip model
|
||||
│ └── preview_dao.rs # New DAO for preview clip status tracking
|
||||
└── data/
|
||||
└── mod.rs # Add PreviewClipRequest, PreviewStatusRequest types
|
||||
|
||||
# Frontend (SynologyFileViewer - React Native)
|
||||
app/(app)/grid/
|
||||
├── video-wall.tsx # New VideoWall view (FlatList grid)
|
||||
└── _layout.tsx # Add video-wall route to stack
|
||||
|
||||
components/
|
||||
└── VideoWallItem.tsx # Single preview clip cell (expo-video player)
|
||||
|
||||
hooks/
|
||||
└── useVideoWall.ts # Preview clip fetching, status polling, audio state
|
||||
```
|
||||
|
||||
**Structure Decision**: Mobile + API pattern. Backend changes extend existing `src/video/` module and `src/main.rs` handlers following established conventions. Frontend adds a new route under the existing grid stack navigator with a dedicated component and hook.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
No constitution violations to justify.
|
||||
Reference in New Issue
Block a user