use crate::report::{Finding, Location, Report, Severity};
use alloc::vec::Vec;
pub fn check_playlist(text: &str, report: &mut Report) {
let lines: Vec<&str> = text.lines().collect();
let mut has_targetduration = false;
let mut targetduration_val: u64 = 0;
let mut extinf_line_nums: Vec<usize> = Vec::new();
let mut extinf_durations: Vec<f64> = Vec::new();
for (i, line) in lines.iter().enumerate() {
let line_num = i + 1; let trimmed = line.trim();
if trimmed.starts_with("#EXT-X-TARGETDURATION:") {
has_targetduration = true;
if let Some(val_str) = trimmed.strip_prefix("#EXT-X-TARGETDURATION:") {
targetduration_val = val_str.trim().parse::<u64>().unwrap_or(0);
}
}
if trimmed.starts_with("#EXTINF:") {
extinf_line_nums.push(line_num);
if let Some(dur_str) = trimmed.strip_prefix("#EXTINF:") {
let dur = dur_str.split(',').next().unwrap_or("0");
let parsed: f64 = dur.trim().parse().unwrap_or(0.0);
extinf_durations.push(parsed);
} else {
extinf_durations.push(0.0);
}
}
}
let first_non_empty = lines.iter().find(|l| !l.trim().is_empty());
match first_non_empty {
Some(line) if line.trim() == "#EXTM3U" => { }
_ => {
report.push(Finding::new(
Severity::Error,
Location::new(1, 0),
"hls-missing-extm3u",
"First non-empty line must be exactly '#EXTM3U'",
));
}
}
if !extinf_line_nums.is_empty() && !has_targetduration {
report.push(Finding::new(
Severity::Error,
Location::new(1, 0),
"hls-missing-targetduration",
"Media playlist with EXTINF entries must include #EXT-X-TARGETDURATION",
));
}
if has_targetduration {
for (idx, &dur) in extinf_durations.iter().enumerate() {
let rounded = (dur + 0.5) as u64;
if rounded > targetduration_val {
report.push(Finding::new(
Severity::Error,
Location::new(extinf_line_nums[idx], 0),
"hls-extinf-exceeds-target",
alloc::format!(
"EXTINF duration {dur} (rounded to {rounded}) exceeds TARGETDURATION {targetduration_val}"
),
));
}
}
}
for (i, line) in lines.iter().enumerate() {
let trimmed = line.trim();
if trimmed.starts_with("#EXT-X-DATERANGE:")
&& timed_metadata::DateRange::parse_tag_line(trimmed).is_err()
{
report.push(Finding::new(
Severity::Error,
Location::new(i + 1, 0),
"hls-malformed-daterange",
"Malformed #EXT-X-DATERANGE line",
));
}
}
}