aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/features/messages.js34
1 files changed, 26 insertions, 8 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 4820baa..c82670b 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -8975,7 +8975,20 @@ export function messagesFeature(ctx) {
b.ws = ws;
bcRecord(b);
b.reconnecting = false;
- if (info.id !== b.id) toast(t('goLiveRestarted'));
+ // The server couldn't take us back (it restarted, or we were gone
+ // too long): this is a new stream at a new address. The old one is
+ // over — said so, with no recording (there's next to nothing in
+ // it) — and the new one is announced as soon as it plays.
+ if (info.id !== b.id) {
+ toast(t('goLiveRestarted'));
+ const old = { ...b, recording: null };
+ Object.assign(b, { id: info.id, liveUrl: info.live, recording: info.recording, a: LIVE_KIND + ':' + b.pk + ':' + info.id });
+ bcAnnounce(old, 'ended').catch(() => {});
+ bcPlaying(b).then(() => (bc === b && b.phase === 'live' ? bcAnnounce(b, 'live') : null)).then(() => {
+ // the streamer's own view of it follows to the new address
+ if (bc === b && b.ev && ui.liveStream && ui.liveStream.a === old.a) { ui.liveStream = { a: b.a, seed: b.ev }; render(); }
+ }).catch(() => {});
+ }
render();
return;
} catch {}
@@ -8994,14 +9007,23 @@ export function messagesFeature(ctx) {
['p', id.pubkey, '', 'Host'], ['relays', ...relays.slice(0, 6)], CLIENT_TAG];
if (me.picture) tags.push(['image', me.picture]);
if (status === 'live' && b.viewers != null) tags.push(['current_participants', String(b.viewers)]);
- if (status === 'ended') tags.push(['ends', String(Math.floor(Date.now() / 1000))], ['recording', b.recording]);
+ if (status === 'ended') tags.push(['ends', String(Math.floor(Date.now() / 1000))]);
+ if (status === 'ended' && b.recording) tags.push(['recording', b.recording]);
const partial = { kind: LIVE_KIND, content: '', created_at: Math.floor(Date.now() / 1000), tags };
const evt = id.signer instanceof Uint8Array ? finalizeEvent(partial, id.signer) : await id.signer.signEvent(partial);
takeLive([evt]);
- b.ev = evt;
+ if (bc === b) b.ev = evt;
scheduleRepaint();
return publishOn(relays, evt);
}
+ async function bcPlaying(b) {
+ const url = b.liveUrl;
+ for (let i = 0; i < 40 && bc === b && b.phase === 'live' && b.liveUrl === url; i++) {
+ await new Promise((r) => setTimeout(r, 500));
+ const pl = await fetch(url, { cache: 'no-store' }).then((r) => r.text()).catch(() => '');
+ if ((pl.match(/\.ts\s*$/gm) || []).length >= 3) break;
+ }
+ }
async function startBroadcast() {
const b = bc;
if (!b || b.phase !== 'setup' || !b.media) return;
@@ -9019,11 +9041,7 @@ export function messagesFeature(ctx) {
// three segments — Chrome's own player won't start a live playlist with
// fewer (it fails to parse it), so a viewer who comes at once would get
// an error, not the stream
- for (let i = 0; i < 40 && bc === b && b.phase === 'live'; i++) {
- await new Promise((r) => setTimeout(r, 500));
- const pl = await fetch(b.liveUrl, { cache: 'no-store' }).then((r) => r.text()).catch(() => '');
- if ((pl.match(/\.ts\s*$/gm) || []).length >= 3) break;
- }
+ await bcPlaying(b);
if (bc !== b || b.phase !== 'live') return;
await bcAnnounce(b, 'live');
b.tick = setInterval(() => bcTick(b), 60_000);