package main import ( "bufio" "bytes" "net/url" "path" "regexp" "strings" ) func rewriteM3U8(raw []byte, id string) []byte { // Wir bauen alle URIs so um, dass sie wieder über /api/record/preview laufen. // Wichtig: play=1 bleibt dran, damit Folge-Requests (segments, chunklists) auch ohne Hover gehen. base := "/api/record/preview?id=" + url.QueryEscape(id) + "&file=" var out bytes.Buffer sc := bufio.NewScanner(bytes.NewReader(raw)) // Scanner default token limit 64K – m3u8 ist normalerweise klein, passt. // Wenn du riesige Playlists hast, kannst du Buffer erhöhen. for sc.Scan() { line := sc.Text() trim := strings.TrimSpace(line) if trim == "" { out.WriteByte('\n') continue } // Kommentare/Tags: ggf. URI="..." in Tags rewriten if strings.HasPrefix(trim, "#") { // EXT-X-KEY:URI="..." line = rewriteAttrURI(line, base) out.WriteString(line) out.WriteByte('\n') continue } // Nicht-Tag => URI (segment oder child-playlist) u := trim // Absolut? dann lassen if strings.HasPrefix(u, "http://") || strings.HasPrefix(u, "https://") { out.WriteString(line) out.WriteByte('\n') continue } // Wenn es schon unser API ist, lassen if strings.Contains(u, "/api/record/preview") { out.WriteString(line) out.WriteByte('\n') continue } // Nur basename nehmen (ffmpeg schreibt i.d.R. keine Subdirs) name := path.Base(u) // Hier play=1 mitschicken: out.WriteString(base + url.QueryEscape(name) + "&play=1") out.WriteByte('\n') } if err := sc.Err(); err != nil { // Wenn Scanner aus irgendeinem Grund scheitert: lieber raw zurück (besser als kaputt) return raw } return out.Bytes() } func rewriteAttrURI(line, base string) string { // Rewritet URI="xyz" in EXT-X-KEY / EXT-X-MAP / EXT-X-MEDIA / EXT-X-I-FRAME-STREAM-INF etc. // Nur relative URIs werden angefasst. const key = `URI="` i := strings.Index(line, key) if i < 0 { return line } j := strings.Index(line[i+len(key):], `"`) if j < 0 { return line } start := i + len(key) end := start + j val := line[start:end] valTrim := strings.TrimSpace(val) // absolut oder schon preview => nix tun if strings.HasPrefix(valTrim, "http://") || strings.HasPrefix(valTrim, "https://") || strings.Contains(valTrim, "/api/record/preview") { return line } name := path.Base(valTrim) repl := base + url.QueryEscape(name) + "&play=1" return line[:start] + repl + line[end:] } func rewriteQuotedURI(line, id string) string { re := regexp.MustCompile(`URI="([^"]+)"`) return re.ReplaceAllStringFunc(line, func(m string) string { sub := re.FindStringSubmatch(m) if len(sub) != 2 { return m } u := sub[1] uu := strings.TrimSpace(u) if uu == "" || strings.HasPrefix(uu, "http://") || strings.HasPrefix(uu, "https://") || strings.HasPrefix(uu, "/") { return m } repl := "/api/record/preview?id=" + url.QueryEscape(id) + "&file=" + url.QueryEscape(uu) return `URI="` + repl + `"` }) } func rewriteM3U8ToPreviewEndpoint(m3u8 string, id string) string { lines := strings.Split(m3u8, "\n") escapedID := url.QueryEscape(id) for i, line := range lines { l := strings.TrimSpace(line) if l == "" || strings.HasPrefix(l, "#") { continue } // Segment/URI-Zeilen umschreiben lines[i] = "/api/record/preview?id=" + escapedID + "&file=" + url.QueryEscape(l) } return strings.Join(lines, "\n") }