vsd 0.5.0

A command-line utility and library for downloading streams from DASH manifests and HLS playlists.
Documentation
use crate::{
    dash::{format_locator, parse_channels, parse_frame_rate},
    playlist::{MasterPlaylist, MediaPlaylist, MediaType, PlaylistType},
    utils,
};
use dash_mpd::MPD;
use reqwest::Url;

pub fn parse_as_master(base_url: &Url, mpd: &MPD) -> MasterPlaylist {
    let mut playlist = MasterPlaylist {
        playlist_type: PlaylistType::Dash,
        streams: Vec::new(),
        uri: base_url.to_string(),
    };

    let Some(period) = mpd.periods.first() else {
        return playlist;
    };

    for (adaptation_index, adaptation_set) in period.adaptations.iter().enumerate() {
        for (representation_index, representation) in
            adaptation_set.representations.iter().enumerate()
        {
            let locator = format_locator(adaptation_index, representation_index);

            let codecs = representation
                .codecs
                .clone()
                .or(adaptation_set.codecs.clone());

            let mime_type = representation
                .mimeType
                .clone()
                .or(adaptation_set.mimeType.clone())
                .or(representation.contentType.clone())
                .or(adaptation_set.contentType.clone());

            let mut media_type = if let Some(mime_type) = &mime_type {
                match mime_type.as_str() {
                    "application/ttml+xml" | "application/x-sami" => MediaType::Subtitles,
                    x if x.starts_with("audio") => MediaType::Audio,
                    x if x.starts_with("text") => MediaType::Subtitles,
                    x if x.starts_with("video") => MediaType::Video,
                    _ => MediaType::Undefined,
                }
            } else {
                MediaType::Undefined
            };

            if media_type == MediaType::Undefined
                && let Some(codecs) = &codecs
            {
                media_type = match codecs.as_str() {
                    "wvtt" | "stpp" => MediaType::Subtitles,
                    x if x.starts_with("stpp.") => MediaType::Subtitles,
                    _ => media_type,
                };
            }

            playlist.streams.push(MediaPlaylist {
                bandwidth: representation.bandwidth,
                channels: parse_channels(&representation.AudioChannelConfiguration)
                    .or_else(|| parse_channels(&adaptation_set.AudioChannelConfiguration)),
                codecs,
                extension: mime_type
                    .as_ref()
                    .and_then(|x| x.split_once('/').map(|(_, y)| y.to_owned())),
                frame_rate: representation
                    .frameRate
                    .as_ref()
                    .and_then(|x| parse_frame_rate(x))
                    .or_else(|| {
                        adaptation_set
                            .frameRate
                            .as_ref()
                            .and_then(|x| parse_frame_rate(x))
                    }),
                id: utils::gen_id(base_url.as_str(), &locator),
                i_frame: false,
                language: adaptation_set.lang.clone(),
                live: mpd
                    .mpdtype
                    .as_ref()
                    .map(|x| x == "dynamic")
                    .unwrap_or(false),
                media_sequence: 0,
                media_type,
                playlist_type: PlaylistType::Dash,
                resolution: representation.width.zip(representation.height),
                segments: Vec::new(),
                uri: locator,
            });
        }
    }

    playlist
}