package cast import ( "context" "sync" "time" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) type CachedDevice struct { Device UpdatedAt time.Time } func (d CachedDevice) Expired() bool { return d.UpdatedAt.Add(30 * time.Minute).Before(time.Now()) } var cache sync.Map func getCachedDevice(uuid string) (Device, bool) { value, exists := cache.Load(uuid) if !exists { return nil, false } cachedDevice, ok := value.(CachedDevice) if !ok { return nil, false } if cachedDevice.Expired() { return nil, false } return cachedDevice.Device, true } func cacheDevice(dev Device) { cache.Store(dev.DeviceID(), CachedDevice{ Device: dev, UpdatedAt: time.Now(), }) } func getDeviceClientByID(ctx context.Context, deviceID string) (Client, error) { device, err := findCachedDeviceByID(ctx, deviceID) if err != nil { return nil, errors.WithStack(err) } client, err := NewClient(ctx, device) if err != nil { return nil, errors.WithStack(err) } return client, nil } func findCachedDeviceByID(ctx context.Context, deviceID string) (Device, error) { device, exists := getCachedDevice(deviceID) if exists { return device, nil } ctx, cancel := context.WithCancel(ctx) defer cancel() device, err := Find(ctx, deviceID) if err != nil { return nil, errors.WithStack(err) } cacheDevice(device) return device, nil } func ListDevices(ctx context.Context, refresh bool) ([]Device, error) { devices := make([]Device, 0) if !refresh { cache.Range(func(key, value any) bool { cached, ok := value.(CachedDevice) if !ok || cached.Expired() { return true } devices = append(devices, cached.Device) return true }) return devices, nil } devices, err := SearchDevices(ctx) if err != nil { return nil, errors.WithStack(err) } return devices, nil } var searchDevicesMutex sync.Mutex func SearchDevices(ctx context.Context) ([]Device, error) { searchDevicesMutex.Lock() defer searchDevicesMutex.Unlock() devices, err := Scan(ctx) if err != nil { return nil, errors.WithStack(err) } for _, device := range devices { cacheDevice(device) } return devices, nil } var loadURLMutex sync.Mutex func LoadURL(ctx context.Context, deviceID string, url string) error { loadURLMutex.Lock() defer loadURLMutex.Unlock() client, err := getDeviceClientByID(ctx, deviceID) if err != nil { return errors.WithStack(err) } defer func() { if err := client.Close(); err != nil { logger.Error(ctx, "could not close client", logger.CapturedE(errors.WithStack(err))) } }() if err := client.Load(ctx, url); err != nil { return errors.WithStack(err) } return nil } var stopCastMutex sync.Mutex func StopCast(ctx context.Context, deviceID string) error { stopCastMutex.Lock() defer stopCastMutex.Unlock() client, err := getDeviceClientByID(ctx, deviceID) if err != nil { return errors.WithStack(err) } defer func() { if err := client.Close(); err != nil { logger.Error(ctx, "could not close client", logger.CapturedE(errors.WithStack(err))) } }() if err := client.Unload(ctx); err != nil { return errors.WithStack(err) } return nil } var getStatusMutex sync.Mutex func GetStatus(ctx context.Context, deviceID string) (DeviceStatus, error) { getStatusMutex.Lock() defer getStatusMutex.Unlock() client, err := getDeviceClientByID(ctx, deviceID) if err != nil { return nil, errors.WithStack(err) } defer func() { if err := client.Close(); err != nil { logger.Error(ctx, "could not close client", logger.CapturedE(errors.WithStack(err))) } }() status, err := client.Status(ctx) if err != nil { return nil, errors.WithStack(err) } return status, nil }