Files
Oxicloud/src/infrastructure/services/mod.rs
T
DioCrafts 5722481c4a feat(thumbnails): server-side video thumbnails via ffmpeg
Videos now get a thumbnail generated eagerly server-side on upload, through
the same WebP/blob-hash pipeline as photos — instead of the old browser path
that only ran when the Photos grid first rendered a video tile, re-downloaded
the whole video to seek a frame, and PUT 3 JPEGs back (and produced nothing at
all for HEVC/.mov, which a browser <video> cannot decode).

- New VideoFramePort (application) + FfmpegVideoFrameService / NoopVideoFrameService
  (infrastructure): shell out to the system ffmpeg (no compile-time libav dep),
  extract one representative frame as PNG, bounded by its own semaphore + a
  per-process timeout + kill_on_drop. Noop when ffmpeg is absent/disabled, so
  videos degrade gracefully to no thumbnail.
- ThumbnailRefreshHook.on_file_created routes video/* to
  generate_video_thumbnails_background: stream the (decrypted, reassembled) blob
  to a size- and time-bounded temp file on the data volume, extract a frame, and
  reuse the shared render_and_persist_all_webp helper — so video thumbnails are
  WebP, blob-hash keyed (dedup'd) and content-negotiated, exactly like photos.
- GET thumbnail serves the video's WebP to every client (byte-sniffed
  Content-Type); a genuine miss returns 204.
- Config: OXICLOUD_ENABLE_VIDEO_THUMBNAILS (default true, needs ffmpeg detected
  at startup) + OXICLOUD_FFMPEG_PATH / _CONCURRENCY / _TIMEOUT_SECS / _MAX_MB.
- Dockerfile installs ffmpeg in the runtime image.
- Frontend: drop the client-side generateVideoThumb/frameFromVideo re-download
  path; the server is now the source of truth.

Benchmark (examples/bench_video_thumbnails.rs, needs ffmpeg): 4/4 codecs incl.
HEVC/.mov produce a thumbnail server-side (was 0% for HEVC); ~50-70 ms/frame in
the background; ~3.9 KB preview WebP; up to ~23x less per-first-view transfer on
the test corpus (far more on real multi-MB clips). Methodology in
benches/VIDEO-THUMB.md.

Hardening from an adversarial review: video render holds the decode_semaphore
like the image path; the ffmpeg scale filter bounds both dimensions; the blob
stream has a timeout; the temp file lives on the data volume; the size cap uses
saturating_mul.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:23:04 +02:00

47 lines
1.2 KiB
Rust

pub mod audio_metadata_service;
pub mod azure_blob_backend;
pub mod cached_blob_backend;
pub mod chunked_upload_service;
pub mod compression_service;
pub mod db_pool_monitor;
pub mod dedup_service;
pub mod encrypted_blob_backend;
pub mod exif_service;
pub mod face_geometry;
pub mod face_indexing_service;
pub mod ffmpeg_video_frame_service;
pub mod file_content_cache;
pub mod file_system_i18n_service;
pub mod image_transcode_service;
pub mod jwt_service;
pub mod local_blob_backend;
pub mod login_lockout_service;
pub mod media_metadata_service;
pub mod migration_blob_backend;
pub mod migration_job;
pub mod mock_email_sender;
pub mod nextcloud_chunked_upload_service;
pub mod noop_face_analyzer;
pub mod oidc_service;
#[cfg(feature = "faces-onnx")]
pub mod onnx_face_analyzer;
pub mod password_hasher;
pub mod path_resolver_service;
pub mod path_service;
pub mod pg_acl_engine;
#[cfg(feature = "plugins")]
pub mod plugins;
pub mod retry_blob_backend;
pub mod s3_blob_backend;
pub mod search_index;
pub mod share_unlock_cookie;
pub mod smtp_email_sender;
pub mod thumbnail_service;
#[cfg(test)]
mod thumbnail_service_test;
pub mod trash_cleanup_service;
pub mod tree_etag_flush_service;
pub mod webdav_lock_service;
pub mod wopi_discovery_service;
pub mod zip_service;