diff --git a/cmd/streampanel/flag.go b/cmd/streampanel/flag.go index b6be3a3..b8aae17 100644 --- a/cmd/streampanel/flag.go +++ b/cmd/streampanel/flag.go @@ -42,6 +42,9 @@ type Flags struct { Subprocess string `yaml:"Subprocess,omitempty"` SplitProcess bool `yaml:"SplitProcess,omitempty"` LockTimeout time.Duration `yaml:"LockTimeout,omitempty"` + + OAuthListenPortTwitch uint16 `yaml:"OAuthListenPortTwitch,omitempty"` + OAuthListenPortYouTube uint16 `yaml:"OAuthListenPortYouTube,omitempty"` } var platformGetFlagsFuncs []func(*Flags) @@ -75,6 +78,9 @@ func parseFlags() Flags { subprocess := pflag.String("subprocess", "", "[internal use flag] run a specific sub-process (format: processName:addressToConnect)") splitProcess := pflag.Bool("split-process", !isMobile(), "split the process into multiple processes for better stability") lockTimeout := pflag.Duration("lock-timeout", 2*time.Minute, "[debug option] change the timeout for locking, before reporting it as a deadlock") + oauthListenPortTwitch := pflag.Uint16("oauth-listen-port-twitch", 8091, "the port that is used for OAuth callbacks while authenticating in Twitch") + oauthListenPortYouTube := pflag.Uint16("oauth-listen-port-youtube", 8092, "the port that is used for OAuth callbacks while authenticating in YouTube") + pflag.Parse() flags := Flags{ @@ -94,6 +100,9 @@ func parseFlags() Flags { Subprocess: *subprocess, SplitProcess: *splitProcess, LockTimeout: *lockTimeout, + + OAuthListenPortTwitch: *oauthListenPortTwitch, + OAuthListenPortYouTube: *oauthListenPortYouTube, } for _, platformGetFlagsFunc := range platformGetFlagsFuncs { diff --git a/cmd/streampanel/main.go b/cmd/streampanel/main.go index 4aca4a6..955e375 100644 --- a/cmd/streampanel/main.go +++ b/cmd/streampanel/main.go @@ -69,7 +69,10 @@ func runPanel( logger.Debugf(ctx, "runPanel: %#+v", flags) defer logger.Debugf(ctx, "/runPanel") - var opts []streampanel.Option + opts := []streampanel.Option{ + streampanel.OptionOAuthListenPortTwitch(flags.OAuthListenPortTwitch), + streampanel.OptionOAuthListenPortYouTube(flags.OAuthListenPortYouTube), + } if flags.RemoteAddr != "" { opts = append(opts, streampanel.OptionRemoteStreamDAddr(flags.RemoteAddr)) } diff --git a/pkg/recoder/libav/saferecoder/process/run_nolibav.go b/pkg/recoder/libav/saferecoder/process/run_nolibav.go index e0366d6..70ec5d7 100644 --- a/pkg/recoder/libav/saferecoder/process/run_nolibav.go +++ b/pkg/recoder/libav/saferecoder/process/run_nolibav.go @@ -82,3 +82,17 @@ func (c *Client) RecodingEndedChan( ) (<-chan struct{}, error) { return nil, fmt.Errorf("not compiled with libav support") } + +func (c *Client) CloseInput( + ctx context.Context, + inputID InputID, +) error { + return fmt.Errorf("not compiled with libav support") +} + +func (c *Client) CloseOutput( + ctx context.Context, + outputID OutputID, +) error { + return fmt.Errorf("not compiled with libav support") +} diff --git a/pkg/streamcontrol/twitch/twitch.go b/pkg/streamcontrol/twitch/twitch.go index c28b55c..489c437 100644 --- a/pkg/streamcontrol/twitch/twitch.go +++ b/pkg/streamcontrol/twitch/twitch.go @@ -44,15 +44,26 @@ func New( if cfg.Config.ClientID == "" || cfg.Config.ClientSecret == "" { return nil, fmt.Errorf("'clientid' or/and 'clientsecret' is/are not set; go to https://dev.twitch.tv/console/apps/create and create an app if it not created, yet") } - client, err := getClient(ctx, cfg) - if err != nil { - return nil, err + + getPortsFn := cfg.Config.GetOAuthListenPorts + if getPortsFn == nil { + // TODO: find a way to adjust the OAuth ports dynamically without re-creating the Twitch client. + return nil, fmt.Errorf("the function GetOAuthListenPorts is not set") } + oauthPorts := getPortsFn() + if len(oauthPorts) == 0 { + return nil, fmt.Errorf("the function GetOAuthListenPorts returned zero ports") + } + t := &Twitch{ - client: client, config: cfg, saveCfgFn: saveCfgFn, } + client, err := getClient(ctx, cfg, oauthPorts[0]) + if err != nil { + return nil, err + } + t.client = client var prevTokenUpdate time.Time client.OnUserAccessTokenRefreshed(func(newAccessToken, newRefreshToken string) { if twitchDebug == true { @@ -451,10 +462,6 @@ func (t *Twitch) getNewClientCode( ) (_err error) { logger.Debugf(ctx, "getNewClientCode") defer func() { logger.Debugf(ctx, "/getNewClientCode: %v", _err) }() - getPortsFn := t.config.Config.GetOAuthListenPorts - if getPortsFn == nil { - return fmt.Errorf("the function GetOAuthListenPorts is not set") - } oauthHandler := t.config.Config.CustomOAuthHandler if oauthHandler == nil { @@ -532,6 +539,13 @@ func (t *Twitch) getNewClientCode( } } + // TODO: either support only one port as in New, or support multiple + // ports as we do below + getPortsFn := t.config.Config.GetOAuthListenPorts + if getPortsFn == nil { + return fmt.Errorf("the function GetOAuthListenPorts is not set") + } + for _, listenPort := range getPortsFn() { startHandlerForPort(listenPort) } @@ -628,14 +642,15 @@ func (t *Twitch) getNewTokenByApp( func getClient( ctx context.Context, cfg Config, + oauthListenPort uint16, ) (*helix.Client, error) { - logger.Debugf(ctx, "getClient") + logger.Debugf(ctx, "getClient(ctx, %#+v, %v)", cfg, oauthListenPort) defer logger.Debugf(ctx, "/getClient") options := &helix.Options{ ClientID: cfg.Config.ClientID, ClientSecret: cfg.Config.ClientSecret, - RedirectURI: authRedirectURI(8091), // TODO: delete this hardcode + RedirectURI: authRedirectURI(oauthListenPort), // TODO: delete this hardcode } client, err := helix.NewClient(options) if err != nil { diff --git a/pkg/streampanel/config/config.go b/pkg/streampanel/config/config.go index b8eacb1..49c0db1 100644 --- a/pkg/streampanel/config/config.go +++ b/pkg/streampanel/config/config.go @@ -20,11 +20,19 @@ type BrowserConfig struct { Command string `yaml:"command"` } +type OAuthConfig struct { + ListenPorts struct { + Twitch uint16 `yaml:"twitch"` + YouTube uint16 `yaml:"youtube"` + } `yaml:"listen_ports"` +} + type Config struct { RemoteStreamDAddr string `yaml:"streamd_remote"` BuiltinStreamD streamd.Config `yaml:"streamd_builtin"` Screenshot ScreenshotConfig `yaml:"screenshot"` Browser BrowserConfig `yaml:"browser"` + OAuth OAuthConfig `yaml:"oauth"` } func DefaultConfig() Config { diff --git a/pkg/streampanel/config/options.go b/pkg/streampanel/config/options.go index b078b0f..3ba1ddb 100644 --- a/pkg/streampanel/config/options.go +++ b/pkg/streampanel/config/options.go @@ -18,3 +18,15 @@ type OptionRemoteStreamDAddr string func (o OptionRemoteStreamDAddr) Apply(cfg *Config) { cfg.RemoteStreamDAddr = string(o) } + +type OptionOAuthListenPortTwitch uint16 + +func (o OptionOAuthListenPortTwitch) Apply(cfg *Config) { + cfg.OAuth.ListenPorts.Twitch = uint16(o) +} + +type OptionOAuthListenPortYouTube uint16 + +func (o OptionOAuthListenPortYouTube) Apply(cfg *Config) { + cfg.OAuth.ListenPorts.YouTube = uint16(o) +} diff --git a/pkg/streampanel/options.go b/pkg/streampanel/options.go index cb0d973..227abd1 100644 --- a/pkg/streampanel/options.go +++ b/pkg/streampanel/options.go @@ -1,8 +1,13 @@ package streampanel -import "github.com/xaionaro-go/streamctl/pkg/streampanel/config" +import ( + "github.com/xaionaro-go/streamctl/pkg/streampanel/config" +) type Config = config.Config type Option = config.Option type Options = config.Options type OptionRemoteStreamDAddr = config.OptionRemoteStreamDAddr + +type OptionOAuthListenPortTwitch = config.OptionOAuthListenPortTwitch +type OptionOAuthListenPortYouTube = config.OptionOAuthListenPortYouTube diff --git a/pkg/streampanel/panel.go b/pkg/streampanel/panel.go index d8c5ffd..e7fbb6a 100644 --- a/pkg/streampanel/panel.go +++ b/pkg/streampanel/panel.go @@ -322,10 +322,10 @@ func (p *Panel) Loop(ctx context.Context, opts ...LoopOption) error { // TODO: delete this hardcoding of the port defer closeLoadingWindow() streamD := p.StreamD.(*streamd.StreamD) - streamD.AddOAuthListenPort(8091) + streamD.AddOAuthListenPort(p.Config.OAuth.ListenPorts.Twitch) observability.Go(ctx, func() { <-ctx.Done() - streamD.RemoveOAuthListenPort(8091) + streamD.RemoveOAuthListenPort(p.Config.OAuth.ListenPorts.Twitch) }) logger.Tracef(ctx, "started oauth listener for the local streamd") } @@ -367,7 +367,7 @@ func (p *Panel) startOAuthListenerForRemoteStreamD( streamD *client.Client, ) error { ctx, cancelFn := context.WithCancel(ctx) - receiver, listenPort, err := oauthhandler.NewCodeReceiver(ctx, 8091) // TODO: remove the hard-code of port 8091, currently it is needed because the port is hardcoded in Twitch + receiver, listenPort, err := oauthhandler.NewCodeReceiver(ctx, p.Config.OAuth.ListenPorts.Twitch) if err != nil { cancelFn() return fmt.Errorf("unable to start listener for OAuth responses: %w", err)