initial
commit
691f2dcbbb
@ -0,0 +1,95 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "toggle source",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"remotePath": "",
|
||||
"port": 38697,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${workspaceFolder}",
|
||||
"env": {
|
||||
},
|
||||
"args": ["-srn", "BRB", "source", "-t"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"buildFlags":""
|
||||
},
|
||||
{
|
||||
"name": "Debug Program",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"remotePath": "",
|
||||
"port": 38697,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${workspaceFolder}",
|
||||
"env": {
|
||||
},
|
||||
"args": ["-h"],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"buildFlags":""
|
||||
},
|
||||
{
|
||||
"name": "Debug current package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"remotePath": "",
|
||||
"port": 38697,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${fileDirname}",
|
||||
"env": {
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"buildFlags":""
|
||||
},
|
||||
{
|
||||
"name": "Launch test function",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "test",
|
||||
"program": "${workspaceFolder}",
|
||||
"args": [
|
||||
"-test.run",
|
||||
"MyTestFunction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Attach main",
|
||||
"type": "go",
|
||||
"request": "attach",
|
||||
"mode": "debug",
|
||||
"remotePath": "",
|
||||
"port": 38697,
|
||||
"host": "127.0.0.1",
|
||||
"program": "${workspaceFolder}/main.go",
|
||||
"env": {
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"processId":"",
|
||||
"envFile": "${workspaceFolder}/.env",
|
||||
"buildFlags":""
|
||||
},
|
||||
{
|
||||
"name": "Attach to Process",
|
||||
"type": "go",
|
||||
"request": "attach",
|
||||
"mode": "local",
|
||||
"processId": 0
|
||||
},
|
||||
{
|
||||
"name": "Launch file",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "debug",
|
||||
"program": "${file}"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
module blob42/remove-users
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1
|
||||
github.com/andreykaipov/goobs v0.12.0
|
||||
github.com/kr/pretty v0.3.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/logutils v1.0.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
)
|
@ -0,0 +1,24 @@
|
||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/andreykaipov/goobs v0.12.0 h1:EsV3QzqWO+FsrLFbzzKsZuHsdFAGp7F0nG9OiDWdaVs=
|
||||
github.com/andreykaipov/goobs v0.12.0/go.mod h1:EqG73Uu/4npyhXIWWszgRelNkEeIz+d0slUT6NKWYs4=
|
||||
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
@ -0,0 +1,353 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/andreykaipov/goobs"
|
||||
"github.com/andreykaipov/goobs/api/requests/sceneitems"
|
||||
"github.com/andreykaipov/goobs/api/typedefs"
|
||||
|
||||
// "github.com/andreykaipov/goobs/api/requests/scenes"
|
||||
|
||||
// toml library
|
||||
"github.com/BurntSushi/toml"
|
||||
pretty "github.com/kr/pretty"
|
||||
)
|
||||
|
||||
var (
|
||||
Client *goobs.Client // OBS websocket client
|
||||
Commands = []string{"source", "scene", "list-scenes", "list-sources"}
|
||||
)
|
||||
|
||||
const (
|
||||
Help = `Usage: obs [options] [command]
|
||||
|
||||
COMMANDS:
|
||||
source
|
||||
scene
|
||||
list-scenes
|
||||
list-sources
|
||||
|
||||
FLAGS:`
|
||||
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Host string `toml:"obs_host"`
|
||||
Pass string `toml:"obs_pass"`
|
||||
}
|
||||
|
||||
type Source struct {
|
||||
enabled bool
|
||||
SceneItem *typedefs.SceneItem
|
||||
Scene
|
||||
}
|
||||
|
||||
type Scene struct {
|
||||
id int
|
||||
name string
|
||||
}
|
||||
|
||||
type CliFlags struct {
|
||||
show *bool
|
||||
hide *bool
|
||||
toggle *bool
|
||||
sourceName *string
|
||||
sceneName *string
|
||||
}
|
||||
|
||||
// Return true if elm in list
|
||||
func InArray[T comparable](list []T, elm T) bool {
|
||||
for _, v := range list {
|
||||
if elm == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Source) sync() error {
|
||||
resp, err := Client.SceneItems.SetSceneItemEnabled(&sceneitems.SetSceneItemEnabledParams{
|
||||
SceneItemEnabled: &s.enabled,
|
||||
SceneItemId: float64(s.SceneItem.SceneItemID),
|
||||
SceneName: s.Scene.name,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pretty.Println(resp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Source) Show() {
|
||||
s.enabled = true
|
||||
err := s.sync()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) Hide() {
|
||||
s.enabled = false
|
||||
err := s.sync()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Source) Toggle() {
|
||||
s.enabled = !s.SceneItem.SceneItemEnabled
|
||||
err := s.sync()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getSourceByName(sources []*Source, name string) *Source {
|
||||
for _, v := range sources {
|
||||
if v.SceneItem.SourceName == name {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// let the user choose a source from active scene
|
||||
func selectSource(scene Scene) *Source {
|
||||
sources := getSources(scene)
|
||||
for i, v := range sources {
|
||||
// Print index, name, type left aligned
|
||||
fmt.Printf("%2d %-20s %-20s\n", i, v.SceneItem.SourceName, v.SceneItem.SourceType)
|
||||
}
|
||||
fmt.Print("Select a source by number> ")
|
||||
var input int
|
||||
fmt.Scanln(&input)
|
||||
|
||||
return sources[input]
|
||||
}
|
||||
|
||||
// return all scenes
|
||||
func getScenes() []Scene {
|
||||
var scenes []Scene
|
||||
if Client == nil {
|
||||
log.Fatal("Client is nil")
|
||||
}
|
||||
resp, err := Client.Scenes.GetSceneList()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, v := range resp.Scenes {
|
||||
scenes = append(scenes, Scene{id: v.SceneIndex, name: v.SceneName})
|
||||
}
|
||||
|
||||
return scenes
|
||||
}
|
||||
|
||||
// returns all sources in a scene
|
||||
func getSources(scene Scene) []*Source {
|
||||
var sources []*Source
|
||||
resp, err := Client.SceneItems.GetSceneItemList(&sceneitems.GetSceneItemListParams{
|
||||
SceneName: scene.name,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, v := range resp.SceneItems {
|
||||
sources = append(sources, &Source{SceneItem: v, Scene: scene})
|
||||
}
|
||||
|
||||
return sources
|
||||
}
|
||||
|
||||
func getCurrentScene() Scene {
|
||||
if Client == nil {
|
||||
log.Fatal("Client is nil")
|
||||
}
|
||||
resp, err := Client.Scenes.GetCurrentProgramScene()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return Scene{name: resp.CurrentProgramSceneName}
|
||||
}
|
||||
|
||||
// Display list of scenes and let the user select a scene by number
|
||||
// Returns a pointer to the selected scene
|
||||
func promptScene() Scene {
|
||||
var scene Scene
|
||||
|
||||
resp, err := Client.Scenes.GetSceneList()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Print list of scenes
|
||||
for i, v := range resp.Scenes {
|
||||
fmt.Printf("%2d %s\n", i, v.SceneName)
|
||||
}
|
||||
|
||||
// Ask user to select a scene
|
||||
fmt.Print("Select a scene by number> ")
|
||||
var input int
|
||||
fmt.Scanln(&input)
|
||||
|
||||
scene.id = resp.Scenes[input].SceneIndex
|
||||
scene.name = resp.Scenes[input].SceneName
|
||||
|
||||
return scene
|
||||
}
|
||||
|
||||
func handleSourceCommand(source *Source, flags CliFlags) {
|
||||
if *flags.show {
|
||||
source.Show()
|
||||
} else if *flags.hide {
|
||||
source.Hide()
|
||||
} else if *flags.toggle {
|
||||
source.Toggle()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
|
||||
// load host and pass from config
|
||||
config := loadConfig()
|
||||
Client, err = goobs.New(config.Host, goobs.WithPassword(config.Pass))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer Client.Disconnect()
|
||||
|
||||
version, _ := Client.General.GetVersion()
|
||||
fmt.Printf("OBS Studio version: %s\n", version.ObsVersion)
|
||||
fmt.Printf("Websocket server version: %s\n", version.ObsWebSocketVersion)
|
||||
|
||||
// Handle CLI
|
||||
// Commands:
|
||||
// - source
|
||||
// - scene
|
||||
// - list-scenes
|
||||
// - list-sources
|
||||
|
||||
// Flags:
|
||||
// -s = show
|
||||
// -d = hide
|
||||
// -t = toggle
|
||||
// -scn = scene name
|
||||
// -srn = source name
|
||||
|
||||
// Examples:
|
||||
// obsctl source -s will show the selected source
|
||||
|
||||
// Parse CLI flags
|
||||
|
||||
help := flag.Bool("h", false, "Show help")
|
||||
flags := CliFlags{
|
||||
show: flag.Bool("s", false, "Show source"),
|
||||
hide: flag.Bool("d", false, "Hide source"),
|
||||
toggle: flag.Bool("t", false, "Toggle source"),
|
||||
sourceName: flag.String("srn", "", "Source name"),
|
||||
sceneName: flag.String("scn", "", "Source name"),
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
if *flags.show && *flags.hide {
|
||||
log.Fatal("Cannot show and hide at the same time")
|
||||
}
|
||||
|
||||
if *help {
|
||||
fmt.Println(Help)
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
// parse commands using os.Args
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal("No command given")
|
||||
}
|
||||
|
||||
// find command in args
|
||||
var cmd string
|
||||
for _, v := range os.Args {
|
||||
// test if v contains a command
|
||||
if InArray(Commands, v) {
|
||||
cmd = v
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch cmd {
|
||||
case "source":
|
||||
// assume current active scene
|
||||
var source *Source
|
||||
if *flags.sourceName == "" {
|
||||
source = selectSource(getCurrentScene())
|
||||
} else {
|
||||
fmt.Println("handling source", *flags.sourceName)
|
||||
source = getSourceByName(getSources(getCurrentScene()), *flags.sourceName)
|
||||
}
|
||||
handleSourceCommand(source, flags)
|
||||
case "scene":
|
||||
// prompt user to select a scene
|
||||
scene := promptScene()
|
||||
source := selectSource(scene)
|
||||
handleSourceCommand(source, flags)
|
||||
|
||||
case "list-scenes":
|
||||
scenes := getScenes()
|
||||
for _, v := range scenes {
|
||||
fmt.Printf("%s\n", v.name)
|
||||
}
|
||||
|
||||
case "list-sources":
|
||||
// If no scene selected assume current scene
|
||||
if flags.sceneName == nil || *flags.sceneName == "" {
|
||||
sources := getSources(getCurrentScene())
|
||||
for _, v := range sources {
|
||||
fmt.Printf("%s\n", v.SceneItem.SourceName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// select a scene
|
||||
// selected := promptScene(client)
|
||||
// fmt.Println(selected)
|
||||
|
||||
// select source from currently active scene
|
||||
// scene := getCurrentScene()
|
||||
// source := selectSource(scene)
|
||||
// source.Hide()
|
||||
// pretty.Println(source)
|
||||
|
||||
// Get current scene
|
||||
// scene := getCurrentScene(client)
|
||||
// fmt.Println(scene)
|
||||
|
||||
// resp, _ := client.Scenes.GetSceneList()
|
||||
// for _, v := range resp.Scenes {
|
||||
// fmt.Printf("%2d %s\n", v.SceneIndex, v.SceneName)
|
||||
// }
|
||||
//
|
||||
// newScene := &scenes.SetCurrentProgramSceneParams{
|
||||
// SceneName: "Coding",
|
||||
// }
|
||||
// _, err = client.Scenes.SetCurrentProgramScene(newScene)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
}
|
||||
|
||||
func loadConfig() Config {
|
||||
var config Config
|
||||
if _, err := toml.DecodeFile("config.toml", &config); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
Loading…
Reference in New Issue