Move away packages audio, observability, secret, speech, subtitleswindow and xsync

This commit is contained in:
Dmitrii Okunev
2025-01-11 14:39:59 +00:00
parent 654d552fd1
commit 20d6995ddd
170 changed files with 195 additions and 4234 deletions

View File

@@ -20,9 +20,9 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/secret"
"github.com/xaionaro-go/streamctl/pkg/consts" "github.com/xaionaro-go/streamctl/pkg/consts"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/secret"
"github.com/xaionaro-go/streamctl/pkg/xpath" "github.com/xaionaro-go/streamctl/pkg/xpath"
) )

View File

@@ -5,9 +5,9 @@ import (
child_process_manager "github.com/AgustinSRG/go-child-process-manager" child_process_manager "github.com/AgustinSRG/go-child-process-manager"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/ffstream" "github.com/xaionaro-go/streamctl/pkg/ffstream"
"github.com/xaionaro-go/streamctl/pkg/ffstreamserver" "github.com/xaionaro-go/streamctl/pkg/ffstreamserver"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder"
_ "github.com/xaionaro-go/streamctl/pkg/streamserver" _ "github.com/xaionaro-go/streamctl/pkg/streamserver"
) )

View File

@@ -9,8 +9,8 @@ import (
"github.com/facebookincubator/go-belt" "github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/astiavlogger" "github.com/xaionaro-go/streamctl/pkg/astiavlogger"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder"
) )

View File

@@ -10,9 +10,9 @@ import (
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/ffstream/types" "github.com/xaionaro-go/streamctl/pkg/ffstream/types"
"github.com/xaionaro-go/streamctl/pkg/ffstreamserver/client" "github.com/xaionaro-go/streamctl/pkg/ffstreamserver/client"
"github.com/xaionaro-go/streamctl/pkg/observability"
) )
var ( var (

View File

@@ -14,7 +14,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol" "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
kick "github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick/types" kick "github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick/types"
obs "github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs/types" obs "github.com/xaionaro-go/streamctl/pkg/streamcontrol/obs/types"

View File

@@ -15,7 +15,7 @@ import (
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick" "github.com/xaionaro-go/streamctl/pkg/streamcontrol/kick"
"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"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
var ( var (

View File

@@ -22,8 +22,8 @@ import (
"github.com/xaionaro-go/grpcproxy/grpcproxyserver" "github.com/xaionaro-go/grpcproxy/grpcproxyserver"
"github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc" "github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc"
"github.com/xaionaro-go/obs-grpc-proxy/protobuf/go/obs_grpc" "github.com/xaionaro-go/obs-grpc-proxy/protobuf/go/obs_grpc"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/cmd/streamd/ui" "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"
"github.com/xaionaro-go/streamctl/pkg/streamd" "github.com/xaionaro-go/streamctl/pkg/streamd"
"github.com/xaionaro-go/streamctl/pkg/streamd/config" "github.com/xaionaro-go/streamctl/pkg/streamd/config"

View File

@@ -14,7 +14,7 @@ import (
twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types" twitch "github.com/xaionaro-go/streamctl/pkg/streamcontrol/twitch/types"
youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types" youtube "github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube/types"
"github.com/xaionaro-go/streamctl/pkg/streamd/ui" "github.com/xaionaro-go/streamctl/pkg/streamd/ui"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type UI struct { type UI struct {

View File

@@ -20,9 +20,9 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/secret"
"github.com/xaionaro-go/streamctl/pkg/consts" "github.com/xaionaro-go/streamctl/pkg/consts"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/secret"
"github.com/xaionaro-go/streamctl/pkg/xpath" "github.com/xaionaro-go/streamctl/pkg/xpath"
) )

View File

@@ -13,10 +13,10 @@ import (
child_process_manager "github.com/AgustinSRG/go-child-process-manager" child_process_manager "github.com/AgustinSRG/go-child-process-manager"
"github.com/facebookincubator/go-belt" "github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/logwriter" "github.com/xaionaro-go/streamctl/pkg/logwriter"
"github.com/xaionaro-go/streamctl/pkg/mainprocess" "github.com/xaionaro-go/streamctl/pkg/mainprocess"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/xsync"
"github.com/xaionaro-go/streamctl/pkg/xsync"
) )
type ProcessName = mainprocess.ProcessName type ProcessName = mainprocess.ProcessName

View File

@@ -12,10 +12,10 @@ import (
child_process_manager "github.com/AgustinSRG/go-child-process-manager" child_process_manager "github.com/AgustinSRG/go-child-process-manager"
"github.com/facebookincubator/go-belt" "github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/cmd/streampanel/autoupdater" "github.com/xaionaro-go/streamctl/cmd/streampanel/autoupdater"
"github.com/xaionaro-go/streamctl/pkg/buildvars" "github.com/xaionaro-go/streamctl/pkg/buildvars"
"github.com/xaionaro-go/streamctl/pkg/mainprocess" "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"
"github.com/xaionaro-go/streamctl/pkg/streamd/grpc/go/streamd_grpc" "github.com/xaionaro-go/streamctl/pkg/streamd/grpc/go/streamd_grpc"
"github.com/xaionaro-go/streamctl/pkg/streampanel" "github.com/xaionaro-go/streamctl/pkg/streampanel"

View File

@@ -13,7 +13,7 @@ import (
"github.com/facebookincubator/go-belt" "github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
) )
func initRuntime( func initRuntime(

View File

@@ -8,7 +8,7 @@ import (
"time" "time"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
) )
func mainProcessSignalHandler( func mainProcessSignalHandler(

View File

@@ -17,9 +17,9 @@ import (
"github.com/xaionaro-go/grpcproxy/grpcproxyserver" "github.com/xaionaro-go/grpcproxy/grpcproxyserver"
"github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc" "github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc"
"github.com/xaionaro-go/obs-grpc-proxy/protobuf/go/obs_grpc" "github.com/xaionaro-go/obs-grpc-proxy/protobuf/go/obs_grpc"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/cmd/streamd/ui" "github.com/xaionaro-go/streamctl/cmd/streamd/ui"
"github.com/xaionaro-go/streamctl/pkg/mainprocess" "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"
"github.com/xaionaro-go/streamctl/pkg/streamd" "github.com/xaionaro-go/streamctl/pkg/streamd"
"github.com/xaionaro-go/streamctl/pkg/streamd/api" "github.com/xaionaro-go/streamctl/pkg/streamd/api"
@@ -28,7 +28,7 @@ import (
"github.com/xaionaro-go/streamctl/pkg/streamd/server" "github.com/xaionaro-go/streamctl/pkg/streamd/server"
streampanelconfig "github.com/xaionaro-go/streamctl/pkg/streampanel/config" streampanelconfig "github.com/xaionaro-go/streamctl/pkg/streampanel/config"
"github.com/xaionaro-go/streamctl/pkg/xpath" "github.com/xaionaro-go/streamctl/pkg/xpath"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
"google.golang.org/grpc" "google.golang.org/grpc"
) )

View File

@@ -7,8 +7,8 @@ import (
"github.com/facebookincubator/go-belt" "github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/mainprocess" "github.com/xaionaro-go/streamctl/pkg/mainprocess"
"github.com/xaionaro-go/streamctl/pkg/observability"
) )
func forkUI(preCtx context.Context, mainProcessAddr, password string) { func forkUI(preCtx context.Context, mainProcessAddr, password string) {

35
go.mod
View File

@@ -28,17 +28,15 @@ require (
github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-multierror v1.1.1
github.com/nicklaw5/helix/v2 v2.30.1-0.20240715193454-0151ccccf980 github.com/nicklaw5/helix/v2 v2.30.1-0.20240715193454-0151ccccf980
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
github.com/xaionaro-go/logrustash v0.0.0-20240804141650-d48034780a5f github.com/xaionaro-go/logrustash v0.0.0-20240804141650-d48034780a5f // indirect
golang.org/x/oauth2 v0.23.0 golang.org/x/oauth2 v0.25.0
google.golang.org/api v0.203.0 google.golang.org/api v0.203.0
) )
require ( require (
cloud.google.com/go v0.116.0 // indirect
cloud.google.com/go/auth v0.9.9 // indirect cloud.google.com/go/auth v0.9.9 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // indirect cloud.google.com/go/compute/metadata v0.5.2 // indirect
cloud.google.com/go/longrunning v0.6.1 // indirect
code.cloudfoundry.org/bytefmt v0.0.0 // indirect code.cloudfoundry.org/bytefmt v0.0.0 // indirect
dario.cat/mergo v1.0.0 // indirect dario.cat/mergo v1.0.0 // indirect
fyne.io/systray v1.11.0 // indirect fyne.io/systray v1.11.0 // indirect
@@ -46,6 +44,7 @@ require (
github.com/Danny-Dasilva/CycleTLS/cycletls v0.0.0-20220620102923-c84d740b4757 // indirect github.com/Danny-Dasilva/CycleTLS/cycletls v0.0.0-20220620102923-c84d740b4757 // indirect
github.com/Danny-Dasilva/fhttp v0.0.0-20220524230104-f801520157d6 // indirect github.com/Danny-Dasilva/fhttp v0.0.0-20220524230104-f801520157d6 // indirect
github.com/Danny-Dasilva/utls v0.0.0-20220604023528-30cb107b834e // indirect github.com/Danny-Dasilva/utls v0.0.0-20220604023528-30cb107b834e // indirect
github.com/DataDog/gostackparse v0.7.0 // indirect
github.com/MicahParks/jwkset v0.5.20 // indirect github.com/MicahParks/jwkset v0.5.20 // indirect
github.com/MicahParks/keyfunc/v3 v3.3.5 // indirect github.com/MicahParks/keyfunc/v3 v3.3.5 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
@@ -70,6 +69,7 @@ require (
github.com/datarhei/gosrt v0.7.0 // indirect github.com/datarhei/gosrt v0.7.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dsnet/compress v0.0.1 // indirect github.com/dsnet/compress v0.0.1 // indirect
github.com/ebitengine/oto/v3 v3.3.2 // indirect
github.com/ebitengine/purego v0.8.0 // indirect github.com/ebitengine/purego v0.8.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.15.0 // indirect github.com/fatih/color v1.15.0 // indirect
@@ -124,6 +124,8 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect github.com/jeandeaual/go-locale v0.0.0-20240223122105-ce5225dcaa49 // indirect
github.com/jezek/xgb v1.1.0 // indirect github.com/jezek/xgb v1.1.0 // indirect
github.com/jfreymuth/oggvorbis v1.0.5 // indirect
github.com/jfreymuth/pulse v0.1.1 // indirect
github.com/jfreymuth/vorbis v1.0.2 // indirect github.com/jfreymuth/vorbis v1.0.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
@@ -176,6 +178,7 @@ require (
github.com/volatiletech/sqlboiler/v4 v4.16.2 // indirect github.com/volatiletech/sqlboiler/v4 v4.16.2 // indirect
github.com/volatiletech/strmangle v0.0.6 // indirect github.com/volatiletech/strmangle v0.0.6 // indirect
github.com/wlynxg/anet v0.0.4 // indirect github.com/wlynxg/anet v0.0.4 // indirect
github.com/xaionaro-go/gorex v0.0.0-20241010205749-bcd59d639c4d // indirect
github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 // indirect github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
@@ -183,7 +186,6 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/yutopp/go-amf0 v0.1.0 // indirect github.com/yutopp/go-amf0 v0.1.0 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect go.opentelemetry.io/otel/metric v1.29.0 // indirect
@@ -195,14 +197,13 @@ require (
golang.org/x/image v0.18.0 // indirect golang.org/x/image v0.18.0 // indirect
golang.org/x/mobile v0.0.0-20240404231514-09dbf07665ed // indirect golang.org/x/mobile v0.0.0-20240404231514-09dbf07665ed // indirect
golang.org/x/mod v0.20.0 // indirect golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.9.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.27.0 // indirect golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.26.0 // indirect golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.20.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.24.0 // indirect golang.org/x/tools v0.24.0 // indirect
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
@@ -211,11 +212,9 @@ require (
) )
require ( require (
cloud.google.com/go/speech v1.25.2
fyne.io/fyne/v2 v2.5.0 fyne.io/fyne/v2 v2.5.0
github.com/AgustinSRG/go-child-process-manager v1.0.1 github.com/AgustinSRG/go-child-process-manager v1.0.1
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802
github.com/DataDog/gostackparse v0.6.0
github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37 github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37
github.com/abhinavxd/youtube-live-chat-downloader/v2 v2.0.3 github.com/abhinavxd/youtube-live-chat-downloader/v2 v2.0.3
github.com/adeithe/go-twitch v0.3.1 github.com/adeithe/go-twitch v0.3.1
@@ -231,7 +230,6 @@ require (
github.com/chai2010/webp v1.1.1 github.com/chai2010/webp v1.1.1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
github.com/ebitengine/oto/v3 v3.3.1
github.com/getsentry/sentry-go v0.28.1 github.com/getsentry/sentry-go v0.28.1
github.com/go-andiamo/splitter v1.2.5 github.com/go-andiamo/splitter v1.2.5
github.com/go-git/go-git/v5 v5.12.0 github.com/go-git/go-git/v5 v5.12.0
@@ -244,8 +242,6 @@ require (
github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/go-version v1.7.0
github.com/iancoleman/strcase v0.3.0 github.com/iancoleman/strcase v0.3.0
github.com/immune-gmbh/attestation-sdk v0.0.0-20230711173209-f44e4502aeca github.com/immune-gmbh/attestation-sdk v0.0.0-20230711173209-f44e4502aeca
github.com/jfreymuth/oggvorbis v1.0.5
github.com/jfreymuth/pulse v0.1.1
github.com/kbinani/screenshot v0.0.0-20230812210009-b87d31814237 github.com/kbinani/screenshot v0.0.0-20230812210009-b87d31814237
github.com/klauspost/compress v1.17.7 github.com/klauspost/compress v1.17.7
github.com/lusingander/colorpicker v0.7.3 github.com/lusingander/colorpicker v0.7.3
@@ -258,10 +254,10 @@ require (
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.10.0
github.com/xaionaro-go/audio v0.0.0-20250111142716-aa10611bd8a0
github.com/xaionaro-go/datacounter v1.0.4 github.com/xaionaro-go/datacounter v1.0.4
github.com/xaionaro-go/go-rtmp v0.0.0-20241009130244-1e3160f27f42 github.com/xaionaro-go/go-rtmp v0.0.0-20241009130244-1e3160f27f42
github.com/xaionaro-go/gorex v0.0.0-20241010205749-bcd59d639c4d
github.com/xaionaro-go/grpcproxy v0.0.0-20241103205849-a8fef42e72f9 github.com/xaionaro-go/grpcproxy v0.0.0-20241103205849-a8fef42e72f9
github.com/xaionaro-go/kickcom v0.0.0-20241022142825-25a234cc8628 github.com/xaionaro-go/kickcom v0.0.0-20241022142825-25a234cc8628
github.com/xaionaro-go/libsrt v0.0.0-20250105232601-e760c79b2bc3 github.com/xaionaro-go/libsrt v0.0.0-20250105232601-e760c79b2bc3
@@ -269,11 +265,14 @@ require (
github.com/xaionaro-go/mediamtx v0.0.0-20241103200202-882a99e8df73 github.com/xaionaro-go/mediamtx v0.0.0-20241103200202-882a99e8df73
github.com/xaionaro-go/object v0.0.0-20241026212449-753ce10ec94c github.com/xaionaro-go/object v0.0.0-20241026212449-753ce10ec94c
github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a
github.com/xaionaro-go/observability v0.0.0-20250111142240-5d72f17a6d12
github.com/xaionaro-go/secret v0.0.0-20250111141743-ced12e1082c2
github.com/xaionaro-go/timeapiio v0.0.0-20240915203246-b907cf699af3 github.com/xaionaro-go/timeapiio v0.0.0-20240915203246-b907cf699af3
github.com/xaionaro-go/typing v0.0.0-20221123235249-2229101d38ba github.com/xaionaro-go/typing v0.0.0-20221123235249-2229101d38ba
github.com/xaionaro-go/unsafetools v0.0.0-20241024014258-a46e1ce3763e github.com/xaionaro-go/unsafetools v0.0.0-20241024014258-a46e1ce3763e
github.com/xaionaro-go/xsync v0.0.0-20250111141039-3e7f31b1a2c6
github.com/yutopp/go-flv v0.3.1 github.com/yutopp/go-flv v0.3.1
golang.org/x/crypto v0.29.0 golang.org/x/crypto v0.32.0
golang.org/x/net v0.31.0 golang.org/x/net v0.31.0
google.golang.org/grpc v1.67.1 google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1 google.golang.org/protobuf v1.35.1

53
go.sum
View File

@@ -29,8 +29,6 @@ cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ= cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ=
cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
@@ -52,14 +50,10 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc=
cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/speech v1.25.2 h1:rKOXU9LAZTOYHhRNB4gZDekNjJx21TktQpetBa5IzOk=
cloud.google.com/go/speech v1.25.2/go.mod h1:KPFirZlLL8SqPaTtG6l+HHIFHPipjbemv4iFg7rTlYs=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
@@ -96,8 +90,8 @@ github.com/Danny-Dasilva/utls v0.0.0-20220418175931-f38e470e04f2/go.mod h1:A2g8g
github.com/Danny-Dasilva/utls v0.0.0-20220604023528-30cb107b834e h1:tqiguW0yAcIwQBQtD+d2rjBnboqB7CwG1OZ12F8avX8= github.com/Danny-Dasilva/utls v0.0.0-20220604023528-30cb107b834e h1:tqiguW0yAcIwQBQtD+d2rjBnboqB7CwG1OZ12F8avX8=
github.com/Danny-Dasilva/utls v0.0.0-20220604023528-30cb107b834e/go.mod h1:ssfbVNUfWJVRfW41RTpedOUlGXSq3J6aLmirUVkDgJk= github.com/Danny-Dasilva/utls v0.0.0-20220604023528-30cb107b834e/go.mod h1:ssfbVNUfWJVRfW41RTpedOUlGXSq3J6aLmirUVkDgJk=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/gostackparse v0.6.0 h1:egCGQviIabPwsyoWpGvIBGrEnNWez35aEO7OJ1vBI4o= github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4=
github.com/DataDog/gostackparse v0.6.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37 h1:/oQBAuySCcme0DLhicWkr7FaAT5nh1XbbbnCMR2WdPA= github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37 h1:/oQBAuySCcme0DLhicWkr7FaAT5nh1XbbbnCMR2WdPA=
github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37/go.mod h1:nMVB54ifXmC1hpgfq7gTpotbv891pd2wAX/whuUj1q4= github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37/go.mod h1:nMVB54ifXmC1hpgfq7gTpotbv891pd2wAX/whuUj1q4=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
@@ -234,8 +228,8 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/ebitengine/oto/v3 v3.3.1 h1:d4McwGQuXOT0GL7bA5g9ZnaUEIEjQvG3hafzMy+T3qE= github.com/ebitengine/oto/v3 v3.3.2 h1:VTWBsKX9eb+dXzaF4jEwQbs4yWIdXukJ0K40KgkpYlg=
github.com/ebitengine/oto/v3 v3.3.1/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U= github.com/ebitengine/oto/v3 v3.3.2/go.mod h1:MZeb/lwoC4DCOdiTIxYezrURTw7EvK/yF863+tmBI+U=
github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE=
github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
@@ -897,8 +891,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@@ -935,6 +930,8 @@ github.com/wlynxg/anet v0.0.4 h1:0de1OFQxnNqAu+x2FAKKCVIrnfGKQbs7FQz++tB0+Uw=
github.com/wlynxg/anet v0.0.4/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/wlynxg/anet v0.0.4/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/xaionaro-go/astiav v0.0.0-20250106205037-a1605f324663 h1:b0lOm2erjkc/D1/BBa1m9F/jV5AEhU6sRzkcqwTUZH8= github.com/xaionaro-go/astiav v0.0.0-20250106205037-a1605f324663 h1:b0lOm2erjkc/D1/BBa1m9F/jV5AEhU6sRzkcqwTUZH8=
github.com/xaionaro-go/astiav v0.0.0-20250106205037-a1605f324663/go.mod h1:K7D8UC6GeQt85FUxk2KVwYxHnotrxuEnp5evkkudc2s= github.com/xaionaro-go/astiav v0.0.0-20250106205037-a1605f324663/go.mod h1:K7D8UC6GeQt85FUxk2KVwYxHnotrxuEnp5evkkudc2s=
github.com/xaionaro-go/audio v0.0.0-20250111142716-aa10611bd8a0 h1:m3CEneWBmYz2mzU3hZAwxURfMcJYhjI+sNjFCT1jdZM=
github.com/xaionaro-go/audio v0.0.0-20250111142716-aa10611bd8a0/go.mod h1:vo8MOC0grCD/ZUSP06SxXJBeDukqBCC1uD2PHmEH9YM=
github.com/xaionaro-go/datacounter v1.0.4 h1:+QMZLmu73R5WGkQfUPwlXF/JFN+Weo4iuDZkiL2wVm8= github.com/xaionaro-go/datacounter v1.0.4 h1:+QMZLmu73R5WGkQfUPwlXF/JFN+Weo4iuDZkiL2wVm8=
github.com/xaionaro-go/datacounter v1.0.4/go.mod h1:Sf9vBevuV6w5iE6K3qJ9pWVKcyS60clWBUSQLjt5++c= github.com/xaionaro-go/datacounter v1.0.4/go.mod h1:Sf9vBevuV6w5iE6K3qJ9pWVKcyS60clWBUSQLjt5++c=
github.com/xaionaro-go/fyne/v2 v2.0.0-20241020235352-fd61e4920f24 h1:eewdCRMkJmK2ipI9653XL2dE3EFS2I3GTFRadIyyEo4= github.com/xaionaro-go/fyne/v2 v2.0.0-20241020235352-fd61e4920f24 h1:eewdCRMkJmK2ipI9653XL2dE3EFS2I3GTFRadIyyEo4=
@@ -965,8 +962,12 @@ github.com/xaionaro-go/object v0.0.0-20241026212449-753ce10ec94c h1:2CIIxTRox9au
github.com/xaionaro-go/object v0.0.0-20241026212449-753ce10ec94c/go.mod h1:vRcA12NWsR0IrS75eqnPBs5aVfCYmJy4bR+6DbJpBCg= github.com/xaionaro-go/object v0.0.0-20241026212449-753ce10ec94c/go.mod h1:vRcA12NWsR0IrS75eqnPBs5aVfCYmJy4bR+6DbJpBCg=
github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a h1:PyX7XpLkj+eAwrPMFMGpvZIG4zBfzAfwNhwTtbORqN0= github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a h1:PyX7XpLkj+eAwrPMFMGpvZIG4zBfzAfwNhwTtbORqN0=
github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a/go.mod h1:exSKIlCibB0ww+ABDwH+YG/iNdqVfdzXBBg5LYxkxGw= github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a/go.mod h1:exSKIlCibB0ww+ABDwH+YG/iNdqVfdzXBBg5LYxkxGw=
github.com/xaionaro-go/observability v0.0.0-20250111142240-5d72f17a6d12 h1:iy02dGEbIbn8yb/pO/gPpyl2QSk6CPiLl8/JisKsrWg=
github.com/xaionaro-go/observability v0.0.0-20250111142240-5d72f17a6d12/go.mod h1:j5y9LVYd0v8sJa9Ks7ZyuwFxAUpaNFHBNKBkiYipxPM=
github.com/xaionaro-go/pulse v0.0.0-20241023202712-7151fa00d4bb h1:9iHPI27CYbmJDhzEuCABQthE/DGVNvT60ybWvv3BV8w= github.com/xaionaro-go/pulse v0.0.0-20241023202712-7151fa00d4bb h1:9iHPI27CYbmJDhzEuCABQthE/DGVNvT60ybWvv3BV8w=
github.com/xaionaro-go/pulse v0.0.0-20241023202712-7151fa00d4bb/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no= github.com/xaionaro-go/pulse v0.0.0-20241023202712-7151fa00d4bb/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
github.com/xaionaro-go/secret v0.0.0-20250111141743-ced12e1082c2 h1:QHpTWfyfmz65cE0MtFXe9fScdi+X0VIYR2wgolSYEUk=
github.com/xaionaro-go/secret v0.0.0-20250111141743-ced12e1082c2/go.mod h1:XKoHGZ4VKMbVBl8VotLIoWQdrB6Q7jnR++RbkiegZFU=
github.com/xaionaro-go/spinlock v0.0.0-20190309154744-55278e21e817/go.mod h1:Nb/15eS0BMty6TMuWgRQM8WCDIUlyPZagcpchHT6c9Y= github.com/xaionaro-go/spinlock v0.0.0-20190309154744-55278e21e817/go.mod h1:Nb/15eS0BMty6TMuWgRQM8WCDIUlyPZagcpchHT6c9Y=
github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 h1:1Kqw9dv2LnznIhJoMt3dNzc/ctSj6VHjyGh4YZHjpE4= github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 h1:1Kqw9dv2LnznIhJoMt3dNzc/ctSj6VHjyGh4YZHjpE4=
github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1/go.mod h1:UwmTXX+EpoEYHuy0rSys1Rp5PW+eVTgZSjgMVLJENKg= github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1/go.mod h1:UwmTXX+EpoEYHuy0rSys1Rp5PW+eVTgZSjgMVLJENKg=
@@ -976,6 +977,8 @@ github.com/xaionaro-go/typing v0.0.0-20221123235249-2229101d38ba h1:wuSIPuGbTeFk
github.com/xaionaro-go/typing v0.0.0-20221123235249-2229101d38ba/go.mod h1:6wJwFezlmN6Lk38K3eNzGUZ4MmHHzgTAIepvldyiprY= github.com/xaionaro-go/typing v0.0.0-20221123235249-2229101d38ba/go.mod h1:6wJwFezlmN6Lk38K3eNzGUZ4MmHHzgTAIepvldyiprY=
github.com/xaionaro-go/unsafetools v0.0.0-20241024014258-a46e1ce3763e h1:FV+/FVPYOncsNNqtlMvqRDrsQznArX0lO0PkFifixDs= github.com/xaionaro-go/unsafetools v0.0.0-20241024014258-a46e1ce3763e h1:FV+/FVPYOncsNNqtlMvqRDrsQznArX0lO0PkFifixDs=
github.com/xaionaro-go/unsafetools v0.0.0-20241024014258-a46e1ce3763e/go.mod h1:ERewyGVM0zYnWA9nxdHPIC3xc9Yrf5CgAnBITuP3FRE= github.com/xaionaro-go/unsafetools v0.0.0-20241024014258-a46e1ce3763e/go.mod h1:ERewyGVM0zYnWA9nxdHPIC3xc9Yrf5CgAnBITuP3FRE=
github.com/xaionaro-go/xsync v0.0.0-20250111141039-3e7f31b1a2c6 h1:ROtz45GepYXGa3EK3bImlfdvZKdAr5LEBgZAW7dKRsg=
github.com/xaionaro-go/xsync v0.0.0-20250111141039-3e7f31b1a2c6/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 h1:jAy7VLg8y8XE1R8jBte4PRDJzOaAE+sUfmttfB9ZcAY=
github.com/xaionaro-go/zerolog2belt v0.0.0-20241103164018-a3bc1ea487e5/go.mod h1:KJuX7yl27vU+KV6CpsWOe5ZMY4zSg70hvEhwoYdr17w= 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= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@@ -1016,8 +1019,6 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
@@ -1067,8 +1068,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= 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.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.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1205,8 +1206,8 @@ golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1221,8 +1222,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1330,8 +1331,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.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.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.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1342,8 +1343,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.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.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.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1359,8 +1360,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.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.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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/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= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1568,8 +1569,6 @@ google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 h1:Df6WuGvthPzc+JiQ/G+m+sNX24kc0aTBqoDN/0yyykE=
google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53/go.mod h1:fheguH3Am2dGp1LfXkrvwqC/KlFq8F0nLq3LryOMrrE=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=

View File

@@ -9,7 +9,7 @@ import (
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type Encoder struct { type Encoder struct {

View File

@@ -1,100 +0,0 @@
package audio
import (
"context"
"fmt"
"io"
"sync"
"time"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/jfreymuth/oggvorbis"
"github.com/xaionaro-go/streamctl/pkg/audio/registry"
)
const BufferSize = 100 * time.Millisecond
type Audio struct {
PlayerPCM
}
func NewAudio(playerPCM PlayerPCM) *Audio {
return &Audio{
PlayerPCM: playerPCM,
}
}
var (
lastSuccessfulFactory registry.PlayerPCMFactory
lastSuccessfulFactoryLocker sync.Mutex
)
func getLastSuccessfulFactory() registry.PlayerPCMFactory {
lastSuccessfulFactoryLocker.Lock()
defer lastSuccessfulFactoryLocker.Unlock()
return lastSuccessfulFactory
}
func NewAudioAuto(
ctx context.Context,
) *Audio {
factory := getLastSuccessfulFactory()
if factory != nil {
player := factory.NewPlayerPCM()
if err := player.Ping(); err == nil {
return NewAudio(player)
}
}
for _, factory := range registry.Factories() {
player := factory.NewPlayerPCM()
err := player.Ping()
logger.Debugf(ctx, "pinging PCM player %T result is %v", player, err)
if err == nil {
lastSuccessfulFactoryLocker.Lock()
defer lastSuccessfulFactoryLocker.Unlock()
lastSuccessfulFactory = factory
return NewAudio(player)
}
}
logger.Infof(ctx, "was unable to initialize any PCM player")
return &Audio{
PlayerPCM: PlayerPCMDummy{},
}
}
func (a *Audio) PlayVorbis(rawReader io.Reader) (Stream, error) {
oggReader, err := oggvorbis.NewReader(rawReader)
if err != nil {
return nil, fmt.Errorf("unable to initialize a vorbis reader: %w", err)
}
stream, err := a.PlayerPCM.PlayPCM(
SampleRate(oggReader.SampleRate()),
Channel(oggReader.Channels()),
PCMFormatFloat32LE,
BufferSize,
newReaderFromFloat32Reader(oggReader),
)
if err != nil {
return nil, fmt.Errorf("unable to playback as PCM: %w", err)
}
return stream, nil
}
func (a *Audio) PlayPCM(
sampleRate SampleRate,
channels Channel,
pcmFormat PCMFormat,
bufferSize time.Duration,
pcmReader io.Reader,
) (Stream, error) {
return a.PlayerPCM.PlayPCM(
sampleRate,
channels,
pcmFormat,
bufferSize,
pcmReader,
)
}

View File

@@ -1,30 +0,0 @@
package audio
import (
"io"
"unsafe"
)
type float32Reader interface {
Read([]float32) (int, error)
}
type readerFromFloat32Reader struct {
float32Reader
}
var _ io.Reader = (*readerFromFloat32Reader)(nil)
func newReaderFromFloat32Reader(r float32Reader) readerFromFloat32Reader {
return readerFromFloat32Reader{
float32Reader: r,
}
}
func (r readerFromFloat32Reader) Read(b []byte) (int, error) {
ptr := unsafe.SliceData(b)
f := unsafe.Slice((*float32)(unsafe.Pointer(ptr)), len(b)/4)
n, err := r.float32Reader.Read(f)
n *= int(unsafe.Sizeof(float32(0)))
return n, err
}

View File

@@ -1,46 +0,0 @@
package oto
import (
"fmt"
"sync"
"time"
"github.com/ebitengine/oto/v3"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
var (
otoContext *oto.Context
otoContextLocker sync.Mutex
)
const (
SampleRate = 48000
Channels = 2
BufferSize = 100 * time.Millisecond
Format = types.PCMFormatFloat32LE
)
func getOtoContext() (*oto.Context, error) {
otoContextLocker.Lock()
defer otoContextLocker.Unlock()
if otoContext != nil {
return otoContext, nil
}
op := &oto.NewContextOptions{
SampleRate: SampleRate,
ChannelCount: Channels,
Format: FormatToOto(Format),
BufferSize: BufferSize,
}
otoCtx, readyChan, err := oto.NewContext(op)
if err != nil {
return otoCtx, fmt.Errorf("unable to initialize an oto context: %w", err)
}
<-readyChan
otoContext = otoCtx
return otoContext, nil
}

View File

@@ -1,14 +0,0 @@
package oto
import (
"github.com/ebitengine/oto/v3"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
func FormatToOto(f types.PCMFormat) oto.Format {
switch f {
case types.PCMFormatFloat32LE:
return oto.FormatFloat32LE
}
return oto.Format(-1)
}

View File

@@ -1,20 +0,0 @@
package oto
import (
"github.com/xaionaro-go/streamctl/pkg/audio/registry"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
const (
Priority = 100
)
func init() {
registry.RegisterFactory(Priority, PlayerPCMOtoFactory{})
}
type PlayerPCMOtoFactory struct{}
func (PlayerPCMOtoFactory) NewPlayerPCM() types.PlayerPCM {
return NewPlayerPCM()
}

View File

@@ -1,65 +0,0 @@
package oto
import (
"fmt"
"io"
"time"
"github.com/xaionaro-go/streamctl/pkg/audio/resampler"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
type PlayerPCM struct {
}
var _ types.PlayerPCM = (*PlayerPCM)(nil)
func NewPlayerPCM() PlayerPCM {
return PlayerPCM{}
}
func (PlayerPCM) Ping() error {
// do not know how to do that, yet
return nil
}
func (PlayerPCM) PlayPCM(
sampleRate types.SampleRate,
channels types.Channel,
format types.PCMFormat,
bufferSize time.Duration,
reader io.Reader,
) (types.Stream, error) {
// Unfortunately, `oto` does not allow to initialize a context multiple times, so we cannot change the context every time different sampleRate, channels, format or bufferSize are given.
// As a result, we've just chosen reasonable values and expect them always :(
if bufferSize != BufferSize {
return nil, fmt.Errorf("expected buffer size is %v, but received a request for %v", BufferSize, bufferSize)
}
if sampleRate != SampleRate || channels != Channels || format != Format {
inFmt := resampler.Format{
Channels: channels,
SampleRate: sampleRate,
PCMFormat: format,
}
outFmt := resampler.Format{
Channels: Channels,
SampleRate: SampleRate,
PCMFormat: Format,
}
var err error
reader, err = resampler.NewResampler(inFmt, reader, outFmt)
if err != nil {
return nil, fmt.Errorf("unable to initialize a resampler from %#+v to %#+v: %w", inFmt, outFmt, err)
}
}
otoCtx, err := getOtoContext()
if err != nil {
return nil, fmt.Errorf("unable to get an oto context: %w", err)
}
player := otoCtx.NewPlayer(reader)
player.Play()
return newStream(player), nil
}

View File

@@ -1,31 +0,0 @@
package oto
import (
"time"
"github.com/ebitengine/oto/v3"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
type Stream struct {
*oto.Player
}
var _ types.Stream = (*Stream)(nil)
func newStream(otoPlayer *oto.Player) *Stream {
return &Stream{
Player: otoPlayer,
}
}
func (stream *Stream) Drain() error {
for stream.Player.IsPlaying() {
time.Sleep(100 * time.Millisecond)
}
return nil
}
func (stream *Stream) Close() error {
return stream.Player.Close()
}

View File

@@ -1,20 +0,0 @@
package pulseaudio
import (
"github.com/xaionaro-go/streamctl/pkg/audio/registry"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
const (
Priority = 50
)
func init() {
registry.RegisterFactory(Priority, PlayerPCMOtoFactory{})
}
type PlayerPCMOtoFactory struct{}
func (PlayerPCMOtoFactory) NewPlayerPCM() types.PlayerPCM {
return NewPlayerPCM()
}

View File

@@ -1,101 +0,0 @@
package pulseaudio
import (
"fmt"
"io"
"time"
"github.com/jfreymuth/pulse"
"github.com/jfreymuth/pulse/proto"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
type PlayerPCM struct {
}
var _ types.PlayerPCM = (*PlayerPCM)(nil)
func NewPlayerPCM() PlayerPCM {
return PlayerPCM{}
}
func (PlayerPCM) Ping() error {
c, err := pulse.NewClient()
if err != nil {
return fmt.Errorf("unable to open a client to Pulse: %w", err)
}
defer c.Close()
return nil
}
func (PlayerPCM) PlayPCM(
sampleRate types.SampleRate,
channels types.Channel,
format types.PCMFormat,
bufferSize time.Duration,
rawReader io.Reader,
) (types.Stream, error) {
reader, err := newPulseReader(format, rawReader)
if err != nil {
return nil, fmt.Errorf("unable to initialize a reader for Pulse: %w", err)
}
c, err := pulse.NewClient()
if err != nil {
return nil, fmt.Errorf("unable to open a client to Pulse: %w", err)
}
defer c.Close()
stream, err := c.NewPlayback(reader, pulse.PlaybackLatency(bufferSize.Seconds()))
if err != nil {
return nil, fmt.Errorf("unable to initialize a playback: %w", err)
}
stream.Start()
stream.Drain()
if stream.Error() != nil {
return nil, fmt.Errorf("an error occurred during playback: %w", stream.Error())
}
if stream.Underflow() {
return nil, fmt.Errorf("underflow")
}
func() {
defer func() {
r := recover()
if r != nil {
err = fmt.Errorf("got a panic: %v", r)
}
}()
stream.Close()
}()
if err != nil {
return nil, fmt.Errorf("unable to close the stream: %w", err)
}
return newStream(stream), nil
}
type pulseReader struct {
pulseFormat byte
io.Reader
}
func newPulseReader(pcmFormat types.PCMFormat, reader io.Reader) (*pulseReader, error) {
var pulseFormat byte
switch pcmFormat {
case types.PCMFormatFloat32LE:
pulseFormat = proto.FormatFloat32LE
default:
return nil, fmt.Errorf("received an unexpected format: %v", pcmFormat)
}
return &pulseReader{
pulseFormat: pulseFormat,
Reader: reader,
}, nil
}
var _ pulse.Reader = (*pulseReader)(nil)
func (r pulseReader) Format() byte {
return r.pulseFormat
}

View File

@@ -1,42 +0,0 @@
package pulseaudio
import (
"fmt"
"github.com/jfreymuth/pulse"
)
type Stream struct {
*pulse.PlaybackStream
}
func newStream(pulseStream *pulse.PlaybackStream) *Stream {
return &Stream{
PlaybackStream: pulseStream,
}
}
func (stream *Stream) Drain() error {
stream.Start()
if err := stream.Drain(); err != nil {
return fmt.Errorf("unable to drain: %w", err)
}
if stream.Error() != nil {
return fmt.Errorf("an error occurred during playback: %w", stream.Error())
}
if stream.Underflow() {
return fmt.Errorf("underflow")
}
return nil
}
func (stream *Stream) Close() (err error) {
defer func() {
r := recover()
if r != nil {
err = fmt.Errorf("got a panic: %v", r)
}
}()
err = stream.Close()
return
}

View File

@@ -1,27 +0,0 @@
package main
import (
"bytes"
"context"
_ "embed"
"github.com/xaionaro-go/streamctl/pkg/audio"
)
//go:embed resources/long_audio.ogg
var longVorbis []byte
func main() {
ctx := context.Background()
a := audio.NewAudioAuto(ctx)
stream, err := a.PlayVorbis(bytes.NewReader(longVorbis))
assertNoError(err)
assertNoError(stream.Drain())
assertNoError(stream.Close())
}
func assertNoError(err error) {
if err != nil {
panic(err)
}
}

View File

@@ -1,92 +0,0 @@
package main
import (
"context"
"fmt"
"net/http"
_ "net/http/pprof"
"os"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
"github.com/spf13/pflag"
"github.com/xaionaro-go/streamctl/pkg/audio/resampler"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
"github.com/xaionaro-go/streamctl/pkg/observability"
)
func syntaxExit(message string) {
fmt.Fprintf(os.Stderr, "syntax error: %s\n", message)
pflag.Usage()
os.Exit(2)
}
func main() {
loggerLevel := logger.LevelDebug
pflag.Var(&loggerLevel, "log-level", "Log level")
netPprofAddr := pflag.String("net-pprof-listen-addr", "", "an address to listen for incoming net/pprof connections")
srcFormatFlag := pflag.String("src-format", "F32LE", "")
srcSampleRate := pflag.Uint("src-rate", 48000, "")
srcChannels := pflag.Uint("src-channels", 2, "")
dstFormatFlag := pflag.String("dst-format", "S16LE", "")
dstSampleRate := pflag.Uint("dst-rate", 16000, "")
dstChannels := pflag.Uint("dst-channels", 1, "")
pflag.Parse()
if pflag.NArg() != 0 {
syntaxExit("expected zero arguments")
}
l := logrus.Default().WithLevel(loggerLevel)
ctx := logger.CtxWithLogger(context.Background(), l)
logger.Default = func() logger.Logger {
return l
}
defer belt.Flush(ctx)
if *netPprofAddr != "" {
observability.Go(ctx, func() { l.Error(http.ListenAndServe(*netPprofAddr, nil)) })
}
srcFormat := types.PCMFormatFromString(*srcFormatFlag)
if srcFormat == types.PCMFormatUndefined {
panic(fmt.Errorf("unknown PCM format '%s'", *srcFormatFlag))
}
dstFormat := types.PCMFormatFromString(*dstFormatFlag)
if srcFormat == types.PCMFormatUndefined {
panic(fmt.Errorf("unknown PCM format '%s'", *dstFormatFlag))
}
formatSrc := resampler.Format{
Channels: types.Channel(*srcChannels),
SampleRate: types.SampleRate(*srcSampleRate),
PCMFormat: srcFormat,
}
formatDst := resampler.Format{
Channels: types.Channel(*dstChannels),
SampleRate: types.SampleRate(*dstSampleRate),
PCMFormat: dstFormat,
}
resampler, err := resampler.NewResampler(formatSrc, os.Stdin, formatDst)
if err != nil {
panic(err)
}
buf := make([]byte, 1024*1024)
for {
n, err := resampler.Read(buf)
if err != nil {
panic(err)
}
w, err := os.Stdout.Write(buf[:n])
if err != nil {
panic(err)
}
if w != n {
panic(fmt.Errorf("the written a message not equal to the expected: %d != %d", w, n))
}
}
}

View File

@@ -1,36 +0,0 @@
package audio
import (
"io"
"time"
)
type PlayerPCMDummy struct{}
var _ PlayerPCM = PlayerPCMDummy{}
func (PlayerPCMDummy) Ping() error {
return nil
}
func (PlayerPCMDummy) PlayPCM(
sampleRate SampleRate,
channels Channel,
format PCMFormat,
bufferSize time.Duration,
reader io.Reader,
) (Stream, error) {
return StreamDummy{}, nil
}
type StreamDummy struct{}
var _ Stream = StreamDummy{}
func (StreamDummy) Drain() error {
return nil
}
func (StreamDummy) Close() error {
return nil
}

View File

@@ -1,54 +0,0 @@
package registry
import (
"fmt"
"reflect"
"sort"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
type PlayerPCMFactory interface {
NewPlayerPCM() types.PlayerPCM
}
type factoryWithPriority struct {
Priority int
PlayerPCMFactory
}
var factoryRegistry = map[reflect.Type]factoryWithPriority{}
func RegisterFactory(
priority int,
playerPCMFactory PlayerPCMFactory,
) {
t := reflect.ValueOf(playerPCMFactory).Type()
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
if _, ok := factoryRegistry[t]; ok {
panic(fmt.Errorf("there is already registered a factory of PlayerPCM of type %v", t))
}
factoryRegistry[t] = factoryWithPriority{
Priority: priority,
PlayerPCMFactory: playerPCMFactory,
}
}
func Factories() []PlayerPCMFactory {
var factoriesWithPriorities []factoryWithPriority
for _, factory := range factoryRegistry {
factoriesWithPriorities = append(factoriesWithPriorities, factory)
}
sort.Slice(factoriesWithPriorities, func(i, j int) bool {
return factoriesWithPriorities[i].Priority < factoriesWithPriorities[j].Priority
})
var factories []PlayerPCMFactory
for _, factory := range factoriesWithPriorities {
factories = append(factories, factory.PlayerPCMFactory)
}
return factories
}

View File

@@ -1,169 +0,0 @@
package resampler
import (
"encoding/binary"
"fmt"
"io"
"math"
"sync"
"github.com/xaionaro-go/streamctl/pkg/audio"
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
const (
distanceStep = 10000
)
type Format struct {
Channels audio.Channel
SampleRate audio.SampleRate
PCMFormat types.PCMFormat
}
type precalculated struct {
inSampleSize uint
outSampleSize uint
inNumAvg uint
outNumRepeat uint
outDistanceStep uint64
convert func(dst, src []byte)
}
type Resampler struct {
inReader io.Reader
inFormat Format
outFormat Format
inDistance uint64
outDistance uint64
locker sync.Mutex
buffer []byte
precalculated
}
var pcmSampleConvertMap = [types.EndOfPCMFormat + 1] /*from*/ [types.EndOfPCMFormat + 1] /*to*/ func(dst, src []byte){
types.PCMFormatU8: {
types.PCMFormatU8: func(dst, src []byte) {
copy(dst, src)
},
types.PCMFormatFloat32LE: func(dst, src []byte) {
v := float32(src[0])
binary.LittleEndian.PutUint32(dst, math.Float32bits(v))
},
},
types.PCMFormatS16LE: {
types.PCMFormatS16LE: func(dst, src []byte) {
copy(dst, src)
},
},
types.PCMFormatFloat32LE: {
types.PCMFormatFloat32LE: func(dst, src []byte) {
copy(dst, src)
},
types.PCMFormatS16LE: func(dst, src []byte) {
f32 := math.Float32frombits(binary.LittleEndian.Uint32(src)) * 0x8000
binary.LittleEndian.PutUint16(dst, uint16(f32))
},
},
}
var _ io.Reader = (*Resampler)(nil)
func NewResampler(
inFormat Format,
inReader io.Reader,
outFormat Format,
) (*Resampler, error) {
r := &Resampler{
inReader: inReader,
inFormat: inFormat,
outFormat: outFormat,
}
err := r.init()
if err != nil {
return nil, fmt.Errorf("unable to initialize a resampler from %#+v to %#+v: %w", inFormat, outFormat, err)
}
return r, nil
}
func (r *Resampler) init() error {
r.inSampleSize = uint(r.inFormat.PCMFormat.Size())
r.outSampleSize = uint(r.outFormat.PCMFormat.Size())
r.inNumAvg = 1
r.outNumRepeat = 1
if r.inFormat.Channels != r.outFormat.Channels {
switch {
case r.inFormat.Channels == 1:
r.outNumRepeat = uint(r.outFormat.Channels)
case r.outFormat.Channels == 1:
r.inNumAvg = uint(r.inFormat.Channels)
default:
return fmt.Errorf("do not know how to convert %d channels to %d", r.inFormat.Channels, r.outFormat.Channels)
}
}
sampleRateAdjust := float64(r.outFormat.SampleRate) / float64(r.inFormat.SampleRate)
r.outDistanceStep = uint64(float64(distanceStep) / sampleRateAdjust)
r.convert = pcmSampleConvertMap[r.inFormat.PCMFormat][r.outFormat.PCMFormat]
if r.convert == nil {
return fmt.Errorf("unable to get a convert function from %s to %s", r.inFormat.PCMFormat, r.outFormat.PCMFormat)
}
return nil
}
func (r *Resampler) Read(p []byte) (int, error) {
r.locker.Lock()
defer r.locker.Unlock()
chunksToRead := uint64(len(p)) / uint64(r.outSampleSize) / uint64(r.outNumRepeat) * uint64(r.inFormat.SampleRate) / uint64(r.outFormat.SampleRate)
bytesToRead := uint64(chunksToRead) * uint64(r.inSampleSize)
if cap(r.buffer) < int(bytesToRead) {
r.buffer = make([]byte, bytesToRead)
} else {
r.buffer = r.buffer[:bytesToRead]
}
n, err := r.inReader.Read(r.buffer)
r.buffer = r.buffer[:n]
if n%int(r.inSampleSize) != 0 {
return 0, fmt.Errorf("read a number of bytes that is not a multiple of %d: %w", r.inSampleSize, err)
}
chunksToWrite := uint64(n) / uint64(r.inSampleSize) / uint64(r.inNumAvg) * uint64(r.outFormat.SampleRate) / uint64(r.inFormat.SampleRate)
for srcChunkIdx, dstChunkIdx := uint64(0), uint64(0); dstChunkIdx < chunksToWrite; {
for int64(r.inDistance) < int64(r.outDistance)-int64(r.outDistanceStep) {
srcChunkIdx++
r.inDistance += distanceStep
}
// TODO: It is assumed that r.inNumAvg == 1 (see `init()`), so we omit
// averaging the value, yet. Fix this.
idxSrc := srcChunkIdx * uint64(r.inSampleSize) * uint64(r.inNumAvg)
for repeatIdx := uint64(0); repeatIdx < uint64(r.outNumRepeat); repeatIdx++ {
idxDst := uint64((dstChunkIdx*uint64(r.outNumRepeat) + repeatIdx) * uint64(r.outSampleSize))
src := r.buffer[idxSrc:]
dst := p[idxDst:]
r.convert(dst, src)
}
prevIdxDst := uint64(dstChunkIdx) * uint64(r.outNumRepeat) * uint64(r.outSampleSize)
for r.inDistance > r.outDistance+r.outDistanceStep {
dstChunkIdx++
r.outDistance += r.outDistanceStep
idxDst := uint64(dstChunkIdx) * uint64(r.outNumRepeat) * uint64(r.outSampleSize)
src := p[prevIdxDst:idxDst]
dst := p[idxDst:]
copy(dst, src)
prevIdxDst = idxDst
}
srcChunkIdx++
r.inDistance += distanceStep
dstChunkIdx++
r.outDistance += r.outDistanceStep
}
return int(chunksToWrite * uint64(r.outSampleSize) * uint64(r.outNumRepeat)), err
}

View File

@@ -1,32 +0,0 @@
package audio
import (
"github.com/xaionaro-go/streamctl/pkg/audio/types"
)
type PlayerPCM = types.PlayerPCM
type Stream = types.Stream
type PCMFormat = types.PCMFormat
const (
PCMFormatUndefined = types.PCMFormatUndefined
PCMFormatU8 = types.PCMFormatU8
PCMFormatS16LE = types.PCMFormatS16LE
PCMFormatS16BE = types.PCMFormatS16BE
PCMFormatFloat32LE = types.PCMFormatFloat32LE
PCMFormatFloat32BE = types.PCMFormatFloat32BE
PCMFormatS24LE = types.PCMFormatS24LE
PCMFormatS24BE = types.PCMFormatS24BE
PCMFormatS32LE = types.PCMFormatS32LE
PCMFormatS32BE = types.PCMFormatS32BE
PCMFormatFloat64LE = types.PCMFormatFloat64LE
PCMFormatFloat64BE = types.PCMFormatFloat64BE
PCMFormatS64LE = types.PCMFormatS64LE
PCMFormatS64BE = types.PCMFormatS64BE
)
type Encoding = types.Encoding
type EncodingPCM = types.EncodingPCM
type SampleRate = types.SampleRate
type Channel = types.Channel

View File

@@ -1,99 +0,0 @@
package types
import (
"fmt"
"io"
"math"
"strings"
"time"
)
type PlayerPCM interface {
Ping() error
PlayPCM(
sampleRate SampleRate,
channels Channel,
format PCMFormat,
bufferSize time.Duration,
reader io.Reader,
) (Stream, error)
}
type PCMFormat uint
const (
PCMFormatUndefined = PCMFormat(iota)
PCMFormatU8
PCMFormatS16LE
PCMFormatS16BE
PCMFormatFloat32LE
PCMFormatFloat32BE
PCMFormatS24LE
PCMFormatS24BE
PCMFormatS32LE
PCMFormatS32BE
PCMFormatFloat64LE
PCMFormatFloat64BE
PCMFormatS64LE
PCMFormatS64BE
EndOfPCMFormat
)
func (f PCMFormat) Size() uint32 {
switch f {
case PCMFormatUndefined:
return math.MaxUint32
case PCMFormatU8:
return 1
case PCMFormatS16LE, PCMFormatS16BE:
return 2
case PCMFormatS24LE, PCMFormatS24BE:
return 3
case PCMFormatFloat32LE, PCMFormatFloat32BE, PCMFormatS32LE, PCMFormatS32BE:
return 4
case PCMFormatFloat64LE, PCMFormatFloat64BE, PCMFormatS64LE, PCMFormatS64BE:
return 8
default:
return math.MaxUint32
}
}
func (f PCMFormat) String() string {
switch f {
case PCMFormatUndefined:
return "<undefined>"
case PCMFormatS16LE:
return "s16le"
case PCMFormatFloat32LE:
return "f32le"
default:
return fmt.Sprintf("<unexpected_value_%d>", f)
}
}
func PCMFormatFromString(in string) PCMFormat {
in = strings.ToLower(in)
for fmt := PCMFormatUndefined + 1; fmt < EndOfPCMFormat; fmt++ {
if strings.ToLower(fmt.String()) == in {
return fmt
}
}
return PCMFormatUndefined
}
type SampleRate uint32
type Channel uint32
type Encoding interface {
BytesPerAudioFrame() uint
}
type EncodingPCM struct {
PCMFormat
SampleRate
}
func (pcm EncodingPCM) BytesPerAudioFrame() uint {
panic("not implemented, yet")
}

View File

@@ -1,6 +0,0 @@
package types
type Stream interface {
Drain() error
Close() error
}

View File

@@ -18,6 +18,7 @@ type Encoder struct {
Config EncoderConfig Config EncoderConfig
Decoder *recoder.Decoder
prevEncodeTS time.Time prevEncodeTS time.Time
locker sync.Mutex locker sync.Mutex
inputStreams map[*recoder.Input]map[int]*astiav.Stream inputStreams map[*recoder.Input]map[int]*astiav.Stream

View File

@@ -8,10 +8,10 @@ import (
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/libsrt" "github.com/xaionaro-go/libsrt"
"github.com/xaionaro-go/observability"
ffstreamtypes "github.com/xaionaro-go/streamctl/pkg/ffstream/types" ffstreamtypes "github.com/xaionaro-go/streamctl/pkg/ffstream/types"
"github.com/xaionaro-go/streamctl/pkg/ffstreamserver/grpc/go/ffstream_grpc" "github.com/xaionaro-go/streamctl/pkg/ffstreamserver/grpc/go/ffstream_grpc"
"github.com/xaionaro-go/streamctl/pkg/ffstreamserver/grpc/goconv" "github.com/xaionaro-go/streamctl/pkg/ffstreamserver/grpc/goconv"
"github.com/xaionaro-go/streamctl/pkg/observability"
recodertypes "github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder/types" recodertypes "github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder/types"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"

View File

@@ -6,9 +6,9 @@ import (
"github.com/facebookincubator/go-belt/tool/experimental/errmon" "github.com/facebookincubator/go-belt/tool/experimental/errmon"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/ffstream" "github.com/xaionaro-go/streamctl/pkg/ffstream"
"github.com/xaionaro-go/streamctl/pkg/ffstreamserver/grpc/go/ffstream_grpc" "github.com/xaionaro-go/streamctl/pkg/ffstreamserver/grpc/go/ffstream_grpc"
"github.com/xaionaro-go/streamctl/pkg/observability"
"google.golang.org/grpc" "google.golang.org/grpc"
) )

View File

@@ -9,8 +9,8 @@ import (
"time" "time"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type logWriter struct { type logWriter struct {

View File

@@ -7,8 +7,8 @@ import (
"net" "net"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type Client struct { type Client struct {

View File

@@ -14,8 +14,8 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/sethvargo/go-password/password" "github.com/sethvargo/go-password/password"
"github.com/xaionaro-go/lockmap" "github.com/xaionaro-go/lockmap"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
func init() { func init() {

View File

@@ -11,7 +11,7 @@ import (
"strings" "strings"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
) )
type OAuthHandlerArgument struct { type OAuthHandlerArgument struct {

View File

@@ -1,34 +0,0 @@
package observability
import (
"runtime"
"strings"
)
func CallerPCFilter(
originalPCFilter func(uintptr) bool,
) func(uintptr) bool {
return func(pc uintptr) bool {
if !originalPCFilter(pc) {
return false
}
fn := runtime.FuncForPC(pc)
funcName := fn.Name()
switch {
case strings.Contains(funcName, "pkg/xsync"):
return false
}
file, _ := fn.FileLine(pc)
switch {
case strings.Contains(file, "context.go"):
return false
case strings.Contains(file, "log_writer.go"):
return false
case strings.Contains(file, "logger.go"):
return false
case strings.Contains(file, "runtime.go"):
return false
}
return true
}
}

View File

@@ -1,207 +0,0 @@
package observability
import (
"bytes"
"context"
"fmt"
"runtime"
"github.com/DataDog/gostackparse"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/pkg/field"
xruntime "github.com/facebookincubator/go-belt/pkg/runtime"
"github.com/facebookincubator/go-belt/tool/experimental/errmon"
errmontypes "github.com/facebookincubator/go-belt/tool/experimental/errmon/types"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/facebookincubator/go-belt/tool/logger/adapter"
loggertypes "github.com/facebookincubator/go-belt/tool/logger/types"
)
func getGoroutines() ([]errmontypes.Goroutine, int) {
// TODO: consider pprof.Lookup("goroutine") instead of runtime.Stack
// getting all goroutines
stackBufferSize := 65536 * runtime.NumGoroutine()
if stackBufferSize > 10*1024*1024 {
stackBufferSize = 10 * 1024 * 1024
}
stackBuffer := make([]byte, stackBufferSize)
n := runtime.Stack(stackBuffer, true)
goroutines, errs := gostackparse.Parse(bytes.NewReader(stackBuffer[:n]))
if len(errs) > 0 { //nolint:staticcheck
// TODO: do something
}
// convert goroutines for the output
goroutinesConverted := make([]errmontypes.Goroutine, 0, len(goroutines))
for _, goroutine := range goroutines {
goroutinesConverted = append(goroutinesConverted, *goroutine)
}
// getting current goroutine ID
n = runtime.Stack(stackBuffer, false)
currentGoroutines, errs := gostackparse.Parse(bytes.NewReader(stackBuffer[:n]))
if len(errs) > 0 { //nolint:staticcheck
// TODO: do something
}
var currentGoroutineID int
switch len(currentGoroutines) {
case 0:
// TODO: do something
case 1:
currentGoroutineID = currentGoroutines[0].ID
default:
// TODO: do something
}
return goroutinesConverted, currentGoroutineID
}
type ErrorMonitorLoggerHook struct {
ErrorMonitor errmontypes.ErrorMonitor
SendChan chan ErrorMonitorMessage
}
func NewErrorMonitorLoggerHook(
errorMonitor errmon.ErrorMonitor,
) *ErrorMonitorLoggerHook {
result := &ErrorMonitorLoggerHook{
ErrorMonitor: errorMonitor,
SendChan: make(chan ErrorMonitorMessage, 10),
}
GoSafe(context.TODO(), result.senderLoop)
return result
}
var _ loggertypes.PreHook = (*ErrorMonitorLoggerHook)(nil)
func (h *ErrorMonitorLoggerHook) ProcessInput(
traceIDs belt.TraceIDs,
level loggertypes.Level,
args ...any,
) loggertypes.PreHookResult {
if level > loggertypes.LevelWarning {
return loggertypes.PreHookResult{
Skip: false,
}
}
emitter := &mockEmitter{}
l := adapter.LoggerFromEmitter(emitter).WithLevel(logger.LevelWarning)
l.Log(level, args...)
h.sendReport(emitter.LastEntry)
return loggertypes.PreHookResult{
Skip: false,
}
}
func (h *ErrorMonitorLoggerHook) ProcessInputf(
traceIDs belt.TraceIDs,
level loggertypes.Level,
format string,
args ...any,
) loggertypes.PreHookResult {
if level > loggertypes.LevelWarning {
return loggertypes.PreHookResult{
Skip: false,
}
}
emitter := &mockEmitter{}
l := adapter.LoggerFromEmitter(emitter).WithLevel(logger.LevelWarning)
l.Logf(level, format, args...)
h.sendReport(emitter.LastEntry)
return loggertypes.PreHookResult{
Skip: false,
}
}
func (h *ErrorMonitorLoggerHook) ProcessInputFields(
traceIDs belt.TraceIDs,
level loggertypes.Level,
message string,
fields field.AbstractFields,
) loggertypes.PreHookResult {
if level > loggertypes.LevelWarning {
return loggertypes.PreHookResult{
Skip: false,
}
}
emitter := &mockEmitter{}
l := adapter.LoggerFromEmitter(emitter).WithLevel(logger.LevelWarning)
l.LogFields(level, message, fields)
h.sendReport(emitter.LastEntry)
return loggertypes.PreHookResult{
Skip: false,
}
}
func copyEntry(entry *loggertypes.Entry) *loggertypes.Entry {
entryDup := *entry
if entry.Fields != nil {
fields := make(field.Fields, 0, entry.Fields.Len())
entry.Fields.ForEachField(func(f *field.Field) bool {
fields = append(fields, *f)
return true
})
entryDup.Fields = fields
}
return &entryDup
}
type ErrorMonitorMessage struct {
Entry *loggertypes.Entry
Goroutines []errmontypes.Goroutine
CurrentGoroutineID int
StackTrace xruntime.PCs
}
func (h *ErrorMonitorLoggerHook) sendReport(
entry *loggertypes.Entry,
) {
if entry == nil {
logger.Default().Errorf("an attempt to send through sentry a nil entry")
return
}
logger.Default().Tracef("sending through sentry entry: %#+v", *entry)
entryDup := copyEntry(entry)
goroutines, currentGoroutineID := getGoroutines()
stackTrace := xruntime.CallerStackTrace(nil)
select {
case h.SendChan <- ErrorMonitorMessage{
Entry: entryDup,
Goroutines: goroutines,
CurrentGoroutineID: currentGoroutineID,
StackTrace: stackTrace,
}:
default:
logger.Default().Errorf("unable to send an error to Sentry, the channel is busy")
return
}
}
func (h *ErrorMonitorLoggerHook) senderLoop() {
for {
message := <-h.SendChan
h.ErrorMonitor.Emitter().Emit(&errmontypes.Event{
Entry: *message.Entry,
ID: "",
ExternalIDs: []any{},
Exception: errmontypes.Exception{
IsPanic: message.Entry.Level <= loggertypes.LevelPanic,
Error: fmt.Errorf("[%s] %s", message.Entry.Level, message.Entry.Message),
StackTrace: message.StackTrace,
},
CurrentGoroutineID: message.CurrentGoroutineID,
Goroutines: message.Goroutines,
})
}
}

View File

@@ -1,23 +0,0 @@
package observability
import (
"context"
)
func Call(ctx context.Context, fn func()) {
defer func() { PanicIfNotNil(ctx, recover()) }()
fn()
}
func CallSafe(ctx context.Context, fn func()) {
defer func() { ReportPanicIfNotNil(ctx, recover()) }()
fn()
}
func Go(ctx context.Context, fn func()) {
go Call(ctx, fn)
}
func GoSafe(ctx context.Context, fn func()) {
go CallSafe(ctx, fn)
}

View File

@@ -1,39 +0,0 @@
package observability
import (
"context"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/pkg/field"
"github.com/facebookincubator/go-belt/tool/logger"
)
type ctxKeyOnInsecureDebugT struct{}
var ctxKeyOnInsecureDebug ctxKeyOnInsecureDebugT
func OnInsecureDebug(ctx context.Context) context.Context {
return belt.WithField(ctx, "ctxKeyOnInsecureDebug", ctxKeyOnInsecureDebug)
}
func IsOnInsecureDebug(ctx context.Context) bool {
_, ok := ctx.Value(ctxKeyOnInsecureDebug).(struct{})
return ok
}
type RemoveInsecureDebugFilter struct{}
func NewRemoveInsecureDebugFilter() *RemoveInsecureDebugFilter {
return &RemoveInsecureDebugFilter{}
}
var _ logger.Hook = (*RemoveInsecureDebugFilter)(nil)
func (f *RemoveInsecureDebugFilter) ProcessLogEntry(entry *logger.Entry) bool {
return entry.Fields.ForEachField(func(f *field.Field) bool {
return f.Value != ctxKeyOnInsecureDebug
})
}
func (f *RemoveInsecureDebugFilter) Flush() {}

View File

@@ -1,97 +0,0 @@
package observability
import (
"context"
"strings"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/pkg/field"
"github.com/facebookincubator/go-belt/pkg/runtime"
loggertypes "github.com/facebookincubator/go-belt/tool/logger/types"
"github.com/xaionaro-go/streamctl/pkg/xsync"
)
var LogLevelFilter LogLevelFilterT
type LogLevelFilterT struct {
Locker xsync.Mutex
Level loggertypes.Level
}
var _ loggertypes.PreHook = (*LogLevelFilterT)(nil)
func (h *LogLevelFilterT) GetLevel() loggertypes.Level {
ctx := xsync.WithNoLogging(context.TODO(), true)
return xsync.DoR1(ctx, &h.Locker, func() loggertypes.Level {
return h.Level
})
}
func (h *LogLevelFilterT) SetLevel(
level loggertypes.Level,
) {
ctx := xsync.WithNoLogging(context.TODO(), true)
h.Locker.Do(ctx, func() {
h.Level = level
})
}
func (h *LogLevelFilterT) ProcessInput(
traceIDs belt.TraceIDs,
level loggertypes.Level,
args ...any,
) loggertypes.PreHookResult {
ctx := xsync.WithNoLogging(context.TODO(), true)
return xsync.DoR1(ctx, &h.Locker, func() loggertypes.PreHookResult {
if !h.shouldLog(level) {
return loggertypes.PreHookResult{Skip: true}
}
return loggertypes.PreHookResult{}
})
}
func (h *LogLevelFilterT) ProcessInputf(
traceIDs belt.TraceIDs,
level loggertypes.Level,
format string,
args ...any,
) loggertypes.PreHookResult {
ctx := xsync.WithNoLogging(context.TODO(), true)
return xsync.DoR1(ctx, &h.Locker, func() loggertypes.PreHookResult {
if !h.shouldLog(level) {
return loggertypes.PreHookResult{Skip: true}
}
return loggertypes.PreHookResult{}
})
}
func (h *LogLevelFilterT) ProcessInputFields(
traceIDs belt.TraceIDs,
level loggertypes.Level,
message string,
fields field.AbstractFields,
) loggertypes.PreHookResult {
ctx := xsync.WithNoLogging(context.TODO(), true)
return xsync.DoR1(ctx, &h.Locker, func() loggertypes.PreHookResult {
if !h.shouldLog(level) {
return loggertypes.PreHookResult{Skip: true}
}
return loggertypes.PreHookResult{}
})
}
func (h *LogLevelFilterT) shouldLog(level loggertypes.Level) bool {
if level > h.Level {
return false
}
pcs := runtime.CallerStackTrace(runtime.DefaultCallerPCFilter)
for _, pc := range pcs {
file, _ := pc.FileLine()
if strings.Contains(file, "xaionaro-go/kickcom") {
if level == h.Level {
return false
}
}
}
return true
}

View File

@@ -1,75 +0,0 @@
package observability
import (
"time"
"github.com/facebookincubator/go-belt/pkg/field"
xlogrus "github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
logger "github.com/facebookincubator/go-belt/tool/logger/types"
"github.com/sirupsen/logrus"
"github.com/xaionaro-go/streamctl/pkg/xsync"
)
type HookAdapter struct {
Locker xsync.Mutex
LogrusLogger *logrus.Logger
LogrusHook logrus.Hook
}
func NewHookAdapter(
l *logrus.Logger,
h logrus.Hook,
) logger.Hook {
return &HookAdapter{
LogrusLogger: l,
LogrusHook: h,
}
}
func (h *HookAdapter) ProcessLogEntry(entry *logger.Entry) bool {
fields := logrus.Fields{}
entry.Fields.ForEachField(func(f *field.Field) bool {
fields[f.Key] = f.Value
return true
})
h.LogrusHook.Fire(&logrus.Entry{
Logger: h.LogrusLogger,
Data: fields,
Time: entry.Timestamp,
Level: xlogrus.LevelToLogrus(entry.Level),
Caller: entry.Caller.Frame(),
Message: entry.Message,
Buffer: nil,
})
return true
}
type Flusher interface {
Flush()
}
type FlusherErr interface {
Flush() error
}
type FlusherDeadlined interface {
Flush(timeout time.Duration)
}
type FlusherDeadlinedErr interface {
Flush(timeout time.Duration) error
}
func (h *HookAdapter) Flush() {
timeout := 5 * time.Second
switch flusher := h.LogrusHook.(type) {
case Flusher:
flusher.Flush()
case FlusherErr:
flusher.Flush()
case FlusherDeadlined:
flusher.Flush(timeout)
case FlusherDeadlinedErr:
flusher.Flush(timeout)
}
}

View File

@@ -1,40 +0,0 @@
package observability
import (
"context"
"net/url"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
"github.com/xaionaro-go/logrustash"
)
func CtxWithLogstash(
ctx context.Context,
logstashAddr string,
appName string,
) context.Context {
addr, err := url.Parse(logstashAddr)
if err != nil {
logger.Errorf(ctx, "unable to parse '%s' as URL: %v", logstashAddr, err)
return ctx
}
hook, err := logrustash.NewHook(addr.Scheme, addr.Host, appName)
if err != nil {
logger.Errorf(ctx, "unable to initialize the hook: %v", err)
return ctx
}
l := logger.FromCtx(ctx)
emitter, ok := l.Emitter().(*logrus.Emitter)
if !ok {
logger.Errorf(ctx, "the Emitter is not a *logrus.Emitter, but %T", l.Emitter())
return ctx
}
l = l.WithHooks(NewHookAdapter(
emitter.LogrusEntry.Logger,
hook,
))
return logger.CtxWithLogger(ctx, l)
}

View File

@@ -1,14 +0,0 @@
package observability
import logger "github.com/facebookincubator/go-belt/tool/logger/types"
type mockEmitter struct {
LastEntry *logger.Entry
}
var _ logger.Emitter = (*mockEmitter)(nil)
func (e *mockEmitter) Emit(entry *logger.Entry) {
e.LastEntry = entry
}
func (e *mockEmitter) Flush() {}

View File

@@ -1,33 +0,0 @@
package observability
import (
"context"
"fmt"
"runtime/debug"
"time"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/experimental/errmon"
"github.com/facebookincubator/go-belt/tool/logger"
)
func PanicIfNotNil(ctx context.Context, r any) {
if r == nil {
return
}
ReportPanicIfNotNil(ctx, r)
time.Sleep(time.Second)
panic(fmt.Sprintf("%#+v", r))
}
func ReportPanicIfNotNil(ctx context.Context, r any) bool {
if r == nil {
return false
}
logger.FromCtx(ctx).
WithField("error_event_exception_stack_trace", string(debug.Stack())).
Errorf("got panic: %v", r)
errmon.ObserveRecoverCtx(ctx, r)
belt.Flush(ctx)
return true
}

View File

@@ -1,148 +0,0 @@
package observability
import (
"context"
"reflect"
"strings"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/pkg/field"
"github.com/facebookincubator/go-belt/tool/logger"
loggertypes "github.com/facebookincubator/go-belt/tool/logger/types"
"github.com/xaionaro-go/object"
)
type SecretsProvider interface {
SecretWords() []string
}
type ctxKeyT uint
const (
ctxKeySecretsProvider = ctxKeyT(iota)
)
func WithSecretsProvider(ctx context.Context, secretsProvider SecretsProvider) context.Context {
return context.WithValue(ctx, ctxKeySecretsProvider, secretsProvider)
}
func SecretsProviderFromCtx(ctx context.Context) SecretsProvider {
v := ctx.Value(ctxKeySecretsProvider)
if v, ok := v.(SecretsProvider); ok {
return v
}
return nil
}
type SecretValuesFilter struct {
SecretsProvider SecretsProvider
}
func NewSecretValuesFilter(sp SecretsProvider) *SecretValuesFilter {
return &SecretValuesFilter{
SecretsProvider: sp,
}
}
var _ logger.PreHook = (*SecretValuesFilter)(nil)
func (sf *SecretValuesFilter) ProcessInput(
_ belt.TraceIDs,
_ logger.Level,
args ...any,
) loggertypes.PreHookResult {
for idx, arg := range args {
args[idx] = filterSecretValues(sf, arg)
}
return loggertypes.PreHookResult{}
}
func (sf *SecretValuesFilter) ProcessInputf(
_ belt.TraceIDs,
_ logger.Level,
format string,
args ...any,
) loggertypes.PreHookResult {
if censored := filterSecretValues(sf, format); censored != format {
logger.Errorf(context.TODO(), "secrets are leaking through the logging format message: %s", censored)
}
for idx, arg := range args {
args[idx] = filterSecretValues(sf, arg)
}
return loggertypes.PreHookResult{}
}
func (sf *SecretValuesFilter) ProcessInputFields(
_ belt.TraceIDs,
_ logger.Level,
message string,
fields field.AbstractFields,
) loggertypes.PreHookResult {
if censored := filterSecretValues(sf, message); censored != message {
logger.Errorf(context.TODO(), "secrets are leaking through the logging message: %s", censored)
}
fields.ForEachField(func(f *field.Field) bool {
f.Value = filterSecretValues(sf, f.Value)
return true
})
return loggertypes.PreHookResult{}
}
func filterSecretValues[T any](
sf *SecretValuesFilter,
in T,
) T {
return object.DeepCopy(in, object.OptionWithVisitorFunc(sf.filterSecretValuesInLeaf))
}
func (sf *SecretValuesFilter) filterSecretValuesInLeaf(
ctx *object.ProcContext,
v reflect.Value,
f *reflect.StructField,
) (reflect.Value, bool, error) {
switch v.Kind() {
case reflect.Pointer:
if v.IsNil() {
return v, false, nil
}
t := v.Type()
result := reflect.New(t).Elem()
result.Set(reflect.New(t.Elem())) // result = (*T)(nil)
newV, _, err := sf.filterSecretValuesInLeaf(ctx, v.Elem(), f)
if err != nil {
return result, false, err
}
result.Elem().Set(newV) // *result = *v
return result, false, nil
case reflect.String:
return reflect.ValueOf(filterSecretValuesInString(sf, v.String())).Convert(v.Type()), false, nil
case reflect.Slice:
if v.Type().Elem().Kind() != reflect.Uint8 {
return v, true, nil
}
orig := string(v.Bytes())
censored := filterSecretValuesInString(sf, orig)
if orig == censored {
return v, false, nil
}
return reflect.ValueOf([]byte(censored)).Convert(v.Type()), false, nil
default:
return v, true, nil
}
}
func filterSecretValuesInString(
sf *SecretValuesFilter,
s string,
) string {
secretWords := sf.SecretsProvider.SecretWords()
for _, secret := range secretWords {
if len(secret) == 0 {
continue
}
s = strings.ReplaceAll(s, secret, "<HIDDEN>")
}
return s
}

View File

@@ -1,85 +0,0 @@
package observability
import (
"context"
"reflect"
"github.com/xaionaro-go/object"
"github.com/xaionaro-go/streamctl/pkg/xsync"
)
type SecretsStaticProvider struct {
xsync.Mutex
SecretWordValues []string
}
var _ SecretsProvider = (*SecretsStaticProvider)(nil)
func NewStaticSecretsProvider() *SecretsStaticProvider {
return &SecretsStaticProvider{}
}
func (sp *SecretsStaticProvider) SetSecretWords(v []string) {
sp.Do(xsync.WithNoLogging(context.TODO(), true), func() {
sp.SecretWordValues = v
})
}
func (sp *SecretsStaticProvider) SecretWords() []string {
return xsync.DoR1(xsync.WithNoLogging(context.TODO(), true), &sp.Mutex, func() []string {
return sp.SecretWordValues
})
}
func (sp *SecretsStaticProvider) ParseSecretsFrom(obj any) {
secrets := ParseSecretsFrom(obj)
sp.SetSecretWords(secrets)
}
func ParseSecretsFrom(obj any) []string {
var secrets []string
object.Traverse(obj, func(ctx *object.ProcContext, v reflect.Value, sf *reflect.StructField) (reflect.Value, bool, error) {
if v.Kind() != reflect.Struct {
return v, true, nil
}
encryptedField := v.FieldByName("encryptedMessage")
if !encryptedField.IsValid() {
return v, true, nil
}
getSecretFunc := v.MethodByName("Get")
if !getSecretFunc.IsValid() {
return v, true, nil
}
// is a secret...
secretValue := getSecretFunc.Call([]reflect.Value{})[0]
if !secretValue.IsValid() {
return v, false, nil
}
secrets = append(secrets, ParseStringsFrom(secretValue.Interface())...)
return v, false, nil
})
return secrets
}
func ParseStringsFrom(obj any) []string {
var strings []string
object.Traverse(
obj,
func(
ctx *object.ProcContext,
v reflect.Value,
sf *reflect.StructField,
) (reflect.Value, bool, error) {
switch v.Kind() {
case reflect.String:
if v.String() != "" {
strings = append(strings, v.String())
}
}
return v, true, nil
},
)
return strings
}

View File

@@ -1,37 +0,0 @@
package observability_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/secret"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
"github.com/xaionaro-go/streamctl/pkg/streamd/config"
"golang.org/x/oauth2"
)
func TestParseSecretsFrom(t *testing.T) {
sample := config.Config{
GitRepo: config.GitRepoConfig{
PrivateKey: secret.New("1"),
},
Backends: map[streamcontrol.PlatformName]*streamcontrol.AbstractPlatformConfig{
youtube.ID: {
Config: &youtube.PlatformSpecificConfig{
ChannelID: "2",
ClientID: "3",
ClientSecret: secret.New("4"),
Token: ptr(secret.New(oauth2.Token{
AccessToken: "5",
TokenType: "6",
RefreshToken: "7",
})),
},
},
},
}
secrets := observability.ParseSecretsFrom(sample)
require.Equal(t, []string{"1", "4", "5", "6", "7"}, secrets)
}

View File

@@ -1,77 +0,0 @@
package observability
import (
"reflect"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/pkg/field"
"github.com/facebookincubator/go-belt/tool/logger"
loggertypes "github.com/facebookincubator/go-belt/tool/logger/types"
"github.com/xaionaro-go/object"
)
type StructFieldSecretsFilter struct{}
var _ logger.PreHook = (*StructFieldSecretsFilter)(nil)
var errorReflectValue = reflect.TypeOf((*error)(nil)).Elem()
func copyWithoutSecrets(in any) any {
return object.DeepCopyWithoutSecrets(
in,
object.OptionWithVisitorFunc(
func(
ctx *object.ProcContext,
v reflect.Value,
sf *reflect.StructField,
) (reflect.Value, bool, error) {
t := v.Type()
if t.Kind() == reflect.Interface {
vElem := v.Elem()
if !vElem.IsValid() {
return v, false, nil
}
if vElem.Type().Implements(errorReflectValue) {
return v, false, nil
}
}
return v, true, nil
},
),
)
}
func (StructFieldSecretsFilter) ProcessInput(
_ belt.TraceIDs,
_ logger.Level,
args ...any,
) loggertypes.PreHookResult {
for idx := range args {
args[idx] = copyWithoutSecrets(args[idx])
}
return loggertypes.PreHookResult{}
}
func (StructFieldSecretsFilter) ProcessInputf(
_ belt.TraceIDs,
_ logger.Level,
format string,
args ...any,
) loggertypes.PreHookResult {
for idx := range args {
args[idx] = copyWithoutSecrets(args[idx])
}
return loggertypes.PreHookResult{}
}
func (StructFieldSecretsFilter) ProcessInputFields(
_ belt.TraceIDs,
_ logger.Level,
message string,
fields field.AbstractFields,
) loggertypes.PreHookResult {
fields.ForEachField(func(f *field.Field) bool {
f.Value = copyWithoutSecrets(f.Value)
return true
})
return loggertypes.PreHookResult{}
}

View File

@@ -1,48 +0,0 @@
package observability_test
import (
"bytes"
"fmt"
"runtime"
"testing"
"github.com/facebookincubator/go-belt/tool/logger"
xlogrus "github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/secret"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol/youtube"
)
func TestSecretsFilter(t *testing.T) {
ll := xlogrus.DefaultLogrusLogger()
ll.Formatter.(*logrus.TextFormatter).TimestampFormat = "unit-test"
ll.Formatter.(*logrus.TextFormatter).CallerPrettyfier = func(f *runtime.Frame) (function string, file string) {
return "", ""
}
l := xlogrus.New(ll).WithLevel(logger.LevelTrace).WithPreHooks(
observability.StructFieldSecretsFilter{},
)
t.Run("youtubeConfig", func(t *testing.T) {
var buf bytes.Buffer
ll.SetOutput(&buf)
sample := youtube.PlatformSpecificConfig{
ChannelID: "someChannel",
ClientID: "someID",
ClientSecret: secret.New("someSecret"),
}
l.Debugf("%#+v", sample)
require.Equal(t, `time=unit-test level=debug msg="youtube.PlatformSpecificConfig{ChannelID:\"someChannel\", ClientID:\"someID\", ClientSecret:HIDDEN{}, Token:<nil>, CustomOAuthHandler:(youtube.OAuthHandler)(nil), GetOAuthListenPorts:(func() []uint16)(nil)}"`+"\n", buf.String())
})
t.Run("error", func(t *testing.T) {
e0 := fmt.Errorf("some error")
e1 := fmt.Errorf("the parent error: %w", e0)
var buf bytes.Buffer
ll.SetOutput(&buf)
l.Debugf("%v", e1)
require.Equal(t, `time=unit-test level=debug msg="the parent error: some error"`+"\n", buf.String())
})
}

View File

@@ -1,3 +0,0 @@
package observability_test
func ptr[T any](v T) *T { return &v }

View File

@@ -19,8 +19,8 @@ import (
"github.com/pojntfx/weron/pkg/services" "github.com/pojntfx/weron/pkg/services"
"github.com/pojntfx/weron/pkg/wrtcconn" "github.com/pojntfx/weron/pkg/wrtcconn"
"github.com/pojntfx/weron/pkg/wrtcip" "github.com/pojntfx/weron/pkg/wrtcip"
"github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/consts" "github.com/xaionaro-go/streamctl/pkg/consts"
"github.com/xaionaro-go/streamctl/pkg/observability"
p2pconsts "github.com/xaionaro-go/streamctl/pkg/p2p/implementations/weron/consts" p2pconsts "github.com/xaionaro-go/streamctl/pkg/p2p/implementations/weron/consts"
"github.com/xaionaro-go/streamctl/pkg/p2p/types" "github.com/xaionaro-go/streamctl/pkg/p2p/types"
"github.com/xaionaro-go/streamctl/pkg/secret" "github.com/xaionaro-go/streamctl/pkg/secret"

View File

@@ -9,7 +9,7 @@ import (
"github.com/pojntfx/weron/pkg/wrtcconn" "github.com/pojntfx/weron/pkg/wrtcconn"
"github.com/xaionaro-go/grpcproxy/grpchttpproxy" "github.com/xaionaro-go/grpcproxy/grpchttpproxy"
"github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc" "github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/p2p/implementations/weron/protobuf/go/p2p_grpc" "github.com/xaionaro-go/streamctl/pkg/p2p/implementations/weron/protobuf/go/p2p_grpc"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"

View File

@@ -10,7 +10,7 @@ import (
"github.com/pojntfx/weron/pkg/wrtcconn" "github.com/pojntfx/weron/pkg/wrtcconn"
"github.com/xaionaro-go/grpcproxy/grpcproxyserver" "github.com/xaionaro-go/grpcproxy/grpcproxyserver"
"github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc" "github.com/xaionaro-go/grpcproxy/protobuf/go/proxy_grpc"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/p2p/implementations/weron/protobuf/go/p2p_grpc" "github.com/xaionaro-go/streamctl/pkg/p2p/implementations/weron/protobuf/go/p2p_grpc"
"google.golang.org/grpc" "google.golang.org/grpc"
) )

View File

@@ -2,7 +2,7 @@ package builtin
import ( import (
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/xaionaro-go/streamctl/pkg/audio" "github.com/xaionaro-go/audio/pkg/audio"
) )
func pcmFormatToAudio(libavPCMFormat astiav.SampleFormat) audio.PCMFormat { func pcmFormatToAudio(libavPCMFormat astiav.SampleFormat) audio.PCMFormat {

View File

@@ -11,10 +11,10 @@ import (
"time" "time"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/audio" "github.com/xaionaro-go/audio/pkg/audio"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
const ( const (

View File

@@ -5,7 +5,7 @@ import (
"io" "io"
"time" "time"
"github.com/xaionaro-go/streamctl/pkg/audio" "github.com/xaionaro-go/audio/pkg/audio"
) )
type ImageRenderer interface { type ImageRenderer interface {

View File

@@ -6,7 +6,7 @@ import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas" "fyne.io/fyne/v2/canvas"
"github.com/xaionaro-go/streamctl/pkg/audio" "github.com/xaionaro-go/audio/pkg/audio"
) )
type WindowRenderer struct { type WindowRenderer struct {

View File

@@ -18,11 +18,11 @@ import (
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus" "github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
"github.com/spf13/pflag" "github.com/spf13/pflag"
_ "github.com/xaionaro-go/streamctl/pkg/audio/backends/oto" _ "github.com/xaionaro-go/audio/pkg/audio/backends/oto"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
//_ "github.com/xaionaro-go/streamctl/pkg/audio/backends/pulseaudio" //_ "github.com/xaionaro-go/audio/pkg/audio/backends/pulseaudio"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/player" "github.com/xaionaro-go/streamctl/pkg/player"
"github.com/xaionaro-go/streamctl/pkg/player/types" "github.com/xaionaro-go/streamctl/pkg/player/types"
"github.com/xaionaro-go/streamctl/pkg/xfyne" "github.com/xaionaro-go/streamctl/pkg/xfyne"

View File

@@ -6,7 +6,7 @@ import (
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/player/types" "github.com/xaionaro-go/streamctl/pkg/player/types"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type Manager struct { type Manager struct {

View File

@@ -17,10 +17,10 @@ import (
"github.com/blang/mpv" "github.com/blang/mpv"
"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/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/logwriter" "github.com/xaionaro-go/streamctl/pkg/logwriter"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/xpath" "github.com/xaionaro-go/streamctl/pkg/xpath"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
const SupportedMPV = true const SupportedMPV = true

View File

@@ -10,7 +10,7 @@ import (
"time" "time"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/player/protobuf/go/player_grpc" "github.com/xaionaro-go/streamctl/pkg/player/protobuf/go/player_grpc"
"github.com/xaionaro-go/streamctl/pkg/player/types" "github.com/xaionaro-go/streamctl/pkg/player/types"
"google.golang.org/grpc" "google.golang.org/grpc"

View File

@@ -12,7 +12,7 @@ import (
vlc "github.com/adrg/libvlc-go/v3" vlc "github.com/adrg/libvlc-go/v3"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
"golang.org/x/net/context" "golang.org/x/net/context"
) )

View File

@@ -13,7 +13,7 @@ import (
"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus" "github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
"github.com/xaionaro-go/streamctl/pkg/player/protobuf/go/player_grpc" "github.com/xaionaro-go/streamctl/pkg/player/protobuf/go/player_grpc"
"github.com/xaionaro-go/streamctl/pkg/player/vlcserver/player" "github.com/xaionaro-go/streamctl/pkg/player/vlcserver/player"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
"google.golang.org/grpc" "google.golang.org/grpc"
) )

View File

@@ -9,7 +9,7 @@ import (
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
) )
type TCPProxy struct { type TCPProxy struct {

View File

@@ -6,7 +6,7 @@ import (
"github.com/gwuhaolin/livego/protocol/rtmp/rtmprelay" "github.com/gwuhaolin/livego/protocol/rtmp/rtmprelay"
"github.com/xaionaro-go/streamctl/pkg/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type Encoder struct { type Encoder struct {

View File

@@ -17,7 +17,6 @@ type InputConfig struct {
CustomOptions []CustomOption CustomOptions []CustomOption
} }
type Input struct { type Input struct {
ID InputID ID InputID
*astikit.Closer *astikit.Closer

View File

@@ -12,7 +12,7 @@ import (
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder/types" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder/types"
) )

View File

@@ -9,7 +9,7 @@ import (
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
) )
type loopStreamOutput struct { type loopStreamOutput struct {

View File

@@ -10,7 +10,7 @@ import (
"github.com/asticode/go-astiav" "github.com/asticode/go-astiav"
"github.com/asticode/go-astikit" "github.com/asticode/go-astikit"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/proxy" "github.com/xaionaro-go/streamctl/pkg/proxy"
"github.com/xaionaro-go/streamctl/pkg/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder"
) )
@@ -23,7 +23,6 @@ type OutputConfig struct {
CustomOptions []CustomOption CustomOptions []CustomOption
} }
type Output struct { type Output struct {
ID OutputID ID OutputID
*astikit.Closer *astikit.Closer

View File

@@ -8,7 +8,7 @@ import (
"reflect" "reflect"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/goconv" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/goconv"

View File

@@ -14,7 +14,7 @@ import (
child_process_manager "github.com/AgustinSRG/go-child-process-manager" child_process_manager "github.com/AgustinSRG/go-child-process-manager"
"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/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process/client" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/process/client"
"github.com/xaionaro-go/streamctl/pkg/xpath" "github.com/xaionaro-go/streamctl/pkg/xpath"

View File

@@ -11,7 +11,7 @@ import (
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/recoder"
"github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc" "github.com/xaionaro-go/streamctl/pkg/recoder/libav/saferecoder/grpc/go/recoder_grpc"
"github.com/xaionaro-go/streamctl/pkg/xcontext" "github.com/xaionaro-go/streamctl/pkg/xcontext"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
"google.golang.org/grpc" "google.golang.org/grpc"
) )

View File

@@ -12,7 +12,7 @@ import (
"github.com/xaionaro-go/streamctl/pkg/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder"
xaionarogortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp" xaionarogortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp"
"github.com/xaionaro-go/streamctl/pkg/streamserver/types" "github.com/xaionaro-go/streamctl/pkg/streamserver/types"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type Input struct { type Input struct {

View File

@@ -12,7 +12,7 @@ import (
"github.com/xaionaro-go/go-rtmp" "github.com/xaionaro-go/go-rtmp"
rtmpmsg "github.com/xaionaro-go/go-rtmp/message" rtmpmsg "github.com/xaionaro-go/go-rtmp/message"
"github.com/xaionaro-go/streamctl/pkg/recoder" "github.com/xaionaro-go/streamctl/pkg/recoder"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
) )
type Output struct { type Output struct {

View File

@@ -13,7 +13,7 @@ import (
rtmpmsg "github.com/xaionaro-go/go-rtmp/message" rtmpmsg "github.com/xaionaro-go/go-rtmp/message"
recoder "github.com/xaionaro-go/streamctl/pkg/recoder" recoder "github.com/xaionaro-go/streamctl/pkg/recoder"
yutoppgortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp" yutoppgortmp "github.com/xaionaro-go/streamctl/pkg/streamserver/implementations/xaionaro-go-rtmp"
"github.com/xaionaro-go/streamctl/pkg/xsync" "github.com/xaionaro-go/xsync"
flvtag "github.com/yutopp/go-flv/tag" flvtag "github.com/yutopp/go-flv/tag"
) )

View File

@@ -2,69 +2,18 @@ package secret
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"github.com/goccy/go-yaml" "github.com/xaionaro-go/secret"
"gopkg.in/yaml.v2"
) )
// Any stores data in an encrypted state in memory, so that
// you don't accidentally leak these secrets via logging or whatever.
type Any[T any] struct { type Any[T any] struct {
encryptedMessage secret.Any[T]
plainText T
} }
func New[T any](in T) Any[T] { func New[T any](in T) Any[T] {
var s Any[T] return Any[T]{Any: secret.New(in)}
s.Set(in)
return s
}
func (s Any[T]) Get() T {
if len(s.encryptedMessage.data) == 0 {
var zeroValue T
return zeroValue
}
b := decrypt(s.encryptedMessage)
var t T
err := json.Unmarshal(b, &t)
if err != nil {
panic(err)
}
return t
}
func (s *Any[T]) GetPointer() *T {
if s == nil {
return nil
}
return ptr(s.Get())
}
func (s *Any[T]) Set(v T) {
b, err := json.Marshal(v)
if err != nil {
panic(err)
}
s.encryptedMessage = encrypt(b)
if !secrecyEnabled {
s.plainText = v
}
}
func (s *Any[T]) String() string {
if secrecyEnabled {
return "<HIDDEN>"
}
return fmt.Sprintf("%#v", s.plainText)
}
func (s Any[T]) GoString() string {
if secrecyEnabled {
return "<HIDDEN>"
}
return fmt.Sprintf("%#+v", s.plainText)
} }
func (s Any[T]) MarshalYAML() (_ret []byte, _err error) { func (s Any[T]) MarshalYAML() (_ret []byte, _err error) {

View File

@@ -1,63 +0,0 @@
package secret
import (
"crypto/cipher"
"crypto/rand"
"fmt"
"golang.org/x/crypto/chacha20poly1305"
)
const (
keyLength = 32
)
var encryptor cipher.AEAD
func init() {
ephemeralKey := make([]byte, keyLength)
n, err := rand.Read(ephemeralKey)
if err != nil {
panic(err)
}
if n != keyLength {
panic(fmt.Errorf("%d != %d", n, keyLength))
}
encryptor, err = chacha20poly1305.NewX(ephemeralKey)
if err != nil {
panic(err)
}
}
type encryptedMessage struct {
nonce []byte
data []byte
}
func (encryptedMessage) String() string {
return "<HIDDEN>"
}
func (encryptedMessage) GoString() string {
return "HIDDEN{}"
}
func encrypt(in []byte) encryptedMessage {
nonce := make([]byte, encryptor.NonceSize())
_, err := rand.Read(nonce)
if err != nil {
panic(err)
}
return encryptedMessage{
nonce: nonce,
data: encryptor.Seal(nil, nonce, in, nil),
}
}
func decrypt(in encryptedMessage) []byte {
b, err := encryptor.Open(nil, in.nonce, in.data, nil)
if err != nil {
panic(err)
}
return b
}

View File

@@ -1,7 +0,0 @@
package secret
var secrecyEnabled = true
func SetSecrecy(secrecy bool) {
secrecyEnabled = secrecy
}

View File

@@ -1,5 +1,3 @@
package secret package secret
// String stores a string in an encrypted state in memory, so that
// you don't accidentally leak these secrets via logging or whatever.
type String = Any[string] type String = Any[string]

View File

@@ -1,5 +0,0 @@
package secret
func ptr[T any](in T) *T {
return &in
}

View File

@@ -1,78 +0,0 @@
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net"
"os"
"github.com/facebookincubator/go-belt"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
"github.com/spf13/pflag"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/speech/speechtotext/whisper"
)
func syntaxExit(message string) {
fmt.Fprintf(os.Stderr, "syntax error: %s\n", message)
pflag.Usage()
os.Exit(2)
}
func main() {
loggerLevel := logger.LevelDebug
pflag.Var(&loggerLevel, "log-level", "Log level")
pflag.Parse()
if pflag.NArg() != 1 {
syntaxExit("expected one argument (address of the server)")
}
whisperSrvAddr := pflag.Arg(0)
l := logrus.Default().WithLevel(loggerLevel)
ctx := logger.CtxWithLogger(context.Background(), l)
logger.Default = func() logger.Logger {
return l
}
defer belt.Flush(ctx)
conn, err := net.Dial("tcp", whisperSrvAddr)
if err != nil {
logger.Panicf(ctx, "unable to connect to whisper by address '%s': %v", whisperSrvAddr, err)
}
stt := whisper.New(ctx, conn, true)
defer stt.Close()
observability.Go(ctx, func() {
defer logger.Infof(ctx, "stopped reader")
logger.Infof(ctx, "started reader")
for t := range stt.OutputChan() {
var out bytes.Buffer
enc := json.NewEncoder(&out)
enc.SetIndent("", " ")
err := enc.Encode(t)
if err != nil {
logger.Panic(ctx, err)
}
fmt.Println(out.String())
}
})
defer logger.Infof(ctx, "stopped writer")
logger.Infof(ctx, "started writer")
buf := make([]byte, 1024*1024)
for {
n, err := os.Stdin.Read(buf)
if n == 0 && err != nil {
break
}
err = stt.WriteAudio(ctx, buf[:n])
if err != nil {
logger.Panic(ctx, err)
}
}
}

View File

@@ -1,8 +0,0 @@
package speech
type Language string
const (
LanguageEnglishUS = "en-US"
LanguageRussian = "ru-RU"
)

View File

@@ -1,24 +0,0 @@
package googleapiv1
import (
"fmt"
"cloud.google.com/go/speech/apiv1/speechpb"
"github.com/xaionaro-go/streamctl/pkg/audio"
)
func AudioEncodingToThrift(
audioEncoding audio.Encoding,
) (speechpb.RecognitionConfig_AudioEncoding, int32, error) {
switch audioEncoding := audioEncoding.(type) {
case audio.AudioEncodingPCM:
switch audioEncoding.PCMFormat {
case audio.PCMFormatS16LE:
return speechpb.RecognitionConfig_LINEAR16, int32(audioEncoding.SampleRate), nil
default:
return 0, 0, fmt.Errorf("google Speech APIv1 does not support PCM format %s", audioEncoding.PCMFormat) // the linter complains if I write "Google" from the capital "G" here...
}
default:
return 0, 0, fmt.Errorf("do not know how to convert %T", audioEncoding)
}
}

View File

@@ -1,189 +0,0 @@
package googleapiv1
import (
"context"
"fmt"
"io"
"sync"
"sync/atomic"
googleapi "cloud.google.com/go/speech/apiv1"
"cloud.google.com/go/speech/apiv1/speechpb"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/hashicorp/go-multierror"
"github.com/xaionaro-go/streamctl/pkg/audio"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/speech"
)
type SpeechToText struct {
closeCount atomic.Uint64
wg sync.WaitGroup
audioEncoding audio.Encoding
audioChannels audio.Channel
googleAPI *googleapi.Client
stream speechpb.Speech_StreamingRecognizeClient
cancelFunc context.CancelFunc
resultQueue chan *speech.Transcript
}
var _ speech.ToText = (*SpeechToText)(nil)
func New(
ctx context.Context,
language speech.Language,
audioEncoding audio.Encoding,
audioChannels audio.Channel,
) (*SpeechToText, error) {
thriftAudioEncoding, thriftSampleRate, err := AudioEncodingToThrift(audioEncoding)
if err != nil {
return nil, fmt.Errorf("unable to convert the audio encoding for Google API: %w", err)
}
ctx, cancelFunc := context.WithCancel(ctx)
googleAPI, err := googleapi.NewClient(ctx)
if err != nil {
cancelFunc()
return nil, fmt.Errorf("unable to initialize a client to Google Speech API: %w", err)
}
stream, err := googleAPI.StreamingRecognize(ctx)
if err != nil {
cancelFunc()
return nil, fmt.Errorf("unable to initialize a recognition stream: %w", err)
}
err = stream.Send(&speechpb.StreamingRecognizeRequest{
StreamingRequest: &speechpb.StreamingRecognizeRequest_StreamingConfig{
StreamingConfig: &speechpb.StreamingRecognitionConfig{
Config: &speechpb.RecognitionConfig{
Encoding: thriftAudioEncoding,
SampleRateHertz: thriftSampleRate,
LanguageCode: string(language),
AudioChannelCount: int32(audioChannels),
},
},
},
})
if err != nil {
cancelFunc()
return nil, fmt.Errorf("unable to send the configuration to the stream: %w", err)
}
stt := &SpeechToText{
audioEncoding: audioEncoding,
audioChannels: audioChannels,
googleAPI: googleAPI,
stream: stream,
resultQueue: make(chan *speech.Transcript, 1024),
cancelFunc: cancelFunc,
}
stt.wg.Add(1)
observability.Go(ctx, func() {
defer stt.wg.Done()
defer stt.Close()
err := stt.loop(ctx)
if err != nil {
logger.Errorf(ctx, "stt.loop returned error: %v", err)
}
})
return stt, nil
}
func (stt *SpeechToText) AudioEncoding() audio.Encoding {
return stt.audioEncoding
}
func (stt *SpeechToText) AudioChannels() audio.Channel {
return stt.audioChannels
}
func (stt *SpeechToText) loop(ctx context.Context) (_err error) {
logger.Debugf(ctx, "stt.loop()")
defer func() { logger.Debugf(ctx, "/stt.loop(): %v", _err) }()
for {
resp, err := stt.stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("unable to read a result from the stream: %w", err)
}
if resp == nil {
return fmt.Errorf("resp == nil")
}
if resp.Error != nil {
return fmt.Errorf("resp.Error != nil: %d: %s", resp.Error.GetCode(), resp.Error.GetMessage())
}
for _, result := range resp.GetResults() {
transcript := speech.Transcript{
IsFinal: result.GetIsFinal(),
}
for _, alt := range result.GetAlternatives() {
logger.Debugf(ctx, "confidence: %.3f, transcript: <%s>", alt.GetTranscript())
variant := speech.TranscriptVariant{
Confidence: alt.GetConfidence(),
}
for _, word := range alt.Words {
variant.TranscriptWords = append(
variant.TranscriptWords,
speech.TranscriptWord{
StartTime: word.GetStartTime().AsDuration(),
EndTime: word.GetEndTime().AsDuration(),
Text: word.GetWord(),
Confidence: word.GetConfidence(),
Speaker: word.GetSpeakerLabel(),
},
)
}
transcript.Variants = append(transcript.Variants, variant)
}
stt.resultQueue <- &transcript
}
}
}
func (stt *SpeechToText) WriteAudio(
ctx context.Context,
audio []byte,
) error {
err := stt.stream.Send(&speechpb.StreamingRecognizeRequest{
StreamingRequest: &speechpb.StreamingRecognizeRequest_AudioContent{
AudioContent: audio,
},
})
if err != nil {
return fmt.Errorf("unable to write audio: %w", err)
}
return nil
}
func (stt *SpeechToText) OutputChan() <-chan *speech.Transcript {
return stt.resultQueue
}
func (stt *SpeechToText) Close() error {
if stt.closeCount.Add(1) != 1 {
return fmt.Errorf("already closed")
}
stt.cancelFunc()
stt.cancelFunc = nil
var mErr *multierror.Error
mErr = multierror.Append(mErr, stt.stream.CloseSend())
mErr = multierror.Append(mErr, stt.googleAPI.Close())
stt.waitForClosure()
close(stt.resultQueue)
return mErr.ErrorOrNil()
}
func (stt *SpeechToText) waitForClosure() {
stt.wg.Wait()
}

View File

@@ -1,100 +0,0 @@
package speechtotext
import (
"context"
"fmt"
"sync"
googleapi "cloud.google.com/go/speech/apiv2"
"cloud.google.com/go/speech/apiv2/speechpb"
"github.com/xaionaro-go/streamctl/pkg/audio"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/speech"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
type SpeechToText struct {
wg sync.WaitGroup
googleAPI *googleapi.Client
cancelFunc context.CancelFunc
opQueue chan *googleapi.BatchRecognizeOperation
}
var _ speech.ToText = (*SpeechToText)(nil)
func New() (*SpeechToText, error) {
ctx := context.Background()
ctx, cancelFunc := context.WithCancel(ctx)
googleAPI, err := googleapi.NewClient(ctx)
if err != nil {
return nil, fmt.Errorf("unable to initialize a client to Google Speech API: %w", err)
}
stt := &SpeechToText{
googleAPI: googleAPI,
opQueue: make(chan *googleapi.BatchRecognizeOperation, 1024),
cancelFunc: cancelFunc,
}
stt.wg.Add(1)
observability.Go(ctx, func() {
defer stt.wg.Done()
stt.loop(ctx)
})
return stt, nil
}
func (stt *SpeechToText) loop(ctx context.Context) {
for {
select {
case op := <-stt.opQueue:
resp, err := op.Wait(ctx)
}
}
}
func (stt *SpeechToText) AudioEncoding() audio.Encoding {
return stt.audioEncoding
}
func (stt *SpeechToText) WriteAudio(
ctx context.Context,
audio []byte,
) error {
req := &speechpb.BatchRecognizeRequest{
Recognizer: "",
Config: &speechpb.RecognitionConfig{},
ConfigMask: &fieldmaskpb.FieldMask{},
Files: []*speechpb.BatchRecognizeFileMetadata{},
RecognitionOutputConfig: &speechpb.RecognitionOutputConfig{},
ProcessingStrategy: 0,
}
op, err := stt.googleAPI.BatchRecognize(ctx, req)
if err != nil {
// TODO: Handle error.
}
}
func (stt *SpeechToText) OutputChan() <-chan string {
}
func (stt *SpeechToText) Close() error {
if stt.cancelFunc == nil {
return fmt.Errorf("already closed!")
}
stt.cancelFunc()
stt.cancelFunc = nil
err := stt.googleAPI.Close()
stt.waitForClosure()
close(stt.opQueue)
return err
}
func (stt *SpeechToText) waitForClosure() {
stt.wg.Wait()
}

View File

@@ -1,11 +0,0 @@
package whisper
import "io"
type noopCloser struct {
io.ReadWriter
}
func (noopCloser) Close() error {
return nil
}

View File

@@ -1,71 +0,0 @@
package whisper
import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"time"
"github.com/xaionaro-go/streamctl/pkg/speech"
)
var lineParseRegexp = regexp.MustCompile(`^([\-0-9]+) ([0-9+]+) (.*)$`)
func parseLine(
_ context.Context,
line string,
) (string, time.Duration, time.Duration, error) {
r := lineParseRegexp.FindAllStringSubmatch(line, -1)
if len(r) < 1 {
return "", 0, 0, fmt.Errorf("expected %s, but received '%s' (len(r) == %d)", lineParseRegexp, line, len(r))
}
if len(r[0]) < 4 {
return "", 0, 0, fmt.Errorf("expected %s, but received '%s' (len(r[0]) == %d)", lineParseRegexp, line, len(r[0]))
}
startTSStr := r[0][1]
endTSStr := r[0][2]
text := r[0][3]
startTS, err := strconv.ParseInt(startTSStr, 10, 64)
if err != nil {
return "", 0, 0, fmt.Errorf("invalid StartTS '%s': %w", startTSStr, err)
}
endTS, err := strconv.ParseUint(endTSStr, 10, 64)
if err != nil {
return "", 0, 0, fmt.Errorf("invalid EndTS '%s': %w", endTSStr, err)
}
return text,
time.Millisecond * time.Duration(startTS),
time.Millisecond * time.Duration(endTS),
nil
}
func parseMessage(
ctx context.Context,
message string,
) (string, []speech.TranscriptWord, error) {
var words []speech.TranscriptWord
var lines []string
for _, line := range strings.Split(message, "\n") {
if len(line) == 0 {
continue
}
text, startTS, endTS, err := parseLine(ctx, line)
if err != nil {
return "", nil, fmt.Errorf("unable to parse line '%s': %w", line, err)
}
lines = append(lines, text)
words = append(words, speech.TranscriptWord{
StartTime: startTS,
EndTime: endTS,
Text: text,
Confidence: 0.5,
Speaker: "",
})
}
return strings.Join(lines, " | "), words, nil
}

View File

@@ -1,47 +0,0 @@
package whisper
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/xaionaro-go/streamctl/pkg/speech"
)
func TestParseMessage(t *testing.T) {
ctx := context.Background()
message := `1592 1912 Well,
2172 3752 you work, I hope so
12028 14168 Yes, it seems to work, but
14168 15388 the translation suffers a little`
_, words, err := parseMessage(ctx, message)
require.NoError(t, err)
require.Equal(t,
[]speech.TranscriptWord{
{
StartTime: 1592 * time.Millisecond,
EndTime: 1912 * time.Millisecond,
Text: "Well,",
Confidence: 0.5,
},
{
StartTime: 2172 * time.Millisecond,
EndTime: 3752 * time.Millisecond,
Text: "you work, I hope so",
Confidence: 0.5,
},
{
StartTime: 12028 * time.Millisecond,
EndTime: 14168 * time.Millisecond,
Text: "Yes, it seems to work, but",
Confidence: 0.5,
},
{
StartTime: 14168 * time.Millisecond,
EndTime: 15388 * time.Millisecond,
Text: "the translation suffers a little",
Confidence: 0.5,
},
}, words)
}

View File

@@ -1,148 +0,0 @@
package whisper
import (
"context"
"fmt"
"io"
"sync"
"sync/atomic"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/hashicorp/go-multierror"
"github.com/xaionaro-go/streamctl/pkg/audio"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/speech"
)
const (
ModelName = ""
)
type SpeechToText struct {
closeCount atomic.Uint64
wg sync.WaitGroup
whisperClient io.ReadWriteCloser
cancelFunc context.CancelFunc
resultQueue chan *speech.Transcript
}
var _ speech.ToText = (*SpeechToText)(nil)
func New(
ctx context.Context,
whisperClient io.ReadWriteCloser,
shouldClose bool,
) *SpeechToText {
ctx, cancelFunc := context.WithCancel(ctx)
if !shouldClose {
whisperClient = noopCloser{whisperClient}
}
stt := &SpeechToText{
whisperClient: whisperClient,
resultQueue: make(chan *speech.Transcript, 1024),
cancelFunc: cancelFunc,
}
stt.wg.Add(1)
observability.Go(ctx, func() {
defer stt.wg.Done()
defer stt.Close()
err := stt.loop(ctx)
if err != nil {
select {
case <-ctx.Done():
default:
logger.Errorf(ctx, "stt.loop returned error: %v", err)
}
}
})
return stt
}
func (stt *SpeechToText) AudioEncoding() audio.Encoding {
return audio.EncodingPCM{
PCMFormat: audio.PCMFormatS16LE,
SampleRate: 16000,
}
}
func (stt *SpeechToText) AudioChannels() audio.Channel {
return 1
}
func (stt *SpeechToText) loop(ctx context.Context) (_err error) {
logger.Debugf(ctx, "stt.loop()")
defer func() { logger.Debugf(ctx, "/stt.loop(): %v", _err) }()
buf := make([]byte, 1024*1024)
for {
n, err := stt.whisperClient.Read(buf)
if err != nil {
return fmt.Errorf("unable to read from the whisper server: %w", err)
}
if n == len(buf) {
return fmt.Errorf("received too big message")
}
if n == 0 {
return fmt.Errorf("received zero bytes")
}
msg := string(buf[:n])
text, words, err := parseMessage(ctx, msg)
if err != nil {
return fmt.Errorf("unable to parse whisper output '%s' (%X): %w", msg, msg, err)
}
stt.resultQueue <- &speech.Transcript{
Variants: []speech.TranscriptVariant{{
Text: text,
TranscriptWords: words,
Confidence: 0.5,
}},
Stability: 0.5,
AudioChannelNum: 0,
Language: "",
IsFinal: true,
}
}
}
func (stt *SpeechToText) WriteAudio(
ctx context.Context,
audio []byte,
) error {
n, err := stt.whisperClient.Write(audio)
if err != nil {
return fmt.Errorf("unable to write audio: %w", err)
}
if n != len(audio) {
return fmt.Errorf("written message is too short: %d < %d", n, len(audio))
}
return nil
}
func (stt *SpeechToText) OutputChan() <-chan *speech.Transcript {
return stt.resultQueue
}
func (stt *SpeechToText) Close() error {
if stt.closeCount.Add(1) != 1 {
return fmt.Errorf("already closed")
}
stt.cancelFunc()
stt.cancelFunc = nil
var mErr *multierror.Error
mErr = multierror.Append(mErr, stt.whisperClient.Close())
stt.waitForClosure()
close(stt.resultQueue)
return mErr.ErrorOrNil()
}
func (stt *SpeechToText) waitForClosure() {
stt.wg.Wait()
}

View File

@@ -1,39 +0,0 @@
package speech
import (
"context"
"io"
"time"
"github.com/xaionaro-go/streamctl/pkg/audio"
)
type TranscriptWord struct {
StartTime time.Duration
EndTime time.Duration
Text string
Confidence float32
Speaker string
}
type TranscriptVariant struct {
Text string
TranscriptWords []TranscriptWord
Confidence float32
}
type Transcript struct {
Variants []TranscriptVariant
Stability float32
AudioChannelNum audio.Channel
Language Language
IsFinal bool
}
type ToText interface {
io.Closer
AudioEncoding() audio.Encoding
AudioChannels() audio.Channel
WriteAudio(context.Context, []byte) error
OutputChan() <-chan *Transcript
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/kickcom" "github.com/xaionaro-go/kickcom"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/observability"
"github.com/xaionaro-go/streamctl/pkg/streamcontrol" "github.com/xaionaro-go/streamctl/pkg/streamcontrol"
) )

Some files were not shown because too many files have changed in this diff Show More