1
//! JSON parsing logic for events from [`MpvIpc`](crate::ipc::MpvIpc).
2

            
3
use std::collections::HashMap;
4

            
5
use serde::{Deserialize, Serialize};
6
use serde_json::{Map, Value};
7

            
8
use crate::{ipc::MpvIpcEvent, Error, ErrorCode, MpvDataType};
9

            
10
/// All possible properties that can be observed through the event system.
11
///
12
/// Not all properties are guaranteed to be implemented.
13
/// If something is missing, please open an issue.
14
///
15
/// Otherwise, the property will be returned as a `Property::Unknown` variant.
16
///
17
/// See <https://mpv.io/manual/master/#properties> for
18
/// the upstream list of properties.
19
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20
pub enum Property {
21
    Path(Option<String>),
22
    Pause(bool),
23
    PlaybackTime(Option<f64>),
24
    Duration(Option<f64>),
25
    Metadata(Option<HashMap<String, MpvDataType>>),
26
    Unknown { name: String, data: MpvDataType },
27
}
28

            
29
/// All possible events that can be sent by mpv.
30
///
31
/// Not all event types are guaranteed to be implemented.
32
/// If something is missing, please open an issue.
33
///
34
/// Otherwise, the event will be returned as an `Event::Unimplemented` variant.
35
///
36
/// See <https://mpv.io/manual/master/#list-of-events> for
37
/// the upstream list of events.
38
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39
pub enum Event {
40
    Shutdown,
41
    StartFile,
42
    EndFile,
43
    FileLoaded,
44
    TracksChanged,
45
    TrackSwitched,
46
    Idle,
47
    Pause,
48
    Unpause,
49
    Tick,
50
    VideoReconfig,
51
    AudioReconfig,
52
    MetadataUpdate,
53
    Seek,
54
    PlaybackRestart,
55
    PropertyChange { id: usize, property: Property },
56
    ChapterChange,
57
    ClientMessage { args: Vec<String> },
58
    Unimplemented,
59
}
60

            
61
/// Parse a highlevel [`Event`] objects from json.
62
8
pub(crate) fn parse_event(raw_event: MpvIpcEvent) -> Result<Event, Error> {
63
8
    let MpvIpcEvent(event) = raw_event;
64
8

            
65
8
    event
66
8
        .as_object()
67
8
        .ok_or(Error(ErrorCode::JsonContainsUnexptectedType))
68
8
        .and_then(|event| {
69
8
            let event_name = event
70
8
                .get("event")
71
8
                .ok_or(Error(ErrorCode::MissingValue))?
72
8
                .as_str()
73
8
                .ok_or(Error(ErrorCode::ValueDoesNotContainString))?;
74

            
75
8
            match event_name {
76
8
                "shutdown" => Ok(Event::Shutdown),
77
8
                "start-file" => Ok(Event::StartFile),
78
8
                "end-file" => Ok(Event::EndFile),
79
8
                "file-loaded" => Ok(Event::FileLoaded),
80
8
                "tracks-changed" => Ok(Event::TracksChanged),
81
8
                "track-switched" => Ok(Event::TrackSwitched),
82
8
                "idle" => Ok(Event::Idle),
83
8
                "pause" => Ok(Event::Pause),
84
8
                "unpause" => Ok(Event::Unpause),
85
8
                "tick" => Ok(Event::Tick),
86
8
                "video-reconfig" => Ok(Event::VideoReconfig),
87
8
                "audio-reconfig" => Ok(Event::AudioReconfig),
88
8
                "metadata-update" => Ok(Event::MetadataUpdate),
89
8
                "seek" => Ok(Event::Seek),
90
8
                "playback-restart" => Ok(Event::PlaybackRestart),
91
8
                "property-change" => parse_event_property(event)
92
8
                    .map(|(id, property)| Event::PropertyChange { id, property }),
93
                "chapter-change" => Ok(Event::ChapterChange),
94
                "client-message" => {
95
                    let args = event
96
                        .get("args")
97
                        .ok_or(Error(ErrorCode::MissingValue))?
98
                        .as_array()
99
                        .ok_or(Error(ErrorCode::ValueDoesNotContainString))?
100
                        .iter()
101
                        .map(|arg| {
102
                            arg.as_str()
103
                                .ok_or(Error(ErrorCode::ValueDoesNotContainString))
104
                                .map(|s| s.to_string())
105
                        })
106
                        .collect::<Result<Vec<String>, Error>>()?;
107
                    Ok(Event::ClientMessage { args })
108
                }
109
                _ => Ok(Event::Unimplemented),
110
            }
111
8
        })
112
8
}
113

            
114
/// Parse a highlevel [`Property`] object from json, used for [`Event::PropertyChange`].
115
8
fn parse_event_property(event: &Map<String, Value>) -> Result<(usize, Property), Error> {
116
8
    let id = event
117
8
        .get("id")
118
8
        .ok_or(Error(ErrorCode::MissingValue))?
119
8
        .as_u64()
120
8
        .ok_or(Error(ErrorCode::ValueDoesNotContainUsize))? as usize;
121
8
    let property_name = event
122
8
        .get("name")
123
8
        .ok_or(Error(ErrorCode::MissingValue))?
124
8
        .as_str()
125
8
        .ok_or(Error(ErrorCode::ValueDoesNotContainString))?;
126

            
127
8
    match property_name {
128
8
        "path" => {
129
            let path = event
130
                .get("data")
131
                .ok_or(Error(ErrorCode::MissingValue))?
132
                .as_str()
133
                .map(|s| s.to_string());
134
            Ok((id, Property::Path(path)))
135
        }
136
8
        "pause" => {
137
4
            let pause = event
138
4
                .get("data")
139
4
                .ok_or(Error(ErrorCode::MissingValue))?
140
4
                .as_bool()
141
4
                .ok_or(Error(ErrorCode::ValueDoesNotContainBool))?;
142
4
            Ok((id, Property::Pause(pause)))
143
        }
144
        // TODO: missing cases
145
        _ => {
146
4
            let data = event
147
4
                .get("data")
148
4
                .ok_or(Error(ErrorCode::MissingValue))?
149
4
                .clone();
150
4
            Ok((
151
4
                id,
152
4
                Property::Unknown {
153
4
                    name: property_name.to_string(),
154
4
                    // TODO: fix
155
4
                    data: MpvDataType::Double(data.as_f64().unwrap_or(0.0)),
156
4
                },
157
4
            ))
158
        }
159
    }
160
8
}