framesmith 0.1.0

A Rust library for controlling Samsung Frame TVs over the local network
Documentation
//! Upload an image to a Samsung Frame TV and display it.
//!
//! Usage:
//!   cargo run --example upload_art -- <TV_IP> <IMAGE_PATH>
//!   cargo run --example upload_art -- <TV_IP> <IMAGE_PATH> --center
//!
//! Examples:
//!   cargo run --example upload_art -- 192.168.1.100 photo.jpg
//!   cargo run --example upload_art -- 192.168.1.100 painting.png --center

use std::net::IpAddr;
use std::path::Path;

use framesmith::{ArtImage, Canvas, FileAuthTokenStore, FrameTv};

#[tokio::main]
async fn main() -> Result<(), framesmith::Error> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .with_target(false)
        .init();
    let args: Vec<String> = std::env::args().collect();
    if args.len() < 3 {
        eprintln!("Usage: {} <TV_IP> <IMAGE_PATH> [--center]", args[0]);
        std::process::exit(1);
    }

    let ip: IpAddr = args[1].parse().expect("invalid IP address");
    let image_path = Path::new(&args[2]);
    let center = args.iter().any(|a| a == "--center");

    if !image_path.exists() {
        eprintln!("File not found: {}", image_path.display());
        std::process::exit(1);
    }

    // Connect to the TV
    let token_path = std::env::temp_dir().join("framesmith_token.txt");
    let tv = FrameTv::connection_builder(ip)
        .auth_token_store(FileAuthTokenStore::new(&token_path))
        .connect()
        .await?;
    println!("Connected to {ip}");

    // Load the image (detect format from extension)
    let ext = image_path
        .extension()
        .and_then(|e| e.to_str())
        .map(|e| e.to_lowercase())
        .unwrap_or_default();

    let mut builder = ArtImage::builder();
    builder = match ext.as_str() {
        "jpg" | "jpeg" => builder.load_jpeg(image_path)?,
        "png" => builder.load_png(image_path)?,
        _ => {
            eprintln!("Unsupported format '{ext}'. Use JPEG or PNG.");
            std::process::exit(1);
        }
    };

    // Optionally center the image on a black canvas sized to the TV's display
    if center {
        let rotation = tv.get_display_rotation().await?;
        let (tv_w, tv_h) = match rotation {
            framesmith::DisplayRotation::Portrait => (2160, 3840),
            _ => (3840, 2160),
        };
        println!("Centering image on {tv_w}x{tv_h} black canvas");
        builder = builder.center_image(tv_w, tv_h, Canvas::black());
    }

    let image = builder.build()?;
    println!(
        "Uploading {} ({}x{})...",
        image_path.display(),
        image.width(),
        image.height()
    );

    let uploaded = tv.upload_and_select_art_image(image).await?;
    println!("Done! Image ID: {}", uploaded.id());

    // Enable art mode so the image is visible
    if !tv.is_artmode_enabled().await? {
        tv.enable_artmode().await?;
        println!("Art Mode enabled");
    }

    Ok(())
}