refactor(probe): use arr-core for pt-PT versus pt-BR
#57 landed the §5.2 signal order in arr-core::lang while this branch was open. arr-probe now feeds it the two signals a file carries — stream strings and the container's BCP-47 tag — instead of keeping a second copy of the rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
14fa9e43d8
commit
60f7ddda2a
@@ -1,28 +1,15 @@
|
||||
//! Turning a track's language tag into an [`arr_core::Language`].
|
||||
//!
|
||||
//! The interesting case is Portuguese. ISO-639-2 has one code, `por`, for both
|
||||
//! variants, so `ffprobe` reports `por` for a Lisbon dub and a São Paulo one
|
||||
//! alike. DESIGN.md §5.2 lists three signals; two of them are visible here —
|
||||
//! the stream `title` and `handler_name` strings, and container-level BCP-47
|
||||
//! tags. The third, release-name markers, belongs to `arr-parse` and is
|
||||
//! deliberately not consulted from a file probe.
|
||||
//!
|
||||
//! When nothing resolves it the track is [`Language::PortugueseUnverified`].
|
||||
//! Guessing is worse than not knowing: guessing towards `kids` hands a child a
|
||||
//! Brazilian dub, guessing towards `main` rejects a Brazilian film's own audio.
|
||||
//! The Portuguese case is not decided here. `arr-core::lang` owns the §5.2
|
||||
//! signal order, and this module's job is to hand it the two signals a file
|
||||
//! carries — the stream `title` and `handler_name` strings, and the
|
||||
//! container's BCP-47 tag. The third signal, release-name markers, belongs to
|
||||
//! the release and is deliberately not consulted from a file probe.
|
||||
|
||||
use arr_core::Language;
|
||||
|
||||
/// Strings a Brazilian track labels itself with.
|
||||
const BRAZIL_MARKERS: [&str; 8] = [
|
||||
"brazil", "brasil", "pt-br", "pt_br", "ptbr", "dublado", "dublagem", "nacional",
|
||||
];
|
||||
|
||||
/// Strings a European Portuguese track labels itself with. `europeu` and
|
||||
/// `european` only ever appear here to distinguish from Brazilian.
|
||||
const PORTUGAL_MARKERS: [&str; 7] = [
|
||||
"portugal", "pt-pt", "pt_pt", "ptpt", "europeu", "europeia", "european",
|
||||
];
|
||||
use arr_core::{
|
||||
lang::{resolve_portuguese, PortugueseEvidence},
|
||||
Language,
|
||||
};
|
||||
|
||||
/// ISO-639-2 to ISO-639-1, for the codes `ffprobe` actually emits.
|
||||
///
|
||||
@@ -34,6 +21,7 @@ const ISO_639_2_TO_1: [(&str, &str); 40] = [
|
||||
("bul", "bg"),
|
||||
("cat", "ca"),
|
||||
("ces", "cs"),
|
||||
("chi", "zh"),
|
||||
("cze", "cs"),
|
||||
("dan", "da"),
|
||||
("deu", "de"),
|
||||
@@ -66,59 +54,34 @@ const ISO_639_2_TO_1: [(&str, &str); 40] = [
|
||||
("tha", "th"),
|
||||
("tur", "tr"),
|
||||
("ukr", "uk"),
|
||||
("und", "und"),
|
||||
("vie", "vi"),
|
||||
("zho", "zh"),
|
||||
("chi", "zh"),
|
||||
("und", "und"),
|
||||
];
|
||||
|
||||
/// Resolve one track's language from its tag and its self-description.
|
||||
pub(crate) fn resolve(tag: Option<&str>, title: Option<&str>, handler: Option<&str>) -> Language {
|
||||
let tag = tag.unwrap_or("und").trim().to_ascii_lowercase();
|
||||
let (primary, region) = match tag.split_once(['-', '_']) {
|
||||
Some((primary, rest)) => (primary, rest.split(['-', '_']).next().unwrap_or("")),
|
||||
None => (tag.as_str(), ""),
|
||||
};
|
||||
let normalised = tag.unwrap_or("und").trim().to_ascii_lowercase();
|
||||
let primary = normalised
|
||||
.split(['-', '_'])
|
||||
.next()
|
||||
.unwrap_or(normalised.as_str());
|
||||
|
||||
if !matches!(primary, "pt" | "por") {
|
||||
let code = ISO_639_2_TO_1
|
||||
.iter()
|
||||
.find(|(long, _)| *long == primary)
|
||||
.map_or(primary, |(_, short)| *short);
|
||||
return Language::Other(code.to_owned());
|
||||
if matches!(primary, "pt" | "por") {
|
||||
return resolve_portuguese(PortugueseEvidence {
|
||||
// Signal 1 is a property of the release, not of the file.
|
||||
name_markers: &[],
|
||||
stream_title: title,
|
||||
handler_name: handler,
|
||||
container_tag: tag,
|
||||
});
|
||||
}
|
||||
|
||||
match region {
|
||||
"br" => return Language::PortugueseBrazil,
|
||||
"pt" => return Language::PortuguesePortugal,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
from_description(title, handler)
|
||||
}
|
||||
|
||||
/// Signal two: what the track calls itself, which is usually literally
|
||||
/// `Portuguese (Brazil)` when it is one.
|
||||
fn from_description(title: Option<&str>, handler: Option<&str>) -> Language {
|
||||
let described = [title, handler]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(str::to_ascii_lowercase)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let contains = |markers: &[&str]| {
|
||||
described
|
||||
.iter()
|
||||
.any(|text| markers.iter().any(|marker| text.contains(marker)))
|
||||
};
|
||||
|
||||
if contains(&BRAZIL_MARKERS) {
|
||||
Language::PortugueseBrazil
|
||||
} else if contains(&PORTUGAL_MARKERS) {
|
||||
Language::PortuguesePortugal
|
||||
} else {
|
||||
Language::PortugueseUnverified
|
||||
}
|
||||
let code = ISO_639_2_TO_1
|
||||
.iter()
|
||||
.find(|(long, _)| *long == primary)
|
||||
.map_or(primary, |(_, short)| *short);
|
||||
Language::Other(code.to_owned())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -163,13 +126,9 @@ mod tests {
|
||||
Language::PortugueseBrazil
|
||||
);
|
||||
assert_eq!(
|
||||
resolve(Some("por"), None, Some("Português Europeu")),
|
||||
resolve(Some("por"), None, Some("Português (Portugal)")),
|
||||
Language::PortuguesePortugal
|
||||
);
|
||||
assert_eq!(
|
||||
resolve(Some("por"), Some("Dublado"), None),
|
||||
Language::PortugueseBrazil
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user