Files
momentry_studio/docs/core-api-fix-video-range.md
Momentry Studio 5951aca086 feat: frame-based positioning and mark system foundation
Core Changes:
- Fix SearchView to use start_frame/end_frame directly (no time*fps conversion)
- Add hard_delete support to delete_trace API
- VideoPlayer: Main timeline + Mark system foundation
- Proxy: Add local routes for auth, media, identity-matches, cluster-results
- Add .gitignore to exclude build artifacts and dependencies

Design Documents:
- Multi-track Mark system design (.opencode/plans/)
- Video editing positioning standards research

Files Modified:
- src/views/SearchView.vue: Frame positioning, ensureMinDuration (240 frames)
- src/views/PeopleView.vue: batchDeleteGroups with hard_delete
- src/api/index.ts: delete_trace with hard_delete body
- src/components/VideoPlayer.vue: Timeline + Mark UI
- src-tauri/src/proxy.rs: New local routes
- AGENTS.md: Update documentation
2026-07-24 20:19:47 +08:00

4.9 KiB

Core API Fix Request: Video Range Requests Support

Issue

Video player in Studio cannot seek (跳轉) because Core API video endpoint does not support HTTP Range requests.

Current Behavior

$ curl -I "http://localhost:3002/api/v1/file/{uuid}/video?start_time=0&end_time=5"
HTTP/1.1 200 OK
content-type: video/mp4
content-length: 1918410
# Missing: Accept-Ranges header
# Missing: 206 Partial Content support

Expected Behavior

$ curl -I "http://localhost:3002/api/v1/file/{uuid}/video" -H "Range: bytes=0-1000"
HTTP/1.1 206 Partial Content
content-type: video/mp4
content-range: bytes 0-1000/1918410
accept-ranges: bytes
content-length: 1001

Why This Matters

  1. Browser seeking requires Range support - HTML5 <video> element uses Range requests to seek to different timestamps
  2. Current workaround is inefficient - Users must download entire video or use start_time param (re-downloads video)
  3. High bitrate videos suffer most - 4K/10Mbps videos cannot be quickly navigated

Implementation Requirements

1. Add Accept-Ranges: bytes header to video endpoint

GET /api/v1/file/{uuid}/video
Response Headers:
  Accept-Ranges: bytes

2. Handle Range requests with 206 Partial Content

// Pseudo-code for Rust implementation
if let Some(range_header) = request.headers().get("Range") {
    let (start, end) = parse_range(range_header)?;
    let file_chunk = read_file_range(file_path, start, end)?;
    
    Response::builder()
        .status(206)
        .header("Content-Range", format!("bytes {}-{}/{}", start, end, file_size))
        .header("Accept-Ranges", "bytes")
        .header("Content-Length", end - start + 1)
        .body(file_chunk)
} else {
    // Full file response
    Response::builder()
        .status(200)
        .header("Accept-Ranges", "bytes")
        .body(full_file)
}

3. Support multiple Range formats

  • Range: bytes=0-999 (first 1000 bytes)
  • Range: bytes=1000- (from byte 1000 to end)
  • Range: bytes=-500 (last 500 bytes)

Affected Files (Core API)

  • src/api/files.rs - Video streaming endpoint
  • Possibly src/api/video.rs if exists

Testing

# Test 1: Initial request should include Accept-Ranges
curl -I "http://localhost:3002/api/v1/file/{uuid}/video"
# Expect: Accept-Ranges: bytes

# Test 2: Range request should return 206
curl -I "http://localhost:3002/api/v1/file/{uuid}/video" -H "Range: bytes=0-1000"
# Expect: HTTP/1.1 206 Partial Content
# Expect: Content-Range: bytes 0-1000/{total}

# Test 3: Browser seeking should work
# Open Studio, play video, click on timeline
# Video should seek without re-downloading

Priority

High - Critical for user experience when navigating long videos

  • src/components/VideoPlayer.vue - Uses <video> element with native controls
  • src-tauri/src/proxy.rs - Already streams video, will pass through Range headers

Notes

  • Studio proxy already supports streaming (lines 325-367 in proxy.rs)
  • Just need Core API to handle Range requests properly
  • This is standard HTTP behavior for video streaming

Additional Request: Adaptive Bitrate / Resolution Selection

Problem

High bitrate videos (4K, 10+ Mbps) cause playback lag in browser due to software decoding limitations.

Solution Options

Option A: Resolution Parameter

GET /api/v1/file/{uuid}/video?start_time=0&end_time=60&resolution=720p
GET /api/v1/file/{uuid}/video?start_time=0&end_time=60&resolution=480p

Implementation:

  • Use FFmpeg to transcode on-the-fly or pre-generate proxy versions
  • Store proxy videos at lower bitrates (720p @ 2Mbps, 480p @ 1Mbps)

Option B: Quality Parameter (1-100)

GET /api/v1/file/{uuid}//video?start_time=0&end_time=60&quality=50

Option C: Max Bitrate Parameter

GET /api/v1/file/{uuid}/video?start_time=0&end_time=60&max_bitrate=2000

Pre-generate proxy videos during processing:

// During file processing, generate multiple versions:
// - Original: {uuid}_original.mp4 (preserve original quality)
// - 720p: {uuid}_720p.mp4 (2 Mbps)
// - 480p: {uuid}_480p.mp4 (1 Mbps)

Storage Impact:

  • Original 100MB video → ~105MB total (5% overhead for proxies)

API Changes:

pub fn get_video_stream(
    file_uuid: String,
    start_time: f64,
    end_time: f64,
    resolution: Option<String>,  // "original" | "720p" | "480p"
) -> Result<VideoStream, Error>

Frontend Integration

Studio VideoPlayer can add quality selector:

<select v-model="selectedResolution">
  <option value="original">Original (1080p)</option>
  <option value="720p">HD (720p)</option>
  <option value="480p">SD (480p)</option>
</select>

Benefits

  1. Eliminates lag on high bitrate videos
  2. Faster loading with smaller file sizes
  3. Bandwidth savings for remote access
  4. Better UX - smooth playback on all devices