| Crates.io | waterui-media |
| lib.rs | waterui-media |
| version | 0.2.1 |
| created_at | 2025-12-14 08:05:21.901619+00 |
| updated_at | 2025-12-14 11:30:37.501254+00 |
| description | Media components for WaterUI (images, videos, audio) |
| homepage | |
| repository | https://github.com/water-rs/waterui |
| max_upload_size | |
| id | 1983996 |
| size | 79,609 |
Media components for WaterUI providing reactive photo, video, and Live Photo display with native platform rendering.
waterui-media delivers a comprehensive media handling system for the WaterUI framework. It bridges Rust's type safety with platform-native media rendering (AVFoundation on Apple platforms, ExoPlayer on Android) while maintaining WaterUI's reactive programming model. The crate supports static images, video playback with controls, Apple Live Photos, and platform-native media picking.
Key features include reactive volume control with mute state preservation, configurable aspect ratios, event-driven loading states, and seamless integration with WaterUI's environment system. Media components automatically render to native widgets: AVPlayerViewController on iOS, AVPlayerLayer for raw video views, and ExoPlayer with PlayerView on Android.
Add to your Cargo.toml:
[dependencies]
waterui-media = "0.1.0"
use waterui_media::{Photo, VideoPlayer, Url};
use waterui_core::prelude::*;
fn main() -> impl View {
VStack::new((
// Display a remote photo
Photo::new(Url::new("https://example.com/image.jpg")),
// Video player with native controls
VideoPlayer::new(Url::new("https://example.com/video.mp4"))
.show_controls(true),
))
}
The crate uses waterui_url::Url for type-safe resource addressing. URLs support web resources, local file paths, data URLs, and blob URLs:
use waterui_media::Url;
// Compile-time web URLs
const REMOTE: Url = Url::new("https://cdn.example.com/video.mp4");
// Runtime parsing
let local: Url = "/path/to/video.mp4".parse().unwrap();
let data_url = Url::from_data("image/png", image_bytes);
waterui-media provides two distinct video components:
Video: Raw video view using AVPlayerLayer/SurfaceView without controls - ideal for custom UIsVideoPlayer: Full-featured player with native platform controls (play/pause, seek, fullscreen)Video components use a special volume encoding that preserves the original level when muting:
> 0): Audible volume level (0.0-1.0)< 0): Muted state storing the original volume as the absolute valueThis approach eliminates the need for separate mute flags while maintaining volume memory.
All media components integrate with WaterUI's reactive system via Binding and Computed signals. Changes to bindings automatically propagate to native platform components through the FFI layer.
use waterui_media::{Photo, photo::Event, Url};
let photo = Photo::new(Url::new("https://example.com/large-image.jpg"))
.on_event(|event| {
match event {
Event::Loaded => tracing::debug!("Image loaded successfully"),
Event::Error(msg) => tracing::debug!("Failed to load: {}", msg),
}
});
use waterui_core::{binding, prelude::*};
use waterui_media::{VideoPlayer, Url};
use waterui_controls::Toggle;
fn volume_demo() -> impl View {
let muted = binding(false);
VStack::new((
VideoPlayer::new(Url::new("https://example.com/video.mp4"))
.muted(&muted),
HStack::new((
Text::new("Mute"),
Toggle::new(&muted),
)),
))
}
use waterui_media::{Video, AspectRatio, Url};
use waterui_core::binding;
let video = Video::new(Url::new("https://example.com/loop.mp4"))
.aspect_ratio(AspectRatio::Fill)
.loops(true)
.on_event(|event| {
// Handle buffering, playback errors, etc.
});
use waterui_media::{LivePhoto, live::LivePhotoSource, Url};
let source = LivePhotoSource::new(
Url::new("https://example.com/live-photo.jpg"),
Url::new("https://example.com/live-photo.mov"),
);
let live_photo = LivePhoto::new(source);
use waterui_core::{binding, prelude::*};
use waterui_media::{MediaPicker, media_picker::{Selected, MediaFilter}, Media};
fn picker_demo() -> impl View {
let selection = binding::<Option<Selected>>(None);
VStack::new((
MediaPicker::new(&selection)
.filter(MediaFilter::Video)
.label(Text::new("Choose Video")),
// Display selected media
selection.get().map(|sel| {
// Load media asynchronously and display
VStack::new(Text::new("Media selected"))
}),
))
}
use waterui_media::{Media, Url};
use waterui_core::prelude::*;
fn display_media(media: Media) -> impl View {
// Media enum automatically chooses the right component
match media {
Media::Image(_) => AnyView::new(Text::new("Displaying Photo")),
Media::Video(_) => AnyView::new(Text::new("Displaying VideoPlayer")),
Media::LivePhoto(_) => AnyView::new(Text::new("Displaying LivePhoto")),
}
}
let image = Media::Image(Url::new("https://example.com/photo.jpg"));
let video = Media::Video(Url::new("https://example.com/clip.mp4"));
Photo - Display static images with event callbacks for load/error statesVideo - Raw video view without controls (AVPlayerLayer/SurfaceView)VideoPlayer - Full-featured video player with native controlsLivePhoto - Apple Live Photo display combining image and videoMediaPicker - Platform-native media selection UIUrl - Type-safe URL representation (web, local, data, blob)Media - Unified enum for Image, Video, or LivePhotoLivePhotoSource - Pairing of image and video URLs for Live PhotosAspectRatio - Video scaling modes: Fit, Fill, StretchVolume - f32 type with special encoding for mute stateEvent - Photo and video event types (loaded, error, buffering, etc.)MediaFilter - Filters for media picker (Image, Video, LivePhoto, combinators)Selected - Selected media item with async loading capabilityThe Image type provides async image manipulation:
Image::new(mime, data) - Decode image from raw data on background threadresize(), resize_to_fit(), resize_to_fill(), resize_exact() - Resize operationsrotate(), rotate_90(), rotate_180(), rotate_270() - Rotationflip_horizontal(), flip_vertical() - Flippingcrop(), blur(), brighten(), adjust_contrast() - Filtersgrayscale(), invert(), huerotate() - Color adjustmentsencode_png(), encode_jpeg() - Export to bytesurl() - Generate base64 data URLAll processing operations run on background threads via blocking::unblock to prevent UI blocking.
default - Enables std featurestd - Standard library support (enables file path handling)Photo: Uses AsyncImage with URLSession for loadingVideo: Uses AVPlayerLayer directly for custom UIsVideoPlayer: Uses AVPlayerViewController (iOS/tvOS) or AVPlayerView (macOS)LivePhoto: Uses PHLivePhotoViewMediaPicker: Uses PHPickerViewController with PHImageManager for loadingPhoto: Uses Coil image loading libraryVideo: Uses ExoPlayer with SurfaceViewVideoPlayer: Uses ExoPlayer with PlayerView (full controls)LivePhoto: Maps to Motion Photos via MediaStoreMediaPicker: Uses ActivityResultContracts.PickVisualMedia with ContentResolver