Skip to content

Commit d47065b

Browse files
feat: Re-introduce watch page reload on live state change (#1894)
1 parent 06f54d6 commit d47065b

4 files changed

Lines changed: 54 additions & 3 deletions

File tree

api/worker_grpc.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ func (s server) NotifyStreamFinished(ctx context.Context, request *pb.StreamFini
235235
if err != nil {
236236
logger.Error("Can't set stream not live", "err", err)
237237
}
238-
NotifyViewersLiveState(uint(request.StreamID), false)
239238

240239
return &pb.Status{Ok: true}, nil
241240
}
@@ -600,7 +599,6 @@ func (s server) NotifyStreamStarted(ctx context.Context, request *pb.StreamStart
600599
default:
601600
s.StreamsDao.SaveCOMBURL(&stream, request.HlsUrl)
602601
}
603-
NotifyViewersLiveState(stream.Model.ID, true)
604602
NotifyLiveUpdateCourseWentLive(stream.Model.ID)
605603
}()
606604

cmd/tumlive/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ func run(ctx context.Context) error {
145145
opts := []runner_manager.Option{
146146
runner_manager.WithMassStorage(tools.Cfg.Paths.Mass),
147147
runner_manager.WithCamService(camService),
148+
runner_manager.WithLiveStateNotifier(api.NotifyViewersLiveState),
148149
}
149150
var subtitleClient pb.SubtitleGeneratorClient
150151
if tools.Cfg.VoiceService.Host != "" {

pkg/runner_manager/manager.go

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,20 @@ type Manager struct {
5353
camsHandledLock sync.Mutex
5454

5555
lightLock sync.Mutex
56+
57+
liveStateNotifier LiveStateNotifier
58+
liveNotified map[uint]bool
59+
liveNotifiedLock sync.Mutex
5660
}
5761

5862
type CamService interface {
5963
For(address string, cameraType model.CameraType) (camera.Cam, error)
6064
}
6165

66+
// LiveStateNotifier is called whenever a stream transitions into or out of the live state,
67+
// so that other parts of the application (e.g. the websocket hub) can react to it.
68+
type LiveStateNotifier func(streamID uint, live bool)
69+
6270
// New returns a new instance of Manager with the given Options
6371
func New(dao dao.DaoWrapper, opts ...Option) *Manager {
6472
m := Manager{
@@ -69,6 +77,7 @@ func New(dao dao.DaoWrapper, opts ...Option) *Manager {
6977
})).With("service", "runner_manager"),
7078
streamStartLock: sync.Mutex{},
7179
camsHandled: make(map[uint]bool),
80+
liveNotified: make(map[uint]bool),
7281
}
7382
m.applyOpts(opts)
7483
return &m
@@ -157,6 +166,14 @@ func WithSubtitleClient(client pb.SubtitleGeneratorClient, auth string) Option {
157166
}
158167
}
159168

169+
// WithLiveStateNotifier registers a callback invoked when a stream starts or stops being live,
170+
// e.g. to notify viewers via websocket.
171+
func WithLiveStateNotifier(notifier LiveStateNotifier) Option {
172+
return func(m *Manager) {
173+
m.liveStateNotifier = notifier
174+
}
175+
}
176+
160177
func (m *Manager) applyOpts(opts []Option) {
161178
for _, opt := range opts {
162179
opt(m)
@@ -274,6 +291,8 @@ func (m *Manager) streamStarted(ctx context.Context, req *protobuf.StreamStartNo
274291
}
275292
m.streamStartLock.Unlock()
276293

294+
m.notifyLiveState(stream.ID, true)
295+
277296
err = m.handleCamera(ctx, stream)
278297
if err != nil {
279298
log.Error("failed to handle camera", "stream", stream.ID, "err", err)
@@ -287,6 +306,28 @@ func (m *Manager) streamStarted(ctx context.Context, req *protobuf.StreamStartNo
287306
return nil
288307
}
289308

309+
// notifyLiveState invokes the configured LiveStateNotifier, making sure viewers are only notified
310+
// once per stream when going live, even though streamStarted is called once per stream version.
311+
func (m *Manager) notifyLiveState(streamID uint, live bool) {
312+
if m.liveStateNotifier == nil {
313+
return
314+
}
315+
if live {
316+
m.liveNotifiedLock.Lock()
317+
alreadyNotified := m.liveNotified[streamID]
318+
m.liveNotified[streamID] = true
319+
m.liveNotifiedLock.Unlock()
320+
if alreadyNotified {
321+
return
322+
}
323+
} else {
324+
m.liveNotifiedLock.Lock()
325+
delete(m.liveNotified, streamID)
326+
m.liveNotifiedLock.Unlock()
327+
}
328+
m.liveStateNotifier(streamID, live)
329+
}
330+
290331
var errNotNoLectureSource = fmt.Errorf("no source configured for this lecture hall ip")
291332

292333
func (m *Manager) requestStreamVersion(ctx context.Context, s model.Stream, client protobuf.RunnerServiceClient, lh model.LectureHall, version protobuf.StreamVersion) (*protobuf.StreamResponse, error) {
@@ -598,10 +639,12 @@ func (m *Manager) handleLightsOff(stream model.Stream) (err error) {
598639

599640
func (m *Manager) streamEnded(ctx context.Context, notification *protobuf.StreamEndNotification) error {
600641
m.logger.Debug("streamEnd", "payload", notification)
601-
err := m.dao.StreamsDao.SetStreamNotLiveById(uint(notification.GetStream().GetId()))
642+
streamID := uint(notification.GetStream().GetId())
643+
err := m.dao.StreamsDao.SetStreamNotLiveById(streamID)
602644
if err != nil {
603645
return err
604646
}
647+
m.notifyLiveState(streamID, false)
605648

606649
stream, err := m.dao.StreamsDao.GetStreamByID(ctx, strconv.FormatUint(notification.GetStream().GetId(), 10))
607650
if err != nil {

web/ts/components/video-information.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export function videoInformationContext(streamId: number): AlpineComponent {
3131
this.handleViewersUpdate(data);
3232
} else if ("description" in data) {
3333
this.handleDescriptionUpdate(data);
34+
} else if ("live" in data) {
35+
this.handleLiveState(data);
3436
}
3537
};
3638
SocketConnections.ws.subscribe(handler);
@@ -44,6 +46,13 @@ export function videoInformationContext(streamId: number): AlpineComponent {
4446
this.less = upd.description.full.length > CUTOFFLENGTH;
4547
this.description = upd.description.full;
4648
},
49+
50+
handleLiveState(upd: { live: boolean }) {
51+
if (upd.live) {
52+
// stream just went live, reload the page to pick up the stream
53+
window.location.reload();
54+
}
55+
},
4756
} as AlpineComponent;
4857
}
4958

0 commit comments

Comments
 (0)