mirror of
https://github.com/owncast/owncast.git
synced 2024-11-23 21:28:29 +03:00
Replace broadcaster API call with generic server status call. Add upgrade check bool
This commit is contained in:
parent
940cc1da71
commit
c4351a53bf
9 changed files with 113 additions and 112 deletions
|
@ -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>
|
||||
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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 || {};
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
55
web/utils/server-status-context.tsx
Normal file
55
web/utils/server-status-context.tsx
Normal 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;
|
Loading…
Reference in a new issue