mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-11-26 00:51:00 +00:00
Rewrote Lines
This commit is contained in:
parent
3721106795
commit
273c0990dc
3 changed files with 72 additions and 78 deletions
109
src/line.rs
109
src/line.rs
|
@ -1,87 +1,74 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::tags;
|
use crate::tags;
|
||||||
use crate::types::SingleLineString;
|
use crate::types::SingleLineString;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Lines<'a> {
|
pub struct Lines(Vec<Line>);
|
||||||
input: &'a str,
|
|
||||||
|
impl Lines {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
}
|
}
|
||||||
impl<'a> Lines<'a> {
|
|
||||||
pub const fn new(input: &'a str) -> Self {
|
|
||||||
Lines { input }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_line(&mut self) -> crate::Result<Line<'a>> {
|
impl FromStr for Lines {
|
||||||
let mut end = self.input.len();
|
type Err = Error;
|
||||||
let mut next_start = self.input.len();
|
|
||||||
let mut adjust = 0;
|
|
||||||
let mut next_line_of_ext_x_stream_inf = false;
|
|
||||||
|
|
||||||
for (i, c) in self.input.char_indices() {
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
match c {
|
let mut result = Lines::new();
|
||||||
'\n' => {
|
|
||||||
if !next_line_of_ext_x_stream_inf
|
for line in input.lines() {
|
||||||
&& self.input.starts_with(tags::ExtXStreamInf::PREFIX)
|
// ignore empty lines
|
||||||
{
|
if line.trim().len() == 0 {
|
||||||
next_line_of_ext_x_stream_inf = true;
|
|
||||||
adjust = 0;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
next_start = i + 1;
|
|
||||||
end = i - adjust;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
'\r' => {
|
|
||||||
adjust = 1;
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if c.is_control() {
|
|
||||||
return Err(Error::invalid_input());
|
|
||||||
}
|
|
||||||
adjust = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let raw_line = &self.input[..end];
|
|
||||||
|
|
||||||
let line = if raw_line.is_empty() {
|
let pline = {
|
||||||
Line::Blank
|
if line.starts_with("#EXT") {
|
||||||
} else if raw_line.starts_with("#EXT") {
|
Line::Tag(line.parse()?)
|
||||||
Line::Tag((raw_line.parse())?)
|
} else if line.starts_with("#") {
|
||||||
} else if raw_line.starts_with('#') {
|
continue; // ignore comments
|
||||||
Line::Comment(raw_line)
|
|
||||||
} else {
|
} else {
|
||||||
let uri = SingleLineString::new(raw_line)?;
|
Line::Uri(SingleLineString::new(line)?)
|
||||||
Line::Uri(uri)
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.input = &self.input[next_start..];
|
result.push(pline);
|
||||||
Ok(line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> Iterator for Lines<'a> {
|
|
||||||
type Item = crate::Result<Line<'a>>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.input.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.read_line() {
|
Ok(result)
|
||||||
Err(e) => Some(Err(e)),
|
}
|
||||||
Ok(line) => Some(Ok(line)),
|
}
|
||||||
}
|
|
||||||
|
impl IntoIterator for Lines {
|
||||||
|
type Item = Line;
|
||||||
|
type IntoIter = ::std::vec::IntoIter<Line>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Lines {
|
||||||
|
type Target = Vec<Line>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Lines {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Line<'a> {
|
pub enum Line {
|
||||||
Blank,
|
|
||||||
Comment(&'a str),
|
|
||||||
Tag(Tag),
|
Tag(Tag),
|
||||||
Uri(SingleLineString),
|
Uri(SingleLineString),
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::tags::{
|
||||||
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion, MasterPlaylistTag,
|
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion, MasterPlaylistTag,
|
||||||
};
|
};
|
||||||
use crate::types::{ClosedCaptions, MediaType, ProtocolVersion};
|
use crate::types::{ClosedCaptions, MediaType, ProtocolVersion};
|
||||||
use crate::{Error, Result};
|
use crate::Error;
|
||||||
|
|
||||||
/// Master playlist builder.
|
/// Master playlist builder.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -69,7 +69,7 @@ impl MasterPlaylistBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a `MasterPlaylist` instance.
|
/// Builds a `MasterPlaylist` instance.
|
||||||
pub fn finish(self) -> Result<MasterPlaylist> {
|
pub fn finish(self) -> crate::Result<MasterPlaylist> {
|
||||||
let required_version = self.required_version();
|
let required_version = self.required_version();
|
||||||
let specified_version = self.version.unwrap_or(required_version);
|
let specified_version = self.version.unwrap_or(required_version);
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ impl MasterPlaylistBuilder {
|
||||||
.expect("Never fails")
|
.expect("Never fails")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_stream_inf_tags(&self) -> Result<()> {
|
fn validate_stream_inf_tags(&self) -> crate::Result<()> {
|
||||||
let mut has_none_closed_captions = false;
|
let mut has_none_closed_captions = false;
|
||||||
for t in &self.stream_inf_tags {
|
for t in &self.stream_inf_tags {
|
||||||
if let Some(group_id) = t.audio() {
|
if let Some(group_id) = t.audio() {
|
||||||
|
@ -159,7 +159,7 @@ impl MasterPlaylistBuilder {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_i_frame_stream_inf_tags(&self) -> Result<()> {
|
fn validate_i_frame_stream_inf_tags(&self) -> crate::Result<()> {
|
||||||
for t in &self.i_frame_stream_inf_tags {
|
for t in &self.i_frame_stream_inf_tags {
|
||||||
if let Some(group_id) = t.video() {
|
if let Some(group_id) = t.video() {
|
||||||
if !self.check_media_group(MediaType::Video, group_id) {
|
if !self.check_media_group(MediaType::Video, group_id) {
|
||||||
|
@ -171,7 +171,7 @@ impl MasterPlaylistBuilder {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_session_data_tags(&self) -> Result<()> {
|
fn validate_session_data_tags(&self) -> crate::Result<()> {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
for t in &self.session_data_tags {
|
for t in &self.session_data_tags {
|
||||||
if !set.insert((t.data_id(), t.language())) {
|
if !set.insert((t.data_id(), t.language())) {
|
||||||
|
@ -182,7 +182,7 @@ impl MasterPlaylistBuilder {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_session_key_tags(&self) -> Result<()> {
|
fn validate_session_key_tags(&self) -> crate::Result<()> {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
for t in &self.session_key_tags {
|
for t in &self.session_key_tags {
|
||||||
if !set.insert(t.key()) {
|
if !set.insert(t.key()) {
|
||||||
|
@ -294,11 +294,11 @@ impl fmt::Display for MasterPlaylist {
|
||||||
|
|
||||||
impl FromStr for MasterPlaylist {
|
impl FromStr for MasterPlaylist {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
|
||||||
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
let mut builder = MasterPlaylistBuilder::new();
|
let mut builder = MasterPlaylistBuilder::new();
|
||||||
for (i, line) in Lines::new(s).enumerate() {
|
for (i, line) in input.parse::<Lines>()?.into_iter().enumerate() {
|
||||||
match (line)? {
|
match line {
|
||||||
Line::Blank | Line::Comment(_) => {}
|
|
||||||
Line::Tag(tag) => {
|
Line::Tag(tag) => {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if tag != Tag::ExtM3u(ExtM3u) {
|
if tag != Tag::ExtM3u(ExtM3u) {
|
||||||
|
@ -373,3 +373,11 @@ impl FromStr for MasterPlaylist {
|
||||||
builder.finish()
|
builder.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parser() {}
|
||||||
|
}
|
||||||
|
|
|
@ -337,9 +337,8 @@ impl MediaPlaylistOptions {
|
||||||
let mut segment = MediaSegmentBuilder::new();
|
let mut segment = MediaSegmentBuilder::new();
|
||||||
let mut has_partial_segment = false;
|
let mut has_partial_segment = false;
|
||||||
let mut has_discontinuity_tag = false;
|
let mut has_discontinuity_tag = false;
|
||||||
for (i, line) in Lines::new(m3u8).enumerate() {
|
for (i, line) in m3u8.parse::<Lines>()?.into_iter().enumerate() {
|
||||||
match (line)? {
|
match line {
|
||||||
Line::Blank | Line::Comment(_) => {}
|
|
||||||
Line::Tag(tag) => {
|
Line::Tag(tag) => {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
if tag != Tag::ExtM3u(ExtM3u) {
|
if tag != Tag::ExtM3u(ExtM3u) {
|
||||||
|
|
Loading…
Reference in a new issue