mirror of
https://github.com/owncast/owncast.git
synced 2024-10-28 10:09:39 +01:00
feat: add translations support to admin pages and components (#3977)
* feat: add translations support to admin pages and components Added translations support admin main page and its components, help page, handware-info page. Added translations support for LogTable, NewsFeed and StreamHealthOverview components. * update package.json * fix rendering issue * Commit updated API documentation --------- Co-authored-by: Owncast <owncast@owncast.online> Co-authored-by: Gabe Kangas <gabek@real-ity.com>
This commit is contained in:
parent
34c3cfcd9a
commit
d03d6e79aa
7 changed files with 86 additions and 69 deletions
|
@ -3,6 +3,7 @@ import { Table, Tag, Typography } from 'antd';
|
|||
import Linkify from 'react-linkify';
|
||||
import { SortOrder, TablePaginationConfig } from 'antd/lib/table/interface';
|
||||
import { format } from 'date-fns';
|
||||
import { useTranslation } from 'next-export-i18n';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
|
@ -28,39 +29,41 @@ export type LogTableProps = {
|
|||
};
|
||||
|
||||
export const LogTable: FC<LogTableProps> = ({ logs, initialPageSize }) => {
|
||||
if (!logs?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { t } = useTranslation();
|
||||
const [pageSize, setPageSize] = useState(initialPageSize);
|
||||
|
||||
const handleTableChange = (pagination: TablePaginationConfig) => {
|
||||
setPageSize(pagination.pageSize);
|
||||
};
|
||||
|
||||
if (!logs?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'Level',
|
||||
title: t('Level'),
|
||||
dataIndex: 'level',
|
||||
key: 'level',
|
||||
filters: [
|
||||
{
|
||||
text: 'Info',
|
||||
text: t('Info'),
|
||||
value: 'info',
|
||||
},
|
||||
{
|
||||
text: 'Warning',
|
||||
text: t('Warning'),
|
||||
value: 'warning',
|
||||
},
|
||||
{
|
||||
text: 'Error',
|
||||
value: 'error',
|
||||
text: t('Error'),
|
||||
value: 'Error',
|
||||
},
|
||||
],
|
||||
onFilter: (level, row) => row.level.indexOf(level) === 0,
|
||||
render: renderColumnLevel,
|
||||
},
|
||||
{
|
||||
title: 'Timestamp',
|
||||
title: t('Timestamp'),
|
||||
dataIndex: 'time',
|
||||
key: 'time',
|
||||
render: timestamp => {
|
||||
|
@ -72,7 +75,7 @@ export const LogTable: FC<LogTableProps> = ({ logs, initialPageSize }) => {
|
|||
defaultSortOrder: 'descend' as SortOrder,
|
||||
},
|
||||
{
|
||||
title: 'Message',
|
||||
title: t('Message'),
|
||||
dataIndex: 'message',
|
||||
key: 'message',
|
||||
render: renderMessage,
|
||||
|
@ -81,7 +84,7 @@ export const LogTable: FC<LogTableProps> = ({ logs, initialPageSize }) => {
|
|||
|
||||
return (
|
||||
<div className="logs-section">
|
||||
<Title>Logs</Title>
|
||||
<Title>{t('Logs')}</Title>
|
||||
<Table
|
||||
size="middle"
|
||||
dataSource={logs}
|
||||
|
|
|
@ -4,6 +4,7 @@ import React, { useState, useEffect, FC } from 'react';
|
|||
import { Collapse, Typography, Skeleton } from 'antd';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
import { useTranslation } from 'next-export-i18n';
|
||||
import { fetchExternalData } from '../../utils/apis';
|
||||
|
||||
const { Panel } = Collapse;
|
||||
|
@ -27,6 +28,7 @@ const ArticleItem: FC<ArticleProps> = ({
|
|||
date_published: date,
|
||||
defaultOpen = false,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const dateObject = new Date(date);
|
||||
const dateString = format(dateObject, 'MMM dd, yyyy, HH:mm');
|
||||
return (
|
||||
|
@ -36,7 +38,7 @@ const ArticleItem: FC<ArticleProps> = ({
|
|||
<p className="timestamp">
|
||||
{dateString} (
|
||||
<Link href={`${OWNCAST_BASE_URL}${url}`} target="_blank" rel="noopener noreferrer">
|
||||
Link
|
||||
{t('Link')}
|
||||
</Link>
|
||||
)
|
||||
</p>
|
||||
|
@ -48,6 +50,7 @@ const ArticleItem: FC<ArticleProps> = ({
|
|||
};
|
||||
|
||||
export const NewsFeed = () => {
|
||||
const { t } = useTranslation();
|
||||
const [feed, setFeed] = useState<ArticleProps[]>([]);
|
||||
const [loading, setLoading] = useState<Boolean>(true);
|
||||
|
||||
|
@ -69,11 +72,11 @@ export const NewsFeed = () => {
|
|||
}, []);
|
||||
|
||||
const loadingSpinner = loading ? <Skeleton loading active /> : null;
|
||||
const noNews = !loading && feed.length === 0 ? <div>No news.</div> : null;
|
||||
const noNews = !loading && feed.length === 0 ? <div>{t('No news.')}</div> : null;
|
||||
|
||||
return (
|
||||
<section className="news-feed form-module">
|
||||
<Title level={2}>News & Updates from Owncast</Title>
|
||||
<Title level={2}>{t('News & Updates from Owncast')}</Title>
|
||||
{loadingSpinner}
|
||||
{feed.map(item => (
|
||||
<ArticleItem {...item} key={item.url} defaultOpen={feed.length === 1} />
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Alert, Button, Card, Col, Row, Statistic, Typography } from 'antd';
|
|||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import React, { FC, useContext } from 'react';
|
||||
import { useTranslation } from 'next-export-i18n';
|
||||
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||
|
||||
// Lazy loaded components
|
||||
|
@ -22,6 +23,7 @@ export type StreamHealthOverviewProps = {
|
|||
};
|
||||
|
||||
export const StreamHealthOverview: FC<StreamHealthOverviewProps> = ({ showTroubleshootButton }) => {
|
||||
const { t } = useTranslation();
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { health } = serverStatusData;
|
||||
if (!health) {
|
||||
|
@ -45,15 +47,15 @@ export const StreamHealthOverview: FC<StreamHealthOverviewProps> = ({ showTroubl
|
|||
<Row gutter={8}>
|
||||
<Col span={12}>
|
||||
<Statistic
|
||||
title="Healthy Stream"
|
||||
value={healthy ? 'Yes' : 'No'}
|
||||
title={t('Healthy Stream')}
|
||||
value={healthy ? t('Yes') : t('No')}
|
||||
valueStyle={{ color }}
|
||||
prefix={healthy ? <CheckCircleOutlined /> : <ExclamationCircleOutlined />}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Statistic
|
||||
title="Playback Health"
|
||||
title={t('Playback Health')}
|
||||
value={healthPercentage}
|
||||
valueStyle={{ color }}
|
||||
suffix="%"
|
||||
|
@ -65,8 +67,7 @@ export const StreamHealthOverview: FC<StreamHealthOverviewProps> = ({ showTroubl
|
|||
type="secondary"
|
||||
style={{ textAlign: 'center', fontSize: '0.7em', opacity: '0.3' }}
|
||||
>
|
||||
Stream health represents {representation}% of all known players. Other player status is
|
||||
unknown.
|
||||
{`${t('Stream health represents')} ${representation}% ${t('of all known players. Other player status is unknown.')}`}
|
||||
</Typography.Text>
|
||||
</Row>
|
||||
<Row
|
||||
|
@ -82,7 +83,7 @@ export const StreamHealthOverview: FC<StreamHealthOverviewProps> = ({ showTroubl
|
|||
showTroubleshootButton && (
|
||||
<Link passHref href="/admin/stream-health">
|
||||
<Button size="small" type="text" style={{ color: 'black' }}>
|
||||
TROUBLESHOOT
|
||||
{t('TROUBLESHOOT')}
|
||||
</Button>
|
||||
</Link>
|
||||
)
|
||||
|
|
|
@ -40,10 +40,12 @@
|
|||
"classnames": "2.5.1",
|
||||
"date-fns": "^3.0.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"i18next-parser": "^8.9.0",
|
||||
"interweave": "^13.0.0",
|
||||
"interweave-autolink": "^5.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"next": "14.2.15",
|
||||
"next-export-i18n": "^2.1.0",
|
||||
"next-pwa": "^5.6.0",
|
||||
"next-with-less": "3.0.1",
|
||||
"picmo": "5.8.5",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Row, Col, Typography, Alert, Spin } from 'antd';
|
||||
import React, { ReactElement, useEffect, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useTranslation } from 'next-export-i18n';
|
||||
import { fetchData, FETCH_INTERVAL, HARDWARE_STATS } from '../../utils/apis';
|
||||
import { Chart } from '../../components/admin/Chart';
|
||||
import { StatisticItem } from '../../components/admin/StatisticItem';
|
||||
|
@ -22,6 +23,7 @@ const SaveOutlined = dynamic(() => import('@ant-design/icons/SaveOutlined'), {
|
|||
});
|
||||
|
||||
export default function HardwareInfo() {
|
||||
const { t } = useTranslation();
|
||||
const [hardwareStatus, setHardwareStatus] = useState({
|
||||
cpu: [], // Array<TimedValue>(),
|
||||
memory: [], // Array<TimedValue>(),
|
||||
|
@ -53,13 +55,13 @@ export default function HardwareInfo() {
|
|||
if (!hardwareStatus.cpu) {
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title>Hardware Info</Typography.Title>
|
||||
<Typography.Title>{t('Hardware Info')}</Typography.Title>
|
||||
|
||||
<Alert
|
||||
style={{ marginTop: '10px' }}
|
||||
banner
|
||||
message="Please wait"
|
||||
description="No hardware details have been collected yet."
|
||||
message={t('Please wait')}
|
||||
description={t('No hardware details have been collected yet.')}
|
||||
type="info"
|
||||
/>
|
||||
<Spin spinning style={{ width: '100%', margin: '10px' }} />
|
||||
|
@ -73,19 +75,19 @@ export default function HardwareInfo() {
|
|||
|
||||
const series = [
|
||||
{
|
||||
name: 'CPU',
|
||||
name: t('CPU'),
|
||||
color: '#B63FFF',
|
||||
data: hardwareStatus.cpu,
|
||||
pointStyle: 'rect',
|
||||
},
|
||||
{
|
||||
name: 'Memory',
|
||||
name: t('Memory'),
|
||||
color: '#2087E2',
|
||||
data: hardwareStatus.memory,
|
||||
pointStyle: 'circle',
|
||||
},
|
||||
{
|
||||
name: 'Disk',
|
||||
name: t('Disk'),
|
||||
color: '#FF7700',
|
||||
data: hardwareStatus.disk,
|
||||
pointStyle: 'rectRounded',
|
||||
|
@ -94,7 +96,7 @@ export default function HardwareInfo() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Typography.Title>Hardware Info</Typography.Title>
|
||||
<Typography.Title>{t('Hardware Info')}</Typography.Title>
|
||||
<br />
|
||||
<div>
|
||||
<Row gutter={[16, 16]} justify="space-around">
|
||||
|
@ -130,7 +132,7 @@ export default function HardwareInfo() {
|
|||
</Col>
|
||||
</Row>
|
||||
|
||||
<Chart title="% used" dataCollections={series} color="#FF7700" unit="%" />
|
||||
<Chart title={`% ${t('used')}`} dataCollections={series} color="#FF7700" unit="%" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -5,6 +5,7 @@ import Title from 'antd/lib/typography/Title';
|
|||
import React, { ReactElement } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import { useTranslation } from 'next-export-i18n';
|
||||
import { AdminLayout } from '../../components/layouts/AdminLayout';
|
||||
|
||||
// Lazy loaded components
|
||||
|
@ -50,10 +51,12 @@ const SlidersTwoTone = dynamic(() => import('@ant-design/icons/SlidersTwoTone'),
|
|||
});
|
||||
|
||||
export default function Help() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const questions = [
|
||||
{
|
||||
icon: <SettingTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I want to configure my owncast instance',
|
||||
title: t('I want to configure my owncast instance'),
|
||||
content: (
|
||||
<div>
|
||||
<a
|
||||
|
@ -61,14 +64,14 @@ export default function Help() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<LinkOutlined /> Learn more
|
||||
<LinkOutlined /> {t('Learn more')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: <CameraTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'Help configuring my broadcasting software',
|
||||
title: t('Help configuring my broadcasting software'),
|
||||
content: (
|
||||
<div>
|
||||
<a
|
||||
|
@ -76,14 +79,14 @@ export default function Help() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<LinkOutlined /> Learn more
|
||||
<LinkOutlined /> {t('Learn more')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: <Html5TwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I want to embed my stream into another site',
|
||||
title: t('I want to embed my stream into another site'),
|
||||
content: (
|
||||
<div>
|
||||
<a
|
||||
|
@ -91,14 +94,14 @@ export default function Help() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<LinkOutlined /> Learn more
|
||||
<LinkOutlined /> {t('Learn more')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: <EditTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I want to customize my website',
|
||||
title: t('I want to customize my website'),
|
||||
content: (
|
||||
<div>
|
||||
<a
|
||||
|
@ -106,14 +109,14 @@ export default function Help() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<LinkOutlined /> Learn more
|
||||
<LinkOutlined /> {t('Learn more')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: <SlidersTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I want to tweak my video output',
|
||||
title: t('I want to tweak my video output'),
|
||||
content: (
|
||||
<div>
|
||||
<a
|
||||
|
@ -121,14 +124,14 @@ export default function Help() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<LinkOutlined /> Learn more
|
||||
<LinkOutlined /> {t('Learn more')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: <DatabaseTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I want to use an external storage provider',
|
||||
title: t('I want to use an external storage provider'),
|
||||
content: (
|
||||
<div>
|
||||
<a
|
||||
|
@ -136,7 +139,7 @@ export default function Help() {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<LinkOutlined /> Learn more
|
||||
<LinkOutlined /> {t('Learn more')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
|
@ -146,58 +149,58 @@ export default function Help() {
|
|||
const otherResources = [
|
||||
{
|
||||
icon: <BugTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I found a bug',
|
||||
title: t('I found a bug'),
|
||||
content: (
|
||||
<div>
|
||||
If you found a bug, then please
|
||||
{t('If you found a bug, then please')}
|
||||
<a
|
||||
href="https://github.com/owncast/owncast/issues/new/choose"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{' '}
|
||||
let us know
|
||||
{t('let us know')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: <QuestionCircleTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I have a general question',
|
||||
title: t('I have a general question'),
|
||||
content: (
|
||||
<div>
|
||||
Most general questions are answered in our
|
||||
{t('Most general questions are answered in our')}
|
||||
<a
|
||||
href="https://owncast.online/faq/?source=admin"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{' '}
|
||||
FAQ
|
||||
{t('FAQ')}
|
||||
</a>{' '}
|
||||
or exist in our{' '}
|
||||
{t('or exist in our')}{' '}
|
||||
<a
|
||||
href="https://github.com/owncast/owncast/discussions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
discussions
|
||||
{t('discussions')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: <ApiTwoTone style={{ fontSize: '24px' }} />,
|
||||
title: 'I want to build add-ons for Owncast',
|
||||
title: t('I want to build add-ons for Owncast'),
|
||||
content: (
|
||||
<div>
|
||||
You can build your own bots, overlays, tools and add-ons with our
|
||||
{t('You can build your own bots, overlays, tools and add-ons with our')}
|
||||
<a
|
||||
href="https://owncast.online/thirdparty?source=admin"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
developer APIs.
|
||||
{t('developer APIs.')}
|
||||
</a>
|
||||
</div>
|
||||
),
|
||||
|
@ -206,11 +209,11 @@ export default function Help() {
|
|||
|
||||
return (
|
||||
<div className="help-page">
|
||||
<Title style={{ textAlign: 'center' }}>How can we help you?</Title>
|
||||
<Title style={{ textAlign: 'center' }}>{t('How can we help you?')}</Title>
|
||||
<Row gutter={[16, 16]} justify="space-around" align="middle">
|
||||
<Col xs={24} lg={12} style={{ textAlign: 'center' }}>
|
||||
<Result status="500" />
|
||||
<Title level={2}>Troubleshooting</Title>
|
||||
<Title level={2}>{t('Troubleshooting')}</Title>
|
||||
<Button
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
|
@ -218,12 +221,12 @@ export default function Help() {
|
|||
icon={<LinkOutlined />}
|
||||
type="primary"
|
||||
>
|
||||
Fix your problems
|
||||
{t('Fix your problems')}
|
||||
</Button>
|
||||
</Col>
|
||||
<Col xs={24} lg={12} style={{ textAlign: 'center' }}>
|
||||
<Result status="404" />
|
||||
<Title level={2}>Documentation</Title>
|
||||
<Title level={2}>{t('Documentation')}</Title>
|
||||
<Button
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
|
@ -231,12 +234,12 @@ export default function Help() {
|
|||
icon={<LinkOutlined />}
|
||||
type="primary"
|
||||
>
|
||||
Read the Docs
|
||||
{t('Read the Docs')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider />
|
||||
<Title level={2}>Common tasks</Title>
|
||||
<Title level={2}>{t('Common tasks')}</Title>
|
||||
<Row gutter={[16, 16]}>
|
||||
{questions.map(question => (
|
||||
<Col xs={24} lg={12} key={question.title}>
|
||||
|
@ -247,7 +250,7 @@ export default function Help() {
|
|||
))}
|
||||
</Row>
|
||||
<Divider />
|
||||
<Title level={2}>Other</Title>
|
||||
<Title level={2}>{t('Other')}</Title>
|
||||
<Row gutter={[16, 16]}>
|
||||
{otherResources.map(question => (
|
||||
<Col xs={24} lg={12} key={question.title}>
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, { useState, useEffect, useContext, ReactElement } from 'react';
|
|||
import { Skeleton, Card, Statistic, Row, Col } from 'antd';
|
||||
import { formatDistanceToNow, formatRelative } from 'date-fns';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useTranslation } from 'next-export-i18n';
|
||||
import { ServerStatusContext } from '../../utils/server-status-context';
|
||||
import { LogTable } from '../../components/admin/LogTable';
|
||||
import { Offline } from '../../components/admin/Offline';
|
||||
|
@ -39,6 +40,8 @@ function streamDetailsFormatter(streamDetails) {
|
|||
}
|
||||
|
||||
export default function Home() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const serverStatusData = useContext(ServerStatusContext);
|
||||
const { broadcaster, serverConfig: configData } = serverStatusData || {};
|
||||
const { remoteAddr, streamDetails } = broadcaster || {};
|
||||
|
@ -101,12 +104,12 @@ export default function Home() {
|
|||
<div className="stream-details-item-container">
|
||||
<Statistic
|
||||
className="stream-details-item"
|
||||
title="Outbound Video Stream"
|
||||
title={t('Outbound Video Stream')}
|
||||
value={videoSetting}
|
||||
/>
|
||||
<Statistic
|
||||
className="stream-details-item"
|
||||
title="Outbound Audio Stream"
|
||||
title={t('Outbound Audio Stream')}
|
||||
value={audioSetting}
|
||||
/>
|
||||
</div>
|
||||
|
@ -130,17 +133,17 @@ export default function Home() {
|
|||
<Row gutter={[16, 16]} align="middle">
|
||||
<Col span={8} sm={24} md={8}>
|
||||
<Statistic
|
||||
title={`Stream started ${formatRelative(broadcastDate, Date.now())}`}
|
||||
title={`${t('Stream started')} ${formatRelative(broadcastDate, Date.now())}`}
|
||||
value={formatDistanceToNow(broadcastDate)}
|
||||
prefix={<ClockCircleOutlined />}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={8} sm={24} md={8}>
|
||||
<Statistic title="Viewers" value={viewerCount} prefix={<UserOutlined />} />
|
||||
<Statistic title={t('Viewers')} value={viewerCount} prefix={<UserOutlined />} />
|
||||
</Col>
|
||||
<Col span={8} sm={24} md={8}>
|
||||
<Statistic
|
||||
title="Peak viewer count"
|
||||
title={t('Peak viewer count')}
|
||||
value={sessionPeakViewerCount}
|
||||
prefix={<UserOutlined />}
|
||||
/>
|
||||
|
@ -154,28 +157,28 @@ export default function Home() {
|
|||
<Col className="stream-details" span={12} sm={24} md={24} lg={12}>
|
||||
<Card
|
||||
size="small"
|
||||
title="Outbound Stream Details"
|
||||
title={t('Outbound Stream Details')}
|
||||
type="inner"
|
||||
className="outbound-details"
|
||||
>
|
||||
{videoQualitySettings}
|
||||
</Card>
|
||||
|
||||
<Card size="small" title="Inbound Stream Details" type="inner">
|
||||
<Card size="small" title={t('Inbound Stream Details')} type="inner">
|
||||
<Statistic
|
||||
className="stream-details-item"
|
||||
title="Input"
|
||||
title={t('Input')}
|
||||
value={`${encoder} ${formatIPAddress(remoteAddr)}`}
|
||||
/>
|
||||
<Statistic
|
||||
className="stream-details-item"
|
||||
title="Inbound Video Stream"
|
||||
title={t('Inbound Video Stream')}
|
||||
value={streamDetails}
|
||||
formatter={streamDetailsFormatter}
|
||||
/>
|
||||
<Statistic
|
||||
className="stream-details-item"
|
||||
title="Inbound Audio Stream"
|
||||
title={t('Inbound Audio Stream')}
|
||||
value={streamAudioDetailString}
|
||||
/>
|
||||
</Card>
|
||||
|
|
Loading…
Reference in a new issue