mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-06-09 16:59:34 +00:00
Compare commits
16 commits
Author | SHA1 | Date | |
---|---|---|---|
f5ddfed738 | |||
1c31d3835f | |||
aa4728aaed | |||
ddb618f0d9 | |||
6c3438a68f | |||
9ec0687f5e | |||
4ba3fcf352 | |||
f7eaeea281 | |||
3a742a95b6 | |||
6c694d186d | |||
c2c94f9352 | |||
8bf28b75fa | |||
5c2852c3ce | |||
fc067c3293 | |||
153c6e3e33 | |||
8ad21ec161 |
82
.github/workflows/rust.yml
vendored
82
.github/workflows/rust.yml
vendored
|
@ -1,36 +1,52 @@
|
||||||
name: rust
|
|
||||||
|
|
||||||
# Trigger the workflow on push or pull request
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
name: Continuous integration
|
||||||
rustfmt:
|
|
||||||
name: rustfmt
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
profile: minimal
|
|
||||||
toolchain: nightly
|
|
||||||
override: true
|
|
||||||
components: rustfmt
|
|
||||||
- uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: --all -- --check
|
|
||||||
|
|
||||||
clippy:
|
jobs:
|
||||||
name: clippy
|
check:
|
||||||
runs-on: ubuntu-latest
|
name: check
|
||||||
steps:
|
runs-on: ubuntu-latest
|
||||||
- uses: actions/checkout@v2
|
steps:
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions/checkout@v2
|
||||||
with:
|
- uses: actions-rs/toolchain@v1
|
||||||
profile: minimal
|
with:
|
||||||
toolchain: nightly
|
profile: minimal
|
||||||
override: true
|
toolchain: stable
|
||||||
components: clippy
|
override: true
|
||||||
- uses: actions-rs/cargo@v1
|
- uses: Swatinem/rust-cache@v1
|
||||||
with:
|
- uses: actions-rs/cargo@v1
|
||||||
command: clippy
|
with:
|
||||||
|
command: check
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
- uses: Swatinem/rust-cache@v1
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: rustfmt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
# rustfmt uses unstable features
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
- run: rustup component add rustfmt
|
||||||
|
- uses: Swatinem/rust-cache@v1
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "hls_m3u8"
|
name = "hls_m3u8"
|
||||||
version = "0.4.0" # remember to update html_root_url
|
version = "0.4.1" # remember to update html_root_url
|
||||||
authors = ["Takeru Ohta <phjgt308@gmail.com>", "Luro02 <24826124+Luro02@users.noreply.github.com>"]
|
authors = ["Takeru Ohta <phjgt308@gmail.com>", "Luro02 <24826124+Luro02@users.noreply.github.com>"]
|
||||||
description = "HLS m3u8 parser/generator"
|
description = "HLS m3u8 parser/generator"
|
||||||
homepage = "https://github.com/sile/hls_m3u8"
|
homepage = "https://github.com/sile/hls_m3u8"
|
||||||
|
|
|
@ -30,7 +30,8 @@ impl<'a> Iterator for AttributePairs<'a> {
|
||||||
// NOTE: it is okay to add 1 to the index, because an `=` is exactly 1 byte.
|
// NOTE: it is okay to add 1 to the index, because an `=` is exactly 1 byte.
|
||||||
self.index = end + 1;
|
self.index = end + 1;
|
||||||
|
|
||||||
&self.string[start..end]
|
// NOTE: See https://github.com/sile/hls_m3u8/issues/64
|
||||||
|
self.string[start..end].trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
let value = {
|
let value = {
|
||||||
|
@ -66,7 +67,8 @@ impl<'a> Iterator for AttributePairs<'a> {
|
||||||
self.index += end;
|
self.index += end;
|
||||||
self.index -= start;
|
self.index -= start;
|
||||||
|
|
||||||
&self.string[start..end]
|
// NOTE: See https://github.com/sile/hls_m3u8/issues/64
|
||||||
|
self.string[start..end].trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((key, value))
|
Some((key, value))
|
||||||
|
@ -190,7 +192,7 @@ mod test {
|
||||||
assert_eq!((1, Some(1)), pairs.size_hint());
|
assert_eq!((1, Some(1)), pairs.size_hint());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pairs.next(),
|
pairs.next(),
|
||||||
Some(("ध्वनि स्थिति और्४५० नीचे ", "देखने लाभो द्वारा करके(विशेष"))
|
Some(("ध्वनि स्थिति और्४५० नीचे", "देखने लाभो द्वारा करके(विशेष"))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!((0, Some(0)), pairs.size_hint());
|
assert_eq!((0, Some(0)), pairs.size_hint());
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#![doc(html_root_url = "https://docs.rs/hls_m3u8/0.4.0")]
|
#![doc(html_root_url = "https://docs.rs/hls_m3u8/0.4.1")]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![warn(
|
#![warn(
|
||||||
|
|
|
@ -220,13 +220,9 @@ impl<'a> MasterPlaylist<'a> {
|
||||||
|
|
||||||
/// Returns all streams, which have an audio group id.
|
/// Returns all streams, which have an audio group id.
|
||||||
pub fn audio_streams(&self) -> impl Iterator<Item = &VariantStream<'a>> {
|
pub fn audio_streams(&self) -> impl Iterator<Item = &VariantStream<'a>> {
|
||||||
self.variant_streams.iter().filter(|stream| {
|
self.variant_streams
|
||||||
if let VariantStream::ExtXStreamInf { audio: Some(_), .. } = stream {
|
.iter()
|
||||||
true
|
.filter(|stream| matches!(stream, VariantStream::ExtXStreamInf { audio: Some(_), .. }))
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all streams, which have a video group id.
|
/// Returns all streams, which have a video group id.
|
||||||
|
@ -416,13 +412,11 @@ impl<'a> MasterPlaylistBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_media_group<T: AsRef<str>>(&self, media_type: MediaType, group_id: T) -> bool {
|
fn check_media_group<T: AsRef<str>>(&self, media_type: MediaType, group_id: T) -> bool {
|
||||||
if let Some(value) = &self.media {
|
self.media.as_ref().map_or(false, |value| {
|
||||||
value.iter().any(|media| {
|
value.iter().any(|media| {
|
||||||
media.media_type == media_type && media.group_id().as_ref() == group_id.as_ref()
|
media.media_type == media_type && media.group_id().as_ref() == group_id.as_ref()
|
||||||
})
|
})
|
||||||
} else {
|
})
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -315,12 +315,11 @@ impl<'a> MediaPlaylistBuilder<'a> {
|
||||||
method, iv, format, ..
|
method, iv, format, ..
|
||||||
})) = key
|
})) = key
|
||||||
{
|
{
|
||||||
if *method == EncryptionMethod::Aes128 && *iv == InitializationVector::Missing {
|
if *method == EncryptionMethod::Aes128
|
||||||
if format.is_none() {
|
&& *iv == InitializationVector::Missing
|
||||||
*iv = InitializationVector::Number(segment.number as u128);
|
&& (format.is_none() || &mut Some(KeyFormat::Identity) == format)
|
||||||
} else if let Some(KeyFormat::Identity) = format {
|
{
|
||||||
*iv = InitializationVector::Number(segment.number as u128);
|
*iv = InitializationVector::Number(segment.number as u128);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -727,7 +726,7 @@ impl FromStr for MediaPlaylist<'static> {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(parse_media_playlist(input, &mut Self::builder())?.into_owned())
|
Ok(parse_media_playlist(input, &mut MediaPlaylist::builder())?.into_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,9 +223,9 @@ impl<'a> TryFrom<&'a str> for ExtXSessionData<'a> {
|
||||||
if let Some(value) = session_value {
|
if let Some(value) = session_value {
|
||||||
if uri.is_some() {
|
if uri.is_some() {
|
||||||
return Err(Error::custom("unexpected URI"));
|
return Err(Error::custom("unexpected URI"));
|
||||||
} else {
|
|
||||||
SessionData::Value(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SessionData::Value(value)
|
||||||
} else if let Some(uri) = uri {
|
} else if let Some(uri) = uri {
|
||||||
SessionData::Uri(uri)
|
SessionData::Uri(uri)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -348,13 +348,7 @@ impl<'a> TryFrom<&'a str> for VariantStream<'a> {
|
||||||
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
|
||||||
if let Ok(input) = tag(input, Self::PREFIX_EXTXIFRAME) {
|
if let Ok(input) = tag(input, Self::PREFIX_EXTXIFRAME) {
|
||||||
let uri = AttributePairs::new(input)
|
let uri = AttributePairs::new(input)
|
||||||
.find_map(|(key, value)| {
|
.find_map(|(key, value)| (key == "URI").then(|| unquote(value)))
|
||||||
if key == "URI" {
|
|
||||||
Some(unquote(value))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ok_or_else(|| Error::missing_value("URI"))?;
|
.ok_or_else(|| Error::missing_value("URI"))?;
|
||||||
|
|
||||||
Ok(Self::ExtXIFrame {
|
Ok(Self::ExtXIFrame {
|
||||||
|
@ -379,7 +373,7 @@ impl<'a> TryFrom<&'a str> for VariantStream<'a> {
|
||||||
"AUDIO" => audio = Some(unquote(value)),
|
"AUDIO" => audio = Some(unquote(value)),
|
||||||
"SUBTITLES" => subtitles = Some(unquote(value)),
|
"SUBTITLES" => subtitles = Some(unquote(value)),
|
||||||
"CLOSED-CAPTIONS" => {
|
"CLOSED-CAPTIONS" => {
|
||||||
closed_captions = Some(ClosedCaptions::try_from(value).unwrap())
|
closed_captions = Some(ClosedCaptions::try_from(value).unwrap());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -380,21 +380,21 @@ impl<'a> TryFrom<&'a str> for ExtXDateRange<'a> {
|
||||||
"START-DATE" => {
|
"START-DATE" => {
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
{
|
{
|
||||||
start_date = Some(unquote(value).parse().map_err(Error::chrono)?)
|
start_date = Some(unquote(value).parse().map_err(Error::chrono)?);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "chrono"))]
|
#[cfg(not(feature = "chrono"))]
|
||||||
{
|
{
|
||||||
start_date = Some(unquote(value))
|
start_date = Some(unquote(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"END-DATE" => {
|
"END-DATE" => {
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
{
|
{
|
||||||
end_date = Some(unquote(value).parse().map_err(Error::chrono)?)
|
end_date = Some(unquote(value).parse().map_err(Error::chrono)?);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "chrono"))]
|
#[cfg(not(feature = "chrono"))]
|
||||||
{
|
{
|
||||||
end_date = Some(unquote(value))
|
end_date = Some(unquote(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"DURATION" => {
|
"DURATION" => {
|
||||||
|
|
|
@ -354,11 +354,9 @@ impl_from_ranges![u64, u32, u16, u8, usize, i32];
|
||||||
#[must_use]
|
#[must_use]
|
||||||
impl RangeBounds<usize> for ByteRange {
|
impl RangeBounds<usize> for ByteRange {
|
||||||
fn start_bound(&self) -> Bound<&usize> {
|
fn start_bound(&self) -> Bound<&usize> {
|
||||||
if let Some(start) = &self.start {
|
self.start
|
||||||
Bound::Included(start)
|
.as_ref()
|
||||||
} else {
|
.map_or(Bound::Unbounded, Bound::Included)
|
||||||
Bound::Unbounded
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::Error;
|
||||||
/// [`NaN`]: core::f32::NAN
|
/// [`NaN`]: core::f32::NAN
|
||||||
/// [`INFINITY`]: core::f32::INFINITY
|
/// [`INFINITY`]: core::f32::INFINITY
|
||||||
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
||||||
#[derive(AsRef, Deref, Default, Debug, Copy, Clone, Display, PartialOrd)]
|
#[derive(AsRef, Deref, Default, Debug, Copy, Clone, Display)]
|
||||||
pub struct Float(f32);
|
pub struct Float(f32);
|
||||||
|
|
||||||
impl Float {
|
impl Float {
|
||||||
|
@ -129,6 +129,11 @@ impl PartialEq<f32> for Float {
|
||||||
// be soundly implemented.
|
// be soundly implemented.
|
||||||
impl Eq for Float {}
|
impl Eq for Float {}
|
||||||
|
|
||||||
|
impl PartialOrd for Float {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||||
|
}
|
||||||
|
|
||||||
impl Ord for Float {
|
impl Ord for Float {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
@ -177,7 +182,7 @@ impl ::core::hash::Hash for Float {
|
||||||
} else {
|
} else {
|
||||||
// I do not think it matters to differentiate between architectures, that use
|
// I do not think it matters to differentiate between architectures, that use
|
||||||
// big endian by default and those, that use little endian.
|
// big endian by default and those, that use little endian.
|
||||||
state.write(&self.to_be_bytes())
|
state.write(&self.to_be_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,8 @@ use crate::types::ProtocolVersion;
|
||||||
/// [`MediaPlaylist`]: crate::MediaPlaylist
|
/// [`MediaPlaylist`]: crate::MediaPlaylist
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[strum(serialize_all = "UPPERCASE")]
|
|
||||||
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
|
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
|
||||||
|
#[strum(serialize_all = "UPPERCASE")]
|
||||||
pub enum InStreamId {
|
pub enum InStreamId {
|
||||||
Cc1,
|
Cc1,
|
||||||
Cc2,
|
Cc2,
|
||||||
|
|
|
@ -36,7 +36,7 @@ use crate::RequiredVersion;
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`KeyFormat`]: crate::types::KeyFormat
|
/// [`KeyFormat`]: crate::types::KeyFormat
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct KeyFormatVersions {
|
pub struct KeyFormatVersions {
|
||||||
// NOTE(Luro02): if the current array is not big enough one can easily increase
|
// NOTE(Luro02): if the current array is not big enough one can easily increase
|
||||||
// the number of elements or change the type to something bigger,
|
// the number of elements or change the type to something bigger,
|
||||||
|
@ -295,7 +295,7 @@ impl Extend<u8> for KeyFormatVersions {
|
||||||
|
|
||||||
impl<'a> Extend<&'a u8> for KeyFormatVersions {
|
impl<'a> Extend<&'a u8> for KeyFormatVersions {
|
||||||
fn extend<I: IntoIterator<Item = &'a u8>>(&mut self, iter: I) {
|
fn extend<I: IntoIterator<Item = &'a u8>>(&mut self, iter: I) {
|
||||||
<Self as Extend<u8>>::extend(self, iter.into_iter().copied())
|
<Self as Extend<u8>>::extend(self, iter.into_iter().copied());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,17 +346,7 @@ impl FromIterator<u8> for KeyFormatVersions {
|
||||||
|
|
||||||
impl<'a> FromIterator<&'a u8> for KeyFormatVersions {
|
impl<'a> FromIterator<&'a u8> for KeyFormatVersions {
|
||||||
fn from_iter<I: IntoIterator<Item = &'a u8>>(iter: I) -> Self {
|
fn from_iter<I: IntoIterator<Item = &'a u8>>(iter: I) -> Self {
|
||||||
<Self as FromIterator<u8>>::from_iter(iter.into_iter().copied())
|
iter.into_iter().copied().collect()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for KeyFormatVersions {
|
|
||||||
#[inline]
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
buffer: [0; 9],
|
|
||||||
len: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -413,7 +403,7 @@ impl fmt::Display for KeyFormatVersions {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsRef<[usize]>> From<T> for KeyFormatVersions {
|
impl<T: AsRef<[usize]>> From<T> for KeyFormatVersions {
|
||||||
fn from(value: T) -> Self { Self::from_iter(value.as_ref().iter().map(|i| *i as u8)) }
|
fn from(value: T) -> Self { value.as_ref().iter().map(|i| *i as u8).collect() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Iterator` for [`KeyFormatVersions`].
|
/// `Iterator` for [`KeyFormatVersions`].
|
||||||
|
|
|
@ -323,12 +323,12 @@ impl<'a> TryFrom<&'a str> for StreamData<'a> {
|
||||||
value
|
value
|
||||||
.parse::<u64>()
|
.parse::<u64>()
|
||||||
.map_err(|e| Error::parse_int(value, e))?,
|
.map_err(|e| Error::parse_int(value, e))?,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
"CODECS" => codecs = Some(TryFrom::try_from(unquote(value))?),
|
"CODECS" => codecs = Some(TryFrom::try_from(unquote(value))?),
|
||||||
"RESOLUTION" => resolution = Some(value.parse()?),
|
"RESOLUTION" => resolution = Some(value.parse()?),
|
||||||
"HDCP-LEVEL" => {
|
"HDCP-LEVEL" => {
|
||||||
hdcp_level = Some(value.parse::<HdcpLevel>().map_err(Error::strum)?)
|
hdcp_level = Some(value.parse::<HdcpLevel>().map_err(Error::strum)?);
|
||||||
}
|
}
|
||||||
"VIDEO" => video = Some(unquote(value)),
|
"VIDEO" => video = Some(unquote(value)),
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::Error;
|
||||||
/// [`NaN`]: core::f32::NAN
|
/// [`NaN`]: core::f32::NAN
|
||||||
/// [`INFINITY`]: core::f32::INFINITY
|
/// [`INFINITY`]: core::f32::INFINITY
|
||||||
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
/// [`NEG_INFINITY`]: core::f32::NEG_INFINITY
|
||||||
#[derive(AsRef, Deref, Default, Debug, Copy, Clone, PartialOrd, Display)]
|
#[derive(AsRef, Deref, Default, Debug, Copy, Clone, Display)]
|
||||||
pub struct UFloat(f32);
|
pub struct UFloat(f32);
|
||||||
|
|
||||||
impl UFloat {
|
impl UFloat {
|
||||||
|
@ -141,6 +141,11 @@ impl PartialEq<f32> for UFloat {
|
||||||
// be soundly implemented.
|
// be soundly implemented.
|
||||||
impl Eq for UFloat {}
|
impl Eq for UFloat {}
|
||||||
|
|
||||||
|
impl PartialOrd for UFloat {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||||
|
}
|
||||||
|
|
||||||
impl Ord for UFloat {
|
impl Ord for UFloat {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
@ -188,7 +193,7 @@ impl ::core::hash::Hash for UFloat {
|
||||||
|
|
||||||
// I do not think it matters to differentiate between architectures, that use
|
// I do not think it matters to differentiate between architectures, that use
|
||||||
// big endian by default and those, that use little endian.
|
// big endian by default and those, that use little endian.
|
||||||
state.write(&self.to_be_bytes())
|
state.write(&self.to_be_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +271,7 @@ mod tests {
|
||||||
fn test_new_negative() { let _ = UFloat::new(-1.1); }
|
fn test_new_negative() { let _ = UFloat::new(-1.1); }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic = "float must be positive: `0`"]
|
#[should_panic = "float must be positive: `-0`"]
|
||||||
fn test_new_negative_zero() { let _ = UFloat::new(-0.0); }
|
fn test_new_negative_zero() { let _ = UFloat::new(-0.0); }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -73,11 +73,7 @@ pub(crate) fn unquote(value: &str) -> Cow<'_, str> {
|
||||||
if value.starts_with('"') && value.ends_with('"') {
|
if value.starts_with('"') && value.ends_with('"') {
|
||||||
let result = Cow::Borrowed(&value[1..value.len() - 1]);
|
let result = Cow::Borrowed(&value[1..value.len() - 1]);
|
||||||
|
|
||||||
if result
|
if !result.chars().any(|c| c == '"' || c == '\n' || c == '\r') {
|
||||||
.chars()
|
|
||||||
.find(|c| *c == '"' || *c == '\n' || *c == '\r')
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
tests/issues/assets/issue_00064.m3u8
Normal file
3
tests/issues/assets/issue_00064.m3u8
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#EXTM3U
|
||||||
|
#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=10000000
|
||||||
|
https://995107575.cloudvdn.com/a.m3u8?cdn=cn-gotcha03&domain=d1--cn-gotcha103.bilivideo.com&expires=1614619920&len=0&oi=1891753406&order=1&player=70YAALwcl0b9RGgW&pt=h5&ptype=0&qn=10000&secondToken=secondToken%3ACZ4ggpPHomuwcnT8XWDjJUp9eh8&sign=325afc8bc3b01ccbadeac084004ece64&sigparams=cdn%2Cexpires%2Clen%2Coi%2Cpt%2Cqn%2Ctrid&sl=1&src=4&streamid=live-qn%3Alive-qn%2Flive_402401719_42665292&trid=20d9f245179b4ef3a7e3635afaaa87ea&v3=1
|
34
tests/issues/issue_00064.rs
Normal file
34
tests/issues/issue_00064.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// The relevant issue:
|
||||||
|
// https://github.com/sile/hls_m3u8/issues/55
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
|
use hls_m3u8::tags::VariantStream;
|
||||||
|
use hls_m3u8::types::StreamData;
|
||||||
|
use hls_m3u8::MasterPlaylist;
|
||||||
|
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
let file = include_str!("assets/issue_00064.m3u8");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
MasterPlaylist::try_from(file).unwrap(),
|
||||||
|
MasterPlaylist::builder()
|
||||||
|
.variant_streams(vec![
|
||||||
|
VariantStream::ExtXStreamInf {
|
||||||
|
uri: "https://995107575.cloudvdn.com/a.m3u8?cdn=cn-gotcha03&domain=d1--cn-gotcha103.bilivideo.com&expires=1614619920&len=0&oi=1891753406&order=1&player=70YAALwcl0b9RGgW&pt=h5&ptype=0&qn=10000&secondToken=secondToken%3ACZ4ggpPHomuwcnT8XWDjJUp9eh8&sign=325afc8bc3b01ccbadeac084004ece64&sigparams=cdn%2Cexpires%2Clen%2Coi%2Cpt%2Cqn%2Ctrid&sl=1&src=4&streamid=live-qn%3Alive-qn%2Flive_402401719_42665292&trid=20d9f245179b4ef3a7e3635afaaa87ea&v3=1".into(),
|
||||||
|
frame_rate: None,
|
||||||
|
audio: None,
|
||||||
|
subtitles: None,
|
||||||
|
closed_captions: None,
|
||||||
|
stream_data: StreamData::builder()
|
||||||
|
.bandwidth(10000000)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
])
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in a new issue