motion_core/audio/
metadata.rs1use super::invalid;
4use crate::{Time, ValidationError};
5
6const MAX_TAGS: usize = 64;
8
9const MAX_CHAPTERS: usize = 4096;
11
12const MAX_TAG_KEY_BYTES: usize = 128;
14
15const MAX_TEXT_BYTES: usize = 4096;
17
18const MAX_CHAPTER_END_SECONDS: f64 = 86400.0;
20
21#[derive(Clone, Debug)]
23pub struct AudioChapter {
24 pub start: Time,
26 pub end: Time,
28 pub title: String,
30}
31
32#[derive(Clone, Debug, Default)]
34pub struct AudioMetadata {
35 pub tags: std::collections::BTreeMap<String, String>,
37 pub chapters: Vec<AudioChapter>,
39}
40
41impl AudioMetadata {
42 pub fn validate(&self) -> Result<(), ValidationError> {
44 if self.tags.len() > MAX_TAGS
45 || self.chapters.len() > MAX_CHAPTERS
46 || self.tags.iter().any(|(key, value)| {
47 key.is_empty()
48 || key.len() > MAX_TAG_KEY_BYTES
49 || value.len() > MAX_TEXT_BYTES
50 || key.contains('\0')
51 || value.contains('\0')
52 })
53 || self.chapters.iter().any(|chapter| {
54 chapter.start < Time::ZERO
55 || chapter.end <= chapter.start
56 || chapter.end.as_seconds() > MAX_CHAPTER_END_SECONDS
57 || chapter.title.len() > MAX_TEXT_BYTES
58 || chapter.title.contains('\0')
59 })
60 || self
61 .chapters
62 .windows(2)
63 .any(|pair| pair[0].end > pair[1].start)
64 {
65 return Err(invalid(
66 "metadata",
67 "Invalid bounded container tags or ordered chapters",
68 ));
69 }
70 Ok(())
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.tags.is_empty() && self.chapters.is_empty()
76 }
77}