mirror of
https://github.com/xaionaro-go/streamctl.git
synced 2025-09-26 19:41:17 +08:00
Few fixes as result of a test in the field
This commit is contained in:
@@ -5,4 +5,4 @@ Website = "https://github.com/xaionaro/streamctl"
|
||||
Name = "streampanel"
|
||||
ID = "center.dx.streampanel"
|
||||
Version = "0.1.0"
|
||||
Build = 368
|
||||
Build = 372
|
||||
|
@@ -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
|
||||
}
|
||||
|
12
go.mod
12
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
|
||||
|
24
go.sum
24
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=
|
||||
|
@@ -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))
|
||||
|
@@ -92,6 +92,7 @@ type Panel struct {
|
||||
dashboardLocker xsync.Mutex
|
||||
dashboardShowHideButton *widget.Button
|
||||
|
||||
localStatus *fyne.Container
|
||||
appStatus *widget.Label
|
||||
appStatusData struct {
|
||||
prevUpdateTS time.Time
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user