libobs_wrapper\bootstrap/
extract.rs

1use std::{
2    env::current_exe,
3    path::{Path, PathBuf},
4};
5
6use async_stream::stream;
7use futures_core::Stream;
8use futures_util::{pin_mut, StreamExt};
9use sevenz_rust::{default_entry_extract_fn, Password, SevenZReader};
10use tokio::task;
11pub enum ExtractStatus {
12    Error(anyhow::Error),
13    Progress(f32, String),
14}
15
16pub(crate) async fn extract_obs(
17    archive_file: &Path,
18) -> anyhow::Result<impl Stream<Item = ExtractStatus>> {
19    log::info!("Extracting OBS at {}", archive_file.display());
20
21    let path = PathBuf::from(archive_file);
22
23    let destination = current_exe()?;
24    let destination = destination
25        .parent()
26        .ok_or_else(|| anyhow::anyhow!("Should be able to get parent of exe"))?
27        .join("obs_new");
28
29    //TODO delete old obs dlls and plugins
30    let dest = destination.clone();
31    let stream = stream! {
32        yield Ok((0.0, "Reading file...".to_string()));
33        let mut sz = SevenZReader::open(&path, Password::empty())?;
34        let (tx, mut rx) = tokio::sync::mpsc::channel(5);
35
36        let total = sz.archive().files.len() as f32;
37        if !dest.exists() {
38            std::fs::create_dir_all(&dest)?;
39        }
40
41        let mut curr = 0;
42        let mut r = task::spawn_blocking(move || {
43            sz.for_each_entries(|entry, reader| {
44                curr += 1;
45                tx.blocking_send((curr as f32 / total, format!("Extracting {}", entry.name()))).unwrap();
46
47                let dest_path = dest.join(entry.name());
48
49                default_entry_extract_fn(entry, reader, &dest_path)
50            })?;
51
52            Result::<_, anyhow::Error>::Ok((1.0, "Extraction done".to_string()))
53        });
54
55        loop {
56            tokio::select! {
57                m = rx.recv() => {
58                    match m {
59                        Some(e) => yield Ok(e),
60                        None => break
61                    }
62                },
63                res = &mut r => {
64                    match res {
65                        Ok(e) => yield e,
66                        Err(e) => {
67                            yield Err(e.into());
68                        }
69                    }
70
71                    break;
72                }
73            };
74        }
75
76        yield Ok((1.0, "Extraction done".to_string()));
77    };
78
79    Ok(stream! {
80            pin_mut!(stream);
81            while let Some(status) = stream.next().await {
82                match status {
83                    Ok(e) => yield ExtractStatus::Progress(e.0, e.1),
84                    Err(err) => {
85                        log::error!("Error extracting OBS: {:?}", err);
86                        yield ExtractStatus::Error(err);
87                        return;
88                    }
89                }
90            }
91
92    })
93}