package ffmpeg import ( "regexp" "strconv" "strings" ) // Progress holds real-time encoding progress from ffmpeg stderr. type Progress struct { Frame int64 `json:"frame"` FPS float64 `json:"fps"` Q float64 `json:"q"` Size string `json:"size"` Time string `json:"time"` Bitrate string `json:"bitrate"` Speed string `json:"speed"` Eta string `json:"eta"` Percent float64 `json:"percent"` } var progressRe = regexp.MustCompile( `frame=\s*(\d+)\s+fps=\s*([\d.]+)\s+q=\s*([\d.-]+)\s+(?:size=\s*(\S+)\s+)?time=\s*([\d:.]+)\s+bitrate=\s*(\S+)\s+speed=\s*(\S+)`, ) func parseProgressLine(line string) (Progress, bool) { m := progressRe.FindStringSubmatch(line) if m == nil { return Progress{}, false } frame, _ := strconv.ParseInt(m[1], 10, 64) fps, _ := strconv.ParseFloat(m[2], 64) q, _ := strconv.ParseFloat(m[3], 64) return Progress{ Frame: frame, FPS: fps, Q: q, Size: m[4], Time: m[5], Bitrate: m[6], Speed: m[7], }, true } // parseSpeedMultiplier converts "1.5x" → 1.5. func parseSpeedMultiplier(s string) float64 { s = strings.TrimSuffix(s, "x") v, _ := strconv.ParseFloat(s, 64) return v } // formatSeconds converts seconds to "MM:SS" or "HH:MM:SS". func formatSeconds(secs float64) string { if secs < 0 { secs = 0 } h := int(secs) / 3600 m := (int(secs) % 3600) / 60 s := int(secs) % 60 if h > 0 { return strconv.Itoa(h) + ":" + pad2(m) + ":" + pad2(s) } return pad2(m) + ":" + pad2(s) } func pad2(n int) string { if n < 10 { return "0" + strconv.Itoa(n) } return strconv.Itoa(n) } // parseTimeSeconds converts "HH:MM:SS.mm" to seconds. func parseTimeSeconds(t string) float64 { parts := strings.Split(t, ":") if len(parts) != 3 { return 0 } h, _ := strconv.ParseFloat(parts[0], 64) m, _ := strconv.ParseFloat(parts[1], 64) s, _ := strconv.ParseFloat(parts[2], 64) return h*3600 + m*60 + s }