Simplify labels API: one call, labels on streams or nothing
labels::apply(session, udf, titles) does everything internally. Disc::scan() is one line: crate::labels::apply(). No intermediate variables, no fallback logic in disc.rs. App reads disc.titles[].streams — labels already applied if disc had config files, empty otherwise. MPLS data always there.
This commit is contained in:
+2
-3
@@ -421,9 +421,8 @@ impl Disc {
|
|||||||
// Step 4: Read disc title from META/DL/bdmt_eng.xml
|
// Step 4: Read disc title from META/DL/bdmt_eng.xml
|
||||||
let meta_title = Self::read_meta_title(session, &udf_fs);
|
let meta_title = Self::read_meta_title(session, &udf_fs);
|
||||||
|
|
||||||
// Step 5: Extract stream labels from disc config files
|
// Step 5: Enhance streams with disc config file labels (if available)
|
||||||
let disc_labels = crate::labels::extract(session, &udf_fs);
|
crate::labels::apply(session, &udf_fs, &mut titles);
|
||||||
Self::apply_disc_labels(&mut titles, &disc_labels);
|
|
||||||
|
|
||||||
// JAR labels (for playlist purpose markers only, not stream labels)
|
// JAR labels (for playlist purpose markers only, not stream labels)
|
||||||
let jar_labels = Self::read_jar_labels(session, &udf_fs);
|
let jar_labels = Self::read_jar_labels(session, &udf_fs);
|
||||||
|
|||||||
+60
-25
@@ -2,12 +2,8 @@
|
|||||||
//!
|
//!
|
||||||
//! Searches the disc UDF filesystem for known config files that contain
|
//! Searches the disc UDF filesystem for known config files that contain
|
||||||
//! stream labels (language, purpose, codec, forced flags). Four formats
|
//! stream labels (language, purpose, codec, forced flags). Four formats
|
||||||
//! supported, tried in order:
|
//! supported, tried in order. If found, labels are applied directly
|
||||||
//!
|
//! to the title streams. If not found, streams keep MPLS data as-is.
|
||||||
//! 1. `language_streams.txt` — Warner CTRM CSV format
|
|
||||||
//! 2. `menu_base.prop` — Warner CTRM properties format
|
|
||||||
//! 3. `streamproperties.xml` + `playbackconfig.xml` — Criterion XML format
|
|
||||||
//! 4. `bluray_project.bin` — Pixelogic binary format
|
|
||||||
|
|
||||||
mod language_streams;
|
mod language_streams;
|
||||||
mod menu_base;
|
mod menu_base;
|
||||||
@@ -16,6 +12,7 @@ mod bluray_project;
|
|||||||
|
|
||||||
use crate::drive::DriveSession;
|
use crate::drive::DriveSession;
|
||||||
use crate::udf::UdfFs;
|
use crate::udf::UdfFs;
|
||||||
|
use crate::disc::{Title, Stream};
|
||||||
|
|
||||||
/// A stream label extracted from disc config files.
|
/// A stream label extracted from disc config files.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -46,43 +43,81 @@ pub enum StreamLabelType {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum LabelPurpose {
|
pub enum LabelPurpose {
|
||||||
/// Normal dialogue track
|
|
||||||
Normal,
|
Normal,
|
||||||
/// Audio commentary
|
|
||||||
Commentary,
|
Commentary,
|
||||||
/// Descriptive audio (visually impaired)
|
|
||||||
Descriptive,
|
Descriptive,
|
||||||
/// Music score only
|
|
||||||
Score,
|
Score,
|
||||||
/// In-movie experience
|
|
||||||
Ime,
|
Ime,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum LabelQualifier {
|
pub enum LabelQualifier {
|
||||||
None,
|
None,
|
||||||
/// Subtitles for deaf and hard of hearing
|
|
||||||
Sdh,
|
Sdh,
|
||||||
/// Descriptive service
|
|
||||||
DescriptiveService,
|
DescriptiveService,
|
||||||
/// Forced/narrative subtitle
|
|
||||||
Forced,
|
Forced,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try all parsers in order, return first successful result.
|
/// Search disc for config files, extract labels, apply to streams.
|
||||||
pub fn extract(session: &mut DriveSession, udf: &UdfFs) -> Vec<StreamLabel> {
|
/// If no config files found, streams are left unchanged.
|
||||||
// Try each format in order
|
pub fn apply(session: &mut DriveSession, udf: &UdfFs, titles: &mut [Title]) {
|
||||||
if let Some(labels) = language_streams::parse(session, udf) {
|
let labels = extract(session, udf);
|
||||||
return labels;
|
if labels.is_empty() { return; }
|
||||||
|
|
||||||
|
for title in titles.iter_mut() {
|
||||||
|
let mut audio_idx: u16 = 0;
|
||||||
|
let mut sub_idx: u16 = 0;
|
||||||
|
|
||||||
|
for stream in &mut title.streams {
|
||||||
|
match stream {
|
||||||
|
Stream::Audio(a) => {
|
||||||
|
audio_idx += 1;
|
||||||
|
if let Some(label) = labels.iter().find(|l|
|
||||||
|
l.stream_type == StreamLabelType::Audio && l.stream_number == audio_idx
|
||||||
|
) {
|
||||||
|
let mut parts = Vec::new();
|
||||||
|
match label.purpose {
|
||||||
|
LabelPurpose::Commentary => parts.push("Commentary".to_string()),
|
||||||
|
LabelPurpose::Descriptive => parts.push("Descriptive Audio".to_string()),
|
||||||
|
LabelPurpose::Score => parts.push("Score".to_string()),
|
||||||
|
LabelPurpose::Ime => parts.push("IME".to_string()),
|
||||||
|
LabelPurpose::Normal => {}
|
||||||
}
|
}
|
||||||
if let Some(labels) = menu_base::parse(session, udf) {
|
if !label.region.is_empty() {
|
||||||
return labels;
|
parts.push(format!("({})", label.region));
|
||||||
}
|
}
|
||||||
if let Some(labels) = stream_properties::parse(session, udf) {
|
if !label.codec_hint.is_empty()
|
||||||
return labels;
|
&& !matches!(label.codec_hint.as_str(), "MLP" | "AC3" | "DTS")
|
||||||
|
{
|
||||||
|
parts.push(label.codec_hint.clone());
|
||||||
}
|
}
|
||||||
if let Some(labels) = bluray_project::parse(session, udf) {
|
if !parts.is_empty() {
|
||||||
return labels;
|
a.label = parts.join(" ");
|
||||||
|
} else if !label.name.is_empty() {
|
||||||
|
a.label = label.name.clone();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stream::Subtitle(s) => {
|
||||||
|
sub_idx += 1;
|
||||||
|
if let Some(label) = labels.iter().find(|l|
|
||||||
|
l.stream_type == StreamLabelType::Subtitle && l.stream_number == sub_idx
|
||||||
|
) {
|
||||||
|
if label.qualifier == LabelQualifier::Forced {
|
||||||
|
s.forced = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract(session: &mut DriveSession, udf: &UdfFs) -> Vec<StreamLabel> {
|
||||||
|
if let Some(labels) = language_streams::parse(session, udf) { return labels; }
|
||||||
|
if let Some(labels) = menu_base::parse(session, udf) { return labels; }
|
||||||
|
if let Some(labels) = stream_properties::parse(session, udf) { return labels; }
|
||||||
|
if let Some(labels) = bluray_project::parse(session, udf) { return labels; }
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user