1mod mpv;
2
3use anyhow::{anyhow, Result};
4use std::sync::{
5 mpsc::{Receiver, Sender},
6 Arc
7};
8use time::{format_description, UtcOffset};
9
10use cursive::{
11 event::{Event, Key},
12 view::SizeConstraint,
13 views::{
14 Dialog, DummyView, LinearLayout, ResizedView, ScrollView, SelectView, TextContent, TextView,
15 },
16 Cursive, View,
17};
18use log::{error};
19use mpv::{Van, DEFAULT_VOL};
20use mpv::VanControl;
21
22use mpv::PlayStatus;
23
24struct CurrentStatus {
25 vol: Option<Arc<TextContent>>,
26 current_song_status: Arc<TextContent>,
27 current_artist_status: Arc<TextContent>,
28 current_time_status: Arc<TextContent>,
29}
30
31pub fn init_siv(
47 siv: &mut Cursive,
48 args: Vec<String>,
49 control_tx: Sender<VanControl>,
50 control_rx: Receiver<VanControl>,
51) -> Result<()> {
52 let (view, current_status) = get_view();
53 let vol_status = current_status.vol.unwrap();
54 let van = Arc::new(Van::new()?);
55 let van_clone = van.clone();
56 let van_clone_2 = van.clone();
57
58 for i in args {
59 van.add(&i)?;
60 }
61
62 start_mpv(
63 CurrentStatus {
64 vol: None,
65 ..current_status
66 },
67 control_rx,
68 van_clone
69 );
70
71 set_cursive(vol_status, control_tx, siv, van_clone_2);
72
73 siv.add_layer(view);
74
75 Ok(())
76}
77
78pub fn destroy_mpv(control_tx: Sender<VanControl>) -> Result<()> {
80 control_tx.send(VanControl::Exit)?;
81
82 Ok(())
83}
84
85fn set_cursive(vol_status: Arc<TextContent>, control_tx: Sender<VanControl>, siv: &mut Cursive, van: Arc<Van>) {
86 let volume_status_clone = vol_status.clone();
87 let control_tx_clone = control_tx.clone();
88 let control_tx_clone_2 = control_tx.clone();
89 let control_tx_clone_3 = control_tx.clone();
90 let control_tx_clone_4 = control_tx.clone();
91 let van_clone = van.clone();
92 let van_clone_2 = van.clone();
93
94 siv.add_global_callback('=', move |_| {
95 if let Err(e) = add_volume(control_tx.clone(), volume_status_clone.clone(), van_clone.clone()) {
96 error!("{}", e);
97 }
98 });
99 siv.add_global_callback('-', move |_| {
100 if let Err(e) = reduce_volume(control_tx_clone_2.clone(), vol_status.clone(), van_clone_2.clone()) {
101 error!("{}", e);
102 }
103 });
104 siv.add_global_callback(Event::Key(Key::Right), move |_| {
105 control_tx_clone_3.send(VanControl::NextSong).unwrap();
106 });
107 siv.add_global_callback(Event::Key(Key::Left), move |_| {
108 control_tx_clone.send(VanControl::PrevSong).unwrap();
109 });
110 siv.add_global_callback('p', move |_| {
111 control_tx_clone_4.send(VanControl::PauseControl).unwrap();
112 });
113 siv.add_global_callback('l', move |s| {
114 playlist_view(s, van.clone());
115 });
116 siv.add_global_callback('~', cursive::Cursive::toggle_debug_console);
117 siv.set_autorefresh(true);
118}
119
120fn get_view() -> (Dialog, CurrentStatus) {
121 let mut vol_view = TextView::new(format!("vol: {}", DEFAULT_VOL));
122 let vol_status = Arc::new(vol_view.get_shared_content());
123
124 let mut current_song_view = TextView::new("Unknown");
125 let current_song_status = Arc::new(current_song_view.get_shared_content());
126
127 let mut current_time_view = TextView::new("-/-");
128 let current_time_status = Arc::new(current_time_view.get_shared_content());
129
130 let mut current_artist_view = TextView::new("Unknown");
131 let current_artist_status = Arc::new(current_artist_view.get_shared_content());
132
133 let view = wrap_in_dialog(
134 LinearLayout::vertical()
135 .child(current_song_view.center())
136 .child(DummyView {})
137 .child(current_artist_view.center())
138 .child(DummyView {})
139 .child(current_time_view.center())
140 .child(DummyView {})
141 .child(vol_view.center()),
142 "Van",
143 None,
144 );
145
146 (
147 view,
148 CurrentStatus {
149 vol: Some(vol_status),
150 current_song_status,
151 current_artist_status,
152 current_time_status,
153 },
154 )
155}
156
157fn playlist_view(siv: &mut Cursive, van: Arc<Van>) {
158 let playlist = van.get_playlist();
159 let mut files = vec![];
160 if let Ok(playlist) = playlist {
161 for i in playlist {
162 files.push(i.filename);
163 }
164 } else {
165 error!("{:?}", playlist.unwrap_err());
166 }
167 let view = wrap_in_dialog(
168 SelectView::new()
169 .with_all_str(files.clone())
170 .on_submit(move |s, c: &String| {
171 let index = files.clone().iter().position(|x| x == c);
172 if let Some(index) = index {
173 van.force_play(index.try_into().unwrap()).ok();
174 }
175 s.pop_layer();
176 }),
177 "Playlist",
178 None,
179 )
180 .button("Back", |s| {
181 s.cb_sink()
182 .send(Box::new(|s| {
183 s.pop_layer();
184 }))
185 .unwrap();
186 });
187 siv.add_layer(view);
188}
189
190fn start_mpv(current_status: CurrentStatus, control_rx: Receiver<VanControl>, van: Arc<Van>) {
191 let van_clone = van.clone();
192 std::thread::spawn(move || {
193 let (getinfo_tx, getinfo_rx) = std::sync::mpsc::channel();
194 let current_song_status_clone = current_status.current_song_status.clone();
195 std::thread::spawn(move || {
196 let buf = std::ffi::CString::new("C").expect("Unknown Error!");
199 unsafe { libc::setlocale(libc::LC_NUMERIC, buf.as_ptr()) };
200 if let Err(e) = van.play(control_rx, getinfo_tx) {
201 eprintln!("{}", e);
202 std::process::exit(1);
203 }
204 });
205 loop {
206 let mut time_str = String::from("-/-");
207 let r = getinfo_rx.try_recv();
208 if let Ok(status) = r {
209 match status {
211 PlayStatus::MediaInfo(m) => {
212 current_song_status_clone.set_content(m.title);
213 current_status.current_artist_status.set_content(m.artist);
214 if let Ok(current_time) = get_time(m.current_time) {
215 time_str = time_str.replace("-/", &format!("{}/", current_time));
216 }
217 if let Ok(duration) = get_time(m.duration) {
218 time_str = time_str.replace("/-", &format!("/{}", duration));
219 }
220 current_status.current_time_status.set_content(time_str);
221 }
222 PlayStatus::Loading => {
223 if let Ok(name) = van_clone.get_file_name() {
224 current_status.current_song_status.clone().set_content(name);
225 current_status
226 .current_artist_status
227 .clone()
228 .set_content("Unknown");
229 current_status
230 .current_time_status
231 .clone()
232 .set_content("-/-");
233 }
234 }
235 }
236 }
237 }
238 });
239}
240
241fn add_volume(control_tx: Sender<VanControl>, vol_status: Arc<TextContent>, van: Arc<Van>) -> Result<()> {
242 let mut current_vol = van.get_volume()?;
243 if current_vol < 100.0 {
244 current_vol += 5.0;
245 control_tx.send(VanControl::SetVolume(current_vol))?;
246 vol_status.set_content(format!("vol: {}", current_vol));
247 }
248
249 Ok(())
250}
251
252fn reduce_volume(control_tx: Sender<VanControl>, vol_status: Arc<TextContent>, van: Arc<Van>) -> Result<()> {
253 let mut current_vol = van.get_volume()?;
254 if current_vol > 0.0 {
255 current_vol -= 5.0;
256 control_tx.send(VanControl::SetVolume(current_vol))?;
257 vol_status.set_content(format!("vol: {}", current_vol));
258 }
259
260 Ok(())
261}
262
263fn wrap_in_dialog<V: View, S: Into<String>>(inner: V, title: S, width: Option<usize>) -> Dialog {
264 Dialog::around(ResizedView::new(
265 SizeConstraint::AtMost(width.unwrap_or(64)),
266 SizeConstraint::Free,
267 ScrollView::new(inner),
268 ))
269 .padding_lrtb(2, 2, 1, 1)
270 .title(title)
271}
272
273fn get_time(time: i64) -> Result<String> {
274 let f = format_description::parse("[offset_minute]:[offset_second]")?;
275 let offset = UtcOffset::from_whole_seconds(time.try_into()?)?;
276 let minute = offset.whole_minutes();
277 let date = offset.format(&f)?;
278 let sess = date
279 .split_once(':')
280 .map(|x| x.1)
281 .ok_or_else(|| anyhow!("Can not convert time!"))?;
282 let date = format!("{}:{}", minute, sess);
283
284 Ok(date)
285}