Wails v2 + Vue3 + Go project with: - Encode/Remux/Subtitle burn with hardware acceleration - Real-time progress with ffmpeg stderr parsing (\r delimiter handling) - Task queue with cancel support - Per-task log viewer with color-coded output - Custom frameless window with resize support - Dark/light theme toggle - Hardware encoder detection (NVENC/QSV/AMF)
86 lines
1.9 KiB
Go
86 lines
1.9 KiB
Go
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
|
|
}
|