use super::base::{ContentType, Part, PartType};
use crate::exc::PptxError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaFormat {
Mp4,
Webm,
Avi,
Wmv,
Mov,
Mp3,
Wav,
Wma,
M4a,
Ogg,
}
impl MediaFormat {
pub fn extension(&self) -> &'static str {
match self {
MediaFormat::Mp4 => "mp4",
MediaFormat::Webm => "webm",
MediaFormat::Avi => "avi",
MediaFormat::Wmv => "wmv",
MediaFormat::Mov => "mov",
MediaFormat::Mp3 => "mp3",
MediaFormat::Wav => "wav",
MediaFormat::Wma => "wma",
MediaFormat::M4a => "m4a",
MediaFormat::Ogg => "ogg",
}
}
pub fn mime_type(&self) -> &'static str {
match self {
MediaFormat::Mp4 => "video/mp4",
MediaFormat::Webm => "video/webm",
MediaFormat::Avi => "video/x-msvideo",
MediaFormat::Wmv => "video/x-ms-wmv",
MediaFormat::Mov => "video/quicktime",
MediaFormat::Mp3 => "audio/mpeg",
MediaFormat::Wav => "audio/wav",
MediaFormat::Wma => "audio/x-ms-wma",
MediaFormat::M4a => "audio/mp4",
MediaFormat::Ogg => "audio/ogg",
}
}
pub fn is_video(&self) -> bool {
matches!(
self,
MediaFormat::Mp4
| MediaFormat::Webm
| MediaFormat::Avi
| MediaFormat::Wmv
| MediaFormat::Mov
)
}
pub fn is_audio(&self) -> bool {
!self.is_video()
}
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_lowercase().as_str() {
"mp4" => Some(MediaFormat::Mp4),
"webm" => Some(MediaFormat::Webm),
"avi" => Some(MediaFormat::Avi),
"wmv" => Some(MediaFormat::Wmv),
"mov" => Some(MediaFormat::Mov),
"mp3" => Some(MediaFormat::Mp3),
"wav" => Some(MediaFormat::Wav),
"wma" => Some(MediaFormat::Wma),
"m4a" => Some(MediaFormat::M4a),
"ogg" => Some(MediaFormat::Ogg),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct MediaPart {
path: String,
media_number: usize,
format: MediaFormat,
data: Vec<u8>,
duration_ms: Option<u64>,
}
impl MediaPart {
pub fn new(media_number: usize, format: MediaFormat, data: Vec<u8>) -> Self {
MediaPart {
path: format!("ppt/media/media{}.{}", media_number, format.extension()),
media_number,
format,
data,
duration_ms: None,
}
}
pub fn from_file(media_number: usize, file_path: &str) -> Result<Self, PptxError> {
let data = std::fs::read(file_path)?;
let ext = std::path::Path::new(file_path)
.extension()
.and_then(|e| e.to_str())
.ok_or_else(|| PptxError::InvalidValue("No file extension".to_string()))?;
let format = MediaFormat::from_extension(ext)
.ok_or_else(|| PptxError::InvalidValue(format!("Unsupported media format: {}", ext)))?;
Ok(Self::new(media_number, format, data))
}
pub fn media_number(&self) -> usize {
self.media_number
}
pub fn format(&self) -> MediaFormat {
self.format
}
pub fn data(&self) -> &[u8] {
&self.data
}
pub fn set_duration(&mut self, duration_ms: u64) {
self.duration_ms = Some(duration_ms);
}
pub fn duration(&self) -> Option<u64> {
self.duration_ms
}
pub fn is_video(&self) -> bool {
self.format.is_video()
}
pub fn is_audio(&self) -> bool {
self.format.is_audio()
}
pub fn rel_target(&self) -> String {
format!(
"../media/media{}.{}",
self.media_number,
self.format.extension()
)
}
}
impl Part for MediaPart {
fn path(&self) -> &str {
&self.path
}
fn part_type(&self) -> PartType {
PartType::Image }
fn content_type(&self) -> ContentType {
ContentType::Image(self.format.extension().to_string())
}
fn to_xml(&self) -> Result<String, PptxError> {
Err(PptxError::InvalidOperation(
"Media parts are binary, not XML".to_string(),
))
}
fn from_xml(_xml: &str) -> Result<Self, PptxError> {
Err(PptxError::InvalidOperation(
"Media parts cannot be created from XML".to_string(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_media_format_extension() {
assert_eq!(MediaFormat::Mp4.extension(), "mp4");
assert_eq!(MediaFormat::Mp3.extension(), "mp3");
}
#[test]
fn test_media_format_mime_type() {
assert_eq!(MediaFormat::Mp4.mime_type(), "video/mp4");
assert_eq!(MediaFormat::Mp3.mime_type(), "audio/mpeg");
}
#[test]
fn test_media_format_is_video() {
assert!(MediaFormat::Mp4.is_video());
assert!(MediaFormat::Webm.is_video());
assert!(!MediaFormat::Mp3.is_video());
}
#[test]
fn test_media_format_is_audio() {
assert!(MediaFormat::Mp3.is_audio());
assert!(MediaFormat::Wav.is_audio());
assert!(!MediaFormat::Mp4.is_audio());
}
#[test]
fn test_media_format_from_extension() {
assert_eq!(MediaFormat::from_extension("mp4"), Some(MediaFormat::Mp4));
assert_eq!(MediaFormat::from_extension("MP3"), Some(MediaFormat::Mp3));
assert_eq!(MediaFormat::from_extension("xyz"), None);
}
#[test]
fn test_media_part_new() {
let media = MediaPart::new(1, MediaFormat::Mp4, vec![0, 1, 2, 3]);
assert_eq!(media.media_number(), 1);
assert_eq!(media.format(), MediaFormat::Mp4);
assert_eq!(media.path(), "ppt/media/media1.mp4");
}
#[test]
fn test_media_part_rel_target() {
let media = MediaPart::new(2, MediaFormat::Mp3, vec![]);
assert_eq!(media.rel_target(), "../media/media2.mp3");
}
#[test]
fn test_media_part_duration() {
let mut media = MediaPart::new(1, MediaFormat::Mp4, vec![]);
assert_eq!(media.duration(), None);
media.set_duration(5000);
assert_eq!(media.duration(), Some(5000));
}
}