mirror of
https://github.com/rutgersc/m3u8-rs.git
synced 2025-01-10 22:55:25 +00:00
Parse PROGRAM-DATE-TIME and DATERANGE start/end as proper datetimes instead of strings
This commit is contained in:
parent
7247e02ee5
commit
bd7cce75e9
4 changed files with 46 additions and 27 deletions
|
@ -11,6 +11,7 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nom = { version = "7", optional = true }
|
nom = { version = "7", optional = true }
|
||||||
|
chrono = { version = "0.4" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["parser"]
|
default = ["parser"]
|
||||||
|
|
|
@ -484,7 +484,7 @@ enum SegmentTag {
|
||||||
Discontinuity,
|
Discontinuity,
|
||||||
Key(Key),
|
Key(Key),
|
||||||
Map(Map),
|
Map(Map),
|
||||||
ProgramDateTime(String),
|
ProgramDateTime(chrono::DateTime<chrono::FixedOffset>),
|
||||||
DateRange(DateRange),
|
DateRange(DateRange),
|
||||||
Unknown(ExtTag),
|
Unknown(ExtTag),
|
||||||
Comment(String),
|
Comment(String),
|
||||||
|
@ -509,8 +509,8 @@ fn media_segment_tag(i: &[u8]) -> IResult<&[u8], SegmentTag> {
|
||||||
SegmentTag::Map(map)
|
SegmentTag::Map(map)
|
||||||
}),
|
}),
|
||||||
map(
|
map(
|
||||||
pair(tag("#EXT-X-PROGRAM-DATE-TIME:"), consume_line),
|
pair(tag("#EXT-X-PROGRAM-DATE-TIME:"), program_date_time),
|
||||||
|(_, line)| SegmentTag::ProgramDateTime(line),
|
|(_, pdt)| SegmentTag::ProgramDateTime(pdt),
|
||||||
),
|
),
|
||||||
map(pair(tag("#EXT-X-DATERANGE:"), daterange), |(_, range)| {
|
map(pair(tag("#EXT-X-DATERANGE:"), daterange), |(_, range)| {
|
||||||
SegmentTag::DateRange(range)
|
SegmentTag::DateRange(range)
|
||||||
|
@ -538,6 +538,10 @@ fn key(i: &[u8]) -> IResult<&[u8], Key> {
|
||||||
map_res(key_value_pairs, Key::from_hashmap)(i)
|
map_res(key_value_pairs, Key::from_hashmap)(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn program_date_time(i: &[u8]) -> IResult<&[u8], chrono::DateTime<chrono::FixedOffset>> {
|
||||||
|
map_res(consume_line, |s| chrono::DateTime::parse_from_rfc3339(&s))(i)
|
||||||
|
}
|
||||||
|
|
||||||
fn daterange(i: &[u8]) -> IResult<&[u8], DateRange> {
|
fn daterange(i: &[u8]) -> IResult<&[u8], DateRange> {
|
||||||
map_res(key_value_pairs, DateRange::from_hashmap)(i)
|
map_res(key_value_pairs, DateRange::from_hashmap)(i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -843,7 +843,7 @@ pub struct MediaSegment {
|
||||||
/// `#EXT-X-MAP:<attribute-list>`
|
/// `#EXT-X-MAP:<attribute-list>`
|
||||||
pub map: Option<Map>,
|
pub map: Option<Map>,
|
||||||
/// `#EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ>`
|
/// `#EXT-X-PROGRAM-DATE-TIME:<YYYY-MM-DDThh:mm:ssZ>`
|
||||||
pub program_date_time: Option<String>,
|
pub program_date_time: Option<chrono::DateTime<chrono::FixedOffset>>,
|
||||||
/// `#EXT-X-DATERANGE:<attribute-list>`
|
/// `#EXT-X-DATERANGE:<attribute-list>`
|
||||||
pub daterange: Option<DateRange>,
|
pub daterange: Option<DateRange>,
|
||||||
/// `#EXT-`
|
/// `#EXT-`
|
||||||
|
@ -875,7 +875,7 @@ impl MediaSegment {
|
||||||
writeln!(w)?;
|
writeln!(w)?;
|
||||||
}
|
}
|
||||||
if let Some(ref v) = self.program_date_time {
|
if let Some(ref v) = self.program_date_time {
|
||||||
writeln!(w, "#EXT-X-PROGRAM-DATE-TIME:{}", v)?;
|
writeln!(w, "#EXT-X-PROGRAM-DATE-TIME:{}", v.to_rfc3339())?;
|
||||||
}
|
}
|
||||||
if let Some(ref v) = self.daterange {
|
if let Some(ref v) = self.daterange {
|
||||||
write!(w, "#EXT-X-DATERANGE:")?;
|
write!(w, "#EXT-X-DATERANGE:")?;
|
||||||
|
@ -1042,12 +1042,12 @@ impl ByteRange {
|
||||||
/// The EXT-X-DATERANGE tag associates a Date Range (i.e. a range of time
|
/// The EXT-X-DATERANGE tag associates a Date Range (i.e. a range of time
|
||||||
/// defined by a starting and ending date) with a set of attribute /
|
/// defined by a starting and ending date) with a set of attribute /
|
||||||
/// value pairs.
|
/// value pairs.
|
||||||
#[derive(Debug, Default, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct DateRange {
|
pub struct DateRange {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub class: Option<String>,
|
pub class: Option<String>,
|
||||||
pub start_date: String,
|
pub start_date: chrono::DateTime<chrono::FixedOffset>,
|
||||||
pub end_date: Option<String>,
|
pub end_date: Option<chrono::DateTime<chrono::FixedOffset>>,
|
||||||
pub duration: Option<f64>,
|
pub duration: Option<f64>,
|
||||||
pub planned_duration: Option<f64>,
|
pub planned_duration: Option<f64>,
|
||||||
pub x_prefixed: Option<HashMap<String, QuotedOrUnquoted>>, // X-<client-attribute>
|
pub x_prefixed: Option<HashMap<String, QuotedOrUnquoted>>, // X-<client-attribute>
|
||||||
|
@ -1060,10 +1060,13 @@ impl DateRange {
|
||||||
let id = quoted_string!(attrs, "ID")
|
let id = quoted_string!(attrs, "ID")
|
||||||
.ok_or_else(|| String::from("EXT-X-DATERANGE without mandatory ID attribute"))?;
|
.ok_or_else(|| String::from("EXT-X-DATERANGE without mandatory ID attribute"))?;
|
||||||
let class = quoted_string!(attrs, "CLASS");
|
let class = quoted_string!(attrs, "CLASS");
|
||||||
let start_date = quoted_string!(attrs, "START-DATE").ok_or_else(|| {
|
let start_date =
|
||||||
|
quoted_string_parse!(attrs, "START-DATE", chrono::DateTime::parse_from_rfc3339)
|
||||||
|
.ok_or_else(|| {
|
||||||
String::from("EXT-X-DATERANGE without mandatory START-DATE attribute")
|
String::from("EXT-X-DATERANGE without mandatory START-DATE attribute")
|
||||||
})?;
|
})?;
|
||||||
let end_date = quoted_string!(attrs, "END-DATE");
|
let end_date =
|
||||||
|
quoted_string_parse!(attrs, "END-DATE", chrono::DateTime::parse_from_rfc3339);
|
||||||
let duration = unquoted_string_parse!(attrs, "DURATION", |s: &str| s
|
let duration = unquoted_string_parse!(attrs, "DURATION", |s: &str| s
|
||||||
.parse::<f64>()
|
.parse::<f64>()
|
||||||
.map_err(|err| format!("Failed to parse DURATION attribute: {}", err)));
|
.map_err(|err| format!("Failed to parse DURATION attribute: {}", err)));
|
||||||
|
@ -1105,8 +1108,12 @@ impl DateRange {
|
||||||
pub fn write_attributes_to<T: Write>(&self, w: &mut T) -> std::io::Result<()> {
|
pub fn write_attributes_to<T: Write>(&self, w: &mut T) -> std::io::Result<()> {
|
||||||
write_some_attribute_quoted!(w, "ID", &Some(&self.id))?;
|
write_some_attribute_quoted!(w, "ID", &Some(&self.id))?;
|
||||||
write_some_attribute_quoted!(w, ",CLASS", &self.class)?;
|
write_some_attribute_quoted!(w, ",CLASS", &self.class)?;
|
||||||
write_some_attribute_quoted!(w, ",START-DATE", &Some(&self.start_date))?;
|
write_some_attribute_quoted!(w, ",START-DATE", &Some(&self.start_date.to_rfc3339()))?;
|
||||||
write_some_attribute_quoted!(w, ",END-DATE", &self.end_date)?;
|
write_some_attribute_quoted!(
|
||||||
|
w,
|
||||||
|
",END-DATE",
|
||||||
|
&self.end_date.as_ref().map(|dt| dt.to_rfc3339())
|
||||||
|
)?;
|
||||||
write_some_attribute!(w, ",DURATION", &self.duration)?;
|
write_some_attribute!(w, ",DURATION", &self.duration)?;
|
||||||
write_some_attribute!(w, ",PLANNED-DURATION", &self.planned_duration)?;
|
write_some_attribute!(w, ",PLANNED-DURATION", &self.planned_duration)?;
|
||||||
if let Some(x_prefixed) = &self.x_prefixed {
|
if let Some(x_prefixed) = &self.x_prefixed {
|
||||||
|
|
11
tests/lib.rs
11
tests/lib.rs
|
@ -1,5 +1,6 @@
|
||||||
#![allow(unused_variables, unused_imports, dead_code)]
|
#![allow(unused_variables, unused_imports, dead_code)]
|
||||||
|
|
||||||
|
use chrono::prelude::*;
|
||||||
use m3u8_rs::QuotedOrUnquoted::Quoted;
|
use m3u8_rs::QuotedOrUnquoted::Quoted;
|
||||||
use m3u8_rs::*;
|
use m3u8_rs::*;
|
||||||
use nom::AsBytes;
|
use nom::AsBytes;
|
||||||
|
@ -356,11 +357,17 @@ fn create_and_parse_media_playlist_full() {
|
||||||
}),
|
}),
|
||||||
other_attributes: Default::default(),
|
other_attributes: Default::default(),
|
||||||
}),
|
}),
|
||||||
program_date_time: Some("broodlordinfestorgg".into()),
|
program_date_time: Some(
|
||||||
|
chrono::FixedOffset::east(8 * 3600)
|
||||||
|
.ymd(2010, 2, 19)
|
||||||
|
.and_hms_milli(14, 54, 23, 31),
|
||||||
|
),
|
||||||
daterange: Some(DateRange {
|
daterange: Some(DateRange {
|
||||||
id: "9999".into(),
|
id: "9999".into(),
|
||||||
class: Some("class".into()),
|
class: Some("class".into()),
|
||||||
start_date: "2018-08-22T21:54:00.079Z".into(),
|
start_date: chrono::FixedOffset::east(8 * 3600)
|
||||||
|
.ymd(2010, 2, 19)
|
||||||
|
.and_hms_milli(14, 54, 23, 31),
|
||||||
end_date: None,
|
end_date: None,
|
||||||
duration: None,
|
duration: None,
|
||||||
planned_duration: Some("40.000".parse().unwrap()),
|
planned_duration: Some("40.000".parse().unwrap()),
|
||||||
|
|
Loading…
Reference in a new issue