diff --git a/packages/www/components/AppPlayer/index.tsx b/packages/www/components/AppPlayer/index.tsx
index 4c46ad011b..69d4185f34 100644
--- a/packages/www/components/AppPlayer/index.tsx
+++ b/packages/www/components/AppPlayer/index.tsx
@@ -2,13 +2,26 @@ import { Player } from "@livepeer/react";
import mux from "mux-embed";
import { memo, useCallback } from "react";
-interface AppPlayerProps {
- playbackUrl: string;
+type AppPlayerProps = {
+ playbackUrl?: string;
+ playbackId?: string;
autoPlay?: boolean;
type: "asset" | "stream";
-}
+} & (
+ | {
+ playbackUrl: string;
+ }
+ | {
+ playbackId: string;
+ }
+);
-const AppPlayer = ({ playbackUrl, autoPlay = true, type }: AppPlayerProps) => {
+const AppPlayer = ({
+ playbackUrl,
+ playbackId,
+ autoPlay = true,
+ type,
+}: AppPlayerProps) => {
const mediaElementRef = useCallback((element: HTMLMediaElement) => {
mux.monitor(element, {
debug: false,
@@ -25,11 +38,12 @@ const AppPlayer = ({ playbackUrl, autoPlay = true, type }: AppPlayerProps) => {
return (
);
};
diff --git a/packages/www/components/AssetDetails/AssetSharePopup.tsx b/packages/www/components/AssetDetails/AssetSharePopup.tsx
index 1d4489a01a..25f0e797b7 100644
--- a/packages/www/components/AssetDetails/AssetSharePopup.tsx
+++ b/packages/www/components/AssetDetails/AssetSharePopup.tsx
@@ -10,6 +10,7 @@ import {
} from "@livepeer/design-system";
import { CodeIcon, Link1Icon } from "@radix-ui/react-icons";
import { CopyToClipboard } from "react-copy-to-clipboard";
+import { isStaging } from "lib/utils";
const buttonLinkCss = {
display: "flex",
@@ -29,7 +30,9 @@ const AssetSharePopup = ({
onEmbedVideoClick,
}: AssetSharePopupProps) => {
const [openSnackbar] = useSnackbar();
- const copyString = `https://lvpr.tv?v=${playbackId}`;
+ const copyString = isStaging()
+ ? `https://monster.lvpr.tv?v=${playbackId}`
+ : `https://lvpr.tv?v=${playbackId}`;
return (
diff --git a/packages/www/components/AssetDetails/EmbedVideoDialog.tsx b/packages/www/components/AssetDetails/EmbedVideoDialog.tsx
index cdd161174a..0bd64dc866 100644
--- a/packages/www/components/AssetDetails/EmbedVideoDialog.tsx
+++ b/packages/www/components/AssetDetails/EmbedVideoDialog.tsx
@@ -9,10 +9,11 @@ import {
TextField,
useSnackbar,
} from "@livepeer/design-system";
+import { isStaging } from "lib/utils";
import { CopyToClipboard } from "react-copy-to-clipboard";
-const embedStringForAsset = (playbackId: string) =>
- ``;
+const embedStringForAsset = (embedUrl: string) =>
+ ``;
export type EmbedVideoDialog = {
isOpen: boolean;
@@ -25,7 +26,10 @@ const EmbedVideoDialog = ({
onOpenChange,
playbackId,
}: EmbedVideoDialog) => {
- const embedString = embedStringForAsset(playbackId);
+ const embedUrl = isStaging()
+ ? `https://monster.lvpr.tv?v=${playbackId}`
+ : `https://lvpr.tv?v=${playbackId}`;
+ const embedString = embedStringForAsset(embedUrl);
const [openSnackbar] = useSnackbar();
const onCopy = () => {
diff --git a/packages/www/components/StreamDetails/StreamChildrenHeadingBox.tsx b/packages/www/components/StreamDetails/StreamChildrenHeadingBox.tsx
index c874d4229f..ed6beebb27 100644
--- a/packages/www/components/StreamDetails/StreamChildrenHeadingBox.tsx
+++ b/packages/www/components/StreamDetails/StreamChildrenHeadingBox.tsx
@@ -22,7 +22,7 @@ export type StreamChildrenHeadingBoxProps = {
stream: Stream;
user: User;
activeTab: string;
- setSwitchTab: Dispatch>;
+ setSwitchTab: Dispatch>;
invalidateStream: () => void;
};
@@ -43,7 +43,7 @@ const StreamChildrenHeadingBox = ({
mb: "$4",
width: "100%",
}}>
-
+
setSwitchTab("Overview")}
@@ -54,14 +54,28 @@ const StreamChildrenHeadingBox = ({
textDecoration: "none",
borderBottom: "2px solid",
borderColor: activeTab === "Overview" ? "$green9" : "transparent",
- mr: "$5",
"&:hover": {
textDecoration: "none",
},
}}>
Overview
-
+ setSwitchTab("Details")}
+ css={{
+ textDecoration: "none",
+ pb: "$2",
+ width: "100%",
+ cursor: "default",
+ borderBottom: "2px solid",
+ borderColor: activeTab === "Details" ? "$green9" : "transparent",
+ "&:hover": {
+ textDecoration: "none",
+ },
+ }}>
+ Details
+
setSwitchTab("Health")}
diff --git a/packages/www/components/StreamDetails/StreamDetailsTab.tsx b/packages/www/components/StreamDetails/StreamDetailsTab.tsx
new file mode 100644
index 0000000000..a8aa58f204
--- /dev/null
+++ b/packages/www/components/StreamDetails/StreamDetailsTab.tsx
@@ -0,0 +1,35 @@
+import { Text } from "@livepeer/design-system";
+import MultistreamTargetsTable from "components/StreamDetails/MultistreamTargetsTable";
+import SessionsTable from "./SessionsTable";
+
+const StreamDetailsTab = ({ id, stream, streamHealth, invalidateStream }) => {
+ return (
+ <>
+
+ No targets
+
+ }
+ tableLayout="auto"
+ border
+ />
+
+ No sessions
+
+ }
+ tableLayout="auto"
+ border
+ />
+ >
+ );
+};
+
+export default StreamDetailsTab;
diff --git a/packages/www/components/StreamDetails/StreamHeadingBox.tsx b/packages/www/components/StreamDetails/StreamHeadingBox.tsx
index d95e2ec573..c8374623ca 100644
--- a/packages/www/components/StreamDetails/StreamHeadingBox.tsx
+++ b/packages/www/components/StreamDetails/StreamHeadingBox.tsx
@@ -21,7 +21,6 @@ const StreamHeadingBox = ({
align="end"
css={{
pb: "$3",
- mb: "$5",
width: "100%",
}}>
diff --git a/packages/www/components/StreamDetails/StreamDetailsBox.tsx b/packages/www/components/StreamDetails/StreamOverviewBox.tsx
similarity index 70%
rename from packages/www/components/StreamDetails/StreamDetailsBox.tsx
rename to packages/www/components/StreamDetails/StreamOverviewBox.tsx
index f6b42197ab..b6724a759e 100644
--- a/packages/www/components/StreamDetails/StreamDetailsBox.tsx
+++ b/packages/www/components/StreamDetails/StreamOverviewBox.tsx
@@ -4,7 +4,6 @@ import RelativeTime from "../RelativeTime";
import ShowURL from "../ShowURL";
import Record from "./Record";
import { QuestionMarkCircledIcon as Help } from "@radix-ui/react-icons";
-import { useState } from "react";
import { Stream } from "livepeer";
const Cell = ({ children, css = {} }) => {
@@ -15,22 +14,17 @@ const Cell = ({ children, css = {} }) => {
);
};
-export type StreamDetailsBoxProps = {
+export type StreamOverviewBoxProps = {
stream: Stream & { suspended?: boolean };
- globalIngestUrl: string;
- globalSrtIngestUrl: string;
globalPlaybackUrl: string;
invalidateStream: () => void;
};
-const StreamDetailsBox = ({
+const StreamOverviewBox = ({
stream,
- globalIngestUrl,
- globalSrtIngestUrl,
globalPlaybackUrl,
invalidateStream,
-}: StreamDetailsBoxProps) => {
- const [keyRevealed, setKeyRevealed] = useState(false);
+}: StreamOverviewBoxProps) => {
return (
<>
- Details
+ Overview
- Stream name |
- {stream.name} |
Stream ID |
|
- Stream key |
-
- {keyRevealed ? (
-
-
-
- ) : (
-
- )}
- |
- RTMP ingest URL |
-
-
- |
- SRT ingest URL |
-
-
- |
Playback ID |
@@ -108,26 +73,6 @@ const StreamDetailsBox = ({
anchor={false}
/>
|
- Record sessions |
-
-
-
-
-
-
- When enabled, transcoded streaming sessions will be recorded
- and stored by Livepeer Studio. Each recorded session will
- have a recording .m3u8 URL for playback and an MP4 download
- link. This feature is currently free.
- |
- }>
-
-
-
-
Created at |
Status |
{stream.isActive ? "Active" : "Idle"} |
- Suspended |
- {stream.suspended ? "Suspended" : "Normal"} |
+ Record sessions |
+
+
+
+
+
+
+ When enabled, transcoded streaming sessions will be recorded
+ and stored by Livepeer Studio. Each recorded session will
+ have a recording .m3u8 URL for playback and an MP4 download
+ link. This feature is currently free.
+ | |
+ }>
+
+
+
+
>
);
};
-export default StreamDetailsBox;
+export default StreamOverviewBox;
diff --git a/packages/www/components/StreamDetails/StreamOverviewTab.tsx b/packages/www/components/StreamDetails/StreamOverviewTab.tsx
index e3f4f2f5c8..b0e5cbd569 100644
--- a/packages/www/components/StreamDetails/StreamOverviewTab.tsx
+++ b/packages/www/components/StreamDetails/StreamOverviewTab.tsx
@@ -1,32 +1,39 @@
-import SessionsTable from "./SessionsTable";
-import MultistreamTargetsTable from "components/StreamDetails/MultistreamTargetsTable";
-import { Text } from "@livepeer/design-system";
+import { useApi } from "hooks";
+import { useEffect, useState } from "react";
+import StreamOverviewBox from "./StreamOverviewBox";
const StreamOverviewTab = ({ id, stream, streamHealth, invalidateStream }) => {
+ const { getIngest } = useApi();
+
+ const [ingest, setIngest] = useState(null);
+
+ useEffect(() => {
+ getIngest(true)
+ .then((ingest) => {
+ if (Array.isArray(ingest)) {
+ ingest.sort((a, b) => a.base.localeCompare(b.base));
+ }
+ if ((ingest?.length ?? 0) > 0) {
+ setIngest(ingest?.[0]);
+ }
+ })
+ .catch((err) => console.error(err)); // todo: surface this
+ }, [id]);
+
+ const playbackId = (stream || {}).playbackId || "";
+
+ let globalPlaybackUrl = "";
+
+ if (ingest) {
+ globalPlaybackUrl = `${ingest?.playback ?? ""}/${playbackId}/index.m3u8`;
+ }
+
return (
<>
-
- No targets
-
- }
- tableLayout="auto"
- border
- />
-
- No sessions
-
- }
- tableLayout="auto"
- border
/>
>
);
diff --git a/packages/www/components/StreamDetails/StreamPlayerBox/ActiveStream.tsx b/packages/www/components/StreamDetails/StreamPlayerBox/ActiveStream.tsx
index 56124bfedc..14e2addbb7 100644
--- a/packages/www/components/StreamDetails/StreamPlayerBox/ActiveStream.tsx
+++ b/packages/www/components/StreamDetails/StreamPlayerBox/ActiveStream.tsx
@@ -1,7 +1,7 @@
import AppPlayer from "components/AppPlayer";
import { Badge, Box, Status } from "@livepeer/design-system";
-const ActiveStream = ({ playbackUrl }: { playbackUrl: string }) => (
+const ActiveStream = ({ playbackId }: { playbackId: string }) => (
<>
(
Active
-
+
>
);
diff --git a/packages/www/components/StreamDetails/StreamPlayerBox/IdleStream.tsx b/packages/www/components/StreamDetails/StreamPlayerBox/IdleStream.tsx
deleted file mode 100644
index 87f4437203..0000000000
--- a/packages/www/components/StreamDetails/StreamPlayerBox/IdleStream.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Badge, Box, Status } from "@livepeer/design-system";
-
-const IdleStream = () => (
-
-
-
-
-
- Idle
-
-
-);
-
-export default IdleStream;
diff --git a/packages/www/components/StreamDetails/StreamPlayerBox/index.tsx b/packages/www/components/StreamDetails/StreamPlayerBox/index.tsx
index 43ec9cfb2b..b3a850d5c7 100644
--- a/packages/www/components/StreamDetails/StreamPlayerBox/index.tsx
+++ b/packages/www/components/StreamDetails/StreamPlayerBox/index.tsx
@@ -1,21 +1,61 @@
-import { Box, Flex, Button } from "@livepeer/design-system";
+import {
+ Badge,
+ Box,
+ Button,
+ Flex,
+ Status,
+ Tooltip,
+} from "@livepeer/design-system";
+import { Broadcast as LivepeerBroadcast } from "@livepeer/react";
import { Share2Icon } from "@radix-ui/react-icons";
import { Stream } from "livepeer";
import AssetSharePopup from "../../AssetDetails/AssetSharePopup";
+
+import { useEffect, useMemo, useState } from "react";
+import { FiKey, FiVideo } from "react-icons/fi";
import ActiveStream from "./ActiveStream";
-import IdleStream from "./IdleStream";
+import StreamSetupBox from "../StreamSetupBox";
+import { FaKey, FaVideo } from "react-icons/fa";
export type StreamPlayerBoxProps = {
stream: Stream;
- globalPlaybackUrl: string;
onEmbedVideoClick: () => void;
+ globalIngestUrl: string;
+ globalSrtIngestUrl: string;
+ globalPlaybackUrl: string;
+ invalidateStream: () => void;
};
const StreamPlayerBox = ({
stream,
- globalPlaybackUrl,
onEmbedVideoClick,
+ globalIngestUrl,
+ globalSrtIngestUrl,
+ globalPlaybackUrl,
+ invalidateStream,
}: StreamPlayerBoxProps) => {
+ const [activeTab, setSwitchTab] = useState<"Browser" | "Streaming Software">(
+ "Browser"
+ );
+ const [showBroadcast, setShowBroadcast] = useState(false);
+
+ const isStreamActiveFromExternal = useMemo(
+ () => !showBroadcast && stream.isActive,
+ [showBroadcast, stream.isActive]
+ );
+
+ useEffect(() => {
+ if (isStreamActiveFromExternal) {
+ setSwitchTab("Streaming Software");
+ }
+ }, [isStreamActiveFromExternal]);
+
+ useEffect(() => {
+ if (showBroadcast) {
+ setSwitchTab("Browser");
+ }
+ }, [showBroadcast]);
+
return (
- {stream.isActive ? (
-
- ) : (
-
- )}
-
-
-
+
+ {showBroadcast ? (
+
+ ) : stream.isActive ? (
+
+ ) : (
+ <>
+
+
+
+
+ Idle
+
+ >
+ )}
+
+
+
+
+ {
+ if (!isStreamActiveFromExternal) setSwitchTab("Browser");
+ }}
+ css={{
+ display: "flex",
+ gap: "$1",
+ m: "$1",
+ p: "$2",
+ borderRadius: "$1",
+ width: "100%",
+ cursor: isStreamActiveFromExternal ? "not-allowed" : "default",
+ textDecoration: "none",
+ alignItems: "center",
+ justifyContent: "center",
+ color: activeTab === "Browser" ? "$neutral1" : "inherit",
+ backgroundColor:
+ activeTab === "Browser" ? "$neutral12" : "transparent",
+ }}>
+
+ Browser
+
+ setSwitchTab("Streaming Software")}
+ css={{
+ display: "flex",
+ gap: "$1",
+ m: "$1",
+ p: "$2",
+ borderRadius: "$1",
+ width: "100%",
+ cursor: "default",
+ textDecoration: "none",
+ alignItems: "center",
+ justifyContent: "center",
+ color: activeTab === "Streaming Software" ? "$neutral1" : "inherit",
+ backgroundColor:
+ activeTab === "Streaming Software" ? "$neutral12" : "transparent",
+ }}>
+
+ Streaming Software
+
+
+
);
};
diff --git a/packages/www/components/StreamDetails/StreamSetupBox.tsx b/packages/www/components/StreamDetails/StreamSetupBox.tsx
new file mode 100644
index 0000000000..c810c8cd77
--- /dev/null
+++ b/packages/www/components/StreamDetails/StreamSetupBox.tsx
@@ -0,0 +1,111 @@
+import { Box, Flex, Heading, Link, Text } from "@livepeer/design-system";
+import { Stream } from "livepeer";
+import ClipButton from "../Clipping/ClipButton";
+import ShowURL from "../ShowURL";
+
+export type StreamSetupBoxProps = {
+ activeTab: "Browser" | "Streaming Software";
+ stream: Stream & { suspended?: boolean };
+ globalIngestUrl: string;
+ globalSrtIngestUrl: string;
+ globalPlaybackUrl: string;
+ invalidateStream: () => void;
+};
+
+const StreamSetupBox = ({
+ activeTab,
+ stream,
+ globalIngestUrl,
+ globalSrtIngestUrl,
+ invalidateStream,
+}: StreamSetupBoxProps) => {
+ return (
+ <>
+
+
+
+ {activeTab === "Streaming Software"
+ ? "Streaming software setup"
+ : "Go live from the browser"}
+
+
+ {activeTab === "Streaming Software" ? (
+ <>
+ Copy and paste the stream key into your streaming software. Use
+ either the RTMP or SRT ingest, depending on your use-case. The
+ RTMP ingest is more common with OBS users.{" "}
+
+ Check out our docs for more details.
+
+ >
+ ) : (
+ 'Check that your camera and microphone inputs are properly working before clicking the "Go live" button above.'
+ )}
+
+ {activeTab === "Streaming Software" && (
+ <>
+
+ Stream key
+
+
+
+
+
+ RTMP ingest
+
+
+
+
+
+ SRT ingest
+
+
+
+
+ >
+ )}
+
+
+ >
+ );
+};
+
+export default StreamSetupBox;
diff --git a/packages/www/layouts/streamDetail.tsx b/packages/www/layouts/streamDetail.tsx
index 15bdc3699d..5717d30688 100644
--- a/packages/www/layouts/streamDetail.tsx
+++ b/packages/www/layouts/streamDetail.tsx
@@ -6,7 +6,7 @@ import { useEffect, useState, useMemo } from "react";
import Spinner from "components/Spinner";
import { Variant as StatusVariant } from "components/StatusBadge";
import StreamPlayerBox from "components/StreamDetails/StreamPlayerBox/";
-import StreamDetailsBox from "components/StreamDetails/StreamDetailsBox";
+import StreamSetupBox from "components/StreamDetails/StreamSetupBox";
import StreamHeadingBox from "components/StreamDetails/StreamHeadingBox";
import StreamChildrenHeadingBox from "components/StreamDetails/StreamChildrenHeadingBox";
import EmbedVideoDialog from "components/AssetDetails/EmbedVideoDialog";
@@ -129,7 +129,7 @@ const StreamDetail = ({
/>
-
+
{stream ? (
<>
@@ -150,12 +150,8 @@ const StreamDetail = ({
stream={stream}
globalPlaybackUrl={globalPlaybackUrl}
onEmbedVideoClick={() => setEmbedVideoDialogOpen(true)}
- />
-
diff --git a/packages/www/package.json b/packages/www/package.json
index 6e2f860ac9..6157c76473 100644
--- a/packages/www/package.json
+++ b/packages/www/package.json
@@ -22,7 +22,7 @@
"@emotion/styled": "^10.0.23",
"@livepeer.studio/api": "^0.15.0",
"@livepeer/design-system": "^1.0.17",
- "@livepeer/react": "^2.5.7",
+ "@livepeer/react": "^2.6.1",
"@mdx-js/loader": "^1.6.16",
"@mdx-js/mdx": "^1.6.16",
"@mdx-js/react": "^1.6.16",
diff --git a/packages/www/pages/_app.tsx b/packages/www/pages/_app.tsx
index de7eead80c..bbfd6affa1 100644
--- a/packages/www/pages/_app.tsx
+++ b/packages/www/pages/_app.tsx
@@ -66,8 +66,11 @@ const livepeerClient = createReactClient({
// eventually should move to using JWT from user's login
apiKey: "",
baseUrl: isStaging()
- ? "https://livepeer.monster"
- : "https://livepeer.studio",
+ ? "https://livepeer.monster/api"
+ : "https://livepeer.studio/api",
+ webrtcIngestBaseUrl: isStaging()
+ ? "https://webrtc.livepeer.monster/webrtc"
+ : "https://webrtc.livepeer.studio/webrtc",
}),
});
@@ -75,6 +78,20 @@ const livepeerTheme: ThemeConfig = {
colors: {
accent: "$colors$green10",
},
+ fontSizes: {
+ timeFontSize: "0.85rem",
+ timeFontSizeMd: "0.85rem",
+ timeFontSizeSm: "0.85rem",
+ titleFontSize: "0.9rem",
+ titleFontSizeMd: "0.9rem",
+ titleFontSizeSm: "0.9rem",
+ errorTitleFontSize: "1.3rem",
+ errorTitleFontSizeMd: "1.3rem",
+ errorTitleFontSizeSm: "1.3rem",
+ errorTextFontSize: "0.85rem",
+ errorTextFontSizeMd: "0.85rem",
+ errorTextFontSizeSm: "0.85rem",
+ },
};
const App = ({ Component, pageProps }) => {
diff --git a/packages/www/pages/dashboard/streams/[id]/index.tsx b/packages/www/pages/dashboard/streams/[id]/index.tsx
index 29d018596b..06f2c75b8c 100644
--- a/packages/www/pages/dashboard/streams/[id]/index.tsx
+++ b/packages/www/pages/dashboard/streams/[id]/index.tsx
@@ -6,6 +6,7 @@ import { useApi, useAnalyzer } from "hooks";
import StreamDetail from "layouts/streamDetail";
import StreamHealthTab from "components/StreamDetails/StreamHealthTab";
import StreamOverviewTab from "components/StreamDetails/StreamOverviewTab";
+import StreamDetailsTab from "components/StreamDetails/StreamDetailsTab";
const refetchInterval = 5 * 1000;
@@ -14,9 +15,9 @@ const StreamDetails = () => {
const queryClient = useQueryClient();
const { getStream } = useApi();
const { getHealth } = useAnalyzer();
- const [currentTab, setCurrentTab] = useState<"Overview" | "Health">(
- "Overview"
- );
+ const [currentTab, setCurrentTab] = useState<
+ "Overview" | "Details" | "Health"
+ >("Overview");
const [embedVideoDialogOpen, setEmbedVideoDialogOpen] = useState(false);
const { query } = router;
@@ -63,6 +64,13 @@ const StreamDetails = () => {
streamHealth={streamHealth}
invalidateStream={invalidateStream}
/>
+ ) : currentTab === "Details" ? (
+
) : (