mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-09-27 14:10:05 +00:00
fix #20
This commit is contained in:
parent
ebddb7a0e2
commit
4b4cffc248
31 changed files with 354 additions and 207 deletions
|
@ -76,6 +76,10 @@ pub enum ErrorKind {
|
||||||
/// An unexpected value.
|
/// An unexpected value.
|
||||||
UnexpectedAttribute(String),
|
UnexpectedAttribute(String),
|
||||||
|
|
||||||
|
#[fail(display = "Unexpected Tag: {:?}", _0)]
|
||||||
|
/// An unexpected tag.
|
||||||
|
UnexpectedTag(String),
|
||||||
|
|
||||||
/// Hints that destructuring should not be exhaustive.
|
/// Hints that destructuring should not be exhaustive.
|
||||||
///
|
///
|
||||||
/// This enum may grow additional variants, so this makes sure clients
|
/// This enum may grow additional variants, so this makes sure clients
|
||||||
|
@ -119,6 +123,10 @@ impl Error {
|
||||||
Self::from(ErrorKind::UnexpectedAttribute(value.to_string()))
|
Self::from(ErrorKind::UnexpectedAttribute(value.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn unexpected_tag<T: ToString>(value: T) -> Self {
|
||||||
|
Self::from(ErrorKind::UnexpectedTag(value.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn invalid_input() -> Self { Self::from(ErrorKind::InvalidInput) }
|
pub(crate) fn invalid_input() -> Self { Self::from(ErrorKind::InvalidInput) }
|
||||||
|
|
||||||
pub(crate) fn parse_int_error<T: ToString>(value: T) -> Self {
|
pub(crate) fn parse_int_error<T: ToString>(value: T) -> Self {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
#![feature(option_flattening)]
|
||||||
#![warn(
|
#![warn(
|
||||||
//clippy::pedantic,
|
//clippy::pedantic,
|
||||||
clippy::nursery,
|
clippy::nursery,
|
||||||
|
@ -49,6 +50,8 @@ mod line;
|
||||||
mod master_playlist;
|
mod master_playlist;
|
||||||
mod media_playlist;
|
mod media_playlist;
|
||||||
mod media_segment;
|
mod media_segment;
|
||||||
|
mod traits;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
pub use error::Result;
|
pub use error::Result;
|
||||||
|
pub use traits::*;
|
||||||
|
|
|
@ -10,8 +10,8 @@ use crate::tags::{
|
||||||
ExtM3u, ExtXIFrameStreamInf, ExtXIndependentSegments, ExtXMedia, ExtXSessionData,
|
ExtM3u, ExtXIFrameStreamInf, ExtXIndependentSegments, ExtXMedia, ExtXSessionData,
|
||||||
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion,
|
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion,
|
||||||
};
|
};
|
||||||
use crate::types::{ClosedCaptions, MediaType, ProtocolVersion, RequiredVersion};
|
use crate::types::{ClosedCaptions, MediaType, ProtocolVersion};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// Master playlist.
|
/// Master playlist.
|
||||||
#[derive(Debug, Clone, Builder)]
|
#[derive(Debug, Clone, Builder)]
|
||||||
|
|
|
@ -11,8 +11,8 @@ use crate::tags::{
|
||||||
ExtM3u, ExtXDiscontinuitySequence, ExtXEndList, ExtXIFramesOnly, ExtXIndependentSegments,
|
ExtM3u, ExtXDiscontinuitySequence, ExtXEndList, ExtXIFramesOnly, ExtXIndependentSegments,
|
||||||
ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion,
|
ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion,
|
||||||
};
|
};
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::Error;
|
use crate::{Encrypted, Error, RequiredVersion};
|
||||||
|
|
||||||
/// Media playlist.
|
/// Media playlist.
|
||||||
#[derive(Debug, Clone, Builder)]
|
#[derive(Debug, Clone, Builder)]
|
||||||
|
@ -28,30 +28,30 @@ pub struct MediaPlaylist {
|
||||||
/// The default is the maximum version among the tags in the playlist.
|
/// The default is the maximum version among the tags in the playlist.
|
||||||
#[builder(setter(name = "version"))]
|
#[builder(setter(name = "version"))]
|
||||||
version_tag: ExtXVersion,
|
version_tag: ExtXVersion,
|
||||||
/// Sets the [ExtXTargetDuration] tag.
|
/// Sets the [`ExtXTargetDuration`] tag.
|
||||||
target_duration_tag: ExtXTargetDuration,
|
target_duration_tag: ExtXTargetDuration,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets the [ExtXMediaSequence] tag.
|
/// Sets the [`ExtXMediaSequence`] tag.
|
||||||
media_sequence_tag: Option<ExtXMediaSequence>,
|
media_sequence_tag: Option<ExtXMediaSequence>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets the [ExtXDiscontinuitySequence] tag.
|
/// Sets the [`ExtXDiscontinuitySequence`] tag.
|
||||||
discontinuity_sequence_tag: Option<ExtXDiscontinuitySequence>,
|
discontinuity_sequence_tag: Option<ExtXDiscontinuitySequence>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets the [ExtXPlaylistType] tag.
|
/// Sets the [`ExtXPlaylistType`] tag.
|
||||||
playlist_type_tag: Option<ExtXPlaylistType>,
|
playlist_type_tag: Option<ExtXPlaylistType>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets the [ExtXIFramesOnly] tag.
|
/// Sets the [`ExtXIFramesOnly`] tag.
|
||||||
i_frames_only_tag: Option<ExtXIFramesOnly>,
|
i_frames_only_tag: Option<ExtXIFramesOnly>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets the [ExtXIndependentSegments] tag.
|
/// Sets the [`ExtXIndependentSegments`] tag.
|
||||||
independent_segments_tag: Option<ExtXIndependentSegments>,
|
independent_segments_tag: Option<ExtXIndependentSegments>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets the [ExtXStart] tag.
|
/// Sets the [`ExtXStart`] tag.
|
||||||
start_tag: Option<ExtXStart>,
|
start_tag: Option<ExtXStart>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets the [ExtXEndList] tag.
|
/// Sets the [`ExtXEndList`] tag.
|
||||||
end_list_tag: Option<ExtXEndList>,
|
end_list_tag: Option<ExtXEndList>,
|
||||||
/// Sets all [MediaSegment]s.
|
/// Sets all [`MediaSegment`]s.
|
||||||
segments: Vec<MediaSegment>,
|
segments: Vec<MediaSegment>,
|
||||||
/// Sets the allowable excess duration of each media segment in the
|
/// Sets the allowable excess duration of each media segment in the
|
||||||
/// associated playlist.
|
/// associated playlist.
|
||||||
|
@ -69,10 +69,7 @@ pub struct MediaPlaylist {
|
||||||
impl MediaPlaylistBuilder {
|
impl MediaPlaylistBuilder {
|
||||||
fn validate(&self) -> Result<(), String> {
|
fn validate(&self) -> Result<(), String> {
|
||||||
let required_version = self.required_version();
|
let required_version = self.required_version();
|
||||||
let specified_version = self
|
let specified_version = self.version_tag.map_or(required_version, |p| p.version());
|
||||||
.version_tag
|
|
||||||
.unwrap_or_else(|| required_version.into())
|
|
||||||
.version();
|
|
||||||
|
|
||||||
if required_version > specified_version {
|
if required_version > specified_version {
|
||||||
return Err(Error::custom(format!(
|
return Err(Error::custom(format!(
|
||||||
|
@ -138,72 +135,6 @@ impl MediaPlaylistBuilder {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_version(&self) -> ProtocolVersion {
|
|
||||||
iter::empty()
|
|
||||||
.chain(
|
|
||||||
self.target_duration_tag
|
|
||||||
.iter()
|
|
||||||
.map(|t| t.required_version()),
|
|
||||||
)
|
|
||||||
.chain(self.media_sequence_tag.iter().map(|t| {
|
|
||||||
if let Some(p) = t {
|
|
||||||
p.required_version()
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.chain(self.discontinuity_sequence_tag.iter().map(|t| {
|
|
||||||
if let Some(p) = t {
|
|
||||||
p.required_version()
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.chain(self.playlist_type_tag.iter().map(|t| {
|
|
||||||
if let Some(p) = t {
|
|
||||||
p.required_version()
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.chain(self.i_frames_only_tag.iter().map(|t| {
|
|
||||||
if let Some(p) = t {
|
|
||||||
p.required_version()
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.chain(self.independent_segments_tag.iter().map(|t| {
|
|
||||||
if let Some(p) = t {
|
|
||||||
p.required_version()
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.chain(self.start_tag.iter().map(|t| {
|
|
||||||
if let Some(p) = t {
|
|
||||||
p.required_version()
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.chain(self.end_list_tag.iter().map(|t| {
|
|
||||||
if let Some(p) = t {
|
|
||||||
p.required_version()
|
|
||||||
} else {
|
|
||||||
ProtocolVersion::V1
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.chain(self.segments.iter().map(|t| {
|
|
||||||
t.iter()
|
|
||||||
.map(|s| s.required_version())
|
|
||||||
.max()
|
|
||||||
.unwrap_or(ProtocolVersion::V1)
|
|
||||||
}))
|
|
||||||
.max()
|
|
||||||
.unwrap_or_else(ProtocolVersion::latest)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a media segment to the resulting playlist.
|
/// Adds a media segment to the resulting playlist.
|
||||||
pub fn push_segment<VALUE: Into<MediaSegment>>(&mut self, value: VALUE) -> &mut Self {
|
pub fn push_segment<VALUE: Into<MediaSegment>>(&mut self, value: VALUE) -> &mut Self {
|
||||||
if let Some(segments) = &mut self.segments {
|
if let Some(segments) = &mut self.segments {
|
||||||
|
@ -214,50 +145,111 @@ impl MediaPlaylistBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the rest of the [MediaPlaylist] from an m3u8 file.
|
/// Parse the rest of the [`MediaPlaylist`] from an m3u8 file.
|
||||||
pub fn parse(&mut self, input: &str) -> crate::Result<MediaPlaylist> {
|
pub fn parse(&mut self, input: &str) -> crate::Result<MediaPlaylist> {
|
||||||
parse_media_playlist(input, self)
|
parse_media_playlist(input, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RequiredVersion for MediaPlaylistBuilder {
|
||||||
|
fn required_version(&self) -> ProtocolVersion {
|
||||||
|
iter::empty()
|
||||||
|
.chain(
|
||||||
|
self.target_duration_tag
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.media_sequence_tag
|
||||||
|
.flatten()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.discontinuity_sequence_tag
|
||||||
|
.flatten()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.playlist_type_tag
|
||||||
|
.flatten()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.i_frames_only_tag
|
||||||
|
.flatten()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.independent_segments_tag
|
||||||
|
.flatten()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.start_tag
|
||||||
|
.flatten()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(
|
||||||
|
self.end_list_tag
|
||||||
|
.flatten()
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.required_version()),
|
||||||
|
)
|
||||||
|
.chain(self.segments.iter().map(|t| {
|
||||||
|
t.iter()
|
||||||
|
.map(|p| p.required_version())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(ProtocolVersion::V1)
|
||||||
|
}))
|
||||||
|
.max()
|
||||||
|
.unwrap_or_else(ProtocolVersion::latest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl MediaPlaylist {
|
impl MediaPlaylist {
|
||||||
/// Creates a [MediaPlaylistBuilder].
|
/// Returns a builder for [`MediaPlaylist`].
|
||||||
pub fn builder() -> MediaPlaylistBuilder { MediaPlaylistBuilder::default() }
|
pub fn builder() -> MediaPlaylistBuilder { MediaPlaylistBuilder::default() }
|
||||||
|
|
||||||
/// Returns the `EXT-X-VERSION` tag contained in the playlist.
|
/// Returns the [`ExtXVersion`] tag contained in the playlist.
|
||||||
pub const fn version_tag(&self) -> ExtXVersion { self.version_tag }
|
pub const fn version_tag(&self) -> ExtXVersion { self.version_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-TARGETDURATION` tag contained in the playlist.
|
/// Returns the [`ExtXTargetDuration`] tag contained in the playlist.
|
||||||
pub const fn target_duration_tag(&self) -> ExtXTargetDuration { self.target_duration_tag }
|
pub const fn target_duration_tag(&self) -> ExtXTargetDuration { self.target_duration_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-MEDIA-SEQUENCE` tag contained in the playlist.
|
/// Returns the `EXT-X-MEDIA-SEQUENCE` tag contained in the playlist.
|
||||||
pub const fn media_sequence_tag(&self) -> Option<ExtXMediaSequence> { self.media_sequence_tag }
|
pub const fn media_sequence_tag(&self) -> Option<ExtXMediaSequence> { self.media_sequence_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-DISCONTINUITY-SEQUENCE` tag contained in the
|
/// Returns the [`ExtXDiscontinuitySequence`] tag contained in the
|
||||||
/// playlist.
|
/// playlist.
|
||||||
pub const fn discontinuity_sequence_tag(&self) -> Option<ExtXDiscontinuitySequence> {
|
pub const fn discontinuity_sequence_tag(&self) -> Option<ExtXDiscontinuitySequence> {
|
||||||
self.discontinuity_sequence_tag
|
self.discontinuity_sequence_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `EXT-X-PLAYLIST-TYPE` tag contained in the playlist.
|
/// Returns the [`ExtXPlaylistType`] tag contained in the playlist.
|
||||||
pub const fn playlist_type_tag(&self) -> Option<ExtXPlaylistType> { self.playlist_type_tag }
|
pub const fn playlist_type_tag(&self) -> Option<ExtXPlaylistType> { self.playlist_type_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-I-FRAMES-ONLY` tag contained in the playlist.
|
/// Returns the [`ExtXIFramesOnly`] tag contained in the playlist.
|
||||||
pub const fn i_frames_only_tag(&self) -> Option<ExtXIFramesOnly> { self.i_frames_only_tag }
|
pub const fn i_frames_only_tag(&self) -> Option<ExtXIFramesOnly> { self.i_frames_only_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-INDEPENDENT-SEGMENTS` tag contained in the playlist.
|
/// Returns the [`ExtXIndependentSegments`] tag contained in the playlist.
|
||||||
pub const fn independent_segments_tag(&self) -> Option<ExtXIndependentSegments> {
|
pub const fn independent_segments_tag(&self) -> Option<ExtXIndependentSegments> {
|
||||||
self.independent_segments_tag
|
self.independent_segments_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `EXT-X-START` tag contained in the playlist.
|
/// Returns the [`ExtXStart`] tag contained in the playlist.
|
||||||
pub const fn start_tag(&self) -> Option<ExtXStart> { self.start_tag }
|
pub const fn start_tag(&self) -> Option<ExtXStart> { self.start_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-ENDLIST` tag contained in the playlist.
|
/// Returns the [`ExtXEndList`] tag contained in the playlist.
|
||||||
pub const fn end_list_tag(&self) -> Option<ExtXEndList> { self.end_list_tag }
|
pub const fn end_list_tag(&self) -> Option<ExtXEndList> { self.end_list_tag }
|
||||||
|
|
||||||
/// Returns the media segments contained in the playlist.
|
/// Returns the [`MediaSegment`]s contained in the playlist.
|
||||||
pub fn segments(&self) -> &[MediaSegment] { &self.segments }
|
pub const fn segments(&self) -> &Vec<MediaSegment> { &self.segments }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for MediaPlaylist {
|
impl fmt::Display for MediaPlaylist {
|
||||||
|
@ -306,6 +298,8 @@ fn parse_media_playlist(
|
||||||
let mut has_discontinuity_tag = false;
|
let mut has_discontinuity_tag = false;
|
||||||
let mut has_version = false; // m3u8 files without ExtXVersion tags are ProtocolVersion::V1
|
let mut has_version = false; // m3u8 files without ExtXVersion tags are ProtocolVersion::V1
|
||||||
|
|
||||||
|
let mut available_key_tags = vec![];
|
||||||
|
|
||||||
for (i, line) in input.parse::<Lines>()?.into_iter().enumerate() {
|
for (i, line) in input.parse::<Lines>()?.into_iter().enumerate() {
|
||||||
match line {
|
match line {
|
||||||
Line::Tag(tag) => {
|
Line::Tag(tag) => {
|
||||||
|
@ -336,10 +330,29 @@ fn parse_media_playlist(
|
||||||
}
|
}
|
||||||
Tag::ExtXKey(t) => {
|
Tag::ExtXKey(t) => {
|
||||||
has_partial_segment = true;
|
has_partial_segment = true;
|
||||||
segment.push_key_tag(t);
|
if !available_key_tags.is_empty() {
|
||||||
|
available_key_tags.push(t);
|
||||||
|
} else {
|
||||||
|
// An ExtXKey applies to every MediaSegment and to every Media
|
||||||
|
// Initialization Section declared by an EXT-X-MAP tag, that appears
|
||||||
|
// between it and the next EXT-X-KEY tag in the Playlist file with the
|
||||||
|
// same KEYFORMAT attribute (or the end of the Playlist file).
|
||||||
|
available_key_tags = available_key_tags
|
||||||
|
.into_iter()
|
||||||
|
.map(|k| {
|
||||||
|
if t.key_format() == k.key_format() {
|
||||||
|
t.clone()
|
||||||
|
} else {
|
||||||
|
k
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Tag::ExtXMap(t) => {
|
Tag::ExtXMap(mut t) => {
|
||||||
has_partial_segment = true;
|
has_partial_segment = true;
|
||||||
|
|
||||||
|
t.set_keys(available_key_tags.clone());
|
||||||
segment.map_tag(t);
|
segment.map_tag(t);
|
||||||
}
|
}
|
||||||
Tag::ExtXProgramDateTime(t) => {
|
Tag::ExtXProgramDateTime(t) => {
|
||||||
|
@ -379,7 +392,7 @@ fn parse_media_playlist(
|
||||||
| Tag::ExtXIFrameStreamInf(_)
|
| Tag::ExtXIFrameStreamInf(_)
|
||||||
| Tag::ExtXSessionData(_)
|
| Tag::ExtXSessionData(_)
|
||||||
| Tag::ExtXSessionKey(_) => {
|
| Tag::ExtXSessionKey(_) => {
|
||||||
return Err(Error::custom(tag));
|
return Err(Error::unexpected_tag(tag));
|
||||||
}
|
}
|
||||||
Tag::ExtXIndependentSegments(t) => {
|
Tag::ExtXIndependentSegments(t) => {
|
||||||
builder.independent_segments_tag(t);
|
builder.independent_segments_tag(t);
|
||||||
|
@ -395,15 +408,18 @@ fn parse_media_playlist(
|
||||||
}
|
}
|
||||||
Line::Uri(uri) => {
|
Line::Uri(uri) => {
|
||||||
segment.uri(uri);
|
segment.uri(uri);
|
||||||
|
segment.keys(available_key_tags.clone());
|
||||||
segments.push(segment.build().map_err(Error::builder_error)?);
|
segments.push(segment.build().map_err(Error::builder_error)?);
|
||||||
segment = MediaSegment::builder();
|
segment = MediaSegment::builder();
|
||||||
has_partial_segment = false;
|
has_partial_segment = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_partial_segment {
|
if has_partial_segment {
|
||||||
return Err(Error::invalid_input());
|
return Err(Error::invalid_input());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_version {
|
if !has_version {
|
||||||
builder.version(ProtocolVersion::V1);
|
builder.version(ProtocolVersion::V1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,43 +6,44 @@ use derive_builder::Builder;
|
||||||
use crate::tags::{
|
use crate::tags::{
|
||||||
ExtInf, ExtXByteRange, ExtXDateRange, ExtXDiscontinuity, ExtXKey, ExtXMap, ExtXProgramDateTime,
|
ExtInf, ExtXByteRange, ExtXDateRange, ExtXDiscontinuity, ExtXKey, ExtXMap, ExtXProgramDateTime,
|
||||||
};
|
};
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
|
use crate::{Encrypted, RequiredVersion};
|
||||||
|
|
||||||
/// Media segment.
|
|
||||||
#[derive(Debug, Clone, Builder)]
|
#[derive(Debug, Clone, Builder)]
|
||||||
#[builder(setter(into, strip_option))]
|
#[builder(setter(into, strip_option))]
|
||||||
|
/// Media segment.
|
||||||
pub struct MediaSegment {
|
pub struct MediaSegment {
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets all [ExtXKey] tags.
|
/// Sets all [`ExtXKey`] tags.
|
||||||
key_tags: Vec<ExtXKey>,
|
keys: Vec<ExtXKey>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets an [ExtXMap] tag.
|
/// Sets an [`ExtXMap`] tag.
|
||||||
map_tag: Option<ExtXMap>,
|
map_tag: Option<ExtXMap>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets an [ExtXByteRange] tag.
|
/// Sets an [`ExtXByteRange`] tag.
|
||||||
byte_range_tag: Option<ExtXByteRange>,
|
byte_range_tag: Option<ExtXByteRange>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets an [ExtXDateRange] tag.
|
/// Sets an [`ExtXDateRange`] tag.
|
||||||
date_range_tag: Option<ExtXDateRange>,
|
date_range_tag: Option<ExtXDateRange>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets an [ExtXDiscontinuity] tag.
|
/// Sets an [`ExtXDiscontinuity`] tag.
|
||||||
discontinuity_tag: Option<ExtXDiscontinuity>,
|
discontinuity_tag: Option<ExtXDiscontinuity>,
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
/// Sets an [ExtXProgramDateTime] tag.
|
/// Sets an [`ExtXProgramDateTime`] tag.
|
||||||
program_date_time_tag: Option<ExtXProgramDateTime>,
|
program_date_time_tag: Option<ExtXProgramDateTime>,
|
||||||
/// Sets an [ExtInf] tag.
|
/// Sets an [`ExtInf`] tag.
|
||||||
inf_tag: ExtInf,
|
inf_tag: ExtInf,
|
||||||
/// Sets an Uri.
|
/// Sets an `URI`.
|
||||||
uri: String,
|
uri: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaSegmentBuilder {
|
impl MediaSegmentBuilder {
|
||||||
/// Pushes an [ExtXKey] tag.
|
/// Pushes an [`ExtXKey`] tag.
|
||||||
pub fn push_key_tag<VALUE: Into<ExtXKey>>(&mut self, value: VALUE) -> &mut Self {
|
pub fn push_key_tag<VALUE: Into<ExtXKey>>(&mut self, value: VALUE) -> &mut Self {
|
||||||
if let Some(key_tags) = &mut self.key_tags {
|
if let Some(key_tags) = &mut self.keys {
|
||||||
key_tags.push(value.into());
|
key_tags.push(value.into());
|
||||||
} else {
|
} else {
|
||||||
self.key_tags = Some(vec![value.into()]);
|
self.keys = Some(vec![value.into()]);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -50,7 +51,7 @@ impl MediaSegmentBuilder {
|
||||||
|
|
||||||
impl fmt::Display for MediaSegment {
|
impl fmt::Display for MediaSegment {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
for value in &self.key_tags {
|
for value in &self.keys {
|
||||||
writeln!(f, "{}", value)?;
|
writeln!(f, "{}", value)?;
|
||||||
}
|
}
|
||||||
if let Some(value) = &self.map_tag {
|
if let Some(value) = &self.map_tag {
|
||||||
|
@ -75,41 +76,38 @@ impl fmt::Display for MediaSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MediaSegment {
|
impl MediaSegment {
|
||||||
/// Creates a [MediaSegmentBuilder].
|
/// Creates a [`MediaSegmentBuilder`].
|
||||||
pub fn builder() -> MediaSegmentBuilder { MediaSegmentBuilder::default() }
|
pub fn builder() -> MediaSegmentBuilder { MediaSegmentBuilder::default() }
|
||||||
|
|
||||||
/// Returns the URI of the media segment.
|
/// Returns the `URI` of the media segment.
|
||||||
pub const fn uri(&self) -> &String { &self.uri }
|
pub const fn uri(&self) -> &String { &self.uri }
|
||||||
|
|
||||||
/// Returns the `EXT-X-INF` tag associated with the media segment.
|
/// Returns the [`ExtInf`] tag associated with the media segment.
|
||||||
pub const fn inf_tag(&self) -> &ExtInf { &self.inf_tag }
|
pub const fn inf_tag(&self) -> &ExtInf { &self.inf_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-BYTERANGE` tag associated with the media segment.
|
/// Returns the [`ExtXByteRange`] tag associated with the media segment.
|
||||||
pub const fn byte_range_tag(&self) -> Option<ExtXByteRange> { self.byte_range_tag }
|
pub const fn byte_range_tag(&self) -> Option<ExtXByteRange> { self.byte_range_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-DATERANGE` tag associated with the media segment.
|
/// Returns the [`ExtXDateRange`] tag associated with the media segment.
|
||||||
pub fn date_range_tag(&self) -> Option<&ExtXDateRange> { self.date_range_tag.as_ref() }
|
pub const fn date_range_tag(&self) -> &Option<ExtXDateRange> { &self.date_range_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-DISCONTINUITY` tag associated with the media segment.
|
/// Returns the [`ExtXDiscontinuity`] tag associated with the media segment.
|
||||||
pub const fn discontinuity_tag(&self) -> Option<ExtXDiscontinuity> { self.discontinuity_tag }
|
pub const fn discontinuity_tag(&self) -> Option<ExtXDiscontinuity> { self.discontinuity_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-PROGRAM-DATE-TIME` tag associated with the media
|
/// Returns the [`ExtXProgramDateTime`] tag associated with the media
|
||||||
/// segment.
|
/// segment.
|
||||||
pub fn program_date_time_tag(&self) -> Option<&ExtXProgramDateTime> {
|
pub const fn program_date_time_tag(&self) -> Option<ExtXProgramDateTime> {
|
||||||
self.program_date_time_tag.as_ref()
|
self.program_date_time_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `EXT-X-MAP` tag associated with the media segment.
|
/// Returns the [`ExtXMap`] tag associated with the media segment.
|
||||||
pub fn map_tag(&self) -> Option<&ExtXMap> { self.map_tag.as_ref() }
|
pub const fn map_tag(&self) -> &Option<ExtXMap> { &self.map_tag }
|
||||||
|
|
||||||
/// Returns the `EXT-X-KEY` tags associated with the media segment.
|
|
||||||
pub fn key_tags(&self) -> &[ExtXKey] { &self.key_tags }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RequiredVersion for MediaSegment {
|
impl RequiredVersion for MediaSegment {
|
||||||
fn required_version(&self) -> ProtocolVersion {
|
fn required_version(&self) -> ProtocolVersion {
|
||||||
iter::empty()
|
iter::empty()
|
||||||
.chain(self.key_tags.iter().map(|t| t.required_version()))
|
.chain(self.keys.iter().map(|t| t.required_version()))
|
||||||
.chain(self.map_tag.iter().map(|t| t.required_version()))
|
.chain(self.map_tag.iter().map(|t| t.required_version()))
|
||||||
.chain(self.byte_range_tag.iter().map(|t| t.required_version()))
|
.chain(self.byte_range_tag.iter().map(|t| t.required_version()))
|
||||||
.chain(self.date_range_tag.iter().map(|t| t.required_version()))
|
.chain(self.date_range_tag.iter().map(|t| t.required_version()))
|
||||||
|
@ -121,6 +119,12 @@ impl RequiredVersion for MediaSegment {
|
||||||
)
|
)
|
||||||
.chain(iter::once(self.inf_tag.required_version()))
|
.chain(iter::once(self.inf_tag.required_version()))
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(ProtocolVersion::V7)
|
.unwrap_or_else(ProtocolVersion::latest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encrypted for MediaSegment {
|
||||||
|
fn keys(&self) -> &Vec<ExtXKey> { &self.keys }
|
||||||
|
|
||||||
|
fn keys_mut(&mut self) -> &mut Vec<ExtXKey> { &mut self.keys }
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.1.1. EXTM3U]
|
/// # [4.4.1.1. EXTM3U]
|
||||||
/// The [`ExtM3u`] tag indicates that the file is an **Ext**ended **[`M3U`]**
|
/// The [`ExtM3u`] tag indicates that the file is an **Ext**ended **[`M3U`]**
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.1.2. EXT-X-VERSION]
|
/// # [4.4.1.2. EXT-X-VERSION]
|
||||||
/// The [`ExtXVersion`] tag indicates the compatibility version of the
|
/// The [`ExtXVersion`] tag indicates the compatibility version of the
|
||||||
|
|
|
@ -3,9 +3,9 @@ use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion, StreamInf};
|
use crate::types::{ProtocolVersion, StreamInf};
|
||||||
use crate::utils::{quote, tag, unquote};
|
use crate::utils::{quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.5.3. EXT-X-I-FRAME-STREAM-INF]
|
/// # [4.4.5.3. EXT-X-I-FRAME-STREAM-INF]
|
||||||
/// The [`ExtXIFrameStreamInf`] tag identifies a [`Media Playlist`] file,
|
/// The [`ExtXIFrameStreamInf`] tag identifies a [`Media Playlist`] file,
|
||||||
|
|
|
@ -4,9 +4,9 @@ use std::str::FromStr;
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{Channels, InStreamId, MediaType, ProtocolVersion, RequiredVersion};
|
use crate::types::{Channels, InStreamId, MediaType, ProtocolVersion};
|
||||||
use crate::utils::{parse_yes_or_no, quote, tag, unquote};
|
use crate::utils::{parse_yes_or_no, quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.5.1. EXT-X-MEDIA]
|
/// # [4.4.5.1. EXT-X-MEDIA]
|
||||||
/// The [`ExtXMedia`] tag is used to relate [`Media Playlist`]s,
|
/// The [`ExtXMedia`] tag is used to relate [`Media Playlist`]s,
|
||||||
|
|
|
@ -4,9 +4,9 @@ use std::str::FromStr;
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::{quote, tag, unquote};
|
use crate::utils::{quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// The data of an [ExtXSessionData] tag.
|
/// The data of an [ExtXSessionData] tag.
|
||||||
#[derive(Hash, Eq, Ord, Debug, PartialEq, Clone, PartialOrd)]
|
#[derive(Hash, Eq, Ord, Debug, PartialEq, Clone, PartialOrd)]
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{DecryptionKey, EncryptionMethod, ProtocolVersion, RequiredVersion};
|
use crate::types::{DecryptionKey, EncryptionMethod, ProtocolVersion};
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.3.4.5. EXT-X-SESSION-KEY]
|
/// # [4.3.4.5. EXT-X-SESSION-KEY]
|
||||||
/// The [`ExtXSessionKey`] tag allows encryption keys from [`Media Playlist`]s
|
/// The [`ExtXSessionKey`] tag allows encryption keys from [`Media Playlist`]s
|
||||||
|
|
|
@ -3,11 +3,9 @@ use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{
|
use crate::types::{ClosedCaptions, DecimalFloatingPoint, ProtocolVersion, StreamInf};
|
||||||
ClosedCaptions, DecimalFloatingPoint, ProtocolVersion, RequiredVersion, StreamInf,
|
|
||||||
};
|
|
||||||
use crate::utils::{quote, tag, unquote};
|
use crate::utils::{quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// [4.3.4.2. EXT-X-STREAM-INF]
|
/// [4.3.4.2. EXT-X-STREAM-INF]
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
|
use crate::RequiredVersion;
|
||||||
|
|
||||||
/// # [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]
|
/// # [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.3.4. EXT-X-ENDLIST]
|
/// # [4.4.3.4. EXT-X-ENDLIST]
|
||||||
/// The [`ExtXEndList`] tag indicates, that no more [`Media Segment`]s will be
|
/// The [`ExtXEndList`] tag indicates, that no more [`Media Segment`]s will be
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.3.6. EXT-X-I-FRAMES-ONLY]
|
/// # [4.4.3.6. EXT-X-I-FRAMES-ONLY]
|
||||||
/// The [`ExtXIFramesOnly`] tag indicates that each [`Media Segment`] in the
|
/// The [`ExtXIFramesOnly`] tag indicates that each [`Media Segment`] in the
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.3.2. EXT-X-MEDIA-SEQUENCE]
|
/// # [4.4.3.2. EXT-X-MEDIA-SEQUENCE]
|
||||||
/// The [`ExtXMediaSequence`] tag indicates the Media Sequence Number of
|
/// The [`ExtXMediaSequence`] tag indicates the Media Sequence Number of
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.3.5. EXT-X-PLAYLIST-TYPE]
|
/// # [4.4.3.5. EXT-X-PLAYLIST-TYPE]
|
||||||
///
|
///
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.3.1. EXT-X-TARGETDURATION]
|
/// # [4.4.3.1. EXT-X-TARGETDURATION]
|
||||||
/// The [`ExtXTargetDuration`] tag specifies the maximum [`Media Segment`]
|
/// The [`ExtXTargetDuration`] tag specifies the maximum [`Media Segment`]
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ByteRange, ProtocolVersion, RequiredVersion};
|
use crate::types::{ByteRange, ProtocolVersion};
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.2.2. EXT-X-BYTERANGE]
|
/// # [4.4.2.2. EXT-X-BYTERANGE]
|
||||||
///
|
///
|
||||||
|
|
|
@ -6,9 +6,9 @@ use std::time::Duration;
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::{quote, tag, unquote};
|
use crate::utils::{quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.3.2.7. EXT-X-DATERANGE]
|
/// # [4.3.2.7. EXT-X-DATERANGE]
|
||||||
///
|
///
|
||||||
|
@ -106,7 +106,8 @@ impl ExtXDateRange {
|
||||||
/// # use hls_m3u8::tags::ExtXDateRange;
|
/// # use hls_m3u8::tags::ExtXDateRange;
|
||||||
/// use chrono::offset::TimeZone;
|
/// use chrono::offset::TimeZone;
|
||||||
/// use chrono::{DateTime, FixedOffset};
|
/// use chrono::{DateTime, FixedOffset};
|
||||||
/// use hls_m3u8::types::{ProtocolVersion, RequiredVersion};
|
/// use hls_m3u8::types::ProtocolVersion;
|
||||||
|
/// use hls_m3u8::RequiredVersion;
|
||||||
///
|
///
|
||||||
/// const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
|
/// const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.2.3. EXT-X-DISCONTINUITY]
|
/// # [4.4.2.3. EXT-X-DISCONTINUITY]
|
||||||
/// The [`ExtXDiscontinuity`] tag indicates a discontinuity between the
|
/// The [`ExtXDiscontinuity`] tag indicates a discontinuity between the
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.2.1. EXTINF]
|
/// # [4.4.2.1. EXTINF]
|
||||||
///
|
///
|
||||||
|
|
|
@ -2,9 +2,10 @@ use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{ByteRange, ProtocolVersion, RequiredVersion};
|
use crate::tags::ExtXKey;
|
||||||
|
use crate::types::{ByteRange, ProtocolVersion};
|
||||||
use crate::utils::{quote, tag, unquote};
|
use crate::utils::{quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::{Encrypted, Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.4.2.5. EXT-X-MAP]
|
/// # [4.4.2.5. EXT-X-MAP]
|
||||||
/// The [`ExtXMap`] tag specifies how to obtain the Media Initialization
|
/// The [`ExtXMap`] tag specifies how to obtain the Media Initialization
|
||||||
|
@ -22,6 +23,7 @@ use crate::Error;
|
||||||
pub struct ExtXMap {
|
pub struct ExtXMap {
|
||||||
uri: String,
|
uri: String,
|
||||||
range: Option<ByteRange>,
|
range: Option<ByteRange>,
|
||||||
|
keys: Vec<ExtXKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtXMap {
|
impl ExtXMap {
|
||||||
|
@ -29,17 +31,19 @@ impl ExtXMap {
|
||||||
|
|
||||||
/// Makes a new [`ExtXMap`] tag.
|
/// Makes a new [`ExtXMap`] tag.
|
||||||
pub fn new<T: ToString>(uri: T) -> Self {
|
pub fn new<T: ToString>(uri: T) -> Self {
|
||||||
ExtXMap {
|
Self {
|
||||||
uri: uri.to_string(),
|
uri: uri.to_string(),
|
||||||
range: None,
|
range: None,
|
||||||
|
keys: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Makes a new [`ExtXMap`] tag with the given range.
|
/// Makes a new [`ExtXMap`] tag with the given range.
|
||||||
pub fn with_range<T: ToString>(uri: T, range: ByteRange) -> Self {
|
pub fn with_range<T: ToString>(uri: T, range: ByteRange) -> Self {
|
||||||
ExtXMap {
|
Self {
|
||||||
uri: uri.to_string(),
|
uri: uri.to_string(),
|
||||||
range: Some(range),
|
range: Some(range),
|
||||||
|
keys: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +55,13 @@ impl ExtXMap {
|
||||||
pub const fn range(&self) -> Option<ByteRange> { self.range }
|
pub const fn range(&self) -> Option<ByteRange> { self.range }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encrypted for ExtXMap {
|
||||||
|
fn keys(&self) -> &Vec<ExtXKey> { &self.keys }
|
||||||
|
|
||||||
|
fn keys_mut(&mut self) -> &mut Vec<ExtXKey> { &mut self.keys }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This tag requires [`ProtocolVersion::V6`].
|
||||||
impl RequiredVersion for ExtXMap {
|
impl RequiredVersion for ExtXMap {
|
||||||
fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V6 }
|
fn required_version(&self) -> ProtocolVersion { ProtocolVersion::V6 }
|
||||||
}
|
}
|
||||||
|
@ -59,9 +70,11 @@ impl fmt::Display for ExtXMap {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", Self::PREFIX)?;
|
write!(f, "{}", Self::PREFIX)?;
|
||||||
write!(f, "URI={}", quote(&self.uri))?;
|
write!(f, "URI={}", quote(&self.uri))?;
|
||||||
|
|
||||||
if let Some(value) = &self.range {
|
if let Some(value) = &self.range {
|
||||||
write!(f, ",BYTERANGE={}", quote(value))?;
|
write!(f, ",BYTERANGE={}", quote(value))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,7 +103,11 @@ impl FromStr for ExtXMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
let uri = uri.ok_or_else(|| Error::missing_value("EXT-X-URI"))?;
|
let uri = uri.ok_or_else(|| Error::missing_value("EXT-X-URI"))?;
|
||||||
Ok(ExtXMap { uri, range })
|
Ok(Self {
|
||||||
|
uri,
|
||||||
|
range,
|
||||||
|
keys: vec![],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// # [4.3.2.6. EXT-X-PROGRAM-DATE-TIME]
|
/// # [4.3.2.6. EXT-X-PROGRAM-DATE-TIME]
|
||||||
/// The [`ExtXProgramDateTime`] tag associates the first sample of a
|
/// The [`ExtXProgramDateTime`] tag associates the first sample of a
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::tag;
|
use crate::utils::tag;
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]
|
/// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]
|
||||||
///
|
///
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion, SignedDecimalFloatingPoint};
|
use crate::types::{ProtocolVersion, SignedDecimalFloatingPoint};
|
||||||
use crate::utils::{parse_yes_or_no, tag};
|
use crate::utils::{parse_yes_or_no, tag};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// [4.3.5.2. EXT-X-START]
|
/// [4.3.5.2. EXT-X-START]
|
||||||
///
|
///
|
||||||
|
|
124
src/traits.rs
Normal file
124
src/traits.rs
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
use crate::tags::ExtXKey;
|
||||||
|
use crate::types::{EncryptionMethod, ProtocolVersion};
|
||||||
|
|
||||||
|
/// A trait, that is implemented on all tags, that could be encrypted.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use hls_m3u8::tags::ExtXKey;
|
||||||
|
/// use hls_m3u8::types::EncryptionMethod;
|
||||||
|
/// use hls_m3u8::Encrypted;
|
||||||
|
///
|
||||||
|
/// struct ExampleTag {
|
||||||
|
/// keys: Vec<ExtXKey>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Implementing the trait is very simple:
|
||||||
|
/// // Simply expose the internal buffer, that contains all the keys.
|
||||||
|
/// impl Encrypted for ExampleTag {
|
||||||
|
/// fn keys(&self) -> &Vec<ExtXKey> { &self.keys }
|
||||||
|
///
|
||||||
|
/// fn keys_mut(&mut self) -> &mut Vec<ExtXKey> { &mut self.keys }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let mut example_tag = ExampleTag { keys: vec![] };
|
||||||
|
///
|
||||||
|
/// // adding new keys:
|
||||||
|
/// example_tag.set_keys(vec![ExtXKey::empty()]);
|
||||||
|
/// example_tag.push_key(ExtXKey::new(
|
||||||
|
/// EncryptionMethod::Aes128,
|
||||||
|
/// "http://www.example.com/data.bin",
|
||||||
|
/// ));
|
||||||
|
///
|
||||||
|
/// // getting the keys:
|
||||||
|
/// assert_eq!(
|
||||||
|
/// example_tag.keys(),
|
||||||
|
/// &vec![
|
||||||
|
/// ExtXKey::empty(),
|
||||||
|
/// ExtXKey::new(EncryptionMethod::Aes128, "http://www.example.com/data.bin",)
|
||||||
|
/// ]
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// example_tag.keys_mut(),
|
||||||
|
/// &mut vec![
|
||||||
|
/// ExtXKey::empty(),
|
||||||
|
/// ExtXKey::new(EncryptionMethod::Aes128, "http://www.example.com/data.bin",)
|
||||||
|
/// ]
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// assert!(example_tag.is_encrypted());
|
||||||
|
/// assert!(!example_tag.is_not_encrypted());
|
||||||
|
/// ```
|
||||||
|
pub trait Encrypted {
|
||||||
|
/// Returns a shared reference to all keys, that can be used to decrypt this
|
||||||
|
/// tag.
|
||||||
|
fn keys(&self) -> &Vec<ExtXKey>;
|
||||||
|
|
||||||
|
/// Returns an exclusive reference to all keys, that can be used to decrypt
|
||||||
|
/// this tag.
|
||||||
|
fn keys_mut(&mut self) -> &mut Vec<ExtXKey>;
|
||||||
|
|
||||||
|
/// Sets all keys, that can be used to decrypt this tag.
|
||||||
|
fn set_keys(&mut self, value: Vec<ExtXKey>) -> &mut Self {
|
||||||
|
let keys = self.keys_mut();
|
||||||
|
*keys = value;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a single key to the list of keys, that can be used to decrypt this
|
||||||
|
/// tag.
|
||||||
|
fn push_key(&mut self, value: ExtXKey) -> &mut Self {
|
||||||
|
self.keys_mut().push(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true`, if the tag is encrypted.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This will return `true`, if any of the keys satisfies
|
||||||
|
/// ```text
|
||||||
|
/// key.method() != EncryptionMethod::None
|
||||||
|
/// ```
|
||||||
|
fn is_encrypted(&self) -> bool {
|
||||||
|
if self.keys().is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.keys()
|
||||||
|
.iter()
|
||||||
|
.any(|k| k.method() != EncryptionMethod::None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `false`, if the tag is not encrypted.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This is the inverse of [`is_encrypted`].
|
||||||
|
///
|
||||||
|
/// [`is_encrypted`]: #method.is_encrypted
|
||||||
|
fn is_not_encrypted(&self) -> bool { !self.is_encrypted() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Example
|
||||||
|
/// Implementing it:
|
||||||
|
/// ```
|
||||||
|
/// # use hls_m3u8::RequiredVersion;
|
||||||
|
/// use hls_m3u8::types::ProtocolVersion;
|
||||||
|
///
|
||||||
|
/// struct ExampleTag(u64);
|
||||||
|
///
|
||||||
|
/// impl RequiredVersion for ExampleTag {
|
||||||
|
/// fn required_version(&self) -> ProtocolVersion {
|
||||||
|
/// if self.0 == 5 {
|
||||||
|
/// ProtocolVersion::V4
|
||||||
|
/// } else {
|
||||||
|
/// ProtocolVersion::V1
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// assert_eq!(ExampleTag(5).required_version(), ProtocolVersion::V4);
|
||||||
|
/// assert_eq!(ExampleTag(2).required_version(), ProtocolVersion::V1);
|
||||||
|
/// ```
|
||||||
|
pub trait RequiredVersion {
|
||||||
|
/// Returns the protocol compatibility version that this tag requires.
|
||||||
|
fn required_version(&self) -> ProtocolVersion;
|
||||||
|
}
|
|
@ -6,10 +6,9 @@ use derive_builder::Builder;
|
||||||
use crate::attribute::AttributePairs;
|
use crate::attribute::AttributePairs;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
EncryptionMethod, InitializationVector, KeyFormat, KeyFormatVersions, ProtocolVersion,
|
EncryptionMethod, InitializationVector, KeyFormat, KeyFormatVersions, ProtocolVersion,
|
||||||
RequiredVersion,
|
|
||||||
};
|
};
|
||||||
use crate::utils::{quote, unquote};
|
use crate::utils::{quote, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
#[derive(Builder, Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Builder, Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[builder(setter(into), build_fn(validate = "Self::validate"))]
|
#[builder(setter(into), build_fn(validate = "Self::validate"))]
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::{quote, tag, unquote};
|
use crate::utils::{quote, tag, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||||
/// [`KeyFormat`] specifies, how the key is represented in the
|
/// [`KeyFormat`] specifies, how the key is represented in the
|
||||||
|
|
|
@ -2,9 +2,9 @@ use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::types::{ProtocolVersion, RequiredVersion};
|
use crate::types::ProtocolVersion;
|
||||||
use crate::utils::{quote, unquote};
|
use crate::utils::{quote, unquote};
|
||||||
use crate::Error;
|
use crate::{Error, RequiredVersion};
|
||||||
|
|
||||||
/// A list of [usize], that can be used to indicate which version(s)
|
/// A list of [usize], that can be used to indicate which version(s)
|
||||||
/// this instance complies with, if more than one version of a particular
|
/// this instance complies with, if more than one version of a particular
|
||||||
|
|
|
@ -3,30 +3,6 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
/// # Example
|
|
||||||
/// Implementing it:
|
|
||||||
/// ```
|
|
||||||
/// # use hls_m3u8::types::{ProtocolVersion, RequiredVersion};
|
|
||||||
/// #
|
|
||||||
/// struct NewTag(u64);
|
|
||||||
///
|
|
||||||
/// impl RequiredVersion for NewTag {
|
|
||||||
/// fn required_version(&self) -> ProtocolVersion {
|
|
||||||
/// if self.0 == 5 {
|
|
||||||
/// ProtocolVersion::V4
|
|
||||||
/// } else {
|
|
||||||
/// ProtocolVersion::V1
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// assert_eq!(NewTag(5).required_version(), ProtocolVersion::V4);
|
|
||||||
/// assert_eq!(NewTag(2).required_version(), ProtocolVersion::V1);
|
|
||||||
/// ```
|
|
||||||
pub trait RequiredVersion {
|
|
||||||
/// Returns the protocol compatibility version that this tag requires.
|
|
||||||
fn required_version(&self) -> ProtocolVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # [7. Protocol Version Compatibility]
|
/// # [7. Protocol Version Compatibility]
|
||||||
/// The [`ProtocolVersion`] specifies, which m3u8 revision is required, to parse
|
/// The [`ProtocolVersion`] specifies, which m3u8 revision is required, to parse
|
||||||
/// a certain tag correctly.
|
/// a certain tag correctly.
|
||||||
|
|
Loading…
Reference in a new issue