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

            
3
use std::str::FromStr;
4

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

            
8
use crate::{ipc::MpvIpcEvent, message_parser::json_to_value, MpvDataType, MpvError};
9

            
10
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11
#[serde(rename_all = "kebab-case")]
12
pub enum EventEndFileReason {
13
    Eof,
14
    Stop,
15
    Quit,
16
    Error,
17
    Redirect,
18
    Unknown,
19
    Unimplemented(String),
20
}
21

            
22
impl FromStr for EventEndFileReason {
23
    type Err = ();
24

            
25
    fn from_str(s: &str) -> Result<Self, Self::Err> {
26
        match s {
27
            "eof" => Ok(EventEndFileReason::Eof),
28
            "stop" => Ok(EventEndFileReason::Stop),
29
            "quit" => Ok(EventEndFileReason::Quit),
30
            "error" => Ok(EventEndFileReason::Error),
31
            "redirect" => Ok(EventEndFileReason::Redirect),
32
            reason => Ok(EventEndFileReason::Unimplemented(reason.to_string())),
33
        }
34
    }
35
}
36

            
37
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38
#[serde(rename_all = "kebab-case")]
39
pub enum EventLogMessageLevel {
40
    Info,
41
    Warn,
42
    Error,
43
    Fatal,
44
    Verbose,
45
    Debug,
46
    Trace,
47
    Unimplemented(String),
48
}
49

            
50
impl FromStr for EventLogMessageLevel {
51
    type Err = ();
52

            
53
    fn from_str(s: &str) -> Result<Self, Self::Err> {
54
        match s {
55
            "info" => Ok(EventLogMessageLevel::Info),
56
            "warn" => Ok(EventLogMessageLevel::Warn),
57
            "error" => Ok(EventLogMessageLevel::Error),
58
            "fatal" => Ok(EventLogMessageLevel::Fatal),
59
            "verbose" => Ok(EventLogMessageLevel::Verbose),
60
            "debug" => Ok(EventLogMessageLevel::Debug),
61
            "trace" => Ok(EventLogMessageLevel::Trace),
62
            level => Ok(EventLogMessageLevel::Unimplemented(level.to_string())),
63
        }
64
    }
65
}
66

            
67
/// All possible events that can be sent by mpv.
68
///
69
/// Not all event types are guaranteed to be implemented.
70
/// If something is missing, please open an issue.
71
///
72
/// Otherwise, the event will be returned as an `Event::Unimplemented` variant.
73
///
74
/// See <https://mpv.io/manual/master/#list-of-events> for
75
/// the upstream list of events.
76
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
77
#[serde(rename_all = "kebab-case")]
78
pub enum Event {
79
    StartFile {
80
        playlist_entry_id: usize,
81
    },
82
    EndFile {
83
        reason: EventEndFileReason,
84
        playlist_entry_id: usize,
85
        file_error: Option<String>,
86
        playlist_insert_id: Option<usize>,
87
        playlist_insert_num_entries: Option<usize>,
88
    },
89
    FileLoaded,
90
    Seek,
91
    PlaybackRestart,
92
    Shutdown,
93
    LogMessage {
94
        prefix: String,
95
        level: EventLogMessageLevel,
96
        text: String,
97
    },
98
    Hook {
99
        hook_id: usize,
100
    },
101
    GetPropertyReply,
102
    SetPropertyReply,
103
    CommandReply {
104
        result: String,
105
    },
106
    ClientMessage {
107
        args: Vec<String>,
108
    },
109
    VideoReconfig,
110
    AudioReconfig,
111
    PropertyChange {
112
        id: usize,
113
        name: String,
114
        data: Option<MpvDataType>,
115
    },
116
    EventQueueOverflow,
117
    None,
118

            
119
    /// Deprecated since mpv v0.33.0
120
    Idle,
121

            
122
    /// Deprecated since mpv v0.31.0
123
    Tick,
124

            
125
    /// Deprecated since mpv v0.7.0, removed in mpv v0.35.0
126
    TracksChanged,
127

            
128
    /// Deprecated since mpv v0.7.0, removed in mpv v0.35.0
129
    TrackSwitched,
130

            
131
    /// Deprecated since mpv v0.7.0, removed in mpv v0.35.0
132
    Pause,
133

            
134
    /// Deprecated since mpv v0.7.0, removed in mpv v0.35.0
135
    Unpause,
136

            
137
    /// Deprecated since mpv v0.7.0, removed in mpv v0.35.0
138
    MetadataUpdate,
139

            
140
    /// Deprecated since mpv v0.7.0, removed in mpv v0.35.0
141
    ChapterChange,
142

            
143
    /// Deprecated since mpv v0.7.0, removed in mpv v0.35.0
144
    ScriptInputDispatch,
145

            
146
    /// Catch-all for unimplemented events
147
    Unimplemented(Map<String, Value>),
148
}
149

            
150
macro_rules! get_key_as {
151
    ($as_type:ident, $key:expr, $event:ident) => {{
152
        let tmp = $event.get($key).ok_or(MpvError::MissingKeyInObject {
153
            key: $key.to_owned(),
154
            map: $event.clone(),
155
        })?;
156

            
157
        tmp.$as_type()
158
            .ok_or(MpvError::ValueContainsUnexpectedType {
159
                expected_type: stringify!($as_type).strip_prefix("as_").unwrap().to_owned(),
160
                received: tmp.clone(),
161
            })?
162
    }};
163
}
164

            
165
macro_rules! get_optional_key_as {
166
    ($as_type:ident, $key:expr, $event:ident) => {{
167
        if let Some(tmp) = $event.get($key) {
168
            Some(
169
                tmp.$as_type()
170
                    .ok_or(MpvError::ValueContainsUnexpectedType {
171
                        expected_type: stringify!($as_type).strip_prefix("as_").unwrap().to_owned(),
172
                        received: tmp.clone(),
173
                    })?,
174
            )
175
        } else {
176
            None
177
        }
178
    }};
179
}
180

            
181
// NOTE: I have not been able to test all of these events,
182
//       so some of the parsing logic might be incorrect.
183
//       In particular, I have not been able to make mpv
184
//       produce any of the commented out events, and since
185
//       the documentation for the most part just says
186
//       "See C API", I have not pursued this further.
187
//
188
//       If you need this, please open an issue or a PR.
189

            
190
/// Parse a highlevel [`Event`] objects from json.
191
28
pub(crate) fn parse_event(raw_event: MpvIpcEvent) -> Result<Event, MpvError> {
192
28
    let MpvIpcEvent(event) = raw_event;
193
28

            
194
28
    event
195
28
        .as_object()
196
28
        .ok_or(MpvError::ValueContainsUnexpectedType {
197
28
            expected_type: "object".to_owned(),
198
28
            received: event.clone(),
199
28
        })
200
28
        .and_then(|event| {
201
28
            let event_name = get_key_as!(as_str, "event", event);
202

            
203
28
            match event_name {
204
28
                "start-file" => parse_start_file(event),
205
28
                "end-file" => parse_end_file(event),
206
28
                "file-loaded" => Ok(Event::FileLoaded),
207
28
                "seek" => Ok(Event::Seek),
208
28
                "playback-restart" => Ok(Event::PlaybackRestart),
209
28
                "shutdown" => Ok(Event::Shutdown),
210
28
                "log-message" => parse_log_message(event),
211
28
                "hook" => parse_hook(event),
212
                // "get-property-reply" =>
213
                // "set-property-reply" =>
214
                // "command-reply" =>
215
28
                "client-message" => parse_client_message(event),
216
28
                "video-reconfig" => Ok(Event::VideoReconfig),
217
28
                "audio-reconfig" => Ok(Event::AudioReconfig),
218
28
                "property-change" => parse_property_change(event),
219
8
                "tick" => Ok(Event::Tick),
220
8
                "idle" => Ok(Event::Idle),
221
                "tracks-changed" => Ok(Event::TracksChanged),
222
                "track-switched" => Ok(Event::TrackSwitched),
223
                "pause" => Ok(Event::Pause),
224
                "unpause" => Ok(Event::Unpause),
225
                "metadata-update" => Ok(Event::MetadataUpdate),
226
                "chapter-change" => Ok(Event::ChapterChange),
227
                _ => Ok(Event::Unimplemented(event.to_owned())),
228
            }
229
28
        })
230
28
}
231

            
232
fn parse_start_file(event: &Map<String, Value>) -> Result<Event, MpvError> {
233
    let playlist_entry_id = get_key_as!(as_u64, "playlist_entry_id", event) as usize;
234

            
235
    Ok(Event::StartFile { playlist_entry_id })
236
}
237

            
238
fn parse_end_file(event: &Map<String, Value>) -> Result<Event, MpvError> {
239
    let reason = get_key_as!(as_str, "reason", event);
240
    let playlist_entry_id = get_key_as!(as_u64, "playlist_entry_id", event) as usize;
241
    let file_error = get_optional_key_as!(as_str, "file_error", event).map(|s| s.to_string());
242
    let playlist_insert_id =
243
        get_optional_key_as!(as_u64, "playlist_insert_id", event).map(|i| i as usize);
244
    let playlist_insert_num_entries =
245
        get_optional_key_as!(as_u64, "playlist_insert_num_entries", event).map(|i| i as usize);
246

            
247
    Ok(Event::EndFile {
248
        reason: reason
249
            .parse()
250
            .unwrap_or(EventEndFileReason::Unimplemented(reason.to_string())),
251
        playlist_entry_id,
252
        file_error,
253
        playlist_insert_id,
254
        playlist_insert_num_entries,
255
    })
256
}
257

            
258
fn parse_log_message(event: &Map<String, Value>) -> Result<Event, MpvError> {
259
    let prefix = get_key_as!(as_str, "prefix", event).to_owned();
260
    let level = get_key_as!(as_str, "level", event);
261
    let text = get_key_as!(as_str, "text", event).to_owned();
262

            
263
    Ok(Event::LogMessage {
264
        prefix,
265
        level: level
266
            .parse()
267
            .unwrap_or(EventLogMessageLevel::Unimplemented(level.to_string())),
268
        text,
269
    })
270
}
271

            
272
fn parse_hook(event: &Map<String, Value>) -> Result<Event, MpvError> {
273
    let hook_id = get_key_as!(as_u64, "hook_id", event) as usize;
274
    Ok(Event::Hook { hook_id })
275
}
276

            
277
fn parse_client_message(event: &Map<String, Value>) -> Result<Event, MpvError> {
278
    let args = get_key_as!(as_array, "args", event)
279
        .iter()
280
        .map(|arg| {
281
            arg.as_str()
282
                .ok_or(MpvError::ValueContainsUnexpectedType {
283
                    expected_type: "string".to_owned(),
284
                    received: arg.clone(),
285
                })
286
                .map(|s| s.to_string())
287
        })
288
        .collect::<Result<Vec<String>, MpvError>>()?;
289
    Ok(Event::ClientMessage { args })
290
}
291

            
292
20
fn parse_property_change(event: &Map<String, Value>) -> Result<Event, MpvError> {
293
20
    let id = get_key_as!(as_u64, "id", event) as usize;
294
20
    let property_name = get_key_as!(as_str, "name", event);
295
20
    let data = event.get("data").map(json_to_value).transpose()?;
296

            
297
20
    Ok(Event::PropertyChange {
298
20
        id,
299
20
        name: property_name.to_string(),
300
20
        data,
301
20
    })
302
20
}