diff --git a/pkg/channels/manager.go b/pkg/channels/manager.go index f7f625271..8a8f9dc03 100644 --- a/pkg/channels/manager.go +++ b/pkg/channels/manager.go @@ -100,6 +100,10 @@ type Manager struct { channelHashes map[string]string // channel name → config hash } +type mediaStoreSetter interface { + SetMediaStore(s media.MediaStore) +} + // ManagerOption configures a channel Manager. type ManagerOption func(*Manager) @@ -485,6 +489,22 @@ func NewManager( return m, nil } +// SetMediaStore updates the store used by the manager and every channel that +// accepts media store injection. Gateway reload creates a fresh store, so +// keeping existing channels on the same store as the agent is required for +// inbound media refs to remain resolvable after reload. +func (m *Manager) SetMediaStore(store media.MediaStore) { + m.mu.Lock() + defer m.mu.Unlock() + + m.mediaStore = store + for _, ch := range m.channels { + if setter, ok := ch.(mediaStoreSetter); ok { + setter.SetMediaStore(store) + } + } +} + // GetStreamer implements bus.StreamDelegate. // It checks if the named channel supports streaming and returns a Streamer. func (m *Manager) GetStreamer(ctx context.Context, channelName, chatID string) (bus.Streamer, bool) { @@ -582,7 +602,7 @@ func (m *Manager) initChannel(typeName, channelName string) { } else { // Inject MediaStore if channel supports it if m.mediaStore != nil { - if setter, ok := ch.(interface{ SetMediaStore(s media.MediaStore) }); ok { + if setter, ok := ch.(mediaStoreSetter); ok { setter.SetMediaStore(m.mediaStore) } } diff --git a/pkg/channels/manager_test.go b/pkg/channels/manager_test.go index 5aeabc888..8c2f6ecf8 100644 --- a/pkg/channels/manager_test.go +++ b/pkg/channels/manager_test.go @@ -15,6 +15,7 @@ import ( "github.com/sipeed/picoclaw/pkg/bus" "github.com/sipeed/picoclaw/pkg/config" runtimeevents "github.com/sipeed/picoclaw/pkg/events" + "github.com/sipeed/picoclaw/pkg/media" "github.com/sipeed/picoclaw/pkg/utils" ) @@ -149,6 +150,26 @@ func newTestManager() *Manager { } } +func TestSetMediaStorePropagatesToExistingChannels(t *testing.T) { + oldStore := media.NewFileMediaStore() + newStore := media.NewFileMediaStore() + ch := &mockChannel{} + ch.SetMediaStore(oldStore) + + m := newTestManager() + m.mediaStore = oldStore + m.channels["telegram"] = ch + + m.SetMediaStore(newStore) + + if m.mediaStore != newStore { + t.Fatal("manager media store was not updated") + } + if got := ch.GetMediaStore(); got != newStore { + t.Fatalf("channel media store = %p, want %p", got, newStore) + } +} + func TestStartAll_AllChannelsFail_ReturnsJoinedError(t *testing.T) { m := newTestManager() errA := errors.New("channel-a start failed") diff --git a/pkg/gateway/gateway.go b/pkg/gateway/gateway.go index bf64e0453..6171fd65f 100644 --- a/pkg/gateway/gateway.go +++ b/pkg/gateway/gateway.go @@ -646,6 +646,9 @@ func restartServices( if fms, ok := runningServices.MediaStore.(*media.FileMediaStore); ok { fms.Start() } + if runningServices.ChannelManager != nil { + runningServices.ChannelManager.SetMediaStore(runningServices.MediaStore) + } al.SetMediaStore(runningServices.MediaStore) al.SetChannelManager(runningServices.ChannelManager)