1
//! High-level API extension for [`Mpv`].
2

            
3
use crate::{
4
    Error, IntoRawCommandPart, Mpv, MpvCommand, MpvDataType, Playlist, PlaylistAddOptions,
5
    PlaylistEntry, SeekOptions,
6
};
7
use serde::{Deserialize, Serialize};
8
use std::collections::HashMap;
9

            
10
/// Generic high-level command for changing a number property.
11
#[derive(Debug, Clone, Serialize, Deserialize)]
12
pub enum NumberChangeOptions {
13
    Absolute,
14
    Increase,
15
    Decrease,
16
}
17

            
18
impl IntoRawCommandPart for NumberChangeOptions {
19
    fn into_raw_command_part(self) -> String {
20
        match self {
21
            NumberChangeOptions::Absolute => "absolute".to_string(),
22
            NumberChangeOptions::Increase => "increase".to_string(),
23
            NumberChangeOptions::Decrease => "decrease".to_string(),
24
        }
25
    }
26
}
27

            
28
/// Generic high-level switch for toggling boolean properties.
29
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
30
pub enum Switch {
31
    On,
32
    Off,
33
    Toggle,
34
}
35

            
36
/// Options for [`MpvExt::playlist_add`].
37
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
38
pub enum PlaylistAddTypeOptions {
39
    File,
40
    Playlist,
41
}
42

            
43
/// A set of typesafe high-level functions to interact with [`Mpv`].
44
// TODO: fix this
45
#[allow(async_fn_in_trait)]
46
pub trait MpvExt {
47
    async fn toggle(&self) -> Result<(), Error>;
48
    async fn stop(&self) -> Result<(), Error>;
49
    async fn set_volume(&self, input_volume: f64, option: NumberChangeOptions)
50
        -> Result<(), Error>;
51
    async fn set_speed(&self, input_speed: f64, option: NumberChangeOptions) -> Result<(), Error>;
52
    async fn set_mute(&self, option: Switch) -> Result<(), Error>;
53
    async fn set_loop_playlist(&self, option: Switch) -> Result<(), Error>;
54
    async fn set_loop_file(&self, option: Switch) -> Result<(), Error>;
55
    async fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), Error>;
56
    async fn playlist_shuffle(&self) -> Result<(), Error>;
57
    async fn playlist_remove_id(&self, id: usize) -> Result<(), Error>;
58
    async fn playlist_play_next(&self, id: usize) -> Result<(), Error>;
59
    async fn playlist_play_id(&self, id: usize) -> Result<(), Error>;
60
    async fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), Error>;
61
    async fn playlist_clear(&self) -> Result<(), Error>;
62
    async fn playlist_add(
63
        &self,
64
        file: &str,
65
        file_type: PlaylistAddTypeOptions,
66
        option: PlaylistAddOptions,
67
    ) -> Result<(), Error>;
68
    async fn restart(&self) -> Result<(), Error>;
69
    async fn prev(&self) -> Result<(), Error>;
70
    async fn pause(&self) -> Result<(), Error>;
71
    async fn unobserve_property(&self, id: isize) -> Result<(), Error>;
72
    async fn observe_property(&self, id: isize, property: &str) -> Result<(), Error>;
73
    async fn next(&self) -> Result<(), Error>;
74
    async fn kill(&self) -> Result<(), Error>;
75
    async fn get_playlist(&self) -> Result<Playlist, Error>;
76
    async fn get_metadata(&self) -> Result<HashMap<String, MpvDataType>, Error>;
77
}
78

            
79
impl MpvExt for Mpv {
80
    async fn get_metadata(&self) -> Result<HashMap<String, MpvDataType>, Error> {
81
        self.get_property("metadata").await
82
    }
83

            
84
8
    async fn get_playlist(&self) -> Result<Playlist, Error> {
85
4
        self.get_property::<Vec<PlaylistEntry>>("playlist")
86
4
            .await
87
4
            .map(Playlist)
88
4
    }
89

            
90
3
    async fn kill(&self) -> Result<(), Error> {
91
3
        self.run_command(MpvCommand::Quit).await
92
3
    }
93

            
94
    async fn next(&self) -> Result<(), Error> {
95
        self.run_command(MpvCommand::PlaylistNext).await
96
    }
97

            
98
4
    async fn observe_property(&self, id: isize, property: &str) -> Result<(), Error> {
99
2
        self.run_command(MpvCommand::Observe {
100
2
            id,
101
2
            property: property.to_string(),
102
2
        })
103
2
        .await
104
2
    }
105

            
106
    async fn unobserve_property(&self, id: isize) -> Result<(), Error> {
107
        self.run_command(MpvCommand::Unobserve(id)).await
108
    }
109

            
110
    async fn pause(&self) -> Result<(), Error> {
111
        self.set_property("pause", true).await
112
    }
113

            
114
    async fn prev(&self) -> Result<(), Error> {
115
        self.run_command(MpvCommand::PlaylistPrev).await
116
    }
117

            
118
    async fn restart(&self) -> Result<(), Error> {
119
        self.run_command(MpvCommand::Seek {
120
            seconds: 0f64,
121
            option: SeekOptions::Absolute,
122
        })
123
        .await
124
    }
125

            
126
    async fn playlist_add(
127
        &self,
128
        file: &str,
129
        file_type: PlaylistAddTypeOptions,
130
        option: PlaylistAddOptions,
131
    ) -> Result<(), Error> {
132
        match file_type {
133
            PlaylistAddTypeOptions::File => {
134
                self.run_command(MpvCommand::LoadFile {
135
                    file: file.to_string(),
136
                    option,
137
                })
138
                .await
139
            }
140

            
141
            PlaylistAddTypeOptions::Playlist => {
142
                self.run_command(MpvCommand::LoadList {
143
                    file: file.to_string(),
144
                    option,
145
                })
146
                .await
147
            }
148
        }
149
    }
150

            
151
    async fn playlist_clear(&self) -> Result<(), Error> {
152
        self.run_command(MpvCommand::PlaylistClear).await
153
    }
154

            
155
    async fn playlist_move_id(&self, from: usize, to: usize) -> Result<(), Error> {
156
        self.run_command(MpvCommand::PlaylistMove { from, to })
157
            .await
158
    }
159

            
160
    async fn playlist_play_id(&self, id: usize) -> Result<(), Error> {
161
        self.set_property("playlist-pos", id).await
162
    }
163

            
164
    async fn playlist_play_next(&self, id: usize) -> Result<(), Error> {
165
        match self.get_property::<usize>("playlist-pos").await {
166
            Ok(current_id) => {
167
                self.run_command(MpvCommand::PlaylistMove {
168
                    from: id,
169
                    to: current_id + 1,
170
                })
171
                .await
172
            }
173
            Err(msg) => Err(msg),
174
        }
175
    }
176

            
177
    async fn playlist_remove_id(&self, id: usize) -> Result<(), Error> {
178
        self.run_command(MpvCommand::PlaylistRemove(id)).await
179
    }
180

            
181
    async fn playlist_shuffle(&self) -> Result<(), Error> {
182
        self.run_command(MpvCommand::PlaylistShuffle).await
183
    }
184

            
185
    async fn seek(&self, seconds: f64, option: SeekOptions) -> Result<(), Error> {
186
        self.run_command(MpvCommand::Seek { seconds, option }).await
187
    }
188

            
189
    async fn set_loop_file(&self, option: Switch) -> Result<(), Error> {
190
        let enabled = match option {
191
            Switch::On => "inf",
192
            Switch::Off => "no",
193
            Switch::Toggle => {
194
                self.get_property::<String>("loop-file")
195
                    .await
196
                    .map(|s| match s.as_str() {
197
                        "inf" => "no",
198
                        "no" => "inf",
199
                        _ => "no",
200
                    })?
201
            }
202
        };
203
        self.set_property("loop-file", enabled).await
204
    }
205

            
206
    async fn set_loop_playlist(&self, option: Switch) -> Result<(), Error> {
207
        let enabled = match option {
208
            Switch::On => "inf",
209
            Switch::Off => "no",
210
            Switch::Toggle => {
211
                self.get_property::<String>("loop-playlist")
212
                    .await
213
                    .map(|s| match s.as_str() {
214
                        "inf" => "no",
215
                        "no" => "inf",
216
                        _ => "no",
217
                    })?
218
            }
219
        };
220
        self.set_property("loo-playlist", enabled).await
221
    }
222

            
223
    async fn set_mute(&self, option: Switch) -> Result<(), Error> {
224
        let enabled = match option {
225
            Switch::On => "yes",
226
            Switch::Off => "no",
227
            Switch::Toggle => {
228
                self.get_property::<String>("mute")
229
                    .await
230
                    .map(|s| match s.as_str() {
231
                        "yes" => "no",
232
                        "no" => "yes",
233
                        _ => "no",
234
                    })?
235
            }
236
        };
237
        self.set_property("mute", enabled).await
238
    }
239

            
240
    async fn set_speed(&self, input_speed: f64, option: NumberChangeOptions) -> Result<(), Error> {
241
        match self.get_property::<f64>("speed").await {
242
            Ok(speed) => match option {
243
                NumberChangeOptions::Increase => {
244
                    self.set_property("speed", speed + input_speed).await
245
                }
246

            
247
                NumberChangeOptions::Decrease => {
248
                    self.set_property("speed", speed - input_speed).await
249
                }
250

            
251
                NumberChangeOptions::Absolute => self.set_property("speed", input_speed).await,
252
            },
253
            Err(msg) => Err(msg),
254
        }
255
    }
256

            
257
    async fn set_volume(
258
        &self,
259
        input_volume: f64,
260
        option: NumberChangeOptions,
261
    ) -> Result<(), Error> {
262
        match self.get_property::<f64>("volume").await {
263
            Ok(volume) => match option {
264
                NumberChangeOptions::Increase => {
265
                    self.set_property("volume", volume + input_volume).await
266
                }
267

            
268
                NumberChangeOptions::Decrease => {
269
                    self.set_property("volume", volume - input_volume).await
270
                }
271

            
272
                NumberChangeOptions::Absolute => self.set_property("volume", input_volume).await,
273
            },
274
            Err(msg) => Err(msg),
275
        }
276
    }
277

            
278
    async fn stop(&self) -> Result<(), Error> {
279
        self.run_command(MpvCommand::Stop).await
280
    }
281

            
282
    async fn toggle(&self) -> Result<(), Error> {
283
        self.run_command_raw("cycle", &["pause"]).await.map(|_| ())
284
    }
285
}