diff --git a/app.go b/app.go index a975446..e54ea08 100644 --- a/app.go +++ b/app.go @@ -4,11 +4,13 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/charmbracelet/bubbles/progress" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" + ffmpeg "github.com/u2takey/ffmpeg-go" ) type Screen int @@ -38,6 +40,7 @@ type model struct { Config map[int]Config FocusIndex int ParsedConfig ParsedConfig + DryRun bool ErrQuit bool ErrQuitMessage string } @@ -99,7 +102,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "enter", " ": // parse config and switch to main screen if we're focused on the start button if m.FocusIndex == len(Configs) { - return m, m.parseConfig + return m, m.parseConfig(false) + } + // parse config, print it and exit + if m.FocusIndex == len(Configs)+1 { + return m, m.parseConfig(true) } case "tab", "shift+tab", "up", "down": if key == "up" || key == "shift+tab" { @@ -108,10 +115,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.FocusIndex++ } - if m.FocusIndex >= len(Configs)+1 { + if m.FocusIndex >= len(Configs)+2 { m.FocusIndex = 0 } else if m.FocusIndex < 0 { - m.FocusIndex = len(Configs) + m.FocusIndex = len(Configs) + 1 } case "left", "right": if cfg, ok := m.Config[m.FocusIndex]; ok { @@ -130,6 +137,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.Config[m.FocusIndex] = cfg } } + case quitMsg: + m.Quitting = true + return m, tea.Quit case parsedCfgMsg: m.Screen = Main m.ParsedConfig = msg.parsedConfig @@ -141,6 +151,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.FileCount = msg.fileCount m.Files = msg.files + if m.DryRun { + return m, gracefullyQuit + } + return m, encodeVideo case encodeVideoMsg: m.CurrentFileName = filepath.Base(m.Files[len(m.Files)-1]) @@ -160,7 +174,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.Files = m.Files[:len(m.Files)-1] if len(m.Files) == 0 { - return m, m.gracefullyQuit + return m, gracefullyQuit } return m, encodeVideo @@ -217,9 +231,15 @@ func CfgScreenView(m model) string { } if m.FocusIndex == len(Configs) { - view += FocusedButton + view += FocusedStartButton } else { - view += BlurredButton + view += BlurredStartButton + } + + if m.FocusIndex == len(Configs)+1 { + view += FocusedDryRunButton + } else { + view += BlurredDryRunButton } view += "\n" @@ -252,7 +272,25 @@ func MainScreenView(m model) string { func (m model) View() string { if m.Quitting { - return fmt.Sprintf("%s %d/%d files encoded\n", checkmark, m.FileCount-len(m.Files), m.FileCount) + if m.DryRun { + fullFilePath := m.Files[len(m.Files)-1] + fileName := filepath.Base(fullFilePath) + extensionIndex := strings.LastIndex(fileName, ".") + extension := fileName[extensionIndex:] + newFullFilePath := "./test-out" + extension + + cli := ffmpeg.Input(fullFilePath).Output(newFullFilePath, ffmpeg.KwArgs{ + "c:v": m.ParsedConfig.VideoEncoder, + "crf": m.ParsedConfig.CRF, + "preset": m.ParsedConfig.Preset, + "c:a": m.ParsedConfig.AudioEncoder, + "b:a": "128k"}). + Compile() + + return fmt.Sprintf("%s %s\n", checkmark, strings.Join(cli.Args, " ")) + } else { + return fmt.Sprintf("%s %d/%d files encoded\n", checkmark, m.FileCount-len(m.Files), m.FileCount) + } } if m.ErrQuit { diff --git a/cmds.go b/cmds.go index ba23981..cbd1fbb 100644 --- a/cmds.go +++ b/cmds.go @@ -66,7 +66,7 @@ func encodeVideo() tea.Msg { type quitMsg struct{} -func (m model) gracefullyQuit() tea.Msg { +func gracefullyQuit() tea.Msg { return quitMsg{} } @@ -78,23 +78,29 @@ func initCfg() tea.Msg { type parsedCfgMsg struct { parsedConfig ParsedConfig + dryRun bool } -func (m *model) parseConfig() tea.Msg { - vEncoder := find(m.Config, "Video Encoder") - aEncoder := find(m.Config, "Audio Encoder") - preset := find(m.Config, "Preset (libx264 & libx265 only)") - crf := find(m.Config, "Constant Rate Factor (CRF)") +func (m *model) parseConfig(dryRun bool) tea.Cmd { + m.DryRun = dryRun - return parsedCfgMsg{ - parsedConfig: ParsedConfig{ - DeleteOldVideo: find(m.Config, "Delete old video(s)?").FocusedOption != 0, - SkipEncodedVid: find(m.Config, "What should we do about encoded videos?").FocusedOption == 0, - VideoEncoder: vEncoder.Opts[vEncoder.FocusedOption], - AudioEncoder: aEncoder.Opts[aEncoder.FocusedOption], - Preset: preset.Opts[preset.FocusedOption], - CRF: crf.Opts[crf.FocusedOption], - }, + return func() tea.Msg { + vEncoder := find(m.Config, "Video Encoder") + aEncoder := find(m.Config, "Audio Encoder") + preset := find(m.Config, "Preset (libx264 & libx265 only)") + crf := find(m.Config, "Constant Rate Factor (CRF)") + + return parsedCfgMsg{ + parsedConfig: ParsedConfig{ + DeleteOldVideo: find(m.Config, "Delete old video(s)?").FocusedOption != 0, + SkipEncodedVid: find(m.Config, "What should we do about encoded videos?").FocusedOption == 0, + VideoEncoder: vEncoder.Opts[vEncoder.FocusedOption], + AudioEncoder: aEncoder.Opts[aEncoder.FocusedOption], + Preset: preset.Opts[preset.FocusedOption], + CRF: crf.Opts[crf.FocusedOption], + }, + dryRun: dryRun, + } } } diff --git a/go.mod b/go.mod index 55b7b39..22aaadc 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,5 @@ require ( github.com/charmbracelet/bubbletea v0.20.0 github.com/charmbracelet/lipgloss v0.5.0 github.com/gabriel-vasile/mimetype v1.4.0 - github.com/u2takey/ffmpeg-go v0.4.1 + github.com/u2takey/ffmpeg-go v0.5.0 ) diff --git a/go.sum b/go.sum index a9d09a7..2c5bd1b 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/u2takey/ffmpeg-go v0.4.1 h1:l5ClIwL3N2LaH1zF3xivb3kP2HW95eyG5xhHE1JdZ9Y= github.com/u2takey/ffmpeg-go v0.4.1/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc= +github.com/u2takey/ffmpeg-go v0.5.0 h1:r7d86XuL7uLWJ5mzSeQ03uvjfIhiJYvsRAJFCW4uklU= +github.com/u2takey/ffmpeg-go v0.5.0/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc= github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys= github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs= gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs= diff --git a/main.go b/main.go index 93d14d4..604f06d 100644 --- a/main.go +++ b/main.go @@ -79,8 +79,11 @@ func encode(fullFilePath string, fileName string, teaP *tea.Program, cfg ParsedC } } - err = ffmpeg.Input(fullFilePath). - Output(newFileFullPath, ffmpeg.KwArgs{ + quotedFilePath := fmt.Sprintf("\"%s\"", fullFilePath) + quotedNewFilePath := fmt.Sprintf("\"%s\"", newFileFullPath) + + err = ffmpeg.Input(quotedFilePath). + Output(quotedNewFilePath, ffmpeg.KwArgs{ "c:v": cfg.VideoEncoder, "crf": cfg.CRF, "preset": cfg.Preset, diff --git a/styles.go b/styles.go index 60eddb1..bdf18ec 100644 --- a/styles.go +++ b/styles.go @@ -32,21 +32,39 @@ var ( FocusedOption = lipgloss.NewStyle(). Foreground(SecondaryColor) - FocusedButton = lipgloss.NewStyle(). - Border(lipgloss.NormalBorder()). - BorderForeground(AccentColor). - Foreground(AccentColor). - MarginTop(1). - Padding(0, 2). - Align(lipgloss.Center). - Bold(true). - Render("Start Encoding!") - BlurredButton = lipgloss.NewStyle(). - Border(lipgloss.NormalBorder()). - BorderForeground(PrimaryColor). - Foreground(PrimaryColor). - MarginTop(1). - Padding(0, 2). - Align(lipgloss.Center). - Render("Start Encoding!") + FocusedStartButton = lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(AccentColor). + Foreground(AccentColor). + MarginTop(1). + Padding(0, 2). + Align(lipgloss.Center). + Bold(true). + Render("Start Encoding!") + BlurredStartButton = lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(PrimaryColor). + Foreground(PrimaryColor). + MarginTop(1). + Padding(0, 2). + Align(lipgloss.Center). + Render("Start Encoding!") + + FocusedDryRunButton = lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(AccentColor). + Foreground(AccentColor). + MarginTop(1). + Padding(0, 2). + Align(lipgloss.Center). + Bold(true). + Render("Print FFmpeg command and exit") + BlurredDryRunButton = lipgloss.NewStyle(). + Border(lipgloss.NormalBorder()). + BorderForeground(PrimaryColor). + Foreground(PrimaryColor). + MarginTop(1). + Padding(0, 2). + Align(lipgloss.Center). + Render("Print FFmpeg command and exit") )