2022-03-19 02:35:23 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"math/rand"
|
|
|
|
|
"net"
|
|
|
|
|
"os"
|
2024-12-18 23:05:03 +02:00
|
|
|
"os/exec"
|
2022-03-19 02:35:23 +02:00
|
|
|
"path"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func getProgressSocket(inFileName string, teaP *tea.Program) string {
|
2024-12-18 23:05:03 +02:00
|
|
|
cmd := exec.Command("ffprobe", "-show_format", "-show_streams", "-of", "json", inFileName)
|
|
|
|
|
probe, err := cmd.Output()
|
2022-03-19 02:35:23 +02:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
2024-12-18 23:05:03 +02:00
|
|
|
totalDuration, err := probeDuration(string(probe))
|
2022-03-19 02:35:23 +02:00
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TempSock(totalDuration, teaP)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TempSock(totalDuration float64, teaP *tea.Program) string {
|
|
|
|
|
// serve
|
|
|
|
|
|
|
|
|
|
sockFileName := path.Join(os.TempDir(), fmt.Sprintf("%d_sock", rand.Int()))
|
|
|
|
|
l, err := net.Listen("unix", sockFileName)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
re := regexp.MustCompile(`out_time_ms=(\d+)`)
|
2024-09-07 03:56:09 +02:00
|
|
|
speedRe := regexp.MustCompile(`speed=(\d+\.\d+x)`)
|
2024-12-27 20:59:04 +02:00
|
|
|
totalSizeRe := regexp.MustCompile(`total_size=(\d+)`)
|
2024-09-07 03:56:09 +02:00
|
|
|
|
2022-03-19 02:35:23 +02:00
|
|
|
fd, err := l.Accept()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal("accept error:", err)
|
|
|
|
|
}
|
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
|
data := ""
|
|
|
|
|
progress := ""
|
2024-09-07 03:56:09 +02:00
|
|
|
estimate := 0
|
|
|
|
|
|
2022-03-19 02:35:23 +02:00
|
|
|
for {
|
|
|
|
|
_, err := fd.Read(buf)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
data = string(buf)
|
|
|
|
|
a := re.FindAllStringSubmatch(data, -1)
|
|
|
|
|
cp := ""
|
|
|
|
|
if len(a) > 0 && len(a[len(a)-1]) > 0 {
|
|
|
|
|
c, _ := strconv.Atoi(a[len(a)-1][len(a[len(a)-1])-1])
|
|
|
|
|
cp = fmt.Sprintf("%.2f", float64(c)/totalDuration/1000000)
|
|
|
|
|
}
|
|
|
|
|
if strings.Contains(data, "progress=end") {
|
2024-12-27 20:59:04 +02:00
|
|
|
totalSize := totalSizeRe.FindString(data)
|
|
|
|
|
if len(totalSize) > 0 && strings.Split(totalSize, "=")[1] == "0" {
|
|
|
|
|
// Command failed. Don't send a finishedEncodingVideo message.
|
|
|
|
|
// encode() will take care of reporting the error.
|
|
|
|
|
continue
|
|
|
|
|
}
|
2022-03-19 02:35:23 +02:00
|
|
|
cp = "done"
|
|
|
|
|
teaP.Send(finishedEncodingVideo{})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if cp == "" {
|
|
|
|
|
cp = ".0"
|
|
|
|
|
}
|
|
|
|
|
if cp != progress {
|
|
|
|
|
progress = cp
|
|
|
|
|
progressFloat, err := strconv.ParseFloat(progress, 64)
|
|
|
|
|
if err == nil {
|
|
|
|
|
teaP.Send(updateProgress{
|
2022-03-21 01:46:36 +02:00
|
|
|
progress: progressFloat,
|
2022-03-19 02:35:23 +02:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-07 03:56:09 +02:00
|
|
|
|
|
|
|
|
speedMatch := speedRe.FindString(data)
|
|
|
|
|
if len(speedMatch) > 0 {
|
|
|
|
|
trimmedSpeed := strings.TrimSuffix(strings.Split(speedMatch, "=")[1], "x")
|
|
|
|
|
speed, _ := strconv.ParseFloat(trimmedSpeed, 32)
|
|
|
|
|
progressFloat, _ := strconv.ParseFloat(progress, 64)
|
|
|
|
|
remainingDuration := totalDuration - (totalDuration * progressFloat)
|
|
|
|
|
estimate = int(remainingDuration / speed)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
teaP.Send(updateEstimate{
|
|
|
|
|
estimate: estimate,
|
|
|
|
|
})
|
2022-03-19 02:35:23 +02:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
return sockFileName
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type probeFormat struct {
|
|
|
|
|
Duration string `json:"duration"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type probeData struct {
|
|
|
|
|
Format probeFormat `json:"format"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func probeDuration(a string) (float64, error) {
|
|
|
|
|
pd := probeData{}
|
|
|
|
|
err := json.Unmarshal([]byte(a), &pd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
f, err := strconv.ParseFloat(pd.Format.Duration, 64)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
return f, nil
|
|
|
|
|
}
|