mirror of
https://github.com/xaionaro-go/streamctl.git
synced 2025-10-12 19:00:36 +08:00
Initial commit, pt. 10
This commit is contained in:
@@ -6,47 +6,36 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OAuth2Handler struct {
|
type OAuthHandlerArgument struct {
|
||||||
authURL string
|
AuthURL string
|
||||||
exchangeFn func(code string) error
|
RedirectURL string
|
||||||
receiverAddr string
|
ExchangeFn func(code string) error
|
||||||
}
|
|
||||||
|
|
||||||
func NewOAuth2Handler(
|
|
||||||
authURL string,
|
|
||||||
exchangeFn func(code string) error,
|
|
||||||
receiverAddr string,
|
|
||||||
) *OAuth2Handler {
|
|
||||||
return &OAuth2Handler{
|
|
||||||
authURL: authURL,
|
|
||||||
exchangeFn: exchangeFn,
|
|
||||||
receiverAddr: receiverAddr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// it is guaranteed exchangeFn was called if error is nil.
|
// it is guaranteed exchangeFn was called if error is nil.
|
||||||
func (h *OAuth2Handler) Handle(ctx context.Context) error {
|
func OAuth2Handler(ctx context.Context, arg OAuthHandlerArgument) error {
|
||||||
if h.receiverAddr != "" {
|
if arg.RedirectURL != "" {
|
||||||
err := h.handleViaBrowser()
|
err := OAuth2HandlerViaBrowser(ctx, arg)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
logger.Errorf(ctx, "unable to authenticate automatically: %v", err)
|
logger.Errorf(ctx, "unable to authenticate automatically: %v", err)
|
||||||
}
|
}
|
||||||
return h.handleViaCLI()
|
return OAuth2HandlerViaCLI(ctx, arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *OAuth2Handler) handleViaCLI() error {
|
func OAuth2HandlerViaCLI(ctx context.Context, arg OAuthHandlerArgument) error {
|
||||||
fmt.Printf(
|
fmt.Printf(
|
||||||
"It is required to get an oauth2 token. "+
|
"It is required to get an oauth2 token. "+
|
||||||
"Please open the link below in the browser:\n\n\t%s\n\n",
|
"Please open the link below in the browser:\n\n\t%s\n\n",
|
||||||
h.authURL,
|
arg.AuthURL,
|
||||||
)
|
)
|
||||||
|
|
||||||
fmt.Printf("Enter the code: ")
|
fmt.Printf("Enter the code: ")
|
||||||
@@ -54,30 +43,35 @@ func (h *OAuth2Handler) handleViaCLI() error {
|
|||||||
if _, err := fmt.Scan(&code); err != nil {
|
if _, err := fmt.Scan(&code); err != nil {
|
||||||
log.Fatalf("Unable to read authorization code %v", err)
|
log.Fatalf("Unable to read authorization code %v", err)
|
||||||
}
|
}
|
||||||
return h.exchangeFn(code)
|
return arg.ExchangeFn(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *OAuth2Handler) handleViaBrowser() error {
|
func OAuth2HandlerViaBrowser(ctx context.Context, arg OAuthHandlerArgument) error {
|
||||||
codeCh, err := h.newCodeReceiver()
|
codeCh, err := NewCodeReceiver(arg.RedirectURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = launchBrowser(h.authURL)
|
err = LaunchBrowser(arg.AuthURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Your browser has been launched (URL: %s).\nPlease approve the permissions.\n", h.authURL)
|
fmt.Printf("Your browser has been launched (URL: %s).\nPlease approve the permissions.\n", arg.AuthURL)
|
||||||
|
|
||||||
// Wait for the web server to get the code.
|
// Wait for the web server to get the code.
|
||||||
code := <-codeCh
|
code := <-codeCh
|
||||||
return h.exchangeFn(code)
|
return arg.ExchangeFn(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *OAuth2Handler) newCodeReceiver() (codeCh chan string, err error) {
|
func NewCodeReceiver(redirectURL string) (codeCh chan string, err error) {
|
||||||
|
urlParsed, err := url.Parse(redirectURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse URL '%s': %w", redirectURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
// this function was mostly borrowed from https://developers.google.com/youtube/v3/code_samples/go#authorize_a_request
|
// this function was mostly borrowed from https://developers.google.com/youtube/v3/code_samples/go#authorize_a_request
|
||||||
listener, err := net.Listen("tcp", h.receiverAddr)
|
listener, err := net.Listen("tcp", urlParsed.Host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -94,7 +88,7 @@ func (h *OAuth2Handler) newCodeReceiver() (codeCh chan string, err error) {
|
|||||||
return codeCh, nil
|
return codeCh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func launchBrowser(url string) error {
|
func LaunchBrowser(url string) error {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return exec.Command("open", url).Start()
|
return exec.Command("open", url).Start()
|
||||||
|
@@ -200,6 +200,8 @@ func GetPlatformSpecificConfig[T any](
|
|||||||
switch platCfgCfg := platCfgCfg.(type) {
|
switch platCfgCfg := platCfgCfg.(type) {
|
||||||
case T:
|
case T:
|
||||||
return platCfgCfg
|
return platCfgCfg
|
||||||
|
case *T:
|
||||||
|
return *platCfgCfg
|
||||||
case RawMessage:
|
case RawMessage:
|
||||||
var v T
|
var v T
|
||||||
err := yaml.Unmarshal(platCfgCfg, &v)
|
err := yaml.Unmarshal(platCfgCfg, &v)
|
||||||
|
@@ -1,20 +1,26 @@
|
|||||||
package twitch
|
package twitch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/oauthhandler"
|
||||||
streamctl "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
streamctl "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ID = streamctl.PlatformName("twitch")
|
const ID = streamctl.PlatformName("twitch")
|
||||||
|
|
||||||
|
type OAuthHandler func(context.Context, oauthhandler.OAuthHandlerArgument) error
|
||||||
|
|
||||||
type PlatformSpecificConfig struct {
|
type PlatformSpecificConfig struct {
|
||||||
Channel string
|
Channel string
|
||||||
ClientID string
|
ClientID string
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
ClientCode string
|
ClientCode string
|
||||||
AuthType string
|
AuthType string
|
||||||
AppAccessToken string
|
AppAccessToken string
|
||||||
UserAccessToken string
|
UserAccessToken string
|
||||||
RefreshToken string
|
RefreshToken string
|
||||||
|
CustomOAuthHandler OAuthHandler `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config = streamctl.PlatformConfig[PlatformSpecificConfig, StreamProfile]
|
type Config = streamctl.PlatformConfig[PlatformSpecificConfig, StreamProfile]
|
||||||
|
@@ -208,11 +208,12 @@ func getClient(
|
|||||||
cfg Config,
|
cfg Config,
|
||||||
safeCfgFn func(Config) error,
|
safeCfgFn func(Config) error,
|
||||||
) (*helix.Client, error) {
|
) (*helix.Client, error) {
|
||||||
client, err := helix.NewClient(&helix.Options{
|
options := &helix.Options{
|
||||||
ClientID: cfg.Config.ClientID,
|
ClientID: cfg.Config.ClientID,
|
||||||
ClientSecret: cfg.Config.ClientSecret,
|
ClientSecret: cfg.Config.ClientSecret,
|
||||||
RedirectURI: "http://localhost/",
|
RedirectURI: "http://localhost:8091/",
|
||||||
})
|
}
|
||||||
|
client, err := helix.NewClient(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to create a helix client object: %w", err)
|
return nil, fmt.Errorf("unable to create a helix client object: %w", err)
|
||||||
}
|
}
|
||||||
@@ -236,13 +237,23 @@ func getClient(
|
|||||||
Scopes: []string{"channel:manage:broadcast"},
|
Scopes: []string{"channel:manage:broadcast"},
|
||||||
})
|
})
|
||||||
|
|
||||||
oauthHandler := oauthhandler.NewOAuth2Handler(authURL, func(code string) error {
|
arg := oauthhandler.OAuthHandlerArgument{
|
||||||
cfg.Config.ClientCode = code
|
AuthURL: authURL,
|
||||||
err = safeCfgFn(cfg)
|
RedirectURL: options.RedirectURI,
|
||||||
errmon.ObserveErrorCtx(ctx, err)
|
ExchangeFn: func(code string) error {
|
||||||
return nil
|
cfg.Config.ClientCode = code
|
||||||
}, "")
|
err = safeCfgFn(cfg)
|
||||||
err := oauthHandler.Handle(ctx)
|
errmon.ObserveErrorCtx(ctx, err)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
oauthHandler := cfg.Config.CustomOAuthHandler
|
||||||
|
if oauthHandler == nil {
|
||||||
|
oauthHandler = oauthhandler.OAuth2HandlerViaCLI
|
||||||
|
}
|
||||||
|
|
||||||
|
err := oauthHandler(ctx, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to get or exchange the oauth code to a token: %w", err)
|
return nil, fmt.Errorf("unable to get or exchange the oauth code to a token: %w", err)
|
||||||
}
|
}
|
||||||
|
@@ -1,16 +1,22 @@
|
|||||||
package youtube
|
package youtube
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/oauthhandler"
|
||||||
streamctl "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
streamctl "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ID = streamctl.PlatformName("youtube")
|
const ID = streamctl.PlatformName("youtube")
|
||||||
|
|
||||||
|
type OAuthHandler func(context.Context, oauthhandler.OAuthHandlerArgument) error
|
||||||
|
|
||||||
type PlatformSpecificConfig struct {
|
type PlatformSpecificConfig struct {
|
||||||
ClientID string
|
ClientID string
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
Token *oauth2.Token
|
Token *oauth2.Token
|
||||||
|
CustomOAuthHandler OAuthHandler `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config = streamctl.PlatformConfig[PlatformSpecificConfig, StreamProfile]
|
type Config = streamctl.PlatformConfig[PlatformSpecificConfig, StreamProfile]
|
||||||
|
@@ -122,18 +122,26 @@ func getAuthCfg(cfg Config) *oauth2.Config {
|
|||||||
|
|
||||||
func getToken(ctx context.Context, cfg Config) (*oauth2.Token, error) {
|
func getToken(ctx context.Context, cfg Config) (*oauth2.Token, error) {
|
||||||
googleAuthCfg := getAuthCfg(cfg)
|
googleAuthCfg := getAuthCfg(cfg)
|
||||||
authURL := googleAuthCfg.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
|
|
||||||
|
|
||||||
var tok *oauth2.Token
|
var tok *oauth2.Token
|
||||||
oauthHandler := oauthhandler.NewOAuth2Handler(authURL, func(code string) error {
|
oauthHandlerArg := oauthhandler.OAuthHandlerArgument{
|
||||||
_tok, err := googleAuthCfg.Exchange(ctx, code)
|
AuthURL: googleAuthCfg.AuthCodeURL("state-token", oauth2.AccessTypeOffline),
|
||||||
if err != nil {
|
RedirectURL: googleAuthCfg.RedirectURL,
|
||||||
return fmt.Errorf("unable to get a token: %w", err)
|
ExchangeFn: func(code string) error {
|
||||||
}
|
_tok, err := googleAuthCfg.Exchange(ctx, code)
|
||||||
tok = _tok
|
if err != nil {
|
||||||
return nil
|
return fmt.Errorf("unable to get a token: %w", err)
|
||||||
}, "")
|
}
|
||||||
err := oauthHandler.Handle(ctx)
|
tok = _tok
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
oauthHandler := cfg.Config.CustomOAuthHandler
|
||||||
|
if oauthHandler == nil {
|
||||||
|
oauthHandler = oauthhandler.OAuth2HandlerViaCLI
|
||||||
|
}
|
||||||
|
err := oauthHandler(ctx, oauthHandlerArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,9 @@ import (
|
|||||||
_ "embed"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -20,6 +22,7 @@ import (
|
|||||||
"github.com/facebookincubator/go-belt/tool/experimental/errmon"
|
"github.com/facebookincubator/go-belt/tool/experimental/errmon"
|
||||||
"github.com/facebookincubator/go-belt/tool/logger"
|
"github.com/facebookincubator/go-belt/tool/logger"
|
||||||
"github.com/go-ng/xmath"
|
"github.com/go-ng/xmath"
|
||||||
|
"github.com/xaionaro-go/streamctl/pkg/oauthhandler"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch"
|
||||||
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
|
||||||
@@ -35,6 +38,7 @@ type Panel struct {
|
|||||||
dataLock sync.Mutex
|
dataLock sync.Mutex
|
||||||
data panelData
|
data panelData
|
||||||
|
|
||||||
|
app fyne.App
|
||||||
config config
|
config config
|
||||||
startStopMutex sync.Mutex
|
startStopMutex sync.Mutex
|
||||||
updateTimerHandler *updateTimerHandler
|
updateTimerHandler *updateTimerHandler
|
||||||
@@ -77,23 +81,24 @@ func (p *Panel) Loop(ctx context.Context) error {
|
|||||||
return fmt.Errorf("unable to load the data '%s': %w", p.dataPath, err)
|
return fmt.Errorf("unable to load the data '%s': %w", p.dataPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.app = fyneapp.New()
|
||||||
|
|
||||||
if err := p.initStreamControllers(); err != nil {
|
if err := p.initStreamControllers(); err != nil {
|
||||||
return fmt.Errorf("unable to initialize stream controllers: %w", err)
|
return fmt.Errorf("unable to initialize stream controllers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a := fyneapp.New()
|
p.initTwitchData()
|
||||||
p.initTwitchData(a)
|
|
||||||
p.normalizeTwitchData()
|
p.normalizeTwitchData()
|
||||||
p.initYoutubeData(a)
|
p.initYoutubeData()
|
||||||
p.normalizeYoutubeData()
|
p.normalizeYoutubeData()
|
||||||
|
|
||||||
p.initMainWindow(ctx, a)
|
p.initMainWindow(ctx)
|
||||||
|
|
||||||
p.mainWindow.ShowAndRun()
|
p.mainWindow.ShowAndRun()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Panel) initTwitchData(a fyne.App) {
|
func (p *Panel) initTwitchData() {
|
||||||
logger.FromCtx(p.defaultContext).Debugf("initializing Twitch data")
|
logger.FromCtx(p.defaultContext).Debugf("initializing Twitch data")
|
||||||
defer logger.FromCtx(p.defaultContext).Debugf("endof initializing Twitch data")
|
defer logger.FromCtx(p.defaultContext).Debugf("endof initializing Twitch data")
|
||||||
|
|
||||||
@@ -110,7 +115,7 @@ func (p *Panel) initTwitchData(a fyne.App) {
|
|||||||
|
|
||||||
allCategories, err := twitch.GetAllCategories(p.defaultContext)
|
allCategories, err := twitch.GetAllCategories(p.defaultContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.displayError(a, err)
|
p.displayError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +138,7 @@ func (p *Panel) normalizeTwitchData() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Panel) initYoutubeData(a fyne.App) {
|
func (p *Panel) initYoutubeData() {
|
||||||
logger.FromCtx(p.defaultContext).Debugf("initializing Youtube data")
|
logger.FromCtx(p.defaultContext).Debugf("initializing Youtube data")
|
||||||
defer logger.FromCtx(p.defaultContext).Debugf("endof initializing Youtube data")
|
defer logger.FromCtx(p.defaultContext).Debugf("endof initializing Youtube data")
|
||||||
|
|
||||||
@@ -150,7 +155,7 @@ func (p *Panel) initYoutubeData(a fyne.App) {
|
|||||||
|
|
||||||
broadcasts, err := youtube.ListBroadcasts(p.defaultContext)
|
broadcasts, err := youtube.ListBroadcasts(p.defaultContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.displayError(a, err)
|
p.displayError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,6 +228,77 @@ func (p *Panel) savePlatformConfig(
|
|||||||
return p.saveData()
|
return p.saveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Panel) oauthHandlerTwitch(ctx context.Context, arg oauthhandler.OAuthHandlerArgument) error {
|
||||||
|
logger.Infof(ctx, "oauthHandlerTwitch: %#+v", arg)
|
||||||
|
defer logger.Infof(ctx, "/oauthHandlerTwitch")
|
||||||
|
return p.oauthHandler(ctx, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Panel) oauthHandlerYouTube(ctx context.Context, arg oauthhandler.OAuthHandlerArgument) error {
|
||||||
|
logger.Infof(ctx, "oauthHandlerYouTube: %#+v", arg)
|
||||||
|
defer logger.Infof(ctx, "/oauthHandlerYouTube")
|
||||||
|
return p.oauthHandler(ctx, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Panel) oauthHandler(ctx context.Context, arg oauthhandler.OAuthHandlerArgument) error {
|
||||||
|
codeCh, err := oauthhandler.NewCodeReceiver(arg.RedirectURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.openBrowser(arg.AuthURL); err != nil {
|
||||||
|
return fmt.Errorf("unable to open browser with URL '%s': %w", arg.AuthURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Infof(ctx, "Your browser has been launched (URL: %s).\nPlease approve the permissions.\n", arg.AuthURL)
|
||||||
|
|
||||||
|
// Wait for the web server to get the code.
|
||||||
|
code := <-codeCh
|
||||||
|
logger.Debugf(ctx, "received the auth code")
|
||||||
|
return arg.ExchangeFn(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Panel) openBrowser(authURL string) error {
|
||||||
|
var browserCmd string
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
browserCmd = "open"
|
||||||
|
case "linux":
|
||||||
|
browserCmd = "xdg-open"
|
||||||
|
default:
|
||||||
|
return oauthhandler.LaunchBrowser(authURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitCh := make(chan struct{})
|
||||||
|
|
||||||
|
w := p.app.NewWindow("Browser selection window")
|
||||||
|
browserField := widget.NewEntry()
|
||||||
|
browserField.PlaceHolder = "command to execute the browser"
|
||||||
|
browserField.OnSubmitted = func(s string) {
|
||||||
|
close(waitCh)
|
||||||
|
}
|
||||||
|
okButton := widget.NewButton("OK", func() {
|
||||||
|
close(waitCh)
|
||||||
|
})
|
||||||
|
w.SetContent(container.NewBorder(
|
||||||
|
browserField,
|
||||||
|
okButton,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
))
|
||||||
|
|
||||||
|
go w.ShowAndRun()
|
||||||
|
<-waitCh
|
||||||
|
w.Close()
|
||||||
|
|
||||||
|
if browserField.Text != "" {
|
||||||
|
browserCmd = browserField.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
return exec.Command(browserCmd, authURL).Start()
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Panel) initStreamControllers() error {
|
func (p *Panel) initStreamControllers() error {
|
||||||
for platName, cfg := range p.data.Backends {
|
for platName, cfg := range p.data.Backends {
|
||||||
var err error
|
var err error
|
||||||
@@ -230,11 +306,11 @@ func (p *Panel) initStreamControllers() error {
|
|||||||
case strings.ToLower(string(twitch.ID)):
|
case strings.ToLower(string(twitch.ID)):
|
||||||
p.streamControllers.Twitch, err = newTwitch(p.defaultContext, cfg, func(cfg *streamcontrol.AbstractPlatformConfig) error {
|
p.streamControllers.Twitch, err = newTwitch(p.defaultContext, cfg, func(cfg *streamcontrol.AbstractPlatformConfig) error {
|
||||||
return p.savePlatformConfig(twitch.ID, cfg)
|
return p.savePlatformConfig(twitch.ID, cfg)
|
||||||
})
|
}, p.oauthHandlerTwitch)
|
||||||
case strings.ToLower(string(youtube.ID)):
|
case strings.ToLower(string(youtube.ID)):
|
||||||
p.streamControllers.YouTube, err = newYouTube(p.defaultContext, cfg, func(cfg *streamcontrol.AbstractPlatformConfig) error {
|
p.streamControllers.YouTube, err = newYouTube(p.defaultContext, cfg, func(cfg *streamcontrol.AbstractPlatformConfig) error {
|
||||||
return p.savePlatformConfig(youtube.ID, cfg)
|
return p.savePlatformConfig(youtube.ID, cfg)
|
||||||
})
|
}, p.oauthHandlerYouTube)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to initialize '%s': %w", platName, err)
|
return fmt.Errorf("unable to initialize '%s': %w", platName, err)
|
||||||
@@ -432,8 +508,8 @@ func (p *Panel) setFilter(filter string) {
|
|||||||
p.refilterProfiles()
|
p.refilterProfiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Panel) initMainWindow(ctx context.Context, a fyne.App) {
|
func (p *Panel) initMainWindow(ctx context.Context) {
|
||||||
w := a.NewWindow("TimeTracker")
|
w := p.app.NewWindow("StreamPanel")
|
||||||
|
|
||||||
p.startStopButton = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), p.onStartStopButton)
|
p.startStopButton = widget.NewButtonWithIcon("", theme.MediaPlayIcon(), p.onStartStopButton)
|
||||||
p.startStopButton.Importance = widget.SuccessImportance
|
p.startStopButton.Importance = widget.SuccessImportance
|
||||||
@@ -449,7 +525,7 @@ func (p *Panel) initMainWindow(ctx context.Context, a fyne.App) {
|
|||||||
topPanel := container.NewVBox(
|
topPanel := container.NewVBox(
|
||||||
container.NewHBox(
|
container.NewHBox(
|
||||||
widget.NewButtonWithIcon("Profile", theme.ContentAddIcon(), func() {
|
widget.NewButtonWithIcon("Profile", theme.ContentAddIcon(), func() {
|
||||||
p.newProfileWindow(ctx, a)
|
p.newProfileWindow(ctx)
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
profileFilter,
|
profileFilter,
|
||||||
@@ -512,13 +588,13 @@ func cleanYoutubeRecordingName(in string) string {
|
|||||||
return strings.ToLower(strings.Trim(in, " "))
|
return strings.ToLower(strings.Trim(in, " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Panel) newProfileWindow(_ context.Context, a fyne.App) fyne.Window {
|
func (p *Panel) newProfileWindow(_ context.Context) fyne.Window {
|
||||||
var (
|
var (
|
||||||
twitchProfile *twitch.StreamProfile
|
twitchProfile *twitch.StreamProfile
|
||||||
youtubeProfile *youtube.StreamProfile
|
youtubeProfile *youtube.StreamProfile
|
||||||
)
|
)
|
||||||
|
|
||||||
w := a.NewWindow("Create a profile")
|
w := p.app.NewWindow("Create a profile")
|
||||||
w.Resize(fyne.NewSize(400, 300))
|
w.Resize(fyne.NewSize(400, 300))
|
||||||
activityTitle := widget.NewEntry()
|
activityTitle := widget.NewEntry()
|
||||||
activityTitle.SetPlaceHolder("title")
|
activityTitle.SetPlaceHolder("title")
|
||||||
@@ -682,7 +758,7 @@ func (p *Panel) newProfileWindow(_ context.Context, a fyne.App) fyne.Window {
|
|||||||
}
|
}
|
||||||
err := fmt.Errorf("creating a profile is not implemented")
|
err := fmt.Errorf("creating a profile is not implemented")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.displayError(a, err)
|
p.displayError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Close()
|
w.Close()
|
||||||
@@ -704,8 +780,8 @@ func (p *Panel) newProfileWindow(_ context.Context, a fyne.App) fyne.Window {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Panel) displayError(a fyne.App, err error) {
|
func (p *Panel) displayError(err error) {
|
||||||
w := a.NewWindow("Got an error: " + err.Error())
|
w := p.app.NewWindow("Got an error: " + err.Error())
|
||||||
errorMessage := fmt.Sprintf("Error: %v\n\nstack trace:\n%s", err, debug.Stack())
|
errorMessage := fmt.Sprintf("Error: %v\n\nstack trace:\n%s", err, debug.Stack())
|
||||||
w.Resize(fyne.NewSize(400, 300))
|
w.Resize(fyne.NewSize(400, 300))
|
||||||
w.SetContent(widget.NewLabelWithStyle(errorMessage, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}))
|
w.SetContent(widget.NewLabelWithStyle(errorMessage, fyne.TextAlignLeading, fyne.TextStyle{Bold: true}))
|
||||||
|
@@ -14,6 +14,7 @@ func newTwitch(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cfg *streamcontrol.AbstractPlatformConfig,
|
cfg *streamcontrol.AbstractPlatformConfig,
|
||||||
saveCfgFunc func(*streamcontrol.AbstractPlatformConfig) error,
|
saveCfgFunc func(*streamcontrol.AbstractPlatformConfig) error,
|
||||||
|
customOAuthHandler twitch.OAuthHandler,
|
||||||
) (
|
) (
|
||||||
*twitch.Twitch,
|
*twitch.Twitch,
|
||||||
error,
|
error,
|
||||||
@@ -26,6 +27,7 @@ func newTwitch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Debugf(ctx, "twitch config: %#+v", platCfg)
|
logger.Debugf(ctx, "twitch config: %#+v", platCfg)
|
||||||
|
platCfg.Config.CustomOAuthHandler = customOAuthHandler
|
||||||
return twitch.New(ctx, *platCfg,
|
return twitch.New(ctx, *platCfg,
|
||||||
func(c twitch.Config) error {
|
func(c twitch.Config) error {
|
||||||
return saveCfgFunc(&streamcontrol.AbstractPlatformConfig{
|
return saveCfgFunc(&streamcontrol.AbstractPlatformConfig{
|
||||||
@@ -40,6 +42,7 @@ func newYouTube(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cfg *streamcontrol.AbstractPlatformConfig,
|
cfg *streamcontrol.AbstractPlatformConfig,
|
||||||
saveCfgFunc func(*streamcontrol.AbstractPlatformConfig) error,
|
saveCfgFunc func(*streamcontrol.AbstractPlatformConfig) error,
|
||||||
|
customOAuthHandler youtube.OAuthHandler,
|
||||||
) (
|
) (
|
||||||
*youtube.YouTube,
|
*youtube.YouTube,
|
||||||
error,
|
error,
|
||||||
@@ -52,6 +55,7 @@ func newYouTube(
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Debugf(ctx, "youtube config: %#+v", platCfg)
|
logger.Debugf(ctx, "youtube config: %#+v", platCfg)
|
||||||
|
platCfg.Config.CustomOAuthHandler = customOAuthHandler
|
||||||
return youtube.New(ctx, *platCfg,
|
return youtube.New(ctx, *platCfg,
|
||||||
func(c youtube.Config) error {
|
func(c youtube.Config) error {
|
||||||
return saveCfgFunc(&streamcontrol.AbstractPlatformConfig{
|
return saveCfgFunc(&streamcontrol.AbstractPlatformConfig{
|
||||||
|
Reference in New Issue
Block a user