From b872f5f8745d38991da1e2b250b33db1bf6a7b0a Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Fri, 7 Oct 2022 06:07:44 +0300 Subject: [PATCH] feat: initial work on configs --- app.go | 264 +++++++++++++++++++++++++++++++++--------------------- cmds.go | 76 ++++++++++++++++ styles.go | 34 +++++++ 3 files changed, 274 insertions(+), 100 deletions(-) create mode 100644 cmds.go create mode 100644 styles.go diff --git a/app.go b/app.go index 5d12ad3..6a74500 100644 --- a/app.go +++ b/app.go @@ -2,21 +2,40 @@ package main import ( "fmt" - "log" "os" "path/filepath" - "sort" - "strings" "github.com/charmbracelet/bubbles/progress" "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" - "github.com/gabriel-vasile/mimetype" ) +type Screen int +type FfuiBool int + var checkmark = lipgloss.NewStyle().Foreground(lipgloss.Color("#22FF33")).Render("✔") +var Configs = []Config{ + {Name: "Delete unencoded video(s)?", Opts: []string{"No", "Yes"}}, +} + +const ( + Cfg Screen = iota + Main +) + +const ( + False FfuiBool = iota + True +) + +type Config struct { + Name string + Opts []string + FocusedOption int +} + type model struct { IsDirectory bool Path string @@ -30,6 +49,9 @@ type model struct { TotalProgress float64 Program *tea.Program Quitting bool + Screen Screen + Config map[int]Config + FocusIndex int } func initialModel(fileInfo os.FileInfo, absolutePath string) *model { @@ -37,6 +59,12 @@ func initialModel(fileInfo os.FileInfo, absolutePath string) *model { s.Spinner = spinner.Dot s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) + cfg := make(map[int]Config) + + for i := 0; i < len(Configs); i++ { + cfg[i] = Configs[i] + } + return &model{ IsDirectory: fileInfo.IsDir(), CurrentFileName: fileInfo.Name(), @@ -49,11 +77,12 @@ func initialModel(fileInfo os.FileInfo, absolutePath string) *model { TotalProgressBar: progress.New(progress.WithDefaultGradient()), TotalProgress: 0.0, Quitting: false, + Config: cfg, } } func (m model) Init() tea.Cmd { - return tea.Batch(m.Spinner.Tick, m.statFile) + return tea.Batch(m.Spinner.Tick, initCfg) } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { @@ -65,59 +94,135 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "ctrl+c": return m, tea.Quit } - case initUi: - m.FileCount = msg.fileCount - m.Files = msg.files - - return m, encodeVideo - case encodeVideoMsg: - m.CurrentFileName = filepath.Base(m.Files[len(m.Files)-1]) - - go func() { - fullFilePath := m.Files[len(m.Files)-1] - encode(fullFilePath, filepath.Base(fullFilePath), m.Program) - }() - - return m, nil - case finishedEncodingVideo: - m.Files = m.Files[:len(m.Files)-1] - - if len(m.Files) == 0 { - return m, m.gracefullyQuit - } - - return m, encodeVideo - case updateProgress: - m.SingleFileProgress = msg.progress - m.TotalProgress = (1.0/float64(m.FileCount))*msg.progress + (1.0 / float64(m.FileCount) * float64(m.FileCount-len(m.Files))) - - singleProgressCmd := m.SingleFileProgressBar.SetPercent(msg.progress) - totalProgressCmd := m.TotalProgressBar.SetPercent(m.TotalProgress) - - return m, tea.Batch(singleProgressCmd, totalProgressCmd) - case quitMsg: - m.Quitting = true - return m, tea.Quit - case progress.FrameMsg: - singleProgressModel, singleProgressCmd := m.SingleFileProgressBar.Update(msg) - m.SingleFileProgressBar = singleProgressModel.(progress.Model) - - totalProgressModel, totalProgressCmd := m.TotalProgressBar.Update(msg) - m.TotalProgressBar = totalProgressModel.(progress.Model) - - return m, tea.Batch(singleProgressCmd, totalProgressCmd) } - m.Spinner, cmd = m.Spinner.Update(msg) + switch m.Screen { + case Cfg: + switch msg := msg.(type) { + case tea.KeyMsg: + key := msg.String() + switch key { + case "enter", " ": + // switch to main screen if we're focused on the start button + if m.FocusIndex == len(Configs) { + m.Screen = Main + return m, tea.Batch(m.Spinner.Tick, m.statFile) + } + case "tab", "shift+tab", "up", "down": + if key == "up" || key == "shift+tab" { + m.FocusIndex-- + } else { + m.FocusIndex++ + } + + // TODO: make this len(Configs) + 1 after start button is added and change the else statement accordingly + if m.FocusIndex >= len(Configs) { + m.FocusIndex = 0 + } else if m.FocusIndex < 0 { + m.FocusIndex = len(Configs) - 1 + } + + // TODO: move to start button + case "left", "right": + if cfg, ok := m.Config[m.FocusIndex]; ok { + if key == "right" { + cfg.FocusedOption++ + } else { + cfg.FocusedOption-- + } + + if cfg.FocusedOption >= len(cfg.Opts) { + cfg.FocusedOption = 0 + } else if cfg.FocusedOption < 0 { + cfg.FocusedOption = len(cfg.Opts) - 1 + } + + m.Config[m.FocusIndex] = cfg + } + } + } + case Main: + switch msg := msg.(type) { + case initUi: + m.FileCount = msg.fileCount + m.Files = msg.files + + return m, encodeVideo + case encodeVideoMsg: + m.CurrentFileName = filepath.Base(m.Files[len(m.Files)-1]) + + go func() { + fullFilePath := m.Files[len(m.Files)-1] + encode(fullFilePath, filepath.Base(fullFilePath), m.Program) + }() + + return m, nil + case finishedEncodingVideo: + m.Files = m.Files[:len(m.Files)-1] + + if len(m.Files) == 0 { + return m, m.gracefullyQuit + } + + return m, encodeVideo + case updateProgress: + m.SingleFileProgress = msg.progress + m.TotalProgress = (1.0/float64(m.FileCount))*msg.progress + (1.0 / float64(m.FileCount) * float64(m.FileCount-len(m.Files))) + + singleProgressCmd := m.SingleFileProgressBar.SetPercent(msg.progress) + totalProgressCmd := m.TotalProgressBar.SetPercent(m.TotalProgress) + + return m, tea.Batch(singleProgressCmd, totalProgressCmd) + case quitMsg: + m.Quitting = true + return m, tea.Quit + case progress.FrameMsg: + singleProgressModel, singleProgressCmd := m.SingleFileProgressBar.Update(msg) + m.SingleFileProgressBar = singleProgressModel.(progress.Model) + + totalProgressModel, totalProgressCmd := m.TotalProgressBar.Update(msg) + m.TotalProgressBar = totalProgressModel.(progress.Model) + + return m, tea.Batch(singleProgressCmd, totalProgressCmd) + } + + m.Spinner, cmd = m.Spinner.Update(msg) + } return m, cmd } -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) +func CfgScreenView(m model) string { + view := "" + + for i, cfg := range Configs { + opts := "" + + for j, opt := range cfg.Opts { + if m.Config[i].FocusedOption == j { + opts += FocusedOption.Render(opt) + } else { + opts += BlurredOption.Render(opt) + } + + if j != len(cfg.Opts)-1 { + opts += ", " + } + } + + if i == m.FocusIndex { + view += fmt.Sprintf("%s { %s }\n", FocusedConfig.Render(cfg.Name), opts) + } else { + view += fmt.Sprintf("%s { %s }\n", BlurredConfig.Render(cfg.Name), opts) + } } + // TODO: add start button + + return view +} + +func MainScreenView(m model) string { progress := "" if m.IsDirectory { @@ -140,58 +245,17 @@ func (m model) View() string { return view } -type initUi struct { - fileCount int - files []string -} - -func (m *model) statFile() tea.Msg { - if m.IsDirectory { - entries, _ := os.ReadDir(m.Path) - - sort.Sort(DirEntrySlice(entries)) - - for _, entry := range entries { - fullFilePath := filepath.Join(m.Path, entry.Name()) - - if entry.IsDir() { - continue - } - - mType, err := mimetype.DetectFile(fullFilePath) - if err != nil { - log.Fatal(err) - } - - if !strings.HasPrefix(mType.String(), "video/") { - continue - } - - m.FileCount++ - m.Files = append(m.Files, fullFilePath) - } - } else { - m.FileCount = 1 - m.Files = append(m.Files, m.Path) +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) } - return initUi{ - fileCount: m.FileCount, - files: m.Files, + switch m.Screen { + case Cfg: + return CfgScreenView(m) + case Main: + return MainScreenView(m) } -} -type encodeVideoMsg struct{} -type finishedEncodingVideo struct{} -type updateProgress struct { - progress float64 -} -type quitMsg struct{} - -func encodeVideo() tea.Msg { - return encodeVideoMsg{} -} - -func (m model) gracefullyQuit() tea.Msg { - return quitMsg{} + return "Error: Invalid Screen" } diff --git a/cmds.go b/cmds.go new file mode 100644 index 0000000..e64fd10 --- /dev/null +++ b/cmds.go @@ -0,0 +1,76 @@ +package main + +import ( + "log" + "os" + "path/filepath" + "sort" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/gabriel-vasile/mimetype" +) + +type finishedEncodingVideo struct{} +type updateProgress struct { + progress float64 +} + +type initUi struct { + fileCount int + files []string +} + +func (m *model) statFile() tea.Msg { + if m.IsDirectory { + entries, _ := os.ReadDir(m.Path) + + sort.Sort(DirEntrySlice(entries)) + + for _, entry := range entries { + fullFilePath := filepath.Join(m.Path, entry.Name()) + + if entry.IsDir() { + continue + } + + mType, err := mimetype.DetectFile(fullFilePath) + if err != nil { + log.Fatal(err) + } + + if !strings.HasPrefix(mType.String(), "video/") { + continue + } + + m.FileCount++ + m.Files = append(m.Files, fullFilePath) + } + } else { + m.FileCount = 1 + m.Files = append(m.Files, m.Path) + } + + return initUi{ + fileCount: m.FileCount, + files: m.Files, + } +} + +type encodeVideoMsg struct{} + +func encodeVideo() tea.Msg { + return encodeVideoMsg{} +} + +type quitMsg struct{} + +func (m model) gracefullyQuit() tea.Msg { + return quitMsg{} +} + +type initCfgMsg struct{} + +func initCfg() tea.Msg { + return initCfgMsg{} +} diff --git a/styles.go b/styles.go new file mode 100644 index 0000000..f2472ed --- /dev/null +++ b/styles.go @@ -0,0 +1,34 @@ +package main + +import "github.com/charmbracelet/lipgloss" + +var ( + PrimaryColor = lipgloss.AdaptiveColor{ + Dark: "#EEE", + Light: "#151515", + } + + SecondaryColor = lipgloss.AdaptiveColor{ + Dark: "#CE53BC", + Light: "#CE53BC", + } + + AccentColor = lipgloss.AdaptiveColor{ + Dark: "#1C6DD0", + Light: "#1C6DD0", + } + + BlurredConfig = lipgloss.NewStyle(). + Foreground(PrimaryColor). + MarginTop(1) + + FocusedConfig = lipgloss.NewStyle(). + Foreground(AccentColor). + MarginTop(1) + + BlurredOption = lipgloss.NewStyle(). + Foreground(PrimaryColor) + + FocusedOption = lipgloss.NewStyle(). + Foreground(SecondaryColor) +)