mirror of
https://github.com/IllusionMan1212/ffui.git
synced 2026-10-24 18:12:35 +00:00
Compare commits
2
Commits
2951ea391f
...
0fb852e034
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fb852e034
|
||
|
|
36f648fd86
|
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/charmbracelet/bubbles/progress"
|
"github.com/charmbracelet/bubbles/progress"
|
||||||
"github.com/charmbracelet/bubbles/spinner"
|
"github.com/charmbracelet/bubbles/spinner"
|
||||||
|
"github.com/charmbracelet/bubbles/viewport"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
)
|
)
|
||||||
@@ -35,8 +36,10 @@ type Model struct {
|
|||||||
IsDirectory bool
|
IsDirectory bool
|
||||||
Path string
|
Path string
|
||||||
CurrentFileName string
|
CurrentFileName string
|
||||||
|
ViewportFocused bool
|
||||||
FileCount int
|
FileCount int
|
||||||
Files []File
|
Files []File
|
||||||
|
Viewport viewport.Model
|
||||||
Spinner spinner.Model
|
Spinner spinner.Model
|
||||||
SingleFileProgressBar progress.Model
|
SingleFileProgressBar progress.Model
|
||||||
SingleFileProgress float64
|
SingleFileProgress float64
|
||||||
@@ -92,6 +95,7 @@ func initialModel(fileInfo os.FileInfo, absolutePath string) *Model {
|
|||||||
Path: absolutePath,
|
Path: absolutePath,
|
||||||
FileCount: 0,
|
FileCount: 0,
|
||||||
Files: make([]File, 0),
|
Files: make([]File, 0),
|
||||||
|
Viewport: viewport.New(0, 0),
|
||||||
Screen: Cfg,
|
Screen: Cfg,
|
||||||
Spinner: s,
|
Spinner: s,
|
||||||
SingleFileProgressBar: progress.New(progress.WithGradient("#1010ff", "#00ff00")),
|
SingleFileProgressBar: progress.New(progress.WithGradient("#1010ff", "#00ff00")),
|
||||||
@@ -133,6 +137,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.Files = msg.files
|
m.Files = msg.files
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
m.Viewport.Width = msg.Width
|
||||||
|
m.Viewport.Height = msg.Height - lipgloss.Height(FilesScreenViewHeader(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
switch m.Screen {
|
switch m.Screen {
|
||||||
@@ -144,7 +151,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
case "ctrl+c", "esc":
|
case "ctrl+c", "esc":
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
case "enter", " ":
|
case "enter", " ":
|
||||||
if m.FocusIndex == 0 && key == "enter" {
|
if !m.ViewportFocused && key == "enter" {
|
||||||
selectAll := !every(m.Files, func(e File) bool { return e.Selected })
|
selectAll := !every(m.Files, func(e File) bool { return e.Selected })
|
||||||
|
|
||||||
if m.ChoiceIndex == 0 {
|
if m.ChoiceIndex == 0 {
|
||||||
@@ -161,20 +168,30 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
|
|
||||||
return m, tea.Batch(m.Spinner.Tick, encodeVideo)
|
return m, tea.Batch(m.Spinner.Tick, encodeVideo)
|
||||||
}
|
}
|
||||||
} else if m.FocusIndex != 0 {
|
} else if m.ViewportFocused {
|
||||||
m.Files[m.FocusIndex-1].Selected = !m.Files[m.FocusIndex-1].Selected
|
m.Files[m.FocusIndex].Selected = !m.Files[m.FocusIndex].Selected
|
||||||
|
|
||||||
// Reset the choice to "Select All" button when no files are selected
|
// Reset the choice to "Select All" button when no files are selected
|
||||||
if !anyOf(m.Files, func(f File) bool { return f.Selected }) {
|
if !anyOf(m.Files, func(f File) bool { return f.Selected }) {
|
||||||
m.ChoiceIndex = 0
|
m.ChoiceIndex = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.SetViewportContent()
|
||||||
case "g":
|
case "g":
|
||||||
m.FocusIndex = 0
|
if m.ViewportFocused {
|
||||||
|
m.FocusIndex = 0
|
||||||
|
m.SetViewportContent()
|
||||||
|
m.Viewport.GotoTop()
|
||||||
|
}
|
||||||
case "G":
|
case "G":
|
||||||
m.FocusIndex = len(m.Files)
|
if m.ViewportFocused {
|
||||||
|
m.FocusIndex = len(m.Files) - 1
|
||||||
|
m.SetViewportContent()
|
||||||
|
m.Viewport.GotoBottom()
|
||||||
|
}
|
||||||
case "left", "right", "h", "l":
|
case "left", "right", "h", "l":
|
||||||
if m.FocusIndex == 0 && anyOf(m.Files, func(f File) bool { return f.Selected }) {
|
if !m.ViewportFocused && anyOf(m.Files, func(f File) bool { return f.Selected }) {
|
||||||
if key == "right" || key == "l" {
|
if key == "right" || key == "l" {
|
||||||
m.ChoiceIndex++
|
m.ChoiceIndex++
|
||||||
} else {
|
} else {
|
||||||
@@ -187,17 +204,34 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.ChoiceIndex = 1
|
m.ChoiceIndex = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "tab", "shift+tab", "up", "down", "j", "k":
|
case "tab", "shift+tab":
|
||||||
if key == "up" || key == "shift+tab" || key == "k" {
|
m.ViewportFocused = !m.ViewportFocused
|
||||||
m.FocusIndex--
|
m.SetViewportContent()
|
||||||
} else {
|
case "up", "down", "j", "k":
|
||||||
m.FocusIndex++
|
if m.ViewportFocused {
|
||||||
}
|
if key == "up" || key == "k" {
|
||||||
|
m.FocusIndex--
|
||||||
|
} else {
|
||||||
|
m.FocusIndex++
|
||||||
|
}
|
||||||
|
|
||||||
if m.FocusIndex >= len(m.Files)+1 {
|
if (key == "down" || key == "j") && m.FocusIndex >= m.Viewport.Height+m.Viewport.YOffset {
|
||||||
m.FocusIndex = 0
|
m.Viewport.LineDown(1)
|
||||||
} else if m.FocusIndex < 0 {
|
}
|
||||||
m.FocusIndex = len(m.Files)
|
|
||||||
|
if (key == "up" || key == "k") && m.FocusIndex < m.Viewport.YOffset {
|
||||||
|
m.Viewport.LineUp(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.FocusIndex >= len(m.Files) {
|
||||||
|
m.Viewport.GotoTop()
|
||||||
|
m.FocusIndex = 0
|
||||||
|
} else if m.FocusIndex < 0 {
|
||||||
|
m.Viewport.GotoBottom()
|
||||||
|
m.FocusIndex = len(m.Files) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SetViewportContent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,6 +313,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.Screen = Files
|
m.Screen = Files
|
||||||
m.FocusIndex = 0
|
m.FocusIndex = 0
|
||||||
m.ChoiceIndex = 0
|
m.ChoiceIndex = 0
|
||||||
|
|
||||||
|
m.SetViewportContent()
|
||||||
} else {
|
} else {
|
||||||
m.Screen = Main
|
m.Screen = Main
|
||||||
|
|
||||||
@@ -354,6 +390,26 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) SetViewportContent() {
|
||||||
|
var files string
|
||||||
|
|
||||||
|
for i, file := range m.Files {
|
||||||
|
selection := " "
|
||||||
|
if file.Selected {
|
||||||
|
selection = "x"
|
||||||
|
}
|
||||||
|
if m.ViewportFocused && m.FocusIndex == i {
|
||||||
|
files += fmt.Sprintf(FocusedConfig.UnsetMarginTop().Render("[%s] %s"), selection, filepath.Base(file.Path))
|
||||||
|
} else {
|
||||||
|
files += fmt.Sprintf(BlurredConfig.UnsetMarginTop().Render("[%s] %s"), selection, filepath.Base(file.Path))
|
||||||
|
}
|
||||||
|
|
||||||
|
files += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Viewport.SetContent(files)
|
||||||
|
}
|
||||||
|
|
||||||
func parseConfig(cfg []Config) ParsedConfig {
|
func parseConfig(cfg []Config) ParsedConfig {
|
||||||
vEncoder := find(cfg, "Video Encoder")
|
vEncoder := find(cfg, "Video Encoder")
|
||||||
aEncoder := find(cfg, "Audio Encoder")
|
aEncoder := find(cfg, "Audio Encoder")
|
||||||
@@ -370,9 +426,8 @@ func parseConfig(cfg []Config) ParsedConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilesScreenView(m Model) string {
|
func FilesScreenViewHeader(m Model) string {
|
||||||
view := lipgloss.NewStyle().Margin(1, 0).Render("Select the files you wish to encode.")
|
view := lipgloss.NewStyle().Margin(1, 0).Render("Select the files you wish to encode.")
|
||||||
var files string
|
|
||||||
var buttons string
|
var buttons string
|
||||||
var selectAllBtnText string
|
var selectAllBtnText string
|
||||||
|
|
||||||
@@ -393,7 +448,7 @@ func FilesScreenView(m Model) string {
|
|||||||
focusedStartButton = DisabledStartButton
|
focusedStartButton = DisabledStartButton
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.FocusIndex == 0 {
|
if !m.ViewportFocused {
|
||||||
if m.ChoiceIndex == 0 {
|
if m.ChoiceIndex == 0 {
|
||||||
buttons = lipgloss.JoinHorizontal(0, FocusedSelectAllButton.Render(selectAllBtnText), blurredStartButton)
|
buttons = lipgloss.JoinHorizontal(0, FocusedSelectAllButton.Render(selectAllBtnText), blurredStartButton)
|
||||||
} else {
|
} else {
|
||||||
@@ -403,24 +458,17 @@ func FilesScreenView(m Model) string {
|
|||||||
buttons = lipgloss.JoinHorizontal(0, BlurredSelectAllButton.Render(selectAllBtnText), blurredStartButton)
|
buttons = lipgloss.JoinHorizontal(0, BlurredSelectAllButton.Render(selectAllBtnText), blurredStartButton)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: files should be a scrollable grid
|
view += buttons
|
||||||
for i, file := range m.Files {
|
|
||||||
selection := " "
|
|
||||||
if file.Selected {
|
|
||||||
selection = "x"
|
|
||||||
}
|
|
||||||
if m.FocusIndex == i+1 {
|
|
||||||
files += fmt.Sprintf(FocusedConfig.Render("[%s] %s"), selection, filepath.Base(file.Path))
|
|
||||||
} else {
|
|
||||||
files += fmt.Sprintf(BlurredConfig.Render("[%s] %s"), selection, filepath.Base(file.Path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
view += lipgloss.JoinVertical(0, buttons, files)
|
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FilesScreenView(m Model) string {
|
||||||
|
// TODO: files should be a grid (?)
|
||||||
|
|
||||||
|
return lipgloss.JoinVertical(0, FilesScreenViewHeader(m), m.Viewport.View())
|
||||||
|
}
|
||||||
|
|
||||||
func CfgScreenView(m Model) string {
|
func CfgScreenView(m Model) string {
|
||||||
view := ""
|
view := ""
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ func TempSock(totalDuration float64, teaP *tea.Program) string {
|
|||||||
go func() {
|
go func() {
|
||||||
re := regexp.MustCompile(`out_time_ms=(\d+)`)
|
re := regexp.MustCompile(`out_time_ms=(\d+)`)
|
||||||
speedRe := regexp.MustCompile(`speed=(\d+\.\d+x)`)
|
speedRe := regexp.MustCompile(`speed=(\d+\.\d+x)`)
|
||||||
|
totalSizeRe := regexp.MustCompile(`total_size=(\d+)`)
|
||||||
|
|
||||||
fd, err := l.Accept()
|
fd, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -65,6 +66,12 @@ func TempSock(totalDuration float64, teaP *tea.Program) string {
|
|||||||
cp = fmt.Sprintf("%.2f", float64(c)/totalDuration/1000000)
|
cp = fmt.Sprintf("%.2f", float64(c)/totalDuration/1000000)
|
||||||
}
|
}
|
||||||
if strings.Contains(data, "progress=end") {
|
if strings.Contains(data, "progress=end") {
|
||||||
|
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
|
||||||
|
}
|
||||||
cp = "done"
|
cp = "done"
|
||||||
teaP.Send(finishedEncodingVideo{})
|
teaP.Send(finishedEncodingVideo{})
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user