mirror of
https://github.com/xaionaro-go/streamctl.git
synced 2025-09-27 03:45:52 +08:00
2062 lines
49 KiB
Go
2062 lines
49 KiB
Go
package server
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/facebookincubator/go-belt/tool/experimental/errmon"
|
|
"github.com/facebookincubator/go-belt/tool/logger"
|
|
"github.com/goccy/go-yaml"
|
|
"github.com/google/uuid"
|
|
"github.com/hashicorp/go-multierror"
|
|
"github.com/xaionaro-go/observability"
|
|
"github.com/xaionaro-go/player/pkg/player/protobuf/go/player_grpc"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamcontrol"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamd"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamd/api"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamd/config"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamd/grpc/go/streamd_grpc"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamd/grpc/goconv"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamd/memoize"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamd/platcollection"
|
|
"github.com/xaionaro-go/streamctl/pkg/streampanel/consts"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamserver/types"
|
|
"github.com/xaionaro-go/streamctl/pkg/streamtypes"
|
|
"github.com/xaionaro-go/xsync"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
type oauthURLHandlers map[uint16]map[uuid.UUID]*OAuthURLHandler
|
|
|
|
type GRPCServer struct {
|
|
streamd_grpc.UnimplementedStreamDServer
|
|
StreamD api.StreamD
|
|
MemoizeDataValue *memoize.MemoizeData
|
|
OAuthURLHandlerLocker xsync.Mutex
|
|
OAuthURLHandlers oauthURLHandlers
|
|
|
|
UnansweredOAuthRequestsLocker xsync.Mutex
|
|
UnansweredOAuthRequests map[streamcontrol.PlatformName]map[uint16]*streamd_grpc.OAuthRequest
|
|
}
|
|
|
|
type OAuthURLHandler struct {
|
|
Sender streamd_grpc.StreamD_SubscribeToOAuthRequestsServer
|
|
CancelFn context.CancelFunc
|
|
}
|
|
|
|
var _ streamd_grpc.StreamDServer = (*GRPCServer)(nil)
|
|
|
|
func NewGRPCServer(streamd api.StreamD) *GRPCServer {
|
|
return &GRPCServer{
|
|
StreamD: streamd,
|
|
MemoizeDataValue: memoize.NewMemoizeData(),
|
|
|
|
OAuthURLHandlers: oauthURLHandlers{},
|
|
UnansweredOAuthRequests: map[streamcontrol.PlatformName]map[uint16]*streamd_grpc.OAuthRequest{},
|
|
}
|
|
}
|
|
|
|
func (grpc *GRPCServer) MemoizeData() *memoize.MemoizeData {
|
|
return grpc.MemoizeDataValue
|
|
}
|
|
|
|
func (grpc *GRPCServer) Ping(
|
|
ctx context.Context,
|
|
req *streamd_grpc.PingRequest,
|
|
) (*streamd_grpc.PingReply, error) {
|
|
var payload strings.Builder
|
|
extraSize := req.GetRequestExtraPayloadSize()
|
|
totalSize := len(
|
|
req.GetPayloadToReturn(),
|
|
) + int(
|
|
extraSize,
|
|
)
|
|
if totalSize > 65535 {
|
|
return nil, fmt.Errorf(
|
|
"requested a too big payload",
|
|
)
|
|
}
|
|
payload.WriteString(req.GetPayloadToReturn())
|
|
payload.WriteString(strings.Repeat("0", int(extraSize)))
|
|
return &streamd_grpc.PingReply{
|
|
Payload: payload.String(),
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) Close() error {
|
|
err := &multierror.Error{}
|
|
ctx := context.TODO()
|
|
hs := xsync.DoR1(
|
|
ctx,
|
|
&grpc.OAuthURLHandlerLocker,
|
|
func() oauthURLHandlers {
|
|
return grpc.OAuthURLHandlers
|
|
},
|
|
)
|
|
for listenPort, sender := range hs {
|
|
_ = sender // TODO: invent sender.Close()
|
|
delete(grpc.OAuthURLHandlers, listenPort)
|
|
}
|
|
return err.ErrorOrNil()
|
|
}
|
|
|
|
func (grpc *GRPCServer) invalidateCache(
|
|
ctx context.Context,
|
|
) {
|
|
grpc.MemoizeDataValue.InvalidateCache(ctx)
|
|
}
|
|
|
|
func (grpc *GRPCServer) SetLoggingLevel(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SetLoggingLevelRequest,
|
|
) (*streamd_grpc.SetLoggingLevelReply, error) {
|
|
err := grpc.StreamD.SetLoggingLevel(
|
|
ctx,
|
|
goconv.LoggingLevelGRPC2Go(req.GetLoggingLevel()),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to set the logging level: %w",
|
|
err,
|
|
)
|
|
}
|
|
return &streamd_grpc.SetLoggingLevelReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) GetLoggingLevel(
|
|
ctx context.Context,
|
|
req *streamd_grpc.GetLoggingLevelRequest,
|
|
) (*streamd_grpc.GetLoggingLevelReply, error) {
|
|
level, err := grpc.StreamD.GetLoggingLevel(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to query the logging level: %w",
|
|
err,
|
|
)
|
|
}
|
|
return &streamd_grpc.GetLoggingLevelReply{
|
|
LoggingLevel: goconv.LoggingLevelGo2GRPC(level),
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) GetConfig(
|
|
ctx context.Context,
|
|
req *streamd_grpc.GetConfigRequest,
|
|
) (*streamd_grpc.GetConfigReply, error) {
|
|
cfg, err := grpc.StreamD.GetConfig(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to get the config: %w",
|
|
err,
|
|
)
|
|
}
|
|
var buf bytes.Buffer
|
|
_, err = cfg.WriteTo(&buf)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to serialize the config: %w",
|
|
err,
|
|
)
|
|
}
|
|
return &streamd_grpc.GetConfigReply{
|
|
Config: buf.String(),
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SetConfig(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SetConfigRequest,
|
|
) (*streamd_grpc.SetConfigReply, error) {
|
|
|
|
logger.Debugf(ctx, "received SetConfig: %s", req.Config)
|
|
|
|
var result config.Config
|
|
_, err := result.Read([]byte(req.Config))
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to unserialize the config: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
err = grpc.StreamD.SetConfig(ctx, &result)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to set the config: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
grpc.invalidateCache(ctx)
|
|
return &streamd_grpc.SetConfigReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SaveConfig(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SaveConfigRequest,
|
|
) (*streamd_grpc.SaveConfigReply, error) {
|
|
err := grpc.StreamD.SaveConfig(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to save the config: %w",
|
|
err,
|
|
)
|
|
}
|
|
grpc.invalidateCache(ctx)
|
|
return &streamd_grpc.SaveConfigReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ResetCache(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ResetCacheRequest,
|
|
) (*streamd_grpc.ResetCacheReply, error) {
|
|
err := grpc.StreamD.ResetCache(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to reset the cache: %w",
|
|
err,
|
|
)
|
|
}
|
|
grpc.invalidateCache(ctx)
|
|
return &streamd_grpc.ResetCacheReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) InitCache(
|
|
ctx context.Context,
|
|
req *streamd_grpc.InitCacheRequest,
|
|
) (*streamd_grpc.InitCacheReply, error) {
|
|
err := grpc.StreamD.InitCache(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to init the cache: %w",
|
|
err,
|
|
)
|
|
}
|
|
grpc.invalidateCache(ctx)
|
|
return &streamd_grpc.InitCacheReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) StartStream(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StartStreamRequest,
|
|
) (*streamd_grpc.StartStreamReply, error) {
|
|
logger.Debugf(
|
|
ctx,
|
|
"grpc:StartStream: raw profile: %#+v",
|
|
req.Profile,
|
|
)
|
|
platID := streamcontrol.PlatformName(req.GetPlatID())
|
|
|
|
profile, err := goconv.ProfileGRPC2Go(
|
|
platID,
|
|
req.GetProfile(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
logger.Debugf(
|
|
ctx,
|
|
"grpc:StartStream: parsed: %#+v",
|
|
profile,
|
|
)
|
|
|
|
err = grpc.StreamD.StartStream(
|
|
ctx,
|
|
platID,
|
|
req.GetTitle(),
|
|
req.GetDescription(),
|
|
profile,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to start the stream: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
grpc.invalidateCache(ctx)
|
|
return &streamd_grpc.StartStreamReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) EndStream(
|
|
ctx context.Context,
|
|
req *streamd_grpc.EndStreamRequest,
|
|
) (*streamd_grpc.EndStreamReply, error) {
|
|
err := grpc.StreamD.EndStream(
|
|
ctx,
|
|
streamcontrol.PlatformName(req.GetPlatID()),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to end the stream: %w",
|
|
err,
|
|
)
|
|
}
|
|
grpc.invalidateCache(ctx)
|
|
return &streamd_grpc.EndStreamReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) GetStreamD() api.StreamD {
|
|
if grpc.StreamD == nil {
|
|
panic("grpc.StreamD == nil")
|
|
}
|
|
streamD, ok := grpc.StreamD.(*streamd.StreamD)
|
|
if !ok {
|
|
return grpc.StreamD
|
|
}
|
|
|
|
<-streamD.ReadyChan
|
|
return streamD
|
|
}
|
|
|
|
func (grpc *GRPCServer) IsBackendEnabled(
|
|
ctx context.Context,
|
|
req *streamd_grpc.IsBackendEnabledRequest,
|
|
) (_ret *streamd_grpc.IsBackendEnabledReply, _err error) {
|
|
platID := streamcontrol.PlatformName(req.GetPlatID())
|
|
logger.Tracef(ctx, "IsBackendEnabled(ctx, '%s')", platID)
|
|
defer func() { logger.Tracef(ctx, "/IsBackendEnabled(ctx, '%s'): %v %v", platID, _ret, _err) }()
|
|
enabled, err := grpc.GetStreamD().IsBackendEnabled(
|
|
ctx,
|
|
platID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to check if backend '%s' is enabled: %w",
|
|
req.GetPlatID(),
|
|
err,
|
|
)
|
|
}
|
|
return &streamd_grpc.IsBackendEnabledReply{
|
|
IsInitialized: enabled,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) GetBackendInfo(
|
|
ctx context.Context,
|
|
req *streamd_grpc.GetBackendInfoRequest,
|
|
) (_ret *streamd_grpc.GetBackendInfoReply, _err error) {
|
|
platID, includeData := streamcontrol.PlatformName(req.GetPlatID()), req.GetIncludeData()
|
|
logger.Tracef(ctx, "GetBackendInfo(ctx, '%s', %t)", platID, includeData)
|
|
defer func() { logger.Tracef(ctx, "/GetBackendInfo(ctx, '%s', %t): %v %v", platID, includeData, _ret, _err) }()
|
|
isEnabled, err := grpc.GetStreamD().IsBackendEnabled(
|
|
ctx,
|
|
platID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to check if the backend is enabled: %w",
|
|
err,
|
|
)
|
|
}
|
|
info, err := grpc.StreamD.GetBackendInfo(ctx, platID, includeData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to get the backend info: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
result := &streamd_grpc.GetBackendInfoReply{
|
|
IsInitialized: isEnabled,
|
|
Capabilities: goconv.CapabilitiesGo2GRPC(ctx, info.Capabilities),
|
|
}
|
|
|
|
if includeData {
|
|
dataSerialized, err := json.Marshal(info.Data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to serialize the backend info: %w",
|
|
err,
|
|
)
|
|
}
|
|
result.Data = string(dataSerialized)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SetTitle(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SetTitleRequest,
|
|
) (*streamd_grpc.SetTitleReply, error) {
|
|
err := grpc.StreamD.SetTitle(
|
|
ctx,
|
|
streamcontrol.PlatformName(req.GetPlatID()),
|
|
req.GetTitle(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.SetTitleReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SetDescription(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SetDescriptionRequest,
|
|
) (*streamd_grpc.SetDescriptionReply, error) {
|
|
err := grpc.StreamD.SetDescription(
|
|
ctx,
|
|
streamcontrol.PlatformName(req.GetPlatID()),
|
|
req.GetDescription(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.SetDescriptionReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ApplyProfile(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ApplyProfileRequest,
|
|
) (*streamd_grpc.ApplyProfileReply, error) {
|
|
platID := streamcontrol.PlatformName(req.GetPlatID())
|
|
profile, err := platcollection.NewStreamProfile(platID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to build a profile for platform '%s': %w",
|
|
platID,
|
|
err,
|
|
)
|
|
}
|
|
err = yaml.Unmarshal([]byte(req.GetProfile()), profile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to unserialize the profile '%s': %w",
|
|
req.GetProfile(),
|
|
err,
|
|
)
|
|
}
|
|
|
|
logger.Debugf(
|
|
ctx,
|
|
"unserialized profile: %#+v",
|
|
profile,
|
|
)
|
|
|
|
err = grpc.StreamD.ApplyProfile(
|
|
ctx,
|
|
streamcontrol.PlatformName(req.GetPlatID()),
|
|
profile,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to apply the profile %#+v: %w",
|
|
profile,
|
|
err,
|
|
)
|
|
}
|
|
return &streamd_grpc.ApplyProfileReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) UpdateStream(
|
|
ctx context.Context,
|
|
req *streamd_grpc.UpdateStreamRequest,
|
|
) (*streamd_grpc.UpdateStreamReply, error) {
|
|
platID := streamcontrol.PlatformName(req.GetPlatID())
|
|
profile, err := platcollection.NewStreamProfile(platID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to build a profile for platform '%s': %w",
|
|
platID,
|
|
err,
|
|
)
|
|
}
|
|
err = yaml.Unmarshal([]byte(req.GetProfile()), profile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to unserialize the profile '%s': %w",
|
|
req.GetProfile(),
|
|
err,
|
|
)
|
|
}
|
|
|
|
logger.Debugf(
|
|
ctx,
|
|
"unserialized profile: %#+v",
|
|
profile,
|
|
)
|
|
|
|
err = grpc.StreamD.UpdateStream(
|
|
ctx,
|
|
platID,
|
|
req.GetTitle(),
|
|
req.GetDescription(),
|
|
profile,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to update stream details: %w",
|
|
err,
|
|
)
|
|
}
|
|
return &streamd_grpc.UpdateStreamReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) EXPERIMENTAL_ReinitStreamControllers(
|
|
ctx context.Context,
|
|
in *streamd_grpc.EXPERIMENTAL_ReinitStreamControllersRequest,
|
|
) (*streamd_grpc.EXPERIMENTAL_ReinitStreamControllersReply, error) {
|
|
err := grpc.StreamD.EXPERIMENTAL_ReinitStreamControllers(
|
|
ctx,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"StreamD returned an error: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
return &streamd_grpc.EXPERIMENTAL_ReinitStreamControllersReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) GetStreamStatus(
|
|
ctx context.Context,
|
|
req *streamd_grpc.GetStreamStatusRequest,
|
|
) (*streamd_grpc.GetStreamStatusReply, error) {
|
|
logger.Tracef(ctx, "GetStreamStatus()")
|
|
defer logger.Tracef(ctx, "/GetStreamStatus()")
|
|
|
|
if req.NoCache {
|
|
ctx = memoize.SetNoCache(ctx, true)
|
|
}
|
|
|
|
platID := streamcontrol.PlatformName(req.GetPlatID())
|
|
streamStatus, err := grpc.StreamD.GetStreamStatus(
|
|
ctx,
|
|
platID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to get the stream status of '%s': %w",
|
|
platID,
|
|
err,
|
|
)
|
|
}
|
|
|
|
var startedAt *int64
|
|
if streamStatus.StartedAt != nil {
|
|
startedAt = ptr(streamStatus.StartedAt.UnixNano())
|
|
}
|
|
|
|
var viewersCount *uint64
|
|
if streamStatus.ViewersCount != nil {
|
|
viewersCount = ptr(uint64(*streamStatus.ViewersCount))
|
|
}
|
|
|
|
var customData string
|
|
if streamStatus.CustomData != nil {
|
|
customDataSerialized, err := json.Marshal(
|
|
streamStatus.CustomData,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to serialize the custom data: %w",
|
|
err,
|
|
)
|
|
}
|
|
customData = string(customDataSerialized)
|
|
}
|
|
|
|
return &streamd_grpc.GetStreamStatusReply{
|
|
IsActive: streamStatus.IsActive,
|
|
StartedAt: startedAt,
|
|
CustomData: customData,
|
|
ViewersCount: viewersCount,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubscribeToOAuthRequests(
|
|
req *streamd_grpc.SubscribeToOAuthRequestsRequest,
|
|
sender streamd_grpc.StreamD_SubscribeToOAuthRequestsServer,
|
|
) (_ret error) {
|
|
ctx, cancelFn := context.WithCancel(sender.Context())
|
|
_uuid, err := uuid.NewRandom()
|
|
if err != nil {
|
|
logger.Errorf(
|
|
ctx,
|
|
"unable to generate an UUID: %v",
|
|
err,
|
|
)
|
|
}
|
|
|
|
logger.Tracef(
|
|
ctx,
|
|
"SubscribeToOAuthRequests(): UUID:%s",
|
|
_uuid,
|
|
)
|
|
defer func() { logger.Tracef(ctx, "/SubscribeToOAuthRequests(): UUID:%s: %v", _uuid, _ret) }()
|
|
|
|
listenPort := uint16(req.ListenPort)
|
|
streamD, _ := grpc.StreamD.(*streamd.StreamD)
|
|
|
|
grpc.OAuthURLHandlerLocker.Do(ctx, func() {
|
|
m := grpc.OAuthURLHandlers[listenPort]
|
|
if m == nil {
|
|
m = map[uuid.UUID]*OAuthURLHandler{}
|
|
}
|
|
m[_uuid] = &OAuthURLHandler{
|
|
Sender: sender,
|
|
CancelFn: cancelFn,
|
|
}
|
|
grpc.OAuthURLHandlers[listenPort] = m // unnecessary, but feels safer
|
|
})
|
|
|
|
var unansweredRequests []*streamd_grpc.OAuthRequest
|
|
grpc.UnansweredOAuthRequestsLocker.Do(ctx, func() {
|
|
for _, m := range grpc.UnansweredOAuthRequests {
|
|
req := m[listenPort]
|
|
if req == nil {
|
|
continue
|
|
}
|
|
unansweredRequests = append(
|
|
unansweredRequests,
|
|
req,
|
|
)
|
|
}
|
|
})
|
|
|
|
for _, req := range unansweredRequests {
|
|
logger.Tracef(ctx, "re-sending an unanswered request to a new client: %#+v", req)
|
|
err := sender.Send(req)
|
|
errmon.ObserveErrorCtx(ctx, err)
|
|
}
|
|
|
|
if streamD != nil {
|
|
streamD.AddOAuthListenPort(listenPort)
|
|
}
|
|
|
|
logger.Tracef(
|
|
ctx,
|
|
"waiting for the subscription to be cancelled",
|
|
)
|
|
|
|
// waiting, while normal actual sending happens via grpc.OAuthURLHandlers[listenPort]
|
|
<-ctx.Done()
|
|
|
|
grpc.OAuthURLHandlerLocker.Do(ctx, func() {
|
|
delete(grpc.OAuthURLHandlers[listenPort], _uuid)
|
|
if len(grpc.OAuthURLHandlers[listenPort]) == 0 {
|
|
delete(grpc.OAuthURLHandlers, listenPort)
|
|
if streamD != nil {
|
|
streamD.RemoveOAuthListenPort(listenPort)
|
|
}
|
|
}
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
type ErrNoOAuthHandler struct {
|
|
Err error
|
|
}
|
|
|
|
func (err ErrNoOAuthHandler) Error() string {
|
|
return fmt.Sprintf("no oauth handler: %v", err.Err)
|
|
}
|
|
|
|
func (err ErrNoOAuthHandler) Unwrap() error {
|
|
return err.Err
|
|
}
|
|
|
|
type ErrNoOAuthHandlerForPort struct {
|
|
Port uint16
|
|
}
|
|
|
|
func (err ErrNoOAuthHandlerForPort) Error() string {
|
|
return fmt.Sprintf("no handler for port %d", err.Port)
|
|
}
|
|
|
|
func (grpc *GRPCServer) OpenBrowser(
|
|
ctx context.Context,
|
|
url string,
|
|
) (_ret error) {
|
|
logger.Debugf(ctx, "OpenBrowser(ctx, '%s')", url)
|
|
defer func() { logger.Debugf(ctx, "/OpenBrowser(ctx, '%s'): %v", url, _ret) }()
|
|
|
|
// TODO: Stop abusing the function for OAuthURLs here! Implement a separate function, or
|
|
// at least rename the old one.
|
|
logger.Warnf(
|
|
ctx,
|
|
"FIXME: Do not use OAuthURLs for 'OpenBrowser'!",
|
|
)
|
|
|
|
return xsync.DoA2R1(
|
|
ctx,
|
|
&grpc.OAuthURLHandlerLocker,
|
|
grpc.openBrowser,
|
|
ctx,
|
|
url,
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) openBrowser(
|
|
ctx context.Context,
|
|
url string,
|
|
) (_ret error) {
|
|
req := &streamd_grpc.OAuthRequest{
|
|
PlatID: string("<OpenBrowser>"),
|
|
AuthURL: url,
|
|
}
|
|
|
|
var resultErr *multierror.Error
|
|
successCount := 0
|
|
for _, handlers := range grpc.OAuthURLHandlers {
|
|
logger.Debugf(ctx, "openBrowser: OpenOAuthURL() sending %#+v", req)
|
|
for _, handler := range handlers {
|
|
err := handler.Sender.Send(req)
|
|
if err != nil {
|
|
resultErr = multierror.Append(resultErr, fmt.Errorf("unable to send oauth request: %w", err))
|
|
continue
|
|
}
|
|
successCount++
|
|
}
|
|
}
|
|
|
|
if successCount == 0 {
|
|
err := resultErr.ErrorOrNil()
|
|
if err == nil {
|
|
err = fmt.Errorf("no handlers available")
|
|
}
|
|
return ErrNoOAuthHandler{Err: err}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) OpenOAuthURL(
|
|
ctx context.Context,
|
|
listenPort uint16,
|
|
platID streamcontrol.PlatformName,
|
|
authURL string,
|
|
) (_ret error) {
|
|
logger.Debugf(ctx, "OpenOAuthURL(ctx, %d, '%s', '%s')", listenPort, platID, authURL)
|
|
defer func() {
|
|
logger.Debugf(ctx, "/OpenOAuthURL(ctx, %d, '%s', '%s'): %v", listenPort, platID, authURL, _ret)
|
|
}()
|
|
|
|
return xsync.DoA4R1(
|
|
ctx,
|
|
&grpc.OAuthURLHandlerLocker,
|
|
grpc.openOAuthURL,
|
|
ctx,
|
|
listenPort,
|
|
platID,
|
|
authURL,
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) openOAuthURL(
|
|
ctx context.Context,
|
|
listenPort uint16,
|
|
platID streamcontrol.PlatformName,
|
|
authURL string,
|
|
) (_ret error) {
|
|
handlers := grpc.OAuthURLHandlers[listenPort]
|
|
if len(handlers) == 0 {
|
|
return ErrNoOAuthHandlerForPort{
|
|
Port: listenPort,
|
|
}
|
|
}
|
|
req := &streamd_grpc.OAuthRequest{
|
|
PlatID: string(platID),
|
|
AuthURL: authURL,
|
|
}
|
|
grpc.UnansweredOAuthRequestsLocker.Do(ctx, func() {
|
|
if grpc.UnansweredOAuthRequests[platID] == nil {
|
|
grpc.UnansweredOAuthRequests[platID] = map[uint16]*streamd_grpc.OAuthRequest{}
|
|
}
|
|
grpc.UnansweredOAuthRequests[platID][listenPort] = req
|
|
})
|
|
logger.Debugf(ctx, "openOAuthURL: OpenOAuthURL() sending %#+v", req)
|
|
var resultErr *multierror.Error
|
|
for _, handler := range handlers {
|
|
err := handler.Sender.Send(req)
|
|
if err != nil {
|
|
resultErr = multierror.Append(resultErr, fmt.Errorf("unable to send oauth request: %w", err))
|
|
}
|
|
}
|
|
return resultErr.ErrorOrNil()
|
|
}
|
|
|
|
func (grpc *GRPCServer) GetVariable(
|
|
ctx context.Context,
|
|
req *streamd_grpc.GetVariableRequest,
|
|
) (*streamd_grpc.GetVariableReply, error) {
|
|
key := consts.VarKey(req.GetKey())
|
|
b, err := grpc.StreamD.GetVariable(ctx, key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to get variable '%s': %w",
|
|
key,
|
|
err,
|
|
)
|
|
}
|
|
|
|
return &streamd_grpc.GetVariableReply{
|
|
Key: string(key),
|
|
Value: b,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) GetVariableHash(
|
|
ctx context.Context,
|
|
req *streamd_grpc.GetVariableHashRequest,
|
|
) (*streamd_grpc.GetVariableHashReply, error) {
|
|
var hashType crypto.Hash
|
|
hashTypeIn := req.GetHashType()
|
|
switch hashTypeIn {
|
|
case streamd_grpc.HashType_HASH_SHA1:
|
|
hashType = crypto.SHA1
|
|
default:
|
|
return nil, fmt.Errorf(
|
|
"unexpected hash type: %v",
|
|
hashTypeIn,
|
|
)
|
|
}
|
|
|
|
key := consts.VarKey(req.GetKey())
|
|
b, err := grpc.StreamD.GetVariableHash(
|
|
ctx,
|
|
key,
|
|
hashType,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to get variable '%s': %w",
|
|
key,
|
|
err,
|
|
)
|
|
}
|
|
|
|
return &streamd_grpc.GetVariableHashReply{
|
|
Key: string(key),
|
|
HashType: hashTypeIn,
|
|
Hash: b,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SetVariable(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SetVariableRequest,
|
|
) (*streamd_grpc.SetVariableReply, error) {
|
|
key := consts.VarKey(req.GetKey())
|
|
err := grpc.StreamD.SetVariable(
|
|
ctx,
|
|
key,
|
|
req.GetValue(),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to set variable '%s': %w",
|
|
key,
|
|
err,
|
|
)
|
|
}
|
|
|
|
return &streamd_grpc.SetVariableReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubscribeToVariable(
|
|
req *streamd_grpc.SubscribeToVariableRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToVariableServer,
|
|
) error {
|
|
return wrapChan(
|
|
func(ctx context.Context) (<-chan api.VariableValue, error) {
|
|
return grpc.StreamD.SubscribeToVariable(ctx, consts.VarKey(req.GetKey()))
|
|
},
|
|
srv,
|
|
func(input api.VariableValue) streamd_grpc.VariableChange {
|
|
return streamd_grpc.VariableChange{
|
|
Value: input,
|
|
}
|
|
},
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubmitOAuthCode(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SubmitOAuthCodeRequest,
|
|
) (*streamd_grpc.SubmitOAuthCodeReply, error) {
|
|
grpc.UnansweredOAuthRequestsLocker.Do(ctx, func() {
|
|
delete(
|
|
grpc.UnansweredOAuthRequests,
|
|
streamcontrol.PlatformName(req.PlatID),
|
|
)
|
|
})
|
|
|
|
_, err := grpc.StreamD.SubmitOAuthCode(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.SubmitOAuthCodeReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ListStreamServers(
|
|
ctx context.Context,
|
|
_ *streamd_grpc.ListStreamServersRequest,
|
|
) (*streamd_grpc.ListStreamServersReply, error) {
|
|
servers, err := grpc.StreamD.ListStreamServers(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*streamd_grpc.StreamServerWithStatistics
|
|
for _, srv := range servers {
|
|
srvGRPC, err := goconv.StreamServerConfigGo2GRPC(
|
|
ctx,
|
|
srv.Type,
|
|
srv.ListenAddr,
|
|
srv.Options(),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to convert the server server value: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
result = append(
|
|
result,
|
|
&streamd_grpc.StreamServerWithStatistics{
|
|
Config: srvGRPC,
|
|
Statistics: &streamd_grpc.StreamServerStatistics{
|
|
NumBytesConsumerWrote: int64(
|
|
srv.NumBytesConsumerWrote,
|
|
),
|
|
NumBytesProducerRead: int64(
|
|
srv.NumBytesProducerRead,
|
|
),
|
|
},
|
|
},
|
|
)
|
|
}
|
|
return &streamd_grpc.ListStreamServersReply{
|
|
StreamServers: result,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) StartStreamServer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StartStreamServerRequest,
|
|
) (*streamd_grpc.StartStreamServerReply, error) {
|
|
srvType, addr, opts, err := goconv.StreamServerConfigGRPC2Go(
|
|
ctx,
|
|
req.GetConfig(),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to convert the stream server config %#+v: %w",
|
|
req.GetConfig(),
|
|
err,
|
|
)
|
|
}
|
|
|
|
err = grpc.StreamD.StartStreamServer(
|
|
ctx,
|
|
srvType,
|
|
addr,
|
|
opts...,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StartStreamServerReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) StopStreamServer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StopStreamServerRequest,
|
|
) (*streamd_grpc.StopStreamServerReply, error) {
|
|
err := grpc.StreamD.StopStreamServer(
|
|
ctx,
|
|
req.GetListenAddr(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StopStreamServerReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ListStreamDestinations(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ListStreamDestinationsRequest,
|
|
) (*streamd_grpc.ListStreamDestinationsReply, error) {
|
|
dsts, err := grpc.StreamD.ListStreamDestinations(
|
|
ctx,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*streamd_grpc.StreamDestination
|
|
for _, dst := range dsts {
|
|
result = append(
|
|
result,
|
|
&streamd_grpc.StreamDestination{
|
|
DestinationID: string(dst.ID),
|
|
Url: dst.URL,
|
|
StreamKey: dst.StreamKey,
|
|
},
|
|
)
|
|
}
|
|
return &streamd_grpc.ListStreamDestinationsReply{
|
|
StreamDestinations: result,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) AddStreamDestination(
|
|
ctx context.Context,
|
|
req *streamd_grpc.AddStreamDestinationRequest,
|
|
) (*streamd_grpc.AddStreamDestinationReply, error) {
|
|
err := grpc.StreamD.AddStreamDestination(
|
|
ctx,
|
|
api.DestinationID(
|
|
req.GetConfig().GetDestinationID(),
|
|
),
|
|
req.GetConfig().GetUrl(),
|
|
req.GetConfig().GetStreamKey(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.AddStreamDestinationReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) UpdateStreamDestination(
|
|
ctx context.Context,
|
|
req *streamd_grpc.UpdateStreamDestinationRequest,
|
|
) (*streamd_grpc.UpdateStreamDestinationReply, error) {
|
|
err := grpc.StreamD.UpdateStreamDestination(
|
|
ctx,
|
|
api.DestinationID(
|
|
req.GetConfig().GetDestinationID(),
|
|
),
|
|
req.GetConfig().GetUrl(),
|
|
req.GetConfig().GetStreamKey(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.UpdateStreamDestinationReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) RemoveStreamDestination(
|
|
ctx context.Context,
|
|
req *streamd_grpc.RemoveStreamDestinationRequest,
|
|
) (*streamd_grpc.RemoveStreamDestinationReply, error) {
|
|
err := grpc.StreamD.RemoveStreamDestination(
|
|
ctx,
|
|
api.DestinationID(req.GetDestinationID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.RemoveStreamDestinationReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) AddIncomingStream(
|
|
ctx context.Context,
|
|
req *streamd_grpc.AddIncomingStreamRequest,
|
|
) (*streamd_grpc.AddIncomingStreamReply, error) {
|
|
err := grpc.StreamD.AddIncomingStream(
|
|
ctx,
|
|
api.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.AddIncomingStreamReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) RemoveIncomingStream(
|
|
ctx context.Context,
|
|
req *streamd_grpc.RemoveIncomingStreamRequest,
|
|
) (*streamd_grpc.RemoveIncomingStreamReply, error) {
|
|
err := grpc.StreamD.RemoveIncomingStream(
|
|
ctx,
|
|
api.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.RemoveIncomingStreamReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ListIncomingStreams(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ListIncomingStreamsRequest,
|
|
) (*streamd_grpc.ListIncomingStreamsReply, error) {
|
|
inStreams, err := grpc.StreamD.ListIncomingStreams(
|
|
ctx,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*streamd_grpc.IncomingStream
|
|
for _, s := range inStreams {
|
|
result = append(
|
|
result,
|
|
&streamd_grpc.IncomingStream{
|
|
StreamID: string(s.StreamID),
|
|
IsActive: s.IsActive,
|
|
},
|
|
)
|
|
}
|
|
return &streamd_grpc.ListIncomingStreamsReply{
|
|
IncomingStreams: result,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ListStreamForwards(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ListStreamForwardsRequest,
|
|
) (*streamd_grpc.ListStreamForwardsReply, error) {
|
|
streamFwds, err := grpc.StreamD.ListStreamForwards(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*streamd_grpc.StreamForwardWithStatistics
|
|
for _, s := range streamFwds {
|
|
item := &streamd_grpc.StreamForwardWithStatistics{
|
|
Config: &streamd_grpc.StreamForward{
|
|
StreamID: string(s.StreamID),
|
|
DestinationID: string(s.DestinationID),
|
|
Enabled: s.Enabled,
|
|
},
|
|
Statistics: &streamd_grpc.StreamForwardStatistics{
|
|
NumBytesWrote: int64(s.NumBytesWrote),
|
|
NumBytesRead: int64(s.NumBytesRead),
|
|
},
|
|
}
|
|
item.Config.Encode = goconv.EncoderConfigToThrift(
|
|
s.Encode.Enabled,
|
|
s.Encode.EncodersConfig,
|
|
)
|
|
item.Config.Quirks = &streamd_grpc.StreamForwardQuirks{
|
|
RestartUntilYoutubeRecognizesStream: &streamd_grpc.RestartUntilYoutubeRecognizesStream{
|
|
Enabled: s.Quirks.RestartUntilYoutubeRecognizesStream.Enabled,
|
|
StartTimeout: s.Quirks.RestartUntilYoutubeRecognizesStream.StartTimeout.Seconds(),
|
|
StopStartDelay: s.Quirks.RestartUntilYoutubeRecognizesStream.StopStartDelay.Seconds(),
|
|
},
|
|
StartAfterYoutubeRecognizedStream: &streamd_grpc.StartAfterYoutubeRecognizedStream{
|
|
Enabled: s.Quirks.StartAfterYoutubeRecognizedStream.Enabled,
|
|
},
|
|
}
|
|
result = append(result, item)
|
|
}
|
|
return &streamd_grpc.ListStreamForwardsReply{
|
|
StreamForwards: result,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) AddStreamForward(
|
|
ctx context.Context,
|
|
req *streamd_grpc.AddStreamForwardRequest,
|
|
) (*streamd_grpc.AddStreamForwardReply, error) {
|
|
cfg := req.GetConfig()
|
|
encode, recodingEnabled := goconv.EncoderConfigFromThrift(cfg.Encode)
|
|
err := grpc.StreamD.AddStreamForward(
|
|
ctx,
|
|
api.StreamID(req.GetConfig().GetStreamID()),
|
|
api.DestinationID(
|
|
req.GetConfig().GetDestinationID(),
|
|
),
|
|
cfg.Enabled,
|
|
types.EncodeConfig{
|
|
Enabled: recodingEnabled,
|
|
EncodersConfig: encode,
|
|
},
|
|
api.StreamForwardingQuirks{
|
|
RestartUntilYoutubeRecognizesStream: api.RestartUntilYoutubeRecognizesStream{
|
|
Enabled: cfg.Quirks.RestartUntilYoutubeRecognizesStream.Enabled,
|
|
StartTimeout: sec2dur(
|
|
cfg.Quirks.RestartUntilYoutubeRecognizesStream.StartTimeout,
|
|
),
|
|
StopStartDelay: sec2dur(
|
|
cfg.Quirks.RestartUntilYoutubeRecognizesStream.StopStartDelay,
|
|
),
|
|
},
|
|
StartAfterYoutubeRecognizedStream: api.StartAfterYoutubeRecognizedStream{
|
|
Enabled: cfg.Quirks.StartAfterYoutubeRecognizedStream.Enabled,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.AddStreamForwardReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) UpdateStreamForward(
|
|
ctx context.Context,
|
|
req *streamd_grpc.UpdateStreamForwardRequest,
|
|
) (*streamd_grpc.UpdateStreamForwardReply, error) {
|
|
cfg := req.GetConfig()
|
|
encode, recodingEnabled := goconv.EncoderConfigFromThrift(cfg.Encode)
|
|
err := grpc.StreamD.UpdateStreamForward(
|
|
ctx,
|
|
api.StreamID(req.GetConfig().GetStreamID()),
|
|
api.DestinationID(
|
|
req.GetConfig().GetDestinationID(),
|
|
),
|
|
cfg.Enabled,
|
|
types.EncodeConfig{
|
|
Enabled: recodingEnabled,
|
|
EncodersConfig: encode,
|
|
},
|
|
api.StreamForwardingQuirks{
|
|
RestartUntilYoutubeRecognizesStream: api.RestartUntilYoutubeRecognizesStream{
|
|
Enabled: cfg.Quirks.RestartUntilYoutubeRecognizesStream.Enabled,
|
|
StartTimeout: sec2dur(
|
|
cfg.Quirks.RestartUntilYoutubeRecognizesStream.StartTimeout,
|
|
),
|
|
StopStartDelay: sec2dur(
|
|
cfg.Quirks.RestartUntilYoutubeRecognizesStream.StopStartDelay,
|
|
),
|
|
},
|
|
StartAfterYoutubeRecognizedStream: api.StartAfterYoutubeRecognizedStream{
|
|
Enabled: cfg.Quirks.StartAfterYoutubeRecognizedStream.Enabled,
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.UpdateStreamForwardReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) RemoveStreamForward(
|
|
ctx context.Context,
|
|
req *streamd_grpc.RemoveStreamForwardRequest,
|
|
) (*streamd_grpc.RemoveStreamForwardReply, error) {
|
|
err := grpc.StreamD.RemoveStreamForward(
|
|
ctx,
|
|
api.StreamID(req.GetConfig().GetStreamID()),
|
|
api.DestinationID(
|
|
req.GetConfig().GetDestinationID(),
|
|
),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.RemoveStreamForwardReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) WaitForStreamPublisher(
|
|
req *streamd_grpc.WaitForStreamPublisherRequest,
|
|
sender streamd_grpc.StreamD_WaitForStreamPublisherServer,
|
|
) (_ret error) {
|
|
ctx := sender.Context()
|
|
logger.Tracef(
|
|
ctx,
|
|
"WaitForStreamPublisher(): StreamID:%s",
|
|
req.GetStreamID(),
|
|
)
|
|
defer func() {
|
|
logger.Tracef(
|
|
ctx,
|
|
"/WaitForStreamPublisher(): StreamID:%s: %v",
|
|
req.GetStreamID(),
|
|
_ret,
|
|
)
|
|
}()
|
|
|
|
ch, err := grpc.StreamD.WaitForStreamPublisher(
|
|
ctx,
|
|
api.StreamID(req.GetStreamID()),
|
|
req.GetWaitForNext(),
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("streamd returned error: %w", err)
|
|
}
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-ch:
|
|
}
|
|
|
|
return sender.Send(&streamd_grpc.StreamPublisher{})
|
|
}
|
|
|
|
func (grpc *GRPCServer) AddStreamPlayer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.AddStreamPlayerRequest,
|
|
) (_ret *streamd_grpc.AddStreamPlayerReply, _err error) {
|
|
cfg := req.GetConfig()
|
|
logger.Tracef(
|
|
ctx,
|
|
"AddStreamPlayer(): StreamID:%s",
|
|
cfg.StreamID,
|
|
)
|
|
defer func() {
|
|
logger.Tracef(
|
|
ctx,
|
|
"/AddStreamPlayer(): StreamID:%s: %v",
|
|
cfg.StreamID,
|
|
_err,
|
|
)
|
|
}()
|
|
|
|
err := grpc.StreamD.AddStreamPlayer(
|
|
ctx,
|
|
streamtypes.StreamID(cfg.GetStreamID()),
|
|
goconv.StreamPlayerTypeGRPC2Go(cfg.PlayerType),
|
|
cfg.GetDisabled(),
|
|
goconv.StreamPlaybackConfigGRPC2Go(
|
|
cfg.GetStreamPlaybackConfig(),
|
|
),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.AddStreamPlayerReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) RemoveStreamPlayer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.RemoveStreamPlayerRequest,
|
|
) (_req *streamd_grpc.RemoveStreamPlayerReply, _err error) {
|
|
logger.Tracef(
|
|
ctx,
|
|
"AddStreamPlayer(): StreamID:%s",
|
|
req.GetStreamID(),
|
|
)
|
|
defer func() {
|
|
logger.Tracef(
|
|
ctx,
|
|
"/AddStreamPlayer(): StreamID:%s: %v",
|
|
req.GetStreamID(),
|
|
_err,
|
|
)
|
|
}()
|
|
|
|
err := grpc.StreamD.RemoveStreamPlayer(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.RemoveStreamPlayerReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) UpdateStreamPlayer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.UpdateStreamPlayerRequest,
|
|
) (_req *streamd_grpc.UpdateStreamPlayerReply, _err error) {
|
|
cfg := req.GetConfig()
|
|
logger.Debugf(
|
|
ctx,
|
|
"UpdateStreamPlayer(): StreamID:%s",
|
|
cfg.StreamID,
|
|
)
|
|
defer func() {
|
|
logger.Debugf(
|
|
ctx,
|
|
"/UpdateStreamPlayer(): StreamID:%s: %v",
|
|
cfg.StreamID,
|
|
_err,
|
|
)
|
|
}()
|
|
|
|
err := grpc.StreamD.UpdateStreamPlayer(
|
|
ctx,
|
|
streamtypes.StreamID(cfg.GetStreamID()),
|
|
goconv.StreamPlayerTypeGRPC2Go(cfg.PlayerType),
|
|
cfg.GetDisabled(),
|
|
goconv.StreamPlaybackConfigGRPC2Go(
|
|
cfg.GetStreamPlaybackConfig(),
|
|
),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.UpdateStreamPlayerReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ListStreamPlayers(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ListStreamPlayersRequest,
|
|
) (*streamd_grpc.ListStreamPlayersReply, error) {
|
|
players, err := grpc.StreamD.ListStreamPlayers(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result := make(
|
|
[]*streamd_grpc.StreamPlayerConfig,
|
|
0,
|
|
len(players),
|
|
)
|
|
for _, player := range players {
|
|
result = append(
|
|
result,
|
|
&streamd_grpc.StreamPlayerConfig{
|
|
StreamID: string(player.StreamID),
|
|
PlayerType: goconv.StreamPlayerTypeGo2GRPC(
|
|
player.PlayerType,
|
|
),
|
|
Disabled: player.Disabled,
|
|
StreamPlaybackConfig: goconv.StreamPlaybackConfigGo2GRPC(
|
|
&player.StreamPlaybackConfig,
|
|
),
|
|
},
|
|
)
|
|
}
|
|
return &streamd_grpc.ListStreamPlayersReply{
|
|
Players: result,
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) GetStreamPlayer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.GetStreamPlayerRequest,
|
|
) (*streamd_grpc.GetStreamPlayerReply, error) {
|
|
player, err := grpc.StreamD.GetStreamPlayer(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.GetStreamPlayerReply{
|
|
Config: &streamd_grpc.StreamPlayerConfig{
|
|
StreamID: string(player.StreamID),
|
|
PlayerType: goconv.StreamPlayerTypeGo2GRPC(
|
|
player.PlayerType,
|
|
),
|
|
Disabled: player.Disabled,
|
|
StreamPlaybackConfig: goconv.StreamPlaybackConfigGo2GRPC(
|
|
&player.StreamPlaybackConfig,
|
|
),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) StreamPlayerOpen(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerOpenRequest,
|
|
) (*streamd_grpc.StreamPlayerOpenReply, error) {
|
|
err := grpc.StreamD.StreamPlayerOpenURL(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
req.GetRequest().GetLink(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerOpenReply{
|
|
Reply: &player_grpc.OpenReply{},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerProcessTitle(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerProcessTitleRequest,
|
|
) (*streamd_grpc.StreamPlayerProcessTitleReply, error) {
|
|
title, err := grpc.StreamD.StreamPlayerProcessTitle(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerProcessTitleReply{
|
|
Reply: &player_grpc.ProcessTitleReply{
|
|
Title: title,
|
|
},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerGetLink(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerGetLinkRequest,
|
|
) (*streamd_grpc.StreamPlayerGetLinkReply, error) {
|
|
link, err := grpc.StreamD.StreamPlayerGetLink(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerGetLinkReply{
|
|
Reply: &player_grpc.GetLinkReply{
|
|
Link: link,
|
|
},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerEndChan(
|
|
req *streamd_grpc.StreamPlayerEndChanRequest,
|
|
srv streamd_grpc.StreamD_StreamPlayerEndChanServer,
|
|
) error {
|
|
ctx, cancelFn := context.WithCancel(srv.Context())
|
|
defer cancelFn()
|
|
ch, err := grpc.StreamD.StreamPlayerEndChan(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-ch:
|
|
}
|
|
return srv.Send(&streamd_grpc.StreamPlayerEndChanReply{
|
|
Reply: &player_grpc.EndChanReply{},
|
|
})
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerIsEnded(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerIsEndedRequest,
|
|
) (*streamd_grpc.StreamPlayerIsEndedReply, error) {
|
|
isEnded, err := grpc.StreamD.StreamPlayerIsEnded(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerIsEndedReply{
|
|
Reply: &player_grpc.IsEndedReply{
|
|
IsEnded: isEnded,
|
|
},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerGetPosition(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerGetPositionRequest,
|
|
) (*streamd_grpc.StreamPlayerGetPositionReply, error) {
|
|
pos, err := grpc.StreamD.StreamPlayerGetPosition(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerGetPositionReply{
|
|
Reply: &player_grpc.GetPositionReply{
|
|
PositionSecs: pos.Seconds(),
|
|
},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerGetLength(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerGetLengthRequest,
|
|
) (*streamd_grpc.StreamPlayerGetLengthReply, error) {
|
|
l, err := grpc.StreamD.StreamPlayerGetPosition(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerGetLengthReply{
|
|
Reply: &player_grpc.GetLengthReply{
|
|
LengthSecs: l.Seconds(),
|
|
},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerSetSpeed(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerSetSpeedRequest,
|
|
) (*streamd_grpc.StreamPlayerSetSpeedReply, error) {
|
|
err := grpc.StreamD.StreamPlayerSetSpeed(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
req.GetRequest().Speed,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerSetSpeedReply{
|
|
Reply: &player_grpc.SetSpeedReply{},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerSetPause(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerSetPauseRequest,
|
|
) (*streamd_grpc.StreamPlayerSetPauseReply, error) {
|
|
err := grpc.StreamD.StreamPlayerSetPause(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
req.GetRequest().IsPaused,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerSetPauseReply{
|
|
Reply: &player_grpc.SetPauseReply{},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerStop(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerStopRequest,
|
|
) (*streamd_grpc.StreamPlayerStopReply, error) {
|
|
err := grpc.StreamD.StreamPlayerStop(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerStopReply{
|
|
Reply: &player_grpc.StopReply{},
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) StreamPlayerClose(
|
|
ctx context.Context,
|
|
req *streamd_grpc.StreamPlayerCloseRequest,
|
|
) (*streamd_grpc.StreamPlayerCloseReply, error) {
|
|
err := grpc.StreamD.StreamPlayerClose(
|
|
ctx,
|
|
streamtypes.StreamID(req.GetStreamID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.StreamPlayerCloseReply{
|
|
Reply: &player_grpc.CloseReply{},
|
|
}, nil
|
|
}
|
|
|
|
type sender[T any] interface {
|
|
grpc.ServerStream
|
|
|
|
Context() context.Context
|
|
Send(*T) error
|
|
}
|
|
|
|
func wrapChan[T any, E any](
|
|
getChan func(ctx context.Context) (<-chan E, error),
|
|
sender sender[T],
|
|
parse func(E) T,
|
|
) (_err error) {
|
|
ctx, cancelFn := context.WithCancel(sender.Context())
|
|
defer cancelFn()
|
|
|
|
var tSample T
|
|
var eSample E
|
|
logger.Tracef(ctx, "wrapChan[%T, %T]", tSample, eSample)
|
|
defer func() { logger.Tracef(ctx, "/wrapChan[%T, %T]: %v", tSample, eSample, _err) }()
|
|
|
|
ch, err := getChan(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
errCh := make(chan error, 1)
|
|
for {
|
|
var input E
|
|
var ok bool
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case input, ok = <-ch:
|
|
}
|
|
if !ok {
|
|
return fmt.Errorf("channel is closed")
|
|
}
|
|
result := parse(input)
|
|
sendCtx, cancelFn := context.WithTimeout(ctx, time.Minute)
|
|
observability.Go(ctx, func(ctx context.Context) {
|
|
errCh <- sender.Send(&result)
|
|
})
|
|
select {
|
|
case <-sendCtx.Done():
|
|
logger.Warnf(ctx, "sending timed out")
|
|
cancelFn()
|
|
return sendCtx.Err()
|
|
case err := <-errCh:
|
|
cancelFn()
|
|
if err != nil {
|
|
return fmt.Errorf(
|
|
"unable to send %#+v: %w",
|
|
result,
|
|
err,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubscribeToConfigChanges(
|
|
req *streamd_grpc.SubscribeToConfigChangesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToConfigChangesServer,
|
|
) error {
|
|
return wrapChan(
|
|
grpc.StreamD.SubscribeToConfigChanges,
|
|
srv,
|
|
func(input api.DiffConfig) streamd_grpc.ConfigChange {
|
|
return streamd_grpc.ConfigChange{}
|
|
},
|
|
)
|
|
}
|
|
func (grpc *GRPCServer) SubscribeToStreamsChanges(
|
|
req *streamd_grpc.SubscribeToStreamsChangesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToStreamsChangesServer,
|
|
) error {
|
|
return wrapChan(
|
|
grpc.StreamD.SubscribeToStreamsChanges,
|
|
srv,
|
|
func(input api.DiffStreams) streamd_grpc.StreamsChange {
|
|
return streamd_grpc.StreamsChange{}
|
|
},
|
|
)
|
|
}
|
|
func (grpc *GRPCServer) SubscribeToStreamServersChanges(
|
|
req *streamd_grpc.SubscribeToStreamServersChangesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToStreamServersChangesServer,
|
|
) error {
|
|
return wrapChan(
|
|
grpc.StreamD.SubscribeToStreamServersChanges,
|
|
srv,
|
|
func(input api.DiffStreamServers) streamd_grpc.StreamServersChange {
|
|
return streamd_grpc.StreamServersChange{}
|
|
},
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubscribeToStreamDestinationsChanges(
|
|
req *streamd_grpc.SubscribeToStreamDestinationsChangesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToStreamDestinationsChangesServer,
|
|
) error {
|
|
return wrapChan(
|
|
grpc.StreamD.SubscribeToStreamDestinationsChanges,
|
|
srv,
|
|
func(input api.DiffStreamDestinations) streamd_grpc.StreamDestinationsChange {
|
|
return streamd_grpc.StreamDestinationsChange{}
|
|
},
|
|
)
|
|
}
|
|
func (grpc *GRPCServer) SubscribeToIncomingStreamsChanges(
|
|
req *streamd_grpc.SubscribeToIncomingStreamsChangesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToIncomingStreamsChangesServer,
|
|
) error {
|
|
return wrapChan(
|
|
grpc.StreamD.SubscribeToIncomingStreamsChanges,
|
|
srv,
|
|
func(input api.DiffIncomingStreams) streamd_grpc.IncomingStreamsChange {
|
|
return streamd_grpc.IncomingStreamsChange{}
|
|
},
|
|
)
|
|
}
|
|
func (grpc *GRPCServer) SubscribeToStreamForwardsChanges(
|
|
req *streamd_grpc.SubscribeToStreamForwardsChangesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToStreamForwardsChangesServer,
|
|
) error {
|
|
return wrapChan(
|
|
grpc.StreamD.SubscribeToStreamForwardsChanges,
|
|
srv,
|
|
func(input api.DiffStreamForwards) streamd_grpc.StreamForwardsChange {
|
|
return streamd_grpc.StreamForwardsChange{}
|
|
},
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubscribeToStreamPlayersChanges(
|
|
req *streamd_grpc.SubscribeToStreamPlayersChangesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToStreamPlayersChangesServer,
|
|
) error {
|
|
return wrapChan(
|
|
grpc.StreamD.SubscribeToStreamPlayersChanges,
|
|
srv,
|
|
func(input api.DiffStreamPlayers) streamd_grpc.StreamPlayersChange {
|
|
return streamd_grpc.StreamPlayersChange{}
|
|
},
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) AddTimer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.AddTimerRequest,
|
|
) (*streamd_grpc.AddTimerReply, error) {
|
|
triggerAtUnixNano := req.GetTriggerAtUnixNano()
|
|
triggerAt := time.Unix(
|
|
triggerAtUnixNano/1000000000,
|
|
triggerAtUnixNano%1000000000,
|
|
)
|
|
|
|
action, err := goconv.ActionGRPC2Go(req.Action)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to convert the action: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
timerID, err := grpc.StreamD.AddTimer(
|
|
ctx,
|
|
triggerAt,
|
|
action,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to add timer: %w",
|
|
err,
|
|
)
|
|
}
|
|
|
|
return &streamd_grpc.AddTimerReply{
|
|
TimerID: int64(timerID),
|
|
}, nil
|
|
}
|
|
func (grpc *GRPCServer) RemoveTimer(
|
|
ctx context.Context,
|
|
req *streamd_grpc.RemoveTimerRequest,
|
|
) (*streamd_grpc.RemoveTimerReply, error) {
|
|
err := grpc.StreamD.RemoveTimer(
|
|
ctx,
|
|
api.TimerID(req.GetTimerID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.RemoveTimerReply{}, nil
|
|
}
|
|
func (grpc *GRPCServer) ListTimers(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ListTimersRequest,
|
|
) (*streamd_grpc.ListTimersReply, error) {
|
|
timers, err := grpc.StreamD.ListTimers(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*streamd_grpc.Timer
|
|
for _, timer := range timers {
|
|
resultAction, err := goconv.ActionGo2GRPC(
|
|
timer.Action,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to convert the action: %w",
|
|
err,
|
|
)
|
|
}
|
|
result = append(result, &streamd_grpc.Timer{
|
|
TimerID: int64(timer.ID),
|
|
TriggerAtUnixNano: timer.TriggerAt.UnixNano(),
|
|
Action: resultAction,
|
|
})
|
|
}
|
|
return &streamd_grpc.ListTimersReply{
|
|
Timers: result,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) ListTriggerRules(
|
|
ctx context.Context,
|
|
req *streamd_grpc.ListTriggerRulesRequest,
|
|
) (*streamd_grpc.ListTriggerRulesReply, error) {
|
|
rules, err := grpc.StreamD.ListTriggerRules(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var result []*streamd_grpc.TriggerRule
|
|
for _, rule := range rules {
|
|
triggerRule, err := goconv.TriggerRuleGo2GRPC(rule)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"unable to convert the trigger rule: %w",
|
|
err,
|
|
)
|
|
}
|
|
result = append(
|
|
result,
|
|
triggerRule,
|
|
)
|
|
}
|
|
return &streamd_grpc.ListTriggerRulesReply{
|
|
Rules: result,
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) AddTriggerRule(
|
|
ctx context.Context,
|
|
req *streamd_grpc.AddTriggerRuleRequest,
|
|
) (*streamd_grpc.AddTriggerRuleReply, error) {
|
|
|
|
triggerRule, err := goconv.TriggerRuleGRPC2Go(req.GetRule())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to convert the trigger rule: %w", err)
|
|
}
|
|
|
|
ruleID, err := grpc.StreamD.AddTriggerRule(ctx, triggerRule)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to add timer: %w", err)
|
|
}
|
|
|
|
return &streamd_grpc.AddTriggerRuleReply{
|
|
RuleID: uint64(ruleID),
|
|
}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) RemoveTriggerRule(
|
|
ctx context.Context,
|
|
req *streamd_grpc.RemoveTriggerRuleRequest,
|
|
) (*streamd_grpc.RemoveTriggerRuleReply, error) {
|
|
err := grpc.StreamD.RemoveTriggerRule(
|
|
ctx,
|
|
api.TriggerRuleID(req.GetRuleID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.RemoveTriggerRuleReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) UpdateTriggerRule(
|
|
ctx context.Context,
|
|
req *streamd_grpc.UpdateTriggerRuleRequest,
|
|
) (*streamd_grpc.UpdateTriggerRuleReply, error) {
|
|
triggerRule, err := goconv.TriggerRuleGRPC2Go(req.GetRule())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to convert the trigger rule: %w", err)
|
|
}
|
|
|
|
err = grpc.StreamD.UpdateTriggerRule(
|
|
ctx,
|
|
api.TriggerRuleID(req.GetRuleID()),
|
|
triggerRule,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.UpdateTriggerRuleReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubmitEvent(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SubmitEventRequest,
|
|
) (*streamd_grpc.SubmitEventReply, error) {
|
|
event, err := goconv.EventGRPC2Go(req.GetEvent())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to convert the trigger rule: %w", err)
|
|
}
|
|
|
|
err = grpc.StreamD.SubmitEvent(ctx, event)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.SubmitEventReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) SubscribeToChatMessages(
|
|
req *streamd_grpc.SubscribeToChatMessagesRequest,
|
|
srv streamd_grpc.StreamD_SubscribeToChatMessagesServer,
|
|
) error {
|
|
ts := req.GetSinceUNIXNano()
|
|
limit := req.GetLimit()
|
|
since := time.Unix(
|
|
int64(ts)/int64(time.Second.Nanoseconds()),
|
|
int64(ts)%int64(time.Second.Nanoseconds()),
|
|
)
|
|
return wrapChan(
|
|
func(ctx context.Context) (<-chan api.ChatMessage, error) {
|
|
return grpc.StreamD.SubscribeToChatMessages(ctx, since, limit)
|
|
},
|
|
srv,
|
|
func(input api.ChatMessage) streamd_grpc.ChatMessage {
|
|
return streamd_grpc.ChatMessage{
|
|
CreatedAtUNIXNano: uint64(input.CreatedAt.UnixNano()),
|
|
PlatID: string(input.Platform),
|
|
IsLive: input.IsLive,
|
|
UserID: string(input.UserID),
|
|
Username: input.Username,
|
|
MessageID: string(input.MessageID),
|
|
Message: input.Message,
|
|
}
|
|
},
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) SendChatMessage(
|
|
ctx context.Context,
|
|
req *streamd_grpc.SendChatMessageRequest,
|
|
) (*streamd_grpc.SendChatMessageReply, error) {
|
|
err := grpc.StreamD.SendChatMessage(
|
|
ctx,
|
|
streamcontrol.PlatformName(req.PlatID),
|
|
req.GetMessage(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.SendChatMessageReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) RemoveChatMessage(
|
|
ctx context.Context,
|
|
req *streamd_grpc.RemoveChatMessageRequest,
|
|
) (*streamd_grpc.RemoveChatMessageReply, error) {
|
|
err := grpc.StreamD.RemoveChatMessage(
|
|
ctx,
|
|
streamcontrol.PlatformName(req.GetPlatID()),
|
|
streamcontrol.ChatMessageID(req.GetMessageID()),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.RemoveChatMessageReply{}, nil
|
|
}
|
|
|
|
func (grpc *GRPCServer) BanUser(
|
|
ctx context.Context,
|
|
req *streamd_grpc.BanUserRequest,
|
|
) (*streamd_grpc.BanUserReply, error) {
|
|
|
|
var deadline time.Time
|
|
if req.DeadlineUnixNano != nil {
|
|
deadline = goconv.UnixGRPC2Go(*req.DeadlineUnixNano)
|
|
}
|
|
err := grpc.StreamD.BanUser(
|
|
ctx,
|
|
streamcontrol.PlatformName(req.GetPlatID()),
|
|
streamcontrol.ChatUserID(req.GetUserID()),
|
|
req.GetReason(),
|
|
deadline,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.BanUserReply{}, nil
|
|
}
|
|
|
|
/*func (grpc *GRPCServer) ProxyConnect(
|
|
req *streamd_grpc.ProxyConnectRequest,
|
|
srv streamd_grpc.StreamD_ProxyConnectServer,
|
|
) error {
|
|
grpc.StreamD.ProxyConnect(
|
|
ctx,
|
|
)
|
|
}
|
|
|
|
func (grpc *GRPCServer) ProxyPackets(
|
|
streamd_grpc.StreamD_ProxyPacketsServer,
|
|
) error {
|
|
|
|
}*/
|
|
|
|
func (grpc *GRPCServer) LLMGenerate(
|
|
ctx context.Context,
|
|
req *streamd_grpc.LLMGenerateRequest,
|
|
) (*streamd_grpc.LLMGenerateReply, error) {
|
|
response, err := grpc.StreamD.LLMGenerate(
|
|
ctx,
|
|
req.GetPrompt(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &streamd_grpc.LLMGenerateReply{
|
|
Response: response,
|
|
}, nil
|
|
}
|