feat(entities): beat-entities
This commit is contained in:
11
crates/beat-entities/Cargo.toml
Normal file
11
crates/beat-entities/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "beat-entities"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
18
crates/beat-entities/src/beat.rs
Normal file
18
crates/beat-entities/src/beat.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
//! A contiguous segment of video bound by a single activity and stable cast.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Beat {
|
||||
pub index: usize,
|
||||
/// Start time in seconds from the beginning of the video.
|
||||
pub start_seconds: f64,
|
||||
/// End time in seconds from the beginning of the video.
|
||||
pub end_seconds: f64,
|
||||
/// Characters present in this beat.
|
||||
pub characters: Vec<String>,
|
||||
/// Primary activity taking place.
|
||||
pub activity: String,
|
||||
/// Detailed description of the beat.
|
||||
pub description: String,
|
||||
}
|
||||
95
crates/beat-entities/src/config.rs
Normal file
95
crates/beat-entities/src/config.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
//! Configuration types for beat.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Top-level configuration loaded from TOML.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
/// Folders to watch for video files.
|
||||
pub watch: Vec<String>,
|
||||
/// Output formats to produce.
|
||||
#[serde(default = "default_formats")]
|
||||
pub formats: Vec<OutputFormat>,
|
||||
/// Where to write sidecar outputs. "alongside" or an absolute path.
|
||||
#[serde(default = "default_output_dir")]
|
||||
pub output_dir: String,
|
||||
/// LLM endpoint configuration.
|
||||
pub llm: LlmConfig,
|
||||
/// Segmentation tuning knobs.
|
||||
#[serde(default)]
|
||||
pub segmentation: SegmentationConfig,
|
||||
}
|
||||
|
||||
fn default_formats() -> Vec<OutputFormat> {
|
||||
vec![OutputFormat::Json, OutputFormat::Markdown]
|
||||
}
|
||||
|
||||
fn default_output_dir() -> String {
|
||||
"alongside".into()
|
||||
}
|
||||
|
||||
/// Output format for sidecar files.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OutputFormat {
|
||||
Json,
|
||||
Markdown,
|
||||
}
|
||||
|
||||
/// LLM API configuration.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct LlmConfig {
|
||||
/// Standard OpenAI-compatible or Anthropic Messages-compatible endpoint.
|
||||
pub endpoint: String,
|
||||
/// Model identifier.
|
||||
pub model: String,
|
||||
/// Maximum concurrent requests to the LLM.
|
||||
#[serde(default = "default_max_concurrent")]
|
||||
pub max_concurrent_requests: usize,
|
||||
}
|
||||
|
||||
fn default_max_concurrent() -> usize {
|
||||
4
|
||||
}
|
||||
|
||||
/// Segmentation tuning knobs.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SegmentationConfig {
|
||||
/// Minimum duration of a beat in seconds.
|
||||
#[serde(default = "default_min_beat_seconds")]
|
||||
pub min_beat_seconds: f64,
|
||||
/// Sensitivity for boundary detection.
|
||||
#[serde(default)]
|
||||
pub sensitivity: Sensitivity,
|
||||
/// Frame sampling rate for analysis (frames per second).
|
||||
#[serde(default = "default_sample_fps")]
|
||||
pub sample_fps: u32,
|
||||
}
|
||||
|
||||
impl Default for SegmentationConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
min_beat_seconds: default_min_beat_seconds(),
|
||||
sensitivity: Sensitivity::Medium,
|
||||
sample_fps: default_sample_fps(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_min_beat_seconds() -> f64 {
|
||||
2.0
|
||||
}
|
||||
|
||||
fn default_sample_fps() -> u32 {
|
||||
3
|
||||
}
|
||||
|
||||
/// Sensitivity level for beat boundary detection.
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Sensitivity {
|
||||
Low,
|
||||
#[default]
|
||||
Medium,
|
||||
High,
|
||||
}
|
||||
22
crates/beat-entities/src/error.rs
Normal file
22
crates/beat-entities/src/error.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
//! Domain errors for beat.
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// Failed to decode video or audio.
|
||||
#[error("media error: {0}")]
|
||||
Media(#[from] std::io::Error),
|
||||
|
||||
/// LLM API request failed.
|
||||
#[error("llm error: {0}")]
|
||||
Llm(String),
|
||||
|
||||
/// Configuration error.
|
||||
#[error("config error: {0}")]
|
||||
Config(String),
|
||||
|
||||
/// Segmentation pipeline error.
|
||||
#[error("segmentation error: {0}")]
|
||||
Segmentation(String),
|
||||
}
|
||||
15
crates/beat-entities/src/lib.rs
Normal file
15
crates/beat-entities/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
//! Domain types for beat.
|
||||
|
||||
pub mod beat;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod llm;
|
||||
pub mod video;
|
||||
|
||||
pub use beat::Beat;
|
||||
pub use config::{Config, LlmConfig, OutputFormat, SegmentationConfig, Sensitivity};
|
||||
pub use error::Error;
|
||||
pub use video::VideoMetadata;
|
||||
|
||||
/// Result type alias for beat operations.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
49
crates/beat-entities/src/llm.rs
Normal file
49
crates/beat-entities/src/llm.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
//! LLM request/response types.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A request to describe a beat from representative frames.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DescribeBeatRequest {
|
||||
/// Base64-encoded representative frames from the beat.
|
||||
pub frames: Vec<Vec<u8>>,
|
||||
/// Start time of the beat in seconds (for context).
|
||||
pub start_seconds: f64,
|
||||
/// End time of the beat in seconds (for context).
|
||||
pub end_seconds: f64,
|
||||
}
|
||||
|
||||
/// LLM response for a single beat description.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DescribeBeatResponse {
|
||||
/// Identified characters in the beat.
|
||||
pub characters: Vec<String>,
|
||||
/// Primary activity taking place.
|
||||
pub activity: String,
|
||||
/// Detailed description of the beat.
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
/// A request to summarise an entire video from its beats.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SummarizeRequest {
|
||||
/// Descriptions of all beats in order.
|
||||
pub beats: Vec<BeatSummary>,
|
||||
}
|
||||
|
||||
/// Condensed beat info for summary context.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct BeatSummary {
|
||||
pub index: usize,
|
||||
pub start_seconds: f64,
|
||||
pub end_seconds: f64,
|
||||
pub characters: Vec<String>,
|
||||
pub activity: String,
|
||||
}
|
||||
|
||||
/// LLM response for video-level summary.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SummarizeResponse {
|
||||
/// Overview of the entire video.
|
||||
pub summary: String,
|
||||
}
|
||||
20
crates/beat-entities/src/video.rs
Normal file
20
crates/beat-entities/src/video.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
//! Video metadata types.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Metadata about a source video file.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct VideoMetadata {
|
||||
/// Path or filename of the source video.
|
||||
pub source: String,
|
||||
/// Total duration in seconds.
|
||||
pub duration_seconds: f64,
|
||||
/// Width in pixels.
|
||||
pub width: u32,
|
||||
/// Height in pixels.
|
||||
pub height: u32,
|
||||
/// Video codec name (e.g., "h264").
|
||||
pub video_codec: String,
|
||||
/// Audio codec name, if present (e.g., "aac").
|
||||
pub audio_codec: Option<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user