SOCIOWIRE.com
Wired to life
diff --git a/src/components/Live/LiveKitBroadcast.jsx b/src/components/Live/LiveKitBroadcast.jsx
index b87098e..9bfaf69 100644
--- a/src/components/Live/LiveKitBroadcast.jsx
+++ b/src/components/Live/LiveKitBroadcast.jsx
@@ -4,6 +4,7 @@ import { useAuth } from "../../auth/AuthContext";
import "./Live.css";
const API_BASE = (import.meta?.env?.VITE_API_BASE_URL || "").replace(/\/+$/, "");
+const LIVEKIT_API = "https://live.us1.sociowire.com";
/**
* LiveKit-based broadcast component
@@ -18,6 +19,19 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated }) {
const [localStream, setLocalStream] = useState(null);
const [showConfirm, setShowConfirm] = useState(false);
const [livePostId, setLivePostId] = useState(null);
+ const [liveCoords] = useState(() => {
+ if (coords && typeof coords === "object") {
+ if (Array.isArray(coords) && coords.length >= 2) {
+ return { lon: coords[0], lat: coords[1] };
+ }
+ const lat = coords.lat ?? coords.latitude;
+ const lon = coords.lon ?? coords.lng ?? coords.longitude;
+ if (Number.isFinite(lat) && Number.isFinite(lon)) {
+ return { lat, lon };
+ }
+ }
+ return { lat: null, lon: null };
+ });
const videoRef = useRef(null);
@@ -90,7 +104,7 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated }) {
onStreamCreated({
roomName,
title: title.trim(),
- coords,
+ coords: liveCoords,
});
}
@@ -106,8 +120,8 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated }) {
snippet: "Live now",
category: "EVENT",
sub_category: "Live",
- lat: coords?.lat,
- lon: coords?.lon,
+ lat: liveCoords?.lat,
+ lon: liveCoords?.lon,
author: username,
source: "sociowire",
visibility: "public",
@@ -128,13 +142,31 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated }) {
console.error("[LiveKit] Failed to create live post:", err);
alert("Live started, but we couldn't create the live post.");
}
- }, [title, localStream, startBroadcast, roomName, coords, onStreamCreated, authToken, username]);
+ }, [title, localStream, startBroadcast, roomName, liveCoords, onStreamCreated, authToken, username]);
const handleEndStream = useCallback(async (save) => {
await stopBroadcast();
if (localStream) {
localStream.getTracks().forEach((t) => t.stop());
}
+
+ // Stop egress recording and get video key
+ let videoKey = "";
+ try {
+ const egressRes = await fetch(`${LIVEKIT_API}/api/livekit/egress/stop`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ room: roomName, save: !!save }),
+ });
+ if (egressRes.ok) {
+ const egressData = await egressRes.json();
+ videoKey = egressData.key || "";
+ console.log("[LiveKit] Egress stopped, video key:", videoKey);
+ }
+ } catch (err) {
+ console.warn("[LiveKit] Failed to stop egress:", err);
+ }
+
if (livePostId) {
try {
const res = await fetch(`${API_BASE}/api/live/finalize`, {
@@ -146,7 +178,7 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated }) {
body: JSON.stringify({
post_id: livePostId,
save: !!save,
- video_url: "",
+ video_key: videoKey,
}),
});
if (res.ok && save) {
@@ -159,7 +191,7 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated }) {
if (!res.ok) {
const errText = await res.text();
console.error("[LiveKit] Finalize failed:", res.status, errText);
- alert("We couldn't save the live post. Please verify your email and try again.");
+ alert("We couldn't save the live post. Please try again.");
}
} catch (err) {
console.error("[LiveKit] Failed to finalize live post:", err);
@@ -167,7 +199,7 @@ export default function LiveKitBroadcast({ coords, onClose, onStreamCreated }) {
}
}
onClose?.();
- }, [stopBroadcast, localStream, onClose, livePostId, authToken, username]);
+ }, [stopBroadcast, localStream, onClose, livePostId, authToken, roomName]);
const handleBackdropClick = (e) => {
if (e.target === e.currentTarget && !isLive) {
diff --git a/src/components/Live/useLiveKit.js b/src/components/Live/useLiveKit.js
index 317958a..198c7bc 100644
--- a/src/components/Live/useLiveKit.js
+++ b/src/components/Live/useLiveKit.js
@@ -148,6 +148,18 @@ export function useLiveKit({ roomName, userID, username, token: authToken }) {
roomRef.current = room;
setIsLive(true);
console.log("[LiveKit] Broadcasting started");
+
+ // Start egress recording
+ try {
+ await fetch(`${LIVEKIT_API}/api/livekit/egress/start`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ room: roomName }),
+ });
+ console.log("[LiveKit] Egress recording started");
+ } catch (err) {
+ console.warn("[LiveKit] Failed to start egress:", err);
+ }
} catch (err) {
console.error("[LiveKit] Failed to start broadcast:", err);
setError(err.message || "Failed to start broadcast");
diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js
index d1e264e..557d2ec 100644
--- a/src/components/Map/markerManager.js
+++ b/src/components/Map/markerManager.js
@@ -235,9 +235,10 @@ function applyPostDetails(modalWrap, post) {
const summaryEl = modalWrap.querySelector(".sw-modal-summary");
if (summaryEl) summaryEl.textContent = summary;
- // Skip hero replacement for camera posts - they have video content
+ // Skip hero replacement for camera and video posts - they have video content
const isCamera = post?.content_type === 'camera';
- if (!isCamera) {
+ const isVideo = post?.content_type === 'video';
+ if (!isCamera && !isVideo) {
const heroGallery = collectGalleryImages(post);
const heroImageSrc = heroGallery[0] || heroImageForPost(post);
const heroFullSrc = heroGallery[0] || fullImageForPost(post);
@@ -1015,11 +1016,15 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {
title: postData.title?.substring(0, 30)
});
- // Camera posts now use the regular modal with video content
+ // Camera and video posts use the regular modal with video content
const isCamera = postData.content_type === 'camera';
+ const isVideo = postData.content_type === 'video';
if (isCamera) {
console.log('[openCenteredOverlay] Opening camera as regular post with video');
}
+ if (isVideo) {
+ console.log('[openCenteredOverlay] Opening saved video post:', postData.video_url);
+ }
const data = adaptPostToTemplateData(postData, { size: "full" });
const cat = normCatLabel(postData);
@@ -1191,7 +1196,7 @@ export function openCenteredOverlay({ map, post, theme, fullScreen = false }) {