2024-04-19 01:36:11 +02:00
|
|
|
use futures::StreamExt;
|
2024-04-16 22:48:27 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
2024-04-17 00:07:11 +02:00
|
|
|
use serde_json::Value;
|
|
|
|
use std::{
|
|
|
|
collections::HashMap,
|
|
|
|
fmt::{self, Display},
|
2024-04-16 22:48:27 +02:00
|
|
|
};
|
2024-04-19 01:36:11 +02:00
|
|
|
use tokio::{
|
|
|
|
net::UnixStream,
|
|
|
|
sync::{broadcast, mpsc, oneshot},
|
|
|
|
};
|
2024-04-17 00:07:11 +02:00
|
|
|
|
2024-04-19 01:36:11 +02:00
|
|
|
use crate::ipc::{MpvIpc, MpvIpcCommand, MpvIpcEvent, MpvIpcResponse};
|
2024-04-17 00:07:11 +02:00
|
|
|
use crate::message_parser::TypeHandler;
|
2024-04-16 22:48:27 +02:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
pub enum Event {
|
|
|
|
Shutdown,
|
|
|
|
StartFile,
|
|
|
|
EndFile,
|
|
|
|
FileLoaded,
|
|
|
|
TracksChanged,
|
|
|
|
TrackSwitched,
|
|
|
|
Idle,
|
|
|
|
Pause,
|
|
|
|
Unpause,
|
|
|
|
Tick,
|
|
|
|
VideoReconfig,
|
|
|
|
AudioReconfig,
|
|
|
|
MetadataUpdate,
|
|
|
|
Seek,
|
|
|
|
PlaybackRestart,
|
|
|
|
PropertyChange { id: usize, property: Property },
|
|
|
|
ChapterChange,
|
|
|
|
ClientMessage { args: Vec<String> },
|
|
|
|
Unimplemented,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
pub enum Property {
|
|
|
|
Path(Option<String>),
|
|
|
|
Pause(bool),
|
|
|
|
PlaybackTime(Option<f64>),
|
|
|
|
Duration(Option<f64>),
|
|
|
|
Metadata(Option<HashMap<String, MpvDataType>>),
|
|
|
|
Unknown { name: String, data: MpvDataType },
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
pub enum MpvCommand {
|
|
|
|
LoadFile {
|
|
|
|
file: String,
|
|
|
|
option: PlaylistAddOptions,
|
|
|
|
},
|
|
|
|
LoadList {
|
|
|
|
file: String,
|
|
|
|
option: PlaylistAddOptions,
|
|
|
|
},
|
|
|
|
PlaylistClear,
|
|
|
|
PlaylistMove {
|
|
|
|
from: usize,
|
|
|
|
to: usize,
|
|
|
|
},
|
|
|
|
Observe {
|
|
|
|
id: isize,
|
|
|
|
property: String,
|
|
|
|
},
|
|
|
|
PlaylistNext,
|
|
|
|
PlaylistPrev,
|
|
|
|
PlaylistRemove(usize),
|
|
|
|
PlaylistShuffle,
|
|
|
|
Quit,
|
|
|
|
ScriptMessage(Vec<String>),
|
|
|
|
ScriptMessageTo {
|
|
|
|
target: String,
|
|
|
|
args: Vec<String>,
|
|
|
|
},
|
|
|
|
Seek {
|
|
|
|
seconds: f64,
|
|
|
|
option: SeekOptions,
|
|
|
|
},
|
|
|
|
Stop,
|
|
|
|
Unobserve(isize),
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
trait IntoRawCommandPart {
|
|
|
|
fn into_raw_command_part(self) -> String;
|
|
|
|
}
|
|
|
|
|
2024-04-16 22:48:27 +02:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
pub enum MpvDataType {
|
|
|
|
Array(Vec<MpvDataType>),
|
|
|
|
Bool(bool),
|
|
|
|
Double(f64),
|
|
|
|
HashMap(HashMap<String, MpvDataType>),
|
|
|
|
Null,
|
|
|
|
Playlist(Playlist),
|
|
|
|
String(String),
|
|
|
|
Usize(usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
pub enum NumberChangeOptions {
|
|
|
|
Absolute,
|
|
|
|
Increase,
|
|
|
|
Decrease,
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
impl IntoRawCommandPart for NumberChangeOptions {
|
|
|
|
fn into_raw_command_part(self) -> String {
|
|
|
|
match self {
|
|
|
|
NumberChangeOptions::Absolute => "absolute".to_string(),
|
|
|
|
NumberChangeOptions::Increase => "increase".to_string(),
|
|
|
|
NumberChangeOptions::Decrease => "decrease".to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-16 22:48:27 +02:00
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
pub enum PlaylistAddOptions {
|
|
|
|
Replace,
|
|
|
|
Append,
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
impl IntoRawCommandPart for PlaylistAddOptions {
|
|
|
|
fn into_raw_command_part(self) -> String {
|
|
|
|
match self {
|
|
|
|
PlaylistAddOptions::Replace => "replace".to_string(),
|
|
|
|
PlaylistAddOptions::Append => "append".to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-16 22:48:27 +02:00
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
pub enum PlaylistAddTypeOptions {
|
|
|
|
File,
|
|
|
|
Playlist,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
pub enum SeekOptions {
|
|
|
|
Relative,
|
|
|
|
Absolute,
|
|
|
|
RelativePercent,
|
|
|
|
AbsolutePercent,
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
impl IntoRawCommandPart for SeekOptions {
|
|
|
|
fn into_raw_command_part(self) -> String {
|
|
|
|
match self {
|
|
|
|
SeekOptions::Relative => "relative".to_string(),
|
|
|
|
SeekOptions::Absolute => "absolute".to_string(),
|
|
|
|
SeekOptions::RelativePercent => "relative-percent".to_string(),
|
|
|
|
SeekOptions::AbsolutePercent => "absolute-percent".to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-16 22:48:27 +02:00
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
|
|
|
pub enum Switch {
|
|
|
|
On,
|
|
|
|
Off,
|
|
|
|
Toggle,
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
2024-04-16 22:48:27 +02:00
|
|
|
pub enum ErrorCode {
|
|
|
|
MpvError(String),
|
|
|
|
JsonParseError(String),
|
|
|
|
ConnectError(String),
|
|
|
|
JsonContainsUnexptectedType,
|
|
|
|
UnexpectedResult,
|
|
|
|
UnexpectedValue,
|
|
|
|
MissingValue,
|
|
|
|
UnsupportedType,
|
|
|
|
ValueDoesNotContainBool,
|
|
|
|
ValueDoesNotContainF64,
|
|
|
|
ValueDoesNotContainHashMap,
|
|
|
|
ValueDoesNotContainPlaylist,
|
|
|
|
ValueDoesNotContainString,
|
|
|
|
ValueDoesNotContainUsize,
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
2024-04-16 22:48:27 +02:00
|
|
|
pub struct PlaylistEntry {
|
|
|
|
pub id: usize,
|
|
|
|
pub filename: String,
|
|
|
|
pub title: String,
|
|
|
|
pub current: bool,
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
2024-04-16 22:48:27 +02:00
|
|
|
pub struct Playlist(pub Vec<PlaylistEntry>);
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub trait GetPropertyTypeHandler: Sized {
|
|
|
|
// TODO: fix this
|
|
|
|
#[allow(async_fn_in_trait)]
|
|
|
|
async fn get_property_generic(instance: &Mpv, property: &str) -> Result<Self, Error>;
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
impl<T> GetPropertyTypeHandler for T
|
|
|
|
where
|
|
|
|
T: TypeHandler,
|
|
|
|
{
|
|
|
|
async fn get_property_generic(instance: &Mpv, property: &str) -> Result<T, Error> {
|
|
|
|
instance
|
|
|
|
.get_property_value(property)
|
|
|
|
.await
|
|
|
|
.and_then(T::get_value)
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub trait SetPropertyTypeHandler<T> {
|
|
|
|
// TODO: fix this
|
|
|
|
#[allow(async_fn_in_trait)]
|
|
|
|
async fn set_property_generic(instance: &Mpv, property: &str, value: T) -> Result<(), Error>;
|
|
|
|
}
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
impl<T> SetPropertyTypeHandler<T> for T
|
|
|
|
where
|
|
|
|
T: Serialize,
|
|
|
|
{
|
|
|
|
async fn set_property_generic(instance: &Mpv, property: &str, value: T) -> Result<(), Error> {
|
|
|
|
let (res_tx, res_rx) = oneshot::channel();
|
|
|
|
let value = serde_json::to_value(value)
|
|
|
|
.map_err(|why| Error(ErrorCode::JsonParseError(why.to_string())))?;
|
|
|
|
instance
|
|
|
|
.command_sender
|
|
|
|
.send((
|
|
|
|
MpvIpcCommand::SetProperty(property.to_owned(), value),
|
|
|
|
res_tx,
|
|
|
|
))
|
|
|
|
.await
|
|
|
|
.map_err(|_| {
|
|
|
|
Error(ErrorCode::ConnectError(
|
|
|
|
"Failed to send command".to_string(),
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
match res_rx.await {
|
|
|
|
Ok(MpvIpcResponse(response)) => response.map(|_| ()),
|
|
|
|
Err(err) => Err(Error(ErrorCode::ConnectError(err.to_string()))),
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
pub struct Error(pub ErrorCode);
|
|
|
|
|
2024-04-16 22:48:27 +02:00
|
|
|
impl Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
Display::fmt(&self.0, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
|
|
|
|
impl Display for ErrorCode {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match *self {
|
|
|
|
ErrorCode::ConnectError(ref msg) => f.write_str(&format!("ConnectError: {}", msg)),
|
|
|
|
ErrorCode::JsonParseError(ref msg) => f.write_str(&format!("JsonParseError: {}", msg)),
|
|
|
|
ErrorCode::MpvError(ref msg) => f.write_str(&format!("MpvError: {}", msg)),
|
|
|
|
ErrorCode::JsonContainsUnexptectedType => {
|
|
|
|
f.write_str("Mpv sent a value with an unexpected type")
|
|
|
|
}
|
|
|
|
ErrorCode::UnexpectedResult => f.write_str("Unexpected result received"),
|
|
|
|
ErrorCode::UnexpectedValue => f.write_str("Unexpected value received"),
|
|
|
|
ErrorCode::MissingValue => f.write_str("Missing value"),
|
|
|
|
ErrorCode::UnsupportedType => f.write_str("Unsupported type received"),
|
|
|
|
ErrorCode::ValueDoesNotContainBool => {
|
|
|
|
f.write_str("The received value is not of type \'std::bool\'")
|
|
|
|
}
|
|
|
|
ErrorCode::ValueDoesNotContainF64 => {
|
|
|
|
f.write_str("The received value is not of type \'std::f64\'")
|
|
|
|
}
|
|
|
|
ErrorCode::ValueDoesNotContainHashMap => {
|
|
|
|
f.write_str("The received value is not of type \'std::collections::HashMap\'")
|
|
|
|
}
|
|
|
|
ErrorCode::ValueDoesNotContainPlaylist => {
|
|
|
|
f.write_str("The received value is not of type \'mpvipc::Playlist\'")
|
|
|
|
}
|
|
|
|
ErrorCode::ValueDoesNotContainString => {
|
|
|
|
f.write_str("The received value is not of type \'std::string::String\'")
|
|
|
|
}
|
|
|
|
ErrorCode::ValueDoesNotContainUsize => {
|
|
|
|
f.write_str("The received value is not of type \'std::usize\'")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Mpv {
|
2024-04-19 01:36:11 +02:00
|
|
|
command_sender: mpsc::Sender<(MpvIpcCommand, oneshot::Sender<MpvIpcResponse>)>,
|
|
|
|
broadcast_channel: broadcast::Sender<MpvIpcEvent>,
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
impl fmt::Debug for Mpv {
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
fmt.debug_struct("Mpv").finish()
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
impl Mpv {
|
|
|
|
pub async fn connect(socket_path: &str) -> Result<Mpv, Error> {
|
|
|
|
log::debug!("Connecting to mpv socket at {}", socket_path);
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
let socket = match UnixStream::connect(socket_path).await {
|
|
|
|
Ok(stream) => Ok(stream),
|
|
|
|
Err(internal_error) => Err(Error(ErrorCode::ConnectError(internal_error.to_string()))),
|
|
|
|
}?;
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
Self::connect_socket(socket).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn connect_socket(socket: UnixStream) -> Result<Mpv, Error> {
|
2024-04-19 01:36:11 +02:00
|
|
|
let (com_tx, com_rx) = mpsc::channel(100);
|
|
|
|
let (ev_tx, _) = broadcast::channel(100);
|
|
|
|
let ipc = MpvIpc::new(socket, com_rx, ev_tx.clone());
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
log::debug!("Starting IPC handler");
|
|
|
|
tokio::spawn(ipc.run());
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
Ok(Mpv {
|
|
|
|
command_sender: com_tx,
|
2024-04-19 01:36:11 +02:00
|
|
|
broadcast_channel: ev_tx,
|
2024-04-17 00:07:11 +02:00
|
|
|
})
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn disconnect(&self) -> Result<(), Error> {
|
|
|
|
let (res_tx, res_rx) = oneshot::channel();
|
|
|
|
self.command_sender
|
|
|
|
.send((MpvIpcCommand::Exit, res_tx))
|
|
|
|
.await
|
|
|
|
.map_err(|_| {
|
|
|
|
Error(ErrorCode::ConnectError(
|
|
|
|
"Failed to send command".to_string(),
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
match res_rx.await {
|
|
|
|
Ok(MpvIpcResponse(response)) => response.map(|_| ()),
|
|
|
|
Err(err) => Err(Error(ErrorCode::ConnectError(err.to_string()))),
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-19 01:36:11 +02:00
|
|
|
pub async fn get_event_stream(&self) -> impl futures::Stream<Item = Result<Event, Error>> {
|
|
|
|
tokio_stream::wrappers::BroadcastStream::new(self.broadcast_channel.subscribe())
|
|
|
|
.map(|event| {
|
|
|
|
match event {
|
|
|
|
Ok(event) => Mpv::map_event(event),
|
|
|
|
Err(_) => Err(Error(ErrorCode::ConnectError("Failed to receive event".to_string()))),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn map_event(raw_event: MpvIpcEvent) -> Result<Event, Error> {
|
|
|
|
let MpvIpcEvent(event) = raw_event;
|
|
|
|
|
|
|
|
event
|
|
|
|
.as_object()
|
|
|
|
.ok_or(Error(ErrorCode::JsonContainsUnexptectedType))
|
|
|
|
.and_then(|event| {
|
|
|
|
let event_name = event
|
|
|
|
.get("event")
|
|
|
|
.ok_or(Error(ErrorCode::MissingValue))?
|
|
|
|
.as_str()
|
|
|
|
.ok_or(Error(ErrorCode::ValueDoesNotContainString))?;
|
|
|
|
|
|
|
|
match event_name {
|
|
|
|
"shutdown" => Ok(Event::Shutdown),
|
|
|
|
"start-file" => Ok(Event::StartFile),
|
|
|
|
"end-file" => Ok(Event::EndFile),
|
|
|
|
"file-loaded" => Ok(Event::FileLoaded),
|
|
|
|
"tracks-changed" => Ok(Event::TracksChanged),
|
|
|
|
"track-switched" => Ok(Event::TrackSwitched),
|
|
|
|
"idle" => Ok(Event::Idle),
|
|
|
|
"pause" => Ok(Event::Pause),
|
|
|
|
"unpause" => Ok(Event::Unpause),
|
|
|
|
"tick" => Ok(Event::Tick),
|
|
|
|
"video-reconfig" => Ok(Event::VideoReconfig),
|
|
|
|
"audio-reconfig" => Ok(Event::AudioReconfig),
|
|
|
|
"metadata-update" => Ok(Event::MetadataUpdate),
|
|
|
|
"seek" => Ok(Event::Seek),
|
|
|
|
"playback-restart" => Ok(Event::PlaybackRestart),
|
|
|
|
"property-change" => {
|
|
|
|
let id = event
|
|
|
|
.get("id")
|
|
|
|
.ok_or(Error(ErrorCode::MissingValue))?
|
|
|
|
.as_u64()
|
|
|
|
.ok_or(Error(ErrorCode::ValueDoesNotContainUsize))?
|
|
|
|
as usize;
|
|
|
|
let property_name = event
|
|
|
|
.get("name")
|
|
|
|
.ok_or(Error(ErrorCode::MissingValue))?
|
|
|
|
.as_str()
|
|
|
|
.ok_or(Error(ErrorCode::ValueDoesNotContainString))?;
|
|
|
|
|
|
|
|
match property_name {
|
|
|
|
"path" => {
|
|
|
|
let path = event
|
|
|
|
.get("data")
|
|
|
|
.ok_or(Error(ErrorCode::MissingValue))?
|
|
|
|
.as_str()
|
|
|
|
.map(|s| s.to_string());
|
|
|
|
Ok(Event::PropertyChange {
|
|
|
|
id,
|
|
|
|
property: Property::Path(path),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
"pause" => {
|
|
|
|
let pause = event
|
|
|
|
.get("data")
|
|
|
|
.ok_or(Error(ErrorCode::MissingValue))?
|
|
|
|
.as_bool()
|
|
|
|
.ok_or(Error(ErrorCode::ValueDoesNotContainBool))?;
|
|
|
|
Ok(Event::PropertyChange {
|
|
|
|
id,
|
|
|
|
property: Property::Pause(pause),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
// TODO: missing cases
|
|
|
|
_ => {
|
|
|
|
let data = event
|
|
|
|
.get("data")
|
|
|
|
.ok_or(Error(ErrorCode::MissingValue))?
|
|
|
|
.clone();
|
|
|
|
Ok(Event::PropertyChange {
|
|
|
|
id,
|
|
|
|
property: Property::Unknown {
|
|
|
|
name: property_name.to_string(),
|
|
|
|
// TODO: fix
|
|
|
|
data: MpvDataType::Double(data.as_f64().unwrap_or(0.0)),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"chapter-change" => Ok(Event::ChapterChange),
|
|
|
|
"client-message" => {
|
|
|
|
let args = event
|
|
|
|
.get("args")
|
|
|
|
.ok_or(Error(ErrorCode::MissingValue))?
|
|
|
|
.as_array()
|
|
|
|
.ok_or(Error(ErrorCode::ValueDoesNotContainString))?
|
|
|
|
.iter()
|
|
|
|
.map(|arg| {
|
|
|
|
arg.as_str()
|
|
|
|
.ok_or(Error(ErrorCode::ValueDoesNotContainString))
|
|
|
|
.map(|s| s.to_string())
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<String>, Error>>()?;
|
|
|
|
Ok(Event::ClientMessage { args })
|
|
|
|
}
|
|
|
|
_ => Ok(Event::Unimplemented),
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn get_metadata(&self) -> Result<HashMap<String, MpvDataType>, Error> {
|
|
|
|
self.get_property("metadata").await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn get_playlist(&self) -> Result<Playlist, Error> {
|
|
|
|
self.get_property::<Vec<PlaylistEntry>>("playlist")
|
|
|
|
.await
|
|
|
|
.map(|entries| Playlist(entries))
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// # Description
|
|
|
|
///
|
|
|
|
/// Retrieves the property value from mpv.
|
|
|
|
///
|
|
|
|
/// ## Supported types
|
|
|
|
/// - String
|
|
|
|
/// - bool
|
|
|
|
/// - HashMap<String, String> (e.g. for the 'metadata' property)
|
|
|
|
/// - Vec<PlaylistEntry> (for the 'playlist' property)
|
|
|
|
/// - usize
|
|
|
|
/// - f64
|
|
|
|
///
|
|
|
|
/// ## Input arguments
|
|
|
|
///
|
|
|
|
/// - **property** defines the mpv property that should be retrieved
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// ```
|
|
|
|
/// use mpvipc::{Mpv, Error};
|
2024-04-17 00:07:11 +02:00
|
|
|
/// async fn main() -> Result<(), Error> {
|
2024-04-16 22:48:27 +02:00
|
|
|
/// let mpv = Mpv::connect("/tmp/mpvsocket")?;
|
2024-04-17 00:07:11 +02:00
|
|
|
/// let paused: bool = mpv.get_property("pause").await?;
|
|
|
|
/// let title: String = mpv.get_property("media-title").await?;
|
2024-04-16 22:48:27 +02:00
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn get_property<T: GetPropertyTypeHandler>(
|
|
|
|
&self,
|
|
|
|
property: &str,
|
|
|
|
) -> Result<T, Error> {
|
|
|
|
T::get_property_generic(self, property).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// # Description
|
|
|
|
///
|
|
|
|
/// Retrieves the property value from mpv.
|
|
|
|
/// The result is always of type String, regardless of the type of the value of the mpv property
|
|
|
|
///
|
|
|
|
/// ## Input arguments
|
|
|
|
///
|
|
|
|
/// - **property** defines the mpv property that should be retrieved
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// use mpvipc::{Mpv, Error};
|
|
|
|
/// fn main() -> Result<(), Error> {
|
|
|
|
/// let mpv = Mpv::connect("/tmp/mpvsocket")?;
|
|
|
|
/// let title = mpv.get_property_string("media-title")?;
|
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn get_property_value(&self, property: &str) -> Result<Value, Error> {
|
|
|
|
let (res_tx, res_rx) = oneshot::channel();
|
|
|
|
self.command_sender
|
|
|
|
.send((MpvIpcCommand::GetProperty(property.to_owned()), res_tx))
|
|
|
|
.await
|
|
|
|
.map_err(|_| {
|
|
|
|
Error(ErrorCode::ConnectError(
|
|
|
|
"Failed to send command".to_string(),
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
match res_rx.await {
|
2024-04-19 01:36:11 +02:00
|
|
|
Ok(MpvIpcResponse(response)) => {
|
|
|
|
response.and_then(|value| value.ok_or(Error(ErrorCode::MissingValue)))
|
|
|
|
}
|
2024-04-17 00:07:11 +02:00
|
|
|
Err(err) => Err(Error(ErrorCode::ConnectError(err.to_string()))),
|
|
|
|
}
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn kill(&self) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::Quit).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// # Description
|
|
|
|
///
|
|
|
|
/// Waits until an mpv event occurs and returns the Event.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```ignore
|
|
|
|
/// let mut mpv = Mpv::connect("/tmp/mpvsocket")?;
|
|
|
|
/// loop {
|
|
|
|
/// let event = mpv.event_listen()?;
|
|
|
|
/// println!("{:?}", event);
|
|
|
|
/// }
|
|
|
|
/// ```
|
2024-04-17 00:07:11 +02:00
|
|
|
// pub fn event_listen(&mut self) -> Result<Event, Error> {
|
|
|
|
// listen(self)
|
|
|
|
// }
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
// pub fn event_listen_raw(&mut self) -> String {
|
|
|
|
// listen_raw(self)
|
|
|
|
// }
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn next(&self) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::PlaylistNext).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn observe_property(&self, id: isize, property: &str) -> Result<(), Error> {
|
2024-04-16 22:48:27 +02:00
|
|
|
self.run_command(MpvCommand::Observe {
|
2024-04-17 00:07:11 +02:00
|
|
|
id,
|
2024-04-16 22:48:27 +02:00
|
|
|
property: property.to_string(),
|
|
|
|
})
|
2024-04-17 00:07:11 +02:00
|
|
|
.await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn unobserve_property(&self, id: isize) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::Unobserve(id)).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn pause(&self) -> Result<(), Error> {
|
|
|
|
self.set_property("pause", true).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn prev(&self) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::PlaylistPrev).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn restart(&self) -> Result<(), Error> {
|
2024-04-16 22:48:27 +02:00
|
|
|
self.run_command(MpvCommand::Seek {
|
|
|
|
seconds: 0f64,
|
|
|
|
option: SeekOptions::Absolute,
|
|
|
|
})
|
2024-04-17 00:07:11 +02:00
|
|
|
.await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// # Description
|
|
|
|
///
|
|
|
|
/// Runs mpv commands. The arguments are passed as a String-Vector reference:
|
|
|
|
///
|
|
|
|
/// ## Input arguments
|
|
|
|
///
|
|
|
|
/// - **command** defines the mpv command that should be executed
|
|
|
|
/// - **args** a slice of &str's which define the arguments
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// ```
|
|
|
|
/// use mpvipc::{Mpv, Error};
|
|
|
|
/// fn main() -> Result<(), Error> {
|
|
|
|
/// let mpv = Mpv::connect("/tmp/mpvsocket")?;
|
|
|
|
///
|
|
|
|
/// //Run command 'playlist-shuffle' which takes no arguments
|
|
|
|
/// mpv.run_command(MpvCommand::PlaylistShuffle)?;
|
|
|
|
///
|
|
|
|
/// //Run command 'seek' which in this case takes two arguments
|
|
|
|
/// mpv.run_command(MpvCommand::Seek {
|
|
|
|
/// seconds: 0f64,
|
|
|
|
/// option: SeekOptions::Absolute,
|
|
|
|
/// })?;
|
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn run_command(&self, command: MpvCommand) -> Result<(), Error> {
|
|
|
|
log::trace!("Running command: {:?}", command);
|
|
|
|
let result = match command {
|
|
|
|
MpvCommand::LoadFile { file, option } => {
|
|
|
|
self.run_command_raw_ignore_value(
|
|
|
|
"loadfile",
|
|
|
|
&[file.as_ref(), option.into_raw_command_part().as_str()],
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
MpvCommand::LoadList { file, option } => {
|
|
|
|
self.run_command_raw_ignore_value(
|
|
|
|
"loadlist",
|
|
|
|
&[file.as_ref(), option.into_raw_command_part().as_str()],
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
MpvCommand::Observe { id, property } => {
|
|
|
|
let (res_tx, res_rx) = oneshot::channel();
|
|
|
|
self.command_sender
|
|
|
|
.send((MpvIpcCommand::ObserveProperty(id, property), res_tx))
|
|
|
|
.await
|
|
|
|
.map_err(|_| {
|
|
|
|
Error(ErrorCode::ConnectError(
|
|
|
|
"Failed to send command".to_string(),
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
match res_rx.await {
|
|
|
|
Ok(MpvIpcResponse(response)) => response.map(|_| ()),
|
|
|
|
Err(err) => Err(Error(ErrorCode::ConnectError(err.to_string()))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
MpvCommand::PlaylistClear => {
|
|
|
|
self.run_command_raw_ignore_value("playlist-clear", &[])
|
|
|
|
.await
|
|
|
|
}
|
2024-04-16 22:48:27 +02:00
|
|
|
MpvCommand::PlaylistMove { from, to } => {
|
2024-04-17 00:07:11 +02:00
|
|
|
self.run_command_raw_ignore_value(
|
|
|
|
"playlist-move",
|
|
|
|
&[&from.to_string(), &to.to_string()],
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
MpvCommand::PlaylistNext => {
|
|
|
|
self.run_command_raw_ignore_value("playlist-next", &[])
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
MpvCommand::PlaylistPrev => {
|
|
|
|
self.run_command_raw_ignore_value("playlist-prev", &[])
|
|
|
|
.await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
MpvCommand::PlaylistRemove(id) => {
|
2024-04-17 00:07:11 +02:00
|
|
|
self.run_command_raw_ignore_value("playlist-remove", &[&id.to_string()])
|
|
|
|
.await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
2024-04-17 00:07:11 +02:00
|
|
|
MpvCommand::PlaylistShuffle => {
|
|
|
|
self.run_command_raw_ignore_value("playlist-shuffle", &[])
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
MpvCommand::Quit => self.run_command_raw_ignore_value("quit", &[]).await,
|
2024-04-16 22:48:27 +02:00
|
|
|
MpvCommand::ScriptMessage(args) => {
|
|
|
|
let str_args: Vec<_> = args.iter().map(String::as_str).collect();
|
2024-04-17 00:07:11 +02:00
|
|
|
self.run_command_raw_ignore_value("script-message", &str_args)
|
|
|
|
.await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
MpvCommand::ScriptMessageTo { target, args } => {
|
|
|
|
let mut cmd_args: Vec<_> = vec![target.as_str()];
|
|
|
|
let mut str_args: Vec<_> = args.iter().map(String::as_str).collect();
|
|
|
|
cmd_args.append(&mut str_args);
|
2024-04-17 00:07:11 +02:00
|
|
|
self.run_command_raw_ignore_value("script-message-to", &cmd_args)
|
|
|
|
.await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
2024-04-17 00:07:11 +02:00
|
|
|
MpvCommand::Seek { seconds, option } => {
|
|
|
|
self.run_command_raw_ignore_value(
|
|
|
|
"seek",
|
|
|
|
&[
|
|
|
|
&seconds.to_string(),
|
|
|
|
option.into_raw_command_part().as_str(),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
MpvCommand::Stop => self.run_command_raw_ignore_value("stop", &[]).await,
|
|
|
|
MpvCommand::Unobserve(id) => {
|
|
|
|
let (res_tx, res_rx) = oneshot::channel();
|
|
|
|
self.command_sender
|
|
|
|
.send((MpvIpcCommand::UnobserveProperty(id), res_tx))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
match res_rx.await {
|
|
|
|
Ok(MpvIpcResponse(response)) => response.map(|_| ()),
|
|
|
|
Err(err) => Err(Error(ErrorCode::ConnectError(err.to_string()))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
log::trace!("Command result: {:?}", result);
|
|
|
|
result
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Run a custom command.
|
|
|
|
/// This should only be used if the desired command is not implemented
|
|
|
|
/// with [MpvCommand].
|
2024-04-19 01:36:11 +02:00
|
|
|
pub async fn run_command_raw(
|
|
|
|
&self,
|
|
|
|
command: &str,
|
|
|
|
args: &[&str],
|
|
|
|
) -> Result<Option<Value>, Error> {
|
2024-04-17 00:07:11 +02:00
|
|
|
let command = Vec::from(
|
|
|
|
[command]
|
|
|
|
.iter()
|
|
|
|
.chain(args.iter())
|
|
|
|
.map(|s| s.to_string())
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
.as_slice(),
|
|
|
|
);
|
|
|
|
let (res_tx, res_rx) = oneshot::channel();
|
|
|
|
self.command_sender
|
|
|
|
.send((MpvIpcCommand::Command(command), res_tx))
|
|
|
|
.await
|
|
|
|
.map_err(|_| {
|
|
|
|
Error(ErrorCode::ConnectError(
|
|
|
|
"Failed to send command".to_string(),
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
match res_rx.await {
|
|
|
|
Ok(MpvIpcResponse(response)) => response,
|
|
|
|
Err(err) => Err(Error(ErrorCode::ConnectError(err.to_string()))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn run_command_raw_ignore_value(
|
|
|
|
&self,
|
|
|
|
command: &str,
|
|
|
|
args: &[&str],
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
self.run_command_raw(command, args).await.map(|_| ())
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn playlist_add(
|
2024-04-16 22:48:27 +02:00
|
|
|
&self,
|
|
|
|
file: &str,
|
|
|
|
file_type: PlaylistAddTypeOptions,
|
|
|
|
option: PlaylistAddOptions,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
match file_type {
|
2024-04-17 00:07:11 +02:00
|
|
|
PlaylistAddTypeOptions::File => {
|
|
|
|
self.run_command(MpvCommand::LoadFile {
|
|
|
|
file: file.to_string(),
|
|
|
|
option,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2024-04-16 22:48:27 +02:00
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
PlaylistAddTypeOptions::Playlist => {
|
|
|
|
self.run_command(MpvCommand::LoadList {
|
|
|
|
file: file.to_string(),
|
|
|
|
option,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn playlist_clear(&self) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::PlaylistClear).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), Error> {
|
2024-04-16 22:48:27 +02:00
|
|
|
self.run_command(MpvCommand::PlaylistMove { from, to })
|
2024-04-17 00:07:11 +02:00
|
|
|
.await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn playlist_play_id(&self, id: usize) -> Result<(), Error> {
|
|
|
|
self.set_property("playlist-pos", id).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn playlist_play_next(&self, id: usize) -> Result<(), Error> {
|
|
|
|
match self.get_property::<usize>("playlist-pos").await {
|
|
|
|
Ok(current_id) => {
|
|
|
|
self.run_command(MpvCommand::PlaylistMove {
|
|
|
|
from: id,
|
|
|
|
to: current_id + 1,
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
}
|
2024-04-16 22:48:27 +02:00
|
|
|
Err(msg) => Err(msg),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn playlist_remove_id(&self, id: usize) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::PlaylistRemove(id)).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn playlist_shuffle(&self) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::PlaylistShuffle).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::Seek { seconds, option }).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn set_loop_file(&self, option: Switch) -> Result<(), Error> {
|
|
|
|
let enabled = match option {
|
|
|
|
Switch::On => "inf",
|
|
|
|
Switch::Off => "no",
|
|
|
|
Switch::Toggle => {
|
|
|
|
self.get_property::<String>("loop-file")
|
|
|
|
.await
|
|
|
|
.map(|s| match s.as_str() {
|
|
|
|
"inf" => "no",
|
|
|
|
"no" => "inf",
|
|
|
|
_ => "no",
|
|
|
|
})?
|
|
|
|
}
|
|
|
|
};
|
|
|
|
self.set_property("loop-file", enabled).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn set_loop_playlist(&self, option: Switch) -> Result<(), Error> {
|
|
|
|
let enabled = match option {
|
|
|
|
Switch::On => "inf",
|
|
|
|
Switch::Off => "no",
|
|
|
|
Switch::Toggle => {
|
|
|
|
self.get_property::<String>("loop-playlist")
|
|
|
|
.await
|
|
|
|
.map(|s| match s.as_str() {
|
|
|
|
"inf" => "no",
|
|
|
|
"no" => "inf",
|
|
|
|
_ => "no",
|
|
|
|
})?
|
|
|
|
}
|
|
|
|
};
|
|
|
|
self.set_property("loo-playlist", enabled).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn set_mute(&self, option: Switch) -> Result<(), Error> {
|
|
|
|
let enabled = match option {
|
|
|
|
Switch::On => "yes",
|
|
|
|
Switch::Off => "no",
|
|
|
|
Switch::Toggle => {
|
|
|
|
self.get_property::<String>("mute")
|
|
|
|
.await
|
|
|
|
.map(|s| match s.as_str() {
|
|
|
|
"yes" => "no",
|
|
|
|
"no" => "yes",
|
|
|
|
_ => "no",
|
|
|
|
})?
|
|
|
|
}
|
|
|
|
};
|
|
|
|
self.set_property("mute", enabled).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// # Description
|
|
|
|
///
|
|
|
|
/// Sets the mpv property _<property>_ to _<value>_.
|
|
|
|
///
|
|
|
|
/// ## 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 _<property>_
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
/// ```
|
|
|
|
/// use mpvipc::{Mpv, Error};
|
2024-04-17 00:07:11 +02:00
|
|
|
/// fn async main() -> Result<(), Error> {
|
2024-04-16 22:48:27 +02:00
|
|
|
/// let mpv = Mpv::connect("/tmp/mpvsocket")?;
|
2024-04-17 00:07:11 +02:00
|
|
|
/// mpv.set_property("pause", true).await?;
|
2024-04-16 22:48:27 +02:00
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn set_property<T: SetPropertyTypeHandler<T>>(
|
2024-04-16 22:48:27 +02:00
|
|
|
&self,
|
|
|
|
property: &str,
|
|
|
|
value: T,
|
|
|
|
) -> Result<(), Error> {
|
2024-04-17 00:07:11 +02:00
|
|
|
T::set_property_generic(self, property, value).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn set_speed(
|
|
|
|
&self,
|
|
|
|
input_speed: f64,
|
|
|
|
option: NumberChangeOptions,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
match self.get_property::<f64>("speed").await {
|
2024-04-16 22:48:27 +02:00
|
|
|
Ok(speed) => match option {
|
|
|
|
NumberChangeOptions::Increase => {
|
2024-04-17 00:07:11 +02:00
|
|
|
self.set_property("speed", speed + input_speed).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
NumberChangeOptions::Decrease => {
|
2024-04-17 00:07:11 +02:00
|
|
|
self.set_property("speed", speed - input_speed).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
NumberChangeOptions::Absolute => self.set_property("speed", input_speed).await,
|
2024-04-16 22:48:27 +02:00
|
|
|
},
|
|
|
|
Err(msg) => Err(msg),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn set_volume(
|
|
|
|
&self,
|
|
|
|
input_volume: f64,
|
|
|
|
option: NumberChangeOptions,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
match self.get_property::<f64>("volume").await {
|
2024-04-16 22:48:27 +02:00
|
|
|
Ok(volume) => match option {
|
|
|
|
NumberChangeOptions::Increase => {
|
2024-04-17 00:07:11 +02:00
|
|
|
self.set_property("volume", volume + input_volume).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
NumberChangeOptions::Decrease => {
|
2024-04-17 00:07:11 +02:00
|
|
|
self.set_property("volume", volume - input_volume).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
NumberChangeOptions::Absolute => self.set_property("volume", input_volume).await,
|
2024-04-16 22:48:27 +02:00
|
|
|
},
|
|
|
|
Err(msg) => Err(msg),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn stop(&self) -> Result<(), Error> {
|
|
|
|
self.run_command(MpvCommand::Stop).await
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
|
2024-04-17 00:07:11 +02:00
|
|
|
pub async fn toggle(&self) -> Result<(), Error> {
|
|
|
|
self.run_command_raw("cycle", &["pause"]).await.map(|_| ())
|
2024-04-16 22:48:27 +02:00
|
|
|
}
|
|
|
|
}
|