unarr/internal/library/mediainfo/lang.go
Deivid Soto 677a8fe083 feat: add migrate command, media server detection, and debrid auto-config
- Migration wizard from Sonarr/Radarr/Prowlarr (unarr migrate) [pre-beta]
  - Auto-detect instances via Docker, config files, port scan, Prowlarr
  - Import wanted list (monitored+missing movies/series)
  - Import download history and blocklist to avoid re-downloading
  - Extract debrid tokens from *arr download clients
  - Quality profile mapping to preferred_quality config
  - DISTINCT ON PostgreSQL query for optimal torrent selection
  - JSON export with --dry-run --json (text to stderr, JSON to stdout)
- Media server detection (Plex/Jellyfin/Emby) in unarr init
  - Detects library paths and offers them as download directory options
- Debrid auto-configuration in unarr init
  - Scans *arr instances for debrid tokens
  - Validates and saves via API if user confirms
- New preferred_quality setting in config (2160p/1080p/720p)
- Library scan command (unarr scan) with ffprobe metadata extraction
2026-03-29 16:54:32 +02:00

115 lines
3.7 KiB
Go

package mediainfo
import (
"sort"
"strings"
)
// langNormalize maps ISO 639-2/B, 639-2/T, 639-1 codes, and full English
// language names (as returned by some ffprobe metadata) to ISO 639-1.
var langNormalize = map[string]string{
// ISO codes
"eng": "en", "en": "en",
"spa": "es", "es": "es",
"fre": "fr", "fra": "fr", "fr": "fr",
"ger": "de", "deu": "de", "de": "de",
"ita": "it", "it": "it",
"por": "pt", "pt": "pt",
"rus": "ru", "ru": "ru",
"jpn": "ja", "ja": "ja",
"kor": "ko", "ko": "ko",
"chi": "zh", "zho": "zh", "zh": "zh",
"hin": "hi", "hi": "hi",
"ara": "ar", "ar": "ar",
"dut": "nl", "nld": "nl", "nl": "nl",
"pol": "pl", "pl": "pl",
"tur": "tr", "tr": "tr",
"swe": "sv", "sv": "sv",
"nor": "no", "nob": "no", "nno": "no", "no": "no",
"dan": "da", "da": "da",
"fin": "fi", "fi": "fi",
"cze": "cs", "ces": "cs", "cs": "cs",
"hun": "hu", "hu": "hu",
"rum": "ro", "ron": "ro", "ro": "ro",
"gre": "el", "ell": "el", "el": "el",
"tha": "th", "th": "th",
"vie": "vi", "vi": "vi",
"ind": "id", "id": "id",
"heb": "he", "he": "he",
"ukr": "uk", "uk": "uk",
"cat": "ca", "ca": "ca",
"bul": "bg", "bg": "bg",
"hrv": "hr", "hr": "hr",
"srp": "sr", "sr": "sr",
"slv": "sl", "sl": "sl",
"lit": "lt", "lt": "lt",
"lav": "lv", "lv": "lv",
"est": "et", "et": "et",
"per": "fa", "fas": "fa", "fa": "fa",
"may": "ms", "msa": "ms", "ms": "ms",
"tgl": "tl", "tl": "tl",
"tam": "ta", "ta": "ta",
"tel": "te", "te": "te",
"ben": "bn", "bn": "bn",
"urd": "ur", "ur": "ur",
"geo": "ka", "kat": "ka", "ka": "ka",
"arm": "hy", "hye": "hy", "hy": "hy",
"alb": "sq", "sqi": "sq", "sq": "sq",
"mac": "mk", "mkd": "mk", "mk": "mk",
"ice": "is", "isl": "is", "is": "is",
"glg": "gl", "gl": "gl",
"baq": "eu", "eus": "eu", "eu": "eu",
"wel": "cy", "cym": "cy", "cy": "cy",
"gle": "ga", "ga": "ga",
"mlt": "mt", "mt": "mt",
"swa": "sw", "sw": "sw",
"afr": "af", "af": "af",
"lat": "la", "la": "la",
// Full English names (ffprobe sometimes returns these instead of codes)
"english": "en", "spanish": "es", "french": "fr", "german": "de",
"italian": "it", "portuguese": "pt", "russian": "ru", "japanese": "ja",
"korean": "ko", "chinese": "zh", "hindi": "hi", "arabic": "ar",
"dutch": "nl", "polish": "pl", "turkish": "tr", "swedish": "sv",
"norwegian": "no", "danish": "da", "finnish": "fi", "czech": "cs",
"hungarian": "hu", "romanian": "ro", "greek": "el", "thai": "th",
"vietnamese": "vi", "indonesian": "id", "hebrew": "he", "ukrainian": "uk",
"catalan": "ca", "bulgarian": "bg", "croatian": "hr", "serbian": "sr",
"slovenian": "sl", "lithuanian": "lt", "latvian": "lv", "estonian": "et",
"persian": "fa", "malay": "ms", "tagalog": "tl", "tamil": "ta",
"telugu": "te", "bengali": "bn", "urdu": "ur", "georgian": "ka",
"armenian": "hy", "albanian": "sq", "macedonian": "mk", "icelandic": "is",
"galician": "gl", "basque": "eu", "welsh": "cy", "irish": "ga",
"maltese": "mt", "swahili": "sw", "afrikaans": "af", "latin": "la",
}
// NormalizeLang converts a language code to ISO 639-1.
// Returns "und" for empty input, the input lowercased if no mapping is found.
func NormalizeLang(raw string) string {
if raw == "" {
return "und"
}
lower := strings.ToLower(raw)
if mapped, ok := langNormalize[lower]; ok {
return mapped
}
return lower
}
// ComputeLanguages extracts unique ISO 639-1 language codes from audio tracks.
func ComputeLanguages(audioTracks []AudioTrack) []string {
seen := make(map[string]struct{})
for _, t := range audioTracks {
lang := t.Lang
if lang != "" && lang != "und" && len(lang) <= 3 {
seen[lang] = struct{}{}
}
}
result := make([]string, 0, len(seen))
for l := range seen {
result = append(result, l)
}
sort.Strings(result)
return result
}