mirror of
https://github.com/rutgersc/m3u8-rs.git
synced 2025-03-14 02:02:39 +00:00
commit
53e9439660
10 changed files with 687 additions and 612 deletions
|
@ -1,15 +1,16 @@
|
|||
[package]
|
||||
name = "m3u8-rs"
|
||||
version = "2.1.0"
|
||||
version = "3.0.0"
|
||||
authors = ["Rutger"]
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rutgersc/m3u8-rs"
|
||||
description = "A library for parsing m3u8 files (Apple's HTTP Live Streaming (HLS) protocol)."
|
||||
documentation = "https://rutgersc.github.io/doc/m3u8_rs/index.html"
|
||||
license = "MIT"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
nom = { version = "5.1.0", optional = true }
|
||||
nom = { version = "7", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["parser"]
|
||||
|
|
|
@ -12,12 +12,6 @@ To use this library, add the following dependency to `Cargo.toml`:
|
|||
m3u8-rs = "1.0.6"
|
||||
```
|
||||
|
||||
And add the crate to `lib.rs`
|
||||
|
||||
```rust
|
||||
extern crate m3u8_rs;
|
||||
```
|
||||
|
||||
Also available on [crates.io](https://crates.io/crates/m3u8-rs)
|
||||
|
||||
# Documentation
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
extern crate m3u8_rs;
|
||||
extern crate nom;
|
||||
|
||||
use m3u8_rs::playlist::Playlist;
|
||||
use m3u8_rs::Playlist;
|
||||
use std::io::Read;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
extern crate m3u8_rs;
|
||||
extern crate nom;
|
||||
|
||||
use m3u8_rs::playlist::Playlist;
|
||||
use m3u8_rs::Playlist;
|
||||
use std::io::Read;
|
||||
|
||||
fn main() {
|
||||
|
@ -12,7 +9,7 @@ fn main() {
|
|||
let parsed = m3u8_rs::parse_playlist(&bytes);
|
||||
|
||||
let playlist = match parsed {
|
||||
Result::Ok((i, playlist)) => playlist,
|
||||
Result::Ok((_i, playlist)) => playlist,
|
||||
Result::Err(e) => panic!("Parsing error: \n{}", e),
|
||||
};
|
||||
|
||||
|
@ -22,6 +19,7 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn main_alt() {
|
||||
let mut file = std::fs::File::open("playlist.m3u8").unwrap();
|
||||
let mut bytes: Vec<u8> = Vec::new();
|
||||
|
@ -30,8 +28,8 @@ fn main_alt() {
|
|||
let parsed = m3u8_rs::parse_playlist(&bytes);
|
||||
|
||||
match parsed {
|
||||
Result::Ok((i, Playlist::MasterPlaylist(pl))) => println!("Master playlist:\n{:?}", pl),
|
||||
Result::Ok((i, Playlist::MediaPlaylist(pl))) => println!("Media playlist:\n{:?}", pl),
|
||||
Result::Ok((_i, Playlist::MasterPlaylist(pl))) => println!("Master playlist:\n{:?}", pl),
|
||||
Result::Ok((_i, Playlist::MediaPlaylist(pl))) => println!("Media playlist:\n{:?}", pl),
|
||||
Result::Err(e) => panic!("Parsing error: \n{}", e),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#EXTM3U
|
||||
#EXTINF:10,
|
||||
http://media.example.com/fileSequence7796.ts
|
||||
#EXTINF:6,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
# https://developer.apple.com/library/ios/technotes/tn2288/_index.html
|
||||
#
|
||||
#EXTM3U
|
||||
#EXT-X-TARGETDURATION:10
|
||||
#EXT-X-VERSION:3
|
||||
|
@ -12,4 +10,4 @@ ad1.ts
|
|||
#EXTINF:10.0,
|
||||
movieA.ts
|
||||
#EXTINF:10.0,
|
||||
movieB.ts
|
||||
movieB.ts
|
||||
|
|
72
src/lib.rs
72
src/lib.rs
|
@ -1,7 +1,73 @@
|
|||
#[path = "playlist.rs"]
|
||||
pub mod playlist;
|
||||
//! A library to parse m3u8 playlists [HTTP Live Streaming](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19).
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Parsing a playlist and let the parser figure out if it's a media or master playlist.
|
||||
//!
|
||||
//! ```
|
||||
//! use m3u8_rs::Playlist;
|
||||
//! use nom::IResult;
|
||||
//! use std::io::Read;
|
||||
//!
|
||||
//! let mut file = std::fs::File::open("playlist.m3u8").unwrap();
|
||||
//! let mut bytes: Vec<u8> = Vec::new();
|
||||
//! file.read_to_end(&mut bytes).unwrap();
|
||||
//!
|
||||
//! match m3u8_rs::parse_playlist(&bytes) {
|
||||
//! Result::Ok((i, Playlist::MasterPlaylist(pl))) => println!("Master playlist:\n{:?}", pl),
|
||||
//! Result::Ok((i, Playlist::MediaPlaylist(pl))) => println!("Media playlist:\n{:?}", pl),
|
||||
//! Result::Err(e) => panic!("Parsing error: \n{}", e),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Parsing a master playlist directly
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io::Read;
|
||||
//! use nom::IResult;
|
||||
//!
|
||||
//! let mut file = std::fs::File::open("masterplaylist.m3u8").unwrap();
|
||||
//! let mut bytes: Vec<u8> = Vec::new();
|
||||
//! file.read_to_end(&mut bytes).unwrap();
|
||||
//!
|
||||
//! if let Result::Ok((_, pl)) = m3u8_rs::parse_master_playlist(&bytes) {
|
||||
//! println!("{:?}", pl);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Creating a playlist and writing it back to a vec/file
|
||||
//!
|
||||
//! ```
|
||||
//! use m3u8_rs::{MediaPlaylist, MediaPlaylistType, MediaSegment};
|
||||
//!
|
||||
//! let playlist = MediaPlaylist {
|
||||
//! version: 6,
|
||||
//! target_duration: 3.0,
|
||||
//! media_sequence: 338559,
|
||||
//! discontinuity_sequence: 1234,
|
||||
//! end_list: true,
|
||||
//! playlist_type: Some(MediaPlaylistType::Vod),
|
||||
//! segments: vec![
|
||||
//! MediaSegment {
|
||||
//! uri: "20140311T113819-01-338559live.ts".into(),
|
||||
//! duration: 2.002,
|
||||
//! title: Some("title".into()),
|
||||
//! ..Default::default()
|
||||
//! },
|
||||
//! ],
|
||||
//! ..Default::default()
|
||||
//! };
|
||||
//!
|
||||
//! //let mut v: Vec<u8> = Vec::new();
|
||||
//! //playlist.write_to(&mut v).unwrap();
|
||||
//!
|
||||
//! //let mut file = std::fs::File::open("playlist.m3u8").unwrap();
|
||||
//! //playlist.write_to(&mut file).unwrap();
|
||||
//! ```
|
||||
|
||||
mod playlist;
|
||||
pub use playlist::*;
|
||||
|
||||
#[path = "parser.rs"]
|
||||
#[cfg(feature = "parser")]
|
||||
mod parser;
|
||||
|
||||
|
|
961
src/parser.rs
961
src/parser.rs
File diff suppressed because it is too large
Load diff
|
@ -65,8 +65,7 @@ impl Playlist {
|
|||
// Master Playlist
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
/// A [Master Playlist]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4)
|
||||
/// A [Master Playlist](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4)
|
||||
/// provides a set of Variant Streams, each of which
|
||||
/// describes a different version of the same content.
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
|
@ -116,11 +115,8 @@ impl MasterPlaylist {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-STREAM-INF:<attribute-list>
|
||||
/// <URI>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.2)
|
||||
/// [`#EXT-X-I-FRAME-STREAM-INF:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.3)
|
||||
/// [`#EXT-X-STREAM-INF:<attribute-list> <URI>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.2)
|
||||
/// [`#EXT-X-I-FRAME-STREAM-INF:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.3)
|
||||
///
|
||||
/// A Variant Stream includes a Media Playlist that specifies media
|
||||
/// encoded at a particular bit rate, in a particular format, and at a
|
||||
|
@ -197,8 +193,7 @@ impl VariantStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-MEDIA:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.1)
|
||||
/// [`#EXT-X-MEDIA:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.1)
|
||||
///
|
||||
/// The EXT-X-MEDIA tag is used to relate Media Playlists that contain
|
||||
/// alternative Renditions (Section 4.3.4.2.1) of the same content. For
|
||||
|
@ -229,7 +224,7 @@ impl AlternativeMedia {
|
|||
media_type: attrs
|
||||
.get("TYPE")
|
||||
.and_then(|s| AlternativeMediaType::from_str(s).ok())
|
||||
.unwrap_or_else(Default::default),
|
||||
.unwrap_or_default(),
|
||||
uri: attrs.remove("URI"),
|
||||
group_id: attrs.remove("GROUP-ID").unwrap_or_else(String::new),
|
||||
language: attrs.remove("LANGUAGE"),
|
||||
|
@ -314,8 +309,7 @@ impl fmt::Display for AlternativeMediaType {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-SESSION-KEY:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.5)
|
||||
/// [`#EXT-X-SESSION-KEY:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.5)
|
||||
/// The EXT-X-SESSION-KEY tag allows encryption keys from Media Playlists
|
||||
/// to be specified in a Master Playlist. This allows the client to
|
||||
/// preload these keys without having to read the Media Playlist(s) first.
|
||||
|
@ -336,8 +330,7 @@ pub enum SessionDataField {
|
|||
Uri(String),
|
||||
}
|
||||
|
||||
/// [`#EXT-X-SESSION-DATA:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.4)
|
||||
/// [`#EXT-X-SESSION-DATA:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.4.4)
|
||||
/// The EXT-X-SESSION-DATA tag allows arbitrary session data to be carried
|
||||
/// in a Master Playlist.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
|
@ -399,8 +392,7 @@ impl SessionData {
|
|||
// Media Playlist
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
/// A [Media Playlist]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.3)
|
||||
/// A [Media Playlist](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.3)
|
||||
/// contains a list of Media Segments, which when played
|
||||
/// sequentially will play the multimedia presentation.
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
|
@ -463,8 +455,7 @@ impl MediaPlaylist {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-PLAYLIST-TYPE:<EVENT|VOD>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.3.5)
|
||||
/// [`#EXT-X-PLAYLIST-TYPE:<EVENT|VOD>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.3.5)
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum MediaPlaylistType {
|
||||
Event,
|
||||
|
@ -577,8 +568,7 @@ impl MediaSegment {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-KEY:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.4)
|
||||
/// [`#EXT-X-KEY:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.4)
|
||||
///
|
||||
/// Media Segments MAY be encrypted. The EXT-X-KEY tag specifies how to
|
||||
/// decrypt them. It applies to every Media Segment that appears between
|
||||
|
@ -615,12 +605,10 @@ impl Key {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-MAP:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.5)
|
||||
/// [`#EXT-X-MAP:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.5)
|
||||
///
|
||||
/// The EXT-X-MAP tag specifies how to obtain the Media Initialization Section
|
||||
/// [(Section 3)]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-3)
|
||||
/// [(Section 3)](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-3)
|
||||
/// required to parse the applicable Media Segments.
|
||||
/// It applies to every Media Segment that appears after it in the
|
||||
/// Playlist until the next EXT-X-MAP tag or until the end of the
|
||||
|
@ -642,8 +630,7 @@ impl Map {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-BYTERANGE:<n>[@<o>]`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.2)
|
||||
/// [`#EXT-X-BYTERANGE:<n>[@<o>]`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.2)
|
||||
///
|
||||
/// The EXT-X-BYTERANGE tag indicates that a Media Segment is a sub-range
|
||||
/// of the resource identified by its URI. It applies only to the next
|
||||
|
@ -664,8 +651,7 @@ impl ByteRange {
|
|||
}
|
||||
}
|
||||
|
||||
/// [`#EXT-X-DATERANGE:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.7)
|
||||
/// [`#EXT-X-DATERANGE:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.7)
|
||||
///
|
||||
/// 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 /
|
||||
|
@ -686,8 +672,7 @@ pub struct DateRange {
|
|||
// Rest
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
/// [`#EXT-X-START:<attribute-list>`]
|
||||
/// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.5.2)
|
||||
/// [`#EXT-X-START:<attribute-list>`](https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.5.2)
|
||||
///
|
||||
/// The EXT-X-START tag indicates a preferred point at which to start
|
||||
/// playing a Playlist. By default, clients SHOULD start playback at
|
||||
|
|
188
tests/lib.rs
188
tests/lib.rs
|
@ -1,11 +1,7 @@
|
|||
#![allow(unused_variables, unused_imports, dead_code)]
|
||||
|
||||
extern crate m3u8_rs;
|
||||
extern crate nom;
|
||||
|
||||
use m3u8_rs::playlist::*;
|
||||
use m3u8_rs::*;
|
||||
use nom::*;
|
||||
use nom::AsBytes;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
|
@ -24,7 +20,7 @@ fn all_sample_m3u_playlists() -> Vec<path::PathBuf> {
|
|||
|
||||
fn getm3u(path: &str) -> String {
|
||||
let mut buf = String::new();
|
||||
let mut file = fs::File::open(path).expect(&format!("Can't find m3u8: {}", path));
|
||||
let mut file = fs::File::open(path).unwrap_or_else(|_| panic!("Can't find m3u8: {}", path));
|
||||
let u = file.read_to_string(&mut buf).expect("Can't read file");
|
||||
buf
|
||||
}
|
||||
|
@ -142,7 +138,7 @@ fn playlist_not_ending_in_newline_media() {
|
|||
fn playlist_type_is_master() {
|
||||
let input = get_sample_playlist("master.m3u8");
|
||||
let result = is_master_playlist(input.as_bytes());
|
||||
assert_eq!(true, result);
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
|
@ -166,158 +162,6 @@ fn playlist_types() {
|
|||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// Variant
|
||||
|
||||
#[test]
|
||||
fn variant_stream() {
|
||||
let input = b"#EXT-X-STREAM-INF:BANDWIDTH=300000,CODECS=\"xxx\"\n";
|
||||
let result = variant_stream_tag(input);
|
||||
println!("{:?}", result);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// Other
|
||||
|
||||
#[test]
|
||||
fn test_key_value_pairs_trailing_equals() {
|
||||
let res = key_value_pairs(b"BANDWIDTH=395000,CODECS=\"avc1.4d001f,mp4a.40.2\"\r\nrest=");
|
||||
println!("{:?}\n\n", res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_value_pairs_multiple_quoted_values() {
|
||||
assert_eq!(
|
||||
key_value_pairs(b"BANDWIDTH=86000,URI=\"low/iframe.m3u8\",PROGRAM-ID=1,RESOLUTION=\"1x1\",VIDEO=1\nrest"),
|
||||
Result::Ok((
|
||||
"\nrest".as_bytes(),
|
||||
vec![
|
||||
("BANDWIDTH".to_string(), "86000".to_string()),
|
||||
("URI".to_string(), "low/iframe.m3u8".to_string()),
|
||||
("PROGRAM-ID".to_string(), "1".to_string()),
|
||||
("RESOLUTION".to_string(), "1x1".to_string()),
|
||||
("VIDEO".to_string(), "1".to_string())
|
||||
].into_iter().collect::<HashMap<String,String>>()
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_value_pairs_quotes() {
|
||||
let res = key_value_pairs(b"BANDWIDTH=300000,CODECS=\"avc1.42c015,mp4a.40.2\"\r\nrest");
|
||||
println!("{:?}\n\n", res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_value_pairs() {
|
||||
let res = key_value_pairs(b"BANDWIDTH=300000,RESOLUTION=22x22,VIDEO=1\r\nrest=");
|
||||
println!("{:?}\n\n", res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_value_pair() {
|
||||
assert_eq!(
|
||||
key_value_pair(b"PROGRAM-ID=1,rest"),
|
||||
Result::Ok((
|
||||
"rest".as_bytes(),
|
||||
("PROGRAM-ID".to_string(), "1".to_string())
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ext_with_value() {
|
||||
assert_eq!(
|
||||
ext_tag(b"#EXT-X-CUE-OUT:DURATION=30\nxxx"),
|
||||
Result::Ok((
|
||||
b"xxx".as_bytes(),
|
||||
ExtTag {
|
||||
tag: "X-CUE-OUT".into(),
|
||||
rest: Some("DURATION=30".into())
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ext_without_value() {
|
||||
assert_eq!(
|
||||
ext_tag(b"#EXT-X-CUE-IN\nxxx"),
|
||||
Result::Ok((
|
||||
b"xxx".as_bytes(),
|
||||
ExtTag {
|
||||
tag: "X-CUE-IN".into(),
|
||||
rest: None
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn comment() {
|
||||
assert_eq!(
|
||||
comment_tag(b"#Hello\nxxx"),
|
||||
Result::Ok(("xxx".as_bytes(), "Hello".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quotes() {
|
||||
assert_eq!(
|
||||
quoted(b"\"value\"rest"),
|
||||
Result::Ok(("rest".as_bytes(), "value".to_string()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn consume_line_empty() {
|
||||
let expected = Result::Ok(("rest".as_bytes(), "".to_string()));
|
||||
let actual = consume_line(b"\r\nrest");
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn consume_line_n() {
|
||||
assert_eq!(
|
||||
consume_line(b"before\nrest"),
|
||||
Result::Ok(("rest".as_bytes(), "before".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn consume_line_rn() {
|
||||
assert_eq!(
|
||||
consume_line(b"before\r\nrest"),
|
||||
Result::Ok(("rest".as_bytes(), "before".into()))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_() {
|
||||
assert_eq!(
|
||||
float(b"33.22rest"),
|
||||
Result::Ok(("rest".as_bytes(), 33.22f32))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_no_decimal() {
|
||||
assert_eq!(float(b"33rest"), Result::Ok(("rest".as_bytes(), 33f32)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_should_ignore_trailing_dot() {
|
||||
assert_eq!(float(b"33.rest"), Result::Ok((".rest".as_bytes(), 33f32)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_duration_title() {
|
||||
assert_eq!(
|
||||
duration_title_tag(b"2.002,title\nrest"),
|
||||
Result::Ok(("rest".as_bytes(), (2.002f32, Some("title".to_string()))))
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// Creating playlists
|
||||
|
||||
|
@ -504,3 +348,29 @@ fn parsing_write_to_should_produce_the_same_structure() {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Failure on arbitrary text files that don't start with #EXTM3U8
|
||||
|
||||
#[test]
|
||||
fn parsing_text_file_should_fail() {
|
||||
let s = "
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
|
||||
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
||||
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
|
||||
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
|
||||
deserunt mollit anim id est laborum.
|
||||
";
|
||||
let res = parse_master_playlist_res(s.as_bytes());
|
||||
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parsing_binary_data_should_fail_cleanly() {
|
||||
let data = (0..1024).map(|i| (i % 255) as u8).collect::<Vec<u8>>();
|
||||
let res = parse_master_playlist_res(&data);
|
||||
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue