Live video on the AT Protocol
1package statedb
2
3import (
4 "fmt"
5
6 "gorm.io/gorm"
7)
8
9// BrandingBlob stores branding assets for broadcasters
10type BrandingBlob struct {
11 gorm.Model
12 BroadcasterID string `gorm:"index:idx_broadcaster_key,priority:1,unique"`
13 Key string `gorm:"index:idx_broadcaster_key,priority:2,unique"` // "mainLogo", "favicon", "siteTitle", etc.
14 MimeType string // "image/svg+xml", "image/png", "text/plain"
15 Data []byte `gorm:"type:bytea"` // actual blob data
16 Width *int // image width in pixels (nullable)
17 Height *int // image height in pixels (nullable)
18}
19
20// GetBrandingBlob fetches a single branding asset
21func (state *StatefulDB) GetBrandingBlob(broadcasterID, key string) (*BrandingBlob, error) {
22 var blob BrandingBlob
23 err := state.DB.Where("broadcaster_id = ? AND key = ?", broadcasterID, key).First(&blob).Error
24 if err != nil {
25 return nil, err
26 }
27 return &blob, nil
28}
29
30// PutBrandingBlob stores or updates a branding asset
31func (state *StatefulDB) PutBrandingBlob(broadcasterID, key, mimeType string, data []byte, width, height *int) error {
32 // try to find existing blob (including soft-deleted ones)
33 var existing BrandingBlob
34 err := state.DB.Unscoped().Where("broadcaster_id = ? AND key = ?", broadcasterID, key).First(&existing).Error
35
36 if err == gorm.ErrRecordNotFound {
37 // create new blob
38 blob := BrandingBlob{
39 BroadcasterID: broadcasterID,
40 Key: key,
41 MimeType: mimeType,
42 Data: data,
43 Width: width,
44 Height: height,
45 }
46 if err := state.DB.Create(&blob).Error; err != nil {
47 return fmt.Errorf("error creating branding blob: %w", err)
48 }
49 return nil
50 } else if err != nil {
51 return fmt.Errorf("error checking for existing branding blob: %w", err)
52 }
53
54 // update existing blob (restore if soft-deleted)
55 existing.MimeType = mimeType
56 existing.Data = data
57 existing.Width = width
58 existing.Height = height
59 existing.DeletedAt = gorm.DeletedAt{} // clear soft delete
60 if err := state.DB.Unscoped().Save(&existing).Error; err != nil {
61 return fmt.Errorf("error updating branding blob: %w", err)
62 }
63
64 return nil
65}
66
67// ListBrandingKeys returns all keys for a broadcaster
68func (state *StatefulDB) ListBrandingKeys(broadcasterID string) ([]string, error) {
69 var blobs []BrandingBlob
70 err := state.DB.Where("broadcaster_id = ?", broadcasterID).Select("key").Find(&blobs).Error
71 if err != nil {
72 return nil, err
73 }
74
75 keys := make([]string, len(blobs))
76 for i, blob := range blobs {
77 keys[i] = blob.Key
78 }
79 return keys, nil
80}
81
82// DeleteBrandingBlob removes a specific asset
83func (state *StatefulDB) DeleteBrandingBlob(broadcasterID, key string) error {
84 err := state.DB.Where("broadcaster_id = ? AND key = ?", broadcasterID, key).Delete(&BrandingBlob{}).Error
85 if err != nil {
86 return fmt.Errorf("error deleting branding blob: %w", err)
87 }
88 return nil
89}