1pub mod error;
13pub mod mode;
14pub mod provider;
15pub mod template;
16
17pub use error::CatchupError;
18pub use mode::{CatchupConfig, CatchupMode, IGNORE_CATCHUP_DAYS, configure_catchup};
19pub use template::{
20 format_catchup_url, format_catchup_url_with_granularity, format_now_only,
21 is_within_catchup_window,
22};
23
24use chrono::Utc;
25
26pub fn process_programme_for_timeshift(
39 config: &CatchupConfig,
40 programme_start: i64,
41 programme_end: i64,
42 programme_catchup_id: Option<&str>,
43 timezone_shift_secs: i32,
44) -> String {
45 let mut duration = programme_end - programme_start;
46
47 let now = Utc::now().timestamp();
49 if programme_start + duration > now {
50 duration = now - programme_start;
51 }
52 if duration < 0 {
53 duration = 0;
54 }
55
56 format_catchup_url_with_granularity(
57 &config.source,
58 programme_start,
59 duration,
60 programme_catchup_id,
61 timezone_shift_secs,
62 config.granularity_seconds,
63 )
64}
65
66pub fn process_programme_for_vod(config: &CatchupConfig, programme_catchup_id: &str) -> String {
77 let now = Utc::now().timestamp();
80 format_catchup_url_with_granularity(
81 &config.source,
82 now,
83 0,
84 Some(programme_catchup_id),
85 0,
86 config.granularity_seconds,
87 )
88}
89
90pub fn process_channel_for_live(config: &CatchupConfig) -> String {
100 format_now_only(&config.source, 0, 0, 0)
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use chrono::TimeZone;
107
108 fn fixed_start() -> i64 {
109 Utc.with_ymd_and_hms(2024, 3, 15, 14, 30, 45)
110 .unwrap()
111 .timestamp()
112 }
113
114 fn make_config(source: &str, granularity: i32) -> CatchupConfig {
115 CatchupConfig {
116 mode: CatchupMode::Default,
117 source: source.to_string(),
118 catchup_days: 7,
119 supports_timeshifting: true,
120 terminates: true,
121 granularity_seconds: granularity,
122 is_ts_stream: false,
123 }
124 }
125
126 #[test]
127 fn process_programme_for_timeshift_produces_correct_url() {
128 let start = fixed_start();
129 let end = start + 3600;
130 let config = make_config(
131 "http://example.com/catchup?start={utc}&end={utcend}&id={catchup-id}",
132 1,
133 );
134
135 let result = process_programme_for_timeshift(&config, start, end, Some("prog_123"), 0);
136
137 assert!(result.contains(&format!("start={start}")));
138 assert!(result.contains("id=prog_123"));
139 assert!(!result.contains("{utc}"));
141 assert!(!result.contains("{utcend}"));
142 assert!(!result.contains("{catchup-id}"));
143 }
144
145 #[test]
146 fn process_programme_for_vod_substitutes_catchup_id() {
147 let config = make_config("http://example.com/vod/{catchup-id}", 1);
148
149 let result = process_programme_for_vod(&config, "movie_456");
150
151 assert_eq!(result, "http://example.com/vod/movie_456");
152 }
153
154 #[test]
155 fn process_channel_for_live_uses_current_time() {
156 let config = make_config("http://example.com/live?now=${now}", 1);
157
158 let before = Utc::now().timestamp();
159 let result = process_channel_for_live(&config);
160 let after = Utc::now().timestamp();
161
162 let now_str = result.split("now=").nth(1).unwrap();
164 let now_val: i64 = now_str.parse().expect("should be a timestamp");
165 assert!(now_val >= before && now_val <= after);
166 }
167}