1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
mod ffprobe;
mod manifest;
mod processing;
use dashmap::DashMap;
use processing::{Subtitles, Video};
use serde::Serialize;
use std::path::PathBuf;
use std::process::Child;
use std::sync::Arc;
use std::thread;

use crate::ffprobe::{get_media_info, MediaInfo};
use crate::manifest::{create_manifest_file, create_manifest_file_subs, create_master_file};
use crate::processing::{process_video, Audio, Profile};

#[derive(Clone, Debug, Serialize)]
pub enum ProcessingState {
    InProgress,
    Done,
    NotStarted,
}

pub struct Parachute {
    id_to_process: Arc<DashMap<String, ProcessingState>>,
    ffprobe_path: String,
    ffmpeg_path: String,
    cdn_dir_path: String,
}

impl Parachute {
    pub fn new(ffprobe_path: &str, ffmpeg_path: &str, cdn_dir_path: &str) -> Self {
        Self {
            id_to_process: Arc::new(DashMap::new()),
            ffprobe_path: ffprobe_path.to_string(),
            ffmpeg_path: ffmpeg_path.to_string(),
            cdn_dir_path: cdn_dir_path.to_string(),
        }
    }

    pub fn play_video_seq(&self, id: &str, path: &PathBuf) -> ProcessingState {
        self.id_to_process
            .entry(id.to_string())
            .or_insert_with(|| {
                let id = id.to_string();
                let mut process = self.start_process(&id, &path);
                let id_to_process = self.id_to_process.clone();
                thread::spawn(move || {
                    process
                        .wait()
                        .expect("Something wen't wrong while processing media");
                    id_to_process.insert(id, ProcessingState::Done);
                    println!("Done processing media");
                });
                ProcessingState::InProgress
            })
            .clone()
    }

    fn start_process(&self, id: &str, path: &PathBuf) -> Child {
        let media_info = get_media_info(&self.ffprobe_path, path).unwrap();
        let video_args = Video::get_args(&media_info);
        let audio_args = Audio::get_args(&media_info);
        let subtitle_args = Subtitles::get_args(&media_info);
        self.create_files(id, &media_info);
        process_video(
            path,
            id,
            &self.cdn_dir_path,
            &self.ffmpeg_path,
            &video_args,
            &audio_args,
            &subtitle_args,
        )
    }

    fn create_files(&self, id: &str, media_info: &MediaInfo) {
        let manifest_path = PathBuf::from(format!("{}/{}_manifest.m3u8", self.cdn_dir_path, id));
        let manifest_subs_path =
            PathBuf::from(format!("{}/{}_manifest_subs.m3u8", self.cdn_dir_path, id));
        let playlist_path = PathBuf::from(format!("{}/{}_playlist.m3u8", self.cdn_dir_path, id));
        let init_seg = format!("{}_init.mp4", id);
        let duration: f32 = media_info.format.duration.parse().unwrap();

        create_manifest_file(id, &manifest_path, duration, &init_seg).unwrap();
        create_manifest_file_subs(id, &manifest_subs_path, duration).unwrap();
        create_master_file(
            &manifest_path,
            &manifest_subs_path,
            &playlist_path,
            media_info.subtitle.len() == 0,
        )
        .unwrap();
    }
}