From 3eb7b8b84a0b14964a290dc7307842a2dcf91323 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sat, 7 Nov 2020 16:44:11 -0800 Subject: [PATCH 01/14] Support API calls without auth and without cors --- web/.env.production | 2 -- web/utils/apis.ts | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/web/.env.production b/web/.env.production index d307f0c91..1fad60181 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1,3 +1 @@ -NEXT_PUBLIC_ADMIN_USERNAME=admin -NEXT_PUBLIC_ADMIN_STREAMKEY=abc123 NEXT_PUBLIC_API_HOST=/ \ No newline at end of file diff --git a/web/utils/apis.ts b/web/utils/apis.ts index 4085631ba..d9dc1009b 100644 --- a/web/utils/apis.ts +++ b/web/utils/apis.ts @@ -37,16 +37,19 @@ export const LOGS_WARN = `${API_LOCATION}logs/warnings`; const GITHUB_RELEASE_URL = "https://api.github.com/repos/owncast/owncast/releases/latest"; export async function fetchData(url) { - const encoded = btoa(`${ADMIN_USERNAME}:${ADMIN_STREAMKEY}`); + let options: RequestInit = {}; + + if (ADMIN_USERNAME && ADMIN_STREAMKEY) { + const encoded = btoa(`${ADMIN_USERNAME}:${ADMIN_STREAMKEY}`); + options.headers = { + 'Authorization': `Basic ${encoded}` + } + options.mode = 'cors'; + options.credentials = 'include' + } try { - const response = await fetch(url, { - headers: { - 'Authorization': `Basic ${encoded}`, - }, - mode: 'cors', - credentials: 'include', - }); + const response = await fetch(url, options); if (!response.ok) { const message = `An error has occured: ${response.status}`; throw new Error(message); From 20d05962332ade11444b458b588271046b93c6fc Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sat, 7 Nov 2020 20:01:45 -0800 Subject: [PATCH 02/14] WIP for some additional statistic views --- web/pages/components/statistic.tsx | 46 ++++++++++++++++++++++++------ web/pages/hardware-info.tsx | 24 ++++++++++------ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/web/pages/components/statistic.tsx b/web/pages/components/statistic.tsx index abe51849b..91cc5209d 100644 --- a/web/pages/components/statistic.tsx +++ b/web/pages/components/statistic.tsx @@ -1,25 +1,55 @@ -import { Statistic, Card, Col} from "antd"; +import { Typography, Statistic, Card, Col, Progress} from "antd"; +const { Text, Link } = Typography; interface ItemProps { title: string, value: string, prefix: JSX.Element, + color: string, + progress: boolean, }; export default function StatisticItem(props: ItemProps) { const { title, value, prefix } = props; - const valueStyle = { color: "#334", fontSize: "1.8rem" }; + const View = props.progress ? ProgressView : StatisticView; return ( - +
+ +
); +} + +function ProgressView({title, value, prefix, color}) { + const endColor = value > 90 ? 'red' : color; + const content = ( +
+ {prefix} +
{title}
+
{value}%
+
+ ) + return ( + content} /> + ) +} + +function StatisticView({title, value, prefix}) { + const valueStyle = { color: "#334", fontSize: "1.8rem" }; + + return ( + + ) } \ No newline at end of file diff --git a/web/pages/hardware-info.tsx b/web/pages/hardware-info.tsx index 9688421ea..3bb3206cb 100644 --- a/web/pages/hardware-info.tsx +++ b/web/pages/hardware-info.tsx @@ -76,19 +76,25 @@ const series = [

Hardware Info

} + title="CPU" + value={`${currentCPUUsage}`} + prefix={} + color="#FF7700" + progress /> } + title="RAM" + value={`${currentRamUsage}`} + prefix={} + color="#004777" + progress /> } + title="Disk" + value={`${currentDiskUsage}`} + prefix={} + color="#A9E190" + progress /> From eaafe57bb384034eabc8a93c5918f82cb1095c90 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sat, 7 Nov 2020 23:00:02 -0800 Subject: [PATCH 03/14] Show warnings even when offline --- web/pages/hardware-info.tsx | 6 +- web/pages/index.tsx | 112 +++++++++++++++++++----------------- 2 files changed, 62 insertions(+), 56 deletions(-) diff --git a/web/pages/hardware-info.tsx b/web/pages/hardware-info.tsx index 3bb3206cb..58995c20c 100644 --- a/web/pages/hardware-info.tsx +++ b/web/pages/hardware-info.tsx @@ -1,8 +1,8 @@ /* eslint-disable no-array-constructor */ -import React, { useState, useEffect } from 'react'; +import { BulbOutlined, LaptopOutlined, SaveOutlined } from "@ant-design/icons"; import { Row } from "antd"; -import {LaptopOutlined, BulbOutlined, SaveOutlined} from "@ant-design/icons" -import { HARDWARE_STATS, fetchData, FETCH_INTERVAL } from '../utils/apis'; +import React, { useEffect, useState } from 'react'; +import { fetchData, FETCH_INTERVAL, HARDWARE_STATS } from '../utils/apis'; import Chart from './components/chart'; import StatisticItem from "./components/statistic"; diff --git a/web/pages/index.tsx b/web/pages/index.tsx index 112108a22..d580cd576 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -21,65 +21,14 @@ import { LOGS_WARN, fetchData, FETCH_INTERVAL, + LOGS_ALL, } from "../utils/apis"; import { formatIPAddress, isEmptyObject } from "../utils/format"; import OwncastLogo from "./components/logo" const { Title } = Typography; -function Offline() { - const data = [ - { - title: "Send some test content", - content: ( -
- With any video you have around you can pass it to the test script and start streaming it. -
- ./test/ocTestStream.sh yourVideo.mp4 -
-
- ), - }, - { - title: "Use your broadcasting software", - content: ( - - ) - }, - { - title: "Something else", - }, - ]; - return ( -
- } - title="No stream is active." - subTitle="You should start one." - /> - ( - - {item.content} - - )} - /> -
- ); -} export default function Stats() { const context = useContext(ServerStatusContext); @@ -153,6 +102,9 @@ export default function Stats() { ); } + const logTable = logs.length > 0 ? : null + console.log(logs) + if (!broadcaster) { return ; } @@ -181,7 +133,6 @@ export default function Stats() { ); }); - const logTable = logs.length > 0 ? : null const { viewerCount, sessionMaxViewerCount } = stats; const streamVideoDetailString = `${streamDetails.videoCodec} ${streamDetails.videoBitrate} kbps ${streamDetails.width}x${streamDetails.height}`; const streamAudioDetailString = `${streamDetails.audioCodec} ${streamDetails.audioBitrate} kpbs`; @@ -246,4 +197,59 @@ export default function Stats() { {logTable} ); + + function Offline() { + const data = [ + { + title: "Send some test content", + content: ( +
+ With any video you have around you can pass it to the test script and start streaming it. +
+ ./test/ocTestStream.sh yourVideo.mp4 +
+
+ ), + }, + { + title: "Use your broadcasting software", + content: ( + + ) + }, + { + title: "Something else", + }, + ]; + return ( +
+ } + title="No stream is active." + subTitle="You should start one." + /> + + ( + + {item.content} + + )} + /> + {logTable} +
+ ); + } } From 4ab6e5df51445791c49b7f804728e01b76496701 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sat, 7 Nov 2020 23:37:17 -0800 Subject: [PATCH 04/14] Add more offline content to admin home page --- web/pages/index.tsx | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/web/pages/index.tsx b/web/pages/index.tsx index d580cd576..edba03a42 100644 --- a/web/pages/index.tsx +++ b/web/pages/index.tsx @@ -8,7 +8,7 @@ TODO: Link each overview value to the sub-page that focuses on it. */ import React, { useState, useEffect, useContext } from "react"; -import { Row, Skeleton, Result, List, Typography, Card } from "antd"; +import { Row, Col, Skeleton, Result, List, Typography, Card } from "antd"; import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons"; import { formatDistanceToNow, formatRelative } from "date-fns"; import { ServerStatusContext } from "../utils/server-status-context"; @@ -21,7 +21,6 @@ import { LOGS_WARN, fetchData, FETCH_INTERVAL, - LOGS_ALL, } from "../utils/apis"; import { formatIPAddress, isEmptyObject } from "../utils/format"; import OwncastLogo from "./components/logo" @@ -204,7 +203,7 @@ export default function Stats() { title: "Send some test content", content: (
- With any video you have around you can pass it to the test script and start streaming it. + Test your server with any video you have around. Pass it to the test script and start streaming it.
./test/ocTestStream.sh yourVideo.mp4
@@ -220,8 +219,17 @@ export default function Stats() { ) }, { - title: "Something else", + title: "Chat is disabled", + content: "Chat will continue to be disabled until you begin a live stream." }, + { + title: "Embed your video onto other sites", + content: ( + + ) + } ]; return (
@@ -236,9 +244,9 @@ export default function Stats() { gutter: 16, xs: 1, sm: 2, - md: 4, - lg: 4, - xl: 6, + md: 2, + lg: 6, + xl: 3, xxl: 3, }} dataSource={data} From 24d71cec580318666776e8d6d213801340f78caa Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sun, 8 Nov 2020 11:38:20 -0800 Subject: [PATCH 05/14] Center without using center --- web/pages/components/statistic.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/pages/components/statistic.tsx b/web/pages/components/statistic.tsx index 91cc5209d..a2fc48ceb 100644 --- a/web/pages/components/statistic.tsx +++ b/web/pages/components/statistic.tsx @@ -1,5 +1,5 @@ import { Typography, Statistic, Card, Col, Progress} from "antd"; -const { Text, Link } = Typography; +const { Text } = Typography; interface ItemProps { title: string, @@ -16,9 +16,9 @@ export default function StatisticItem(props: ItemProps) { return ( -
+
-
+
); From f2499ebb5d549894e3586aaca7a71a3a6a91ad6c Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sun, 8 Nov 2020 11:51:04 -0800 Subject: [PATCH 06/14] Fix typescript errors --- web/pages/components/statistic.tsx | 6 +++--- web/pages/index.tsx | 10 ++++++++++ web/pages/viewer-info.tsx | 3 +++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/web/pages/components/statistic.tsx b/web/pages/components/statistic.tsx index a2fc48ceb..45cca053e 100644 --- a/web/pages/components/statistic.tsx +++ b/web/pages/components/statistic.tsx @@ -6,7 +6,7 @@ interface ItemProps { value: string, prefix: JSX.Element, color: string, - progress: boolean, + progress?: boolean, }; export default function StatisticItem(props: ItemProps) { @@ -41,8 +41,8 @@ function ProgressView({title, value, prefix, color}) { ) } -function StatisticView({title, value, prefix}) { - const valueStyle = { color: "#334", fontSize: "1.8rem" }; +function StatisticView({title, value, prefix, color}) { + const valueStyle = { fontSize: "1.8rem" }; return ( ); @@ -147,16 +149,19 @@ export default function Stats() { )}`} value={formatDistanceToNow(new Date(broadcaster.time))} prefix={} + color="#334" /> } + color="#334" /> } + color="#334" /> @@ -165,16 +170,19 @@ export default function Stats() { title="Input" value={formatIPAddress(remoteAddr)} prefix={null} + color="#334" /> @@ -185,11 +193,13 @@ export default function Stats() { title="Stream key" value={config.streamKey} prefix={null} + color="#334" /> diff --git a/web/pages/viewer-info.tsx b/web/pages/viewer-info.tsx index 04977adf3..5595eacac 100644 --- a/web/pages/viewer-info.tsx +++ b/web/pages/viewer-info.tsx @@ -109,16 +109,19 @@ export default function ViewersOverTime() { title="Current viewers" value={viewerCount.toString()} prefix={} + color="#334" /> } + color="#334" /> } + color="#334" />
From 0ccb60c5283f1dd00e4c93342a3fcc4577bbb565 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Mon, 9 Nov 2020 12:06:28 -0800 Subject: [PATCH 07/14] Change export paths --- web/next.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/next.config.js b/web/next.config.js index 5213ae8de..a94cfa43a 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -1,3 +1,4 @@ module.exports = { basePath: "/admin", + trailingSlash: true, }; From 6cafb29a8fb7d8ada8af6a0adba761b88a41a830 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Wed, 11 Nov 2020 19:05:38 -0800 Subject: [PATCH 08/14] Customize legend titles. Hide empty graph lines. Update colors. --- web/pages/components/chart.tsx | 26 +++++++++++++++++--------- web/pages/hardware-info.tsx | 24 ++++++++++++------------ web/pages/viewer-info.tsx | 2 +- 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/web/pages/components/chart.tsx b/web/pages/components/chart.tsx index 080e722a4..14c8f89f7 100644 --- a/web/pages/components/chart.tsx +++ b/web/pages/components/chart.tsx @@ -22,6 +22,7 @@ interface TimedValue { interface ChartProps { data?: TimedValue[], + title?: string, color: string, unit: string, dataCollections?: any[], @@ -43,7 +44,7 @@ function CustomizedTooltip(props: ToolTipProps) { } CustomizedTooltip.defaultProps = defaultProps; -export default function Chart({ data, color, unit, dataCollections }: ChartProps) { +export default function Chart({ data, title, color, unit, dataCollections }: ChartProps) { if (!data && !dataCollections) { return null; } @@ -67,6 +68,18 @@ export default function Chart({ data, color, unit, dataCollections }: ChartProps }); } + const line = data ? ( + + ) : null; + return (
@@ -87,23 +100,18 @@ export default function Chart({ data, color, unit, dataCollections }: ChartProps /> } /> - + {line} {dataCollections?.map((s) => ( ))} diff --git a/web/pages/hardware-info.tsx b/web/pages/hardware-info.tsx index 58995c20c..c82e3cb5a 100644 --- a/web/pages/hardware-info.tsx +++ b/web/pages/hardware-info.tsx @@ -55,17 +55,17 @@ export default function HardwareInfo() { const series = [ { name: "CPU", - color: "#FF7700", + color: "#B63FFF", data: hardwareStatus.cpu, }, { name: "Memory", - color: "#004777", + color: "#2087E2", data: hardwareStatus.memory, }, { name: "Disk", - color: "#A9E190", + color: "#FF7700", data: hardwareStatus.disk, }, ]; @@ -76,24 +76,24 @@ const series = [

Hardware Info

} - color="#FF7700" + prefix={} + color={series[0].color} progress /> } - color="#004777" + prefix={} + color={series[1].color} progress /> } - color="#A9E190" + prefix={} + color={series[2].color} progress /> diff --git a/web/pages/viewer-info.tsx b/web/pages/viewer-info.tsx index 5595eacac..ad8fbb8a0 100644 --- a/web/pages/viewer-info.tsx +++ b/web/pages/viewer-info.tsx @@ -125,7 +125,7 @@ export default function ViewersOverTime() { />
- +
; From 99cefa19c8314ca390289123300104c01b4692fc Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Wed, 11 Nov 2020 19:18:01 -0800 Subject: [PATCH 09/14] Remove random semicolons --- web/pages/components/log-table.tsx | 1 - web/pages/upgrade.tsx | 4 ++-- web/pages/viewer-info.tsx | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/web/pages/components/log-table.tsx b/web/pages/components/log-table.tsx index 6d0c01cb0..b4f385832 100644 --- a/web/pages/components/log-table.tsx +++ b/web/pages/components/log-table.tsx @@ -77,7 +77,6 @@ export default function LogTable({ logs, pageSize }: Props) { rowKey={(row) => row.time} pagination={{ pageSize: pageSize || 20 }} /> - ; ); } diff --git a/web/pages/upgrade.tsx b/web/pages/upgrade.tsx index e8cdb8d0a..b591f6bd6 100644 --- a/web/pages/upgrade.tsx +++ b/web/pages/upgrade.tsx @@ -38,7 +38,7 @@ export default function Logs() { {release.name} {new Date(release.created_at).toDateString()} - {release.body};

Downloads

+ {release.body}

Downloads

); @@ -68,6 +68,6 @@ function AssetTable(assets) { }, ]; - return
; + return
} diff --git a/web/pages/viewer-info.tsx b/web/pages/viewer-info.tsx index ad8fbb8a0..de1099178 100644 --- a/web/pages/viewer-info.tsx +++ b/web/pages/viewer-info.tsx @@ -127,7 +127,7 @@ export default function ViewersOverTime() {
-
; +
); } From d1dbe1679641bc44e1e8d1ed9094abae8a167ae8 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Wed, 11 Nov 2020 19:20:46 -0800 Subject: [PATCH 10/14] Hide pagination on upgrade asset table --- web/pages/upgrade.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/pages/upgrade.tsx b/web/pages/upgrade.tsx index b591f6bd6..b90444e7c 100644 --- a/web/pages/upgrade.tsx +++ b/web/pages/upgrade.tsx @@ -68,6 +68,6 @@ function AssetTable(assets) { }, ]; - return
+ return
} From 7316d512c6600672bdc08de7a5f1c3692aefcc99 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Wed, 11 Nov 2020 19:39:57 -0800 Subject: [PATCH 11/14] Add support for multi-value tooltips --- web/pages/components/chart.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/web/pages/components/chart.tsx b/web/pages/components/chart.tsx index 14c8f89f7..5a6968f36 100644 --- a/web/pages/components/chart.tsx +++ b/web/pages/components/chart.tsx @@ -5,7 +5,7 @@ import styles from '../../styles/styles.module.css'; interface ToolTipProps { active?: boolean, - payload?: object, + payload?: {name: string, payload: {value: string, time: Date}}[], unit?: string } @@ -32,12 +32,17 @@ function CustomizedTooltip(props: ToolTipProps) { const { active, payload, unit } = props; if (active && payload && payload[0]) { const time = payload[0].payload ? timeFormat("%I:%M")(new Date(payload[0].payload.time)) : ""; + + const tooltipDetails = payload.map(data => { + return
+ {data.payload.value}{unit} {data.name} +
+ }); return ( -
-

- {time} {payload[0].payload.value} {unit} -

-
+ + {time} + {tooltipDetails} + ); } return null; From 2fe1277cfaec8e8cd7bfcea8e3bf67d51b0126b3 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Wed, 11 Nov 2020 19:48:12 -0800 Subject: [PATCH 12/14] Cleanup debug views --- web/pages/hardware-info.tsx | 12 ------------ web/pages/storage.tsx | 15 +-------------- web/pages/update-server-config.tsx | 15 --------------- web/pages/video-config.tsx | 13 ------------- 4 files changed, 1 insertion(+), 54 deletions(-) diff --git a/web/pages/hardware-info.tsx b/web/pages/hardware-info.tsx index c82e3cb5a..122cc0671 100644 --- a/web/pages/hardware-info.tsx +++ b/web/pages/hardware-info.tsx @@ -102,18 +102,6 @@ const series = [ -

cpu:[], disk: [], memory: []; value = %age.

-

the times should be the same for each, though milliseconds differ

-
- {JSON.stringify(hardwareStatus)} -
); } \ No newline at end of file diff --git a/web/pages/storage.tsx b/web/pages/storage.tsx index fc9a9810f..88862a0af 100644 --- a/web/pages/storage.tsx +++ b/web/pages/storage.tsx @@ -12,7 +12,7 @@ function Storage({ config }) { return (

Local storage is being used. Enable external S3 storage if you want - to use it. + to use it. TODO: Make this message somewhat more informative. Point to documentation or something.

); } @@ -74,20 +74,7 @@ export default function ServerConfig() { return (
-

Server Config

-

- Display this data all pretty, most things will be editable in the - future, not now. -

-
-
); } diff --git a/web/pages/update-server-config.tsx b/web/pages/update-server-config.tsx index 901fd1704..4527efda2 100644 --- a/web/pages/update-server-config.tsx +++ b/web/pages/update-server-config.tsx @@ -149,24 +149,9 @@ export default function ServerConfig() { return (
-

Server Config

-

- Display this data all pretty, most things will be editable in the - future, not now. -

-
- - {JSON.stringify(config)} -
); } diff --git a/web/pages/video-config.tsx b/web/pages/video-config.tsx index faef9a149..b8d58dfea 100644 --- a/web/pages/video-config.tsx +++ b/web/pages/video-config.tsx @@ -109,20 +109,7 @@ export default function VideoConfig() { return (
-

Server Config

-

- Display this data all pretty, most things will be editable in the - future, not now. -

-
-
); } From d14743c396d7aa1d5240f36578c3ced835096b7b Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Wed, 11 Nov 2020 22:41:47 -0800 Subject: [PATCH 13/14] Support centered and left-justified statistic views --- web/pages/components/statistic.tsx | 4 +++- web/pages/hardware-info.tsx | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/web/pages/components/statistic.tsx b/web/pages/components/statistic.tsx index 45cca053e..64910e443 100644 --- a/web/pages/components/statistic.tsx +++ b/web/pages/components/statistic.tsx @@ -7,16 +7,18 @@ interface ItemProps { prefix: JSX.Element, color: string, progress?: boolean, + centered: boolean, }; export default function StatisticItem(props: ItemProps) { const { title, value, prefix } = props; const View = props.progress ? ProgressView : StatisticView; + const style = props.centered ? {display: 'flex', alignItems: 'center', justifyContent: 'center'} : {}; return (
-
+
diff --git a/web/pages/hardware-info.tsx b/web/pages/hardware-info.tsx index 122cc0671..8ae70a5df 100644 --- a/web/pages/hardware-info.tsx +++ b/web/pages/hardware-info.tsx @@ -81,6 +81,7 @@ const series = [ prefix={} color={series[0].color} progress + centered /> } color={series[1].color} progress + centered /> } color={series[2].color} progress + centered /> From 500e20281c206010a38f75635fe40f31f65dd610 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Wed, 11 Nov 2020 22:54:27 -0800 Subject: [PATCH 14/14] Add popover with thumbnail --- web/pages/components/main-layout.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/web/pages/components/main-layout.tsx b/web/pages/components/main-layout.tsx index 1b7ff11cb..64070873e 100644 --- a/web/pages/components/main-layout.tsx +++ b/web/pages/components/main-layout.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import Link from 'next/link'; import { differenceInSeconds } from "date-fns"; import { useRouter } from 'next/router'; -import { Layout, Menu } from 'antd'; +import { Layout, Menu, Popover } from 'antd'; import { SettingOutlined, @@ -38,6 +38,11 @@ export default function MainLayout(props) { const streamDurationString = online ? parseSecondsToDurationString(differenceInSeconds(new Date(), new Date(broadcaster.time))) : ""; + const content = ( +
+ +
+ ); const statusIcon = online ? : ; const statusMessage = online ? `Online ${streamDurationString}` : "Offline"; @@ -145,10 +150,12 @@ export default function MainLayout(props) {
+
{statusMessage} {statusIcon}
+
{children}