This commit is contained in:
2026-03-26 13:02:43 +00:00
parent a689f8b9da
commit eb7b519e49
64 changed files with 7081 additions and 5572 deletions

View File

@@ -0,0 +1,103 @@
package playerconfigs
import (
"context"
"strings"
appv1 "stream.api/internal/gen/proto/app/v1"
)
type Handler struct {
appv1.UnimplementedPlayerConfigsServiceServer
module *Module
}
var _ appv1.PlayerConfigsServiceServer = (*Handler)(nil)
func NewHandler(module *Module) *Handler { return &Handler{module: module} }
func (h *Handler) ListPlayerConfigs(ctx context.Context, _ *appv1.ListPlayerConfigsRequest) (*appv1.ListPlayerConfigsResponse, error) {
result, err := h.module.runtime.Authenticate(ctx)
if err != nil {
return nil, err
}
payload, err := h.module.ListPlayerConfigs(ctx, ListPlayerConfigsQuery{UserID: result.UserID})
if err != nil {
return nil, err
}
return presentListPlayerConfigsResponse(payload), nil
}
func (h *Handler) CreatePlayerConfig(ctx context.Context, req *appv1.CreatePlayerConfigRequest) (*appv1.CreatePlayerConfigResponse, error) {
result, err := h.module.runtime.Authenticate(ctx)
if err != nil {
return nil, err
}
payload, err := h.module.CreatePlayerConfig(ctx, CreatePlayerConfigCommand{UserID: result.UserID, Name: req.GetName(), Description: req.Description, Autoplay: req.GetAutoplay(), Loop: req.GetLoop(), Muted: req.GetMuted(), ShowControls: req.GetShowControls(), Pip: req.GetPip(), Airplay: req.GetAirplay(), Chromecast: req.GetChromecast(), IsActive: req.IsActive, IsDefault: req.IsDefault, EncrytionM3U8: req.EncrytionM3U8, LogoURL: req.LogoUrl})
if err != nil {
return nil, err
}
return presentCreatePlayerConfigResponse(*payload), nil
}
func (h *Handler) UpdatePlayerConfig(ctx context.Context, req *appv1.UpdatePlayerConfigRequest) (*appv1.UpdatePlayerConfigResponse, error) {
result, err := h.module.runtime.Authenticate(ctx)
if err != nil {
return nil, err
}
payload, err := h.module.UpdatePlayerConfig(ctx, UpdatePlayerConfigCommand{UserID: result.UserID, ID: strings.TrimSpace(req.GetId()), Name: req.GetName(), Description: req.Description, Autoplay: req.GetAutoplay(), Loop: req.GetLoop(), Muted: req.GetMuted(), ShowControls: req.GetShowControls(), Pip: req.GetPip(), Airplay: req.GetAirplay(), Chromecast: req.GetChromecast(), IsActive: req.IsActive, IsDefault: req.IsDefault, EncrytionM3U8: req.EncrytionM3U8, LogoURL: req.LogoUrl})
if err != nil {
return nil, err
}
return presentUpdatePlayerConfigResponse(*payload), nil
}
func (h *Handler) DeletePlayerConfig(ctx context.Context, req *appv1.DeletePlayerConfigRequest) (*appv1.MessageResponse, error) {
result, err := h.module.runtime.Authenticate(ctx)
if err != nil {
return nil, err
}
if err := h.module.DeletePlayerConfig(ctx, DeletePlayerConfigCommand{UserID: result.UserID, ID: strings.TrimSpace(req.GetId())}); err != nil {
return nil, err
}
return &appv1.MessageResponse{Message: "Player config deleted"}, nil
}
func (h *Handler) ListAdminPlayerConfigs(ctx context.Context, req *appv1.ListAdminPlayerConfigsRequest) (*appv1.ListAdminPlayerConfigsResponse, error) {
payload, err := h.module.ListAdminPlayerConfigs(ctx, ListAdminPlayerConfigsQuery{Page: req.GetPage(), Limit: req.GetLimit(), Search: req.Search, UserID: req.UserId})
if err != nil {
return nil, err
}
return presentListAdminPlayerConfigsResponse(payload), nil
}
func (h *Handler) GetAdminPlayerConfig(ctx context.Context, req *appv1.GetAdminPlayerConfigRequest) (*appv1.GetAdminPlayerConfigResponse, error) {
payload, err := h.module.GetAdminPlayerConfig(ctx, GetAdminPlayerConfigQuery{ID: strings.TrimSpace(req.GetId())})
if err != nil {
return nil, err
}
return presentGetAdminPlayerConfigResponse(*payload), nil
}
func (h *Handler) CreateAdminPlayerConfig(ctx context.Context, req *appv1.CreateAdminPlayerConfigRequest) (*appv1.CreateAdminPlayerConfigResponse, error) {
payload, err := h.module.CreateAdminPlayerConfig(ctx, CreateAdminPlayerConfigCommand{UserID: strings.TrimSpace(req.GetUserId()), Name: req.GetName(), Description: req.Description, Autoplay: req.GetAutoplay(), Loop: req.GetLoop(), Muted: req.GetMuted(), ShowControls: req.GetShowControls(), Pip: req.GetPip(), Airplay: req.GetAirplay(), Chromecast: req.GetChromecast(), IsActive: req.GetIsActive(), IsDefault: req.GetIsDefault(), EncrytionM3U8: req.EncrytionM3U8, LogoURL: req.LogoUrl})
if err != nil {
return nil, err
}
return presentCreateAdminPlayerConfigResponse(*payload), nil
}
func (h *Handler) UpdateAdminPlayerConfig(ctx context.Context, req *appv1.UpdateAdminPlayerConfigRequest) (*appv1.UpdateAdminPlayerConfigResponse, error) {
payload, err := h.module.UpdateAdminPlayerConfig(ctx, UpdateAdminPlayerConfigCommand{ID: strings.TrimSpace(req.GetId()), UserID: strings.TrimSpace(req.GetUserId()), Name: req.GetName(), Description: req.Description, Autoplay: req.GetAutoplay(), Loop: req.GetLoop(), Muted: req.GetMuted(), ShowControls: req.GetShowControls(), Pip: req.GetPip(), Airplay: req.GetAirplay(), Chromecast: req.GetChromecast(), IsActive: req.GetIsActive(), IsDefault: req.GetIsDefault(), EncrytionM3U8: req.EncrytionM3U8, LogoURL: req.LogoUrl})
if err != nil {
return nil, err
}
return presentUpdateAdminPlayerConfigResponse(*payload), nil
}
func (h *Handler) DeleteAdminPlayerConfig(ctx context.Context, req *appv1.DeleteAdminPlayerConfigRequest) (*appv1.MessageResponse, error) {
if err := h.module.DeleteAdminPlayerConfig(ctx, DeleteAdminPlayerConfigCommand{ID: strings.TrimSpace(req.GetId())}); err != nil {
return nil, err
}
return &appv1.MessageResponse{Message: "Player config deleted"}, nil
}

View File

@@ -0,0 +1,394 @@
package playerconfigs
import (
"context"
"errors"
"strings"
"time"
"github.com/google/uuid"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"gorm.io/gorm"
"stream.api/internal/database/model"
"stream.api/internal/modules/common"
)
type Module struct {
runtime *common.Runtime
}
func New(runtime *common.Runtime) *Module {
return &Module{runtime: runtime}
}
func (m *Module) ListPlayerConfigs(ctx context.Context, queryValue ListPlayerConfigsQuery) (*ListPlayerConfigsResult, error) {
var items []model.PlayerConfig
if err := m.runtime.DB().WithContext(ctx).Where("user_id = ?", queryValue.UserID).Order("is_default DESC").Order("created_at DESC").Find(&items).Error; err != nil {
m.runtime.Logger().Error("Failed to list player configs", "error", err)
return nil, status.Error(codes.Internal, "Failed to load player configs")
}
result := &ListPlayerConfigsResult{Items: make([]PlayerConfigView, 0, len(items))}
for i := range items {
result.Items = append(result.Items, PlayerConfigView{Config: &items[i]})
}
return result, nil
}
func (m *Module) CreatePlayerConfig(ctx context.Context, cmd CreatePlayerConfigCommand) (*PlayerConfigView, error) {
name := strings.TrimSpace(cmd.Name)
if name == "" {
return nil, status.Error(codes.InvalidArgument, "Name is required")
}
item := &model.PlayerConfig{ID: uuid.New().String(), UserID: cmd.UserID, Name: name, Description: common.NullableTrimmedString(cmd.Description), Autoplay: cmd.Autoplay, Loop: cmd.Loop, Muted: cmd.Muted, ShowControls: model.BoolPtr(cmd.ShowControls), Pip: model.BoolPtr(cmd.Pip), Airplay: model.BoolPtr(cmd.Airplay), Chromecast: model.BoolPtr(cmd.Chromecast), IsActive: model.BoolPtr(cmd.IsActive == nil || *cmd.IsActive), IsDefault: cmd.IsDefault != nil && *cmd.IsDefault, EncrytionM3u8: model.BoolPtr(cmd.EncrytionM3U8 == nil || *cmd.EncrytionM3U8), LogoURL: common.NullableTrimmedString(cmd.LogoURL)}
if !common.PlayerConfigIsActive(item.IsActive) {
item.IsDefault = false
}
if err := m.runtime.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
lockedUser, err := common.LockUserForUpdate(ctx, tx, cmd.UserID)
if err != nil {
return err
}
var configCount int64
if err := tx.WithContext(ctx).Model(&model.PlayerConfig{}).Where("user_id = ?", cmd.UserID).Count(&configCount).Error; err != nil {
return err
}
if err := common.PlayerConfigActionAllowed(lockedUser, configCount, "create"); err != nil {
return err
}
if item.IsDefault {
if err := common.UnsetDefaultPlayerConfigs(tx, cmd.UserID, ""); err != nil {
return err
}
}
return tx.Create(item).Error
}); err != nil {
if status.Code(err) != codes.Unknown {
return nil, err
}
m.runtime.Logger().Error("Failed to create player config", "error", err)
return nil, status.Error(codes.Internal, "Failed to save player config")
}
return &PlayerConfigView{Config: item}, nil
}
func (m *Module) UpdatePlayerConfig(ctx context.Context, cmd UpdatePlayerConfigCommand) (*PlayerConfigView, error) {
if strings.TrimSpace(cmd.ID) == "" {
return nil, status.Error(codes.NotFound, "Player config not found")
}
name := strings.TrimSpace(cmd.Name)
if name == "" {
return nil, status.Error(codes.InvalidArgument, "Name is required")
}
var item model.PlayerConfig
if err := m.runtime.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
lockedUser, err := common.LockUserForUpdate(ctx, tx, cmd.UserID)
if err != nil {
return err
}
var configCount int64
if err := tx.WithContext(ctx).Model(&model.PlayerConfig{}).Where("user_id = ?", cmd.UserID).Count(&configCount).Error; err != nil {
return err
}
if err := tx.WithContext(ctx).Where("id = ? AND user_id = ?", cmd.ID, cmd.UserID).First(&item).Error; err != nil {
return err
}
action := "update"
wasActive := common.PlayerConfigIsActive(item.IsActive)
if cmd.IsActive != nil && *cmd.IsActive != wasActive {
action = "toggle-active"
}
if cmd.IsDefault != nil && *cmd.IsDefault {
action = "set-default"
}
if err := common.PlayerConfigActionAllowed(lockedUser, configCount, action); err != nil {
return err
}
item.Name = name
item.Description = common.NullableTrimmedString(cmd.Description)
item.Autoplay = cmd.Autoplay
item.Loop = cmd.Loop
item.Muted = cmd.Muted
item.ShowControls = model.BoolPtr(cmd.ShowControls)
item.Pip = model.BoolPtr(cmd.Pip)
item.Airplay = model.BoolPtr(cmd.Airplay)
item.Chromecast = model.BoolPtr(cmd.Chromecast)
if cmd.EncrytionM3U8 != nil {
item.EncrytionM3u8 = model.BoolPtr(*cmd.EncrytionM3U8)
}
if cmd.LogoURL != nil {
item.LogoURL = common.NullableTrimmedString(cmd.LogoURL)
}
if cmd.IsActive != nil {
item.IsActive = model.BoolPtr(*cmd.IsActive)
}
if cmd.IsDefault != nil {
item.IsDefault = *cmd.IsDefault
}
if !common.PlayerConfigIsActive(item.IsActive) {
item.IsDefault = false
}
if item.IsDefault {
if err := common.UnsetDefaultPlayerConfigs(tx, cmd.UserID, item.ID); err != nil {
return err
}
}
return tx.Save(&item).Error
}); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.NotFound, "Player config not found")
}
if status.Code(err) != codes.Unknown {
return nil, err
}
m.runtime.Logger().Error("Failed to update player config", "error", err)
return nil, status.Error(codes.Internal, "Failed to save player config")
}
return &PlayerConfigView{Config: &item}, nil
}
func (m *Module) DeletePlayerConfig(ctx context.Context, cmd DeletePlayerConfigCommand) error {
if strings.TrimSpace(cmd.ID) == "" {
return status.Error(codes.NotFound, "Player config not found")
}
if err := m.runtime.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
lockedUser, err := common.LockUserForUpdate(ctx, tx, cmd.UserID)
if err != nil {
return err
}
var configCount int64
if err := tx.WithContext(ctx).Model(&model.PlayerConfig{}).Where("user_id = ?", cmd.UserID).Count(&configCount).Error; err != nil {
return err
}
if err := common.PlayerConfigActionAllowed(lockedUser, configCount, "delete"); err != nil {
return err
}
res := tx.Where("id = ? AND user_id = ?", cmd.ID, cmd.UserID).Delete(&model.PlayerConfig{})
if res.Error != nil {
return res.Error
}
if res.RowsAffected == 0 {
return gorm.ErrRecordNotFound
}
return nil
}); err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return status.Error(codes.NotFound, "Player config not found")
}
if status.Code(err) != codes.Unknown {
return err
}
m.runtime.Logger().Error("Failed to delete player config", "error", err)
return status.Error(codes.Internal, "Failed to delete player config")
}
return nil
}
func (m *Module) ListAdminPlayerConfigs(ctx context.Context, queryValue ListAdminPlayerConfigsQuery) (*ListAdminPlayerConfigsResult, error) {
if _, err := m.runtime.RequireAdmin(ctx); err != nil {
return nil, err
}
page, limit, offset := common.AdminPageLimitOffset(queryValue.Page, queryValue.Limit)
limitInt := int(limit)
search := strings.TrimSpace(common.ProtoStringValue(queryValue.Search))
userID := strings.TrimSpace(common.ProtoStringValue(queryValue.UserID))
db := m.runtime.DB().WithContext(ctx).Model(&model.PlayerConfig{})
if search != "" {
like := "%" + search + "%"
db = db.Where("name ILIKE ?", like)
}
if userID != "" {
db = db.Where("user_id = ?", userID)
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, status.Error(codes.Internal, "Failed to list player configs")
}
var configs []model.PlayerConfig
if err := db.Order("created_at DESC").Offset(offset).Limit(limitInt).Find(&configs).Error; err != nil {
return nil, status.Error(codes.Internal, "Failed to list player configs")
}
items := make([]AdminPlayerConfigView, 0, len(configs))
for i := range configs {
payload, err := m.buildAdminPlayerConfig(ctx, &configs[i])
if err != nil {
return nil, status.Error(codes.Internal, "Failed to list player configs")
}
items = append(items, payload)
}
return &ListAdminPlayerConfigsResult{Items: items, Total: total, Page: page, Limit: limit}, nil
}
func (m *Module) GetAdminPlayerConfig(ctx context.Context, queryValue GetAdminPlayerConfigQuery) (*AdminPlayerConfigView, error) {
if _, err := m.runtime.RequireAdmin(ctx); err != nil {
return nil, err
}
if strings.TrimSpace(queryValue.ID) == "" {
return nil, status.Error(codes.NotFound, "Player config not found")
}
var item model.PlayerConfig
if err := m.runtime.DB().WithContext(ctx).Where("id = ?", queryValue.ID).First(&item).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.NotFound, "Player config not found")
}
return nil, status.Error(codes.Internal, "Failed to load player config")
}
payload, err := m.buildAdminPlayerConfig(ctx, &item)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to load player config")
}
return &payload, nil
}
func (m *Module) CreateAdminPlayerConfig(ctx context.Context, cmd CreateAdminPlayerConfigCommand) (*AdminPlayerConfigView, error) {
if _, err := m.runtime.RequireAdmin(ctx); err != nil {
return nil, err
}
if msg := validateAdminPlayerConfigInput(cmd.UserID, cmd.Name); msg != "" {
return nil, status.Error(codes.InvalidArgument, msg)
}
var user model.User
if err := m.runtime.DB().WithContext(ctx).Where("id = ?", strings.TrimSpace(cmd.UserID)).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.InvalidArgument, "User not found")
}
return nil, status.Error(codes.Internal, "Failed to save player config")
}
item := &model.PlayerConfig{ID: uuid.New().String(), UserID: user.ID, Name: strings.TrimSpace(cmd.Name), Description: common.NullableTrimmedStringPtr(cmd.Description), Autoplay: cmd.Autoplay, Loop: cmd.Loop, Muted: cmd.Muted, ShowControls: model.BoolPtr(cmd.ShowControls), Pip: model.BoolPtr(cmd.Pip), Airplay: model.BoolPtr(cmd.Airplay), Chromecast: model.BoolPtr(cmd.Chromecast), IsActive: model.BoolPtr(cmd.IsActive), IsDefault: cmd.IsDefault, EncrytionM3u8: model.BoolPtr(cmd.EncrytionM3U8 == nil || *cmd.EncrytionM3U8), LogoURL: common.NullableTrimmedStringPtr(cmd.LogoURL)}
if !common.BoolValue(item.IsActive) {
item.IsDefault = false
}
if err := m.runtime.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if item.IsDefault {
if err := common.UnsetDefaultPlayerConfigs(tx, item.UserID, ""); err != nil {
return err
}
}
return tx.Create(item).Error
}); err != nil {
return nil, status.Error(codes.Internal, "Failed to save player config")
}
payload, err := m.buildAdminPlayerConfig(ctx, item)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to save player config")
}
return &payload, nil
}
func (m *Module) UpdateAdminPlayerConfig(ctx context.Context, cmd UpdateAdminPlayerConfigCommand) (*AdminPlayerConfigView, error) {
if _, err := m.runtime.RequireAdmin(ctx); err != nil {
return nil, err
}
if strings.TrimSpace(cmd.ID) == "" {
return nil, status.Error(codes.NotFound, "Player config not found")
}
if msg := validateAdminPlayerConfigInput(cmd.UserID, cmd.Name); msg != "" {
return nil, status.Error(codes.InvalidArgument, msg)
}
var user model.User
if err := m.runtime.DB().WithContext(ctx).Where("id = ?", strings.TrimSpace(cmd.UserID)).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.InvalidArgument, "User not found")
}
return nil, status.Error(codes.Internal, "Failed to save player config")
}
var item model.PlayerConfig
if err := m.runtime.DB().WithContext(ctx).Where("id = ?", cmd.ID).First(&item).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, status.Error(codes.NotFound, "Player config not found")
}
return nil, status.Error(codes.Internal, "Failed to save player config")
}
item.UserID = user.ID
item.Name = strings.TrimSpace(cmd.Name)
item.Description = common.NullableTrimmedStringPtr(cmd.Description)
item.Autoplay = cmd.Autoplay
item.Loop = cmd.Loop
item.Muted = cmd.Muted
item.ShowControls = model.BoolPtr(cmd.ShowControls)
item.Pip = model.BoolPtr(cmd.Pip)
item.Airplay = model.BoolPtr(cmd.Airplay)
item.Chromecast = model.BoolPtr(cmd.Chromecast)
item.IsActive = model.BoolPtr(cmd.IsActive)
item.IsDefault = cmd.IsDefault
if cmd.EncrytionM3U8 != nil {
item.EncrytionM3u8 = model.BoolPtr(*cmd.EncrytionM3U8)
}
if cmd.LogoURL != nil {
item.LogoURL = common.NullableTrimmedStringPtr(cmd.LogoURL)
}
if !common.BoolValue(item.IsActive) {
item.IsDefault = false
}
if err := m.runtime.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
if item.IsDefault {
if err := common.UnsetDefaultPlayerConfigs(tx, item.UserID, item.ID); err != nil {
return err
}
}
return tx.Save(&item).Error
}); err != nil {
return nil, status.Error(codes.Internal, "Failed to save player config")
}
payload, err := m.buildAdminPlayerConfig(ctx, &item)
if err != nil {
return nil, status.Error(codes.Internal, "Failed to save player config")
}
return &payload, nil
}
func (m *Module) DeleteAdminPlayerConfig(ctx context.Context, cmd DeleteAdminPlayerConfigCommand) error {
if _, err := m.runtime.RequireAdmin(ctx); err != nil {
return err
}
if strings.TrimSpace(cmd.ID) == "" {
return status.Error(codes.NotFound, "Player config not found")
}
res := m.runtime.DB().WithContext(ctx).Where("id = ?", cmd.ID).Delete(&model.PlayerConfig{})
if res.Error != nil {
return status.Error(codes.Internal, "Failed to delete player config")
}
if res.RowsAffected == 0 {
return status.Error(codes.NotFound, "Player config not found")
}
return nil
}
func (m *Module) buildAdminPlayerConfig(ctx context.Context, item *model.PlayerConfig) (AdminPlayerConfigView, error) {
if item == nil {
return AdminPlayerConfigView{}, nil
}
ownerEmail, err := m.loadAdminUserEmail(ctx, item.UserID)
if err != nil {
return AdminPlayerConfigView{}, err
}
var createdAt *string
if item.CreatedAt != nil {
formatted := item.CreatedAt.UTC().Format(time.RFC3339)
createdAt = &formatted
}
updated := item.UpdatedAt.UTC().Format(time.RFC3339)
updatedAt := &updated
return AdminPlayerConfigView{ID: item.ID, UserID: item.UserID, Name: item.Name, Description: item.Description, Autoplay: item.Autoplay, Loop: item.Loop, Muted: item.Muted, ShowControls: common.BoolValue(item.ShowControls), Pip: common.BoolValue(item.Pip), Airplay: common.BoolValue(item.Airplay), Chromecast: common.BoolValue(item.Chromecast), IsActive: common.BoolValue(item.IsActive), IsDefault: item.IsDefault, OwnerEmail: ownerEmail, CreatedAt: createdAt, UpdatedAt: updatedAt, EncrytionM3U8: common.BoolValue(item.EncrytionM3u8), LogoURL: common.NullableTrimmedString(item.LogoURL)}, nil
}
func (m *Module) loadAdminUserEmail(ctx context.Context, userID string) (*string, error) {
var user model.User
if err := m.runtime.DB().WithContext(ctx).Select("id, email").Where("id = ?", userID).First(&user).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return common.NullableTrimmedString(&user.Email), nil
}
func validateAdminPlayerConfigInput(userID, name string) string {
if strings.TrimSpace(userID) == "" {
return "User ID is required"
}
if strings.TrimSpace(name) == "" {
return "Name is required"
}
return ""
}

View File

@@ -0,0 +1,83 @@
package playerconfigs
import (
"time"
appv1 "stream.api/internal/gen/proto/app/v1"
"google.golang.org/protobuf/types/known/timestamppb"
"stream.api/internal/modules/common"
)
func presentPlayerConfig(view PlayerConfigView) *appv1.PlayerConfig {
return common.ToProtoPlayerConfig(view.Config)
}
func presentListPlayerConfigsResponse(result *ListPlayerConfigsResult) *appv1.ListPlayerConfigsResponse {
items := make([]*appv1.PlayerConfig, 0, len(result.Items))
for _, item := range result.Items {
items = append(items, presentPlayerConfig(item))
}
return &appv1.ListPlayerConfigsResponse{Configs: items}
}
func presentCreatePlayerConfigResponse(view PlayerConfigView) *appv1.CreatePlayerConfigResponse {
return &appv1.CreatePlayerConfigResponse{Config: presentPlayerConfig(view)}
}
func presentUpdatePlayerConfigResponse(view PlayerConfigView) *appv1.UpdatePlayerConfigResponse {
return &appv1.UpdatePlayerConfigResponse{Config: presentPlayerConfig(view)}
}
func presentAdminPlayerConfig(view AdminPlayerConfigView) *appv1.AdminPlayerConfig {
return &appv1.AdminPlayerConfig{
Id: view.ID,
UserId: view.UserID,
Name: view.Name,
Description: view.Description,
Autoplay: view.Autoplay,
Loop: view.Loop,
Muted: view.Muted,
ShowControls: view.ShowControls,
Pip: view.Pip,
Airplay: view.Airplay,
Chromecast: view.Chromecast,
IsActive: view.IsActive,
IsDefault: view.IsDefault,
OwnerEmail: view.OwnerEmail,
CreatedAt: parseRFC3339ToProto(view.CreatedAt),
UpdatedAt: parseRFC3339ToProto(view.UpdatedAt),
EncrytionM3U8: view.EncrytionM3U8,
LogoUrl: view.LogoURL,
}
}
func presentListAdminPlayerConfigsResponse(result *ListAdminPlayerConfigsResult) *appv1.ListAdminPlayerConfigsResponse {
items := make([]*appv1.AdminPlayerConfig, 0, len(result.Items))
for _, item := range result.Items {
items = append(items, presentAdminPlayerConfig(item))
}
return &appv1.ListAdminPlayerConfigsResponse{Configs: items, Total: result.Total, Page: result.Page, Limit: result.Limit}
}
func presentGetAdminPlayerConfigResponse(view AdminPlayerConfigView) *appv1.GetAdminPlayerConfigResponse {
return &appv1.GetAdminPlayerConfigResponse{Config: presentAdminPlayerConfig(view)}
}
func presentCreateAdminPlayerConfigResponse(view AdminPlayerConfigView) *appv1.CreateAdminPlayerConfigResponse {
return &appv1.CreateAdminPlayerConfigResponse{Config: presentAdminPlayerConfig(view)}
}
func presentUpdateAdminPlayerConfigResponse(view AdminPlayerConfigView) *appv1.UpdateAdminPlayerConfigResponse {
return &appv1.UpdateAdminPlayerConfigResponse{Config: presentAdminPlayerConfig(view)}
}
func parseRFC3339ToProto(value *string) *timestamppb.Timestamp {
if value == nil || *value == "" {
return nil
}
parsed, err := time.Parse(time.RFC3339, *value)
if err != nil {
return nil
}
return timestamppb.New(parsed.UTC())
}

View File

@@ -0,0 +1,133 @@
package playerconfigs
import "stream.api/internal/database/model"
type PlayerConfigView struct {
Config *model.PlayerConfig
}
type ListPlayerConfigsQuery struct {
UserID string
}
type ListPlayerConfigsResult struct {
Items []PlayerConfigView
}
type CreatePlayerConfigCommand struct {
UserID string
Name string
Description *string
Autoplay bool
Loop bool
Muted bool
ShowControls bool
Pip bool
Airplay bool
Chromecast bool
IsActive *bool
IsDefault *bool
EncrytionM3U8 *bool
LogoURL *string
}
type UpdatePlayerConfigCommand struct {
UserID string
ID string
Name string
Description *string
Autoplay bool
Loop bool
Muted bool
ShowControls bool
Pip bool
Airplay bool
Chromecast bool
IsActive *bool
IsDefault *bool
EncrytionM3U8 *bool
LogoURL *string
}
type DeletePlayerConfigCommand struct {
UserID string
ID string
}
type AdminPlayerConfigView struct {
ID string
UserID string
Name string
Description *string
Autoplay bool
Loop bool
Muted bool
ShowControls bool
Pip bool
Airplay bool
Chromecast bool
IsActive bool
IsDefault bool
OwnerEmail *string
CreatedAt *string
UpdatedAt *string
EncrytionM3U8 bool
LogoURL *string
}
type ListAdminPlayerConfigsQuery struct {
Page int32
Limit int32
Search *string
UserID *string
}
type ListAdminPlayerConfigsResult struct {
Items []AdminPlayerConfigView
Total int64
Page int32
Limit int32
}
type GetAdminPlayerConfigQuery struct {
ID string
}
type CreateAdminPlayerConfigCommand struct {
UserID string
Name string
Description *string
Autoplay bool
Loop bool
Muted bool
ShowControls bool
Pip bool
Airplay bool
Chromecast bool
IsActive bool
IsDefault bool
EncrytionM3U8 *bool
LogoURL *string
}
type UpdateAdminPlayerConfigCommand struct {
ID string
UserID string
Name string
Description *string
Autoplay bool
Loop bool
Muted bool
ShowControls bool
Pip bool
Airplay bool
Chromecast bool
IsActive bool
IsDefault bool
EncrytionM3U8 *bool
LogoURL *string
}
type DeleteAdminPlayerConfigCommand struct {
ID string
}