use std::{ ffi::c_void, fs::File, net::TcpStream, path::PathBuf, sync::mpsc::channel, }; use dirs::home_dir; use gdk_pixbuf::{ ffi::GdkPixbuf, glib::translate::IntoGlibPtr, traits::PixbufLoaderExt, Pixbuf, PixbufLoader, }; use mlua::{ prelude::*, LuaSerdeExt, }; use mpd::Client; use once_cell::sync::Lazy; use symphonia::{ core::{ formats::FormatOptions, io::{ MediaSourceStream, MediaSourceStreamOptions, }, meta::MetadataOptions, probe::Hint, }, default::get_probe, }; static MPD_MUSIC_PATH: Lazy = Lazy::new(|| { [ home_dir().expect("home directory should be set"), "Music".into(), ] .iter() .collect() }); static COVER_FORMATS: [&str; 2] = ["png", "jpg"]; pub struct Connection(Client); impl LuaUserData for Connection { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method_mut("status", |lua, this: &mut Connection, (): ()| { lua.to_value(&this.0.status().map_err(LuaError::external)?) }); methods.add_method_mut("song", |lua, this: &mut Connection, (): ()| { Ok( if let Some(song) = this.0.currentsong().map_err(LuaError::external)? { lua.to_value(&song)? } else { LuaNil }, ) }); methods.add_method( "get_cover_pixbuf", |_, _: &Connection, file: String| { let song_path = MPD_MUSIC_PATH.join(file); let mut has_external_cover = false; let mut cover_path = PathBuf::new(); for format in COVER_FORMATS { let cover = song_path .parent() .unwrap() .to_owned() .join(format!("cover.{}", format)); if cover.exists() { has_external_cover = cover.exists(); cover_path = cover; break; } } let mss = MediaSourceStream::new( Box::new(File::open(song_path)?), MediaSourceStreamOptions::default(), ); let mut probed = get_probe() .format( &Hint::default(), mss, &FormatOptions::default(), &MetadataOptions::default(), ) .map_err(LuaError::external)?; let visuals; if let Some(metadata) = probed.format.metadata().skip_to_latest() { visuals = metadata.visuals(); if visuals.is_empty() && has_external_cover { let pixbuf = Pixbuf::from_file(cover_path).map_err(LuaError::external)?; return Ok(( Some(LuaLightUserData(unsafe { >::into_glib_ptr(pixbuf) .cast::() })), Some(true), )); } let loader = PixbufLoader::new(); loader .write(visuals.first().unwrap().data.as_ref()) .map_err(LuaError::external)?; loader.close().map_err(LuaError::external)?; return Ok(( Some(LuaLightUserData(unsafe { >::into_glib_ptr( loader.pixbuf().expect("Pixbuf should be initialized"), ) .cast::() })), Some(false), )); } Ok((None, None)) }, ); } } pub fn init(_: &Lua, _: ()) -> LuaResult { Ok(Connection( Client::connect("localhost:6600").map_err(LuaError::external)?, )) }