parachute/
lib.rs

1mod ffprobe;
2mod manifest;
3mod processing;
4use dashmap::DashMap;
5use processing::{Subtitles, Video};
6use serde::Serialize;
7use std::path::PathBuf;
8use std::process::Child;
9use std::sync::Arc;
10use std::thread;
11
12use crate::ffprobe::{get_media_info, MediaInfo};
13use crate::manifest::{create_manifest_file, create_manifest_file_subs, create_master_file};
14use crate::processing::{process_video, Audio, Profile};
15
16#[derive(Clone, Debug, Serialize)]
17pub enum ProcessingState {
18    InProgress,
19    Done,
20    NotStarted,
21}
22
23pub struct Parachute {
24    id_to_process: Arc<DashMap<String, ProcessingState>>,
25    ffprobe_path: String,
26    ffmpeg_path: String,
27    cdn_dir_path: String,
28}
29
30impl Parachute {
31    pub fn new(ffprobe_path: &str, ffmpeg_path: &str, cdn_dir_path: &str) -> Self {
32        Self {
33            id_to_process: Arc::new(DashMap::new()),
34            ffprobe_path: ffprobe_path.to_string(),
35            ffmpeg_path: ffmpeg_path.to_string(),
36            cdn_dir_path: cdn_dir_path.to_string(),
37        }
38    }
39
40    pub fn play_video_seq(&self, id: &str, path: &PathBuf) -> ProcessingState {
41        self.id_to_process
42            .entry(id.to_string())
43            .or_insert_with(|| {
44                let id = id.to_string();
45                let mut process = self.start_process(&id, &path);
46                let id_to_process = self.id_to_process.clone();
47                thread::spawn(move || {
48                    process
49                        .wait()
50                        .expect("Something wen't wrong while processing media");
51                    id_to_process.insert(id, ProcessingState::Done);
52                    println!("Done processing media");
53                });
54                ProcessingState::InProgress
55            })
56            .clone()
57    }
58
59    fn start_process(&self, id: &str, path: &PathBuf) -> Child {
60        let media_info = get_media_info(&self.ffprobe_path, path).unwrap();
61        let video_args = Video::get_args(&media_info);
62        let audio_args = Audio::get_args(&media_info);
63        let subtitle_args = Subtitles::get_args(&media_info);
64        self.create_files(id, &media_info);
65        process_video(
66            path,
67            id,
68            &self.cdn_dir_path,
69            &self.ffmpeg_path,
70            &video_args,
71            &audio_args,
72            &subtitle_args,
73        )
74    }
75
76    fn create_files(&self, id: &str, media_info: &MediaInfo) {
77        let manifest_path = PathBuf::from(format!("{}/{}_manifest.m3u8", self.cdn_dir_path, id));
78        let manifest_subs_path =
79            PathBuf::from(format!("{}/{}_manifest_subs.m3u8", self.cdn_dir_path, id));
80        let playlist_path = PathBuf::from(format!("{}/{}_playlist.m3u8", self.cdn_dir_path, id));
81        let init_seg = format!("{}_init.mp4", id);
82        let duration: f32 = media_info.format.duration.parse().unwrap();
83
84        create_manifest_file(id, &manifest_path, duration, &init_seg).unwrap();
85        create_manifest_file_subs(id, &manifest_subs_path, duration).unwrap();
86        create_master_file(
87            &manifest_path,
88            &manifest_subs_path,
89            &playlist_path,
90            media_info.subtitle.len() == 0,
91        )
92        .unwrap();
93    }
94}