Live Streams
Live streams are addressable kind 30311 events that describe a stream's URLs, status, and participants (NIP-53). Viewers chat with kind 1311 messages that tag the stream's address.
Finding streams
Cast kind 30311 events with Stream. To find a user's streams, query both the streams they published and the ones that tag them as a participant.
const streams = use$(
() =>
store
.timeline([
{ kinds: [30311], authors: [pubkey] },
{ kinds: [30311], "#p": [pubkey] },
])
.pipe(castTimelineStream(Stream, store)),
[pubkey],
);
const live = streams?.filter((s) => s.status === "live");Reading a stream
status is "live", "planned", or "ended", and streams older than two weeks always read as "ended".
stream.title;
stream.host; // User from the "host" p tag, or the author
stream.participants; // User[]
stream.streamingVideos; // video or HLS URLs
stream.recording; // URL after the stream ends
stream.viewers; // current participant countstream.zaps$ and stream.shares$ return the stream's zaps and reposts. stream.goal$ loads its zap goal when goalPointer is set.
Live chat
stream.chat$ returns the chat messages in the store as StreamChatMessage casts, backed by StreamChatMessagesModel. Each message links back with stream and stream$, and exposes zaps$ and reactions$.
const chat = use$(stream.chat$);
const message = await StreamChatMessageFactory.create(stream.event, "gm").sign(signer);Subscribe to { kinds: [1311], "#a": [address] } on the stream's relays to receive new messages.
Publishing a stream
LiveStreamFactory.create(title) generates the d identifier. Update the stream with modify() as its status changes, since clients only keep the latest version.
const event = await LiveStreamFactory.create("Building with applesauce")
.streamingUrl("https://example.com/live.m3u8")
.status("live")
.startTime(new Date())
.addParticipant({ pubkey }, "speaker")
.sign(signer);
const ended = await LiveStreamFactory.modify(event).status("ended").recordingUrl(vod).sign(signer);Helpers
getStreamTitle,getStreamStatus,getStreamHost,getStreamParticipants, andgetStreamStreamingURLsread a stream;getStreamParticipantskeeps each participant's rolegetStreamRelays,getStreamGoalPointer,getStreamStartTime,getStreamEndTime, andgetStreamViewersread the rest of the tagsisValidStreamChatMessage(event)andgetStreamChatMessageStream(event)read chat messages
Example: Stream viewer
Best Practices
- Read chat from the relays in
stream.relayswhen set, since streams pick where their chat lives - Prefer
streamingVideosoverstreamingURLsfor a video player, and fall back torecordingonce ended - Set the status to
"ended"when a stream stops, since applesauce only treats streams as ended after two weeks