From c263ccbda08395232360e08bc07bfe38663ef001 Mon Sep 17 00:00:00 2001 From: ChaotiCryptidz Date: Thu, 4 Aug 2022 15:59:06 +0100 Subject: [PATCH] progress shenanigan! --- commands/copy/copy.go | 2 +- commands/transcode/transcode.go | 17 +++++- utils/transcoder/transcoder.go | 104 +++++++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/commands/copy/copy.go b/commands/copy/copy.go index 04164a8..6aff434 100644 --- a/commands/copy/copy.go +++ b/commands/copy/copy.go @@ -194,7 +194,7 @@ func (c *CopyCommand) _transcode_file(file *types.File, trans_config *transcoder log.Info().Str("file", new_filename).Msg("Transcoding File") } - output, err := transcoder.Transcode(file, trans_config, dest_filepath) + output, err := transcoder.Transcode(file, trans_config, nil, dest_filepath) if err != nil { log.Fatal().Err(err).Str("output", output).Msg("Transcode Failed") } diff --git a/commands/transcode/transcode.go b/commands/transcode/transcode.go index 51d424c..f36d3fc 100644 --- a/commands/transcode/transcode.go +++ b/commands/transcode/transcode.go @@ -95,7 +95,22 @@ func (c *TranscodeCommand) Run() { output_file.Extension, transcode_config.FileExtension) } - output, err := transcoder.Transcode(input_file, transcode_config, output_file.JoinPathTo()) + progress_chan := make(chan string) + go func() { + for { + progress, isnt_closed := <-progress_chan + if isnt_closed { + log.Info(). + Str("src", *c.Args.Source). + Str("progress", progress). + Msg("Transcoding") + } else { + return + } + } + }() + + output, err := transcoder.Transcode(input_file, transcode_config, progress_chan, output_file.JoinPathTo()) if err != nil { log.Fatal().Err(err).Str("output", output).Msg("Transcode Failed") } diff --git a/utils/transcoder/transcoder.go b/utils/transcoder/transcoder.go index 4834fc3..14a4396 100644 --- a/utils/transcoder/transcoder.go +++ b/utils/transcoder/transcoder.go @@ -1,10 +1,16 @@ package transcoder import ( + "bufio" + "encoding/json" "errors" "fmt" + "math" + "os" "os/exec" + "strconv" "strings" + "time" "gitlab.com/ChaotiCryptidz/musicutil/types" ) @@ -29,7 +35,59 @@ func isNotEmptyString(x string) bool { return true } -func Transcode(file *types.File, config *TranscodeConfig, dest string) (string, error) { +func progressDurationToMilliSeconds(duration string) int64 { + fields := strings.Split(strings.TrimSpace(duration), ":") + durationNs := int64(0) + + h, _ := strconv.ParseInt(fields[0], 10, 64) + durationNs += int64(time.Hour) * h + + m, _ := strconv.ParseInt(fields[1], 10, 64) + durationNs += int64(time.Minute) * m + + s, _ := strconv.ParseFloat(fields[2], 64) + durationNs += int64(float64(time.Second) * s) + + return time.Duration(durationNs).Milliseconds() +} + +func getFileLengthMilliSeconds(filename string) (int64, error) { + type FFProbeFormat struct { + Duration string `json:"duration"` + } + type FFProbeOutput struct { + Format FFProbeFormat `json:"format"` + } + + ffprobe_output_str, err := exec.Command( + "ffprobe", + "-v", "quiet", + "-print_format", "json", + "-show_format", + filename, + ).Output() + + if err != nil { + return 0, err + } + + var ffprobe_output FFProbeOutput + err = json.Unmarshal([]byte(ffprobe_output_str), &ffprobe_output) + if err != nil { + return 0, err + } else { + duration_str := ffprobe_output.Format.Duration + duration_float, err := strconv.ParseFloat(duration_str, 64) + if err != nil { + return 0, err + } + + duration_millis := int64(math.Round(duration_float * 1000)) + return duration_millis, nil + } +} + +func Transcode(file *types.File, config *TranscodeConfig, progress_chan chan string, dest string) (string, error) { command_args := make([]string, 0) command_args = append(command_args, "-y") command_args = append(command_args, "-hide_banner") @@ -69,11 +127,55 @@ func Transcode(file *types.File, config *TranscodeConfig, dest string) (string, command_args = append(command_args, dest) + // Progress Shenanigans + + if progress_chan != nil { + total_length_milliseconds, err := getFileLengthMilliSeconds(file.JoinPathTo()) + if err != nil { + goto end + } + + progress_temp_dir, err := os.MkdirTemp("", "*-musicutil_transcode_temp") + if err != nil { + goto end + } + defer os.RemoveAll(progress_temp_dir) + + progress_filename := progress_temp_dir + "/progress.log" + progress_file, err := os.Create(progress_filename) + if err != nil { + goto end + } + defer os.Remove(progress_filename) + + command_args = append(command_args, "-progress", progress_filename, "-nostats") + + go func() { + reader := bufio.NewReader(progress_file) + for { + line, err := reader.ReadBytes('\n') + if err == nil { + if strings.HasPrefix(string(line), "out_time=") { + out_time := strings.TrimSuffix(strings.TrimPrefix(string(line), "out_time="), "\n") + out_time_ms := progressDurationToMilliSeconds(out_time) + + progress_chan <- fmt.Sprintf("%0.2f%%", (float64(out_time_ms) / float64(total_length_milliseconds) * 100)) + } + } + } + }() + } + +end: cmd := exec.Command("ffmpeg", command_args...) output, err := cmd.CombinedOutput() if err != nil { fmt.Println(output) return string(output), errors.New("ffmpeg error") } + + if progress_chan != nil { + close(progress_chan) + } return string(output), nil }