owncast/web/components/admin/VideoVariantForm.tsx

345 lines
13 KiB
TypeScript
Raw Normal View History

// This content populates the video variant modal, which is spawned from the variants table. This relies on the `dataState` prop fed in by the table.
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
import React, { FC } from 'react';
import { Popconfirm, Row, Col, Slider, Collapse, Typography, Alert, Button } from 'antd';
import classNames from 'classnames';
import dynamic from 'next/dynamic';
import { FieldUpdaterFunc, VideoVariant, UpdateArgs } from '../../types/config-section';
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
import { TextField } from './TextField';
import {
DEFAULT_VARIANT_STATE,
VIDEO_VARIANT_SETTING_DEFAULTS,
VIDEO_NAME_DEFAULTS,
ENCODER_PRESET_SLIDER_MARKS,
ENCODER_PRESET_TOOLTIPS,
VIDEO_BITRATE_DEFAULTS,
VIDEO_BITRATE_SLIDER_MARKS,
FRAMERATE_SLIDER_MARKS,
FRAMERATE_DEFAULTS,
FRAMERATE_TOOLTIPS,
} from '../../utils/config-constants';
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
import { ToggleSwitch } from './ToggleSwitch';
2021-01-10 13:37:22 +03:00
const { Panel } = Collapse;
// Lazy loaded components
const ExclamationCircleFilled = dynamic(() => import('@ant-design/icons/ExclamationCircleFilled'), {
ssr: false,
});
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
export type VideoVariantFormProps = {
2021-01-10 13:37:22 +03:00
dataState: VideoVariant;
onUpdateField: FieldUpdaterFunc;
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
};
2021-01-10 13:37:22 +03:00
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
export const VideoVariantForm: FC<VideoVariantFormProps> = ({
2021-01-31 12:38:20 +03:00
dataState = DEFAULT_VARIANT_STATE,
onUpdateField,
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
}) => {
const videoPassthroughEnabled = dataState.videoPassthrough;
2021-01-10 13:37:22 +03:00
const handleFramerateChange = (value: number) => {
onUpdateField({ fieldName: 'framerate', value });
2021-01-10 13:37:22 +03:00
};
const handleVideoBitrateChange = (value: number) => {
onUpdateField({ fieldName: 'videoBitrate', value });
2021-01-10 13:37:22 +03:00
};
2021-01-31 09:53:00 +03:00
const handleVideoCpuUsageLevelChange = (value: number) => {
2021-01-31 12:38:20 +03:00
onUpdateField({ fieldName: 'cpuUsageLevel', value });
};
const handleScaledWidthChanged = (args: UpdateArgs) => {
const value = Number(args.value);
// eslint-disable-next-line no-restricted-globals
if (isNaN(value)) {
return;
}
onUpdateField({ fieldName: 'scaledWidth', value: value || 0 });
};
const handleScaledHeightChanged = (args: UpdateArgs) => {
const value = Number(args.value);
// eslint-disable-next-line no-restricted-globals
if (isNaN(value)) {
return;
}
onUpdateField({ fieldName: 'scaledHeight', value: value || 0 });
};
2021-02-21 02:14:50 +03:00
// Video passthrough handling
const handleVideoPassConfirm = () => {
onUpdateField({ fieldName: 'videoPassthrough', value: true });
};
// If passthrough is currently on, set it back to false on toggle.
// Else let the Popconfirm turn it on.
const handleVideoPassthroughToggle = (value: boolean) => {
if (videoPassthroughEnabled) {
2021-02-21 02:14:50 +03:00
onUpdateField({ fieldName: 'videoPassthrough', value });
}
};
const handleNameChanged = (args: UpdateArgs) => {
onUpdateField({ fieldName: 'name', value: args.value });
};
// Slider notes
const selectedVideoBRnote = () => {
if (videoPassthroughEnabled) {
return 'Bitrate selection is disabled when Video Passthrough is enabled.';
}
let note = `${dataState.videoBitrate}${VIDEO_BITRATE_DEFAULTS.unit}`;
if (dataState.videoBitrate < 2000) {
note = `${note} - Good for low bandwidth environments.`;
} else if (dataState.videoBitrate < 3500) {
note = `${note} - Good for most bandwidth environments.`;
} else {
note = `${note} - Good for high bandwidth environments.`;
}
return note;
};
const selectedFramerateNote = () => {
if (videoPassthroughEnabled) {
return 'Framerate selection is disabled when Video Passthrough is enabled.';
}
return FRAMERATE_TOOLTIPS[dataState.framerate] || '';
};
const cpuUsageNote = () => {
if (videoPassthroughEnabled) {
return 'CPU usage selection is disabled when Video Passthrough is enabled.';
}
return ENCODER_PRESET_TOOLTIPS[dataState.cpuUsageLevel] || '';
};
2021-01-10 13:37:22 +03:00
const classes = classNames({
'config-variant-form': true,
'video-passthrough-enabled': videoPassthroughEnabled,
});
2021-01-10 13:37:22 +03:00
return (
<div className={classes}>
<div className="video-varient-alert">
<Alert
type="info"
action={
<a
href="https://owncast.online/docs/video?source=admin"
target="_blank"
rel="noopener noreferrer"
>
<div className="video-varient-alert-button-container">
<Button size="small" type="text" icon={<ExclamationCircleFilled />}>
Read more about how each of these settings can impact the performance of your
server.
</Button>
</div>
</a>
}
/>
</div>
{videoPassthroughEnabled && (
<p className="passthrough-warning">
NOTE: Video Passthrough for this output stream variant is <em>enabled</em>, disabling the
below video encoding settings.
</p>
)}
<Row gutter={16}>
<Col xs={24} lg={{ span: 24, pull: 3 }} className="video-text-field-container">
<TextField
maxLength="10"
{...VIDEO_NAME_DEFAULTS}
value={dataState.name}
onChange={handleNameChanged}
/>
</Col>
<Col sm={24} md={12}>
<div className="form-module cpu-usage-container">
2021-04-02 08:00:31 +03:00
<Typography.Title level={3}>CPU or GPU Utilization</Typography.Title>
<p className="description">
Reduce to improve server performance, or increase it to improve video quality.
</p>
<div className="segment-slider-container">
<Slider
tipFormatter={value => ENCODER_PRESET_TOOLTIPS[value]}
onChange={handleVideoCpuUsageLevelChange}
min={1}
max={Object.keys(ENCODER_PRESET_SLIDER_MARKS).length}
marks={ENCODER_PRESET_SLIDER_MARKS}
defaultValue={dataState.cpuUsageLevel}
value={dataState.cpuUsageLevel}
disabled={dataState.videoPassthrough}
/>
<p className="selected-value-note">{cpuUsageNote()}</p>
</div>
2021-02-16 01:09:34 +03:00
<p className="read-more-subtext">
This could mean GPU or CPU usage depending on your server environment.
<br />
<br />
<a
2021-03-26 09:05:13 +03:00
href="https://owncast.online/docs/video/?source=admin#cpu-usage"
target="_blank"
rel="noopener noreferrer"
>
2021-04-02 08:00:31 +03:00
Read more about hardware performance.
</a>
2021-02-16 01:09:34 +03:00
</p>
2021-01-10 13:37:22 +03:00
</div>
</Col>
<Col sm={24} md={11} offset={1}>
{/* VIDEO BITRATE FIELD */}
<div
2021-02-16 01:09:34 +03:00
className={`form-module bitrate-container ${
dataState.videoPassthrough ? 'disabled' : ''
}`}
>
<Typography.Title level={3}>Video Bitrate</Typography.Title>
<p className="description">{VIDEO_BITRATE_DEFAULTS.tip}</p>
<div className="segment-slider-container">
2021-01-10 13:37:22 +03:00
<Slider
tipFormatter={value => `${value} ${VIDEO_BITRATE_DEFAULTS.unit}`}
disabled={dataState.videoPassthrough}
defaultValue={dataState.videoBitrate}
value={dataState.videoBitrate}
onChange={handleVideoBitrateChange}
step={VIDEO_BITRATE_DEFAULTS.incrementBy}
min={VIDEO_BITRATE_DEFAULTS.min}
max={VIDEO_BITRATE_DEFAULTS.max}
marks={VIDEO_BITRATE_SLIDER_MARKS}
2021-01-10 13:37:22 +03:00
/>
<p className="selected-value-note">{selectedVideoBRnote()}</p>
2021-01-10 13:37:22 +03:00
</div>
2021-02-16 01:09:34 +03:00
<p className="read-more-subtext">
<a
2021-03-26 09:05:13 +03:00
href="https://owncast.online/docs/video/?source=admin"
target="_blank"
rel="noopener noreferrer"
>
Read more about bitrates.
</a>
2021-02-16 01:09:34 +03:00
</p>
2021-01-10 13:37:22 +03:00
</div>
</Col>
</Row>
<Collapse className="advanced-settings">
<Panel header="Advanced Settings" key="1">
2021-02-21 02:14:50 +03:00
<Row gutter={16}>
<Col sm={24} md={12}>
<div className="form-module resolution-module">
<Typography.Title level={3}>Resolution</Typography.Title>
<p className="description">
2021-02-21 03:09:40 +03:00
Resizing your content will take additional resources on your server. If you wish
to optionally resize your content for this stream output then you should either
set the width <strong>or</strong> the height to keep your aspect ratio. <br />
<br />
<a
2021-03-26 09:05:13 +03:00
href="https://owncast.online/docs/video/?source=admin"
target="_blank"
rel="noopener noreferrer"
>
2021-02-21 03:09:40 +03:00
Read more about resolutions.
</a>
2021-02-21 02:14:50 +03:00
</p>
<br />
<TextField
type="number"
{...VIDEO_VARIANT_SETTING_DEFAULTS.scaledWidth}
2021-02-21 02:14:50 +03:00
value={dataState.scaledWidth}
onChange={handleScaledWidthChanged}
disabled={dataState.videoPassthrough}
2021-02-21 02:14:50 +03:00
/>
<TextField
type="number"
{...VIDEO_VARIANT_SETTING_DEFAULTS.scaledHeight}
2021-02-21 02:14:50 +03:00
value={dataState.scaledHeight}
onChange={handleScaledHeightChanged}
disabled={dataState.videoPassthrough}
2021-02-21 02:14:50 +03:00
/>
</div>
</Col>
<Col sm={24} md={12}>
{/* VIDEO PASSTHROUGH FIELD */}
<div className="form-module video-passthrough-module">
2021-02-21 02:14:50 +03:00
<Typography.Title level={3}>Video Passthrough</Typography.Title>
2021-03-03 09:39:27 +03:00
<div className="description">
2021-02-21 03:09:40 +03:00
<p>
Enabling video passthrough may allow for less hardware utilization, but may also
make your stream <strong>unplayable</strong>.
</p>
<p>
All other settings for this stream output will be disabled if passthrough is
used.
</p>
<p>
<a
2021-03-26 09:05:13 +03:00
href="https://owncast.online/docs/video/?source=admin#video-passthrough"
target="_blank"
rel="noopener noreferrer"
>
2021-02-21 03:09:40 +03:00
Read the documentation before enabling, as it impacts your stream.
</a>
</p>
2021-03-03 09:39:27 +03:00
</div>
<div className="advanced-switch-container">
<Popconfirm
disabled={dataState.videoPassthrough === true}
title="Did you read the documentation about video passthrough and understand the risks involved with enabling it?"
icon={<ExclamationCircleFilled />}
onConfirm={handleVideoPassConfirm}
okText="Yes"
cancelText="No"
getPopupContainer={triggerNode => triggerNode}
placement="topLeft"
>
{/* adding an <a> tag to force Popcofirm to register click on toggle */}
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a href="#">
<div className="advanced-description-switch-container">
<div className="advanced-description-wrapper">
<p>Use Video Passthrough?</p>
</div>
<ToggleSwitch
label=""
fieldName="video-passthrough"
checked={dataState.videoPassthrough}
onChange={handleVideoPassthroughToggle}
/>
</div>
</a>
<p>*{VIDEO_VARIANT_SETTING_DEFAULTS.videoPassthrough.tip}</p>
</Popconfirm>
</div>
2021-02-21 02:14:50 +03:00
</div>
</Col>
</Row>
{/* FRAME RATE FIELD */}
2021-02-21 02:14:50 +03:00
<div className="form-module frame-rate-module">
<Typography.Title level={3}>Frame rate</Typography.Title>
<p className="description">{FRAMERATE_DEFAULTS.tip}</p>
<div className="segment-slider-container">
<Slider
tipFormatter={value => `${value} ${FRAMERATE_DEFAULTS.unit}`}
defaultValue={dataState.framerate}
value={dataState.framerate}
onChange={handleFramerateChange}
step={FRAMERATE_DEFAULTS.incrementBy}
min={FRAMERATE_DEFAULTS.min}
max={FRAMERATE_DEFAULTS.max}
marks={FRAMERATE_SLIDER_MARKS}
disabled={dataState.videoPassthrough}
/>
<p className="selected-value-note">{selectedFramerateNote()}</p>
</div>
2021-02-16 01:09:34 +03:00
<p className="read-more-subtext">
<a
2021-03-26 09:05:13 +03:00
href="https://owncast.online/docs/video/?source=admin#framerate"
target="_blank"
rel="noopener noreferrer"
>
Read more about framerates.
</a>
2021-02-16 01:09:34 +03:00
</p>
</div>
</Panel>
</Collapse>
2021-01-10 13:37:22 +03:00
</div>
);
reafctor: normalize component formatting (#2082) * refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
2022-09-07 10:00:28 +03:00
};