2017-05-24 00:13:23 +02:00
|
|
|
use serde_json::{self, Value};
|
|
|
|
use std::collections::HashMap;
|
2017-05-22 18:31:20 +02:00
|
|
|
use std::error::Error;
|
|
|
|
use std::io::BufReader;
|
2017-05-24 00:13:23 +02:00
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::iter::Iterator;
|
|
|
|
use std::sync::mpsc::Sender;
|
2017-05-29 17:54:12 +02:00
|
|
|
use super::Mpv;
|
2017-05-22 18:31:20 +02:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PlaylistEntry {
|
|
|
|
pub id: usize,
|
|
|
|
pub filename: String,
|
|
|
|
pub title: String,
|
|
|
|
pub current: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait TypeHandler: Sized {
|
|
|
|
fn get_value(value: Value) -> Result<Self, String>;
|
|
|
|
fn as_string(&self) -> String;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TypeHandler for String {
|
|
|
|
fn get_value(value: Value) -> Result<String, String> {
|
|
|
|
if let Value::Object(map) = value {
|
|
|
|
if let Value::String(ref error) = map["error"] {
|
|
|
|
if error == "success" && map.contains_key("data") {
|
|
|
|
if let Value::String(ref s) = map["data"] {
|
|
|
|
Ok(s.to_string())
|
|
|
|
} else {
|
|
|
|
Err("Value did not contain a String".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_string(&self) -> String {
|
|
|
|
self.to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TypeHandler for bool {
|
|
|
|
fn get_value(value: Value) -> Result<bool, String> {
|
|
|
|
if let Value::Object(map) = value {
|
|
|
|
if let Value::String(ref error) = map["error"] {
|
|
|
|
if error == "success" && map.contains_key("data") {
|
|
|
|
if let Value::Bool(ref b) = map["data"] {
|
|
|
|
Ok(*b)
|
|
|
|
} else {
|
|
|
|
Err("Value did not contain a bool".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn as_string(&self) -> String {
|
|
|
|
if *self {
|
|
|
|
"true".to_string()
|
|
|
|
} else {
|
|
|
|
"false".to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TypeHandler for f64 {
|
|
|
|
fn get_value(value: Value) -> Result<f64, String> {
|
|
|
|
if let Value::Object(map) = value {
|
|
|
|
if let Value::String(ref error) = map["error"] {
|
|
|
|
if error == "success" && map.contains_key("data") {
|
|
|
|
if let Value::Number(ref num) = map["data"] {
|
|
|
|
Ok(num.as_f64().unwrap())
|
|
|
|
} else {
|
|
|
|
Err("Value did not contain a f64".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_string(&self) -> String {
|
|
|
|
self.to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TypeHandler for usize {
|
|
|
|
fn get_value(value: Value) -> Result<usize, String> {
|
|
|
|
if let Value::Object(map) = value {
|
|
|
|
if let Value::String(ref error) = map["error"] {
|
|
|
|
if error == "success" && map.contains_key("data") {
|
|
|
|
if let Value::Number(ref num) = map["data"] {
|
|
|
|
Ok(num.as_u64().unwrap() as usize)
|
|
|
|
} else {
|
|
|
|
Err("Value did not contain an usize".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_string(&self) -> String {
|
|
|
|
self.to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TypeHandler for HashMap<String, String> {
|
|
|
|
fn get_value(value: Value) -> Result<HashMap<String, String>, String> {
|
|
|
|
if let Value::Object(map) = value {
|
|
|
|
if let Value::String(ref error) = map["error"] {
|
|
|
|
if error == "success" && map.contains_key("data") {
|
|
|
|
if let Value::Object(ref inner_map) = map["data"] {
|
|
|
|
let mut output_map: HashMap<String, String> = HashMap::new();
|
|
|
|
for (ref key, ref value) in inner_map.iter() {
|
|
|
|
if let Value::String(ref val) = **value {
|
|
|
|
output_map.insert(key.to_string(), val.to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let output_map = output_map;
|
|
|
|
Ok(output_map)
|
|
|
|
} else {
|
|
|
|
Err("Value did not contain a HashMap".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_string(&self) -> String {
|
|
|
|
format!("{:?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TypeHandler for Vec<PlaylistEntry> {
|
|
|
|
fn get_value(value: Value) -> Result<Vec<PlaylistEntry>, String> {
|
|
|
|
if let Value::Object(map) = value {
|
|
|
|
if let Value::String(ref error) = map["error"] {
|
|
|
|
if error == "success" && map.contains_key("data") {
|
|
|
|
if let Value::Array(ref playlist_vec) = map["data"] {
|
|
|
|
let mut output: Vec<PlaylistEntry> = Vec::new();
|
|
|
|
for (id, entry) in playlist_vec.iter().enumerate() {
|
|
|
|
let mut filename: String = String::new();
|
|
|
|
let mut title: String = String::new();
|
|
|
|
let mut current: bool = false;
|
|
|
|
if let Value::String(ref f) = entry["filename"] {
|
|
|
|
filename = f.to_string();
|
|
|
|
}
|
|
|
|
if let Value::String(ref t) = entry["title"] {
|
|
|
|
title = t.to_string();
|
|
|
|
}
|
|
|
|
if let Value::Bool(ref b) = entry["current"] {
|
|
|
|
current = *b;
|
|
|
|
}
|
|
|
|
output.push(PlaylistEntry {
|
|
|
|
id: id,
|
|
|
|
filename: filename,
|
|
|
|
title: title,
|
|
|
|
current: current,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let output = output;
|
|
|
|
Ok(output)
|
|
|
|
} else {
|
|
|
|
Err("Value did not contain a playlist".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_string(&self) -> String {
|
|
|
|
format!("{:?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 17:54:12 +02:00
|
|
|
pub fn get_mpv_property<T: TypeHandler>(instance: &Mpv, property: &str) -> Result<T, String> {
|
2017-05-22 18:31:20 +02:00
|
|
|
let ipc_string = format!("{{ \"command\": [\"get_property\",\"{}\"] }}\n", property);
|
|
|
|
|
2017-05-29 17:54:12 +02:00
|
|
|
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
|
2017-05-22 18:31:20 +02:00
|
|
|
Ok(val) => T::get_value(val),
|
|
|
|
Err(why) => Err(format!("Error while getting property: {}", why)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 17:54:12 +02:00
|
|
|
pub fn get_mpv_property_string(instance: &Mpv, property: &str) -> Result<String, String> {
|
2017-05-22 18:31:20 +02:00
|
|
|
let ipc_string = format!("{{ \"command\": [\"get_property\",\"{}\"] }}\n", property);
|
2017-05-29 17:54:12 +02:00
|
|
|
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
|
2017-05-22 18:31:20 +02:00
|
|
|
Ok(val) => {
|
|
|
|
if let Value::Object(map) = val {
|
|
|
|
if let Value::String(ref error) = map["error"] {
|
|
|
|
if error == "success" && map.contains_key("data") {
|
|
|
|
match map["data"] {
|
|
|
|
Value::Bool(b) => Ok(b.to_string()),
|
|
|
|
Value::Number(ref n) => Ok(n.to_string()),
|
|
|
|
Value::String(ref s) => Ok(s.to_string()),
|
|
|
|
Value::Array(ref array) => Ok(format!("{:?}", array)),
|
|
|
|
Value::Object(ref map) => Ok(format!("{:?}", map)),
|
|
|
|
_ => Err("Value contains an unsupported type".to_string()),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err("Unexpected value received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(why) => Err(format!("Error while getting property: {}", why)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 17:54:12 +02:00
|
|
|
pub fn set_mpv_property<T: TypeHandler>(instance: &Mpv,
|
2017-05-22 18:31:20 +02:00
|
|
|
property: &str,
|
|
|
|
value: T)
|
|
|
|
-> Result<(), String> {
|
|
|
|
let ipc_string = format!("{{ \"command\": [\"set_property\", \"{}\", {}] }}\n",
|
|
|
|
property,
|
|
|
|
value.as_string());
|
2017-05-29 17:54:12 +02:00
|
|
|
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
|
2017-05-22 18:31:20 +02:00
|
|
|
Ok(_) => Ok(()),
|
|
|
|
Err(why) => Err(why.description().to_string()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 17:54:12 +02:00
|
|
|
pub fn run_mpv_command(instance: &Mpv, command: &str, args: &Vec<&str>) -> Result<(), String> {
|
2017-05-22 18:31:20 +02:00
|
|
|
let mut ipc_string = format!("{{ \"command\": [\"{}\"", command);
|
|
|
|
if args.len() > 0 {
|
|
|
|
for arg in args.iter() {
|
|
|
|
ipc_string.push_str(&format!(", \"{}\"", arg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ipc_string.push_str("] }\n");
|
|
|
|
ipc_string = ipc_string;
|
2017-05-29 17:54:12 +02:00
|
|
|
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
|
2017-05-22 18:31:20 +02:00
|
|
|
Ok(feedback) => {
|
|
|
|
if let Value::String(ref error) = feedback["error"] {
|
|
|
|
if error == "success" {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
|
|
|
}
|
|
|
|
} else {
|
2017-05-29 17:54:12 +02:00
|
|
|
//Ok(())
|
2017-05-22 18:31:20 +02:00
|
|
|
Err("Error: Unexpected result received".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(why) => Err(why.description().to_string()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-29 17:54:12 +02:00
|
|
|
pub fn observe_mpv_property(instance: &Mpv, id: &usize, property: &str) -> Result<(), String> {
|
2017-05-24 00:13:23 +02:00
|
|
|
let ipc_string = format!("{{ \"command\": [\"observe_property\", {}, \"{}\"] }}\n",
|
|
|
|
id,
|
|
|
|
property);
|
2017-05-29 17:54:12 +02:00
|
|
|
match serde_json::from_str::<Value>(&send_command_sync(instance, &ipc_string)) {
|
2017-05-24 00:13:23 +02:00
|
|
|
Ok(feedback) => {
|
|
|
|
if let Value::String(ref error) = feedback["error"] {
|
|
|
|
if error == "success" {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(error.to_string())
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
2017-05-24 00:13:23 +02:00
|
|
|
} else {
|
|
|
|
Err("Unexpected result received".to_string())
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
|
|
|
}
|
2017-05-24 00:13:23 +02:00
|
|
|
Err(why) => Err(why.description().to_string()),
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// #Description
|
|
|
|
///
|
2017-05-24 00:13:23 +02:00
|
|
|
/// Listens on socket <socket> for events and prints them in real-time to stdout.
|
|
|
|
/// This function contains an infinite-loop which keeps the application open indefinitely.
|
2017-05-22 18:31:20 +02:00
|
|
|
///
|
|
|
|
/// #Example
|
|
|
|
/// ```
|
2017-05-24 00:13:23 +02:00
|
|
|
/// listen("/tmp/mpvsocket");
|
2017-05-22 18:31:20 +02:00
|
|
|
/// ```
|
2017-05-29 17:54:12 +02:00
|
|
|
pub fn listen(instance: &Mpv, tx: &Sender<String>) {
|
|
|
|
let mut response = String::new();
|
|
|
|
let mut reader = BufReader::new(instance);
|
|
|
|
reader.read_line(&mut response).unwrap();
|
|
|
|
match serde_json::from_str::<Value>(&response) {
|
|
|
|
Ok(e) => {
|
|
|
|
if let Value::String(ref name) = e["event"] {
|
|
|
|
tx.send(name.to_string()).unwrap();
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
|
|
|
}
|
2017-05-29 17:54:12 +02:00
|
|
|
Err(why) => panic!("{}", why.description().to_string()),
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
2017-05-29 17:54:12 +02:00
|
|
|
response.clear();
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
|
|
|
|
2017-05-29 17:54:12 +02:00
|
|
|
fn send_command_sync(instance: &Mpv, command: &str) -> String {
|
|
|
|
let mut stream = instance;
|
|
|
|
match stream.write_all(command.as_bytes()) {
|
|
|
|
Err(why) => panic!("Error: Could not write to socket: {}", why.description()),
|
|
|
|
Ok(_) => {
|
|
|
|
let mut response = String::new();
|
|
|
|
{
|
|
|
|
let mut reader = BufReader::new(stream);
|
|
|
|
while !response.contains("\"error\":") {
|
|
|
|
response.clear();
|
|
|
|
reader.read_line(&mut response).unwrap();
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
|
|
|
}
|
2017-05-29 17:54:12 +02:00
|
|
|
response
|
2017-05-22 18:31:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|