add domain concepts: FillStrategy variants, Chapter, InterstitialRule, MidRollRule, gap_filler
- FillStrategy: +Alternating (round-robin series), +Weighted (fresh-first), +Marathon (always ep1, loops) - Chapter value object on MediaItem (JSON column in library_items) - InterstitialRule + MidRollRule on ProgrammingBlock (data model only) - gap_filler: Option<MediaFilter> on Channel (persisted as JSON column) - migration: 20260712000002_add_chapters_and_gap_filler.sql
This commit is contained in:
@@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::value_objects::{
|
||||
AccessMode, BlockId, ChannelId, FillStrategy, LogoPosition, MediaFilter, MediaItemId,
|
||||
RotationPolicy, UserId, Weekday,
|
||||
AccessMode, BlockId, ChannelId, FillStrategy, InterstitialRule, LogoPosition, MediaFilter,
|
||||
MediaItemId, MidRollRule, RotationPolicy, UserId, Weekday,
|
||||
};
|
||||
|
||||
const SECONDS_IN_DAY: u32 = 86_400;
|
||||
@@ -29,6 +29,8 @@ pub struct Channel {
|
||||
webhook_poll_interval_secs: u32,
|
||||
webhook_body_template: Option<String>,
|
||||
webhook_headers: Option<String>,
|
||||
#[serde(default)]
|
||||
gap_filler: Option<MediaFilter>,
|
||||
created_at: DateTime<Utc>,
|
||||
updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -50,6 +52,7 @@ pub struct ChannelRow {
|
||||
pub webhook_poll_interval_secs: u32,
|
||||
pub webhook_body_template: Option<String>,
|
||||
pub webhook_headers: Option<String>,
|
||||
pub gap_filler: Option<MediaFilter>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -78,6 +81,7 @@ impl Channel {
|
||||
webhook_poll_interval_secs: DEFAULT_WEBHOOK_POLL_INTERVAL_SECS,
|
||||
webhook_body_template: None,
|
||||
webhook_headers: None,
|
||||
gap_filler: None,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
@@ -101,6 +105,7 @@ impl Channel {
|
||||
webhook_poll_interval_secs: row.webhook_poll_interval_secs,
|
||||
webhook_body_template: row.webhook_body_template,
|
||||
webhook_headers: row.webhook_headers,
|
||||
gap_filler: row.gap_filler,
|
||||
created_at: row.created_at,
|
||||
updated_at: row.updated_at,
|
||||
}
|
||||
@@ -207,6 +212,15 @@ impl Channel {
|
||||
self.auto_schedule = enabled;
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
|
||||
pub fn gap_filler(&self) -> Option<&MediaFilter> {
|
||||
self.gap_filler.as_ref()
|
||||
}
|
||||
|
||||
pub fn set_gap_filler(&mut self, gap_filler: Option<MediaFilter>) {
|
||||
self.gap_filler = gap_filler;
|
||||
self.updated_at = Utc::now();
|
||||
}
|
||||
}
|
||||
|
||||
// deny_unknown_fields required so #[serde(untagged)] compat enum correctly rejects V1 payloads
|
||||
@@ -324,6 +338,12 @@ pub struct ProgrammingBlock {
|
||||
|
||||
#[serde(default)]
|
||||
ignore_rotation_policy: bool,
|
||||
|
||||
#[serde(default)]
|
||||
interstitial_rule: Option<InterstitialRule>,
|
||||
|
||||
#[serde(default)]
|
||||
mid_roll_rule: Option<MidRollRule>,
|
||||
}
|
||||
|
||||
impl ProgrammingBlock {
|
||||
@@ -345,6 +365,8 @@ impl ProgrammingBlock {
|
||||
},
|
||||
loop_on_finish: true,
|
||||
ignore_rotation_policy: false,
|
||||
interstitial_rule: None,
|
||||
mid_roll_rule: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +386,8 @@ impl ProgrammingBlock {
|
||||
},
|
||||
loop_on_finish: true,
|
||||
ignore_rotation_policy: false,
|
||||
interstitial_rule: None,
|
||||
mid_roll_rule: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,6 +418,14 @@ impl ProgrammingBlock {
|
||||
pub fn ignore_rotation_policy(&self) -> bool {
|
||||
self.ignore_rotation_policy
|
||||
}
|
||||
|
||||
pub fn interstitial_rule(&self) -> Option<&InterstitialRule> {
|
||||
self.interstitial_rule.as_ref()
|
||||
}
|
||||
|
||||
pub fn mid_roll_rule(&self) -> Option<&MidRollRule> {
|
||||
self.mid_roll_rule.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user