mirror of
https://github.com/IllusionMan1212/ffui.git
synced 2026-10-24 02:12:35 +00:00
feat: add button to print compiled ffmpeg command and exit
This commit is contained in:
@@ -4,11 +4,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/progress"
|
"github.com/charmbracelet/bubbles/progress"
|
||||||
"github.com/charmbracelet/bubbles/spinner"
|
"github.com/charmbracelet/bubbles/spinner"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
ffmpeg "github.com/u2takey/ffmpeg-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Screen int
|
type Screen int
|
||||||
@@ -38,6 +40,7 @@ type model struct {
|
|||||||
Config map[int]Config
|
Config map[int]Config
|
||||||
FocusIndex int
|
FocusIndex int
|
||||||
ParsedConfig ParsedConfig
|
ParsedConfig ParsedConfig
|
||||||
|
DryRun bool
|
||||||
ErrQuit bool
|
ErrQuit bool
|
||||||
ErrQuitMessage string
|
ErrQuitMessage string
|
||||||
}
|
}
|
||||||
@@ -99,7 +102,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
case "enter", " ":
|
case "enter", " ":
|
||||||
// parse config and switch to main screen if we're focused on the start button
|
// parse config and switch to main screen if we're focused on the start button
|
||||||
if m.FocusIndex == len(Configs) {
|
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":
|
case "tab", "shift+tab", "up", "down":
|
||||||
if key == "up" || key == "shift+tab" {
|
if key == "up" || key == "shift+tab" {
|
||||||
@@ -108,10 +115,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.FocusIndex++
|
m.FocusIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.FocusIndex >= len(Configs)+1 {
|
if m.FocusIndex >= len(Configs)+2 {
|
||||||
m.FocusIndex = 0
|
m.FocusIndex = 0
|
||||||
} else if m.FocusIndex < 0 {
|
} else if m.FocusIndex < 0 {
|
||||||
m.FocusIndex = len(Configs)
|
m.FocusIndex = len(Configs) + 1
|
||||||
}
|
}
|
||||||
case "left", "right":
|
case "left", "right":
|
||||||
if cfg, ok := m.Config[m.FocusIndex]; ok {
|
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
|
m.Config[m.FocusIndex] = cfg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case quitMsg:
|
||||||
|
m.Quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
case parsedCfgMsg:
|
case parsedCfgMsg:
|
||||||
m.Screen = Main
|
m.Screen = Main
|
||||||
m.ParsedConfig = msg.parsedConfig
|
m.ParsedConfig = msg.parsedConfig
|
||||||
@@ -141,6 +151,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.FileCount = msg.fileCount
|
m.FileCount = msg.fileCount
|
||||||
m.Files = msg.files
|
m.Files = msg.files
|
||||||
|
|
||||||
|
if m.DryRun {
|
||||||
|
return m, gracefullyQuit
|
||||||
|
}
|
||||||
|
|
||||||
return m, encodeVideo
|
return m, encodeVideo
|
||||||
case encodeVideoMsg:
|
case encodeVideoMsg:
|
||||||
m.CurrentFileName = filepath.Base(m.Files[len(m.Files)-1])
|
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]
|
m.Files = m.Files[:len(m.Files)-1]
|
||||||
|
|
||||||
if len(m.Files) == 0 {
|
if len(m.Files) == 0 {
|
||||||
return m, m.gracefullyQuit
|
return m, gracefullyQuit
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, encodeVideo
|
return m, encodeVideo
|
||||||
@@ -217,9 +231,15 @@ func CfgScreenView(m model) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.FocusIndex == len(Configs) {
|
if m.FocusIndex == len(Configs) {
|
||||||
view += FocusedButton
|
view += FocusedStartButton
|
||||||
} else {
|
} else {
|
||||||
view += BlurredButton
|
view += BlurredStartButton
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.FocusIndex == len(Configs)+1 {
|
||||||
|
view += FocusedDryRunButton
|
||||||
|
} else {
|
||||||
|
view += BlurredDryRunButton
|
||||||
}
|
}
|
||||||
|
|
||||||
view += "\n"
|
view += "\n"
|
||||||
@@ -252,7 +272,25 @@ func MainScreenView(m model) string {
|
|||||||
|
|
||||||
func (m model) View() string {
|
func (m model) View() string {
|
||||||
if m.Quitting {
|
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 {
|
if m.ErrQuit {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func encodeVideo() tea.Msg {
|
|||||||
|
|
||||||
type quitMsg struct{}
|
type quitMsg struct{}
|
||||||
|
|
||||||
func (m model) gracefullyQuit() tea.Msg {
|
func gracefullyQuit() tea.Msg {
|
||||||
return quitMsg{}
|
return quitMsg{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,23 +78,29 @@ func initCfg() tea.Msg {
|
|||||||
|
|
||||||
type parsedCfgMsg struct {
|
type parsedCfgMsg struct {
|
||||||
parsedConfig ParsedConfig
|
parsedConfig ParsedConfig
|
||||||
|
dryRun bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *model) parseConfig() tea.Msg {
|
func (m *model) parseConfig(dryRun bool) tea.Cmd {
|
||||||
vEncoder := find(m.Config, "Video Encoder")
|
m.DryRun = dryRun
|
||||||
aEncoder := find(m.Config, "Audio Encoder")
|
|
||||||
preset := find(m.Config, "Preset (libx264 & libx265 only)")
|
|
||||||
crf := find(m.Config, "Constant Rate Factor (CRF)")
|
|
||||||
|
|
||||||
return parsedCfgMsg{
|
return func() tea.Msg {
|
||||||
parsedConfig: ParsedConfig{
|
vEncoder := find(m.Config, "Video Encoder")
|
||||||
DeleteOldVideo: find(m.Config, "Delete old video(s)?").FocusedOption != 0,
|
aEncoder := find(m.Config, "Audio Encoder")
|
||||||
SkipEncodedVid: find(m.Config, "What should we do about encoded videos?").FocusedOption == 0,
|
preset := find(m.Config, "Preset (libx264 & libx265 only)")
|
||||||
VideoEncoder: vEncoder.Opts[vEncoder.FocusedOption],
|
crf := find(m.Config, "Constant Rate Factor (CRF)")
|
||||||
AudioEncoder: aEncoder.Opts[aEncoder.FocusedOption],
|
|
||||||
Preset: preset.Opts[preset.FocusedOption],
|
return parsedCfgMsg{
|
||||||
CRF: crf.Opts[crf.FocusedOption],
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ require (
|
|||||||
github.com/charmbracelet/bubbletea v0.20.0
|
github.com/charmbracelet/bubbletea v0.20.0
|
||||||
github.com/charmbracelet/lipgloss v0.5.0
|
github.com/charmbracelet/lipgloss v0.5.0
|
||||||
github.com/gabriel-vasile/mimetype v1.4.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
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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/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 h1:l5ClIwL3N2LaH1zF3xivb3kP2HW95eyG5xhHE1JdZ9Y=
|
||||||
github.com/u2takey/ffmpeg-go v0.4.1/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc=
|
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 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys=
|
||||||
github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs=
|
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=
|
gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs=
|
||||||
|
|||||||
@@ -79,8 +79,11 @@ func encode(fullFilePath string, fileName string, teaP *tea.Program, cfg ParsedC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ffmpeg.Input(fullFilePath).
|
quotedFilePath := fmt.Sprintf("\"%s\"", fullFilePath)
|
||||||
Output(newFileFullPath, ffmpeg.KwArgs{
|
quotedNewFilePath := fmt.Sprintf("\"%s\"", newFileFullPath)
|
||||||
|
|
||||||
|
err = ffmpeg.Input(quotedFilePath).
|
||||||
|
Output(quotedNewFilePath, ffmpeg.KwArgs{
|
||||||
"c:v": cfg.VideoEncoder,
|
"c:v": cfg.VideoEncoder,
|
||||||
"crf": cfg.CRF,
|
"crf": cfg.CRF,
|
||||||
"preset": cfg.Preset,
|
"preset": cfg.Preset,
|
||||||
|
|||||||
@@ -32,21 +32,39 @@ var (
|
|||||||
FocusedOption = lipgloss.NewStyle().
|
FocusedOption = lipgloss.NewStyle().
|
||||||
Foreground(SecondaryColor)
|
Foreground(SecondaryColor)
|
||||||
|
|
||||||
FocusedButton = lipgloss.NewStyle().
|
FocusedStartButton = lipgloss.NewStyle().
|
||||||
Border(lipgloss.NormalBorder()).
|
Border(lipgloss.NormalBorder()).
|
||||||
BorderForeground(AccentColor).
|
BorderForeground(AccentColor).
|
||||||
Foreground(AccentColor).
|
Foreground(AccentColor).
|
||||||
MarginTop(1).
|
MarginTop(1).
|
||||||
Padding(0, 2).
|
Padding(0, 2).
|
||||||
Align(lipgloss.Center).
|
Align(lipgloss.Center).
|
||||||
Bold(true).
|
Bold(true).
|
||||||
Render("Start Encoding!")
|
Render("Start Encoding!")
|
||||||
BlurredButton = lipgloss.NewStyle().
|
BlurredStartButton = lipgloss.NewStyle().
|
||||||
Border(lipgloss.NormalBorder()).
|
Border(lipgloss.NormalBorder()).
|
||||||
BorderForeground(PrimaryColor).
|
BorderForeground(PrimaryColor).
|
||||||
Foreground(PrimaryColor).
|
Foreground(PrimaryColor).
|
||||||
MarginTop(1).
|
MarginTop(1).
|
||||||
Padding(0, 2).
|
Padding(0, 2).
|
||||||
Align(lipgloss.Center).
|
Align(lipgloss.Center).
|
||||||
Render("Start Encoding!")
|
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")
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user