146 lines
3.9 KiB
Rust
146 lines
3.9 KiB
Rust
//! Turning a track's language tag into an [`arr_core::Language`].
|
|
//!
|
|
//! 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::{
|
|
lang::{resolve_portuguese, PortugueseEvidence},
|
|
Language,
|
|
};
|
|
|
|
/// ISO-639-2 to ISO-639-1, for the codes `ffprobe` actually emits.
|
|
///
|
|
/// TMDB reports a title's original language as ISO-639-1 (`en`), `ffprobe`
|
|
/// reports a track's as ISO-639-2/B (`eng`). The §5.2 rule compares the two,
|
|
/// so they have to be spelled the same way by the time they meet.
|
|
const ISO_639_2_TO_1: [(&str, &str); 40] = [
|
|
("ara", "ar"),
|
|
("bul", "bg"),
|
|
("cat", "ca"),
|
|
("ces", "cs"),
|
|
("chi", "zh"),
|
|
("cze", "cs"),
|
|
("dan", "da"),
|
|
("deu", "de"),
|
|
("dut", "nl"),
|
|
("ell", "el"),
|
|
("eng", "en"),
|
|
("fin", "fi"),
|
|
("fra", "fr"),
|
|
("fre", "fr"),
|
|
("ger", "de"),
|
|
("gle", "ga"),
|
|
("heb", "he"),
|
|
("hin", "hi"),
|
|
("hun", "hu"),
|
|
("ind", "id"),
|
|
("isl", "is"),
|
|
("ita", "it"),
|
|
("jpn", "ja"),
|
|
("kor", "ko"),
|
|
("nld", "nl"),
|
|
("nor", "no"),
|
|
("pol", "pl"),
|
|
("ron", "ro"),
|
|
("rum", "ro"),
|
|
("rus", "ru"),
|
|
("slk", "sk"),
|
|
("slo", "sk"),
|
|
("spa", "es"),
|
|
("swe", "sv"),
|
|
("tha", "th"),
|
|
("tur", "tr"),
|
|
("ukr", "uk"),
|
|
("und", "und"),
|
|
("vie", "vi"),
|
|
("zho", "zh"),
|
|
];
|
|
|
|
/// 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 normalised = tag.unwrap_or("und").trim().to_ascii_lowercase();
|
|
let primary = normalised
|
|
.split(['-', '_'])
|
|
.next()
|
|
.unwrap_or(normalised.as_str());
|
|
|
|
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,
|
|
});
|
|
}
|
|
|
|
let code = ISO_639_2_TO_1
|
|
.iter()
|
|
.find(|(long, _)| *long == primary)
|
|
.map_or(primary, |(_, short)| *short);
|
|
Language::Other(code.to_owned())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use arr_core::Language;
|
|
|
|
use super::resolve;
|
|
|
|
#[test]
|
|
fn iso_639_2_becomes_iso_639_1() {
|
|
assert_eq!(
|
|
resolve(Some("eng"), None, None),
|
|
Language::Other("en".to_owned())
|
|
);
|
|
assert_eq!(
|
|
resolve(Some("ger"), None, None),
|
|
Language::Other("de".to_owned())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn an_untagged_track_is_undetermined_not_portuguese() {
|
|
assert_eq!(resolve(None, None, None), Language::Other("und".to_owned()));
|
|
}
|
|
|
|
#[test]
|
|
fn bcp_47_region_decides_on_its_own() {
|
|
assert_eq!(
|
|
resolve(Some("pt-BR"), None, None),
|
|
Language::PortugueseBrazil
|
|
);
|
|
assert_eq!(
|
|
resolve(Some("pt-PT"), None, None),
|
|
Language::PortuguesePortugal
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn the_track_title_decides_when_the_code_cannot() {
|
|
assert_eq!(
|
|
resolve(Some("por"), Some("Portuguese (Brazil)"), None),
|
|
Language::PortugueseBrazil
|
|
);
|
|
assert_eq!(
|
|
resolve(Some("por"), None, Some("Português (Portugal)")),
|
|
Language::PortuguesePortugal
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn plain_portuguese_stays_unverified() {
|
|
assert_eq!(
|
|
resolve(Some("por"), Some("Português"), Some("SoundHandler")),
|
|
Language::PortugueseUnverified
|
|
);
|
|
assert_eq!(
|
|
resolve(Some("pt"), None, None),
|
|
Language::PortugueseUnverified
|
|
);
|
|
}
|
|
}
|