diff --git a/Cargo.toml b/Cargo.toml index cd5c5d2..dcb2c98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mpvipc" -version = "1.1.1" +version = "1.1.2" authors = ["Jonas Frei "] description = "A small library which provides bindings to control existing mpv instances through sockets." license = "GPL-3.0" diff --git a/src/ipc.rs b/src/ipc.rs index e6d85ed..55fa524 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -4,7 +4,7 @@ use std::io::BufReader; use std::io::prelude::*; use std::iter::Iterator; use std::sync::mpsc::Sender; -use super::{Mpv, Error, ErrorCode}; +use super::*; #[derive(Debug)] pub struct PlaylistEntry { @@ -33,10 +33,10 @@ impl TypeHandler for String { Err(Error(ErrorCode::MpvError(error.to_string()))) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } @@ -59,10 +59,10 @@ impl TypeHandler for bool { Err(Error(ErrorCode::MpvError(error.to_string()))) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } fn as_string(&self) -> String { @@ -88,10 +88,10 @@ impl TypeHandler for f64 { Err(Error(ErrorCode::MpvError(error.to_string()))) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } @@ -114,10 +114,10 @@ impl TypeHandler for usize { Err(Error(ErrorCode::MpvError(error.to_string()))) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } @@ -147,10 +147,10 @@ impl TypeHandler for HashMap { Err(Error(ErrorCode::MpvError(error.to_string()))) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } @@ -195,10 +195,10 @@ impl TypeHandler for Vec { Err(Error(ErrorCode::MpvError(error.to_string()))) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } else { - Err(Error(ErrorCode::UnexpectedValueReceived)) + Err(Error(ErrorCode::UnexpectedValue)) } } @@ -235,10 +235,10 @@ pub fn get_mpv_property_string(instance: &Mpv, property: &str) -> Result Err(Error(ErrorCode::JsonParseError(why.to_string()))), @@ -312,14 +312,33 @@ pub fn observe_mpv_property(instance: &Mpv, id: &usize, property: &str) -> Resul /// ``` /// listen("/tmp/mpvsocket"); /// ``` -pub fn listen(instance: &Mpv, tx: &Sender) { +pub fn listen(instance: &Mpv, tx: &Sender) { let mut response = String::new(); - let mut reader = BufReader::new(instance); + let mut reader = BufReader::new(&instance.0); reader.read_line(&mut response).unwrap(); match serde_json::from_str::(&response) { Ok(e) => { if let Value::String(ref name) = e["event"] { - tx.send(name.to_string()).unwrap(); + let event: Event = match name.as_str() { + "shutdown" => Event::Shutdown, + "start-file" => Event::StartFile, + "file-loaded" => Event::FileLoaded, + "seek" => Event::Seek, + "playback-restart" => Event::PlaybackRestart, + "idle" => Event::Idle, + "tick" => Event::Tick, + "video-reconfig" => Event::VideoReconfig, + "audio-reconfig" => Event::AudioReconfig, + "tracks-changed" => Event::TracksChanged, + "track-switched" => Event::TrackSwitched, + "pause" => Event::Pause, + "unpause" => Event::Unpause, + "metadata-update" => Event::MetadataUpdate, + "chapter-change" => Event::ChapterChange, + "end-file" => Event::EndFile, + _ => Event::Unimplemented, + }; + tx.send(event).unwrap(); } } Err(why) => panic!("{}", why.to_string()), @@ -329,14 +348,14 @@ pub fn listen(instance: &Mpv, tx: &Sender) { pub fn listen_raw(instance: &Mpv, tx: &Sender) { let mut response = String::new(); - let mut reader = BufReader::new(instance); + let mut reader = BufReader::new(&instance.0); reader.read_line(&mut response).unwrap(); tx.send(response.clone()).unwrap(); response.clear(); } fn send_command_sync(instance: &Mpv, command: &str) -> String { - let mut stream = instance; + let mut stream = &instance.0; match stream.write_all(command.as_bytes()) { Err(why) => panic!("Error: Could not write to socket: {}", why), Ok(_) => { diff --git a/src/lib.rs b/src/lib.rs index 4b6130a..ad0aa9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,53 @@ use std::fmt::{self, Display}; use std::os::unix::net::UnixStream; use std::sync::mpsc::Sender; -pub type Mpv = UnixStream; +#[derive(Debug)] +pub enum Event { + Shutdown, + // LogMessage { + // prefix: &'static str, + // level: &'static str, + // text: &'static str, + // log_level: LogLevel, + // }, + // GetPropertyReply { + // name: &'static str, + // result: Result>, + // reply_userdata: u32, + // }, + // SetPropertyReply(Result<()>, u32), + // CommandReply(Result<()>, u32), + StartFile, + EndFile, + FileLoaded, + TracksChanged, + TrackSwitched, + Idle, + Pause, + Unpause, + Tick, + VideoReconfig, + AudioReconfig, + MetadataUpdate, + Seek, + PlaybackRestart, + // PropertyChange { + // name: &'static str, + // change: MpvDataType, + // reply_userdata: u32, + // }, + ChapterChange, + Unimplemented, +} + +pub enum MpvDataType { + Bool(bool), + String(String), + Double(f64), + Usize(usize), + HashMap(HashMap), + Playlist(Playlist), +} pub enum NumberChangeOptions { Absolute, @@ -17,6 +63,17 @@ pub enum NumberChangeOptions { Decrease, } +pub enum PlaylistAddOptions { + Replace, + Append, + AppendPlay, +} + +pub enum PlaylistAddTypeOptions { + File, + Playlist, +} + pub enum SeekOptions { Relative, Absolute, @@ -24,12 +81,6 @@ pub enum SeekOptions { AbsolutePercent, } -pub enum PlaylistAddOptions { - Replace, - Append, - AppendPlay, -} - pub enum Switch { On, Off, @@ -42,7 +93,7 @@ pub enum ErrorCode { JsonParseError(String), ConnectError(String), UnexpectedResult, - UnexpectedValueReceived, + UnexpectedValue, UnsupportedType, ValueDoesNotContainBool, ValueDoesNotContainF64, @@ -52,12 +103,17 @@ pub enum ErrorCode { ValueDoesNotContainUsize, } +pub struct Mpv(UnixStream); pub struct Playlist(pub Vec); #[derive(Debug)] pub struct Error(pub ErrorCode); -pub trait MpvConnector { - fn connect(socket: &str) -> Result; +impl Drop for Mpv { + fn drop(&mut self) { + self.0 + .shutdown(std::net::Shutdown::Both) + .expect("stream shutdown"); + } } impl Display for Error { @@ -75,7 +131,7 @@ impl Display for ErrorCode { f.write_str(&format!("mpv returned an error value: {}", msg)) } ErrorCode::UnexpectedResult => f.write_str("Unexpected result received"), - ErrorCode::UnexpectedValueReceived => f.write_str("Unexpected value received"), + ErrorCode::UnexpectedValue => f.write_str("Unexpected value received"), ErrorCode::UnsupportedType => f.write_str("Unsupported type received"), ErrorCode::ValueDoesNotContainBool => { f.write_str("The received value is not of type \'std::bool\'") @@ -99,15 +155,6 @@ impl Display for ErrorCode { } } -impl MpvConnector for Mpv { - fn connect(socket: &str) -> Result { - match UnixStream::connect(socket) { - Ok(stream) => Ok(stream), - Err(internal_error) => Err(Error(ErrorCode::ConnectError(internal_error.to_string()))), - } - } -} - pub trait GetPropertyTypeHandler: Sized { fn get_property_generic(instance: &Mpv, property: &str) -> Result; } @@ -178,9 +225,27 @@ impl SetPropertyTypeHandler for usize { } } -pub trait Commands { - fn get_metadata(&self) -> Result, Error>; - fn get_playlist(&self) -> Result; +impl Mpv { + pub fn connect(socket: &str) -> Result { + match UnixStream::connect(socket) { + Ok(stream) => Ok(Mpv(stream)), + Err(internal_error) => Err(Error(ErrorCode::ConnectError(internal_error.to_string()))), + } + } + + pub fn get_metadata(&self) -> Result, Error> { + match get_mpv_property(self, "metadata") { + Ok(map) => Ok(map), + Err(err) => Err(err), + } + } + + pub fn get_playlist(&self) -> Result { + match get_mpv_property::>(self, "playlist") { + Ok(entries) => Ok(Playlist(entries)), + Err(msg) => Err(msg), + } + } /// #Description /// @@ -204,7 +269,9 @@ pub trait Commands { /// let paused: bool = mpv.get_property("pause").unwrap(); /// let title: String = mpv.get_property("media-title").unwrap(); /// ``` - fn get_property(&self, property: &str) -> Result; + pub fn get_property(&self, property: &str) -> Result { + T::get_property_generic(self, property) + } /// #Description /// @@ -221,22 +288,41 @@ pub trait Commands { /// let mpv = Mpv::connect("/tmp/mpvsocket").unwrap(); /// let title = mpv.get_property_string("media-title").unwrap(); /// ``` - fn get_property_string(&self, property: &str) -> Result; - fn kill(&self) -> Result<(), Error>; - fn listen(&self, tx: &Sender); - fn listen_raw(&self, tx: &Sender); - fn next(&self) -> Result<(), Error>; - fn observe_property(&self, id: &usize, property: &str) -> Result<(), Error>; - fn pause(&self) -> Result<(), Error>; - fn playlist_add(&self, file: &str, option: PlaylistAddOptions) -> Result<(), Error>; - fn playlist_clear(&self) -> Result<(), Error>; - fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), Error>; - fn playlist_play_id(&self, id: usize) -> Result<(), Error>; - fn playlist_play_next(&self, id: usize) -> Result<(), Error>; - fn playlist_shuffle(&self) -> Result<(), Error>; - fn playlist_remove_id(&self, id: usize) -> Result<(), Error>; - fn prev(&self) -> Result<(), Error>; - fn restart(&self) -> Result<(), Error>; + pub fn get_property_string(&self, property: &str) -> Result { + get_mpv_property_string(self, property) + } + + pub fn kill(&self) -> Result<(), Error> { + run_mpv_command(self, "quit", &[]) + } + + pub fn event_listen(&self, tx: &Sender) { + listen(self, tx); + } + + pub fn event_listen_raw(&self, tx: &Sender) { + listen_raw(self, tx); + } + + pub fn next(&self) -> Result<(), Error> { + run_mpv_command(self, "playlist-next", &[]) + } + + pub fn observe_property(&self, id: &usize, property: &str) -> Result<(), Error> { + observe_mpv_property(self, id, property) + } + + pub fn pause(&self) -> Result<(), Error> { + set_mpv_property(self, "pause", true) + } + + pub fn prev(&self) -> Result<(), Error> { + run_mpv_command(self, "playlist-prev", &[]) + } + + pub fn restart(&self) -> Result<(), Error> { + run_mpv_command(self, "seek", &["0", "absolute"]) + } /// #Description /// @@ -257,126 +343,60 @@ pub trait Commands { /// //Run command 'seek' which in this case takes two arguments /// mpv.run_command("seek", &["0", "absolute"]); /// ``` - fn run_command(&self, command: &str, args: &[&str]) -> Result<(), Error>; - fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), Error>; - fn set_loop_file(&self, option: Switch) -> Result<(), Error>; - fn set_loop_playlist(&self, option: Switch) -> Result<(), Error>; - fn set_mute(&self, option: Switch) -> Result<(), Error>; - - /// #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 = Mpv::connect("/tmp/mpvsocket").unwrap(); - /// mpv.set_property("pause", true); - /// ``` - fn set_property>(&self, - property: &str, - value: T) - -> Result<(), Error>; - fn set_speed(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), Error>; - fn set_volume(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), Error>; - fn stop(&self) -> Result<(), Error>; - fn toggle(&self) -> Result<(), Error>; -} - -impl Commands for Mpv { - fn get_metadata(&self) -> Result, Error> { - match get_mpv_property(self, "metadata") { - Ok(map) => Ok(map), - Err(err) => Err(err), - } - } - - fn get_playlist(&self) -> Result { - match get_mpv_property::>(self, "playlist") { - Ok(entries) => Ok(Playlist(entries)), - Err(msg) => Err(msg), - } - } - - fn get_property(&self, property: &str) -> Result { - T::get_property_generic(self, property) - } - - fn get_property_string(&self, property: &str) -> Result { - get_mpv_property_string(self, property) - } - - fn kill(&self) -> Result<(), Error> { - run_mpv_command(self, "quit", &[]) - } - - fn listen(&self, tx: &Sender) { - listen(self, tx); - } - - fn listen_raw(&self, tx: &Sender) { - listen_raw(self, tx); - } - - fn next(&self) -> Result<(), Error> { - run_mpv_command(self, "playlist-next", &[]) - } - - fn observe_property(&self, id: &usize, property: &str) -> Result<(), Error> { - observe_mpv_property(self, id, property) - } - - fn pause(&self) -> Result<(), Error> { - set_mpv_property(self, "pause", true) - } - - fn prev(&self) -> Result<(), Error> { - run_mpv_command(self, "playlist-prev", &[]) - } - - fn restart(&self) -> Result<(), Error> { - run_mpv_command(self, "seek", &["0", "absolute"]) - } - - fn run_command(&self, command: &str, args: &[&str]) -> Result<(), Error> { + pub fn run_command(&self, command: &str, args: &[&str]) -> Result<(), Error> { run_mpv_command(self, command, args) } - fn playlist_add(&self, file: &str, option: PlaylistAddOptions) -> Result<(), Error> { - match option { - PlaylistAddOptions::Replace => run_mpv_command(self, "loadfile", &[file, "replace"]), - PlaylistAddOptions::Append => run_mpv_command(self, "loadfile", &[file, "append"]), - PlaylistAddOptions::AppendPlay => { - run_mpv_command(self, "loadfile", &[file, "append-play"]) + pub fn playlist_add(&self, + file: &str, + file_type: PlaylistAddTypeOptions, + option: PlaylistAddOptions) + -> Result<(), Error> { + match file_type { + PlaylistAddTypeOptions::File => { + match option { + PlaylistAddOptions::Replace => { + run_mpv_command(self, "loadfile", &[file, "replace"]) + } + PlaylistAddOptions::Append => { + run_mpv_command(self, "loadfile", &[file, "append"]) + } + PlaylistAddOptions::AppendPlay => { + run_mpv_command(self, "loadfile", &[file, "append-play"]) + } + } + } + + PlaylistAddTypeOptions::Playlist => { + match option { + PlaylistAddOptions::Replace => { + run_mpv_command(self, "loadlist", &[file, "replace"]) + } + PlaylistAddOptions::Append | + PlaylistAddOptions::AppendPlay => { + run_mpv_command(self, "loadlist", &[file, "append"]) + } + } } } + } - fn playlist_clear(&self) -> Result<(), Error> { + pub fn playlist_clear(&self) -> Result<(), Error> { run_mpv_command(self, "playlist-clear", &[]) } - fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), Error> { + pub fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), Error> { run_mpv_command(self, "playlist-remove", &[&from.to_string(), &to.to_string()]) } - fn playlist_play_id(&self, id: usize) -> Result<(), Error> { + pub fn playlist_play_id(&self, id: usize) -> Result<(), Error> { set_mpv_property(self, "playlist-pos", id) } - fn playlist_play_next(&self, id: usize) -> Result<(), Error> { + pub fn playlist_play_next(&self, id: usize) -> Result<(), Error> { match get_mpv_property::(self, "playlist-pos") { Ok(current_id) => { run_mpv_command(self, @@ -387,15 +407,15 @@ impl Commands for Mpv { } } - fn playlist_remove_id(&self, id: usize) -> Result<(), Error> { + pub fn playlist_remove_id(&self, id: usize) -> Result<(), Error> { run_mpv_command(self, "playlist-remove", &[&id.to_string()]) } - fn playlist_shuffle(&self) -> Result<(), Error> { + pub fn playlist_shuffle(&self) -> Result<(), Error> { run_mpv_command(self, "playlist-shuffle", &[]) } - fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), Error> { + pub fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), Error> { match option { SeekOptions::Absolute => { run_mpv_command(self, "seek", &[&seconds.to_string(), "absolute"]) @@ -412,7 +432,7 @@ impl Commands for Mpv { } } - fn set_loop_file(&self, option: Switch) -> Result<(), Error> { + pub fn set_loop_file(&self, option: Switch) -> Result<(), Error> { let mut enabled = false; match option { Switch::On => enabled = true, @@ -436,7 +456,7 @@ impl Commands for Mpv { set_mpv_property(self, "loop-file", enabled) } - fn set_loop_playlist(&self, option: Switch) -> Result<(), Error> { + pub fn set_loop_playlist(&self, option: Switch) -> Result<(), Error> { let mut enabled = false; match option { Switch::On => enabled = true, @@ -460,7 +480,7 @@ impl Commands for Mpv { set_mpv_property(self, "loop-playlist", enabled) } - fn set_mute(&self, option: Switch) -> Result<(), Error> { + pub fn set_mute(&self, option: Switch) -> Result<(), Error> { let mut enabled = false; match option { Switch::On => enabled = true, @@ -477,14 +497,34 @@ impl Commands for Mpv { set_mpv_property(self, "mute", enabled) } - fn set_property>(&self, - property: &str, - value: T) - -> Result<(), Error> { + /// #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 = Mpv::connect("/tmp/mpvsocket").unwrap(); + /// mpv.set_property("pause", true); + /// ``` + pub fn set_property>(&self, + property: &str, + value: T) + -> Result<(), Error> { T::set_property_generic(self, property, value) } - fn set_speed(&self, input_speed: f64, option: NumberChangeOptions) -> Result<(), Error> { + pub fn set_speed(&self, input_speed: f64, option: NumberChangeOptions) -> Result<(), Error> { match get_mpv_property::(self, "speed") { Ok(speed) => { match option { @@ -503,7 +543,7 @@ impl Commands for Mpv { } } - fn set_volume(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), Error> { + pub fn set_volume(&self, input_volume: f64, option: NumberChangeOptions) -> Result<(), Error> { match get_mpv_property::(self, "volume") { Ok(volume) => { match option { @@ -522,11 +562,11 @@ impl Commands for Mpv { } } - fn stop(&self) -> Result<(), Error> { + pub fn stop(&self) -> Result<(), Error> { run_mpv_command(self, "stop", &[]) } - fn toggle(&self) -> Result<(), Error> { + pub fn toggle(&self) -> Result<(), Error> { match get_mpv_property::(self, "pause") { Ok(paused) => set_mpv_property(self, "pause", !paused), Err(msg) => Err(msg),