Initial commit, pt. 6

This commit is contained in:
Dmitrii Okunev
2024-04-20 21:49:00 +01:00
parent a4aff24989
commit e68174a7a0
5 changed files with 257 additions and 16 deletions

View File

@@ -3,10 +3,26 @@ package main
import (
"context"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/facebookincubator/go-belt/tool/logger/implementation/zap"
"github.com/spf13/pflag"
"github.com/xaionaro-go/streamctl/pkg/streampanel"
)
func main() {
streampanel.New("/tmp/test.yaml").Loop(context.Background())
loggerLevel := logger.LevelWarning
pflag.Var(&loggerLevel, "log-level", "Log level")
configPath := pflag.String("config-path", "~/.streampanel.yaml", "the path to the config file")
pflag.Parse()
l := zap.Default().WithLevel(loggerLevel)
ctx := context.Background()
ctx = logger.CtxWithLogger(ctx, l)
logger.Default = func() logger.Logger {
return l
}
defer belt.Flush(ctx)
l.Fatal(streampanel.New(*configPath).Loop(ctx))
}

View File

@@ -126,12 +126,22 @@ func GetPlatformConfig[T any, S StreamProfile](
logger.Debugf(ctx, "config '%s' was not found in cfg: %#+v", id, cfg)
return nil
}
return ConvertPlatformConfig[T, S](ctx, platCfg, id)
}
func ConvertPlatformConfig[T any, S StreamProfile](
ctx context.Context,
platCfg *AbstractPlatformConfig,
id PlatformName,
) *PlatformConfig[T, S] {
platCfgCfg, ok := platCfg.Config.(*T)
if !ok {
var zeroValue T
logger.Errorf(ctx, "unable to get the config: expected type '%T', but received type '%T'", zeroValue, platCfg.Config)
return nil
}
return &PlatformConfig[T, S]{
Config: *platCfgCfg,
StreamProfiles: GetStreamProfiles[S](platCfg.StreamProfiles),

View File

@@ -4,6 +4,8 @@ import (
"context"
_ "embed"
"fmt"
"os"
"path"
"runtime/debug"
"sort"
"strings"
@@ -33,10 +35,15 @@ type Profile struct {
}
type Panel struct {
saveConfigLock sync.Mutex
startStopMutex sync.Mutex
updateTimerHandler *updateTimerHandler
panelConfig config
streamConfig streamcontrol.Config
streamConfig streamConfig
streamControllers struct {
Twitch *twitch.Twitch
YouTube *youtube.YouTube
}
profilesOrder []streamcontrol.ProfileName
profilesOrderFiltered []streamcontrol.ProfileName
selectedProfileName *streamcontrol.ProfileName
@@ -68,6 +75,14 @@ func (p *Panel) Loop(ctx context.Context) error {
p.defaultContext = ctx
logger.Debug(ctx, "config", p.panelConfig)
if err := p.initStreamControllers(); err != nil {
return fmt.Errorf("unable to initialize stream controllers: %w", err)
}
if err := p.loadConfig(); err != nil {
return fmt.Errorf("unable to load the config '%s': %w", p.configPath, err)
}
a := fyneapp.New()
p.initMainWindow(ctx, a)
@@ -75,32 +90,82 @@ func (p *Panel) Loop(ctx context.Context) error {
return nil
}
func expandPath(rawPath string) (string, error) {
switch {
case strings.HasPrefix(rawPath, "~/"):
homeDir, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("unable to get user home dir: %w", err)
}
return path.Join(homeDir, rawPath[2:]), nil
}
return rawPath, nil
}
func (p *Panel) getExpandedConfigPath() (string, error) {
return expandPath(p.configPath)
}
func (p *Panel) loadConfig() error {
return readConfigFromPath(p.defaultContext, p.configPath, &p.streamConfig)
}
func (p *Panel) savePlatformConfig(
platID streamcontrol.PlatformName,
platCfg *streamcontrol.AbstractPlatformConfig,
) error {
p.saveConfigLock.Lock()
defer p.saveConfigLock.Unlock()
p.streamConfig.ControllersConfig[platID] = platCfg
return p.saveConfig()
}
func (p *Panel) initStreamControllers() error {
for platName, cfg := range p.streamConfig.ControllersConfig {
var err error
switch strings.ToLower(string(platName)) {
case strings.ToLower(string(twitch.ID)):
p.streamControllers.Twitch, err = newTwitch(p.defaultContext, cfg, func(cfg *streamcontrol.AbstractPlatformConfig) error {
return p.savePlatformConfig(twitch.ID, cfg)
})
case strings.ToLower(string(youtube.ID)):
p.streamControllers.YouTube, err = newYouTube(p.defaultContext, cfg, func(cfg *streamcontrol.AbstractPlatformConfig) error {
return p.savePlatformConfig(youtube.ID, cfg)
})
}
if err != nil {
return fmt.Errorf("unable to initialize '%s': %w", platName, err)
}
}
return nil
}
func (p *Panel) onProfileCreatedOrUpdated(profile Profile) {
logger.Trace(p.defaultContext, "onProfileCreatedOrUpdated(%s)", profile.Name)
for platformName, platformProfile := range profile.PerPlatform {
p.streamConfig[platformName].StreamProfiles[profile.Name] = platformProfile
p.streamConfig.ControllersConfig[platformName].StreamProfiles[profile.Name] = platformProfile
}
p.rearrangeProfiles()
p.saveConfig()
}
func (p *Panel) onProfileDeleted(profileName streamcontrol.ProfileName) {
for platformName := range p.streamConfig {
delete(p.streamConfig[platformName].StreamProfiles, profileName)
for platformName := range p.streamConfig.ControllersConfig {
delete(p.streamConfig.ControllersConfig[platformName].StreamProfiles, profileName)
}
p.rearrangeProfiles()
p.saveConfig()
}
func (p *Panel) saveConfig() {
panic("not implemented")
func (p *Panel) saveConfig() error {
return writeConfigToPath(p.defaultContext, p.configPath, p.streamConfig)
}
func (p *Panel) getProfile(profileName streamcontrol.ProfileName) Profile {
prof := Profile{
Name: profileName,
}
for platName, platCfg := range p.streamConfig {
for platName, platCfg := range p.streamConfig.ControllersConfig {
platProf, ok := platCfg.GetStreamProfile(profileName)
if !ok {
continue
@@ -111,8 +176,8 @@ func (p *Panel) getProfile(profileName streamcontrol.ProfileName) Profile {
}
func (p *Panel) rearrangeProfiles() {
var curProfilesMap map[streamcontrol.ProfileName]*Profile
for platName, platCfg := range p.streamConfig {
curProfilesMap := map[streamcontrol.ProfileName]*Profile{}
for platName, platCfg := range p.streamConfig.ControllersConfig {
for profName, platProf := range platCfg.StreamProfiles {
prof := curProfilesMap[profName]
if prof == nil {
@@ -165,7 +230,7 @@ func (p *Panel) refilterProfiles() {
for _, profileName := range p.profilesOrder {
titleMatch := strings.Contains(strings.ToLower(string(profileName)), filterValue)
subValueMatch := false
for _, platCfg := range p.streamConfig {
for _, platCfg := range p.streamConfig.ControllersConfig {
prof, ok := platCfg.GetStreamProfile(profileName)
if !ok {
continue
@@ -230,7 +295,7 @@ func (p *Panel) profilesListItemUpdate(
profileName := streamcontrol.ProfileName(p.profilesOrderFiltered[itemID])
profile := p.getProfile(profileName)
w.SetText(fmt.Sprintf("%s", profile.Name))
w.SetText(string(profile.Name))
}
func ptrCopy[T any](v T) *T {
@@ -330,13 +395,14 @@ func (p *Panel) onStartStopButton() {
p.startStopButton.Refresh()
}
func (p *Panel) newProfileWindow(ctx context.Context, a fyne.App) fyne.Window {
func (p *Panel) newProfileWindow(_ context.Context, a fyne.App) fyne.Window {
w := a.NewWindow("Create a profile")
w.Resize(fyne.NewSize(400, 300))
activityTitle := widget.NewEntry()
activityTitle.SetPlaceHolder("title")
activityDescription := widget.NewMultiLineEntry()
activityDescription.SetPlaceHolder("description")
twitchCategory := widget.NewSelectEntry()
tagsEntryField := widget.NewEntry()
tagsEntryField.SetPlaceHolder("add a tag")
s := tagsEntryField.Size()

View File

@@ -0,0 +1,86 @@
package streampanel
import (
"context"
"fmt"
"os"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/goccy/go-yaml"
"github.com/spf13/cobra"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
)
type streamConfig struct {
ControllersConfig streamcontrol.Config
}
func newStreamConfig() streamConfig {
cfg := streamcontrol.Config{}
twitch.InitConfig(cfg)
youtube.InitConfig(cfg)
return streamConfig{
ControllersConfig: cfg,
}
}
func newSampleStreamConfig(cmd *cobra.Command, args []string) streamConfig {
cfg := newStreamConfig()
cfg.ControllersConfig[twitch.ID].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{"some_profile": twitch.StreamProfile{}}
cfg.ControllersConfig[youtube.ID].StreamProfiles = map[streamcontrol.ProfileName]streamcontrol.AbstractStreamProfile{"some_profile": youtube.StreamProfile{}}
return cfg
}
func readConfigFromPath(
ctx context.Context,
cfgPath string,
cfg *streamConfig,
) error {
b, err := os.ReadFile(cfgPath)
if err != nil {
return fmt.Errorf("unable to read file '%s': %w", cfgPath, err)
}
err = yaml.Unmarshal(b, cfg)
if err != nil {
return fmt.Errorf("unable to unserialize config: %w: <%s>", err, b)
}
err = streamcontrol.ConvertStreamProfiles[twitch.StreamProfile](ctx, cfg.ControllersConfig[twitch.ID].StreamProfiles)
if err != nil {
return fmt.Errorf("unable to convert stream profiles of twitch: %w: <%s>", err, b)
}
logger.Debugf(ctx, "final stream profiles of twitch: %#+v", cfg.ControllersConfig[twitch.ID].StreamProfiles)
err = streamcontrol.ConvertStreamProfiles[youtube.StreamProfile](ctx, cfg.ControllersConfig[youtube.ID].StreamProfiles)
if err != nil {
return fmt.Errorf("unable to convert stream profiles of twitch: %w: <%s>", err, b)
}
logger.Debugf(ctx, "final stream profiles of youtube: %#+v", cfg.ControllersConfig[youtube.ID].StreamProfiles)
return nil
}
func writeConfigToPath(
ctx context.Context,
cfgPath string,
cfg streamConfig,
) error {
b, err := yaml.Marshal(cfg)
if err != nil {
return fmt.Errorf("unable to serialize config %#+v: %w", cfg, err)
}
pathNew := cfgPath + ".new"
err = os.WriteFile(pathNew, b, 0750)
if err != nil {
return fmt.Errorf("unable to write config to file '%s': %w", pathNew, err)
}
err = os.Rename(pathNew, cfgPath)
if err != nil {
return fmt.Errorf("cannot move '%s' to '%s': %w", pathNew, cfgPath, err)
}
logger.Infof(ctx, "wrote to '%s' config <%s>", cfgPath, b)
return nil
}

View File

@@ -0,0 +1,63 @@
package streampanel
import (
"context"
"fmt"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
)
func newTwitch(
ctx context.Context,
cfg *streamcontrol.AbstractPlatformConfig,
saveCfgFunc func(*streamcontrol.AbstractPlatformConfig) error,
) (
*twitch.Twitch,
error,
) {
platCfg := streamcontrol.ConvertPlatformConfig[twitch.PlatformSpecificConfig, twitch.StreamProfile](
ctx, cfg, twitch.ID,
)
if platCfg == nil {
return nil, fmt.Errorf("twitch config was not found")
}
logger.Debugf(ctx, "twitch config: %#+v", platCfg)
return twitch.New(ctx, *platCfg,
func(c twitch.Config) error {
return saveCfgFunc(&streamcontrol.AbstractPlatformConfig{
Config: c.Config,
StreamProfiles: streamcontrol.ToAbstractStreamProfiles(c.StreamProfiles),
})
},
)
}
func newYouTube(
ctx context.Context,
cfg *streamcontrol.AbstractPlatformConfig,
saveCfgFunc func(*streamcontrol.AbstractPlatformConfig) error,
) (
*youtube.YouTube,
error,
) {
platCfg := streamcontrol.ConvertPlatformConfig[youtube.PlatformSpecificConfig, youtube.StreamProfile](
ctx, cfg, twitch.ID,
)
if platCfg == nil {
return nil, fmt.Errorf("youtube config was not found")
}
logger.Debugf(ctx, "youtube config: %#+v", platCfg)
return youtube.New(ctx, *platCfg,
func(c youtube.Config) error {
return saveCfgFunc(&streamcontrol.AbstractPlatformConfig{
Config: c.Config,
StreamProfiles: streamcontrol.ToAbstractStreamProfiles(c.StreamProfiles),
})
},
)
}