diff --git a/js/app/components/live-dashboard/stream-monitor.tsx b/js/app/components/live-dashboard/stream-monitor.tsx
index 8c8d6e02..05d1529d 100644
--- a/js/app/components/live-dashboard/stream-monitor.tsx
+++ b/js/app/components/live-dashboard/stream-monitor.tsx
@@ -5,6 +5,7 @@ import {
useLivestream,
useLivestreamStore,
usePlayerStore,
+ useSegmentTiming,
zero,
} from "@streamplace/components";
import { DesktopUi } from "components/mobile/desktop-ui";
@@ -14,7 +15,6 @@ import { useEffect, useState } from "react";
import { Image, TouchableOpacity, View } from "react-native";
import Animated from "react-native-reanimated";
import { useLiveUser } from "../../hooks/useLiveUser";
-import { useSegmentTiming } from "../../hooks/useSegmentTiming";
import StreamScreen from "./live-selector";
const { flex, bg, r, borders, layout, p, text, w, h, mt } = zero;
diff --git a/js/app/components/mobile/desktop-ui.tsx b/js/app/components/mobile/desktop-ui.tsx
index 8cf2b6b1..689c5a7a 100644
--- a/js/app/components/mobile/desktop-ui.tsx
+++ b/js/app/components/mobile/desktop-ui.tsx
@@ -54,8 +54,6 @@ export function DesktopUi({
setShowCountdown,
recordSubmitted,
setRecordSubmitted,
- ingestStarting,
- setIngestStarting,
toggleGoLive,
} = useLivestreamInfo();
const { width, height } = usePlayerDimensions();
@@ -112,11 +110,8 @@ export function DesktopUi({
return () => {
if (fadeTimeout.current) clearTimeout(fadeTimeout.current);
- if (ingestStarting) {
- setIngestStarting(false);
- }
};
- }, [ingestStarting, setIngestStarting, resetFadeTimer]);
+ }, [resetFadeTimer]);
const animatedFadeStyle = useAnimatedStyle(() => ({
opacity: shouldShowFloatingMetrics ? 1 : fadeOpacity.value,
@@ -252,7 +247,6 @@ export function DesktopUi({
diff --git a/js/app/components/mobile/ui.tsx b/js/app/components/mobile/ui.tsx
index aa7aee33..c33d2bba 100644
--- a/js/app/components/mobile/ui.tsx
+++ b/js/app/components/mobile/ui.tsx
@@ -7,6 +7,7 @@ import {
Toast,
useAvatars,
useCameraToggle,
+ useLivestream,
useLivestreamInfo,
useLivestreamStore,
useMuted,
@@ -69,8 +70,6 @@ export function MobileUi({
setShowCountdown,
recordSubmitted,
setRecordSubmitted,
- ingestStarting,
- setIngestStarting,
toggleGoLive,
toggleStopStream,
} = useLivestreamInfo();
@@ -83,32 +82,19 @@ export function MobileUi({
const setMuteWasForced = usePlayerStore((state) => state.setMuteWasForced);
const muted = useMuted();
const setMuted = useSetMuted();
+ const ls = useLivestream();
const { shouldShowFloatingMetrics, shouldShowChatSidePanel, chatPanelWidth } =
useResponsiveLayout();
const [showLoading, setShowLoading] = useState(false);
- useEffect(() => {
- return () => {
- if (ingestStarting) {
- setIngestStarting(false);
- }
- };
- }, [ingestStarting, setIngestStarting]);
-
useEffect(() => {
if (recordSubmitted) setShowLoading(false);
}, [recordSubmitted]);
- const isSelfAndNotLive = ingest === "new";
- const isLive = ingest !== null && ingest !== "new";
-
- useEffect(() => {
- if (isLive && ingestStarting) {
- setIngestStarting(false);
- }
- }, [isLive, ingestStarting, setIngestStarting]);
+ const isSelfAndNotLive = ingest !== null && ls === null;
+ const isSelfAndLive = ingest !== null && ls !== null;
const FADE_OUT_DELAY = 4000;
const fadeOpacity = useSharedValue(1);
@@ -225,7 +211,7 @@ export function MobileUi({
- {shouldShowFloatingMetrics && isLive && (
+ {shouldShowFloatingMetrics && isSelfAndLive && (
)}
@@ -281,7 +266,7 @@ export function MobileUi({
- {showChat === undefined && ingest !== "new" && (
+ {showChat === undefined && !isSelfAndNotLive && (
)}
>
diff --git a/js/app/hooks/useSegmentTiming.tsx b/js/app/hooks/useSegmentTiming.tsx
deleted file mode 100644
index cd6a8e52..00000000
--- a/js/app/hooks/useSegmentTiming.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import { useLivestreamStore } from "@streamplace/components";
-import { useEffect, useRef, useState } from "react";
-
-export type ConnectionQuality = "good" | "degraded" | "poor";
-
-function getLiveConnectionQuality(
- timeBetweenSegments: number | null,
- range: number | null,
- numOfSegments: number = 1,
-): ConnectionQuality {
- if (timeBetweenSegments === null || range === null) return "poor";
-
- if (timeBetweenSegments <= 1500 && range <= (1500 * 60) / numOfSegments) {
- return "good";
- }
- if (timeBetweenSegments <= 3000 && range <= (3000 * 60) / numOfSegments) {
- return "degraded";
- }
- return "poor";
-}
-
-export function useSegmentTiming() {
- const latestSegment = useLivestreamStore((x) => x.segment);
- const [segmentDeltas, setSegmentDeltas] = useState([]);
- const prevSegmentRef = useRef();
- const prevTimestampRef = useRef(null);
-
- // Dummy state to force update every second
- const [, setNow] = useState(Date.now());
-
- useEffect(() => {
- const interval = setInterval(() => {
- setNow(Date.now());
- }, 1000);
- return () => clearInterval(interval);
- }, []);
-
- useEffect(() => {
- if (latestSegment && prevSegmentRef.current !== latestSegment) {
- const now = Date.now();
- if (prevTimestampRef.current !== null) {
- const delta = now - prevTimestampRef.current;
- // Only store the last 25 deltas
- setSegmentDeltas((prev) => [...prev, delta].slice(-25));
- }
- prevTimestampRef.current = now;
- prevSegmentRef.current = latestSegment;
- }
- }, [latestSegment]);
-
- // The most recent time between segments
- const timeBetweenSegments =
- segmentDeltas.length > 0
- ? segmentDeltas[segmentDeltas.length - 1]
- : prevTimestampRef.current
- ? Date.now() - prevTimestampRef.current
- : null;
-
- // Calculate mean and range of deltas
- const mean =
- segmentDeltas.length > 0
- ? Math.round(
- segmentDeltas.reduce((acc, curr) => acc + curr, 0) /
- segmentDeltas.length,
- )
- : null;
-
- const range =
- segmentDeltas.length > 0
- ? Math.max(...segmentDeltas) - Math.min(...segmentDeltas)
- : null;
-
- let to_ret = {
- segmentDeltas,
- timeBetweenSegments,
- mean,
- range,
- connectionQuality: "poor",
- };
-
- to_ret.connectionQuality = getLiveConnectionQuality(
- timeBetweenSegments,
- range,
- segmentDeltas.length,
- );
-
- return to_ret;
-}
diff --git a/js/app/src/router.tsx b/js/app/src/router.tsx
index 055b7a4f..c69c71cc 100644
--- a/js/app/src/router.tsx
+++ b/js/app/src/router.tsx
@@ -542,7 +542,7 @@ export function StreamplaceDrawer() {
// are we in the live dashboard?
const [isLiveDashboard, setIsLiveDashboard] = useState(false);
useEffect(() => {
- if (!isLiveDashboard && userIsLive) {
+ if (!isLiveDashboard && userIsLive && isWeb) {
toast.show(
"You are streaming!",
"Do you want to go to your Live Dashboard?",
diff --git a/js/components/src/components/dashboard/header.tsx b/js/components/src/components/dashboard/header.tsx
index f10710eb..9b6d94f1 100644
--- a/js/components/src/components/dashboard/header.tsx
+++ b/js/components/src/components/dashboard/header.tsx
@@ -8,7 +8,7 @@ interface MetricItemProps {
icon: any;
label: string;
value: string;
- status?: "good" | "warning" | "error";
+ status?: "good" | "warning" | "error" | "pre-live";
}
function MetricItem({ icon: Icon, label, value, status }: MetricItemProps) {
diff --git a/js/components/src/components/mobile-player/ui/input.tsx b/js/components/src/components/mobile-player/ui/input.tsx
index ac5888ed..3fa09e1a 100644
--- a/js/components/src/components/mobile-player/ui/input.tsx
+++ b/js/components/src/components/mobile-player/ui/input.tsx
@@ -7,7 +7,6 @@ const { gap, h, layout, mt, p, position, px, py, sizes, w } = atoms;
type InputPanelProps = {
title: string | undefined;
setTitle: (title: string) => void;
- ingestStarting: boolean;
toggleGoLive: () => void;
isLive: boolean;
toggleStopStream?: () => void;
@@ -16,7 +15,6 @@ type InputPanelProps = {
export function InputPanel({
title,
setTitle,
- ingestStarting,
toggleGoLive,
isLive,
toggleStopStream,
@@ -51,9 +49,7 @@ export function InputPanel({
/>
)}
- {ingestStarting ? (
- Starting your stream...
- ) : isLive ? (
+ {isLive ? (
;
let color = "#d44";
- if (connectionQuality === "good") {
+ if (connectionQuality === "pre-live") {
+ icon = ;
+ color = atoms.colors.blue[500];
+ } else if (connectionQuality === "good") {
icon = ;
color = "#4d4";
} else if (connectionQuality === "degraded") {
@@ -23,6 +26,18 @@ export function MetricsPanel({ showMetrics }: MetricsPanelProps) {
color = "#d44";
}
+ const connectionText = () => {
+ if (connectionQuality === "pre-live") {
+ return "READY TO STREAM";
+ } else if (connectionQuality === "good") {
+ return "GOOD";
+ } else if (connectionQuality === "degraded") {
+ return "DEGRADED";
+ } else {
+ return "POOR";
+ }
+ };
+
return (
- {connectionQuality.toUpperCase()}
+ {connectionText()}
{showMetrics && (
diff --git a/js/components/src/components/mobile-player/video-async.native.tsx b/js/components/src/components/mobile-player/video-async.native.tsx
index 821c236b..ce3cd04a 100644
--- a/js/components/src/components/mobile-player/video-async.native.tsx
+++ b/js/components/src/components/mobile-player/video-async.native.tsx
@@ -285,9 +285,9 @@ export function NativeWHEP(props?: {
export function NativeIngestPlayer(props?: {
objectFit?: "contain" | "cover";
}) {
- const ingestStarting = useIngestPlayerStore((x) => x.ingestStarting);
const ingestMediaSource = useIngestPlayerStore((x) => x.ingestMediaSource);
const ingestAutoStart = useIngestPlayerStore((x) => x.ingestAutoStart);
+ const setIngestLive = useIngestPlayerStore((x) => x.setIngestLive);
const setStatus = useIngestPlayerStore((x) => x.setStatus);
const setVideoRef = usePlayerStore((x) => x.setVideoRef);
@@ -377,17 +377,19 @@ export function NativeIngestPlayer(props?: {
}, [ingestMediaSource, ingestCamera]);
useEffect(() => {
- if (!ingestStarting && !ingestAutoStart) {
- setRemoteMediaStream(null);
- return;
+ if (localMediaStream) {
+ setIngestLive(true);
}
+ }, [localMediaStream]);
+
+ useEffect(() => {
if (!localMediaStream) {
return;
}
console.log("setting remote media stream", localMediaStream);
// @ts-expect-error: WebRTCMediaStream may not have all MediaStream properties, but is compatible for our use
setRemoteMediaStream(localMediaStream);
- }, [localMediaStream, ingestStarting, ingestAutoStart, setRemoteMediaStream]);
+ }, [localMediaStream, ingestAutoStart, setRemoteMediaStream]);
if (!localMediaStream) {
return null;
diff --git a/js/components/src/components/mobile-player/video.tsx b/js/components/src/components/mobile-player/video.tsx
index ace0d103..fb57b1ed 100644
--- a/js/components/src/components/mobile-player/video.tsx
+++ b/js/components/src/components/mobile-player/video.tsx
@@ -542,7 +542,6 @@ export function WebRTCPlayerInner({
}
export function WebcamIngestPlayer(props: VideoProps) {
- const ingestStarting = usePlayerStore((x) => x.ingestStarting);
const ingestMediaSource = usePlayerStore((x) => x.ingestMediaSource);
const ingestAutoStart = usePlayerStore((x) => x.ingestAutoStart);
@@ -607,7 +606,7 @@ export function WebcamIngestPlayer(props: VideoProps) {
}, [ingestMediaSource]);
useEffect(() => {
- if (!ingestStarting && !ingestAutoStart) {
+ if (!ingestAutoStart) {
setRemoteMediaStream(null);
return;
}
@@ -615,7 +614,7 @@ export function WebcamIngestPlayer(props: VideoProps) {
return;
}
setRemoteMediaStream(localMediaStream);
- }, [localMediaStream, ingestStarting, ingestAutoStart]);
+ }, [localMediaStream, ingestAutoStart]);
useEffect(() => {
if (!videoElement) {
diff --git a/js/components/src/hooks/useLivestreamInfo.ts b/js/components/src/hooks/useLivestreamInfo.ts
index 690a1142..3bef6835 100644
--- a/js/components/src/hooks/useLivestreamInfo.ts
+++ b/js/components/src/hooks/useLivestreamInfo.ts
@@ -6,8 +6,6 @@ import { useCreateStreamRecord } from "../streamplace-store";
export function useLivestreamInfo(url?: string) {
const ingest = usePlayerStore((x) => x.ingestConnectionState);
const profile = useLivestreamStore((x) => x.profile);
- const ingestStarting = usePlayerStore((x) => x.ingestStarting);
- const setIngestStarting = usePlayerStore((x) => x.setIngestStarting);
const setIngestLive = usePlayerStore((x) => x.setIngestLive);
const stopIngest = usePlayerStore((x) => x.stopIngest);
@@ -39,20 +37,13 @@ export function useLivestreamInfo(url?: string) {
keyboardHeight?: number,
closeKeyboard?: () => void,
) => {
- if (!ingestStarting) {
- // Optionally close keyboard if provided
- if (closeKeyboard) closeKeyboard();
- setShowCountdown(true);
- setIngestStarting(true);
- setIngestLive(true);
- // wait ~3 seconds before announcing
- setTimeout(() => {
- handleSubmit();
- }, 3000);
- } else {
- setIngestStarting(false);
- setIngestLive(false);
- }
+ // Optionally close keyboard if provided
+ if (closeKeyboard) closeKeyboard();
+ setShowCountdown(true);
+ // wait ~3 seconds before announcing
+ setTimeout(() => {
+ handleSubmit();
+ }, 3000);
};
// Stop the current broadcast
@@ -70,8 +61,6 @@ export function useLivestreamInfo(url?: string) {
setShowCountdown,
recordSubmitted,
setRecordSubmitted,
- ingestStarting,
- setIngestStarting,
handleSubmit,
toggleGoLive,
toggleStopStream,
diff --git a/js/components/src/hooks/useSegmentTiming.tsx b/js/components/src/hooks/useSegmentTiming.tsx
index d01f346a..94571052 100644
--- a/js/components/src/hooks/useSegmentTiming.tsx
+++ b/js/components/src/hooks/useSegmentTiming.tsx
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "react";
-import { useLivestreamStore } from "../livestream-store";
+import { useLivestream, useLivestreamStore } from "../livestream-store";
-export type ConnectionQuality = "good" | "degraded" | "poor";
+export type ConnectionQuality = "good" | "degraded" | "poor" | "pre-live";
function getLiveConnectionQuality(
timeBetweenSegments: number | null,
@@ -24,6 +24,7 @@ export function useSegmentTiming() {
const [segmentDeltas, setSegmentDeltas] = useState([]);
const prevSegmentRef = useRef();
const prevTimestampRef = useRef(null);
+ const ls = useLivestream();
// Dummy state to force update every second
const [, setNow] = useState(Date.now());
@@ -84,5 +85,9 @@ export function useSegmentTiming() {
segmentDeltas.length,
);
+ if (!ls) {
+ to_ret.connectionQuality = "pre-live";
+ }
+
return to_ret;
}
diff --git a/js/components/src/player-store/player-state.tsx b/js/components/src/player-store/player-state.tsx
index ac885209..fa48bc21 100644
--- a/js/components/src/player-store/player-state.tsx
+++ b/js/components/src/player-store/player-state.tsx
@@ -38,12 +38,6 @@ export interface PlayerState {
/** Function to set the source URL */
setSrc: (src: string) => void;
- /** Flag indicating if ingest (stream input) is currently starting */
- ingestStarting: boolean;
-
- /** Function to set the ingestStarting flag */
- setIngestStarting: (ingestStarting: boolean) => void;
-
/** Flag indicating if ingest is live */
ingestLive: boolean;
setIngestLive: (ingestLive: boolean) => void;
diff --git a/js/components/src/player-store/player-store.tsx b/js/components/src/player-store/player-store.tsx
index 115dfc94..4eb3ea0b 100644
--- a/js/components/src/player-store/player-store.tsx
+++ b/js/components/src/player-store/player-store.tsx
@@ -28,10 +28,6 @@ export const makePlayerStore = (id?: string): StoreApi => {
src: "",
setSrc: (src: string) => set(() => ({ src })),
- ingestStarting: false,
- setIngestStarting: (ingestStarting: boolean) =>
- set(() => ({ ingestStarting })),
-
ingestMediaSource: undefined,
setIngestMediaSource: (ingestMediaSource: IngestMediaSource | undefined) =>
set(() => ({ ingestMediaSource })),
@@ -45,7 +41,7 @@ export const makePlayerStore = (id?: string): StoreApi => {
ingestConnectionState: RTCPeerConnectionState | null,
) => set(() => ({ ingestConnectionState })),
- ingestAutoStart: false,
+ ingestAutoStart: true,
setIngestAutoStart: (ingestAutoStart: boolean) =>
set(() => ({ ingestAutoStart })),
diff --git a/js/components/src/player-store/single-player-provider.tsx b/js/components/src/player-store/single-player-provider.tsx
index 5effed54..950a39e1 100644
--- a/js/components/src/player-store/single-player-provider.tsx
+++ b/js/components/src/player-store/single-player-provider.tsx
@@ -143,16 +143,12 @@ export function useCurrentPlayerRendition(): [
* Hook to get the ingest state of the current player
*/
export function useCurrentPlayerIngest(): {
- starting: boolean;
- setStarting: (starting: boolean) => void;
connectionState: RTCPeerConnectionState | null;
setConnectionState: (state: RTCPeerConnectionState | null) => void;
startedTimestamp: number | null;
setStartedTimestamp: (timestamp: number | null) => void;
} {
return useCurrentPlayerStore((state) => ({
- starting: state.ingestStarting,
- setStarting: state.setIngestStarting,
connectionState: state.ingestConnectionState,
setConnectionState: state.setIngestConnectionState,
startedTimestamp: state.ingestStarted,
diff --git a/js/components/src/streamplace-store/stream.tsx b/js/components/src/streamplace-store/stream.tsx
index c1776e2a..a5c7d061 100644
--- a/js/components/src/streamplace-store/stream.tsx
+++ b/js/components/src/streamplace-store/stream.tsx
@@ -156,50 +156,50 @@ export function useCreateStreamRecord() {
const u = new URL(url);
- let thumbnail: BlobRef | undefined = undefined;
-
- if (customThumbnail) {
- try {
- thumbnail = await uploadThumbnail(agent, customThumbnail);
- } catch (e) {
- throw new Error(`Custom thumbnail upload failed ${e}`);
- }
- } else {
- // No custom thumbnail: fetch the server-side image and upload it
- // try thrice lel
- let tries = 0;
- try {
- for (; tries < 3; tries++) {
- try {
- console.log(
- `Fetching thumbnail from ${u.protocol}//${u.host}/api/playback/${agent.did}/stream.png`,
- );
- const thumbnailRes = await fetch(
- `${u.protocol}//${u.host}/api/playback/${agent.did}/stream.png`,
- );
- if (!thumbnailRes.ok) {
- throw new Error(
- `Failed to fetch thumbnail: ${thumbnailRes.status})`,
- );
- }
- const thumbnailBlob = await thumbnailRes.blob();
- console.log(thumbnailBlob);
- thumbnail = await uploadThumbnail(agent, thumbnailBlob);
- } catch (e) {
- console.warn(
- `Failed to fetch thumbnail, retrying (${tries + 1}/3): ${e}`,
- );
- // Wait 1 second before retrying
- await new Promise((resolve) => setTimeout(resolve, 2000));
- if (tries === 2) {
- throw new Error(`Failed to fetch thumbnail after 3 tries: ${e}`);
- }
- }
- }
- } catch (e) {
- throw new Error(`Thumbnail upload failed ${e}`);
- }
- }
+ // let thumbnail: BlobRef | undefined = undefined;
+
+ // if (customThumbnail) {
+ // try {
+ // thumbnail = await uploadThumbnail(agent, customThumbnail);
+ // } catch (e) {
+ // throw new Error(`Custom thumbnail upload failed ${e}`);
+ // }
+ // } else {
+ // // No custom thumbnail: fetch the server-side image and upload it
+ // // try thrice lel
+ // let tries = 0;
+ // try {
+ // for (; tries < 3; tries++) {
+ // try {
+ // console.log(
+ // `Fetching thumbnail from ${u.protocol}//${u.host}/api/playback/${agent.did}/stream.png`,
+ // );
+ // const thumbnailRes = await fetch(
+ // `${u.protocol}//${u.host}/api/playback/${agent.did}/stream.png`,
+ // );
+ // if (!thumbnailRes.ok) {
+ // throw new Error(
+ // `Failed to fetch thumbnail: ${thumbnailRes.status})`,
+ // );
+ // }
+ // const thumbnailBlob = await thumbnailRes.blob();
+ // console.log(thumbnailBlob);
+ // thumbnail = await uploadThumbnail(agent, thumbnailBlob);
+ // } catch (e) {
+ // console.warn(
+ // `Failed to fetch thumbnail, retrying (${tries + 1}/3): ${e}`,
+ // );
+ // // Wait 1 second before retrying
+ // await new Promise((resolve) => setTimeout(resolve, 2000));
+ // if (tries === 2) {
+ // throw new Error(`Failed to fetch thumbnail after 3 tries: ${e}`);
+ // }
+ // }
+ // }
+ // } catch (e) {
+ // throw new Error(`Thumbnail upload failed ${e}`);
+ // }
+ // }
let newPost: undefined | { uri: string; cid: string } = undefined;
@@ -221,7 +221,7 @@ export function useCreateStreamRecord() {
u,
profile.data,
params,
- thumbnail,
+ undefined,
agent,
);
@@ -264,7 +264,7 @@ export function useCreateStreamRecord() {
// e.g. `@streamplace/components/0.1.0 (ios, 32.0)`
agent: `@streamplace/components/${PackageJson.version} (${platform}, ${platVersion})`,
post: newPost,
- thumb: thumbnail,
+ // thumb: thumbnail,
idleTimeoutSeconds: idleTimeoutSeconds,
};
console.log("record", record);
diff --git a/pkg/api/playback.go b/pkg/api/playback.go
index cdae6e22..87b49c8e 100644
--- a/pkg/api/playback.go
+++ b/pkg/api/playback.go
@@ -16,6 +16,7 @@ import (
"stream.place/streamplace/pkg/errors"
"stream.place/streamplace/pkg/log"
"stream.place/streamplace/pkg/spmetrics"
+ "stream.place/streamplace/pkg/streamplace"
)
func (a *StreamplaceAPI) NormalizeUser(ctx context.Context, user string) (string, error) {
@@ -272,6 +273,29 @@ func (a *StreamplaceAPI) HandleThumbnailPlayback(ctx context.Context) httprouter
errors.WriteHTTPNotFound(w, "user not found", err)
return
}
+ ls, err := a.Model.GetLatestLivestreamForRepo(user)
+ if err != nil {
+ errors.WriteHTTPInternalServerError(w, "could not get livestream", err)
+ return
+ }
+ if ls == nil {
+ errors.WriteHTTPNotFound(w, "livestream not found", err)
+ return
+ }
+ lsrv, err := ls.ToLivestreamView()
+ if err != nil {
+ errors.WriteHTTPInternalServerError(w, "could not marshal livestream", err)
+ return
+ }
+ lsr, ok := lsrv.Record.Val.(*streamplace.Livestream)
+ if !ok {
+ errors.WriteHTTPInternalServerError(w, "livestream is not a streamplace livestream", nil)
+ return
+ }
+ if lsr.EndedAt != nil {
+ errors.WriteHTTPNotFound(w, "livestream has ended", nil)
+ return
+ }
thumb, err := a.LocalDB.LatestThumbnailForUser(user)
if err != nil {
errors.WriteHTTPInternalServerError(w, "could not query thumbnail", err)
diff --git a/pkg/director/stream_session.go b/pkg/director/stream_session.go
index e1746063..8ff036b0 100644
--- a/pkg/director/stream_session.go
+++ b/pkg/director/stream_session.go
@@ -195,17 +195,17 @@ func (ss *StreamSession) NewSegment(ctx context.Context, notif *media.NewSegment
return ss.statefulDB.UpsertBroadcastOrigin(spseg.Creator, ss.cli.BroadcasterDID(), time.Now())
})
- // everything else is for published segments
- if !notif.Metadata.Published {
- return nil
- }
-
if ss.cli.Thumbnail {
ss.Go(ctx, func() error {
return ss.Thumbnail(ctx, spseg.Creator, notif)
})
}
+ // everything else is for published segments
+ if !notif.Metadata.Published {
+ return nil
+ }
+
if notif.Local {
ss.UpdateStatus(ctx, spseg.Creator)
ss.UpdateBroadcastOrigin(ctx)