Merge branch '0.0.6' of github.com:owncast/owncast-admin into 0.0.6
This commit is contained in:
commit
31728f2538
@ -1,6 +1,6 @@
|
|||||||
import React, { useContext, useState, useEffect } from 'react';
|
import React, { useContext, useState, useEffect } from 'react';
|
||||||
import { Typography, Slider } from 'antd';
|
import { Typography, Slider } from 'antd';
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
@ -4,8 +4,8 @@ import { Typography } from 'antd';
|
|||||||
|
|
||||||
import ToggleSwitch from './form-toggleswitch-with-submit';
|
import ToggleSwitch from './form-toggleswitch-with-submit';
|
||||||
|
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import { FIELD_PROPS_NSFW, FIELD_PROPS_YP } from './constants';
|
import { FIELD_PROPS_NSFW, FIELD_PROPS_YP } from '../../utils/config-constants';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
@ -4,7 +4,7 @@ import TextFieldWithSubmit, {
|
|||||||
TEXTFIELD_TYPE_URL,
|
TEXTFIELD_TYPE_URL,
|
||||||
} from './form-textfield-with-submit';
|
} from './form-textfield-with-submit';
|
||||||
|
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import {
|
import {
|
||||||
postConfigUpdateToAPI,
|
postConfigUpdateToAPI,
|
||||||
TEXTFIELD_PROPS_INSTANCE_URL,
|
TEXTFIELD_PROPS_INSTANCE_URL,
|
||||||
@ -12,9 +12,9 @@ import {
|
|||||||
TEXTFIELD_PROPS_SERVER_SUMMARY,
|
TEXTFIELD_PROPS_SERVER_SUMMARY,
|
||||||
TEXTFIELD_PROPS_LOGO,
|
TEXTFIELD_PROPS_LOGO,
|
||||||
API_YP_SWITCH,
|
API_YP_SWITCH,
|
||||||
} from './constants';
|
} from '../../utils/config-constants';
|
||||||
|
|
||||||
import { UpdateArgs } from '../../../types/config-section';
|
import { UpdateArgs } from '../../types/config-section';
|
||||||
|
|
||||||
export default function EditInstanceDetails() {
|
export default function EditInstanceDetails() {
|
||||||
const [formDataValues, setFormDataValues] = useState(null);
|
const [formDataValues, setFormDataValues] = useState(null);
|
@ -5,17 +5,17 @@ import { CopyOutlined, RedoOutlined } from '@ant-design/icons';
|
|||||||
import { TEXTFIELD_TYPE_NUMBER, TEXTFIELD_TYPE_PASSWORD } from './form-textfield';
|
import { TEXTFIELD_TYPE_NUMBER, TEXTFIELD_TYPE_PASSWORD } from './form-textfield';
|
||||||
import TextFieldWithSubmit from './form-textfield-with-submit';
|
import TextFieldWithSubmit from './form-textfield-with-submit';
|
||||||
|
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import { AlertMessageContext } from '../../../utils/alert-message-context';
|
import { AlertMessageContext } from '../../utils/alert-message-context';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TEXTFIELD_PROPS_FFMPEG,
|
TEXTFIELD_PROPS_FFMPEG,
|
||||||
TEXTFIELD_PROPS_RTMP_PORT,
|
TEXTFIELD_PROPS_RTMP_PORT,
|
||||||
TEXTFIELD_PROPS_STREAM_KEY,
|
TEXTFIELD_PROPS_STREAM_KEY,
|
||||||
TEXTFIELD_PROPS_WEB_PORT,
|
TEXTFIELD_PROPS_WEB_PORT,
|
||||||
} from './constants';
|
} from '../../utils/config-constants';
|
||||||
|
|
||||||
import { UpdateArgs } from '../../../types/config-section';
|
import { UpdateArgs } from '../../types/config-section';
|
||||||
|
|
||||||
export default function EditInstanceDetails() {
|
export default function EditInstanceDetails() {
|
||||||
const [formDataValues, setFormDataValues] = useState(null);
|
const [formDataValues, setFormDataValues] = useState(null);
|
||||||
@ -55,7 +55,9 @@ export default function EditInstanceDetails() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const showStreamKeyChangeMessage = () => {
|
const showStreamKeyChangeMessage = () => {
|
||||||
setMessage('Changing your stream key will log you out of the admin and block you from streaming until you change the key in your broadcasting software.');
|
setMessage(
|
||||||
|
'Changing your stream key will log you out of the admin and block you from streaming until you change the key in your broadcasting software.',
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const showFfmpegChangeMessage = () => {
|
const showFfmpegChangeMessage = () => {
|
@ -3,19 +3,19 @@ import { Typography, Table, Button, Modal, Input } from 'antd';
|
|||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { DeleteOutlined } from '@ant-design/icons';
|
import { DeleteOutlined } from '@ant-design/icons';
|
||||||
import SocialDropdown from './social-icons-dropdown';
|
import SocialDropdown from './social-icons-dropdown';
|
||||||
import { fetchData, NEXT_PUBLIC_API_HOST, SOCIAL_PLATFORMS_LIST } from '../../../utils/apis';
|
import { fetchData, NEXT_PUBLIC_API_HOST, SOCIAL_PLATFORMS_LIST } from '../../utils/apis';
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import {
|
import {
|
||||||
API_SOCIAL_HANDLES,
|
API_SOCIAL_HANDLES,
|
||||||
postConfigUpdateToAPI,
|
postConfigUpdateToAPI,
|
||||||
RESET_TIMEOUT,
|
RESET_TIMEOUT,
|
||||||
DEFAULT_SOCIAL_HANDLE,
|
DEFAULT_SOCIAL_HANDLE,
|
||||||
OTHER_SOCIAL_HANDLE_OPTION,
|
OTHER_SOCIAL_HANDLE_OPTION,
|
||||||
} from './constants';
|
} from '../../utils/config-constants';
|
||||||
import { SocialHandle, UpdateArgs } from '../../../types/config-section';
|
import { SocialHandle, UpdateArgs } from '../../types/config-section';
|
||||||
import { isValidUrl } from '../../../utils/urls';
|
import { isValidUrl } from '../../utils/urls';
|
||||||
import TextField from './form-textfield';
|
import TextField from './form-textfield';
|
||||||
import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../../utils/input-statuses';
|
import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../utils/input-statuses';
|
||||||
import FormStatusIndicator from './form-status-indicator';
|
import FormStatusIndicator from './form-status-indicator';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
@ -43,9 +43,9 @@ export default function EditSocialLinks() {
|
|||||||
let resetTimer = null;
|
let resetTimer = null;
|
||||||
|
|
||||||
const PLACEHOLDERS = {
|
const PLACEHOLDERS = {
|
||||||
'mastodon': 'https://mastodon.social/@username',
|
mastodon: 'https://mastodon.social/@username',
|
||||||
'twitter': 'https://twitter.com/username'
|
twitter: 'https://twitter.com/username',
|
||||||
}
|
};
|
||||||
|
|
||||||
const getAvailableIcons = async () => {
|
const getAvailableIcons = async () => {
|
||||||
try {
|
try {
|
@ -1,26 +1,26 @@
|
|||||||
import { Switch, Button, Collapse } from 'antd';
|
import { Switch, Button, Collapse } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { useContext, useState, useEffect } from 'react';
|
import React, { useContext, useState, useEffect } from 'react';
|
||||||
import { UpdateArgs } from '../../../types/config-section';
|
import { UpdateArgs } from '../../types/config-section';
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import { AlertMessageContext } from '../../../utils/alert-message-context';
|
import { AlertMessageContext } from '../../utils/alert-message-context';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
postConfigUpdateToAPI,
|
postConfigUpdateToAPI,
|
||||||
API_S3_INFO,
|
API_S3_INFO,
|
||||||
RESET_TIMEOUT,
|
RESET_TIMEOUT,
|
||||||
S3_TEXT_FIELDS_INFO,
|
S3_TEXT_FIELDS_INFO,
|
||||||
} from './constants';
|
} from '../../utils/config-constants';
|
||||||
import {
|
import {
|
||||||
createInputStatus,
|
createInputStatus,
|
||||||
StatusState,
|
StatusState,
|
||||||
STATUS_ERROR,
|
STATUS_ERROR,
|
||||||
STATUS_PROCESSING,
|
STATUS_PROCESSING,
|
||||||
STATUS_SUCCESS,
|
STATUS_SUCCESS,
|
||||||
} from '../../../utils/input-statuses';
|
} from '../../utils/input-statuses';
|
||||||
import TextField from './form-textfield';
|
import TextField from './form-textfield';
|
||||||
import FormStatusIndicator from './form-status-indicator';
|
import FormStatusIndicator from './form-status-indicator';
|
||||||
import { isValidUrl } from '../../../utils/urls';
|
import { isValidUrl } from '../../utils/urls';
|
||||||
|
|
||||||
const { Panel } = Collapse;
|
const { Panel } = Collapse;
|
||||||
|
|
@ -2,10 +2,14 @@
|
|||||||
import React, { useContext, useState, useEffect } from 'react';
|
import React, { useContext, useState, useEffect } from 'react';
|
||||||
import { Typography, Tag } from 'antd';
|
import { Typography, Tag } from 'antd';
|
||||||
|
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import { FIELD_PROPS_TAGS, RESET_TIMEOUT, postConfigUpdateToAPI } from './constants';
|
import {
|
||||||
|
FIELD_PROPS_TAGS,
|
||||||
|
RESET_TIMEOUT,
|
||||||
|
postConfigUpdateToAPI,
|
||||||
|
} from '../../utils/config-constants';
|
||||||
import TextField from './form-textfield';
|
import TextField from './form-textfield';
|
||||||
import { UpdateArgs } from '../../../types/config-section';
|
import { UpdateArgs } from '../../types/config-section';
|
||||||
import {
|
import {
|
||||||
createInputStatus,
|
createInputStatus,
|
||||||
StatusState,
|
StatusState,
|
||||||
@ -13,7 +17,7 @@ import {
|
|||||||
STATUS_PROCESSING,
|
STATUS_PROCESSING,
|
||||||
STATUS_SUCCESS,
|
STATUS_SUCCESS,
|
||||||
STATUS_WARNING,
|
STATUS_WARNING,
|
||||||
} from '../../../utils/input-statuses';
|
} from '../../utils/input-statuses';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { StatusState } from '../../../utils/input-statuses';
|
import { StatusState } from '../../utils/input-statuses';
|
||||||
|
|
||||||
interface FormStatusIndicatorProps {
|
interface FormStatusIndicatorProps {
|
||||||
status: StatusState;
|
status: StatusState;
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useEffect, useState, useContext } from 'react';
|
import React, { useEffect, useState, useContext } from 'react';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { RESET_TIMEOUT, postConfigUpdateToAPI } from './constants';
|
import { RESET_TIMEOUT, postConfigUpdateToAPI } from '../../utils/config-constants';
|
||||||
|
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import TextField, { TextFieldProps } from './form-textfield';
|
import TextField, { TextFieldProps } from './form-textfield';
|
||||||
import {
|
import {
|
||||||
createInputStatus,
|
createInputStatus,
|
||||||
@ -11,8 +11,8 @@ import {
|
|||||||
STATUS_ERROR,
|
STATUS_ERROR,
|
||||||
STATUS_PROCESSING,
|
STATUS_PROCESSING,
|
||||||
STATUS_SUCCESS,
|
STATUS_SUCCESS,
|
||||||
} from '../../../utils/input-statuses';
|
} from '../../utils/input-statuses';
|
||||||
import { UpdateArgs } from '../../../types/config-section';
|
import { UpdateArgs } from '../../types/config-section';
|
||||||
import FormStatusIndicator from './form-status-indicator';
|
import FormStatusIndicator from './form-status-indicator';
|
||||||
|
|
||||||
export const TEXTFIELD_TYPE_TEXT = 'default';
|
export const TEXTFIELD_TYPE_TEXT = 'default';
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Input, InputNumber } from 'antd';
|
import { Input, InputNumber } from 'antd';
|
||||||
import { FieldUpdaterFunc } from '../../../types/config-section';
|
import { FieldUpdaterFunc } from '../../types/config-section';
|
||||||
// import InfoTip from '../info-tip';
|
// import InfoTip from '../info-tip';
|
||||||
import { StatusState } from '../../../utils/input-statuses';
|
import { StatusState } from '../../utils/input-statuses';
|
||||||
import FormStatusIndicator from './form-status-indicator';
|
import FormStatusIndicator from './form-status-indicator';
|
||||||
|
|
||||||
export const TEXTFIELD_TYPE_TEXT = 'default';
|
export const TEXTFIELD_TYPE_TEXT = 'default';
|
||||||
@ -96,10 +96,6 @@ export default function TextField(props: TextFieldProps) {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 10 ** maxLength - 1,
|
max: 10 ** maxLength - 1,
|
||||||
onKeyDown: (e: React.KeyboardEvent) => {
|
|
||||||
if (e.target.value.length > maxLength - 1) e.preventDefault();
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
} else if (type === TEXTFIELD_TYPE_URL) {
|
} else if (type === TEXTFIELD_TYPE_URL) {
|
||||||
fieldProps = {
|
fieldProps = {
|
||||||
@ -140,7 +136,7 @@ export default function TextField(props: TextFieldProps) {
|
|||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
onPressEnter={handlePressEnter}
|
onPressEnter={handlePressEnter}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={value}
|
value={value as number | (readonly string[] & number)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<FormStatusIndicator status={status} />
|
<FormStatusIndicator status={status} />
|
||||||
@ -159,7 +155,7 @@ TextField.defaultProps = {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
// initialValue: '',
|
// initialValue: '',
|
||||||
label: '',
|
label: '',
|
||||||
maxLength: null,
|
maxLength: 255,
|
||||||
|
|
||||||
placeholder: '',
|
placeholder: '',
|
||||||
required: false,
|
required: false,
|
@ -6,12 +6,12 @@ import {
|
|||||||
STATUS_ERROR,
|
STATUS_ERROR,
|
||||||
STATUS_PROCESSING,
|
STATUS_PROCESSING,
|
||||||
STATUS_SUCCESS,
|
STATUS_SUCCESS,
|
||||||
} from '../../../utils/input-statuses';
|
} from '../../utils/input-statuses';
|
||||||
import FormStatusIndicator from './form-status-indicator';
|
import FormStatusIndicator from './form-status-indicator';
|
||||||
|
|
||||||
import { RESET_TIMEOUT, postConfigUpdateToAPI } from './constants';
|
import { RESET_TIMEOUT, postConfigUpdateToAPI } from '../../utils/config-constants';
|
||||||
|
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import InfoTip from '../info-tip';
|
import InfoTip from '../info-tip';
|
||||||
|
|
||||||
interface ToggleSwitchProps {
|
interface ToggleSwitchProps {
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import { SocialHandleDropdownItem } from '../../../types/config-section';
|
import { SocialHandleDropdownItem } from '../../types/config-section';
|
||||||
import { NEXT_PUBLIC_API_HOST } from '../../../utils/apis';
|
import { NEXT_PUBLIC_API_HOST } from '../../utils/apis';
|
||||||
import { OTHER_SOCIAL_HANDLE_OPTION } from './constants';
|
import { OTHER_SOCIAL_HANDLE_OPTION } from '../../utils/config-constants';
|
||||||
|
|
||||||
interface DropdownProps {
|
interface DropdownProps {
|
||||||
iconList: SocialHandleDropdownItem[];
|
iconList: SocialHandleDropdownItem[];
|
@ -1,15 +1,19 @@
|
|||||||
import React, { useContext, useState, useEffect } from 'react';
|
import React, { useContext, useState, useEffect } from 'react';
|
||||||
import { Typography, Slider } from 'antd';
|
import { Typography, Slider } from 'antd';
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import { AlertMessageContext } from '../../../utils/alert-message-context';
|
import { AlertMessageContext } from '../../utils/alert-message-context';
|
||||||
import { API_VIDEO_SEGMENTS, RESET_TIMEOUT, postConfigUpdateToAPI } from './constants';
|
import {
|
||||||
|
API_VIDEO_SEGMENTS,
|
||||||
|
RESET_TIMEOUT,
|
||||||
|
postConfigUpdateToAPI,
|
||||||
|
} from '../../utils/config-constants';
|
||||||
import {
|
import {
|
||||||
createInputStatus,
|
createInputStatus,
|
||||||
StatusState,
|
StatusState,
|
||||||
STATUS_ERROR,
|
STATUS_ERROR,
|
||||||
STATUS_PROCESSING,
|
STATUS_PROCESSING,
|
||||||
STATUS_SUCCESS,
|
STATUS_SUCCESS,
|
||||||
} from '../../../utils/input-statuses';
|
} from '../../utils/input-statuses';
|
||||||
import FormStatusIndicator from './form-status-indicator';
|
import FormStatusIndicator from './form-status-indicator';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
@ -88,7 +92,9 @@ export default function VideoLatency() {
|
|||||||
// setSubmitStatusMessage('Variants updated.');
|
// setSubmitStatusMessage('Variants updated.');
|
||||||
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
resetTimer = setTimeout(resetStates, RESET_TIMEOUT);
|
||||||
if (serverStatusData.online) {
|
if (serverStatusData.online) {
|
||||||
setMessage('Your latency buffer setting will take effect the next time you begin a live stream.')
|
setMessage(
|
||||||
|
'Your latency buffer setting will take effect the next time you begin a live stream.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (message: string) => {
|
onError: (message: string) => {
|
||||||
@ -109,11 +115,13 @@ export default function VideoLatency() {
|
|||||||
<div className="config-video-segements-conatiner">
|
<div className="config-video-segements-conatiner">
|
||||||
<Title level={3}>Latency Buffer</Title>
|
<Title level={3}>Latency Buffer</Title>
|
||||||
<p>
|
<p>
|
||||||
While it's natural to want to keep your latency as low as possible, you may experience reduced error tolerance and stability in some environments the lower you go.
|
While it's natural to want to keep your latency as low as possible, you may experience
|
||||||
</p>
|
reduced error tolerance and stability in some environments the lower you go.
|
||||||
For interactive live streams you may want to experiment with a lower latency, for non-interactive broadcasts you may want to increase it. <a href="https://owncast.online/docs/encoding#latency-buffer">Read to learn more.</a>
|
|
||||||
<p>
|
|
||||||
</p>
|
</p>
|
||||||
|
For interactive live streams you may want to experiment with a lower latency, for
|
||||||
|
non-interactive broadcasts you may want to increase it.{' '}
|
||||||
|
<a href="https://owncast.online/docs/encoding#latency-buffer">Read to learn more.</a>
|
||||||
|
<p></p>
|
||||||
<div className="segment-slider-container">
|
<div className="segment-slider-container">
|
||||||
<Slider
|
<Slider
|
||||||
tipFormatter={value => <SegmentToolTip value={SLIDER_COMMENTS[value]} />}
|
tipFormatter={value => <SegmentToolTip value={SLIDER_COMMENTS[value]} />}
|
@ -1,8 +1,8 @@
|
|||||||
// This content populates the video variant modal, which is spawned from the variants table.
|
// This content populates the video variant modal, which is spawned from the variants table.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Slider, Switch, Collapse } from 'antd';
|
import { Slider, Switch, Collapse } from 'antd';
|
||||||
import { FieldUpdaterFunc, VideoVariant } from '../../../types/config-section';
|
import { FieldUpdaterFunc, VideoVariant } from '../../types/config-section';
|
||||||
import { DEFAULT_VARIANT_STATE } from './constants';
|
import { DEFAULT_VARIANT_STATE } from '../../utils/config-constants';
|
||||||
import InfoTip from '../info-tip';
|
import InfoTip from '../info-tip';
|
||||||
import CPUUsageSelector from './cpu-usage';
|
import CPUUsageSelector from './cpu-usage';
|
||||||
|
|
@ -4,9 +4,9 @@ import React, { useContext, useState } from 'react';
|
|||||||
import { Typography, Table, Modal, Button } from 'antd';
|
import { Typography, Table, Modal, Button } from 'antd';
|
||||||
import { ColumnsType } from 'antd/lib/table';
|
import { ColumnsType } from 'antd/lib/table';
|
||||||
import { DeleteOutlined } from '@ant-design/icons';
|
import { DeleteOutlined } from '@ant-design/icons';
|
||||||
import { ServerStatusContext } from '../../../utils/server-status-context';
|
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||||
import { AlertMessageContext } from '../../../utils/alert-message-context';
|
import { AlertMessageContext } from '../../utils/alert-message-context';
|
||||||
import { UpdateArgs, VideoVariant } from '../../../types/config-section';
|
import { UpdateArgs, VideoVariant } from '../../types/config-section';
|
||||||
|
|
||||||
import VideoVariantForm from './video-variant-form';
|
import VideoVariantForm from './video-variant-form';
|
||||||
import {
|
import {
|
||||||
@ -15,7 +15,7 @@ import {
|
|||||||
SUCCESS_STATES,
|
SUCCESS_STATES,
|
||||||
RESET_TIMEOUT,
|
RESET_TIMEOUT,
|
||||||
postConfigUpdateToAPI,
|
postConfigUpdateToAPI,
|
||||||
} from './constants';
|
} from '../../utils/config-constants';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
@ -18,17 +18,17 @@ import {
|
|||||||
ExperimentOutlined,
|
ExperimentOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { upgradeVersionAvailable } from '../../utils/apis';
|
import { upgradeVersionAvailable } from '../utils/apis';
|
||||||
import { parseSecondsToDurationString } from '../../utils/format';
|
import { parseSecondsToDurationString } from '../utils/format';
|
||||||
|
|
||||||
import OwncastLogo from './logo';
|
import OwncastLogo from './logo';
|
||||||
import { ServerStatusContext } from '../../utils/server-status-context';
|
import { ServerStatusContext } from '../utils/server-status-context';
|
||||||
import { AlertMessageContext } from '../../utils/alert-message-context';
|
import { AlertMessageContext } from '../utils/alert-message-context';
|
||||||
|
|
||||||
import TextFieldWithSubmit from './config/form-textfield-with-submit';
|
import TextFieldWithSubmit from './config/form-textfield-with-submit';
|
||||||
import { TEXTFIELD_PROPS_STREAM_TITLE } from './config/constants';
|
import { TEXTFIELD_PROPS_STREAM_TITLE } from '../utils/config-constants';
|
||||||
|
|
||||||
import { UpdateArgs } from '../../types/config-section';
|
import { UpdateArgs } from '../types/config-section';
|
||||||
|
|
||||||
let performedUpgradeCheck = false;
|
let performedUpgradeCheck = false;
|
||||||
|
|
@ -7,10 +7,10 @@ import {
|
|||||||
CheckCircleFilled,
|
CheckCircleFilled,
|
||||||
ExclamationCircleFilled,
|
ExclamationCircleFilled,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { fetchData, UPDATE_CHAT_MESSGAE_VIZ } from '../../utils/apis';
|
import { fetchData, UPDATE_CHAT_MESSGAE_VIZ } from '../utils/apis';
|
||||||
import { MessageType } from '../../types/chat';
|
import { MessageType } from '../types/chat';
|
||||||
import { OUTCOME_TIMEOUT } from '../chat';
|
import { OUTCOME_TIMEOUT } from '../pages/chat';
|
||||||
import { isEmptyObject } from '../../utils/format';
|
import { isEmptyObject } from '../utils/format';
|
||||||
|
|
||||||
interface MessageToggleProps {
|
interface MessageToggleProps {
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
@ -23,7 +23,7 @@ import { AppProps } from 'next/app';
|
|||||||
import ServerStatusProvider from '../utils/server-status-context';
|
import ServerStatusProvider from '../utils/server-status-context';
|
||||||
import AlertMessageProvider from '../utils/alert-message-context';
|
import AlertMessageProvider from '../utils/alert-message-context';
|
||||||
|
|
||||||
import MainLayout from './components/main-layout';
|
import MainLayout from '../components/main-layout';
|
||||||
|
|
||||||
function App({ Component, pageProps }: AppProps) {
|
function App({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Table, Typography, Tooltip, Button } from "antd";
|
import { Table, Typography, Tooltip, Button } from 'antd';
|
||||||
import { CheckCircleFilled, ExclamationCircleFilled } from "@ant-design/icons";
|
import { CheckCircleFilled, ExclamationCircleFilled } from '@ant-design/icons';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { ColumnsType } from 'antd/es/table';
|
import { ColumnsType } from 'antd/es/table';
|
||||||
import format from 'date-fns/format'
|
import format from 'date-fns/format';
|
||||||
|
|
||||||
import { CHAT_HISTORY, fetchData, FETCH_INTERVAL, UPDATE_CHAT_MESSGAE_VIZ } from "../utils/apis";
|
import { CHAT_HISTORY, fetchData, FETCH_INTERVAL, UPDATE_CHAT_MESSGAE_VIZ } from '../utils/apis';
|
||||||
import { MessageType } from '../types/chat';
|
import { MessageType } from '../types/chat';
|
||||||
import { isEmptyObject } from "../utils/format";
|
import { isEmptyObject } from '../utils/format';
|
||||||
import MessageVisiblityToggle from "./components/message-visiblity-toggle";
|
import MessageVisiblityToggle from '../components/message-visiblity-toggle';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ export default function Chat() {
|
|||||||
setMessages(result);
|
setMessages(result);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("==== error", error);
|
console.log('==== error', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ export default function Chat() {
|
|||||||
|
|
||||||
const updateMessage = message => {
|
const updateMessage = message => {
|
||||||
const messageIndex = messages.findIndex(m => m.id === message.id);
|
const messageIndex = messages.findIndex(m => m.id === message.id);
|
||||||
messages.splice(messageIndex, 1, message)
|
messages.splice(messageIndex, 1, message);
|
||||||
setMessages([...messages]);
|
setMessages([...messages]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ export default function Chat() {
|
|||||||
setBulkAction('');
|
setBulkAction('');
|
||||||
}, OUTCOME_TIMEOUT);
|
}, OUTCOME_TIMEOUT);
|
||||||
};
|
};
|
||||||
const handleSubmitBulk = async (bulkVisibility) => {
|
const handleSubmitBulk = async bulkVisibility => {
|
||||||
setBulkProcessing(true);
|
setBulkProcessing(true);
|
||||||
const result = await fetchData(UPDATE_CHAT_MESSGAE_VIZ, {
|
const result = await fetchData(UPDATE_CHAT_MESSGAE_VIZ, {
|
||||||
auth: true,
|
auth: true,
|
||||||
@ -104,7 +104,7 @@ export default function Chat() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.success && result.message === "changed") {
|
if (result.success && result.message === 'changed') {
|
||||||
setBulkOutcome(<CheckCircleFilled />);
|
setBulkOutcome(<CheckCircleFilled />);
|
||||||
resetBulkOutcome();
|
resetBulkOutcome();
|
||||||
|
|
||||||
@ -123,15 +123,15 @@ export default function Chat() {
|
|||||||
resetBulkOutcome();
|
resetBulkOutcome();
|
||||||
}
|
}
|
||||||
setBulkProcessing(false);
|
setBulkProcessing(false);
|
||||||
}
|
};
|
||||||
const handleSubmitBulkShow = () => {
|
const handleSubmitBulkShow = () => {
|
||||||
setBulkAction('show');
|
setBulkAction('show');
|
||||||
handleSubmitBulk(true);
|
handleSubmitBulk(true);
|
||||||
}
|
};
|
||||||
const handleSubmitBulkHide = () => {
|
const handleSubmitBulkHide = () => {
|
||||||
setBulkAction('hide');
|
setBulkAction('hide');
|
||||||
handleSubmitBulk(false);
|
handleSubmitBulk(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const chatColumns: ColumnsType<MessageType> = [
|
const chatColumns: ColumnsType<MessageType> = [
|
||||||
{
|
{
|
||||||
@ -140,7 +140,7 @@ export default function Chat() {
|
|||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
className: 'timestamp-col',
|
className: 'timestamp-col',
|
||||||
defaultSortOrder: 'descend',
|
defaultSortOrder: 'descend',
|
||||||
render: (timestamp) => {
|
render: timestamp => {
|
||||||
const dateObject = new Date(timestamp);
|
const dateObject = new Date(timestamp);
|
||||||
return format(dateObject, 'PP pp');
|
return format(dateObject, 'PP pp');
|
||||||
},
|
},
|
||||||
@ -176,21 +176,20 @@ export default function Chat() {
|
|||||||
// eslint-disable-next-line react/no-danger
|
// eslint-disable-next-line react/no-danger
|
||||||
dangerouslySetInnerHTML={{ __html: body }}
|
dangerouslySetInnerHTML={{ __html: body }}
|
||||||
/>
|
/>
|
||||||
)
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
dataIndex: 'visible',
|
dataIndex: 'visible',
|
||||||
key: 'visible',
|
key: 'visible',
|
||||||
className: 'toggle-col',
|
className: 'toggle-col',
|
||||||
filters: [{ text: 'Visible messages', value: true }, { text: 'Hidden messages', value: false }],
|
filters: [
|
||||||
|
{ text: 'Visible messages', value: true },
|
||||||
|
{ text: 'Hidden messages', value: false },
|
||||||
|
],
|
||||||
onFilter: (value, record) => record.visible === value,
|
onFilter: (value, record) => record.visible === value,
|
||||||
render: (visible, record) => (
|
render: (visible, record) => (
|
||||||
<MessageVisiblityToggle
|
<MessageVisiblityToggle isVisible={visible} message={record} setMessage={updateMessage} />
|
||||||
isVisible={visible}
|
|
||||||
message={record}
|
|
||||||
setMessage={updateMessage}
|
|
||||||
/>
|
|
||||||
),
|
),
|
||||||
width: 30,
|
width: 30,
|
||||||
},
|
},
|
||||||
@ -238,12 +237,12 @@ export default function Chat() {
|
|||||||
className="messages-table"
|
className="messages-table"
|
||||||
pagination={{ pageSize: 100 }}
|
pagination={{ pageSize: 100 }}
|
||||||
scroll={{ y: 540 }}
|
scroll={{ y: 540 }}
|
||||||
rowClassName={record => !record.visible ? 'hidden' : ''}
|
rowClassName={record => (!record.visible ? 'hidden' : '')}
|
||||||
dataSource={messages}
|
dataSource={messages}
|
||||||
columns={chatColumns}
|
columns={chatColumns}
|
||||||
rowKey={(row) => row.id}
|
rowKey={row => row.id}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
/>
|
/>
|
||||||
</div>)
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
postConfigUpdateToAPI,
|
postConfigUpdateToAPI,
|
||||||
RESET_TIMEOUT,
|
RESET_TIMEOUT,
|
||||||
API_CUSTOM_CONTENT,
|
API_CUSTOM_CONTENT,
|
||||||
} from './components/config/constants';
|
} from '../utils/config-constants';
|
||||||
import {
|
import {
|
||||||
createInputStatus,
|
createInputStatus,
|
||||||
StatusState,
|
StatusState,
|
||||||
@ -17,7 +17,7 @@ import {
|
|||||||
STATUS_SUCCESS,
|
STATUS_SUCCESS,
|
||||||
} from '../utils/input-statuses';
|
} from '../utils/input-statuses';
|
||||||
import 'react-markdown-editor-lite/lib/index.css';
|
import 'react-markdown-editor-lite/lib/index.css';
|
||||||
import FormStatusIndicator from './components/config/form-status-indicator';
|
import FormStatusIndicator from '../components/config/form-status-indicator';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
@ -87,7 +87,8 @@ export default function PageContentEditor() {
|
|||||||
<Title level={2}>Page Content</Title>
|
<Title level={2}>Page Content</Title>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Edit the content of your page by using simple <a href="https://www.markdownguide.org/basic-syntax/">Markdown syntax</a>.
|
Edit the content of your page by using simple{' '}
|
||||||
|
<a href="https://www.markdownguide.org/basic-syntax/">Markdown syntax</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<MdEditor
|
<MdEditor
|
||||||
@ -108,7 +109,6 @@ export default function PageContentEditor() {
|
|||||||
</Button>
|
</Button>
|
||||||
) : null}
|
) : null}
|
||||||
<FormStatusIndicator status={submitStatus} />
|
<FormStatusIndicator status={submitStatus} />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -2,9 +2,9 @@ import React from 'react';
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import EditInstanceDetails from './components/config/edit-instance-details';
|
import EditInstanceDetails from '../components/config/edit-instance-details';
|
||||||
import EditDirectoryDetails from './components/config/edit-directory';
|
import EditDirectoryDetails from '../components/config/edit-directory';
|
||||||
import EditInstanceTags from './components/config/edit-tags';
|
import EditInstanceTags from '../components/config/edit-tags';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
@ -13,7 +13,8 @@ export default function PublicFacingDetails() {
|
|||||||
<>
|
<>
|
||||||
<Title level={2}>General Settings</Title>
|
<Title level={2}>General Settings</Title>
|
||||||
<p>
|
<p>
|
||||||
The following are displayed on your site to describe your stream and its content. <a href="https://owncast.online/docs/website/">Learn more.</a>
|
The following are displayed on your site to describe your stream and its content.{' '}
|
||||||
|
<a href="https://owncast.online/docs/website/">Learn more.</a>
|
||||||
</p>
|
</p>
|
||||||
<div className="edit-public-details-container">
|
<div className="edit-public-details-container">
|
||||||
<EditInstanceDetails />
|
<EditInstanceDetails />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import EditServerDetails from './components/config/edit-server-details';
|
import EditServerDetails from '../components/config/edit-server-details';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
@ -9,7 +9,8 @@ export default function ConfigServerDetails() {
|
|||||||
<div className="config-server-details-form">
|
<div className="config-server-details-form">
|
||||||
<Title level={2}>Server Settings</Title>
|
<Title level={2}>Server Settings</Title>
|
||||||
<p>
|
<p>
|
||||||
You should change your stream key from the default and keep it safe. For most people it's likely the other settings will not need to be changed.
|
You should change your stream key from the default and keep it safe. For most people it's
|
||||||
|
likely the other settings will not need to be changed.
|
||||||
</p>
|
</p>
|
||||||
<div className="config-server-details-container">
|
<div className="config-server-details-container">
|
||||||
<EditServerDetails />
|
<EditServerDetails />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import EditSocialLinks from './components/config/edit-social-links';
|
import EditSocialLinks from '../components/config/edit-social-links';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import EditStorage from './components/config/edit-storage';
|
import EditStorage from '../components/config/edit-storage';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
@ -9,10 +9,14 @@ export default function ConfigStorageInfo() {
|
|||||||
<>
|
<>
|
||||||
<Title level={2}>Storage</Title>
|
<Title level={2}>Storage</Title>
|
||||||
<p>
|
<p>
|
||||||
Owncast supports optionally using external storage providers to distribute your video. Learn more about this by visiting our <a href="https://owncast.online/docs/storage/">Storage Documentation</a>.
|
Owncast supports optionally using external storage providers to distribute your video. Learn
|
||||||
|
more about this by visiting our{' '}
|
||||||
|
<a href="https://owncast.online/docs/storage/">Storage Documentation</a>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Configuring this incorrectly will likely cause your video to be unplayable. Double check the documentation for your storage provider on how to configure the bucket you created for Owncast.
|
Configuring this incorrectly will likely cause your video to be unplayable. Double check the
|
||||||
|
documentation for your storage provider on how to configure the bucket you created for
|
||||||
|
Owncast.
|
||||||
</p>
|
</p>
|
||||||
<EditStorage />
|
<EditStorage />
|
||||||
</>
|
</>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Typography } from 'antd';
|
import { Typography } from 'antd';
|
||||||
|
|
||||||
import VideoVariantsTable from './components/config/video-variants-table';
|
import VideoVariantsTable from '../components/config/video-variants-table';
|
||||||
import VideoLatency from './components/config/video-latency';
|
import VideoLatency from '../components/config/video-latency';
|
||||||
|
|
||||||
const { Title } = Typography;
|
const { Title } = Typography;
|
||||||
|
|
||||||
@ -11,18 +11,20 @@ export default function ConfigVideoSettings() {
|
|||||||
<div className="config-video-variants">
|
<div className="config-video-variants">
|
||||||
<Title level={2}>Video configuration</Title>
|
<Title level={2}>Video configuration</Title>
|
||||||
<p>
|
<p>
|
||||||
Before changing your video configuration <a href="https://owncast.online/docs/encoding">visit the video documentation</a>{' '}
|
Before changing your video configuration{' '}
|
||||||
to learn how it impacts your stream performance.
|
<a href="https://owncast.online/docs/encoding">visit the video documentation</a> to learn
|
||||||
|
how it impacts your stream performance.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<VideoVariantsTable />
|
<VideoVariantsTable />
|
||||||
</p>
|
</p>
|
||||||
<br/><hr/><br/>
|
<br />
|
||||||
|
<hr />
|
||||||
|
<br />
|
||||||
<p>
|
<p>
|
||||||
<VideoLatency />
|
<VideoLatency />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import { BulbOutlined, LaptopOutlined, SaveOutlined } from "@ant-design/icons";
|
import { BulbOutlined, LaptopOutlined, SaveOutlined } from '@ant-design/icons';
|
||||||
import { Row } from "antd";
|
import { Row } from 'antd';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { fetchData, FETCH_INTERVAL, HARDWARE_STATS } from '../utils/apis';
|
import { fetchData, FETCH_INTERVAL, HARDWARE_STATS } from '../utils/apis';
|
||||||
import Chart from './components/chart';
|
import Chart from '../components/chart';
|
||||||
import StatisticItem from "./components/statistic";
|
import StatisticItem from '../components/statistic';
|
||||||
|
|
||||||
interface TimedValue {
|
interface TimedValue {
|
||||||
time: Date,
|
time: Date;
|
||||||
value: Number
|
value: Number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HardwareInfo() {
|
export default function HardwareInfo() {
|
||||||
@ -15,14 +15,13 @@ export default function HardwareInfo() {
|
|||||||
cpu: Array<TimedValue>(),
|
cpu: Array<TimedValue>(),
|
||||||
memory: Array<TimedValue>(),
|
memory: Array<TimedValue>(),
|
||||||
disk: Array<TimedValue>(),
|
disk: Array<TimedValue>(),
|
||||||
message: "",
|
message: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const getHardwareStatus = async () => {
|
const getHardwareStatus = async () => {
|
||||||
try {
|
try {
|
||||||
const result = await fetchData(HARDWARE_STATS);
|
const result = await fetchData(HARDWARE_STATS);
|
||||||
setHardwareStatus({ ...result });
|
setHardwareStatus({ ...result });
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setHardwareStatus({ ...hardwareStatus, message: error.message });
|
setHardwareStatus({ ...hardwareStatus, message: error.message });
|
||||||
}
|
}
|
||||||
@ -37,34 +36,31 @@ export default function HardwareInfo() {
|
|||||||
// returned function will be called on component unmount
|
// returned function will be called on component unmount
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(getStatusIntervalId);
|
clearInterval(getStatusIntervalId);
|
||||||
}
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
if (!hardwareStatus.cpu) {
|
if (!hardwareStatus.cpu) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentCPUUsage = hardwareStatus.cpu[hardwareStatus.cpu.length - 1]?.value;
|
const currentCPUUsage = hardwareStatus.cpu[hardwareStatus.cpu.length - 1]?.value;
|
||||||
const currentRamUsage =
|
const currentRamUsage = hardwareStatus.memory[hardwareStatus.memory.length - 1]?.value;
|
||||||
hardwareStatus.memory[hardwareStatus.memory.length - 1]?.value;
|
const currentDiskUsage = hardwareStatus.disk[hardwareStatus.disk.length - 1]?.value;
|
||||||
const currentDiskUsage =
|
|
||||||
hardwareStatus.disk[hardwareStatus.disk.length - 1]?.value;
|
|
||||||
|
|
||||||
const series = [
|
const series = [
|
||||||
{
|
{
|
||||||
name: "CPU",
|
name: 'CPU',
|
||||||
color: "#B63FFF",
|
color: '#B63FFF',
|
||||||
data: hardwareStatus.cpu,
|
data: hardwareStatus.cpu,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Memory",
|
name: 'Memory',
|
||||||
color: "#2087E2",
|
color: '#2087E2',
|
||||||
data: hardwareStatus.memory,
|
data: hardwareStatus.memory,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Disk",
|
name: 'Disk',
|
||||||
color: "#FF7700",
|
color: '#FF7700',
|
||||||
data: hardwareStatus.disk,
|
data: hardwareStatus.disk,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -7,29 +7,29 @@ Will display an overview with the following datasources:
|
|||||||
TODO: Link each overview value to the sub-page that focuses on it.
|
TODO: Link each overview value to the sub-page that focuses on it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect, useContext } from "react";
|
import React, { useState, useEffect, useContext } from 'react';
|
||||||
import { Skeleton, Card, Statistic } from "antd";
|
import { Skeleton, Card, Statistic } from 'antd';
|
||||||
import { UserOutlined, ClockCircleOutlined } from "@ant-design/icons";
|
import { UserOutlined, ClockCircleOutlined } from '@ant-design/icons';
|
||||||
import { formatDistanceToNow, formatRelative } from "date-fns";
|
import { formatDistanceToNow, formatRelative } from 'date-fns';
|
||||||
import { ServerStatusContext } from "../utils/server-status-context";
|
import { ServerStatusContext } from '../utils/server-status-context';
|
||||||
import StatisticItem from "./components/statistic"
|
import StatisticItem from '../components/statistic';
|
||||||
import LogTable from "./components/log-table";
|
import LogTable from '../components/log-table';
|
||||||
import Offline from './offline-notice';
|
import Offline from './offline-notice';
|
||||||
|
|
||||||
import {
|
import { LOGS_WARN, fetchData, FETCH_INTERVAL } from '../utils/apis';
|
||||||
LOGS_WARN,
|
import { formatIPAddress, isEmptyObject } from '../utils/format';
|
||||||
fetchData,
|
import { UpdateArgs } from '../types/config-section';
|
||||||
FETCH_INTERVAL,
|
|
||||||
} from "../utils/apis";
|
|
||||||
import { formatIPAddress, isEmptyObject } from "../utils/format";
|
|
||||||
import { UpdateArgs } from "../types/config-section";
|
|
||||||
|
|
||||||
function streamDetailsFormatter(streamDetails) {
|
function streamDetailsFormatter(streamDetails) {
|
||||||
return (
|
return (
|
||||||
<ul className="statistics-list">
|
<ul className="statistics-list">
|
||||||
<li>{streamDetails.videoCodec || 'Unknown'} @ {streamDetails.videoBitrate || 'Unknown'} kbps</li>
|
<li>
|
||||||
|
{streamDetails.videoCodec || 'Unknown'} @ {streamDetails.videoBitrate || 'Unknown'} kbps
|
||||||
|
</li>
|
||||||
<li>{streamDetails.framerate || 'Unknown'} fps</li>
|
<li>{streamDetails.framerate || 'Unknown'} fps</li>
|
||||||
<li>{streamDetails.width} x {streamDetails.height}</li>
|
<li>
|
||||||
|
{streamDetails.width} x {streamDetails.height}
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ export default function Home() {
|
|||||||
const { broadcaster, serverConfig: configData } = serverStatusData || {};
|
const { broadcaster, serverConfig: configData } = serverStatusData || {};
|
||||||
const { remoteAddr, streamDetails } = broadcaster || {};
|
const { remoteAddr, streamDetails } = broadcaster || {};
|
||||||
|
|
||||||
const encoder = streamDetails?.encoder || "Unknown encoder";
|
const encoder = streamDetails?.encoder || 'Unknown encoder';
|
||||||
|
|
||||||
const [logsData, setLogs] = useState([]);
|
const [logsData, setLogs] = useState([]);
|
||||||
const getLogs = async () => {
|
const getLogs = async () => {
|
||||||
@ -47,12 +47,12 @@ export default function Home() {
|
|||||||
const result = await fetchData(LOGS_WARN);
|
const result = await fetchData(LOGS_WARN);
|
||||||
setLogs(result);
|
setLogs(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("==== error", error);
|
console.log('==== error', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const getMoreStats = () => {
|
const getMoreStats = () => {
|
||||||
getLogs();
|
getLogs();
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getMoreStats();
|
getMoreStats();
|
||||||
@ -62,7 +62,7 @@ export default function Home() {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
}
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (isEmptyObject(configData) || isEmptyObject(serverStatusData)) {
|
if (isEmptyObject(configData) || isEmptyObject(serverStatusData)) {
|
||||||
@ -80,7 +80,8 @@ export default function Home() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// map out settings
|
// map out settings
|
||||||
const videoQualitySettings = serverStatusData?.currentBroadcast?.outputSettings?.map((setting, index) => {
|
const videoQualitySettings = serverStatusData?.currentBroadcast?.outputSettings?.map(
|
||||||
|
(setting, index) => {
|
||||||
const { audioPassthrough, videoPassthrough, audioBitrate, videoBitrate, framerate } = setting;
|
const { audioPassthrough, videoPassthrough, audioBitrate, videoBitrate, framerate } = setting;
|
||||||
|
|
||||||
const audioSetting = audioPassthrough
|
const audioSetting = audioPassthrough
|
||||||
@ -88,32 +89,29 @@ export default function Home() {
|
|||||||
: `${audioBitrate || 'Unknown'} kbps`;
|
: `${audioBitrate || 'Unknown'} kbps`;
|
||||||
|
|
||||||
const videoSetting = videoPassthrough
|
const videoSetting = videoPassthrough
|
||||||
? `${streamDetails.videoBitrate || 'Unknown'} kbps, ${streamDetails.framerate} fps ${streamDetails.width} x ${streamDetails.height}`
|
? `${streamDetails.videoBitrate || 'Unknown'} kbps, ${streamDetails.framerate} fps ${
|
||||||
|
streamDetails.width
|
||||||
|
} x ${streamDetails.height}`
|
||||||
: `${videoBitrate || 'Unknown'} kbps, ${framerate} fps`;
|
: `${videoBitrate || 'Unknown'} kbps, ${framerate} fps`;
|
||||||
|
|
||||||
let settingTitle = 'Outbound Stream Details';
|
let settingTitle = 'Outbound Stream Details';
|
||||||
settingTitle = (videoQualitySettings?.length > 1) ?
|
settingTitle =
|
||||||
`${settingTitle} ${index + 1}` : settingTitle;
|
videoQualitySettings?.length > 1 ? `${settingTitle} ${index + 1}` : settingTitle;
|
||||||
return (
|
return (
|
||||||
<Card title={settingTitle} type="inner" key={`${settingTitle}${index}`}>
|
<Card title={settingTitle} type="inner" key={`${settingTitle}${index}`}>
|
||||||
<StatisticItem
|
<StatisticItem title="Outbound Video Stream" value={videoSetting} prefix={null} />
|
||||||
title="Outbound Video Stream"
|
<StatisticItem title="Outbound Audio Stream" value={audioSetting} prefix={null} />
|
||||||
value={videoSetting}
|
|
||||||
prefix={null}
|
|
||||||
/>
|
|
||||||
<StatisticItem
|
|
||||||
title="Outbound Audio Stream"
|
|
||||||
value={audioSetting}
|
|
||||||
prefix={null}
|
|
||||||
/>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// inbound
|
// inbound
|
||||||
const { viewerCount, sessionPeakViewerCount } = serverStatusData;
|
const { viewerCount, sessionPeakViewerCount } = serverStatusData;
|
||||||
|
|
||||||
const streamAudioDetailString = `${streamDetails.audioCodec}, ${streamDetails.audioBitrate || 'Unknown'} kbps`;
|
const streamAudioDetailString = `${streamDetails.audioCodec}, ${
|
||||||
|
streamDetails.audioBitrate || 'Unknown'
|
||||||
|
} kbps`;
|
||||||
|
|
||||||
const broadcastDate = new Date(broadcaster.time);
|
const broadcastDate = new Date(broadcaster.time);
|
||||||
|
|
||||||
@ -123,18 +121,11 @@ export default function Home() {
|
|||||||
<div className="section online-status-section">
|
<div className="section online-status-section">
|
||||||
<Card title="Stream is online" type="inner">
|
<Card title="Stream is online" type="inner">
|
||||||
<Statistic
|
<Statistic
|
||||||
title={`Stream started ${formatRelative(
|
title={`Stream started ${formatRelative(broadcastDate, Date.now())}`}
|
||||||
broadcastDate,
|
|
||||||
Date.now()
|
|
||||||
)}`}
|
|
||||||
value={formatDistanceToNow(broadcastDate)}
|
value={formatDistanceToNow(broadcastDate)}
|
||||||
prefix={<ClockCircleOutlined />}
|
prefix={<ClockCircleOutlined />}
|
||||||
/>
|
/>
|
||||||
<Statistic
|
<Statistic title="Viewers" value={viewerCount} prefix={<UserOutlined />} />
|
||||||
title="Viewers"
|
|
||||||
value={viewerCount}
|
|
||||||
prefix={<UserOutlined />}
|
|
||||||
/>
|
|
||||||
<Statistic
|
<Statistic
|
||||||
title="Peak viewer count"
|
title="Peak viewer count"
|
||||||
value={sessionPeakViewerCount}
|
value={sessionPeakViewerCount}
|
||||||
@ -144,10 +135,7 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="section stream-details-section">
|
<div className="section stream-details-section">
|
||||||
|
<div className="details outbound-details">{videoQualitySettings}</div>
|
||||||
<div className="details outbound-details">
|
|
||||||
{videoQualitySettings}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="details other-details">
|
<div className="details other-details">
|
||||||
<Card title="Inbound Stream Details" type="inner">
|
<Card title="Inbound Stream Details" type="inner">
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from 'react';
|
||||||
import LogTable from "./components/log-table";
|
import LogTable from '../components/log-table';
|
||||||
|
|
||||||
import {
|
import { LOGS_ALL, fetchData } from '../utils/apis';
|
||||||
LOGS_ALL,
|
|
||||||
fetchData,
|
|
||||||
} from "../utils/apis";
|
|
||||||
|
|
||||||
const FETCH_INTERVAL = 5 * 1000; // 5 sec
|
const FETCH_INTERVAL = 5 * 1000; // 5 sec
|
||||||
|
|
||||||
@ -16,7 +13,7 @@ export default function Logs() {
|
|||||||
const result = await fetchData(LOGS_ALL);
|
const result = await fetchData(LOGS_ALL);
|
||||||
setLogs(result);
|
setLogs(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("==== error", error);
|
console.log('==== error', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -35,4 +32,3 @@ export default function Logs() {
|
|||||||
|
|
||||||
return <LogTable logs={logs} pageSize={20} />;
|
return <LogTable logs={logs} pageSize={20} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ import {
|
|||||||
BookTwoTone,
|
BookTwoTone,
|
||||||
PlaySquareTwoTone,
|
PlaySquareTwoTone,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import OwncastLogo from './components/logo';
|
import OwncastLogo from '../components/logo';
|
||||||
import LogTable from './components/log-table';
|
import LogTable from '../components/log-table';
|
||||||
|
|
||||||
const { Meta } = Card;
|
const { Meta } = Card;
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ import { Table, Row } from 'antd';
|
|||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import { UserOutlined } from '@ant-design/icons';
|
import { UserOutlined } from '@ant-design/icons';
|
||||||
import { SortOrder } from 'antd/lib/table/interface';
|
import { SortOrder } from 'antd/lib/table/interface';
|
||||||
import Chart from './components/chart';
|
import Chart from '../components/chart';
|
||||||
import StatisticItem from './components/statistic';
|
import StatisticItem from '../components/statistic';
|
||||||
|
|
||||||
import { ServerStatusContext } from '../utils/server-status-context';
|
import { ServerStatusContext } from '../utils/server-status-context';
|
||||||
|
|
||||||
|
@ -3,9 +3,8 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
export const AlertMessageContext = React.createContext({
|
export const AlertMessageContext = React.createContext({
|
||||||
message: null,
|
message: null,
|
||||||
setMessage: (text?: string) => {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
return text;
|
setMessage: (text?: string) => null,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const AlertMessageProvider = ({ children }) => {
|
const AlertMessageProvider = ({ children }) => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// DEFAULT VALUES
|
// DEFAULT VALUES
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CheckCircleFilled, ExclamationCircleFilled } from '@ant-design/icons';
|
import { CheckCircleFilled, ExclamationCircleFilled } from '@ant-design/icons';
|
||||||
import { fetchData, SERVER_CONFIG_UPDATE_URL } from '../../../utils/apis';
|
import { fetchData, SERVER_CONFIG_UPDATE_URL } from './apis';
|
||||||
import { ApiPostArgs, VideoVariant, SocialHandle } from '../../../types/config-section';
|
import { ApiPostArgs, VideoVariant, SocialHandle } from '../types/config-section';
|
||||||
|
|
||||||
export const TEXT_MAXLENGTH = 255;
|
export const TEXT_MAXLENGTH = 255;
|
||||||
|
|
@ -15,13 +15,16 @@ export const STATUS_WARNING = 'warning';
|
|||||||
|
|
||||||
export type InputStatusTypes = 'error' | 'invalid' | 'proessing' | 'success' | 'warning';
|
export type InputStatusTypes = 'error' | 'invalid' | 'proessing' | 'success' | 'warning';
|
||||||
|
|
||||||
export type StatusState = {
|
export interface StatusState {
|
||||||
type: InputStatusTypes;
|
type: InputStatusTypes;
|
||||||
icon: any; // Element type of sorts?
|
icon: any; // Element type of sorts?
|
||||||
message: string;
|
message: string;
|
||||||
};
|
}
|
||||||
|
interface InputStates {
|
||||||
|
[key: string]: StatusState;
|
||||||
|
}
|
||||||
|
|
||||||
export const INPUT_STATES = {
|
export const INPUT_STATES: InputStates = {
|
||||||
[STATUS_SUCCESS]: {
|
[STATUS_SUCCESS]: {
|
||||||
type: STATUS_SUCCESS,
|
type: STATUS_SUCCESS,
|
||||||
icon: <CheckCircleFilled style={{ color: 'green' }} />,
|
icon: <CheckCircleFilled style={{ color: 'green' }} />,
|
||||||
|
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import { STATUS, fetchData, FETCH_INTERVAL, SERVER_CONFIG } from './apis';
|
import { STATUS, fetchData, FETCH_INTERVAL, SERVER_CONFIG } from './apis';
|
||||||
import { ConfigDetails, UpdateArgs } from '../types/config-section';
|
import { ConfigDetails, UpdateArgs } from '../types/config-section';
|
||||||
import { DEFAULT_VARIANT_STATE } from '../pages/components/config/constants';
|
import { DEFAULT_VARIANT_STATE } from './config-constants';
|
||||||
|
|
||||||
export const initialServerConfigState: ConfigDetails = {
|
export const initialServerConfigState: ConfigDetails = {
|
||||||
streamKey: '',
|
streamKey: '',
|
||||||
@ -47,6 +47,7 @@ export const initialServerConfigState: ConfigDetails = {
|
|||||||
const initialServerStatusState = {
|
const initialServerStatusState = {
|
||||||
broadcastActive: false,
|
broadcastActive: false,
|
||||||
broadcaster: null,
|
broadcaster: null,
|
||||||
|
currentBroadcast: null,
|
||||||
online: false,
|
online: false,
|
||||||
viewerCount: 0,
|
viewerCount: 0,
|
||||||
sessionMaxViewerCount: 0,
|
sessionMaxViewerCount: 0,
|
||||||
@ -60,9 +61,8 @@ export const ServerStatusContext = React.createContext({
|
|||||||
...initialServerStatusState,
|
...initialServerStatusState,
|
||||||
serverConfig: initialServerConfigState,
|
serverConfig: initialServerConfigState,
|
||||||
|
|
||||||
setFieldInConfigState: (args: UpdateArgs) => {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
return args;
|
setFieldInConfigState: (args: UpdateArgs) => null,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const ServerStatusProvider = ({ children }) => {
|
const ServerStatusProvider = ({ children }) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user