From 8e317afc1650ffae8896683e3ce1819bab02c416 Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:49:14 -0700 Subject: [PATCH] source: an image or folder names its title by the meta title, else the playlist, as 1.7.7 ed02bc1 named every disc layout's output title by its meta title, else its volume id. A folder's volume id is its directory name, so a DVD title ripped from an image and from its extracted folder got different segment titles and the two MKVs stopped being byte-identical. A drive still names by meta title, else volume id. --- CHANGELOG.md | 2 +- src/mux/source.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e39efe03..a78568c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ - **`json://`** audio `sample_rate` is a number in Hz (`null` when unknown), not a string such as `"48kHz"`; a `sample_rates` array lists every rate a stream carries. - **FMKV (`network://`, `stdio://`) header version 2** is used when a track carries decoder timing (Opus CodecDelay/SeekPreRoll) and adds per-frame DiscardPadding; streams without timing stay version 1. Headers now also carry display aspect, CICP, stream purpose/qualifier, chapters and content format, and PIDs actually written by M2TS. A header declaring more than 256 streams is refused with E9008. An older receiver omits the MKV Channels element for the new channel-layout strings; keep both ends on one version. - **`dir://` image bytes:** the PVD declares interchange level 3, Unique IDs start at 16 and the volume id round-trips through the d-string encoder; `dir://` sources are stamped as images in the `fvi://` provenance header. -- **One output title name:** a title read from a disc layout (`disc://`, `iso://`, `dir://`, a staged image, a server live rip) is named by the disc's meta title (else its volume id) in every container's title field and in `fvi://` provenance; 1.7.7 named image and server-live rips by the MPLS playlist file. `fvi://` provenance of an `iso://`/`dir://` source now carries the volume id. +- **Output title names:** a title read from a drive (`disc://`, a server live rip) is named by the disc's meta title (else its volume id) in every container's title field and in `fvi://` provenance; one read from an image or folder (`iso://`, `dir://`, a staged image) by the disc's meta title, else its own playlist name as in 1.7.7, so an image and its extracted folder name a title alike. 1.7.7 named server-live rips by the MPLS playlist file. `fvi://` provenance of an `iso://`/`dir://` source now carries the volume id. - **TrueHD 7.1 / Atmos labels on every path:** the playlist labels a 7.1 or Atmos TrueHD track 5.1 at 48 kHz; every mux (live disc, staged image, `iso://`/`dir://`, containers) now completes the track's channels, sample rate and label from its first major sync before the output opens, and `json://` / `chapters://` get the completed title too. 1.7.7 corrected only `iso://`/`dir://` URL rips, so live and staged-image rips now label such tracks 7.1 / Atmos. - **Track selection applies to container inputs:** `selection` keeps a subset of the tracks of an `mkv://`, `mp4://`, `m2ts://`, `network://` or `stdio://` input (renumbered in order, video always kept), as it already did for disc titles; an unknown PID is refused at open. - **A live DVD extracted to `dir://` is descrambled:** `Disc::extract_tree` finds and cracks each scrambled VTS from its content on every DVD-Video layout. A live drive scan records no disc-wide CSS key, so a live DVD's title VOBs were written still scrambled. diff --git a/src/mux/source.rs b/src/mux/source.rs index 66b6b216..22b61944 100644 --- a/src/mux/source.rs +++ b/src/mux/source.rs @@ -30,12 +30,13 @@ pub struct ScannedTitle { } impl ScannedTitle { - /// Title `idx` of `disc`, named by the disc. `None` when out of range. + /// Title `idx` of an image's `disc`, named as [`Source`] names an image's title (its + /// meta title, else the title's own playlist name). `None` when out of range. pub fn of(disc: &Disc, idx: usize) -> Option { Some(ScannedTitle { title: disc.titles.get(idx)?.clone(), format: disc.content_format, - disc_name: Some(disc_name(disc)), + disc_name: disc.meta_title.clone(), volume_id: disc.volume_id.clone(), }) } @@ -294,14 +295,74 @@ impl<'a> Source<'a> { } } - // The output title's name (SO14): every disc layout names it by the disc (its meta - // title, else its volume id); a container keeps its own. + // The output title's name (SO14): a drive's is the disc's meta title, else its volume id; + // an image's or folder's the meta title, else the playlist (a folder's volume id is its + // directory name, and an image and its folder must name a title alike); a container's own. pub(crate) fn title_name(&self) -> Option { match &self.origin { Origin::Prescanned { title, .. } | Origin::Live { title, .. } => { title.disc_name.clone() } + Origin::Image { disc, .. } => disc.meta_title.clone(), _ => self.layout().map(disc_name), } } } + +#[cfg(test)] +mod tests { + use super::*; + + fn disc(volume_id: &str, meta_title: Option<&str>) -> Disc { + let mut title = crate::disc::DiscTitle::empty(); + title.playlist = "VTS_11_26.VOB".into(); + Disc { + volume_id: volume_id.into(), + meta_title: meta_title.map(str::to_string), + format: crate::disc::DiscFormat::Dvd, + capacity_sectors: 1, + capacity_bytes: 2048, + layers: 1, + titles: vec![title], + region: crate::disc::DiscRegion::Free, + aacs: None, + css: None, + encrypted: false, + aacs_error: None, + css_error: None, + content_format: crate::disc::ContentFormat::MpegPs, + } + } + + fn image(d: Disc, folder: bool) -> Source<'static> { + Source { + origin: Origin::Image { + path: PathBuf::from("x"), + folder, + reader: Box::new(crate::test_util::MemSource::new(vec![0u8; 2048])), + disc: d, + }, + } + } + + // An image and its extracted folder name a title alike: the folder's volume id is its + // directory name, so with no meta title both keep the title's playlist name, and the + // engine's pre-scanned image title agrees. + #[test] + fn an_image_and_its_folder_name_a_title_alike() { + let iso = image(disc("GREENLAND", None), false); + let dir = image(disc("fmt_dir", None), true); + assert_eq!(iso.title_name(), None); + assert_eq!(dir.title_name(), None); + let scanned = ScannedTitle::of(&disc("GREENLAND", None), 0).unwrap(); + assert_eq!(scanned.disc_name, None); + assert_eq!(scanned.title.playlist, "VTS_11_26.VOB"); + + let iso = image(disc("GREENLAND", Some("Greenland")), false); + let dir = image(disc("fmt_dir", Some("Greenland")), true); + assert_eq!(iso.title_name().as_deref(), Some("Greenland")); + assert_eq!(dir.title_name().as_deref(), Some("Greenland")); + let scanned = ScannedTitle::of(&disc("GREENLAND", Some("Greenland")), 0).unwrap(); + assert_eq!(scanned.disc_name.as_deref(), Some("Greenland")); + } +}