master
blob42 1 year ago
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…
Cancel
Save