diff --git a/examples/media_player.rs b/examples/media_player.rs index 2f394de..891015c 100644 --- a/examples/media_player.rs +++ b/examples/media_player.rs @@ -1,5 +1,5 @@ use futures::StreamExt; -use mpvipc::{parse_event_property, Event, Mpv, MpvDataType, MpvError, MpvExt, Property}; +use mpvipc::{parse_property, Event, Mpv, MpvDataType, MpvError, MpvExt, Property}; fn seconds_to_hms(total: f64) -> String { let total = total as u64; @@ -25,34 +25,36 @@ async fn main() -> Result<(), MpvError> { let mut events = mpv.get_event_stream().await; while let Some(Ok(event)) = events.next().await { match event { - mpvipc::Event::PropertyChange { .. } => match parse_event_property(event)? { - (1, Property::Path(Some(value))) => println!("\nPlaying: {}", value), - (2, Property::Pause(value)) => { - println!("Pause: {}", value); - } - (3, Property::PlaybackTime(Some(value))) => { - println!("Playback time: {}", seconds_to_hms(value)); - } - (4, Property::Duration(Some(value))) => { - println!("Duration: {}", seconds_to_hms(value)); - } - (5, Property::Metadata(Some(value))) => { - println!("File tags:"); - if let Some(MpvDataType::String(value)) = value.get("ARTIST") { - println!(" Artist: {}", value); + mpvipc::Event::PropertyChange { name, data, .. } => { + match parse_property(&name, data)? { + Property::Path(Some(value)) => println!("\nPlaying: {}", value), + Property::Pause(value) => { + println!("Pause: {}", value); } - if let Some(MpvDataType::String(value)) = value.get("ALBUM") { - println!(" Album: {}", value); + Property::PlaybackTime(Some(value)) => { + println!("Playback time: {}", seconds_to_hms(value)); } - if let Some(MpvDataType::String(value)) = value.get("TITLE") { - println!(" Title: {}", value); + Property::Duration(Some(value)) => { + println!("Duration: {}", seconds_to_hms(value)); } - if let Some(MpvDataType::String(value)) = value.get("TRACK") { - println!(" Track: {}", value); + Property::Metadata(Some(value)) => { + println!("File tags:"); + if let Some(MpvDataType::String(value)) = value.get("ARTIST") { + println!(" Artist: {}", value); + } + if let Some(MpvDataType::String(value)) = value.get("ALBUM") { + println!(" Album: {}", value); + } + if let Some(MpvDataType::String(value)) = value.get("TITLE") { + println!(" Title: {}", value); + } + if let Some(MpvDataType::String(value)) = value.get("TRACK") { + println!(" Track: {}", value); + } } + _ => (), } - _ => (), - }, + } Event::Shutdown => return Ok(()), Event::Unimplemented(_) => panic!("Unimplemented event"), _ => (), diff --git a/src/error.rs b/src/error.rs index 6426f8c..9f4a860 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,7 +3,7 @@ use serde_json::{Map, Value}; use thiserror::Error; -use crate::MpvDataType; +use crate::{MpvDataType, Property}; /// Any error that can occur when interacting with mpv. #[derive(Error, Debug)] @@ -43,6 +43,9 @@ pub enum MpvError { map: Map, }, + #[error("Unexpected property: {0:?}")] + UnexpectedProperty(Property), + #[error("Unknown error: {0}")] Other(String), } diff --git a/src/highlevel_api_extension.rs b/src/highlevel_api_extension.rs index 255f53e..9f50897 100644 --- a/src/highlevel_api_extension.rs +++ b/src/highlevel_api_extension.rs @@ -1,8 +1,8 @@ //! High-level API extension for [`Mpv`]. use crate::{ - IntoRawCommandPart, Mpv, MpvCommand, MpvDataType, MpvError, Playlist, PlaylistAddOptions, - PlaylistEntry, SeekOptions, + parse_property, IntoRawCommandPart, LoopProperty, Mpv, MpvCommand, MpvDataType, MpvError, + Playlist, PlaylistAddOptions, Property, SeekOptions, }; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -44,35 +44,7 @@ pub enum PlaylistAddTypeOptions { // TODO: fix this #[allow(async_fn_in_trait)] pub trait MpvExt { - /// Stop the player completely (as opposed to pausing), - /// removing the pointer to the current video. - async fn stop(&self) -> Result<(), MpvError>; - - /// Set the volume of the player. - async fn set_volume( - &self, - input_volume: f64, - option: NumberChangeOptions, - ) -> Result<(), MpvError>; - - /// Set the playback speed of the player. - async fn set_speed( - &self, - input_speed: f64, - option: NumberChangeOptions, - ) -> Result<(), MpvError>; - - /// Toggle/set the pause state of the player. - async fn set_playback(&self, option: Switch) -> Result<(), MpvError>; - - /// Toggle/set the mute state of the player. - async fn set_mute(&self, option: Switch) -> Result<(), MpvError>; - - /// Toggle/set whether the player should loop the current playlist. - async fn set_loop_playlist(&self, option: Switch) -> Result<(), MpvError>; - - /// Toggle/set whether the player should loop the current video. - async fn set_loop_file(&self, option: Switch) -> Result<(), MpvError>; + // COMMANDS /// Seek to a specific position in the current video. async fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), MpvError>; @@ -132,74 +104,122 @@ pub trait MpvExt { /// it to exit itself. If mpv is stuck, it may not respond to this command. async fn kill(&self) -> Result<(), MpvError>; + /// Stop the player completely (as opposed to pausing), + /// removing the pointer to the current video. + async fn stop(&self) -> Result<(), MpvError>; + + // SETTERS + + /// Set the volume of the player. + async fn set_volume( + &self, + input_volume: f64, + option: NumberChangeOptions, + ) -> Result<(), MpvError>; + + /// Set the playback speed of the player. + async fn set_speed( + &self, + input_speed: f64, + option: NumberChangeOptions, + ) -> Result<(), MpvError>; + + /// Toggle/set the pause state of the player. + async fn set_playback(&self, option: Switch) -> Result<(), MpvError>; + + /// Toggle/set the mute state of the player. + async fn set_mute(&self, option: Switch) -> Result<(), MpvError>; + + /// Toggle/set whether the player should loop the current playlist. + async fn set_loop_playlist(&self, option: Switch) -> Result<(), MpvError>; + + /// Toggle/set whether the player should loop the current video. + async fn set_loop_file(&self, option: Switch) -> Result<(), MpvError>; + + // GETTERS + /// Get a list of all entries in the playlist. async fn get_playlist(&self) -> Result; /// Get metadata about the current video. async fn get_metadata(&self) -> Result, MpvError>; + + /// Get the path of the current video. + async fn get_file_path(&self) -> Result; + + /// Get the current volume of the player. + async fn get_volume(&self) -> Result; + + /// Get the playback speed of the player. + async fn get_speed(&self) -> Result; + + /// Get the current position in the current video. + async fn get_time_pos(&self) -> Result; + + /// Get the amount of time remaining in the current video. + async fn get_time_remaining(&self) -> Result; + + /// Get the total duration of the current video. + async fn get_duration(&self) -> Result; + + /// Get the current position in the playlist. + async fn get_playlist_pos(&self) -> Result; + + // BOOLEAN GETTERS + + /// Check whether the player is muted. + async fn is_muted(&self) -> Result; + + /// Check whether the player is currently playing. + async fn is_playing(&self) -> Result; + + /// Check whether the player is looping the current playlist. + async fn playlist_is_looping(&self) -> Result; + + /// Check whether the player is looping the current video. + async fn file_is_looping(&self) -> Result; } impl MpvExt for Mpv { - async fn get_metadata(&self) -> Result, MpvError> { - self.get_property("metadata") - .await? - .ok_or(MpvError::MissingMpvData) + // COMMANDS + + async fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), MpvError> { + self.run_command(MpvCommand::Seek { seconds, option }).await } - async fn get_playlist(&self) -> Result { - self.get_property::>("playlist") - .await? - .ok_or(MpvError::MissingMpvData) - .map(Playlist) + async fn playlist_shuffle(&self) -> Result<(), MpvError> { + self.run_command(MpvCommand::PlaylistShuffle).await } - async fn kill(&self) -> Result<(), MpvError> { - self.run_command(MpvCommand::Quit).await + async fn playlist_remove_id(&self, id: usize) -> Result<(), MpvError> { + self.run_command(MpvCommand::PlaylistRemove(id)).await } - async fn next(&self) -> Result<(), MpvError> { - self.run_command(MpvCommand::PlaylistNext).await - } + async fn playlist_play_next(&self, id: usize) -> Result<(), MpvError> { + let data = self.get_property("playlist-pos").await?; + let current_id = match parse_property("playlist-pos", data)? { + Property::PlaylistPos(Some(current_id)) => Ok(current_id), + prop => Err(MpvError::UnexpectedProperty(prop)), + }?; - async fn observe_property(&self, id: usize, property: &str) -> Result<(), MpvError> { - self.run_command(MpvCommand::Observe { - id, - property: property.to_string(), + self.run_command(MpvCommand::PlaylistMove { + from: id, + to: current_id + 1, }) .await } - async fn unobserve_property(&self, id: usize) -> Result<(), MpvError> { - self.run_command(MpvCommand::Unobserve(id)).await + async fn playlist_play_id(&self, id: usize) -> Result<(), MpvError> { + self.set_property("playlist-pos", id).await } - async fn set_playback(&self, option: Switch) -> Result<(), MpvError> { - let enabled = match option { - Switch::On => "yes", - Switch::Off => "no", - Switch::Toggle => self - .get_property::("pause") - .await? - .ok_or(MpvError::MissingMpvData) - .map(|s| match s.as_str() { - "yes" => "no", - "no" => "yes", - _ => "no", - })?, - }; - self.set_property("pause", enabled).await + async fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), MpvError> { + self.run_command(MpvCommand::PlaylistMove { from, to }) + .await } - async fn prev(&self) -> Result<(), MpvError> { - self.run_command(MpvCommand::PlaylistPrev).await - } - - async fn restart(&self) -> Result<(), MpvError> { - self.run_command(MpvCommand::Seek { - seconds: 0f64, - option: SeekOptions::Absolute, - }) - .await + async fn playlist_clear(&self) -> Result<(), MpvError> { + self.run_command(MpvCommand::PlaylistClear).await } async fn playlist_add( @@ -227,121 +247,50 @@ impl MpvExt for Mpv { } } - async fn playlist_clear(&self) -> Result<(), MpvError> { - self.run_command(MpvCommand::PlaylistClear).await - } - - async fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), MpvError> { - self.run_command(MpvCommand::PlaylistMove { from, to }) - .await - } - - async fn playlist_play_id(&self, id: usize) -> Result<(), MpvError> { - self.set_property("playlist-pos", id).await - } - - async fn playlist_play_next(&self, id: usize) -> Result<(), MpvError> { - let current_id = self - .get_property::("playlist-pos") - .await? - .ok_or(MpvError::MissingMpvData)?; - - self.run_command(MpvCommand::PlaylistMove { - from: id, - to: current_id + 1, + async fn restart(&self) -> Result<(), MpvError> { + self.run_command(MpvCommand::Seek { + seconds: 0f64, + option: SeekOptions::Absolute, }) .await } - async fn playlist_remove_id(&self, id: usize) -> Result<(), MpvError> { - self.run_command(MpvCommand::PlaylistRemove(id)).await + async fn prev(&self) -> Result<(), MpvError> { + self.run_command(MpvCommand::PlaylistPrev).await } - async fn playlist_shuffle(&self) -> Result<(), MpvError> { - self.run_command(MpvCommand::PlaylistShuffle).await + async fn observe_property(&self, id: usize, property: &str) -> Result<(), MpvError> { + self.run_command(MpvCommand::Observe { + id, + property: property.to_string(), + }) + .await } - async fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), MpvError> { - self.run_command(MpvCommand::Seek { seconds, option }).await + async fn unobserve_property(&self, id: usize) -> Result<(), MpvError> { + self.run_command(MpvCommand::Unobserve(id)).await } - async fn set_loop_file(&self, option: Switch) -> Result<(), MpvError> { - let enabled = match option { - Switch::On => "inf", - Switch::Off => "no", - Switch::Toggle => self - .get_property::("loop-file") - .await? - .ok_or(MpvError::MissingMpvData) - .map(|s| match s.as_str() { - "inf" => "no", - "no" => "inf", - _ => "no", - })?, - }; - self.set_property("loop-file", enabled).await + async fn next(&self) -> Result<(), MpvError> { + self.run_command(MpvCommand::PlaylistNext).await } - async fn set_loop_playlist(&self, option: Switch) -> Result<(), MpvError> { - let enabled = match option { - Switch::On => "inf", - Switch::Off => "no", - Switch::Toggle => self - .get_property::("loop-playlist") - .await? - .ok_or(MpvError::MissingMpvData) - .map(|s| match s.as_str() { - "inf" => "no", - "no" => "inf", - _ => "no", - })?, - }; - self.set_property("loop-playlist", enabled).await + async fn kill(&self) -> Result<(), MpvError> { + self.run_command(MpvCommand::Quit).await } - async fn set_mute(&self, option: Switch) -> Result<(), MpvError> { - let enabled = match option { - Switch::On => "yes", - Switch::Off => "no", - Switch::Toggle => self - .get_property::("mute") - .await? - .ok_or(MpvError::MissingMpvData) - .map(|s| match s.as_str() { - "yes" => "no", - "no" => "yes", - _ => "no", - })?, - }; - self.set_property("mute", enabled).await + async fn stop(&self) -> Result<(), MpvError> { + self.run_command(MpvCommand::Stop).await } - async fn set_speed( - &self, - input_speed: f64, - option: NumberChangeOptions, - ) -> Result<(), MpvError> { - let speed = self - .get_property::("speed") - .await? - .ok_or(MpvError::MissingMpvData)?; - - match option { - NumberChangeOptions::Increase => self.set_property("speed", speed + input_speed).await, - NumberChangeOptions::Decrease => self.set_property("speed", speed - input_speed).await, - NumberChangeOptions::Absolute => self.set_property("speed", input_speed).await, - } - } + // SETTERS async fn set_volume( &self, input_volume: f64, option: NumberChangeOptions, ) -> Result<(), MpvError> { - let volume = self - .get_property::("volume") - .await? - .ok_or(MpvError::MissingMpvData)?; + let volume = self.get_volume().await?; match option { NumberChangeOptions::Increase => { @@ -354,7 +303,181 @@ impl MpvExt for Mpv { } } - async fn stop(&self) -> Result<(), MpvError> { - self.run_command(MpvCommand::Stop).await + async fn set_speed( + &self, + input_speed: f64, + option: NumberChangeOptions, + ) -> Result<(), MpvError> { + let speed = self.get_speed().await?; + + match option { + NumberChangeOptions::Increase => self.set_property("speed", speed + input_speed).await, + NumberChangeOptions::Decrease => self.set_property("speed", speed - input_speed).await, + NumberChangeOptions::Absolute => self.set_property("speed", input_speed).await, + } + } + + async fn set_playback(&self, option: Switch) -> Result<(), MpvError> { + let enabled = match option { + Switch::On => "yes", + Switch::Off => "no", + Switch::Toggle => { + if self.is_playing().await? { + "no" + } else { + "yes" + } + } + }; + self.set_property("pause", enabled).await + } + + async fn set_mute(&self, option: Switch) -> Result<(), MpvError> { + let enabled = match option { + Switch::On => "yes", + Switch::Off => "no", + Switch::Toggle => { + if self.is_muted().await? { + "no" + } else { + "yes" + } + } + }; + self.set_property("mute", enabled).await + } + + async fn set_loop_playlist(&self, option: Switch) -> Result<(), MpvError> { + let enabled = match option { + Switch::On => "inf", + Switch::Off => "no", + Switch::Toggle => match self.playlist_is_looping().await? { + LoopProperty::Inf => "no", + LoopProperty::N(_) => "no", + LoopProperty::No => "inf", + }, + }; + self.set_property("loop-playlist", enabled).await + } + + async fn set_loop_file(&self, option: Switch) -> Result<(), MpvError> { + let enabled = match option { + Switch::On => "inf", + Switch::Off => "no", + Switch::Toggle => match self.file_is_looping().await? { + LoopProperty::Inf => "no", + LoopProperty::N(_) => "no", + LoopProperty::No => "inf", + }, + }; + self.set_property("loop-file", enabled).await + } + + // GETTERS + + async fn get_playlist(&self) -> Result { + let data = self.get_property("playlist").await?; + match parse_property("playlist", data)? { + Property::Playlist(value) => Ok(Playlist(value)), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_metadata(&self) -> Result, MpvError> { + let data = self.get_property("metadata").await?; + match parse_property("metadata", data)? { + Property::Metadata(Some(value)) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_file_path(&self) -> Result { + let data = self.get_property("path").await?; + match parse_property("path", data)? { + Property::Path(Some(value)) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_volume(&self) -> Result { + let data = self.get_property("volume").await?; + match parse_property("volume", data)? { + Property::Volume(value) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_speed(&self) -> Result { + let data = self.get_property("speed").await?; + match parse_property("speed", data)? { + Property::Speed(value) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_time_pos(&self) -> Result { + let data = self.get_property("time-pos").await?; + match parse_property("time-pos", data)? { + Property::TimePos(value) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_time_remaining(&self) -> Result { + let data = self.get_property("time-remaining").await?; + match parse_property("time-remaining", data)? { + Property::TimeRemaining(value) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_duration(&self) -> Result { + let data = self.get_property("duration").await?; + match parse_property("duration", data)? { + Property::Duration(Some(value)) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn get_playlist_pos(&self) -> Result { + let data = self.get_property("playlist-pos").await?; + match parse_property("playlist-pos", data)? { + Property::PlaylistPos(Some(value)) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + // BOOLEAN GETTERS + + async fn is_muted(&self) -> Result { + let data = self.get_property("mute").await?; + match parse_property("mute", data)? { + Property::Mute(value) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn is_playing(&self) -> Result { + let data = self.get_property("pause").await?; + match parse_property("pause", data)? { + Property::Pause(value) => Ok(!value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn playlist_is_looping(&self) -> Result { + let data = self.get_property("loop-playlist").await?; + match parse_property("loop-playlist", data)? { + Property::LoopPlaylist(value) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } + } + + async fn file_is_looping(&self) -> Result { + let data = self.get_property("loop-file").await?; + match parse_property("loop-file", data)? { + Property::LoopFile(value) => Ok(value), + prop => Err(MpvError::UnexpectedProperty(prop)), + } } } diff --git a/src/lib.rs b/src/lib.rs index 8be302b..955b70c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,13 +3,13 @@ mod core_api; mod error; mod event_parser; -mod event_property_parser; mod highlevel_api_extension; mod ipc; mod message_parser; +mod property_parser; pub use core_api::*; pub use error::*; pub use event_parser::*; -pub use event_property_parser::*; pub use highlevel_api_extension::*; +pub use property_parser::*; diff --git a/src/event_property_parser.rs b/src/property_parser.rs similarity index 77% rename from src/event_property_parser.rs rename to src/property_parser.rs index 5250772..6995b61 100644 --- a/src/event_property_parser.rs +++ b/src/property_parser.rs @@ -1,21 +1,24 @@ -//! JSON parsing logic for properties returned in [`Event::PropertyChange`] +//! JSON parsing logic for properties returned by +//! [[`Event::PropertyChange`], and used internally in `MpvExt` +//! to parse the response from `Mpv::get_property()`. //! -//! This module is used to parse the json data from the `data` field of the -//! [`Event::PropertyChange`] variant. Mpv has about 1000 different properties +//! This module is used to parse the json data from the `data` field of +//! known properties. Mpv has about 1000 different properties //! as of `v0.38.0`, so this module will only implement the most common ones. +// TODO: reuse this logic for providing a more typesafe response API to `Mpv::get_property()` +// Although this data is currently of type `Option<` + use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use crate::{Event, MpvDataType, MpvError, PlaylistEntry}; +use crate::{MpvDataType, MpvError, PlaylistEntry}; -/// All possible properties that can be observed through the event system. +/// An incomplete list of properties that mpv can return. /// -/// Not all properties are guaranteed to be implemented. -/// If something is missing, please open an issue. -/// -/// Otherwise, the property will be returned as a `Property::Unknown` variant. +/// Unimplemented properties will be returned with it's data +/// as a `Property::Unknown` variant. /// /// See for /// the upstream list of properties. @@ -31,6 +34,8 @@ pub enum Property { PlaylistPos(Option), LoopFile(LoopProperty), LoopPlaylist(LoopProperty), + TimePos(f64), + TimeRemaining(f64), Speed(f64), Volume(f64), Mute(bool), @@ -48,17 +53,12 @@ pub enum LoopProperty { No, } -/// Parse a highlevel [`Property`] object from json, used for [`Event::PropertyChange`]. -pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> { - let (id, name, data) = match event { - Event::PropertyChange { id, name, data } => (id, name, data), - // TODO: return proper error - _ => { - panic!("Event is not a PropertyChange event") - } - }; - - match name.as_str() { +/// Parse a highlevel [`Property`] object from mpv data. +/// +/// This is intended to be used with the `data` field of +/// `Event::PropertyChange` and the response from `Mpv::get_property_value()`. +pub fn parse_property(name: &str, data: Option) -> Result { + match name { "path" => { let path = match data { Some(MpvDataType::String(s)) => Some(s), @@ -73,7 +73,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> return Err(MpvError::MissingMpvData); } }; - Ok((id, Property::Path(path))) + Ok(Property::Path(path)) } "pause" => { let pause = match data { @@ -88,7 +88,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> return Err(MpvError::MissingMpvData); } }; - Ok((id, Property::Pause(pause))) + Ok(Property::Pause(pause)) } "playback-time" => { let playback_time = match data { @@ -101,7 +101,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> }) } }; - Ok((id, Property::PlaybackTime(playback_time))) + Ok(Property::PlaybackTime(playback_time)) } "duration" => { let duration = match data { @@ -114,7 +114,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> }) } }; - Ok((id, Property::Duration(duration))) + Ok(Property::Duration(duration)) } "metadata" => { let metadata = match data { @@ -127,7 +127,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> }) } }; - Ok((id, Property::Metadata(metadata))) + Ok(Property::Metadata(metadata)) } "playlist" => { let playlist = match data { @@ -140,7 +140,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> }) } }; - Ok((id, Property::Playlist(playlist))) + Ok(Property::Playlist(playlist)) } "playlist-pos" => { let playlist_pos = match data { @@ -155,7 +155,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> }) } }; - Ok((id, Property::PlaylistPos(playlist_pos))) + Ok(Property::PlaylistPos(playlist_pos)) } "loop-file" => { let loop_file = match data.to_owned() { @@ -177,7 +177,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> }, None => MpvError::MissingMpvData, })?; - Ok((id, Property::LoopFile(loop_file))) + Ok(Property::LoopFile(loop_file)) } "loop-playlist" => { let loop_playlist = match data.to_owned() { @@ -200,7 +200,37 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> None => MpvError::MissingMpvData, })?; - Ok((id, Property::LoopPlaylist(loop_playlist))) + Ok(Property::LoopPlaylist(loop_playlist)) + } + "time-pos" => { + let time_pos = match data { + Some(MpvDataType::Double(d)) => d, + Some(data) => { + return Err(MpvError::DataContainsUnexpectedType { + expected_type: "f64".to_owned(), + received: data, + }) + } + None => { + return Err(MpvError::MissingMpvData); + } + }; + Ok(Property::TimePos(time_pos)) + } + "time-remaining" => { + let time_remaining = match data { + Some(MpvDataType::Double(d)) => d, + Some(data) => { + return Err(MpvError::DataContainsUnexpectedType { + expected_type: "f64".to_owned(), + received: data, + }) + } + None => { + return Err(MpvError::MissingMpvData); + } + }; + Ok(Property::TimeRemaining(time_remaining)) } "speed" => { let speed = match data { @@ -215,7 +245,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> return Err(MpvError::MissingMpvData); } }; - Ok((id, Property::Speed(speed))) + Ok(Property::Speed(speed)) } "volume" => { let volume = match data { @@ -230,7 +260,7 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> return Err(MpvError::MissingMpvData); } }; - Ok((id, Property::Volume(volume))) + Ok(Property::Volume(volume)) } "mute" => { let mute = match data { @@ -245,10 +275,13 @@ pub fn parse_event_property(event: Event) -> Result<(usize, Property), MpvError> return Err(MpvError::MissingMpvData); } }; - Ok((id, Property::Mute(mute))) + Ok(Property::Mute(mute)) } // TODO: add missing cases - _ => Ok((id, Property::Unknown { name, data })), + _ => Ok(Property::Unknown { + name: name.to_owned(), + data, + }), } } diff --git a/tests/integration_tests/event_property_parser.rs b/tests/integration_tests/event_property_parser.rs index 6981e97..04a2e34 100644 --- a/tests/integration_tests/event_property_parser.rs +++ b/tests/integration_tests/event_property_parser.rs @@ -1,5 +1,5 @@ use futures::{stream::StreamExt, Stream}; -use mpvipc::{parse_event_property, Event, Mpv, MpvError, MpvExt}; +use mpvipc::{parse_property, Event, Mpv, MpvError, MpvExt}; use thiserror::Error; use tokio::time::sleep; use tokio::time::{timeout, Duration}; @@ -38,8 +38,8 @@ where match event { Some(Ok(event)) => { match event { - Event::PropertyChange { id: MPV_CHANNEL_ID, .. } => { - let property = parse_event_property(event).unwrap().1; + Event::PropertyChange { id: MPV_CHANNEL_ID, name, data } => { + let property = parse_property(&name, data).unwrap(); if !on_property(property.clone()) { return Err(PropertyCheckingThreadError::UnexpectedPropertyError(property)) } diff --git a/tests/mock_socket_tests/events.rs b/tests/mock_socket_tests/events.rs index 1e482af..018e642 100644 --- a/tests/mock_socket_tests/events.rs +++ b/tests/mock_socket_tests/events.rs @@ -1,7 +1,5 @@ -use std::panic; - use futures::{stream::StreamExt, SinkExt}; -use mpvipc::{parse_event_property, Mpv, MpvDataType, MpvExt, Property}; +use mpvipc::{Event, Mpv, MpvDataType, MpvExt}; use serde_json::json; use test_log::test; use tokio::{net::UnixStream, task::JoinHandle}; @@ -51,19 +49,14 @@ async fn test_observe_event_successful() { tokio::spawn(async move { let event = mpv2.get_event_stream().await.next().await.unwrap().unwrap(); - let data = match parse_event_property(event) { - Ok((_, Property::Unknown { name, data })) => { - assert_eq!(name, "volume"); - data + assert_eq!( + event, + Event::PropertyChange { + id: 1, + name: "volume".to_string(), + data: Some(MpvDataType::Double(64.0)) } - Ok((_, property)) => panic!("{:?}", property), - Err(err) => panic!("{:?}", err), - }; - match data { - Some(MpvDataType::Double(data)) => assert_eq!(data, 64.0), - Some(data) => panic!("Unexpected value: {:?}", data), - None => panic!("No data"), - } + ) }); mpv.set_property("volume", 64.0).await.unwrap();