diff --git a/TODO.md b/TODO.md index 4e7fc08..ee45932 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,3 @@ ## TODO: Don't show incompatible encoding options for encoders -## TODO: send a SIGINT to the ffmpeg process on ctrl+c ## TODO: calculate total progress based on file sizes instead of counts of files for a more accurate progress percentage. ## TODO: when passing in a directory, list all video files in that directory and have the user choose which ones they want encoded diff --git a/app.go b/app.go index 5a7d4ee..cd29c12 100644 --- a/app.go +++ b/app.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "log" "os" "os/exec" "path/filepath" @@ -36,7 +37,9 @@ type model struct { TotalProgress float64 Estimate int Program *tea.Program + Command *exec.Cmd Quitting bool + Cancelled bool Screen Screen Config map[int]Config FocusIndex int @@ -83,12 +86,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "ctrl+c", "esc": - // TODO: cancel the ffmpeg command context to terminate the process - return m, tea.Quit - } case errQuitMsg: m.ErrQuitMessage = msg.msg m.ErrQuit = true @@ -101,6 +98,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: key := msg.String() switch key { + case "ctrl+c", "esc": + return m, tea.Quit case "enter", " ": // parse config and switch to main screen if we're focused on the start button if m.FocusIndex == len(Configs) { @@ -149,6 +148,18 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case Main: switch msg := msg.(type) { + case tea.KeyMsg: + key := msg.String() + switch key { + case "ctrl+c": + m.Cancelled = true + err := m.Command.Process.Signal(os.Interrupt) + if err != nil { + log.Println("An error occurred when sending SIGINT to the ffmpeg process:") + log.Println(err) + } + return m, tea.Quit + } case initUi: m.FileCount = msg.fileCount m.Files = msg.files @@ -166,6 +177,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { encode(fullFilePath, filepath.Base(fullFilePath), m.Program, m.ParsedConfig) }() + return m, nil + case ffmpegProcessStart: + log.Printf("Running command: %s\n", msg.cmd.String()) + m.Command = msg.cmd return m, nil case finishedEncodingVideo: if m.ParsedConfig.DeleteOldVideo { @@ -300,20 +315,27 @@ func formatEstimate(estimate int) string { func (m model) View() string { if m.Quitting { if m.DryRun { - fullFilePath := m.Files[len(m.Files)-1] - parentDir := filepath.Dir(fullFilePath) - fileName := filepath.Base(fullFilePath) - extensionIndex := strings.LastIndex(fileName, ".") - newFileName := fileName[:extensionIndex] - extension := fileName[extensionIndex:] - outFileFullPath := filepath.Join(parentDir, newFileName+fmt.Sprintf("_[%s]_[%s]", m.ParsedConfig.VideoEncoder, m.ParsedConfig.AudioEncoder)+extension) + sb := strings.Builder{} + for _, file := range m.Files { + parentDir := filepath.Dir(file) + fileName := filepath.Base(file) + extensionIndex := strings.LastIndex(fileName, ".") + newFileName := fileName[:extensionIndex] + extension := fileName[extensionIndex:] + outFileFullPath := filepath.Join(parentDir, newFileName+fmt.Sprintf("_[%s]_[%s]", m.ParsedConfig.VideoEncoder, m.ParsedConfig.AudioEncoder)+extension) - cmd := exec.Command("ffmpeg", buildFFmpegCmdArgs(fullFilePath, outFileFullPath, m.ParsedConfig)...) + cmd := exec.Command("ffmpeg", buildFFmpegCmdArgs(file, outFileFullPath, m.ParsedConfig)...) - return fmt.Sprintf("%s %s\n", checkmark, cmd.String()) + sb.WriteString(cmd.String()) + sb.WriteByte('\n') + } + + return fmt.Sprintf("%s %s\n", checkmark, sb.String()) } else { return fmt.Sprintf("%s %d/%d files encoded\n", checkmark, m.FileCount-len(m.Files), m.FileCount) } + } else if m.Cancelled { + return fmt.Sprintf("%s Encoding cancelled. Stopped ffmpeg process.\n Make sure to clean up the created file\n", x) } if m.ErrQuit { diff --git a/cmds.go b/cmds.go index ba367d1..ae3e885 100644 --- a/cmds.go +++ b/cmds.go @@ -4,6 +4,7 @@ import ( "fmt" "log" "os" + "os/exec" "path/filepath" "sort" "strings" @@ -20,6 +21,10 @@ type updateEstimate struct { estimate int } +type ffmpegProcessStart struct { + cmd *exec.Cmd +} + type initUi struct { fileCount int files []string diff --git a/main.go b/main.go index 1301539..9e16e9b 100644 --- a/main.go +++ b/main.go @@ -120,7 +120,7 @@ func encode(fullFilePath string, fileName string, teaP *tea.Program, cfg ParsedC if _, err := os.Stat(newFileFullPath); err == nil { if cfg.SkipEncodedVid { - log.Printf("%s already exists with the exact same encodings (crf and preset might be different though), skipping.", newFileFullPath) + log.Printf("Skipping \"%s\" because it already exists with the exact same encodings (crf and preset might be different though)", newFileFullPath) teaP.Send(finishedEncodingVideo{}) return } else { @@ -130,8 +130,7 @@ func encode(fullFilePath string, fileName string, teaP *tea.Program, cfg ParsedC cmdArgs := buildFFmpegCmdArgs(fullFilePath, newFileFullPath, cfg, "-progress", "unix://"+getProgressSocket(fullFilePath, teaP)) cmd := exec.Command("ffmpeg", cmdArgs...) - // TODO: send the cmd obj as a tea msg so we can send interrupt signals to the process when ctrl+c is pressed - //teaP.Send() + teaP.Send(ffmpegProcessStart{cmd}) err = cmd.Run() if err != nil {