Compare commits
4 Commits
test-highl
...
main
Author | SHA1 | Date | |
---|---|---|---|
a6c6bf4388 | |||
5a74dd0b02 | |||
ee5aa30335 | |||
e3297bef15 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,2 @@
|
|||||||
target
|
target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
||||||
test_assets/*
|
|
||||||
!test_assets/.gitkeep
|
|
@ -7,8 +7,8 @@ authors = [
|
|||||||
]
|
]
|
||||||
description = "A small library which provides bindings to control existing mpv instances through sockets."
|
description = "A small library which provides bindings to control existing mpv instances through sockets."
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
repository = "https://git.pvv.ntnu.no/Projects/mpvipc-async"
|
repository = "https://git.pvv.ntnu.no/Grzegorz/mpvipc-async"
|
||||||
documentation = "https://pages.pvv.ntnu.no/Projects/mpvipc-async/main/docs/mpvipc_async/"
|
documentation = "https://pages.pvv.ntnu.no/Grzegorz/mpvipc-async/main/docs/mpvipc_async/"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.75"
|
rust-version = "1.75"
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[![Coverage](https://pages.pvv.ntnu.no/Projects/mpvipc-async/main/coverage/badges/for_the_badge.svg)](https://pages.pvv.ntnu.no/Projects/mpvipc-async/main/coverage/src/)
|
[![Coverage](https://pages.pvv.ntnu.no/Grzegorz/mpvipc-async/main/coverage/badges/for_the_badge.svg)](https://pages.pvv.ntnu.no/Grzegorz/mpvipc-async/main/coverage/src/)
|
||||||
[![Docs](https://img.shields.io/badge/docs-blue?style=for-the-badge&logo=rust)](https://pages.pvv.ntnu.no/Projects/mpvipc-async/main/docs/mpvipc_async/)
|
[![Docs](https://img.shields.io/badge/docs-blue?style=for-the-badge&logo=rust)](https://pages.pvv.ntnu.no/Grzegorz/mpvipc-async/main/docs/mpvipc_async/)
|
||||||
|
|
||||||
# mpvipc-async
|
# mpvipc-async
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
pkgs.mpv
|
pkgs.mpv
|
||||||
pkgs.grcov
|
pkgs.grcov
|
||||||
pkgs.cargo-nextest
|
pkgs.cargo-nextest
|
||||||
pkgs.ffmpeg
|
|
||||||
];
|
];
|
||||||
RUST_SRC_PATH = "${toolchain.rust-src}/lib/rustlib/src/rust/library";
|
RUST_SRC_PATH = "${toolchain.rust-src}/lib/rustlib/src/rust/library";
|
||||||
});
|
});
|
||||||
|
@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
REQUIRED_COMMANDS=(
|
|
||||||
"git"
|
|
||||||
"ffmpeg"
|
|
||||||
)
|
|
||||||
|
|
||||||
for cmd in "${REQUIRED_COMMANDS[@]}"; do
|
|
||||||
if ! command -v "$cmd" &> /dev/null; then
|
|
||||||
echo "Command '$cmd' not found. Please install it and try again."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
ROOT_DIR=$(git rev-parse --show-toplevel)
|
|
||||||
|
|
||||||
# Generate 30 seconds of 480p video with black background
|
|
||||||
|
|
||||||
ffmpeg -f lavfi -i color=c=black:s=640x480:d=30 -c:v libx264 -t 30 -pix_fmt yuv420p "$ROOT_DIR/test_assets/black-background-30s-480p.mp4"
|
|
@ -95,6 +95,7 @@ pub(crate) trait IntoRawCommandPart {
|
|||||||
|
|
||||||
/// Generic data type representing all possible data types that mpv can return.
|
/// Generic data type representing all possible data types that mpv can return.
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
pub enum MpvDataType {
|
pub enum MpvDataType {
|
||||||
Array(Vec<MpvDataType>),
|
Array(Vec<MpvDataType>),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
@ -109,7 +109,7 @@ pub enum Event {
|
|||||||
VideoReconfig,
|
VideoReconfig,
|
||||||
AudioReconfig,
|
AudioReconfig,
|
||||||
PropertyChange {
|
PropertyChange {
|
||||||
id: u64,
|
id: Option<u64>,
|
||||||
name: String,
|
name: String,
|
||||||
data: Option<MpvDataType>,
|
data: Option<MpvDataType>,
|
||||||
},
|
},
|
||||||
@ -296,7 +296,7 @@ fn parse_client_message(event: &Map<String, Value>) -> Result<Event, MpvError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_property_change(event: &Map<String, Value>) -> Result<Event, MpvError> {
|
fn parse_property_change(event: &Map<String, Value>) -> Result<Event, MpvError> {
|
||||||
let id = get_key_as!(as_u64, "id", event);
|
let id = get_optional_key_as!(as_u64, "id", event);
|
||||||
let property_name = get_key_as!(as_str, "name", event);
|
let property_name = get_key_as!(as_str, "name", event);
|
||||||
let data = event.get("data").map(json_to_value).transpose()?;
|
let data = event.get("data").map(json_to_value).transpose()?;
|
||||||
|
|
||||||
|
@ -323,9 +323,9 @@ impl MpvExt for Mpv {
|
|||||||
Switch::Off => "yes",
|
Switch::Off => "yes",
|
||||||
Switch::Toggle => {
|
Switch::Toggle => {
|
||||||
if self.is_playing().await? {
|
if self.is_playing().await? {
|
||||||
"no"
|
|
||||||
} else {
|
|
||||||
"yes"
|
"yes"
|
||||||
|
} else {
|
||||||
|
"no"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
BIN
test_assets/black-background-30s-480p.mp4
Normal file
BIN
test_assets/black-background-30s-480p.mp4
Normal file
Binary file not shown.
@ -43,7 +43,7 @@ where
|
|||||||
match event {
|
match event {
|
||||||
Some(Ok(event)) => {
|
Some(Ok(event)) => {
|
||||||
match event {
|
match event {
|
||||||
Event::PropertyChange { id: MPV_CHANNEL_ID, name, data } => {
|
Event::PropertyChange { id: Some(MPV_CHANNEL_ID), name, data } => {
|
||||||
let property = parse_property(&name, data).unwrap();
|
let property = parse_property(&name, data).unwrap();
|
||||||
if !on_property(property.clone()) {
|
if !on_property(property.clone()) {
|
||||||
return Err(PropertyCheckingThreadError::UnexpectedPropertyError(property))
|
return Err(PropertyCheckingThreadError::UnexpectedPropertyError(property))
|
||||||
@ -115,7 +115,7 @@ async fn graceful_shutdown(
|
|||||||
#[test(tokio::test)]
|
#[test(tokio::test)]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_highlevel_event_pause() -> Result<(), MpvError> {
|
async fn test_highlevel_event_pause() -> Result<(), MpvError> {
|
||||||
let (proc, mpv) = spawn_mpv(true).await?;
|
let (proc, mpv) = spawn_headless_mpv().await?;
|
||||||
|
|
||||||
mpv.observe_property(MPV_CHANNEL_ID, "pause").await?;
|
mpv.observe_property(MPV_CHANNEL_ID, "pause").await?;
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ async fn test_highlevel_event_pause() -> Result<(), MpvError> {
|
|||||||
#[test(tokio::test)]
|
#[test(tokio::test)]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_highlevel_event_volume() -> Result<(), MpvError> {
|
async fn test_highlevel_event_volume() -> Result<(), MpvError> {
|
||||||
let (proc, mpv) = spawn_mpv(true).await?;
|
let (proc, mpv) = spawn_headless_mpv().await?;
|
||||||
|
|
||||||
mpv.observe_property(1337, "volume").await?;
|
mpv.observe_property(1337, "volume").await?;
|
||||||
let events = mpv.get_event_stream().await;
|
let events = mpv.get_event_stream().await;
|
||||||
@ -174,7 +174,7 @@ async fn test_highlevel_event_volume() -> Result<(), MpvError> {
|
|||||||
#[test(tokio::test)]
|
#[test(tokio::test)]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_highlevel_event_mute() -> Result<(), MpvError> {
|
async fn test_highlevel_event_mute() -> Result<(), MpvError> {
|
||||||
let (proc, mpv) = spawn_mpv(true).await?;
|
let (proc, mpv) = spawn_headless_mpv().await?;
|
||||||
|
|
||||||
mpv.observe_property(1337, "mute").await?;
|
mpv.observe_property(1337, "mute").await?;
|
||||||
let events = mpv.get_event_stream().await;
|
let events = mpv.get_event_stream().await;
|
||||||
@ -202,7 +202,7 @@ async fn test_highlevel_event_mute() -> Result<(), MpvError> {
|
|||||||
#[test(tokio::test)]
|
#[test(tokio::test)]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_highlevel_event_duration() -> Result<(), MpvError> {
|
async fn test_highlevel_event_duration() -> Result<(), MpvError> {
|
||||||
let (proc, mpv) = spawn_mpv(true).await?;
|
let (proc, mpv) = spawn_headless_mpv().await?;
|
||||||
|
|
||||||
mpv.observe_property(1337, "duration").await?;
|
mpv.observe_property(1337, "duration").await?;
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
use super::util::{get_test_asset, spawn_mpv};
|
|
||||||
|
|
||||||
use mpvipc_async::{
|
|
||||||
MpvError, MpvExt, PlaylistAddOptions, PlaylistAddTypeOptions, SeekOptions, Switch,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[cfg(target_family = "unix")]
|
|
||||||
async fn test_seek() -> Result<(), MpvError> {
|
|
||||||
let (mut proc, mpv) = spawn_mpv(false).await.unwrap();
|
|
||||||
mpv.playlist_add(
|
|
||||||
&get_test_asset("black-background-30s-480p.mp4"),
|
|
||||||
PlaylistAddTypeOptions::File,
|
|
||||||
PlaylistAddOptions::Append,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
mpv.set_playback(Switch::On).await?;
|
|
||||||
mpv.set_playback(Switch::Off).await?;
|
|
||||||
|
|
||||||
// TODO: wait for property "seekable" to be true
|
|
||||||
|
|
||||||
mpv.seek(10.0, SeekOptions::Relative).await?;
|
|
||||||
let time_pos: f64 = mpv.get_property("time-pos").await?.unwrap();
|
|
||||||
assert_eq!(time_pos, 10.0);
|
|
||||||
|
|
||||||
mpv.seek(5.0, SeekOptions::Relative).await?;
|
|
||||||
let time_pos: f64 = mpv.get_property("time-pos").await?.unwrap();
|
|
||||||
assert_eq!(time_pos, 15.0);
|
|
||||||
|
|
||||||
mpv.kill().await.unwrap();
|
|
||||||
proc.kill().await.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -5,7 +5,7 @@ use super::*;
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_get_mpv_version() -> Result<(), MpvError> {
|
async fn test_get_mpv_version() -> Result<(), MpvError> {
|
||||||
let (mut proc, mpv) = spawn_mpv(true).await.unwrap();
|
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||||
let version: String = mpv.get_property("mpv-version").await?.unwrap();
|
let version: String = mpv.get_property("mpv-version").await?.unwrap();
|
||||||
assert!(version.starts_with("mpv"));
|
assert!(version.starts_with("mpv"));
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ async fn test_get_mpv_version() -> Result<(), MpvError> {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_set_property() -> Result<(), MpvError> {
|
async fn test_set_property() -> Result<(), MpvError> {
|
||||||
let (mut proc, mpv) = spawn_mpv(true).await.unwrap();
|
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||||
mpv.set_property("pause", true).await.unwrap();
|
mpv.set_property("pause", true).await.unwrap();
|
||||||
let paused: bool = mpv.get_property("pause").await?.unwrap();
|
let paused: bool = mpv.get_property("pause").await?.unwrap();
|
||||||
assert!(paused);
|
assert!(paused);
|
||||||
@ -32,7 +32,7 @@ async fn test_set_property() -> Result<(), MpvError> {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_get_unavailable_property() -> Result<(), MpvError> {
|
async fn test_get_unavailable_property() -> Result<(), MpvError> {
|
||||||
let (mut proc, mpv) = spawn_mpv(true).await.unwrap();
|
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||||
let time_pos = mpv.get_property::<f64>("time-pos").await;
|
let time_pos = mpv.get_property::<f64>("time-pos").await;
|
||||||
assert_eq!(time_pos, Ok(None));
|
assert_eq!(time_pos, Ok(None));
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ async fn test_get_unavailable_property() -> Result<(), MpvError> {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
async fn test_get_nonexistent_property() -> Result<(), MpvError> {
|
async fn test_get_nonexistent_property() -> Result<(), MpvError> {
|
||||||
let (mut proc, mpv) = spawn_mpv(true).await.unwrap();
|
let (mut proc, mpv) = spawn_headless_mpv().await.unwrap();
|
||||||
let nonexistent = mpv.get_property::<f64>("nonexistent").await;
|
let nonexistent = mpv.get_property::<f64>("nonexistent").await;
|
||||||
|
|
||||||
match nonexistent {
|
match nonexistent {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
mod event_property_parser;
|
mod event_property_parser;
|
||||||
mod highlevel_api;
|
|
||||||
mod misc;
|
mod misc;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
@ -6,35 +6,8 @@ use tokio::{
|
|||||||
time::{sleep, timeout},
|
time::{sleep, timeout},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn assert_test_assets_exist() {
|
|
||||||
let test_data_dir = Path::new("test_assets");
|
|
||||||
if !test_data_dir.exists()
|
|
||||||
|| !test_data_dir.is_dir()
|
|
||||||
// `.gitkeep` should always be present, so there should be at least 2 entries
|
|
||||||
|| test_data_dir.read_dir().unwrap().count() <= 1
|
|
||||||
{
|
|
||||||
panic!(
|
|
||||||
"Test assets directory not found at {:?}, please run `./setup_test_assets.sh`",
|
|
||||||
test_data_dir
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_test_assets_dir() -> &'static Path {
|
|
||||||
Path::new("test_assets")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_test_asset(file_name: &str) -> String {
|
|
||||||
assert_test_assets_exist();
|
|
||||||
|
|
||||||
let test_assets_dir = get_test_assets_dir();
|
|
||||||
let file_path = test_assets_dir.join(file_name);
|
|
||||||
file_path.to_str().unwrap().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
pub async fn spawn_mpv(headless: bool) -> Result<(Child, Mpv), MpvError> {
|
pub async fn spawn_headless_mpv() -> Result<(Child, Mpv), MpvError> {
|
||||||
let socket_path_str = format!("/tmp/mpv-ipc-{}", uuid::Uuid::new_v4());
|
let socket_path_str = format!("/tmp/mpv-ipc-{}", uuid::Uuid::new_v4());
|
||||||
let socket_path = Path::new(&socket_path_str);
|
let socket_path = Path::new(&socket_path_str);
|
||||||
|
|
||||||
@ -42,11 +15,8 @@ pub async fn spawn_mpv(headless: bool) -> Result<(Child, Mpv), MpvError> {
|
|||||||
let process_handle = Command::new("mpv")
|
let process_handle = Command::new("mpv")
|
||||||
.arg("--no-config")
|
.arg("--no-config")
|
||||||
.arg("--idle")
|
.arg("--idle")
|
||||||
.args(if headless {
|
.arg("--no-video")
|
||||||
vec!["--no-video", "--no-audio"]
|
.arg("--no-audio")
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
})
|
|
||||||
.arg(format!(
|
.arg(format!(
|
||||||
"--input-ipc-server={}",
|
"--input-ipc-server={}",
|
||||||
&socket_path.to_str().unwrap()
|
&socket_path.to_str().unwrap()
|
||||||
|
@ -52,7 +52,7 @@ async fn test_observe_event_successful() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
event,
|
event,
|
||||||
Event::PropertyChange {
|
Event::PropertyChange {
|
||||||
id: 1,
|
id: Some(1),
|
||||||
name: "volume".to_string(),
|
name: "volume".to_string(),
|
||||||
data: Some(MpvDataType::Double(64.0))
|
data: Some(MpvDataType::Double(64.0))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user