use super::renderer_error::FFramesRendererResult;
use crate::{
DynamicMediaProvider, RawFontData,
media::{RawMediaFile, Subtitles, VideoMedia},
};
use rayon::prelude::*;
use std::{
collections::HashMap,
ffi::OsStr,
fs,
io::{self},
path::{Path, PathBuf},
sync::{Arc, Mutex},
};
pub struct MediaDirectory(Vec<(PathBuf, RawMediaFile)>);
#[cfg(not(target_arch = "wasm32"))]
impl MediaDirectory {
pub fn read_folder(folder_path: impl AsRef<Path>) -> FFramesRendererResult<MediaDirectory> {
let mut folder_content = vec![];
if !folder_path.as_ref().is_dir() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput, "resources_dir must be a folder",
)
.into());
}
for file in fs::read_dir(folder_path)? {
let path = file?.path();
if !path.is_file() {
continue;
}
match path.extension().and_then(OsStr::to_str) {
Some(
"jpg" | "jpeg" | "png" | "gif" | "vtt" | "json" | "mp3" | "wav" | "flac"
| "aac" | "pcm" | "ogg" | "mp2",
) => {
let bytes = fs::read(&path)?;
folder_content.push((path, RawMediaFile::Data(bytes)));
}
Some(
"mp4" | "webm" | "mkv" | "avi" | "mov" | "flv" | "wmv" | "m4v" | "ttf" | "ttc"
| "otf" | "otc",
) => {
folder_content.push((path.clone(), RawMediaFile::Stream(path)));
}
_ => {}
}
}
Ok(MediaDirectory(folder_content))
}
pub fn process_media_source(&self) -> FFramesRendererResult<DynamicMediaProvider<'_>> {
let audio_hash = Mutex::new(HashMap::new());
let subtitles_hash = Mutex::new(HashMap::new());
let image_hash = Mutex::new(HashMap::new());
let fontdata = Mutex::new(Vec::new());
let video_paths = Mutex::new(HashMap::new());
#[cfg(feature = "lottie")]
let lottie_hash = Mutex::new(HashMap::new());
self.0
.par_iter()
.try_for_each(|(path, raw_file)| -> FFramesRendererResult<()> {
if let Some((extension, filename)) = path
.extension()
.and_then(OsStr::to_str)
.zip(path.file_name().and_then(OsStr::to_str))
{
match (extension, raw_file) {
("mp3" | "wav" | "flac" | "aac" | "pcm" | "ogg" | "mp2", _) => {
let audio_data =
crate::media::PreloadedAudioData::decode_raw_file_stereo(
None, path,
)?;
audio_hash.lock()?.insert(
filename.to_owned(),
crate::AudioData::Preloaded(audio_data),
);
}
("vtt", RawMediaFile::Data(bytes)) => {
let str_bytes = std::str::from_utf8(bytes)?;
subtitles_hash.lock()?.insert(
filename.to_owned(),
Subtitles::parse(str_bytes)
.map_err(crate::media::FFramesMediaError::from)?,
);
}
#[cfg(feature = "lottie")]
("json", RawMediaFile::Data(bytes)) => {
lottie_hash.lock()?.insert(
filename.to_owned(),
crate::Lottie::parse(bytes)
.map_err(crate::error::FFramesError::from)?,
);
}
("ttf" | "ttc" | "otf" | "otc", RawMediaFile::Stream(path)) => {
let bytes = fs::read(path)?;
fontdata.lock()?.push(RawFontData {
file_name: filename.to_owned(),
data: Arc::new(bytes),
});
}
("jpg" | "jpeg" | "png", RawMediaFile::Data(bytes)) => {
image_hash.lock()?.insert(
filename.to_owned(),
crate::media::ImageData::new_from_bytes(filename, bytes)?,
);
}
("mp4" | "webm" | "mkv" | "avi" | "mov" | "flv" | "wmv" | "m4v", _) => {
video_paths.lock()?.insert(
filename.to_owned(),
VideoMedia {
path: path.to_owned(),
metadata: None,
},
);
if let Ok(audio_data) =
crate::media::PreloadedAudioData::decode_raw_file_stereo(None, path)
{
audio_hash.lock()?.insert(
filename.to_owned(),
crate::AudioData::Preloaded(audio_data),
);
}
}
_ => {}
}
}
Ok(())
})?;
Ok(DynamicMediaProvider {
audio: audio_hash.into_inner()?,
images: image_hash.into_inner()?,
subtitles: subtitles_hash.into_inner()?,
videos: video_paths.into_inner()?,
fontdata: fontdata.into_inner()?,
#[cfg(feature = "lottie")]
lottie: lottie_hash.into_inner()?,
})
}
}