You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
7.0 KiB
Go
354 lines
7.0 KiB
Go
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
|
|
}
|
|
|