use bitflags::bitflags;
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MediaErrorFlags: u16 {
const VIDEO_ERROR = 0x0001;
const AUDIO_ERROR = 0x0002;
const SUBTITLE_ERROR = 0x0004;
}
}
impl MediaErrorFlags {
#[inline(always)]
pub const fn new() -> Self {
Self::empty()
}
}
impl Default for MediaErrorFlags {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VideoIndexStatus: u32 {
const PROBED = 0x01;
const SCENE_DETECTED = 0x02;
const KEYFRAME_EXTRACTED = 0x04;
const VLM_ANALYZED = 0x10;
const APPLE_VISION_ANALYZED = 0x20;
const TEXT_EMBEDDING_FINISHED = 0x40;
const SCENE_EMBEDDING_FINISHED = 0x80;
}
}
impl VideoIndexStatus {
#[inline(always)]
pub const fn new() -> Self {
Self::empty()
}
#[inline]
pub const fn fully_indexed_mask() -> Self {
Self::from_bits_truncate(
Self::PROBED.bits()
| Self::SCENE_DETECTED.bits()
| Self::KEYFRAME_EXTRACTED.bits()
| Self::VLM_ANALYZED.bits()
| Self::APPLE_VISION_ANALYZED.bits()
| Self::TEXT_EMBEDDING_FINISHED.bits()
| Self::SCENE_EMBEDDING_FINISHED.bits(),
)
}
#[inline]
pub fn is_fully_indexed(&self) -> bool {
self.contains(Self::fully_indexed_mask())
}
}
impl Default for VideoIndexStatus {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AudioIndexStatus: u32 {
const EXTRACTED = 0x001;
const CLASSIFIED = 0x002;
const VAD_DONE = 0x004;
const STT_DONE = 0x008;
const SPEAKER_DONE = 0x010;
const LLM_DONE = 0x020;
const TEXT_EMBED = 0x040;
const CED_DONE = 0x080;
const CLAP_DONE = 0x100;
const EBUR128_DONE = 0x200;
const FPRINT_DONE = 0x400;
}
}
impl AudioIndexStatus {
#[inline(always)]
pub const fn new() -> Self {
Self::empty()
}
#[inline]
pub const fn fully_indexed_mask() -> Self {
Self::from_bits_truncate(
Self::EXTRACTED.bits()
| Self::CLASSIFIED.bits()
| Self::VAD_DONE.bits()
| Self::STT_DONE.bits()
| Self::SPEAKER_DONE.bits()
| Self::LLM_DONE.bits()
| Self::TEXT_EMBED.bits()
| Self::CED_DONE.bits()
| Self::CLAP_DONE.bits()
| Self::EBUR128_DONE.bits()
| Self::FPRINT_DONE.bits(),
)
}
#[inline]
pub fn is_fully_indexed(&self) -> bool {
self.contains(Self::fully_indexed_mask())
}
}
impl Default for AudioIndexStatus {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SubtitleIndexStatus: u32 {
const TRACKS_DISCOVERED = 0x01;
const CUES_EXTRACTED = 0x02;
const OCR_DONE = 0x04;
const SEARCH_INDEXED = 0x08;
}
}
impl SubtitleIndexStatus {
#[inline(always)]
pub const fn new() -> Self {
Self::empty()
}
#[inline]
#[allow(dead_code)]
pub(crate) const fn fully_indexed_mask(requires_ocr: bool) -> Self {
let base =
Self::TRACKS_DISCOVERED.bits() | Self::CUES_EXTRACTED.bits() | Self::SEARCH_INDEXED.bits();
if requires_ocr {
Self::from_bits_truncate(base | Self::OCR_DONE.bits())
} else {
Self::from_bits_truncate(base)
}
}
#[inline]
#[allow(dead_code)]
pub(crate) fn is_fully_indexed(&self, requires_ocr: bool) -> bool {
self.contains(Self::fully_indexed_mask(requires_ocr))
}
}
impl Default for SubtitleIndexStatus {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn media_error_flags_bit_values() {
assert_eq!(MediaErrorFlags::VIDEO_ERROR.bits(), 0x0001);
assert_eq!(MediaErrorFlags::AUDIO_ERROR.bits(), 0x0002);
assert_eq!(MediaErrorFlags::SUBTITLE_ERROR.bits(), 0x0004);
}
#[test]
fn video_index_status_verified_vs_findit_proto() {
assert_eq!(VideoIndexStatus::PROBED.bits(), 0x01);
assert_eq!(VideoIndexStatus::SCENE_DETECTED.bits(), 0x02);
assert_eq!(VideoIndexStatus::KEYFRAME_EXTRACTED.bits(), 0x04);
assert_eq!(VideoIndexStatus::VLM_ANALYZED.bits(), 0x10);
assert_eq!(VideoIndexStatus::APPLE_VISION_ANALYZED.bits(), 0x20);
assert_eq!(VideoIndexStatus::TEXT_EMBEDDING_FINISHED.bits(), 0x40);
assert_eq!(VideoIndexStatus::SCENE_EMBEDDING_FINISHED.bits(), 0x80);
assert_ne!(
VideoIndexStatus::VLM_ANALYZED,
VideoIndexStatus::APPLE_VISION_ANALYZED
);
assert_ne!(
VideoIndexStatus::TEXT_EMBEDDING_FINISHED,
VideoIndexStatus::SCENE_EMBEDDING_FINISHED
);
}
#[test]
fn video_index_status_fully_indexed() {
let mask = VideoIndexStatus::fully_indexed_mask();
assert!(mask.is_fully_indexed());
assert_eq!(mask.bits() & 0x08, 0);
for bit in [
VideoIndexStatus::PROBED,
VideoIndexStatus::SCENE_DETECTED,
VideoIndexStatus::KEYFRAME_EXTRACTED,
VideoIndexStatus::VLM_ANALYZED,
VideoIndexStatus::APPLE_VISION_ANALYZED,
VideoIndexStatus::TEXT_EMBEDDING_FINISHED,
VideoIndexStatus::SCENE_EMBEDDING_FINISHED,
] {
assert!(
mask.contains(bit),
"fully_indexed_mask must contain {bit:?}"
);
}
assert!(!VideoIndexStatus::empty().is_fully_indexed());
}
#[test]
fn subtitle_fully_indexed_mask_branches_on_requires_ocr() {
let text_mask = SubtitleIndexStatus::fully_indexed_mask(false);
assert_eq!(text_mask.bits(), 0x0B); assert!(!text_mask.contains(SubtitleIndexStatus::OCR_DONE));
let img_mask = SubtitleIndexStatus::fully_indexed_mask(true);
assert_eq!(img_mask.bits(), 0x0F); assert!(img_mask.contains(SubtitleIndexStatus::OCR_DONE));
let text_status = SubtitleIndexStatus::TRACKS_DISCOVERED
| SubtitleIndexStatus::CUES_EXTRACTED
| SubtitleIndexStatus::SEARCH_INDEXED;
assert!(text_status.is_fully_indexed(false));
assert!(!text_status.is_fully_indexed(true));
}
#[test]
fn audio_index_status_verified_11_bit_processing_stage() {
assert_eq!(AudioIndexStatus::EXTRACTED.bits(), 0x001);
assert_eq!(AudioIndexStatus::CLASSIFIED.bits(), 0x002);
assert_eq!(AudioIndexStatus::VAD_DONE.bits(), 0x004);
assert_eq!(AudioIndexStatus::STT_DONE.bits(), 0x008);
assert_eq!(AudioIndexStatus::SPEAKER_DONE.bits(), 0x010);
assert_eq!(AudioIndexStatus::LLM_DONE.bits(), 0x020);
assert_eq!(AudioIndexStatus::TEXT_EMBED.bits(), 0x040);
assert_eq!(AudioIndexStatus::CED_DONE.bits(), 0x080);
assert_eq!(AudioIndexStatus::CLAP_DONE.bits(), 0x100);
assert_eq!(AudioIndexStatus::EBUR128_DONE.bits(), 0x200);
assert_eq!(AudioIndexStatus::FPRINT_DONE.bits(), 0x400);
assert_eq!(AudioIndexStatus::fully_indexed_mask().bits(), 0x7FF);
}
#[test]
fn subtitle_index_status_verified_names_and_bits() {
assert_eq!(SubtitleIndexStatus::TRACKS_DISCOVERED.bits(), 0x01);
assert_eq!(SubtitleIndexStatus::CUES_EXTRACTED.bits(), 0x02);
assert_eq!(SubtitleIndexStatus::OCR_DONE.bits(), 0x04);
assert_eq!(SubtitleIndexStatus::SEARCH_INDEXED.bits(), 0x08);
assert_eq!(SubtitleIndexStatus::fully_indexed_mask(true).bits(), 0x0F);
assert_eq!(SubtitleIndexStatus::fully_indexed_mask(false).bits(), 0x0B);
}
#[test]
fn bitflags_default_is_empty() {
assert_eq!(VideoIndexStatus::default(), VideoIndexStatus::empty());
assert_eq!(AudioIndexStatus::default(), AudioIndexStatus::empty());
assert_eq!(SubtitleIndexStatus::default(), SubtitleIndexStatus::empty());
assert_eq!(MediaErrorFlags::default(), MediaErrorFlags::empty());
}
}