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