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`].
|
//! 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
|
//! The Portuguese case is not decided here. `arr-core::lang` owns the §5.2
|
||||||
//! variants, so `ffprobe` reports `por` for a Lisbon dub and a São Paulo one
|
//! signal order, and this module's job is to hand it the two signals a file
|
||||||
//! alike. DESIGN.md §5.2 lists three signals; two of them are visible here —
|
//! carries — the stream `title` and `handler_name` strings, and the
|
||||||
//! the stream `title` and `handler_name` strings, and container-level BCP-47
|
//! container's BCP-47 tag. The third signal, release-name markers, belongs to
|
||||||
//! tags. The third, release-name markers, belongs to `arr-parse` and is
|
//! the release and is deliberately not consulted from a file probe.
|
||||||
//! 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.
|
|
||||||
|
|
||||||
use arr_core::Language;
|
use arr_core::{
|
||||||
|
lang::{resolve_portuguese, PortugueseEvidence},
|
||||||
/// Strings a Brazilian track labels itself with.
|
Language,
|
||||||
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",
|
|
||||||
];
|
|
||||||
|
|
||||||
/// ISO-639-2 to ISO-639-1, for the codes `ffprobe` actually emits.
|
/// 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"),
|
("bul", "bg"),
|
||||||
("cat", "ca"),
|
("cat", "ca"),
|
||||||
("ces", "cs"),
|
("ces", "cs"),
|
||||||
|
("chi", "zh"),
|
||||||
("cze", "cs"),
|
("cze", "cs"),
|
||||||
("dan", "da"),
|
("dan", "da"),
|
||||||
("deu", "de"),
|
("deu", "de"),
|
||||||
@@ -66,59 +54,34 @@ const ISO_639_2_TO_1: [(&str, &str); 40] = [
|
|||||||
("tha", "th"),
|
("tha", "th"),
|
||||||
("tur", "tr"),
|
("tur", "tr"),
|
||||||
("ukr", "uk"),
|
("ukr", "uk"),
|
||||||
|
("und", "und"),
|
||||||
("vie", "vi"),
|
("vie", "vi"),
|
||||||
("zho", "zh"),
|
("zho", "zh"),
|
||||||
("chi", "zh"),
|
|
||||||
("und", "und"),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Resolve one track's language from its tag and its self-description.
|
/// 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 {
|
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 normalised = tag.unwrap_or("und").trim().to_ascii_lowercase();
|
||||||
let (primary, region) = match tag.split_once(['-', '_']) {
|
let primary = normalised
|
||||||
Some((primary, rest)) => (primary, rest.split(['-', '_']).next().unwrap_or("")),
|
.split(['-', '_'])
|
||||||
None => (tag.as_str(), ""),
|
.next()
|
||||||
};
|
.unwrap_or(normalised.as_str());
|
||||||
|
|
||||||
if !matches!(primary, "pt" | "por") {
|
if matches!(primary, "pt" | "por") {
|
||||||
let code = ISO_639_2_TO_1
|
return resolve_portuguese(PortugueseEvidence {
|
||||||
.iter()
|
// Signal 1 is a property of the release, not of the file.
|
||||||
.find(|(long, _)| *long == primary)
|
name_markers: &[],
|
||||||
.map_or(primary, |(_, short)| *short);
|
stream_title: title,
|
||||||
return Language::Other(code.to_owned());
|
handler_name: handler,
|
||||||
|
container_tag: tag,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
match region {
|
let code = ISO_639_2_TO_1
|
||||||
"br" => return Language::PortugueseBrazil,
|
.iter()
|
||||||
"pt" => return Language::PortuguesePortugal,
|
.find(|(long, _)| *long == primary)
|
||||||
_ => {}
|
.map_or(primary, |(_, short)| *short);
|
||||||
}
|
Language::Other(code.to_owned())
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -163,13 +126,9 @@ mod tests {
|
|||||||
Language::PortugueseBrazil
|
Language::PortugueseBrazil
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolve(Some("por"), None, Some("Português Europeu")),
|
resolve(Some("por"), None, Some("Português (Portugal)")),
|
||||||
Language::PortuguesePortugal
|
Language::PortuguesePortugal
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
resolve(Some("por"), Some("Dublado"), None),
|
|
||||||
Language::PortugueseBrazil
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user