feat: initial work on configs

This commit is contained in:
2022-10-07 06:07:44 +03:00
parent 11e5d22c9f
commit b872f5f874
3 changed files with 274 additions and 100 deletions
+126 -62
View File
@@ -2,21 +2,40 @@ package main
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"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"
"github.com/gabriel-vasile/mimetype"
) )
type Screen int
type FfuiBool int
var checkmark = lipgloss.NewStyle().Foreground(lipgloss.Color("#22FF33")).Render("✔") 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 { type model struct {
IsDirectory bool IsDirectory bool
Path string Path string
@@ -30,6 +49,9 @@ type model struct {
TotalProgress float64 TotalProgress float64
Program *tea.Program Program *tea.Program
Quitting bool Quitting bool
Screen Screen
Config map[int]Config
FocusIndex int
} }
func initialModel(fileInfo os.FileInfo, absolutePath string) *model { 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.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) 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{ return &model{
IsDirectory: fileInfo.IsDir(), IsDirectory: fileInfo.IsDir(),
CurrentFileName: fileInfo.Name(), CurrentFileName: fileInfo.Name(),
@@ -49,11 +77,12 @@ func initialModel(fileInfo os.FileInfo, absolutePath string) *model {
TotalProgressBar: progress.New(progress.WithDefaultGradient()), TotalProgressBar: progress.New(progress.WithDefaultGradient()),
TotalProgress: 0.0, TotalProgress: 0.0,
Quitting: false, Quitting: false,
Config: cfg,
} }
} }
func (m model) Init() tea.Cmd { 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) { func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
@@ -65,6 +94,55 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case "ctrl+c": case "ctrl+c":
return m, tea.Quit return m, tea.Quit
} }
}
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: case initUi:
m.FileCount = msg.fileCount m.FileCount = msg.fileCount
m.Files = msg.files m.Files = msg.files
@@ -109,15 +187,42 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
} }
m.Spinner, cmd = m.Spinner.Update(msg) m.Spinner, cmd = m.Spinner.Update(msg)
}
return m, cmd return m, cmd
} }
func (m model) View() string { func CfgScreenView(m model) string {
if m.Quitting { view := ""
return fmt.Sprintf("%s %d/%d files encoded\n", checkmark, m.FileCount-len(m.Files), m.FileCount)
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 := "" progress := ""
if m.IsDirectory { if m.IsDirectory {
@@ -140,58 +245,17 @@ func (m model) View() string {
return view return view
} }
type initUi struct { func (m model) View() string {
fileCount int if m.Quitting {
files []string return fmt.Sprintf("%s %d/%d files encoded\n", checkmark, m.FileCount-len(m.Files), m.FileCount)
} }
func (m *model) statFile() tea.Msg { switch m.Screen {
if m.IsDirectory { case Cfg:
entries, _ := os.ReadDir(m.Path) return CfgScreenView(m)
case Main:
sort.Sort(DirEntrySlice(entries)) return MainScreenView(m)
}
for _, entry := range entries {
fullFilePath := filepath.Join(m.Path, entry.Name()) return "Error: Invalid Screen"
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{}
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{}
} }
+76
View File
@@ -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{}
}
+34
View File
@@ -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)
)