//! Example: Timeline browsing //! //! This example demonstrates Immich's unique time-based asset organization. //! Immich groups photos by date into "time buckets", allowing efficient //! browsing of large photo collections chronologically. //! //! Environment variables: //! - IMMICH_URL: The Immich server URL (defaults to http://localhost:2283) //! - IMMICH_API_KEY: Your API key (required) use immich_sdk::Client; #[tokio::main] async fn main() -> Result<(), Box> { // Configure the client from environment variables let url = std::env::var("IMMICH_URL").unwrap_or_else(|_| "http://localhost:2283".to_string()); let api_key = std::env::var("IMMICH_API_KEY").expect("IMMICH_API_KEY environment variable not set"); // Create a client let client = Client::from_url(&url)?.with_api_key(api_key); // Get all time buckets // Time buckets are date-based groupings (e.g., "2024-01-15") let buckets = client.timeline().buckets().execute().await?; println!("Found {} time buckets", buckets.len()); if buckets.is_empty() { println!("No time buckets found. Upload some photos first!"); return Ok(()); } // Show first few buckets println!("\n--- First 5 Time Buckets ---"); for (i, bucket) in buckets.iter().take(5).enumerate() { println!( "Bucket {}: {} - {} assets", i + 1, bucket.time_bucket, bucket.count ); } // Get assets from the first bucket if let Some(first_bucket) = buckets.first() { println!( "\n--- Fetching assets from bucket: {} ---", first_bucket.time_bucket ); let assets = client .timeline() .bucket(&first_bucket.time_bucket) .execute() .await?; println!("Found {} assets in this bucket", assets.id.len()); // The TimeBucketAssetResponse contains parallel arrays // This is a memory-efficient design where each field is a column: // - assets.id[i], assets.file_created_at[i], etc. all refer to the same asset // - All arrays have the same length println!("\n--- Sample assets from this bucket (showing up to 5) ---"); for i in 0..assets.id.len().min(5) { println!(" Asset {}:", i + 1); println!(" ID: {}", assets.id[i]); println!(" Created: {}", assets.file_created_at[i]); println!(" Is Image: {}", assets.is_image[i]); println!(" Is Favorite: {}", assets.is_favorite[i]); println!(" Owner ID: {}", assets.owner_id[i]); // Optional fields (may be None) if let Some(ref durations) = assets.duration { if let Some(duration) = &durations[i] { println!(" Duration: {}", duration); } } if let Some(ref cities) = assets.city { if let Some(city) = &cities[i] { println!(" City: {}", city); } } if let Some(ref countries) = assets.country { if let Some(country) = &countries[i] { println!(" Country: {}", country); } } } // Demonstrate iterating through parallel arrays println!("\n--- Understanding the parallel array structure ---"); println!("The response contains these parallel arrays (all same length):"); println!(" - id.len() = {}", assets.id.len()); println!( " - file_created_at.len() = {}", assets.file_created_at.len() ); println!(" - is_image.len() = {}", assets.is_image.len()); println!(" - is_favorite.len() = {}", assets.is_favorite.len()); println!(" - owner_id.len() = {}", assets.owner_id.len()); println!("\nIndex 'i' corresponds to the same asset across all arrays."); println!("This columnar structure is memory-efficient for large collections."); } // Demonstrate filtering with the timeline API println!("\n--- Timeline API supports filtering ---"); println!("You can filter time buckets with:"); println!(" - album_id: Filter by specific album"); println!(" - is_favorite: Show only favorited assets"); println!(" - is_trashed: Include/exclude trashed assets"); println!(" - visibility: Timeline, Archive, Hidden, or Locked"); println!(" - with_partners: Include assets shared by partners"); // Example: Get only favorite buckets let favorite_buckets = client .timeline() .buckets() .is_favorite(true) .execute() .await?; println!( "\nFound {} time buckets with favorite assets", favorite_buckets.len() ); Ok(()) }