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>
2.9 KiB
2.9 KiB
API Contracts: VideoWall
GET /video/preview
Retrieve the preview clip MP4 file for a given video. If the preview is not yet generated, triggers on-demand generation and returns 202.
Authentication: Required (Bearer token)
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| path | string | yes | Relative path of the source video from BASE_PATH |
Responses:
| Status | Content-Type | Body | Description |
|---|---|---|---|
| 200 | video/mp4 | MP4 file stream | Preview clip is ready and served |
| 202 | application/json | {"status": "processing", "path": "<path>"} |
Preview generation has been triggered; client should retry |
| 400 | application/json | {"error": "Invalid path"} |
Path validation failed |
| 404 | application/json | {"error": "Video not found"} |
Source video does not exist |
| 500 | application/json | {"error": "Generation failed: <detail>"} |
Preview generation failed |
Behavior:
- Validate path with
is_valid_full_path() - Check if preview clip exists on disk and status is
complete→ serve MP4 (200) - If status is
pendingor no record exists → trigger generation, return 202 - If status is
processing→ return 202 - If status is
failed→ return 500 with error detail
POST /video/preview/status
Check the preview generation status for a batch of video paths. Used by the mobile app to determine which previews are ready before requesting them.
Authentication: Required (Bearer token)
Request Body (application/json):
{
"paths": [
"2024/vacation/beach.mov",
"2024/vacation/sunset.mp4",
"2024/birthday.avi"
]
}
| Field | Type | Required | Description |
|---|---|---|---|
| paths | string[] | yes | Array of relative video paths from BASE_PATH |
Response (200, application/json):
{
"previews": [
{
"path": "2024/vacation/beach.mov",
"status": "complete",
"preview_url": "/video/preview?path=2024/vacation/beach.mov"
},
{
"path": "2024/vacation/sunset.mp4",
"status": "processing",
"preview_url": null
},
{
"path": "2024/birthday.avi",
"status": "pending",
"preview_url": null
}
]
}
| Field | Type | Description |
|---|---|---|
| previews | object[] | Status for each requested path |
| previews[].path | string | The requested video path |
| previews[].status | string | One of: pending, processing, complete, failed, not_found |
| previews[].preview_url | string? | Relative URL to fetch the preview (only when status is complete) |
Behavior:
- Accept up to 200 paths per request
- Batch query the
video_preview_clipstable for all paths - For paths not in the table, return status
not_found(video may not exist or hasn't been scanned yet) - Return results in the same order as the input paths