Replace broadcaster API call with generic server status call. Add upgrade check bool

This commit is contained in:
Gabe Kangas 2020-11-05 18:30:14 -08:00
parent 940cc1da71
commit c4351a53bf
9 changed files with 113 additions and 112 deletions

View file

@ -2,17 +2,17 @@ import 'antd/dist/antd.compact.css';
import "../styles/globals.scss";
import { AppProps } from 'next/app';
import BroadcastStatusProvider from '../utils/broadcast-status-context';
import ServerStatusProvider from '../utils/server-status-context';
import MainLayout from './components/main-layout';
function App({ Component, pageProps }: AppProps) {
return (
<BroadcastStatusProvider>
<ServerStatusProvider>
<MainLayout>
<Component {...pageProps} />
</MainLayout>
</BroadcastStatusProvider>
</ServerStatusProvider>
)
}

View file

@ -1,9 +1,9 @@
import React, { useContext } from 'react';
import { BroadcastStatusContext } from '../utils/broadcast-status-context';
import { ServerStatusContext } from '../utils/server-status-context';
export default function BroadcastInfo() {
const context = useContext(BroadcastStatusContext);
const context = useContext(ServerStatusContext);
const { broadcaster } = context || {};
const { remoteAddr, time, streamDetails } = broadcaster || {};

View file

@ -18,15 +18,17 @@ import { upgradeVersionAvailable } from "../../utils/apis";
import { parseSecondsToDurationString } from '../../utils/format'
import OwncastLogo from './logo';
import { BroadcastStatusContext } from '../../utils/broadcast-status-context';
import { ServerStatusContext } from '../../utils/server-status-context';
import adminStyles from '../../styles/styles.module.css';
let performedUpgradeCheck = false;
export default function MainLayout(props) {
const { children } = props;
const context = useContext(BroadcastStatusContext);
const { broadcastActive, broadcaster } = context || {};
const context = useContext(ServerStatusContext);
const { online, broadcaster, versionNumber } = context || {};
const router = useRouter();
const { route } = router || {};
@ -34,19 +36,15 @@ export default function MainLayout(props) {
const { Header, Footer, Content, Sider } = Layout;
const { SubMenu } = Menu;
const streamDurationString = broadcastActive ?
parseSecondsToDurationString(differenceInSeconds(new Date(), new Date(broadcaster.time))) : ""
const streamDurationString = online ? parseSecondsToDurationString(differenceInSeconds(new Date(), new Date(broadcaster.time))) : "";
const statusIcon = broadcastActive ?
<PlayCircleFilled /> : <MinusSquareFilled />;
const statusMessage = broadcastActive
? `Online ${ streamDurationString}`
: "Offline";
const statusIcon = online ? <PlayCircleFilled /> : <MinusSquareFilled />;
const statusMessage = online ? `Online ${streamDurationString}` : "Offline";
const [upgradeVersion, setUpgradeVersion] = useState(null);
const checkForUpgrade = async () => {
try {
const result = await upgradeVersionAvailable();
const result = await upgradeVersionAvailable(versionNumber);
setUpgradeVersion(result);
} catch (error) {
console.log("==== error", error);
@ -54,13 +52,17 @@ export default function MainLayout(props) {
};
useEffect(() => {
checkForUpgrade();
}, []);
if (!performedUpgradeCheck && !context.disableUpgradeChecks) {
checkForUpgrade();
console.log('checking')
performedUpgradeCheck = true
}
});
const appClass = classNames({
'owncast-layout': true,
[adminStyles.online]: broadcastActive,
})
"owncast-layout": true,
[adminStyles.online]: online,
});
const upgradeMenuItemStyle = upgradeVersion ? 'block' : 'none';
const upgradeVersionString = upgradeVersion || '';
@ -98,7 +100,7 @@ export default function MainLayout(props) {
<Menu.Item key="viewer-info">
<Link href="/viewer-info">Viewers</Link>
</Menu.Item>
{broadcastActive ? (
{online ? (
<Menu.Item key="disconnect-stream" icon={<CloseCircleOutlined />}>
<Link href="/disconnect-stream">Disconnect Stream...</Link>
</Menu.Item>
@ -151,7 +153,7 @@ export default function MainLayout(props) {
<Content className={adminStyles.contentMain}>{children}</Content>
<Footer style={{ textAlign: "center" }}>
<a href="https://owncast.online/">About Owncast</a>
<a href="https://owncast.online/">About Owncast v{versionNumber}</a>
</Footer>
</Layout>
</Layout>

View file

@ -11,12 +11,12 @@ import React, { useState, useEffect, useContext } from "react";
import { Row, Skeleton, Result, List, Typography, Card } from "antd";
import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons";
import { formatDistanceToNow, formatRelative } from "date-fns";
import { BroadcastStatusContext } from "../utils/broadcast-status-context";
import { ServerStatusContext } from "../utils/server-status-context";
import StatisticItem from "./components/statistic"
import LogTable from "./components/log-table";
import {
STREAM_STATUS,
STATUS,
SERVER_CONFIG,
LOGS_WARN,
fetchData,
@ -82,7 +82,7 @@ function Offline() {
}
export default function Stats() {
const context = useContext(BroadcastStatusContext);
const context = useContext(ServerStatusContext);
const { broadcaster } = context || {};
const { remoteAddr, streamDetails } = broadcaster || {};
@ -90,7 +90,7 @@ export default function Stats() {
const [stats, setStats] = useState(null);
const getStats = async () => {
try {
const result = await fetchData(STREAM_STATUS);
const result = await fetchData(STATUS);
setStats(result);
} catch (error) {
console.log(error);
@ -182,7 +182,7 @@ export default function Stats() {
});
const logTable = logs.length > 0 ? <LogTable logs={logs} pageSize={5} /> : null
const { viewerCount, sessionMaxViewerCount, lastConnectTime } = stats;
const { viewerCount, sessionMaxViewerCount } = stats;
const streamVideoDetailString = `${streamDetails.videoCodec} ${streamDetails.videoBitrate} kbps ${streamDetails.width}x${streamDetails.height}`;
const streamAudioDetailString = `${streamDetails.audioCodec} ${streamDetails.audioBitrate} kpbs`;
@ -192,10 +192,10 @@ export default function Stats() {
<Row gutter={[16, 16]}>
<StatisticItem
title={`Stream started ${formatRelative(
new Date(lastConnectTime),
new Date(broadcaster.time),
new Date()
)}`}
value={formatDistanceToNow(new Date(lastConnectTime))}
value={formatDistanceToNow(new Date(broadcaster.time))}
prefix={<ClockCircleOutlined />}
/>
<StatisticItem

View file

@ -1,8 +1,10 @@
import React, { useState, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import { Table, Tag } from "antd";
import { Table, Typography } from "antd";
import { getGithubRelease } from "../utils/apis";
const { Title } = Typography;
export default function Logs() {
const [release, setRelease] = useState({
html_url: "",
@ -32,13 +34,11 @@ export default function Logs() {
return (
<div>
<h2>
<Title level={2}>
<a href={release.html_url}>{release.name}</a>
</h2>
<h1>{release.created_at}</h1>
<ReactMarkdown>{release.body}</ReactMarkdown>;
<h3>Downloads</h3>
</Title>
<Title level={5}>{new Date(release.created_at).toDateString()}</Title>
<ReactMarkdown>{release.body}</ReactMarkdown>;<h3>Downloads</h3>
<AssetTable {...release.assets} />
</div>
);

View file

@ -7,23 +7,27 @@ import { SortOrder } from "antd/lib/table/interface";
import Chart from "./components/chart";
import StatisticItem from "./components/statistic";
import { BroadcastStatusContext } from '../utils/broadcast-status-context';
import { ServerStatusContext } from '../utils/server-status-context';
import {
CONNECTED_CLIENTS,
STREAM_STATUS, VIEWERS_OVER_TIME,
VIEWERS_OVER_TIME,
fetchData,
} from "../utils/apis";
const FETCH_INTERVAL = 5 * 60 * 1000; // 5 mins
export default function ViewersOverTime() {
const context = useContext(BroadcastStatusContext);
const { broadcastActive } = context || {};
const context = useContext(ServerStatusContext);
const {
broadcastActive,
viewerCount,
overallPeakViewerCount,
sessionPeakViewerCount,
} = context || {};
const [viewerInfo, setViewerInfo] = useState([]);
const [clients, setClients] = useState([]);
const [stats, setStats] = useState(null);
const getInfo = async () => {
try {
@ -39,14 +43,6 @@ export default function ViewersOverTime() {
} catch (error) {
console.log("==== error", error);
}
try {
const result = await fetchData(STREAM_STATUS);
setStats(result);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
@ -111,12 +107,17 @@ export default function ViewersOverTime() {
<Row gutter={[16, 16]}>
<StatisticItem
title="Current viewers"
value={stats?.viewerCount ?? ""}
value={viewerCount.toString()}
prefix={<UserOutlined />}
/>
<StatisticItem
title="Peak viewers this session"
value={stats?.sessionMaxViewerCount ?? ""}
value={sessionPeakViewerCount.toString()}
prefix={<UserOutlined />}
/>
<StatisticItem
title="Peak viewers overall"
value={overallPeakViewerCount.toString()}
prefix={<UserOutlined />}
/>
</Row>

View file

@ -8,7 +8,7 @@ const API_LOCATION = `${NEXT_PUBLIC_API_HOST}api/admin/`;
export const FETCH_INTERVAL = 15000;
// Current inbound broadcaster info
export const BROADCASTER = `${API_LOCATION}broadcaster`;
export const STATUS = `${API_LOCATION}status`;
// Disconnect inbound stream
export const DISCONNECT = `${API_LOCATION}disconnect`;
@ -36,11 +36,6 @@ export const LOGS_WARN = `${API_LOCATION}logs/warnings`;
const GITHUB_RELEASE_URL = "https://api.github.com/repos/owncast/owncast/releases/latest";
// Current Stream status.
// This is literally the same as /api/status except it supports
// auth.
export const STREAM_STATUS = `${API_LOCATION}status`;
export async function fetchData(url) {
const encoded = btoa(`${ADMIN_USERNAME}:${ADMIN_STREAMKEY}`);
@ -81,8 +76,7 @@ export async function getGithubRelease() {
// Make a request to the server status API and the Github releases API
// and return a release if it's newer than the server version.
export async function upgradeVersionAvailable() {
const serverVersion = await fetchData(STREAM_STATUS)
export async function upgradeVersionAvailable(currentVersion) {
const recentRelease = await getGithubRelease();
let recentReleaseVersion = recentRelease.tag_name;
@ -90,7 +84,7 @@ export async function upgradeVersionAvailable() {
recentReleaseVersion = recentReleaseVersion.substr(1)
}
if (!upToDate(serverVersion.versionNumber, recentReleaseVersion)) {
if (!upToDate(currentVersion, recentReleaseVersion)) {
return recentReleaseVersion
}

View file

@ -1,51 +0,0 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { BROADCASTER, fetchData, FETCH_INTERVAL } from './apis';
const initialState = {
broadcastActive: false,
message: '',
broadcaster: null,
};
export const BroadcastStatusContext = React.createContext(initialState);
const BroadcastStatusProvider = ({ children }) => {
const [broadcasterStatus, setBroadcasterStatus] = useState(initialState);
const getBroadcastStatus = async () => {
try {
const result = await fetchData(BROADCASTER);
const broadcastActive = !!result.broadcaster || result.success;
setBroadcasterStatus({ ...result, broadcastActive });
} catch (error) {
setBroadcasterStatus({ ...broadcasterStatus, message: error.message });
}
};
useEffect(() => {
let getStatusIntervalId = null;
getBroadcastStatus();
getStatusIntervalId = setInterval(getBroadcastStatus, FETCH_INTERVAL);
// returned function will be called on component unmount
return () => {
clearInterval(getStatusIntervalId);
}
}, [])
return (
<BroadcastStatusContext.Provider value={broadcasterStatus}>
{children}
</BroadcastStatusContext.Provider>
);
}
BroadcastStatusProvider.propTypes = {
children: PropTypes.element.isRequired,
};
export default BroadcastStatusProvider;

View file

@ -0,0 +1,55 @@
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { STATUS, fetchData, FETCH_INTERVAL } from './apis';
const initialState = {
broadcastActive: false,
broadcaster: null,
online: false,
viewerCount: 0,
sessionPeakViewerCount: 0,
overallPeakViewerCount: 0,
disableUpgradeChecks: true,
versionNumber: '0.0.0',
};
export const ServerStatusContext = React.createContext(initialState);
const ServerStatusProvider = ({ children }) => {
const [status, setStatus] = useState(initialState);
const getStatus = async () => {
try {
const result = await fetchData(STATUS);
setStatus({ ...result });
} catch (error) {
// setBroadcasterStatus({ ...broadcasterStatus, message: error.message });
}
};
useEffect(() => {
let getStatusIntervalId = null;
getStatus();
getStatusIntervalId = setInterval(getStatus, FETCH_INTERVAL);
// returned function will be called on component unmount
return () => {
clearInterval(getStatusIntervalId);
}
}, [])
return (
<ServerStatusContext.Provider value={status}>
{children}
</ServerStatusContext.Provider>
);
}
ServerStatusProvider.propTypes = {
children: PropTypes.element.isRequired,
};
export default ServerStatusProvider;