use anyhow::{Result, bail};
use crate::cli::{
ArtCommand, FilterCommand, MatteCommand, ModeCommand, OnOff, SlideshowCategoryValue,
SlideshowCommand,
};
use crate::executor::Executor;
use crate::ipc::{Request, Response};
pub async fn run(exec: &Executor, sub: &ArtCommand, json: bool) -> Result<()> {
let request = match sub {
ArtCommand::List => Request::ArtList,
ArtCommand::ShowInfo { content_id } => Request::ArtShowInfo {
content_id: content_id.clone(),
},
ArtCommand::Select { content_id } => Request::ArtSelect {
content_id: content_id.clone(),
},
ArtCommand::Upload {
file,
select,
replace_selected,
canvas,
} => Request::ArtUpload {
file_path: file.clone(),
select: *select,
replace_selected: *replace_selected,
canvas_color: canvas.clone(),
},
ArtCommand::Delete { content_ids } => Request::ArtDelete {
content_ids: content_ids.clone(),
},
ArtCommand::Thumbnail { content_id, o } => Request::ArtThumbnail {
content_id: content_id.clone(),
output_path: o.clone(),
},
ArtCommand::Favorite { content_id, state } => Request::ArtFavorite {
content_id: content_id.clone(),
on: matches!(state, OnOff::On),
},
ArtCommand::Matte { sub } => match sub {
MatteCommand::List => Request::MatteList,
MatteCommand::Set {
content_id,
matte_id,
portrait,
} => Request::MatteSet {
content_id: content_id.clone(),
matte_id: matte_id.clone(),
portrait: portrait.clone(),
},
},
ArtCommand::Filter { sub } => match sub {
FilterCommand::List => Request::FilterList,
FilterCommand::Set {
content_id,
filter_id,
} => Request::FilterSet {
content_id: content_id.clone(),
filter_id: filter_id.clone(),
},
},
ArtCommand::Mode { sub } => match sub {
ModeCommand::On => Request::ModeOn,
ModeCommand::Off => Request::ModeOff,
ModeCommand::Status => Request::ModeStatus,
},
ArtCommand::Slideshow { sub } => match sub {
SlideshowCommand::On {
duration,
shuffle,
category,
} => Request::SlideshowOn {
duration_secs: *duration,
shuffle: *shuffle,
category: category.as_ref().map(|c| match c {
SlideshowCategoryValue::MyPictures => "my-pictures".to_string(),
SlideshowCategoryValue::Favorites => "favorites".to_string(),
}),
},
SlideshowCommand::Off => Request::SlideshowOff,
SlideshowCommand::Status => Request::SlideshowStatus,
SlideshowCommand::Configure {
duration,
shuffle,
category,
} => Request::SlideshowConfigure {
duration_secs: *duration,
shuffle: shuffle.as_ref().map(|s| matches!(s, OnOff::On)),
category: category.as_ref().map(|c| match c {
SlideshowCategoryValue::MyPictures => "my-pictures".to_string(),
SlideshowCategoryValue::Favorites => "favorites".to_string(),
}),
},
},
};
let response = exec.send_request(request).await?;
render(sub, &response, json)
}
fn render(sub: &ArtCommand, response: &Response, json: bool) -> Result<()> {
let data = match response {
Response::Ok { data } => data,
Response::Error { message } => bail!("{message}"),
Response::TvDisconnected { message } => bail!("{message}"),
};
match sub {
ArtCommand::List => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let empty = vec![];
let items = data.as_array().unwrap_or(&empty);
if items.is_empty() {
println!("No uploaded art images.");
} else {
let id_w = items
.iter()
.filter_map(|i| i.get("content_id").and_then(|v| v.as_str()))
.map(|s| s.len())
.max()
.unwrap_or(10)
.max(10);
println!("{:<id_w$} {:>5} x {:<5} MATTE", "CONTENT ID", "W", "H");
for img in items {
let id = img.get("content_id").and_then(|v| v.as_str()).unwrap_or("");
let w = img.get("width").and_then(|v| v.as_u64()).unwrap_or(0);
let h = img.get("height").and_then(|v| v.as_u64()).unwrap_or(0);
let matte = img.get("matte_id").and_then(|v| v.as_str()).unwrap_or("-");
println!("{id:<id_w$} {w:>5} x {h:<5} {matte}");
}
}
}
}
ArtCommand::ShowInfo { .. } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let id = data
.get("content_id")
.and_then(|v| v.as_str())
.unwrap_or("");
let cat = data.get("category").and_then(|v| v.as_str()).unwrap_or("");
let w = data.get("width").and_then(|v| v.as_u64()).unwrap_or(0);
let h = data.get("height").and_then(|v| v.as_u64()).unwrap_or(0);
println!("Content ID: {id}");
println!("Category: {cat}");
println!("Size: {w}x{h}");
if let Some(matte) = data.get("matte_id").and_then(|v| v.as_str()) {
println!("Matte: {matte}");
}
if let Some(portrait) = data.get("portrait_matte_id").and_then(|v| v.as_str()) {
println!("Portrait matte: {portrait}");
}
}
}
ArtCommand::Select { content_id } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Selected {content_id}.");
}
}
ArtCommand::Upload {
select,
replace_selected,
..
} => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let id = data
.get("content_id")
.and_then(|v| v.as_str())
.unwrap_or("");
println!("Uploaded: {id}");
if *select || *replace_selected {
println!("Selected for display.");
}
if data
.get("replaced")
.and_then(|v| v.as_bool())
.unwrap_or(false)
{
println!("Deleted previous selection.");
}
}
}
ArtCommand::Delete { content_ids } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Deleted {} image(s).", content_ids.len());
}
}
ArtCommand::Thumbnail { .. } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let path = data.get("path").and_then(|v| v.as_str()).unwrap_or("");
let size = data.get("size").and_then(|v| v.as_u64()).unwrap_or(0);
println!("Thumbnail saved to {path} ({size} bytes).");
}
}
ArtCommand::Favorite { content_id, state } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let word = match state {
OnOff::On => "set",
OnOff::Off => "removed",
};
println!("Favorite {word} on {content_id}.");
}
}
ArtCommand::Matte { sub } => match sub {
MatteCommand::List => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let empty = vec![];
let items = data.as_array().unwrap_or(&empty);
if items.is_empty() {
println!("No mattes available.");
} else {
let id_w = items
.iter()
.filter_map(|m| m.get("matte_id").and_then(|v| v.as_str()))
.map(|s| s.len())
.max()
.unwrap_or(8)
.max(8);
println!("{:<id_w$} TYPE", "MATTE ID");
for m in items {
let id = m.get("matte_id").and_then(|v| v.as_str()).unwrap_or("");
let mt = m.get("matte_type").and_then(|v| v.as_str()).unwrap_or("");
println!("{id:<id_w$} {mt}");
}
}
}
}
MatteCommand::Set { content_id, .. } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Matte set on {content_id}.");
}
}
},
ArtCommand::Filter { sub } => match sub {
FilterCommand::List => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let empty = vec![];
let items = data.as_array().unwrap_or(&empty);
if items.is_empty() {
println!("No photo filters available.");
} else {
let id_w = items
.iter()
.filter_map(|f| f.get("filter_id").and_then(|v| v.as_str()))
.map(|s| s.len())
.max()
.unwrap_or(9)
.max(9);
println!("{:<id_w$} NAME", "FILTER ID");
for f in items {
let id = f.get("filter_id").and_then(|v| v.as_str()).unwrap_or("");
let name = f.get("filter_name").and_then(|v| v.as_str()).unwrap_or("");
println!("{id:<id_w$} {name}");
}
}
}
}
FilterCommand::Set { content_id, .. } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Filter set on {content_id}.");
}
}
},
ArtCommand::Mode { sub } => match sub {
ModeCommand::On => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Art mode enabled.");
}
}
ModeCommand::Off => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Art mode disabled.");
}
}
ModeCommand::Status => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let enabled = data
.get("art_mode")
.and_then(|v| v.as_bool())
.unwrap_or(false);
println!("Art mode: {}", if enabled { "on" } else { "off" });
}
}
},
ArtCommand::Slideshow { sub } => match sub {
SlideshowCommand::On { .. } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Slideshow enabled.");
}
}
SlideshowCommand::Off => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Slideshow disabled.");
}
}
SlideshowCommand::Status => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
let enabled = data
.get("enabled")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let dur = data
.get("duration_secs")
.and_then(|v| v.as_u64())
.unwrap_or(0);
let shuffle = data
.get("shuffle")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let cat = data
.get("category")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
println!("Slideshow: {}", if enabled { "on" } else { "off" });
println!("Duration: {dur}s");
println!("Shuffle: {}", if shuffle { "on" } else { "off" });
println!("Category: {cat}");
}
}
SlideshowCommand::Configure { .. } => {
if json {
println!("{}", serde_json::to_string_pretty(data)?);
} else {
println!("Slideshow configuration updated.");
}
}
},
}
Ok(())
}