Skip to main content

Crate vsd

Crate vsd 

Source
Expand description

A command-line utility and library for downloading HLS and DASH video streams.

vsd enables concurrent chunk/segment downloading, automatic decryption of AES-128 and Sample-AES encrypted streams, PSSH DRM metadata extraction, and automated muxing using ffmpeg.

§Cargo Features

The following Cargo features can be enabled or disabled:

FeatureDescription
capture (default)Enables the capture sub-command.
license (default)Enables the license sub-command.
rustls-tls (default)Enables the rustls TLS backend for the reqwest crate.
native-tlsEnables the native-tls TLS backend for the reqwest crate.
native-tls-vendoredEnables the native-tls-vendored TLS backend for the reqwest crate.

§Examples

Below are examples demonstrating how to use the library to download files and playlists.

§examples/playlist_dl.rs

// This example shows using vsd as library in other rust project.
// Also see https://github.com/clitic/vsd/blob/main/vsd/src/core/playlist.rs
//
// [dependencies]
// vsd = { version = "0.5", default-features = false, features = ["rustls-tls"]}

use std::{io::Write, sync::Arc};
use vsd::{
    Error, Muxer, PlaylistDownloader, Result,
    playlist::MediaType,
    progress::{ByteSize, Eta, ProgressCallback, ProgressState},
    reqwest::Client,
    tokio,
    tokio_util::sync::CancellationToken,
};

struct Progress;

impl ProgressCallback for Progress {
    fn on_progress(&self, state: &ProgressState) {
        let stderr = std::io::stderr();
        let mut handle = stderr.lock();
        write!(
            handle,
            "\r\x1B[2K[{}/~{}({:.0}%) PT:{}/{} DL:{} ETA:{}]",
            ByteSize(state.downloaded_bytes),
            ByteSize(state.estimated_bytes),
            state.percent,
            state.downloaded_parts,
            state.total_parts,
            ByteSize(state.speed_bps as usize),
            Eta(state.eta_seconds)
        )
        .unwrap();
        handle.flush().unwrap();
    }

    fn on_finish(&self, state: &ProgressState) {
        self.on_progress(state);
        eprintln!();
    }
}

#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<()> {
    let client = Client::new();

    // We use ./target as temporary directory for downloaded files.
    let dl = PlaylistDownloader::new(&client).directory("target");
    let config = dl.get_config();

    let mp = dl
        .parse(
            "https://media.axprod.net/TestVectors/Dash/not_protected_dash_1080p_h264/manifest.mpd",
            false,
        )
        .await?;

    // You can clone this token and call .cancel() to pause a download.
    let token = CancellationToken::new();
    let mut muxer = Muxer::new();

    // Download first subtitle stream.
    for stream in mp.streams {
        if stream.media_type == MediaType::Subtitles {
            println!(
                "Downloading {} subtitles",
                stream.language.as_deref().unwrap_or("unknown")
            );

            // We download to a temporary file, mux it in, and then clean up.
            // You could also just move the file after download and avoid muxing if you want.
            let dl_info = match stream.download(config, Arc::new(Progress), &token).await {
                Ok(info) => info,
                Err(Error::MissingSegments) => {
                    println!("Stream has no segments");
                    continue;
                }
                Err(Error::UnsupportedEncryption(e)) => {
                    println!("Unsupported encryption {}", e);
                    continue;
                }
                Err(Error::MissingKey(key_id)) => {
                    println!("Missing decryption key for {key_id}");
                    continue;
                }
                Err(Error::DownloadInterrupted) => {
                    println!("Download paused");
                    std::process::exit(0);
                }
                Err(e) => return Err(e),
            };

            println!("Downloaded {}", dl_info.path.to_string_lossy());
            muxer.push(dl_info);

            break;
        }
    }

    println!("Muxing to target/output.srt");
    muxer
        .mux(&vsd::find_ffmpeg().unwrap(), "target/output.srt", "srt")
        .await?;
    muxer.clean(config.directory.as_deref()).await?;

    Ok(())
}

§examples/file_dl.rs

use vsd::{FileDownloader, Result, reqwest::Client, tokio};

#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<()> {
    let client = Client::new();

    FileDownloader::new(&client)
        .threads(5)
        .resume(true)
        .download(
            "https://github.com/llvm/llvm-project/releases/download/llvmorg-22.1.6/llvm-project-22.1.6.src.tar.xz",
            "target/llvm-project-22.1.6.src.tar.xz",
        )
        .await?;

    println!("Download complete!");
    Ok(())
}

Re-exports§

pub use reqwest;
pub use tokio;
pub use tokio_util;
pub use vsd_mp4;

Modules§

cookie
Parser and representation of HTTP cookies in Netscape format.
playlist
Common types used for parsing, representing, and describing HLS and DASH playlists.
progress
Progress rendering, speed calculation, and status reporting utilities.

Macros§

bail
Early-returns with an Error::Other. Accepts formatted message parameters.

Structs§

FileDownloader
A multi-threaded, resume-capable file downloader.
Muxer
A wrapper around a collection of Streams to be merged.
PlaylistDownloadConfig
Configuration options for the playlist download process.
PlaylistDownloader
A downloader for HLS and DASH playlist streams.
Stream
Represents a single media stream track (video, audio, or subtitle) ready for muxing.

Enums§

Error
Represents errors that can occur during playlist fetching, parsing, downloading, or merging.

Functions§

find_ffmpeg
Discovers the absolute path of the ffmpeg executable binary.
gen_id
Generates a short, unique 7-character hexadecimal identifier.

Type Aliases§

Result
A specialized Result type for operations in this crate.