use crate::m3u8::playlist::Playlist;
use crate::m3u8::tags::{ByteRange, Key, Map, Tag};
use crate::m3u8::validation::ValidationError;
#[derive(Debug, Clone, Default)]
pub struct PlaylistBuilder {
tags: Vec<Tag>,
}
impl PlaylistBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn tag(mut self, tag: Tag) -> Self {
self.tags.push(tag);
self
}
pub fn uri(self, uri: &str) -> Self {
self.tag(Tag::Uri(uri.to_string()))
}
pub fn discontinuity(self) -> Self {
self.tag(Tag::ExtXDiscontinuity)
}
pub fn i_frames_only(self) -> Self {
self.tag(Tag::ExtXIFramesOnly)
}
pub fn date_range(self, id: &str, start_date: &str) -> Self {
self.tag(Tag::ExtXDateRange {
id: id.to_string(),
class: None,
start_date: start_date.to_string(),
cue: None,
end_date: None,
duration: None,
planned_duration: None,
end_on_next: None,
scte35_cmd: None,
scte35_out: None,
scte35_in: None,
extra_attributes: Vec::new(),
})
}
pub fn server_control(
self,
can_skip_until: Option<f64>,
part_hold_back: Option<f64>,
can_block_reload: bool,
) -> Self {
self.tag(Tag::ExtXServerControl {
can_skip_until,
can_skip_dateranges: None,
hold_back: None,
part_hold_back,
can_block_reload: can_block_reload.then_some(true),
})
}
pub fn part_inf(self, part_target_duration: f64) -> Self {
self.tag(Tag::ExtXPartInf {
part_target_duration,
})
}
pub fn part(self, uri: &str, duration: f64) -> Self {
self.tag(Tag::ExtXPart {
uri: uri.to_string(),
duration,
independent: None,
byterange: None,
gap: None,
})
}
pub fn preload_hint(self, type_: &str, uri: &str) -> Self {
self.tag(Tag::ExtXPreloadHint {
type_: type_.to_string(),
uri: uri.to_string(),
byterange_start: None,
byterange_length: None,
})
}
pub fn rendition_report(
self,
uri: &str,
last_msn: Option<u64>,
last_part: Option<u64>,
) -> Self {
self.tag(Tag::ExtXRenditionReport {
uri: uri.to_string(),
last_msn,
last_part,
})
}
pub fn skip(self, skipped_segments: u32) -> Self {
self.tag(Tag::ExtXSkip {
skipped_segments,
recently_removed_dateranges: None,
})
}
pub fn extm3u(self) -> Self {
self.tag(Tag::ExtM3U)
}
pub fn version(self, version: u8) -> Self {
self.tag(Tag::ExtXVersion(version))
}
pub fn extinf(self, url: &str, duration: f64, title: Option<String>) -> Self {
self.tag(Tag::ExtInf {
uri: url.to_string(),
duration,
title,
})
}
pub fn target_duration(self, duration: u64) -> Self {
self.tag(Tag::ExtXTargetDuration(duration))
}
pub fn media_sequence(self, sequence: u64) -> Self {
self.tag(Tag::ExtXMediaSequence(sequence))
}
pub fn discontinuity_sequence(self, sequence: u32) -> Self {
self.tag(Tag::ExtXDiscontinuitySequence(sequence))
}
pub fn end_list(self) -> Self {
self.tag(Tag::ExtXEndList)
}
pub fn key(
self,
method: &str,
uri: Option<&str>,
iv: Option<&str>,
keyformat: Option<&str>,
keyformatversions: Option<&str>,
) -> Self {
self.tag(Tag::ExtXKey(Key {
method: method.to_string(),
uri: uri.map(|s| s.to_string()),
iv: iv.map(|s| s.to_string()),
keyformat: keyformat.map(|s| s.to_string()),
keyformatversions: keyformatversions.map(|s| s.to_string()),
}))
}
pub fn map(self, uri: &str, byterange: Option<ByteRange>) -> Self {
self.tag(Tag::ExtXMap(Map {
uri: uri.to_string(),
byterange,
}))
}
pub fn program_date_time(self, date_time: &str) -> Self {
self.tag(Tag::ExtXProgramDateTime(date_time.to_string()))
}
pub fn gap(self) -> Self {
self.tag(Tag::ExtXGap)
}
pub fn byte_range(self, byterange: ByteRange) -> Self {
self.tag(Tag::ExtXByteRange(byterange))
}
pub fn define(self, value: &str) -> Self {
self.tag(Tag::ExtXDefine(value.to_string()))
}
#[allow(clippy::too_many_arguments)]
pub fn media(
self,
type_: &str,
group_id: &str,
name: Option<&str>,
uri: Option<&str>,
default: Option<bool>,
autoselect: Option<bool>,
characteristics: Option<&str>,
language: Option<&str>,
forced: Option<bool>,
language_codec: Option<&str>,
instream_id: Option<&str>,
) -> Self {
self.tag(Tag::ExtXMedia {
type_: type_.to_string(),
group_id: group_id.to_string(),
name: name.map(|s| s.to_string()),
uri: uri.map(|s| s.to_string()),
default,
autoselect,
characteristics: characteristics.map(|s| s.to_string()),
language: language.map(|s| s.to_string()),
instream_id: instream_id.map(|s| s.to_string()),
language_codec: language_codec.map(|s| s.to_string()),
forced,
channels: None,
extra_attributes: Vec::new(),
})
}
#[allow(clippy::too_many_arguments)]
pub fn stream_inf(
self,
bandwidth: u32,
average_bandwidth: Option<u32>,
codecs: Option<&str>,
resolution: Option<&str>,
frame_rate: Option<f64>,
audio: Option<&str>,
video: Option<&str>,
subtitle: Option<&str>,
closed_captions: Option<&str>,
) -> Self {
self.tag(Tag::ExtXStreamInf {
bandwidth,
average_bandwidth,
codecs: codecs.map(|s| s.to_string()),
resolution: resolution.map(|s| s.to_string()),
frame_rate,
audio: audio.map(|s| s.to_string()),
video: video.map(|s| s.to_string()),
subtitle: subtitle.map(|s| s.to_string()),
closed_captions: closed_captions.map(|s| s.to_string()),
extra_attributes: Vec::new(),
})
}
#[allow(clippy::too_many_arguments)]
pub fn iframe_stream_inf(
self,
bandwidth: u32,
average_bandwidth: Option<u32>,
codecs: Option<&str>,
resolution: Option<&str>,
frame_rate: Option<f64>,
uri: &str,
) -> Self {
self.tag(Tag::ExtXIFrameStreamInf {
bandwidth,
average_bandwidth,
codecs: codecs.map(|s| s.to_string()),
resolution: resolution.map(|s| s.to_string()),
frame_rate,
uri: uri.to_string(),
extra_attributes: Vec::new(),
})
}
pub fn bitrate(self, bitrate: u32) -> Self {
self.tag(Tag::ExtXBitrate(bitrate))
}
pub fn independent_segments(self) -> Self {
self.tag(Tag::ExtXIndependentSegments)
}
pub fn start(self, time_offset: f64, precise: Option<bool>) -> Self {
self.tag(Tag::ExtXStart {
time_offset,
precise,
})
}
pub fn session_data(self, id: &str, value: &str, language: Option<&str>) -> Self {
self.tag(Tag::ExtXSessionData {
id: id.to_string(),
value: Some(value.to_string()),
uri: None,
language: language.map(|s| s.to_string()),
})
}
pub fn session_key(self, method: &str, uri: Option<&str>, iv: Option<&str>) -> Self {
self.tag(Tag::ExtXSessionKey(Key {
method: method.to_string(),
uri: uri.map(|s| s.to_string()),
iv: iv.map(|s| s.to_string()),
keyformat: None,
keyformatversions: None,
}))
}
pub fn build(self) -> Result<Playlist, Vec<ValidationError>> {
let playlist = Playlist { tags: self.tags };
playlist.validate()?;
Ok(playlist)
}
pub fn playlist_type(self, playlist_type: &str) -> Self {
self.tag(Tag::ExtXPlaylistType(playlist_type.to_string()))
}
}