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.
124 lines
4.3 KiB
Rust
124 lines
4.3 KiB
Rust
//! Stream label extraction from BD-J disc files.
|
|
//!
|
|
//! Searches the disc UDF filesystem for known config files that contain
|
|
//! stream labels (language, purpose, codec, forced flags). Four formats
|
|
//! supported, tried in order. If found, labels are applied directly
|
|
//! to the title streams. If not found, streams keep MPLS data as-is.
|
|
|
|
mod language_streams;
|
|
mod menu_base;
|
|
mod stream_properties;
|
|
mod bluray_project;
|
|
|
|
use crate::drive::DriveSession;
|
|
use crate::udf::UdfFs;
|
|
use crate::disc::{Title, Stream};
|
|
|
|
/// A stream label extracted from disc config files.
|
|
#[derive(Debug, Clone)]
|
|
pub struct StreamLabel {
|
|
/// STN index (1-based)
|
|
pub stream_number: u16,
|
|
/// Audio or Subtitle
|
|
pub stream_type: StreamLabelType,
|
|
/// ISO 639-2 language code
|
|
pub language: String,
|
|
/// Display name (e.g. "AudioEnglishDolby", "English Dolby Atmos")
|
|
pub name: String,
|
|
/// Stream purpose
|
|
pub purpose: LabelPurpose,
|
|
/// Additional qualifier
|
|
pub qualifier: LabelQualifier,
|
|
/// Codec hint from config (e.g. "MLP", "AC3", "atmos")
|
|
pub codec_hint: String,
|
|
/// Regional variant (e.g. "US", "UK", "CF", "CS")
|
|
pub region: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub enum StreamLabelType {
|
|
Audio,
|
|
Subtitle,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub enum LabelPurpose {
|
|
Normal,
|
|
Commentary,
|
|
Descriptive,
|
|
Score,
|
|
Ime,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
pub enum LabelQualifier {
|
|
None,
|
|
Sdh,
|
|
DescriptiveService,
|
|
Forced,
|
|
}
|
|
|
|
/// Search disc for config files, extract labels, apply to streams.
|
|
/// If no config files found, streams are left unchanged.
|
|
pub fn apply(session: &mut DriveSession, udf: &UdfFs, titles: &mut [Title]) {
|
|
let labels = extract(session, udf);
|
|
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 !label.region.is_empty() {
|
|
parts.push(format!("({})", label.region));
|
|
}
|
|
if !label.codec_hint.is_empty()
|
|
&& !matches!(label.codec_hint.as_str(), "MLP" | "AC3" | "DTS")
|
|
{
|
|
parts.push(label.codec_hint.clone());
|
|
}
|
|
if !parts.is_empty() {
|
|
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()
|
|
}
|