diff --git a/filter.go b/filter.go index 76c8ef0..0ed0e18 100644 --- a/filter.go +++ b/filter.go @@ -2,8 +2,11 @@ package main import ( "fmt" + "sort" + "strings" "github.com/andreykaipov/goobs/api/requests/filters" + "github.com/aquasecurity/table" ) // FilterCmd provides commands to manage filters in OBS Studio. @@ -28,10 +31,34 @@ func (cmd *FilterListCmd) Run(ctx *context) error { if err != nil { return err } - for _, filter := range filters.Filters { - fmt.Fprintf(ctx.Out, "Name: %s\n Kind: %s\n Enabled: %t\n Settings: %+v\n", - filter.FilterName, filter.FilterKind, filter.FilterEnabled, filter.FilterSettings) + + if len(filters.Filters) == 0 { + fmt.Fprintf(ctx.Out, "No filters found for source %s.\n", cmd.SourceName) + return nil } + + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignCenter, table.AlignLeft) + t.SetHeaders("Filter Name", "Kind", "Enabled", "Settings") + + for _, filter := range filters.Filters { + var lines []string + for k, v := range filter.FilterSettings { + lines = append(lines, fmt.Sprintf("%s %v", k, v)) + } + sort.Slice(lines, func(i, j int) bool { + return strings.ToLower(lines[i]) < strings.ToLower(lines[j]) + }) + + t.AddRow( + filter.FilterName, + snakeCaseToTitleCase(filter.FilterKind), + getEnabledMark(filter.FilterEnabled), + strings.Join(lines, "\n"), + ) + } + t.Render() return nil } diff --git a/go.mod b/go.mod index a2f96b4..7547a9d 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/alecthomas/kong v1.10.0 github.com/alecthomas/mango-kong v0.1.0 github.com/andreykaipov/goobs v1.5.6 + github.com/aquasecurity/table v1.10.0 github.com/titusjaka/kong-dotenv-go v0.1.0 ) @@ -14,9 +15,13 @@ require ( github.com/gorilla/websocket v1.5.3 // indirect github.com/hashicorp/logutils v1.0.0 // indirect github.com/joho/godotenv v1.5.1 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/profile v0.1.1 // indirect github.com/muesli/mango v0.1.1-0.20220205060214-77e2058169ab // indirect github.com/muesli/roff v0.1.0 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect + github.com/rivo/uniseg v0.2.0 // indirect + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect + golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect ) diff --git a/go.sum b/go.sum index 150d5db..fcb34d8 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/andreykaipov/goobs v1.5.6 h1:eIkEqYN99+2VJvmlY/56Ah60nkRKS6efMQvpM3oUgPQ= github.com/andreykaipov/goobs v1.5.6/go.mod h1:iSZP93FJ4d9X/U1x4DD4IyILLtig+vViqZWBGjLywcY= +github.com/aquasecurity/table v1.10.0 h1:gPWV28qp9XSlvXdT3ku8yKQoZE6II0vsmegKpW+dB08= +github.com/aquasecurity/table v1.10.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -20,6 +22,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mmcloughlin/profile v0.1.1 h1:jhDmAqPyebOsVDOCICJoINoLb/AnLBaUw58nFzxWS2w= @@ -32,9 +36,15 @@ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/ github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/titusjaka/kong-dotenv-go v0.1.0 h1:TmUjP/sXoNiKLr6oR7n9xrB5XyXi/Ssuebzfz5nxZj4= github.com/titusjaka/kong-dotenv-go v0.1.0/go.mod h1:pBgLjcu82oqUgb7+bngK9+Ch7jg49E0YADP8Wnj2MXU= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/group.go b/group.go index 3e93ddd..e16d323 100644 --- a/group.go +++ b/group.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/andreykaipov/goobs/api/requests/sceneitems" + "github.com/aquasecurity/table" ) // GroupCmd provides commands to manage groups in OBS Studio. @@ -17,21 +18,36 @@ type GroupCmd struct { // GroupListCmd provides a command to list all groups in a scene. type GroupListCmd struct { - SceneName string `arg:"" help:"Name of the scene to list groups from."` + SceneName string `arg:"" help:"Name of the scene to list groups from." default:""` } // Run executes the command to list all groups in a scene. func (cmd *GroupListCmd) Run(ctx *context) error { + if cmd.SceneName == "" { + currentScene, err := ctx.Client.Scenes.GetCurrentProgramScene() + if err != nil { + return fmt.Errorf("failed to get current program scene: %w", err) + } + cmd.SceneName = currentScene.SceneName + } + resp, err := ctx.Client.SceneItems.GetSceneItemList(sceneitems.NewGetSceneItemListParams(). WithSceneName(cmd.SceneName)) if err != nil { return fmt.Errorf("failed to get scene item list: %w", err) } + + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignCenter, table.AlignLeft, table.AlignCenter) + t.SetHeaders("ID", "Group Name", "Enabled") + for _, item := range resp.SceneItems { if item.IsGroup { - fmt.Fprintf(ctx.Out, "Group ID: %d, Source Name: %s\n", item.SceneItemID, item.SourceName) + t.AddRow(fmt.Sprintf("%d", item.SceneItemID), item.SourceName, getEnabledMark(item.SceneItemEnabled)) } } + t.Render() return nil } diff --git a/hotkey.go b/hotkey.go index d98f540..90bf9b1 100644 --- a/hotkey.go +++ b/hotkey.go @@ -1,10 +1,9 @@ package main import ( - "fmt" - "github.com/andreykaipov/goobs/api/requests/general" "github.com/andreykaipov/goobs/api/typedefs" + "github.com/aquasecurity/table" ) // HotkeyCmd provides commands to manage hotkeys in OBS Studio. @@ -24,9 +23,15 @@ func (cmd *HotkeyListCmd) Run(ctx *context) error { return err } + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignLeft) + t.SetHeaders("Hotkey Name") + for _, hotkey := range resp.Hotkeys { - fmt.Fprintln(ctx.Out, hotkey) + t.AddRow(hotkey) } + t.Render() return nil } diff --git a/input.go b/input.go index cc0301a..f33a38b 100644 --- a/input.go +++ b/input.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/andreykaipov/goobs/api/requests/inputs" + "github.com/aquasecurity/table" ) // InputCmd provides commands to manage inputs in OBS Studio. @@ -28,21 +29,28 @@ func (cmd *InputListCmd) Run(ctx *context) error { if err != nil { return err } + + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignLeft, table.AlignLeft) + t.SetHeaders("Input Name", "Kind") + for _, input := range resp.Inputs { if cmd.Input && strings.Contains(input.InputKind, "input") { - fmt.Fprintln(ctx.Out, "Input:", input.InputName) + t.AddRow(input.InputName, input.InputKind) } if cmd.Output && strings.Contains(input.InputKind, "output") { - fmt.Fprintln(ctx.Out, "Output:", input.InputName) + t.AddRow(input.InputName, input.InputKind) } if cmd.Colour && strings.Contains(input.InputKind, "color") { // nolint - fmt.Fprintln(ctx.Out, "Colour Source:", input.InputName) + t.AddRow(input.InputName, input.InputKind) } if !cmd.Input && !cmd.Output && !cmd.Colour { - fmt.Fprintln(ctx.Out, "Source:", input.InputName) + t.AddRow(input.InputName, input.InputKind) } } + t.Render() return nil } diff --git a/profile.go b/profile.go index 4c35800..7913f0e 100644 --- a/profile.go +++ b/profile.go @@ -5,6 +5,7 @@ import ( "slices" "github.com/andreykaipov/goobs/api/requests/config" + "github.com/aquasecurity/table" ) // ProfileCmd provides commands to manage profiles in OBS Studio. @@ -26,10 +27,20 @@ func (cmd *ProfileListCmd) Run(ctx *context) error { return err } - for _, profile := range profiles.Profiles { - fmt.Fprintln(ctx.Out, profile) - } + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignLeft, table.AlignCenter) + t.SetHeaders("Profile Name", "Current") + for _, profile := range profiles.Profiles { + var enabledMark string + if profile == profiles.CurrentProfileName { + enabledMark = getEnabledMark(true) + } + + t.AddRow(profile, enabledMark) + } + t.Render() return nil } diff --git a/scene.go b/scene.go index a2fbb48..26d11cb 100644 --- a/scene.go +++ b/scene.go @@ -5,6 +5,7 @@ import ( "slices" "github.com/andreykaipov/goobs/api/requests/scenes" + "github.com/aquasecurity/table" ) // SceneCmd provides commands to manage scenes in OBS Studio. @@ -24,10 +25,16 @@ func (cmd *SceneListCmd) Run(ctx *context) error { return err } + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignLeft, table.AlignLeft) + t.SetHeaders("Scene Name", "UUID") + slices.Reverse(scenes.Scenes) for _, scene := range scenes.Scenes { - fmt.Fprintln(ctx.Out, scene.SceneName) + t.AddRow(scene.SceneName, scene.SceneUuid) } + t.Render() return nil } diff --git a/scenecollection.go b/scenecollection.go index 720adfa..e190a92 100644 --- a/scenecollection.go +++ b/scenecollection.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/andreykaipov/goobs/api/requests/config" + "github.com/aquasecurity/table" ) // SceneCollectionCmd provides commands to manage scene collections in OBS Studio. @@ -24,10 +25,15 @@ func (cmd *SceneCollectionListCmd) Run(ctx *context) error { return fmt.Errorf("failed to get scene collection list: %w", err) } - for _, collection := range collections.SceneCollections { - fmt.Fprintln(ctx.Out, collection) - } + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignLeft) + t.SetHeaders("Scene Collection Name") + for _, collection := range collections.SceneCollections { + t.AddRow(collection) + } + t.Render() return nil } diff --git a/sceneitem.go b/sceneitem.go index 0f4979f..0f52e3e 100644 --- a/sceneitem.go +++ b/sceneitem.go @@ -5,6 +5,7 @@ import ( "github.com/andreykaipov/goobs" "github.com/andreykaipov/goobs/api/requests/sceneitems" + "github.com/aquasecurity/table" ) // SceneItemCmd provides commands to manage scene items in OBS Studio. @@ -19,19 +20,39 @@ type SceneItemCmd struct { // SceneItemListCmd provides a command to list all scene items in a scene. type SceneItemListCmd struct { - SceneName string `arg:"" help:"Scene name."` + SceneName string `arg:"" help:"Name of the scene to list items from." default:""` } // Run executes the command to list all scene items in a scene. func (cmd *SceneItemListCmd) Run(ctx *context) error { + if cmd.SceneName == "" { + currentScene, err := ctx.Client.Scenes.GetCurrentProgramScene() + if err != nil { + return fmt.Errorf("failed to get current program scene: %w", err) + } + cmd.SceneName = currentScene.SceneName + } + resp, err := ctx.Client.SceneItems.GetSceneItemList(sceneitems.NewGetSceneItemListParams(). WithSceneName(cmd.SceneName)) if err != nil { return fmt.Errorf("failed to get scene item list: %w", err) } - for _, item := range resp.SceneItems { - fmt.Fprintf(ctx.Out, "Item ID: %d, Source Name: %s\n", item.SceneItemID, item.SourceName) + + if len(resp.SceneItems) == 0 { + fmt.Fprintf(ctx.Out, "No scene items found in scene '%s'.\n", cmd.SceneName) + return nil } + + t := table.New(ctx.Out) + t.SetPadding(3) + t.SetAlignment(table.AlignLeft) + t.SetHeaders("Item Name") + + for _, item := range resp.SceneItems { + t.AddRow(item.SourceName) + } + t.Render() return nil } diff --git a/util.go b/util.go new file mode 100644 index 0000000..11717b7 --- /dev/null +++ b/util.go @@ -0,0 +1,22 @@ +// Package util provides utility functions for the application. + +package main + +import "strings" + +func snakeCaseToTitleCase(snake string) string { + words := strings.Split(snake, "_") + for i, word := range words { + if len(word) > 0 { + words[i] = strings.ToUpper(word[:1]) + word[1:] + } + } + return strings.Join(words, " ") +} + +func getEnabledMark(enabled bool) string { + if enabled { + return "\u2713" // green check mark + } + return "\u274c" // red cross mark +} diff --git a/util_test.go b/util_test.go new file mode 100644 index 0000000..fa4b6f8 --- /dev/null +++ b/util_test.go @@ -0,0 +1,20 @@ +package main + +import "testing" + +func TestSnakeCaseToTitleCase(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"hello_world", "Hello World"}, + {"snake_case_to_title_case", "Snake Case To Title Case"}, + } + + for _, test := range tests { + result := snakeCaseToTitleCase(test.input) + if result != test.expected { + t.Errorf("Expected '%s' but got '%s'", test.expected, result) + } + } +}