- Add docker/podman-compose.yml for Immich server (no ML) - Add test-data/sample-photos/ with 3 public domain images - Add scripts/start-immich.sh for container startup + admin/API key creation - Add scripts/seed-data.sh for uploading test photos and creating albums - Add scripts/stop-immich.sh for cleanup with volume destruction - Add scripts/run-example.sh wrapper (fixed env var export) - Update examples to use IMMICH_URL and IMMICH_API_KEY env vars - Add .github/workflows/integration-test.yml for CI - Update assets.list() to use /search/metadata endpoint - Update AGENTS.md with example running instructions - Add 5 new examples: search_metadata, album_management, timeline_browsing, delete_assets, server_info
128 lines
4.8 KiB
Rust
128 lines
4.8 KiB
Rust
//! 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<dyn std::error::Error>> {
|
|
// 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(())
|
|
}
|