use super::Model;
#[cfg(feature = "cover")]
use log::error;
use std::io::Write;
#[cfg(feature = "cover")]
use std::path::Path;
#[cfg(feature = "cover")]
use std::process::Stdio;
use anyhow::{anyhow, bail, Result};
#[allow(dead_code)]
pub struct Xywh {
x: u16,
y: u16,
width: u32,
height: u32,
}
impl Model {
#[cfg(feature = "cover")]
fn draw_cover_ueberzug(&self, url: &str, draw_xywh: &Xywh) {
if draw_xywh.width <= 1 || draw_xywh.height <= 1 {
return;
}
let cmd = format!("{{\"action\":\"add\",\"scaler\":\"fit_contain\",\"identifier\":\"cover\",\"x\":{},\"y\":{},\"width\":{},\"height\":{},\"path\":\"{}\"}}\n",
draw_xywh.x, draw_xywh.y-1,
draw_xywh.width, draw_xywh.height,
url,
);
if let Err(e) = self.run_ueberzug_cmd(&cmd) {
error!("Failed to run Ueberzug: {}", e);
}
}
#[cfg(feature = "cover")]
fn clear_cover_ueberzug(&self) {
let cmd = "{\"action\": \"remove\", \"identifier\": \"cover\"}\n";
if let Err(e) = self.run_ueberzug_cmd(cmd) {
error!("Failed to run Ueberzug: {}", e);
}
}
#[cfg(feature = "cover")]
fn run_ueberzug_cmd(&self, cmd: &str) -> Result<(), std::io::Error> {
let mut ueberzug = self.ueberzug.write().unwrap();
if ueberzug.is_none() {
*ueberzug = Some(
std::process::Command::new("ueberzug")
.args(&["layer", "--silent"])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?,
);
}
let stdin = (*ueberzug).as_mut().unwrap().stdin.as_mut().unwrap();
stdin.write_all(cmd.as_bytes())?;
Ok(())
}
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub fn update_photo(&mut self) -> Result<()> {
self.clear_photo()?;
let song = match &self.current_song {
Some(song) => song,
None => bail!("no current song"),
};
if let Some(picture) = song.picture() {
if let Ok(image) = image::load_from_memory(&picture.data) {
let (term_width, term_height) = viuer::terminal_size();
let (orig_width, orig_height) = image::GenericImageView::dimensions(&image);
let width = 20_u16;
let height = (width * orig_height as u16).checked_div(orig_width as u16);
if let Some(height) = height {
let xywh = Xywh {
x: term_width - width - 1,
y: (term_height - height / 2 - 8) - 1,
width: u32::from(width),
height: u32::from(height),
};
if self.viuer_supported {
let config = viuer::Config {
transparent: true,
absolute_offset: true,
x: xywh.x,
y: xywh.y as i16,
width: Some(xywh.width),
height: None,
..viuer::Config::default()
};
viuer::print(&image, &config)
.map_err(|e| anyhow!("viuer print error: {}", e))?;
return Ok(());
};
#[cfg(feature = "cover")]
image.save(Path::new("/tmp/termusic_cover.jpg"))?;
#[cfg(feature = "cover")]
self.draw_cover_ueberzug("/tmp/termusic_cover.jpg", &xywh);
}
}
}
Ok(())
}
pub fn clear_photo(&mut self) -> Result<()> {
if self.viuer_supported {
self.clear_image_viuer()
.map_err(|e| anyhow!("Clear album photo error: {}", e))?;
return Ok(());
}
#[cfg(feature = "cover")]
self.clear_cover_ueberzug();
Ok(())
}
fn clear_image_viuer(&mut self) -> Result<()> {
write!(self.terminal.raw_mut().backend_mut(), "\x1b_Ga=d\x1b\\")?;
self.terminal.raw_mut().backend_mut().flush()?;
Ok(())
}
}