feat(twitch): Add flag --oauth-listen-port-twitch

This commit is contained in:
Dmitrii Okunev
2024-10-15 20:48:22 +01:00
parent 3ac051113f
commit a75efc79e1
8 changed files with 81 additions and 15 deletions

View File

@@ -42,6 +42,9 @@ type Flags struct {
Subprocess string `yaml:"Subprocess,omitempty"` Subprocess string `yaml:"Subprocess,omitempty"`
SplitProcess bool `yaml:"SplitProcess,omitempty"` SplitProcess bool `yaml:"SplitProcess,omitempty"`
LockTimeout time.Duration `yaml:"LockTimeout,omitempty"` LockTimeout time.Duration `yaml:"LockTimeout,omitempty"`
OAuthListenPortTwitch uint16 `yaml:"OAuthListenPortTwitch,omitempty"`
OAuthListenPortYouTube uint16 `yaml:"OAuthListenPortYouTube,omitempty"`
} }
var platformGetFlagsFuncs []func(*Flags) 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)") 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") 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") 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() pflag.Parse()
flags := Flags{ flags := Flags{
@@ -94,6 +100,9 @@ func parseFlags() Flags {
Subprocess: *subprocess, Subprocess: *subprocess,
SplitProcess: *splitProcess, SplitProcess: *splitProcess,
LockTimeout: *lockTimeout, LockTimeout: *lockTimeout,
OAuthListenPortTwitch: *oauthListenPortTwitch,
OAuthListenPortYouTube: *oauthListenPortYouTube,
} }
for _, platformGetFlagsFunc := range platformGetFlagsFuncs { for _, platformGetFlagsFunc := range platformGetFlagsFuncs {

View File

@@ -69,7 +69,10 @@ func runPanel(
logger.Debugf(ctx, "runPanel: %#+v", flags) logger.Debugf(ctx, "runPanel: %#+v", flags)
defer logger.Debugf(ctx, "/runPanel") defer logger.Debugf(ctx, "/runPanel")
var opts []streampanel.Option opts := []streampanel.Option{
streampanel.OptionOAuthListenPortTwitch(flags.OAuthListenPortTwitch),
streampanel.OptionOAuthListenPortYouTube(flags.OAuthListenPortYouTube),
}
if flags.RemoteAddr != "" { if flags.RemoteAddr != "" {
opts = append(opts, streampanel.OptionRemoteStreamDAddr(flags.RemoteAddr)) opts = append(opts, streampanel.OptionRemoteStreamDAddr(flags.RemoteAddr))
} }

View File

@@ -82,3 +82,17 @@ func (c *Client) RecodingEndedChan(
) (<-chan struct{}, error) { ) (<-chan struct{}, error) {
return nil, fmt.Errorf("not compiled with libav support") 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")
}

View File

@@ -44,15 +44,26 @@ func New(
if cfg.Config.ClientID == "" || cfg.Config.ClientSecret == "" { 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") 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 { getPortsFn := cfg.Config.GetOAuthListenPorts
return nil, err 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{ t := &Twitch{
client: client,
config: cfg, config: cfg,
saveCfgFn: saveCfgFn, saveCfgFn: saveCfgFn,
} }
client, err := getClient(ctx, cfg, oauthPorts[0])
if err != nil {
return nil, err
}
t.client = client
var prevTokenUpdate time.Time var prevTokenUpdate time.Time
client.OnUserAccessTokenRefreshed(func(newAccessToken, newRefreshToken string) { client.OnUserAccessTokenRefreshed(func(newAccessToken, newRefreshToken string) {
if twitchDebug == true { if twitchDebug == true {
@@ -451,10 +462,6 @@ func (t *Twitch) getNewClientCode(
) (_err error) { ) (_err error) {
logger.Debugf(ctx, "getNewClientCode") logger.Debugf(ctx, "getNewClientCode")
defer func() { logger.Debugf(ctx, "/getNewClientCode: %v", _err) }() 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 oauthHandler := t.config.Config.CustomOAuthHandler
if oauthHandler == nil { 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() { for _, listenPort := range getPortsFn() {
startHandlerForPort(listenPort) startHandlerForPort(listenPort)
} }
@@ -628,14 +642,15 @@ func (t *Twitch) getNewTokenByApp(
func getClient( func getClient(
ctx context.Context, ctx context.Context,
cfg Config, cfg Config,
oauthListenPort uint16,
) (*helix.Client, error) { ) (*helix.Client, error) {
logger.Debugf(ctx, "getClient") logger.Debugf(ctx, "getClient(ctx, %#+v, %v)", cfg, oauthListenPort)
defer logger.Debugf(ctx, "/getClient") defer logger.Debugf(ctx, "/getClient")
options := &helix.Options{ options := &helix.Options{
ClientID: cfg.Config.ClientID, ClientID: cfg.Config.ClientID,
ClientSecret: cfg.Config.ClientSecret, ClientSecret: cfg.Config.ClientSecret,
RedirectURI: authRedirectURI(8091), // TODO: delete this hardcode RedirectURI: authRedirectURI(oauthListenPort), // TODO: delete this hardcode
} }
client, err := helix.NewClient(options) client, err := helix.NewClient(options)
if err != nil { if err != nil {

View File

@@ -20,11 +20,19 @@ type BrowserConfig struct {
Command string `yaml:"command"` Command string `yaml:"command"`
} }
type OAuthConfig struct {
ListenPorts struct {
Twitch uint16 `yaml:"twitch"`
YouTube uint16 `yaml:"youtube"`
} `yaml:"listen_ports"`
}
type Config struct { type Config struct {
RemoteStreamDAddr string `yaml:"streamd_remote"` RemoteStreamDAddr string `yaml:"streamd_remote"`
BuiltinStreamD streamd.Config `yaml:"streamd_builtin"` BuiltinStreamD streamd.Config `yaml:"streamd_builtin"`
Screenshot ScreenshotConfig `yaml:"screenshot"` Screenshot ScreenshotConfig `yaml:"screenshot"`
Browser BrowserConfig `yaml:"browser"` Browser BrowserConfig `yaml:"browser"`
OAuth OAuthConfig `yaml:"oauth"`
} }
func DefaultConfig() Config { func DefaultConfig() Config {

View File

@@ -18,3 +18,15 @@ type OptionRemoteStreamDAddr string
func (o OptionRemoteStreamDAddr) Apply(cfg *Config) { func (o OptionRemoteStreamDAddr) Apply(cfg *Config) {
cfg.RemoteStreamDAddr = string(o) 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)
}

View File

@@ -1,8 +1,13 @@
package streampanel 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 Config = config.Config
type Option = config.Option type Option = config.Option
type Options = config.Options type Options = config.Options
type OptionRemoteStreamDAddr = config.OptionRemoteStreamDAddr type OptionRemoteStreamDAddr = config.OptionRemoteStreamDAddr
type OptionOAuthListenPortTwitch = config.OptionOAuthListenPortTwitch
type OptionOAuthListenPortYouTube = config.OptionOAuthListenPortYouTube

View File

@@ -322,10 +322,10 @@ func (p *Panel) Loop(ctx context.Context, opts ...LoopOption) error {
// TODO: delete this hardcoding of the port // TODO: delete this hardcoding of the port
defer closeLoadingWindow() defer closeLoadingWindow()
streamD := p.StreamD.(*streamd.StreamD) streamD := p.StreamD.(*streamd.StreamD)
streamD.AddOAuthListenPort(8091) streamD.AddOAuthListenPort(p.Config.OAuth.ListenPorts.Twitch)
observability.Go(ctx, func() { observability.Go(ctx, func() {
<-ctx.Done() <-ctx.Done()
streamD.RemoveOAuthListenPort(8091) streamD.RemoveOAuthListenPort(p.Config.OAuth.ListenPorts.Twitch)
}) })
logger.Tracef(ctx, "started oauth listener for the local streamd") logger.Tracef(ctx, "started oauth listener for the local streamd")
} }
@@ -367,7 +367,7 @@ func (p *Panel) startOAuthListenerForRemoteStreamD(
streamD *client.Client, streamD *client.Client,
) error { ) error {
ctx, cancelFn := context.WithCancel(ctx) 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 { if err != nil {
cancelFn() cancelFn()
return fmt.Errorf("unable to start listener for OAuth responses: %w", err) return fmt.Errorf("unable to start listener for OAuth responses: %w", err)