extern crate serde; extern crate serde_json; pub mod ipc; use std::collections::HashMap; use ipc::*; pub type Socket = String; pub enum NumberChangeOptions { Absolute, Increase, Decrease, } pub enum SeekOptions { Relative, Absolute, RelativePercent, AbsolutePercent, } pub enum PlaylistAddOptions { Replace, Append, AppendPlay, } pub enum Switch { On, Off, Toggle, } pub struct Playlist { pub socket: Socket, pub entries: Vec, } pub trait GetPropertyTypeHandler: Sized { fn get_property_generic(socket: &str, property: &str) -> Result; } impl GetPropertyTypeHandler for bool { fn get_property_generic(socket: &str, property: &str) -> Result { get_mpv_property::(socket, property) } } impl GetPropertyTypeHandler for String { fn get_property_generic(socket: &str, property: &str) -> Result { get_mpv_property::(socket, property) } } impl GetPropertyTypeHandler for f64 { fn get_property_generic(socket: &str, property: &str) -> Result { get_mpv_property::(socket, property) } } impl GetPropertyTypeHandler for usize { fn get_property_generic(socket: &str, property: &str) -> Result { get_mpv_property::(socket, property) } } impl GetPropertyTypeHandler for Vec { fn get_property_generic(socket: &str, property: &str) -> Result, String> { get_mpv_property::>(socket, property) } } impl GetPropertyTypeHandler for HashMap { fn get_property_generic(socket: &str, property: &str) -> Result, String> { get_mpv_property::>(socket, property) } } pub trait SetPropertyTypeHandler { fn set_property_generic(socket: &str, property: &str, value: T) -> Result<(), String>; } impl SetPropertyTypeHandler for bool { fn set_property_generic(socket: &str, property: &str, value: bool) -> Result<(), String> { set_mpv_property::(socket, property, value) } } impl SetPropertyTypeHandler for String { fn set_property_generic(socket: &str, property: &str, value: String) -> Result<(), String> { set_mpv_property::(socket, property, value) } } impl SetPropertyTypeHandler for f64 { fn set_property_generic(socket: &str, property: &str, value: f64) -> Result<(), String> { set_mpv_property::(socket, property, value) } } impl SetPropertyTypeHandler for usize { fn set_property_generic(socket: &str, property: &str, value: usize) -> Result<(), String> { set_mpv_property::(socket, property, value) } } pub trait PlaylistHandler { fn get_from(socket: Socket) -> Result; fn shuffle(&mut self) -> &mut Playlist; fn remove_id(&mut self, id: usize) -> &mut Playlist; fn move_entry(&mut self, from: usize, to: usize) -> &mut Playlist; fn current_id(&self) -> Option; } impl PlaylistHandler for Playlist { fn get_from(socket: Socket) -> Result { match get_mpv_property(&socket, "playlist") { Ok(playlist) => { Ok(Playlist { socket: socket, entries: playlist, }) } Err(why) => Err(why), } } fn shuffle(&mut self) -> &mut Playlist { if let Err(error_msg) = run_mpv_command(&self.socket, "playlist-shuffle", &vec![]) { panic!("Error: {}", error_msg); } if let Ok(mut playlist_entries) = get_mpv_property::>(&self.socket, "playlist") { if self.entries.len() == playlist_entries.len() { for (i, entry) in playlist_entries.drain(0..).enumerate() { self.entries[i] = entry; } } } self } fn remove_id(&mut self, id: usize) -> &mut Playlist { self.entries.remove(id); if let Err(error_msg) = run_mpv_command(&self.socket, "playlist-remove", &vec![&id.to_string()]) { panic!("Error: {}", error_msg); } self } fn move_entry(&mut self, from: usize, to: usize) -> &mut Playlist { if from != to { if let Err(error_msg) = run_mpv_command(&self.socket, "playlist-move", &vec![&from.to_string(), &to.to_string()]) { panic!("Error: {}", error_msg); } if from < to { self.entries[from].id = to - 1; self.entries[to].id = to - 2; for i in from..to - 2 { self.entries[i + 1].id = i; } self.entries.sort_by_key(|entry| entry.id); } else if from > to { self.entries[from].id = to; for i in to..from - 1 { self.entries[i].id = i + 1; } self.entries.sort_by_key(|entry| entry.id); } } self } fn current_id(&self) -> Option { for entry in self.entries.iter() { if entry.current { return Some(entry.id); } } None } } pub trait Commands { fn get_metadata(&self) -> Result, String>; fn get_playlist(&self) -> Result; fn get_property(&self, property: &str) -> Result; fn get_property_string(&self, property: &str) -> Result; fn kill(&self) -> Result<(), String>; fn next(&self) -> Result<(), String>; fn pause(&self) -> Result<(), String>; fn playlist_add(&self, file: &str, option: PlaylistAddOptions) -> Result<(), String>; fn playlist_clear(&self) -> Result<(), String>; fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), String>; fn playlist_play_id(&self, id: usize) -> Result<(), String>; fn playlist_play_next(&self, id: usize) -> Result<(), String>; fn playlist_shuffle(&self) -> Result<(), String>; fn playlist_remove_id(&self, id: usize) -> Result<(), String>; fn prev(&self) -> Result<(), String>; fn restart(&self) -> Result<(), String>; fn run_command(&self, command: &str, args: &Vec<&str>) -> Result<(), String>; fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), String>; fn set_loop_file(&self, option: Switch) -> Result<(), String>; fn set_loop_playlist(&self, option: Switch) -> Result<(), String>; fn set_mute(&self, option: Switch) -> Result<(), String>; fn set_property>(&self, property: &str, value: T) -> Result<(), String>; fn set_speed(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), String>; fn set_volume(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), String>; fn stop(&self) -> Result<(), String>; fn toggle(&self) -> Result<(), String>; } impl Commands for Socket { fn get_metadata(&self) -> Result, String> { match get_mpv_property(self, "metadata") { Ok(map) => Ok(map), Err(err) => Err(err), } } fn get_playlist(&self) -> Result { Playlist::get_from(self.to_string()) } /// #Description /// /// Retrieves the property value from mpv. /// /// ##Supported types /// - String /// - bool /// - HashMap (e.g. for the 'metadata' property) /// - Vec (for the 'playlist' property) /// /// ##Input arguments /// /// - **socket** defines the socket that ipc connects to /// - **property** defines the mpv property that should be retrieved /// /// #Example /// ``` /// let mpv: Socket = String::from(matches.value_of("socket").unwrap()); /// let paused: bool = mpv.get_property("pause").unwrap(); /// let title: String = mpv.get_property("media-title").unwrap(); /// ``` fn get_property(&self, property: &str) -> Result { T::get_property_generic(self, property) } /// #Description /// /// Retrieves the property value from mpv. Implemented for the following types: /// The result is always of type String, regardless of the type of the value of the mpv property /// /// ##Input arguments /// /// - **socket** defines the socket that ipc connects to /// - **property** defines the mpv property that should be retrieved /// /// #Example /// /// ``` /// let mpv: Socket = String::from(matches.value_of("socket").unwrap()); /// let title = mpv.get_property_string("media-title").unwrap(); /// ``` fn get_property_string(&self, property: &str) -> Result { get_mpv_property_string(self, property) } fn kill(&self) -> Result<(), String> { run_mpv_command(self, "quit", &vec![]) } fn next(&self) -> Result<(), String> { run_mpv_command(self, "playlist-next", &vec![]) } fn pause(&self) -> Result<(), String> { set_mpv_property(self, "pause", true) } fn prev(&self) -> Result<(), String> { run_mpv_command(self, "playlist-prev", &vec![]) } fn restart(&self) -> Result<(), String> { run_mpv_command(self, "seek", &vec!["0", "absolute"]) } /// #Description /// /// Runs mpv commands. The arguments are passed as a String-Vector reference: /// /// #Example /// ``` /// let mpv: Socket = String::from(matches.value_of("socket").unwrap()); /// /// //Run command 'playlist-shuffle' which takes no arguments /// mpv.run_command("playlist-shuffle", &vec![]); /// /// //Run command 'seek' which in this case takes two arguments /// mpv.run_command("seek", &vec!["0", "absolute"]); /// ``` fn run_command(&self, command: &str, args: &Vec<&str>) -> Result<(), String> { run_mpv_command(self, command, args) } fn playlist_add(&self, file: &str, option: PlaylistAddOptions) -> Result<(), String> { match option { PlaylistAddOptions::Replace => { run_mpv_command(self, "loadfile", &vec![file, "replace"]) } PlaylistAddOptions::Append => run_mpv_command(self, "loadfile", &vec![file, "append"]), PlaylistAddOptions::AppendPlay => { run_mpv_command(self, "loadfile", &vec![file, "append-play"]) } } } fn playlist_clear(&self) -> Result<(), String> { run_mpv_command(self, "playlist-clear", &vec![]) } fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), String> { run_mpv_command(self, "playlist-remove", &vec![&from.to_string(), &to.to_string()]) } fn playlist_play_id(&self, id: usize) -> Result<(), String> { set_mpv_property(self, "playlist-pos", id) } fn playlist_play_next(&self, id: usize) -> Result<(), String> { match Playlist::get_from(self.to_string()) { Ok(playlist) => { if let Some(current_id) = playlist.current_id() { run_mpv_command(self, "playlist-move", &vec![&id.to_string(), &(current_id + 1).to_string()]) } else { Err("There is no file playing at the moment.".to_string()) } } Err(why) => Err(why), } } fn playlist_remove_id(&self, id: usize) -> Result<(), String> { run_mpv_command(self, "playlist-remove", &vec![&id.to_string()]) } fn playlist_shuffle(&self) -> Result<(), String> { run_mpv_command(self, "playlist-shuffle", &vec![]) } fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), String> { match option { SeekOptions::Absolute => { run_mpv_command(self, "seek", &vec![&seconds.to_string(), "absolute"]) } SeekOptions::AbsolutePercent => { run_mpv_command(self, "seek", &vec![&seconds.to_string(), "absolute-percent"]) } SeekOptions::Relative => { run_mpv_command(self, "seek", &vec![&seconds.to_string(), "relative"]) } SeekOptions::RelativePercent => { run_mpv_command(self, "seek", &vec![&seconds.to_string(), "relative-percent"]) } } } fn set_loop_file(&self, option: Switch) -> Result<(), String> { let mut enabled = false; match option { Switch::On => enabled = true, Switch::Off => {} Switch::Toggle => { match get_mpv_property_string(self, "loop-file") { Ok(value) => { match value.as_ref() { "false" => { enabled = true; } _ => { enabled = false; } } } Err(msg) => return Err(msg), } } } set_mpv_property(self, "loop-file", enabled) } fn set_loop_playlist(&self, option: Switch) -> Result<(), String> { let mut enabled = false; match option { Switch::On => enabled = true, Switch::Off => {} Switch::Toggle => { match get_mpv_property_string(self, "loop-playlist") { Ok(value) => { match value.as_ref() { "false" => { enabled = true; } _ => { enabled = false; } } } Err(msg) => return Err(msg), } } } set_mpv_property(self, "loop-playlist", enabled) } fn set_mute(&self, option: Switch) -> Result<(), String> { let mut enabled = false; match option { Switch::On => enabled = true, Switch::Off => {} Switch::Toggle => { match get_mpv_property::(self, "mute") { Ok(value) => { enabled = !value; } Err(msg) => return Err(msg), } } } set_mpv_property(self, "mute", enabled) } /// #Description /// /// Sets the mpv property __ to __. /// /// ##Supported types /// - String /// - bool /// - f64 /// - usize /// /// ##Input arguments /// /// - **property** defines the mpv property that should be retrieved /// - **value** defines the value of the given mpv property __ /// /// #Example /// ``` /// let mpv: Socket = String::from(matches.value_of("socket").unwrap()); /// mpv.set_property("pause", true); /// ``` fn set_property>(&self, property: &str, value: T) -> Result<(), String> { T::set_property_generic(self, property, value) } fn set_speed(&self, input_speed: f64, option: NumberChangeOptions) -> Result<(), String> { match get_mpv_property::(self, "speed") { Ok(speed) => { match option { NumberChangeOptions::Increase => { set_mpv_property(self, "speed", speed + input_speed) } NumberChangeOptions::Decrease => { set_mpv_property(self, "speed", speed - input_speed) } NumberChangeOptions::Absolute => set_mpv_property(self, "speed", input_speed), } } Err(msg) => Err(msg), } } fn set_volume(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), String> { match get_mpv_property::(self, "volume") { Ok(volume) => { match option { NumberChangeOptions::Increase => { set_mpv_property(self, "volume", volume + input_volume) } NumberChangeOptions::Decrease => { set_mpv_property(self, "volume", volume - input_volume) } NumberChangeOptions::Absolute => set_mpv_property(self, "volume", input_volume), } } Err(msg) => Err(msg), } } fn stop(&self) -> Result<(), String> { run_mpv_command(self, "stop", &vec![]) } fn toggle(&self) -> Result<(), String> { match get_mpv_property::(self, "pause") { Ok(paused) => set_mpv_property(self, "pause", !paused), Err(msg) => Err(msg), } } }