unbundle 5.2.0

Unbundle media files - extract still frames, audio tracks, and subtitles from video files
Documentation
//! Audio stream-copy integration tests.
//!
//! These tests require fixture files generated by
//! `tests/fixtures/generate_fixtures.sh` (or `.bat` on Windows).

use std::{path::Path, time::Duration};

use unbundle::{MediaFile, UnbundleError};

fn sample_video_path() -> &'static str {
    "tests/fixtures/sample_video.mp4"
}

#[test]
fn stream_copy_audio_to_file() {
    let path = sample_video_path();
    if !Path::new(path).exists() {
        return;
    }

    let output = tempfile::NamedTempFile::new().expect("Failed to create temp file");
    let output_path = output.path().with_extension("aac");

    let mut unbundler = MediaFile::open(path).expect("Failed to open test video");
    unbundler
        .audio()
        .stream_copy(&output_path)
        .expect("Failed to stream-copy audio to file");

    let written_bytes = std::fs::read(&output_path).expect("Failed to read output file");
    assert!(
        !written_bytes.is_empty(),
        "Expected non-empty output after audio stream copy"
    );

    let _ = std::fs::remove_file(&output_path);
}

#[test]
fn stream_copy_audio_range_to_file() {
    let path = sample_video_path();
    if !Path::new(path).exists() {
        return;
    }

    let output = tempfile::NamedTempFile::new().expect("Failed to create temp file");
    let output_path = output.path().with_extension("aac");

    let mut unbundler = MediaFile::open(path).expect("Failed to open test video");
    unbundler
        .audio()
        .stream_copy_range(
            &output_path,
            Duration::from_millis(500),
            Duration::from_secs(2),
        )
        .expect("Failed to stream-copy audio range to file");

    let written_bytes = std::fs::read(&output_path).expect("Failed to read output file");
    assert!(
        !written_bytes.is_empty(),
        "Expected non-empty range output after audio stream copy"
    );

    let _ = std::fs::remove_file(&output_path);
}

#[test]
fn stream_copy_audio_to_memory() {
    let path = sample_video_path();
    if !Path::new(path).exists() {
        return;
    }

    let mut unbundler = MediaFile::open(path).expect("Failed to open test video");
    let bytes = unbundler
        .audio()
        .stream_copy_to_memory("adts")
        .expect("Failed to stream-copy audio to memory");

    assert!(
        !bytes.is_empty(),
        "Expected non-empty in-memory payload after audio stream copy"
    );
}

#[test]
fn stream_copy_audio_invalid_range_returns_error() {
    let path = sample_video_path();
    if !Path::new(path).exists() {
        return;
    }

    let mut unbundler = MediaFile::open(path).expect("Failed to open test video");
    let result = unbundler.audio().stream_copy_range(
        "unused.aac",
        Duration::from_secs(2),
        Duration::from_secs(1),
    );

    assert!(result.is_err(), "Expected invalid range error");
    assert!(
        matches!(result.unwrap_err(), UnbundleError::InvalidRange { .. }),
        "Expected InvalidRange variant"
    );
}