package main import ( "strings" "unicode" ) // Trailing dotted suffixes that EPG providers commonly append to channel ids // (e.g. "Italia 1.it"). Conservative allow-list to avoid eating real id chars. var idSuffixes = []string{".it", ".uk", ".de", ".com", ".us", ".fr", ".es", ".tv"} // Trailing video-quality markers commonly appended by either the playlist or // the EPG (e.g. open-epg uses "Italia 1 HD.it" while the playlist may say // "Italia 1"). Stripped symmetrically from both sides. Order matters: longer // markers first so " full hd" wins over a partial " hd" match. var qualitySuffixes = []string{" full hd", " uhd", " fhd", " hd", " 4k", " sd"} // normalizeChannelID returns a stable lookup key for a channel identifier, // collapsing cosmetic divergences across sources: // // "Italia 1" / "Italia 1.it" / "Italia 1 HD.it" / "italia1" → "italia1" func normalizeChannelID(id string) string { s := strings.ToLower(strings.TrimSpace(id)) for _, suf := range idSuffixes { if strings.HasSuffix(s, suf) { s = s[:len(s)-len(suf)] break } } // Strip trailing quality markers, possibly stacked ("Italia 1 Full HD HD"). for { matched := false for _, suf := range qualitySuffixes { if strings.HasSuffix(s, suf) { s = s[:len(s)-len(suf)] matched = true break } } if !matched { break } } var b strings.Builder b.Grow(len(s)) for _, r := range s { if unicode.IsLetter(r) || unicode.IsDigit(r) { b.WriteRune(r) } } return b.String() } // buildIDMap turns a list of playlist tvg-ids into a normalized→canonical // lookup. The canonical form is the playlist's exact tvg-id string; the EPG // merger uses it to rewrite incoming channel/programme references. func buildIDMap(tvgIDs []string) map[string]string { m := make(map[string]string, len(tvgIDs)) for _, id := range tvgIDs { key := normalizeChannelID(id) if key == "" { continue } if _, exists := m[key]; !exists { m[key] = id } } return m }