mirror of
https://github.com/xaionaro-go/streamctl.git
synced 2025-10-18 21:34:35 +08:00
Add the boilerplate code for Kick
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
kick "github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick/types"
|
||||
obs "github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs/types"
|
||||
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
|
||||
youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types"
|
||||
@@ -216,7 +217,7 @@ func streamStatus(cmd *cobra.Command, args []string) {
|
||||
|
||||
result := map[streamcontrol.PlatformName]*streamcontrol.StreamStatus{}
|
||||
for _, platID := range []streamcontrol.PlatformName{
|
||||
obs.ID, twitch.ID, youtube.ID,
|
||||
obs.ID, twitch.ID, youtube.ID, kick.ID,
|
||||
} {
|
||||
isEnabled, err := streamD.IsBackendEnabled(ctx, platID)
|
||||
assertNoError(ctx, err)
|
||||
|
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
"github.com/xaionaro-go/streamctl/pkg/xsync"
|
||||
@@ -138,12 +139,14 @@ func expandPath(rawPath string) string {
|
||||
|
||||
const (
|
||||
idTwitch = twitch.ID
|
||||
idKick = kick.ID
|
||||
idYoutube = youtube.ID
|
||||
)
|
||||
|
||||
func newConfig() streamcontrol.Config {
|
||||
cfg := streamcontrol.Config{}
|
||||
twitch.InitConfig(cfg)
|
||||
kick.InitConfig(cfg)
|
||||
youtube.InitConfig(cfg)
|
||||
return cfg
|
||||
}
|
||||
@@ -157,6 +160,9 @@ func generateConfig(cmd *cobra.Command, args []string) {
|
||||
cfg[idTwitch].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{
|
||||
"some_profile": twitch.StreamProfile{},
|
||||
}
|
||||
cfg[idKick].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{
|
||||
"some_profile": kick.StreamProfile{},
|
||||
}
|
||||
cfg[idYoutube].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{
|
||||
"some_profile": youtube.StreamProfile{},
|
||||
}
|
||||
@@ -218,13 +224,24 @@ func readConfigFromPath(
|
||||
logger.Debugf(ctx, "final stream profiles of twitch: %#+v", (*cfg)[idTwitch].StreamProfiles)
|
||||
}
|
||||
|
||||
if (*cfg)[idKick] != nil {
|
||||
err = streamcontrol.ConvertStreamProfiles[kick.StreamProfile](
|
||||
ctx,
|
||||
(*cfg)[idKick].StreamProfiles,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to convert stream profiles of kick: %w: <%s>", err, b)
|
||||
}
|
||||
logger.Debugf(ctx, "final stream profiles of kick: %#+v", (*cfg)[idKick].StreamProfiles)
|
||||
}
|
||||
|
||||
if (*cfg)[idYoutube] != nil {
|
||||
err = streamcontrol.ConvertStreamProfiles[youtube.StreamProfile](
|
||||
ctx,
|
||||
(*cfg)[idYoutube].StreamProfiles,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to convert stream profiles of twitch: %w: <%s>", err, b)
|
||||
return fmt.Errorf("unable to convert stream profiles of youtube: %w: <%s>", err, b)
|
||||
}
|
||||
logger.Debugf(
|
||||
ctx,
|
||||
@@ -283,6 +300,34 @@ func getTwitchStreamController(
|
||||
)
|
||||
}
|
||||
|
||||
func getKickStreamController(
|
||||
ctx context.Context,
|
||||
cfg streamcontrol.Config,
|
||||
) (*kick.Kick, error) {
|
||||
platCfg := streamcontrol.GetPlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile](
|
||||
ctx,
|
||||
cfg,
|
||||
idKick,
|
||||
)
|
||||
if platCfg == nil {
|
||||
logger.Infof(ctx, "kick config was not found")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
logger.Debugf(ctx, "kick config: %#+v", platCfg)
|
||||
return kick.New(ctx, *platCfg,
|
||||
func(c kick.Config) error {
|
||||
return xsync.DoR1(ctx, &saveConfigLock, func() error {
|
||||
cfg[idKick] = &streamcontrol.AbstractPlatformConfig{
|
||||
Config: c.Config,
|
||||
StreamProfiles: streamcontrol.ToAbstractStreamProfiles(c.StreamProfiles),
|
||||
}
|
||||
return saveConfig(ctx, cfg)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func getYouTubeStreamController(
|
||||
ctx context.Context,
|
||||
cfg streamcontrol.Config,
|
||||
@@ -325,6 +370,14 @@ func getStreamControllers(
|
||||
result = append(result, streamcontrol.ToAbstract(twitch))
|
||||
}
|
||||
|
||||
kick, err := getKickStreamController(ctx, cfg)
|
||||
if err != nil {
|
||||
logger.Panic(ctx, err)
|
||||
}
|
||||
if kick != nil {
|
||||
result = append(result, streamcontrol.ToAbstract(kick))
|
||||
}
|
||||
|
||||
youtube, err := getYouTubeStreamController(ctx, cfg)
|
||||
if err != nil {
|
||||
logger.Panic(ctx, err)
|
||||
|
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/xaionaro-go/streamctl/cmd/streamd/ui"
|
||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -265,6 +266,9 @@ func main() {
|
||||
func(ctx context.Context, cfg *streamcontrol.PlatformConfig[twitch.PlatformSpecificConfig, twitch.StreamProfile]) (bool, error) {
|
||||
return false, streamd.ErrSkipBackend
|
||||
},
|
||||
func(ctx context.Context, cfg *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile]) (bool, error) {
|
||||
return false, streamd.ErrSkipBackend
|
||||
},
|
||||
func(ctx context.Context, cfg *streamcontrol.PlatformConfig[youtube.PlatformSpecificConfig, youtube.StreamProfile]) (bool, error) {
|
||||
return false, streamd.ErrSkipBackend
|
||||
},
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/facebookincubator/go-belt/tool/logger"
|
||||
"github.com/xaionaro-go/streamctl/pkg/oauthhandler"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
obs "github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs/types"
|
||||
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
|
||||
youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types"
|
||||
@@ -28,6 +29,10 @@ type UI struct {
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[twitch.PlatformSpecificConfig, twitch.StreamProfile],
|
||||
) (bool, error)
|
||||
InputKickUserInfoFn func(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile],
|
||||
) (bool, error)
|
||||
InputYouTubeUserInfoFn func(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[youtube.PlatformSpecificConfig, youtube.StreamProfile],
|
||||
@@ -50,6 +55,10 @@ func NewUI(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[twitch.PlatformSpecificConfig, twitch.StreamProfile],
|
||||
) (bool, error),
|
||||
inputKickUserInfoFn func(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile],
|
||||
) (bool, error),
|
||||
inputYouTubeUserInfoFn func(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[youtube.PlatformSpecificConfig, youtube.StreamProfile],
|
||||
@@ -68,6 +77,7 @@ func NewUI(
|
||||
CodeChMapLocker: xsync.RWMutex{},
|
||||
SetLoggingLevelFn: setLoggingLevel,
|
||||
InputTwitchUserInfoFn: inputTwitchUserInfoFn,
|
||||
InputKickUserInfoFn: inputKickUserInfoFn,
|
||||
InputYouTubeUserInfoFn: inputYouTubeUserInfoFn,
|
||||
InputOBSConnectInfoFn: inputOBSConnectInfoFn,
|
||||
}
|
||||
@@ -220,6 +230,13 @@ func (ui *UI) OAuthHandlerTwitch(
|
||||
return ui.oauth2Handler(ctx, twitch.ID, arg)
|
||||
}
|
||||
|
||||
func (ui *UI) OAuthHandlerKick(
|
||||
ctx context.Context,
|
||||
arg oauthhandler.OAuthHandlerArgument,
|
||||
) error {
|
||||
return ui.oauth2Handler(ctx, kick.ID, arg)
|
||||
}
|
||||
|
||||
func (ui *UI) OAuthHandlerYouTube(
|
||||
ctx context.Context,
|
||||
arg oauthhandler.OAuthHandlerArgument,
|
||||
@@ -234,6 +251,13 @@ func (ui *UI) InputTwitchUserInfo(
|
||||
return ui.InputTwitchUserInfoFn(ctx, cfg)
|
||||
}
|
||||
|
||||
func (ui *UI) InputKickUserInfo(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile],
|
||||
) (bool, error) {
|
||||
return ui.InputKickUserInfoFn(ctx, cfg)
|
||||
}
|
||||
|
||||
func (ui *UI) InputYouTubeUserInfo(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[youtube.PlatformSpecificConfig, youtube.StreamProfile],
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/xaionaro-go/streamctl/pkg/mainprocess"
|
||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -147,6 +148,12 @@ func runStreamd(
|
||||
) (bool, error) {
|
||||
return false, streamd.ErrSkipBackend
|
||||
},
|
||||
func(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile],
|
||||
) (bool, error) {
|
||||
return false, streamd.ErrSkipBackend
|
||||
},
|
||||
func(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[youtube.PlatformSpecificConfig, youtube.StreamProfile],
|
||||
|
@@ -19,19 +19,27 @@ type Kick struct {
|
||||
Channel *kickcom.ChannelV1
|
||||
Client Client
|
||||
ChatHandler *ChatHandler
|
||||
SaveCfgFn func(Config) error
|
||||
}
|
||||
|
||||
var _ streamcontrol.StreamController[StreamProfile] = (*Kick)(nil)
|
||||
|
||||
func New(channelSlug string) (*Kick, error) {
|
||||
ctx := context.TODO()
|
||||
func New(
|
||||
ctx context.Context,
|
||||
cfg Config,
|
||||
saveCfgFn func(Config) error,
|
||||
) (*Kick, error) {
|
||||
|
||||
if cfg.Config.Channel == "" {
|
||||
return nil, fmt.Errorf("channel is not set")
|
||||
}
|
||||
|
||||
client, err := kickcom.New()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to initialize a client to Kick: %w", err)
|
||||
}
|
||||
|
||||
channel, err := client.GetChannelV1(ctx, channelSlug)
|
||||
channel, err := client.GetChannelV1(ctx, cfg.Config.Channel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to obtain channel info: %w", err)
|
||||
}
|
||||
@@ -39,6 +47,7 @@ func New(channelSlug string) (*Kick, error) {
|
||||
k := &Kick{
|
||||
Client: client,
|
||||
Channel: channel,
|
||||
SaveCfgFn: saveCfgFn,
|
||||
}
|
||||
|
||||
chatHandler, err := k.newChatHandler(ctx, channel.ID)
|
||||
|
@@ -298,6 +298,9 @@ type BackendDataTwitch struct {
|
||||
Cache cache.Twitch
|
||||
}
|
||||
|
||||
type BackendDataKick struct {
|
||||
}
|
||||
|
||||
type BackendDataYouTube struct {
|
||||
Cache cache.YouTube
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/xaionaro-go/streamctl/pkg/player"
|
||||
"github.com/xaionaro-go/streamctl/pkg/player/protobuf/go/player_grpc"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
obs "github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs/types"
|
||||
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
|
||||
youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types"
|
||||
@@ -802,6 +803,13 @@ func (c *Client) GetBackendData(
|
||||
&_data,
|
||||
)
|
||||
data = _data
|
||||
case kick.ID:
|
||||
_data := api.BackendDataKick{}
|
||||
err = json.Unmarshal(
|
||||
[]byte(reply.GetData()),
|
||||
&_data,
|
||||
)
|
||||
data = _data
|
||||
case youtube.ID:
|
||||
_data := api.BackendDataYouTube{}
|
||||
err = json.Unmarshal(
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/facebookincubator/go-belt/tool/logger"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -35,6 +36,7 @@ func NewConfig() Config {
|
||||
cfg := streamcontrol.Config{}
|
||||
obs.InitConfig(cfg)
|
||||
twitch.InitConfig(cfg)
|
||||
kick.InitConfig(cfg)
|
||||
youtube.InitConfig(cfg)
|
||||
return Config{
|
||||
Backends: cfg,
|
||||
@@ -54,6 +56,9 @@ func NewSampleConfig() Config {
|
||||
cfg.Backends[twitch.ID].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{
|
||||
"some_profile": twitch.StreamProfile{},
|
||||
}
|
||||
cfg.Backends[kick.ID].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{
|
||||
"some_profile": kick.StreamProfile{},
|
||||
}
|
||||
cfg.Backends[youtube.ID].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{
|
||||
"some_profile": youtube.StreamProfile{},
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -89,6 +90,16 @@ func (cfg *Config) UnmarshalYAML(b []byte) (_err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Backends[kick.ID] != nil {
|
||||
err = streamcontrol.ConvertStreamProfiles[kick.StreamProfile](
|
||||
context.Background(),
|
||||
cfg.Backends[kick.ID].StreamProfiles,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to convert stream profiles of kick: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Backends[youtube.ID] != nil {
|
||||
err = streamcontrol.ConvertStreamProfiles[youtube.StreamProfile](
|
||||
context.Background(),
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/goccy/go-yaml"
|
||||
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -22,6 +23,8 @@ func ProfileGRPC2Go(
|
||||
profile = &obs.StreamProfile{}
|
||||
case twitch.ID:
|
||||
profile = &twitch.StreamProfile{}
|
||||
case kick.ID:
|
||||
profile = &kick.StreamProfile{}
|
||||
case youtube.ID:
|
||||
profile = &youtube.StreamProfile{}
|
||||
default:
|
||||
|
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -15,6 +16,8 @@ func NewStreamProfile(
|
||||
switch platID {
|
||||
case twitch.ID:
|
||||
return &twitch.StreamProfile{}, nil
|
||||
case kick.ID:
|
||||
return &kick.StreamProfile{}, nil
|
||||
case youtube.ID:
|
||||
return &youtube.StreamProfile{}, nil
|
||||
case obs.ID:
|
||||
|
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/facebookincubator/go-belt/tool/logger"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -37,6 +38,8 @@ func (d *StreamD) EXPERIMENTAL_ReinitStreamControllers(ctx context.Context) erro
|
||||
err = d.initOBSBackend(ctx)
|
||||
case strings.ToLower(string(twitch.ID)):
|
||||
err = d.initTwitchBackend(ctx)
|
||||
case strings.ToLower(string(kick.ID)):
|
||||
err = d.initKickBackend(ctx)
|
||||
case strings.ToLower(string(youtube.ID)):
|
||||
err = d.initYouTubeBackend(ctx)
|
||||
}
|
||||
@@ -196,6 +199,47 @@ func newTwitch(
|
||||
return twitch, nil
|
||||
}
|
||||
|
||||
func newKick(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.AbstractPlatformConfig,
|
||||
setUserData func(context.Context, *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile]) (bool, error),
|
||||
saveCfgFunc func(*streamcontrol.AbstractPlatformConfig) error,
|
||||
customOAuthHandler kick.OAuthHandler,
|
||||
getOAuthListenPorts func() []uint16,
|
||||
) (
|
||||
*kick.Kick,
|
||||
error,
|
||||
) {
|
||||
platCfg := streamcontrol.ConvertPlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile](
|
||||
ctx,
|
||||
cfg,
|
||||
)
|
||||
if platCfg == nil {
|
||||
return nil, fmt.Errorf("kick config was not found")
|
||||
}
|
||||
|
||||
if cfg.Enable != nil && !*cfg.Enable {
|
||||
return nil, ErrSkipBackend
|
||||
}
|
||||
|
||||
logger.Debugf(ctx, "kick config: %#+v", platCfg)
|
||||
cfg = streamcontrol.ToAbstractPlatformConfig(ctx, platCfg)
|
||||
kick, err := kick.New(ctx, *platCfg,
|
||||
func(c kick.Config) error {
|
||||
return saveCfgFunc(&streamcontrol.AbstractPlatformConfig{
|
||||
Enable: c.Enable,
|
||||
Config: c.Config,
|
||||
StreamProfiles: streamcontrol.ToAbstractStreamProfiles(c.StreamProfiles),
|
||||
Custom: c.Custom,
|
||||
})
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to initialize Kick client: %w", err)
|
||||
}
|
||||
return kick, nil
|
||||
}
|
||||
|
||||
func newYouTube(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.AbstractPlatformConfig,
|
||||
@@ -365,6 +409,24 @@ func (d *StreamD) initTwitchBackend(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *StreamD) initKickBackend(ctx context.Context) error {
|
||||
kick, err := newKick(
|
||||
ctx,
|
||||
d.Config.Backends[kick.ID],
|
||||
d.UI.InputKickUserInfo,
|
||||
func(cfg *streamcontrol.AbstractPlatformConfig) error {
|
||||
return d.setPlatformConfig(ctx, kick.ID, cfg)
|
||||
},
|
||||
d.UI.OAuthHandlerKick,
|
||||
d.GetOAuthListenPorts,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.StreamControllers.Kick = kick
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *StreamD) initYouTubeBackend(ctx context.Context) error {
|
||||
youTube, err := newYouTube(
|
||||
ctx,
|
||||
|
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/facebookincubator/go-belt/tool/logger"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
|
||||
youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamd/api"
|
||||
@@ -35,6 +36,8 @@ func (a *platformsControllerAdapter) CheckStreamStartedByURL(
|
||||
platID = youtube.ID
|
||||
case strings.Contains(destination.Hostname(), "twitch"):
|
||||
platID = twitch.ID
|
||||
case strings.Contains(destination.Hostname(), "global-contribute.live-video.net"):
|
||||
platID = kick.ID
|
||||
default:
|
||||
return false, fmt.Errorf(
|
||||
"do not know how to check if the stream started for '%s'",
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/xaionaro-go/streamctl/pkg/player"
|
||||
"github.com/xaionaro-go/streamctl/pkg/repository"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -42,6 +43,7 @@ import (
|
||||
type StreamControllers struct {
|
||||
OBS *obs.OBS
|
||||
Twitch *twitch.Twitch
|
||||
Kick *kick.Kick
|
||||
YouTube *youtube.YouTube
|
||||
}
|
||||
|
||||
@@ -450,6 +452,8 @@ func (d *StreamD) IsBackendEnabled(
|
||||
return d.StreamControllers.OBS != nil, nil
|
||||
case twitch.ID:
|
||||
return d.StreamControllers.Twitch != nil, nil
|
||||
case kick.ID:
|
||||
return d.StreamControllers.Kick != nil, nil
|
||||
case youtube.ID:
|
||||
return d.StreamControllers.YouTube != nil, nil
|
||||
default:
|
||||
@@ -518,6 +522,21 @@ func (d *StreamD) StartStream(
|
||||
return fmt.Errorf("unable to start the stream on Twitch: %w", err)
|
||||
}
|
||||
return nil
|
||||
case kick.ID:
|
||||
profile, err := streamcontrol.GetStreamProfile[kick.StreamProfile](ctx, profile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to convert the profile into Twitch profile: %w", err)
|
||||
}
|
||||
err = d.StreamControllers.Kick.StartStream(
|
||||
d.ctxForController(ctx),
|
||||
title,
|
||||
description,
|
||||
*profile,
|
||||
customArgs...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to start the stream on Twitch: %w", err)
|
||||
}
|
||||
return nil
|
||||
case youtube.ID:
|
||||
profile, err := streamcontrol.GetStreamProfile[youtube.StreamProfile](ctx, profile)
|
||||
if err != nil {
|
||||
@@ -617,6 +636,8 @@ func (d *StreamD) GetBackendData(
|
||||
return api.BackendDataOBS{}, nil
|
||||
case twitch.ID:
|
||||
return api.BackendDataTwitch{Cache: d.Cache.Twitch}, nil
|
||||
case kick.ID:
|
||||
return api.BackendDataKick{}, nil
|
||||
case youtube.ID:
|
||||
return api.BackendDataYouTube{Cache: d.Cache.Youtube}, nil
|
||||
default:
|
||||
@@ -644,6 +665,21 @@ func (d *StreamD) tryConnectTwitch(
|
||||
errmon.ObserveErrorCtx(ctx, err)
|
||||
}
|
||||
|
||||
func (d *StreamD) tryConnectKick(
|
||||
ctx context.Context,
|
||||
) {
|
||||
if d.StreamControllers.Kick != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := d.Config.Backends[kick.ID]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
err := d.initKickBackend(ctx)
|
||||
errmon.ObserveErrorCtx(ctx, err)
|
||||
}
|
||||
|
||||
func (d *StreamD) tryConnectYouTube(
|
||||
ctx context.Context,
|
||||
) {
|
||||
@@ -676,6 +712,13 @@ func (d *StreamD) streamController(
|
||||
if d.StreamControllers.Twitch != nil {
|
||||
result = streamcontrol.ToAbstract(d.StreamControllers.Twitch)
|
||||
}
|
||||
case kick.ID:
|
||||
if d.StreamControllers.Kick == nil {
|
||||
d.tryConnectKick(ctx)
|
||||
}
|
||||
if d.StreamControllers.Kick != nil {
|
||||
result = streamcontrol.ToAbstract(d.StreamControllers.Kick)
|
||||
}
|
||||
case youtube.ID:
|
||||
if d.StreamControllers.YouTube == nil {
|
||||
d.tryConnectYouTube(ctx)
|
||||
|
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/facebookincubator/go-belt/tool/logger"
|
||||
"github.com/xaionaro-go/streamctl/pkg/oauthhandler"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
obs "github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs/types"
|
||||
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
|
||||
youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types"
|
||||
@@ -19,12 +20,17 @@ type UI interface {
|
||||
ctx context.Context,
|
||||
) (bool, string, []byte, error)
|
||||
OAuthHandlerTwitch(ctx context.Context, arg oauthhandler.OAuthHandlerArgument) error
|
||||
OAuthHandlerKick(ctx context.Context, arg oauthhandler.OAuthHandlerArgument) error
|
||||
OAuthHandlerYouTube(ctx context.Context, arg oauthhandler.OAuthHandlerArgument) error
|
||||
OpenBrowser(ctx context.Context, url string) error
|
||||
InputTwitchUserInfo(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[twitch.PlatformSpecificConfig, twitch.StreamProfile],
|
||||
) (bool, error)
|
||||
InputKickUserInfo(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile],
|
||||
) (bool, error)
|
||||
InputYouTubeUserInfo(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[youtube.PlatformSpecificConfig, youtube.StreamProfile],
|
||||
|
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/xaionaro-go/streamctl/pkg/colorx"
|
||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -278,6 +279,7 @@ func (p *Panel) updateMonitorPageStreamStatus(
|
||||
obs.ID,
|
||||
youtube.ID,
|
||||
twitch.ID,
|
||||
kick.ID,
|
||||
} {
|
||||
wg.Add(1)
|
||||
observability.Go(ctx, func() {
|
||||
|
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/xaionaro-go/streamctl/pkg/screenshot"
|
||||
"github.com/xaionaro-go/streamctl/pkg/screenshoter"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||
@@ -107,6 +108,7 @@ type Panel struct {
|
||||
|
||||
youtubeCheck *widget.Check
|
||||
twitchCheck *widget.Check
|
||||
kickCheck *widget.Check
|
||||
|
||||
configPath string
|
||||
configCache *streamdconfig.Config
|
||||
@@ -662,6 +664,15 @@ func (p *Panel) OAuthHandlerTwitch(
|
||||
return p.oauthHandler(ctx, twitch.ID, arg)
|
||||
}
|
||||
|
||||
func (p *Panel) OAuthHandlerKick(
|
||||
ctx context.Context,
|
||||
arg oauthhandler.OAuthHandlerArgument,
|
||||
) error {
|
||||
logger.Infof(ctx, "OAuthHandlerKick: %#+v", arg)
|
||||
defer logger.Infof(ctx, "/OAuthHandlerKick")
|
||||
return p.oauthHandler(ctx, kick.ID, arg)
|
||||
}
|
||||
|
||||
func (p *Panel) OAuthHandlerYouTube(
|
||||
ctx context.Context,
|
||||
arg oauthhandler.OAuthHandlerArgument,
|
||||
@@ -857,6 +868,13 @@ func (p *Panel) InputTwitchUserInfo(
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Panel) InputKickUserInfo(
|
||||
ctx context.Context,
|
||||
cfg *streamcontrol.PlatformConfig[kick.PlatformSpecificConfig, kick.StreamProfile],
|
||||
) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented, yet")
|
||||
}
|
||||
|
||||
var youtubeCredentialsCreateLink, _ = url.Parse(
|
||||
"https://console.cloud.google.com/apis/credentials/oauthclient",
|
||||
)
|
||||
@@ -1105,6 +1123,7 @@ func (p *Panel) refilterProfiles(ctx context.Context) {
|
||||
subValueMatch = true
|
||||
break
|
||||
}
|
||||
case kick.StreamProfile:
|
||||
case youtube.StreamProfile:
|
||||
if containTagSubstringCI(prof.Tags, filterValue) {
|
||||
subValueMatch = true
|
||||
@@ -1214,6 +1233,7 @@ func (p *Panel) openSettingsWindow(ctx context.Context) error {
|
||||
for _, backendID := range []streamcontrol.PlatformName{
|
||||
obs.ID,
|
||||
twitch.ID,
|
||||
kick.ID,
|
||||
youtube.ID,
|
||||
} {
|
||||
isEnabled, err := p.StreamD.IsBackendEnabled(ctx, backendID)
|
||||
@@ -1319,6 +1339,13 @@ func (p *Panel) openSettingsWindow(ctx context.Context) error {
|
||||
twitchAlreadyLoggedIn.SetText("(already logged in)")
|
||||
}
|
||||
|
||||
kickAlreadyLoggedIn := widget.NewLabel("")
|
||||
if !backendEnabled[kick.ID] {
|
||||
kickAlreadyLoggedIn.SetText("(not logged in)")
|
||||
} else {
|
||||
kickAlreadyLoggedIn.SetText("(already logged in)")
|
||||
}
|
||||
|
||||
youtubeAlreadyLoggedIn := widget.NewLabel("")
|
||||
if !backendEnabled[youtube.ID] {
|
||||
youtubeAlreadyLoggedIn.SetText("(not logged in)")
|
||||
@@ -1432,6 +1459,27 @@ func (p *Panel) openSettingsWindow(ctx context.Context) error {
|
||||
}),
|
||||
twitchAlreadyLoggedIn,
|
||||
),
|
||||
container.NewHBox(
|
||||
widget.NewButtonWithIcon("(Re-)login in Kick", theme.LoginIcon(), func() {
|
||||
if cfg.Backends[kick.ID] == nil {
|
||||
kick.InitConfig(cfg.Backends)
|
||||
}
|
||||
|
||||
cfg.Backends[kick.ID].Enable = nil
|
||||
cfg.Backends[kick.ID].Config = kick.PlatformSpecificConfig{}
|
||||
|
||||
if err := p.StreamD.SetConfig(ctx, cfg); err != nil {
|
||||
p.DisplayError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := p.StreamD.EXPERIMENTAL_ReinitStreamControllers(ctx); err != nil {
|
||||
p.DisplayError(err)
|
||||
return
|
||||
}
|
||||
}),
|
||||
kickAlreadyLoggedIn,
|
||||
),
|
||||
container.NewHBox(
|
||||
widget.NewButtonWithIcon("(Re-)login in YouTube", theme.LoginIcon(), func() {
|
||||
if cfg.Backends[youtube.ID] == nil {
|
||||
@@ -1603,6 +1651,7 @@ func (p *Panel) getUpdatedStatus_backends_noLock(ctx context.Context) {
|
||||
for _, backendID := range []streamcontrol.PlatformName{
|
||||
obs.ID,
|
||||
twitch.ID,
|
||||
kick.ID,
|
||||
youtube.ID,
|
||||
} {
|
||||
isEnabled, err := p.StreamD.IsBackendEnabled(ctx, backendID)
|
||||
@@ -1616,6 +1665,9 @@ func (p *Panel) getUpdatedStatus_backends_noLock(ctx context.Context) {
|
||||
if backendEnabled[twitch.ID] {
|
||||
p.twitchCheck.Enable()
|
||||
}
|
||||
if backendEnabled[kick.ID] {
|
||||
p.kickCheck.Enable()
|
||||
}
|
||||
if backendEnabled[youtube.ID] {
|
||||
p.youtubeCheck.Enable()
|
||||
}
|
||||
@@ -1856,6 +1908,10 @@ func (p *Panel) initMainWindow(
|
||||
p.twitchCheck.SetChecked(true)
|
||||
p.twitchCheck.Disable()
|
||||
|
||||
p.kickCheck = widget.NewCheck("Kick", nil)
|
||||
p.kickCheck.SetChecked(true)
|
||||
p.kickCheck.Disable()
|
||||
|
||||
p.youtubeCheck = widget.NewCheck("YouTube", nil)
|
||||
p.youtubeCheck.SetChecked(true)
|
||||
p.youtubeCheck.Disable()
|
||||
@@ -1892,6 +1948,9 @@ func (p *Panel) initMainWindow(
|
||||
twLabel := widget.NewLabel("TW:")
|
||||
twLabel.Importance = widget.HighImportance
|
||||
p.streamStatus[twitch.ID] = widget.NewLabel("")
|
||||
kcLabel := widget.NewLabel("Kc:")
|
||||
kcLabel.Importance = widget.HighImportance
|
||||
p.streamStatus[kick.ID] = widget.NewLabel("")
|
||||
ytLabel := widget.NewLabel("YT:")
|
||||
ytLabel.Importance = widget.HighImportance
|
||||
p.streamStatus[youtube.ID] = widget.NewLabel("")
|
||||
@@ -1903,6 +1962,7 @@ func (p *Panel) initMainWindow(
|
||||
}
|
||||
streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), obsLabel, p.streamStatus[obs.ID]))
|
||||
streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), twLabel, p.streamStatus[twitch.ID]))
|
||||
streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), kcLabel, p.streamStatus[kick.ID]))
|
||||
streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), ytLabel, p.streamStatus[youtube.ID]))
|
||||
streamInfoContainer := container.NewBorder(
|
||||
nil,
|
||||
@@ -2243,6 +2303,7 @@ func (p *Panel) setupStreamNoLock(ctx context.Context) {
|
||||
for _, backendID := range []streamcontrol.PlatformName{
|
||||
obs.ID,
|
||||
twitch.ID,
|
||||
kick.ID,
|
||||
youtube.ID,
|
||||
} {
|
||||
isEnabled, err := p.StreamD.IsBackendEnabled(ctx, backendID)
|
||||
@@ -2278,6 +2339,19 @@ func (p *Panel) setupStreamNoLock(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if p.kickCheck.Checked && backendEnabled[kick.ID] {
|
||||
err := p.StreamD.StartStream(
|
||||
ctx,
|
||||
kick.ID,
|
||||
p.streamTitleField.Text,
|
||||
p.streamDescriptionField.Text,
|
||||
profile.PerPlatform[kick.ID],
|
||||
)
|
||||
if err != nil {
|
||||
p.DisplayError(fmt.Errorf("unable to setup the stream on Twitch: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
if p.youtubeCheck.Checked && backendEnabled[youtube.ID] {
|
||||
if p.streamIsRunning(ctx, youtube.ID) {
|
||||
logger.Debugf(ctx, "updating the stream info at YouTube")
|
||||
@@ -2435,6 +2509,9 @@ func (p *Panel) stopStreamNoLock(ctx context.Context) {
|
||||
if backendEnabled[twitch.ID] {
|
||||
p.twitchCheck.Enable()
|
||||
}
|
||||
if backendEnabled[kick.ID] {
|
||||
p.kickCheck.Enable()
|
||||
}
|
||||
if backendEnabled[youtube.ID] {
|
||||
p.youtubeCheck.Enable()
|
||||
}
|
||||
@@ -2845,6 +2922,7 @@ func (p *Panel) profileWindow(
|
||||
var (
|
||||
obsProfile *obs.StreamProfile
|
||||
twitchProfile *twitch.StreamProfile
|
||||
kickProfile *kick.StreamProfile
|
||||
youtubeProfile *youtube.StreamProfile
|
||||
)
|
||||
|
||||
@@ -2870,6 +2948,7 @@ func (p *Panel) profileWindow(
|
||||
for _, backendID := range []streamcontrol.PlatformName{
|
||||
obs.ID,
|
||||
twitch.ID,
|
||||
kick.ID,
|
||||
youtube.ID,
|
||||
} {
|
||||
isEnabled, err := p.StreamD.IsBackendEnabled(ctx, backendID)
|
||||
@@ -2893,6 +2972,8 @@ func (p *Panel) profileWindow(
|
||||
}
|
||||
_ = backendData[obs.ID].(api.BackendDataOBS)
|
||||
dataTwitch := backendData[twitch.ID].(api.BackendDataTwitch)
|
||||
dataKick := backendData[kick.ID].(api.BackendDataKick)
|
||||
_ = dataKick // TODO: delete me!
|
||||
dataYouTube := backendData[youtube.ID].(api.BackendDataYouTube)
|
||||
|
||||
var bottomContent []fyne.CanvasObject
|
||||
@@ -3026,6 +3107,14 @@ func (p *Panel) profileWindow(
|
||||
bottomContent = append(bottomContent, widget.NewLabel("Twitch is disabled"))
|
||||
}
|
||||
|
||||
bottomContent = append(bottomContent, widget.NewSeparator())
|
||||
bottomContent = append(bottomContent, widget.NewRichTextFromMarkdown("# Kick:"))
|
||||
if backendEnabled[kick.ID] {
|
||||
bottomContent = append(bottomContent, widget.NewLabel("Kick configuration is not implemented, yet"))
|
||||
} else {
|
||||
bottomContent = append(bottomContent, widget.NewLabel("Kick is disabled"))
|
||||
}
|
||||
|
||||
var getYoutubeTags func() []string
|
||||
bottomContent = append(bottomContent, widget.NewSeparator())
|
||||
bottomContent = append(bottomContent, widget.NewRichTextFromMarkdown("# YouTube:"))
|
||||
@@ -3228,6 +3317,9 @@ func (p *Panel) profileWindow(
|
||||
}
|
||||
profile.PerPlatform[twitch.ID] = twitchProfile
|
||||
}
|
||||
if kickProfile != nil {
|
||||
profile.PerPlatform[kick.ID] = kickProfile
|
||||
}
|
||||
if youtubeProfile != nil {
|
||||
if getYoutubeTags != nil {
|
||||
youtubeProfile.Tags = sanitizeTags(getYoutubeTags())
|
||||
|
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/xaionaro-go/streamctl/pkg/observability"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
|
||||
obs "github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs/types"
|
||||
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
|
||||
youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types"
|
||||
@@ -250,6 +251,7 @@ func (ui *timersUI) kickOffRemotely(
|
||||
for _, platID := range []streamcontrol.PlatformName{
|
||||
youtube.ID,
|
||||
twitch.ID,
|
||||
kick.ID,
|
||||
obs.ID,
|
||||
} {
|
||||
_, err := streamD.AddTimer(ctx, deadline, &action.EndStream{
|
||||
|
Reference in New Issue
Block a user