package importmedia import ( "encoding/base64" "os" "path/filepath" "regexp" "strings" ) var langSuffixRe = regexp.MustCompile(`(?i)\.(nl|nld|dut|dutch|en|eng|english|forced|sdh|hi)(?:\.(forced|sdh|hi))?$`) var subtitleExt = map[string]string{ ".srt": "srt", ".vtt": "vtt", ".ass": "ass", ".ssa": "ass", } const maxSidecarBytes = 2 * 1024 * 1024 type SidecarHit struct { FileName string Language string Format string Content []byte } // FindSidecarsBesideVideo returns subtitle files in the same folder that match the video stem. func FindSidecarsBesideVideo(videoPath string) ([]SidecarHit, error) { videoPath = filepath.Clean(strings.TrimSpace(videoPath)) if videoPath == "" { return nil, nil } info, err := os.Stat(videoPath) if err != nil || info.IsDir() { return nil, err } dir := filepath.Dir(videoPath) stem := strings.TrimSuffix(filepath.Base(videoPath), filepath.Ext(videoPath)) stemLower := strings.ToLower(stem) entries, err := os.ReadDir(dir) if err != nil { return nil, err } var out []SidecarHit for _, ent := range entries { if ent.IsDir() { continue } name := ent.Name() ext := strings.ToLower(filepath.Ext(name)) format, ok := subtitleExt[ext] if !ok { continue } base := strings.TrimSuffix(name, filepath.Ext(name)) baseLower := strings.ToLower(base) if !sidecarMatchesStem(stemLower, baseLower) { continue } full := filepath.Join(dir, name) fi, err := ent.Info() if err != nil || fi.Size() <= 0 || fi.Size() > maxSidecarBytes { continue } raw, err := os.ReadFile(full) if err != nil || len(raw) == 0 { continue } out = append(out, SidecarHit{ FileName: name, Language: guessLangFromName(baseLower, stemLower), Format: format, Content: raw, }) } return out, nil } func sidecarMatchesStem(stemLower, subBaseLower string) bool { if subBaseLower == stemLower { return true } if strings.HasPrefix(subBaseLower, stemLower+".") { return true } // Strip known lang tags and compare stripped := langSuffixRe.ReplaceAllString(subBaseLower, "") return stripped == stemLower } func guessLangFromName(subBaseLower, stemLower string) string { rest := strings.TrimPrefix(subBaseLower, stemLower) rest = strings.TrimPrefix(rest, ".") if rest == "" { return "nl" } parts := strings.Split(rest, ".") for _, p := range parts { switch p { case "nl", "nld", "dut", "dutch": return "nl" case "en", "eng", "english": return "en" } } return "nl" } // EncodeSidecarBase64 helper for control payloads. func EncodeSidecarBase64(b []byte) string { return base64.StdEncoding.EncodeToString(b) }