From 1b832f66efe607e1cb1d3dc94c74a6b84d243322 Mon Sep 17 00:00:00 2001 From: Dmitrii Okunev Date: Sat, 14 Jun 2025 22:04:15 +0100 Subject: [PATCH] Few fixes as result of a test in the field --- cmd/streampanel/FyneApp.toml | 2 +- cmd/streampanel/runtime_android.go | 4 ++ go.mod | 12 ++-- go.sum | 24 +++---- pkg/streampanel/dashboard.go | 104 ++++++++++++++++++++++++++++- pkg/streampanel/panel.go | 1 + pkg/streamplayer/stream_player.go | 4 +- 7 files changed, 129 insertions(+), 22 deletions(-) diff --git a/cmd/streampanel/FyneApp.toml b/cmd/streampanel/FyneApp.toml index b76dfd1..299e379 100755 --- a/cmd/streampanel/FyneApp.toml +++ b/cmd/streampanel/FyneApp.toml @@ -5,4 +5,4 @@ Website = "https://github.com/xaionaro/streamctl" Name = "streampanel" ID = "center.dx.streampanel" Version = "0.1.0" - Build = 368 + Build = 372 diff --git a/cmd/streampanel/runtime_android.go b/cmd/streampanel/runtime_android.go index 4ed1a38..3127fa3 100644 --- a/cmd/streampanel/runtime_android.go +++ b/cmd/streampanel/runtime_android.go @@ -1,9 +1,13 @@ package main import ( + "time" + "github.com/xaionaro-go/xpath" + "github.com/xaionaro-go/xsync" ) func init() { xpath.HomeDirOverride = "/data/user/0/center.dx.streampanel/files" + xsync.DefaultDeadlockTimeout = time.Hour } diff --git a/go.mod b/go.mod index d0c7ac3..01ec5e5 100755 --- a/go.mod +++ b/go.mod @@ -233,10 +233,10 @@ require ( golang.org/x/image v0.18.0 // indirect golang.org/x/mobile v0.0.0-20240404231514-09dbf07665ed // indirect golang.org/x/net v0.39.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.9.0 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a // indirect @@ -313,9 +313,9 @@ require ( github.com/xaionaro-go/xfyne v0.0.0-20241018233531-26123724a6c6 github.com/xaionaro-go/xlogrus v0.0.0-20250111150201-60557109545a github.com/xaionaro-go/xpath v0.0.0-20250111145115-55f5728f643f - github.com/xaionaro-go/xsync v0.0.0-20250420144932-1e27f4332d4d + github.com/xaionaro-go/xsync v0.0.0-20250614210231-b74f647f859f github.com/yutopp/go-flv v0.3.1 - golang.org/x/crypto v0.37.0 + golang.org/x/crypto v0.38.0 google.golang.org/grpc v1.71.1 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 2af0f73..6b58949 100755 --- a/go.sum +++ b/go.sum @@ -1184,8 +1184,8 @@ github.com/xaionaro-go/xlogrus v0.0.0-20250111150201-60557109545a h1:EoNRdOtBMnZ github.com/xaionaro-go/xlogrus v0.0.0-20250111150201-60557109545a/go.mod h1:RPfWNuwqJykkA2TEvisXqHgy1ypA/1H2HBdIRSeVJ9o= github.com/xaionaro-go/xpath v0.0.0-20250111145115-55f5728f643f h1:ofxY1akRlVdJ/AEDj0EakK4Aez8fzeWTTe2mCAZiJ0A= github.com/xaionaro-go/xpath v0.0.0-20250111145115-55f5728f643f/go.mod h1:f0DVcqddOy1RALOgXJHwpQnkp1u1yeBX/+A2+Bf4EGc= -github.com/xaionaro-go/xsync v0.0.0-20250420144932-1e27f4332d4d h1:paN6zI8tpStL7gEbt4m24vbOFdkNUwnDqXtT3B2dzSo= -github.com/xaionaro-go/xsync v0.0.0-20250420144932-1e27f4332d4d/go.mod h1:FCpywNAl4a4hgzE8j7Z+TpdhBQi5WHxnI35jOrFpoQw= +github.com/xaionaro-go/xsync v0.0.0-20250614210231-b74f647f859f h1:Yn/hgHYgZ05zaTo6cK+Fmx46ah0O8ey0JMsWQ4TsgnY= +github.com/xaionaro-go/xsync v0.0.0-20250614210231-b74f647f859f/go.mod h1:FCpywNAl4a4hgzE8j7Z+TpdhBQi5WHxnI35jOrFpoQw= github.com/xaionaro-go/zerolog2belt v0.0.0-20241103164018-a3bc1ea487e5 h1:jAy7VLg8y8XE1R8jBte4PRDJzOaAE+sUfmttfB9ZcAY= github.com/xaionaro-go/zerolog2belt v0.0.0-20241103164018-a3bc1ea487e5/go.mod h1:KJuX7yl27vU+KV6CpsWOe5ZMY4zSg70hvEhwoYdr17w= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= @@ -1284,8 +1284,8 @@ golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1451,8 +1451,8 @@ golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1565,8 +1565,8 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1579,8 +1579,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1598,8 +1598,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/streampanel/dashboard.go b/pkg/streampanel/dashboard.go index c2b0a58..0dbd00f 100644 --- a/pkg/streampanel/dashboard.go +++ b/pkg/streampanel/dashboard.go @@ -7,10 +7,12 @@ import ( "image/color" "image/draw" "math" + "os" "runtime" "slices" "sort" "strconv" + "strings" "sync" "sync/atomic" @@ -42,6 +44,7 @@ import ( "github.com/xaionaro-go/streamctl/pkg/streampanel/consts" "github.com/xaionaro-go/streamctl/pkg/ximage" xfyne "github.com/xaionaro-go/xfyne/widget" + "github.com/xaionaro-go/xpath" "github.com/xaionaro-go/xsync" ) @@ -50,6 +53,9 @@ const ( dashboardFullUpdatesInterval = 2 * time.Second ) +// TODO: DELETE ME: +var qualityFilePath = must(xpath.Expand(`~/quality`)) + func (p *Panel) focusDashboardWindow( ctx context.Context, ) { @@ -92,6 +98,82 @@ type imageInfo struct { Image *image.NRGBA } +func (w *dashboardWindow) renderLocalStatus(ctx context.Context) { + // TODO: remove the ugly hardcode above, and make it generic (support different use cases) + + b, err := os.ReadFile(qualityFilePath) + if err != nil { + logger.Debugf(ctx, "unable to open the 'quality' file: %v", err) + return + } + words := strings.Split(strings.Trim(string(b), "\n\r\t"), " ") + if len(words) != 3 { + logger.Debugf(ctx, "expected 3 words, but received %d", len(words)) + return + } + + aQ, err := strconv.ParseInt(words[0], 10, 64) + if err != nil { + logger.Debugf(ctx, "unable to parse aQ '%s': %v", words[0], err) + return + } + + pQ, err := strconv.ParseInt(words[1], 10, 64) + if err != nil { + logger.Debugf(ctx, "unable to parse pQ '%s': %v", words[0], err) + return + } + + wQ, err := strconv.ParseInt(words[2], 10, 64) + if err != nil { + logger.Debugf(ctx, "unable to parse wQ '%s': %v", words[0], err) + return + } + + qToImportance := func(in int64) widget.Importance { + switch { + case in <= -5: + return widget.DangerImportance + case in <= 0: + return widget.MediumImportance + default: + return widget.SuccessImportance + } + } + + qToStr := func(in int64) string { + if in <= -30 { + return "DEAD" + } + if in <= -5 { + return "BAD" + } + if in <= 0 { + return "SO-SO" + } + if in > 0 { + return "GOOD" + } + return "UNKNOWN" + } + + qToLabel := func(name string, q int64) *widget.Label { + l := widget.NewLabel(name + ":" + qToStr(q)) + l.Importance = qToImportance(q) + l.TextStyle.Bold = true + return l + } + + w.localStatus.Objects = []fyne.CanvasObject{ + container.NewHBox( + layout.NewSpacer(), + qToLabel("A", aQ), + qToLabel("P", pQ), + qToLabel("W", wQ), + ), + } +} + func (w *dashboardWindow) renderStreamStatus(ctx context.Context) { w.streamStatusLocker.Do(ctx, func() { streamDClient, ok := w.StreamD.(*client.Client) @@ -180,6 +262,7 @@ func (p *Panel) newDashboardWindow( bgFyne := canvas.NewImageFromImage(bg) bgFyne.FillMode = canvas.ImageFillStretch + p.localStatus = container.NewStack() p.appStatus = widget.NewLabel("") obsLabel := widget.NewLabel("OBS:") obsLabel.Importance = widget.HighImportance @@ -197,8 +280,13 @@ func (p *Panel) newDashboardWindow( if _, ok := p.StreamD.(*client.Client); ok { appLabel := widget.NewLabel("App:") appLabel.Importance = widget.HighImportance - streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), appLabel, p.appStatus)) + streamInfoItems.Add(container.NewHBox( + layout.NewSpacer(), + appLabel, + p.appStatus, + )) } + streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), p.localStatus)) streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), obsLabel, w.streamStatus[obs.ID])) streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), twLabel, w.streamStatus[twitch.ID])) streamInfoItems.Add(container.NewHBox(layout.NewSpacer(), kcLabel, w.streamStatus[kick.ID])) @@ -575,6 +663,20 @@ func (w *dashboardWindow) startUpdatingNoLock( ctx, cancelFunc := context.WithCancel(ctx) w.stopUpdatingFunc = cancelFunc + w.renderLocalStatus(ctx) + observability.Go(ctx, func() { + t := time.NewTicker(2 * time.Second) + for { + select { + case <-ctx.Done(): + return + case <-t.C: + } + + w.renderLocalStatus(ctx) + } + }) + cfg, err := w.GetStreamDConfig(ctx) if err != nil { w.DisplayError(fmt.Errorf("unable to get the current config: %w", err)) diff --git a/pkg/streampanel/panel.go b/pkg/streampanel/panel.go index 3901867..f581b70 100644 --- a/pkg/streampanel/panel.go +++ b/pkg/streampanel/panel.go @@ -92,6 +92,7 @@ type Panel struct { dashboardLocker xsync.Mutex dashboardShowHideButton *widget.Button + localStatus *fyne.Container appStatus *widget.Label appStatusData struct { prevUpdateTS time.Time diff --git a/pkg/streamplayer/stream_player.go b/pkg/streamplayer/stream_player.go index 30286fc..c734551 100644 --- a/pkg/streamplayer/stream_player.go +++ b/pkg/streamplayer/stream_player.go @@ -909,8 +909,8 @@ func (p *StreamPlayerHandler) controllerLoop( return } speed = float64(uint(speed*10)) / 10 // to avoid flickering (for example between 1.0001 and 1.0) - if speed < 0.5 { - speed = 0.5 + if speed < 0.8 { + speed = 0.8 } if speed == curSpeed { return