feat(entities): beat-entities

This commit is contained in:
2026-05-29 08:42:28 +03:00
parent ee4383c3b1
commit 85d6e21ebd
7 changed files with 230 additions and 0 deletions

View 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 }

View 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,
}

View 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,
}

View 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),
}

View 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>;

View 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,
}

View 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>,
}