Merge branch '0.0.6' of github.com:owncast/owncast-admin into 0.0.6

This commit is contained in:
gingervitis 2021-02-07 19:28:50 -08:00
commit 6de5b3af19
7 changed files with 131 additions and 45 deletions

View File

@ -23,7 +23,7 @@ jobs:
uses: creyD/prettier_action@v3.1 uses: creyD/prettier_action@v3.1
with: with:
# This part is also where you can pass other options, for example: # This part is also where you can pass other options, for example:
prettier_options: --write **/*.{js,tsx,jsx} prettier_options: --write **/*.{js,tsx,jsx,css,scss}
only_changed: true only_changed: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,6 +1,7 @@
import React, { useState, useContext, useEffect } from 'react'; import React, { useState, useContext, useEffect } from 'react';
import { Button, Tooltip } from 'antd'; import { Button, Tooltip, Collapse, Popconfirm } from 'antd';
import { CopyOutlined, RedoOutlined } from '@ant-design/icons'; import { CopyOutlined, RedoOutlined } from '@ant-design/icons';
const { Panel } = Collapse;
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';
@ -14,6 +15,7 @@ import {
TEXTFIELD_PROPS_STREAM_KEY, TEXTFIELD_PROPS_STREAM_KEY,
TEXTFIELD_PROPS_WEB_PORT, TEXTFIELD_PROPS_WEB_PORT,
} from '../../utils/config-constants'; } from '../../utils/config-constants';
import { fetchData, API_YP_RESET } from '../../utils/apis';
import { UpdateArgs } from '../../types/config-section'; import { UpdateArgs } from '../../types/config-section';
@ -24,7 +26,7 @@ export default function EditInstanceDetails() {
const { serverConfig } = serverStatusData || {}; const { serverConfig } = serverStatusData || {};
const { streamKey, ffmpegPath, rtmpServerPort, webServerPort } = serverConfig; const { streamKey, ffmpegPath, rtmpServerPort, webServerPort, yp } = serverConfig;
const [copyIsVisible, setCopyVisible] = useState(false); const [copyIsVisible, setCopyVisible] = useState(false);
@ -66,6 +68,41 @@ export default function EditInstanceDetails() {
} }
}; };
const resetDirectoryRegistration = async () => {
try {
await fetchData(API_YP_RESET);
setMessage('');
} catch (error) {
alert(error);
}
};
function ResetYP() {
// TODO: Uncomment this after it's styled.
// if (yp.enabled) {
return (
<div className="field-container">
Reset Directory:
<Popconfirm
placement="topLeft"
title={'Are you sure you want to reset your connection to the Owncast directory?'}
onConfirm={resetDirectoryRegistration}
okText="Yes"
cancelText="No"
>
<Button>Reset Directory Connection</Button>
</Popconfirm>
<p>
If you are experiencing issues with your listing on the Owncast Directory and were asked
to "reset" your connection to the service, you can do that here. The next time you go live
it will try and re-register your server with the directory from scratch.
</p>
</div>
);
// }
// return null;
}
function generateStreamKey() { function generateStreamKey() {
let key = ''; let key = '';
for (let i = 0; i < 3; i += 1) { for (let i = 0; i < 3; i += 1) {
@ -135,6 +172,13 @@ export default function EditInstanceDetails() {
onChange={handleFieldChange} onChange={handleFieldChange}
onSubmit={showConfigurationRestartMessage} onSubmit={showConfigurationRestartMessage}
/> />
<Collapse>
<Panel header="Advanced Settings" key="1">
<div className="form-fields">
<ResetYP />
</div>
</Panel>
</Collapse>
</div> </div>
); );
} }

View File

@ -1,7 +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, UpdateArgs } from '../../types/config-section';
import TextField from './form-textfield';
import { DEFAULT_VARIANT_STATE } from '../../utils/config-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';
@ -39,6 +40,20 @@ const VIDEO_VARIANT_DEFAULTS = {
audioPassthrough: { audioPassthrough: {
tip: 'If No is selected, then you should set your desired Audio Bitrate.', tip: 'If No is selected, then you should set your desired Audio Bitrate.',
}, },
scaledWidth: {
fieldName: 'scaledWidth',
label: 'Resized Width',
maxLength: 4,
placeholder: '1080',
tip: "Optionally resize this content's width.",
},
scaledHeight: {
fieldName: 'scaledHeight',
label: 'Resized Height',
maxLength: 4,
placeholder: '720',
tip: "Optionally resize this content's height.",
},
}; };
interface VideoVariantFormProps { interface VideoVariantFormProps {
@ -62,7 +77,21 @@ export default function VideoVariantForm({
const handleVideoCpuUsageLevelChange = (value: number) => { const handleVideoCpuUsageLevelChange = (value: number) => {
onUpdateField({ fieldName: 'cpuUsageLevel', value }); onUpdateField({ fieldName: 'cpuUsageLevel', value });
}; };
const handleScaledWidthChanged = (args: UpdateArgs) => {
const value = Number(args.value);
if (!isNaN(value)) {
return;
}
onUpdateField({ fieldName: 'scaledWidth', value: value });
};
const handleScaledHeightChanged = (args: UpdateArgs) => {
const value = Number(args.value);
if (!isNaN(value)) {
return;
}
onUpdateField({ fieldName: 'scaledHeight', value: value });
};
const framerateDefaults = VIDEO_VARIANT_DEFAULTS.framerate; const framerateDefaults = VIDEO_VARIANT_DEFAULTS.framerate;
const framerateMin = framerateDefaults.min; const framerateMin = framerateDefaults.min;
const framerateMax = framerateDefaults.max; const framerateMax = framerateDefaults.max;
@ -145,14 +174,29 @@ export default function VideoVariantForm({
</div> </div>
</div> </div>
<br />
<br />
<br />
<br />
<Collapse> <Collapse>
<Panel header="Advanced Settings" key="1"> <Panel header="Advanced Settings" key="1">
<div className="section-intro">Touch if you dare.</div> <div className="section-intro">
Resizing your content will take additional resources on your server. If you wish to
optionally resize your output for this stream variant then you should either set the
width <strong>or</strong> the height to keep your aspect ratio.
</div>
<div className="field">
<TextField
type="number"
{...VIDEO_VARIANT_DEFAULTS.scaledWidth}
value={dataState.scaledWidth}
onChange={handleScaledWidthChanged}
/>
</div>
<div className="field">
<TextField
type="number"
{...VIDEO_VARIANT_DEFAULTS.scaledHeight}
value={dataState.scaledHeight}
onChange={handleScaledHeightChanged}
/>
</div>
{/* FRAME RATE FIELD */} {/* FRAME RATE FIELD */}
<div className="field"> <div className="field">

View File

@ -56,6 +56,9 @@ export interface VideoVariant {
audioBitrate: number; audioBitrate: number;
videoPassthrough: boolean; videoPassthrough: boolean;
videoBitrate: number; videoBitrate: number;
scaledWidth: number;
scaledHeight: number;
} }
export interface VideoSettingsFields { export interface VideoSettingsFields {
latencyLevel: number; latencyLevel: number;

View File

@ -63,6 +63,8 @@ export const CREATE_WEBHOOK = `${API_LOCATION}webhooks/create`;
// hard coded social icons list // hard coded social icons list
export const SOCIAL_PLATFORMS_LIST = `${NEXT_PUBLIC_API_HOST}api/socialplatforms`; export const SOCIAL_PLATFORMS_LIST = `${NEXT_PUBLIC_API_HOST}api/socialplatforms`;
export const API_YP_RESET = `${API_LOCATION}yp/reset`;
export const TEMP_UPDATER_API = LOGS_ALL; export const TEMP_UPDATER_API = LOGS_ALL;
const GITHUB_RELEASE_URL = 'https://api.github.com/repos/owncast/owncast/releases/latest'; const GITHUB_RELEASE_URL = 'https://api.github.com/repos/owncast/owncast/releases/latest';
@ -71,14 +73,10 @@ interface FetchOptions {
data?: any; data?: any;
method?: string; method?: string;
auth?: boolean; auth?: boolean;
}; }
export async function fetchData(url: string, options?: FetchOptions) { export async function fetchData(url: string, options?: FetchOptions) {
const { const { data, method = 'GET', auth = true } = options || {};
data,
method = 'GET',
auth = true,
} = options || {};
const requestOptions: RequestInit = { const requestOptions: RequestInit = {
method, method,
@ -114,7 +112,6 @@ export async function fetchData(url: string, options?: FetchOptions) {
return {}; return {};
} }
export async function getGithubRelease() { export async function getGithubRelease() {
try { try {
const response = await fetch(GITHUB_RELEASE_URL); const response = await fetch(GITHUB_RELEASE_URL);
@ -133,29 +130,25 @@ export async function getGithubRelease() {
// Stolen from https://gist.github.com/prenagha/98bbb03e27163bc2f5e4 // Stolen from https://gist.github.com/prenagha/98bbb03e27163bc2f5e4
const VPAT = /^\d+(\.\d+){0,2}$/; const VPAT = /^\d+(\.\d+){0,2}$/;
function upToDate(local, remote) { function upToDate(local, remote) {
if (!local || !remote || local.length === 0 || remote.length === 0) if (!local || !remote || local.length === 0 || remote.length === 0) return false;
return false; if (local === remote) return true;
if (local === remote) if (VPAT.test(local) && VPAT.test(remote)) {
return true; const lparts = local.split('.');
if (VPAT.test(local) && VPAT.test(remote)) { while (lparts.length < 3) lparts.push('0');
const lparts = local.split('.'); const rparts = remote.split('.');
while(lparts.length < 3) while (rparts.length < 3) rparts.push('0');
lparts.push("0"); // eslint-disable-next-line no-plusplus
const rparts = remote.split('.'); for (let i = 0; i < 3; i++) {
while (rparts.length < 3) const l = parseInt(lparts[i], 10);
rparts.push("0"); const r = parseInt(rparts[i], 10);
// eslint-disable-next-line no-plusplus if (l === r)
for (let i=0; i<3; i++) { // eslint-disable-next-line no-continue
const l = parseInt(lparts[i], 10); continue;
const r = parseInt(rparts[i], 10); return l > r;
if (l === r) }
// eslint-disable-next-line no-continue return true;
continue; }
return l > r; return local >= remote;
}
return true;
}
return local >= remote;
} }
// Make a request to the server status API and the Github releases API // Make a request to the server status API and the Github releases API
@ -165,12 +158,12 @@ export async function upgradeVersionAvailable(currentVersion) {
let recentReleaseVersion = recentRelease.tag_name; let recentReleaseVersion = recentRelease.tag_name;
if (recentReleaseVersion.substr(0, 1) === 'v') { if (recentReleaseVersion.substr(0, 1) === 'v') {
recentReleaseVersion = recentReleaseVersion.substr(1) recentReleaseVersion = recentReleaseVersion.substr(1);
} }
if (!upToDate(currentVersion, recentReleaseVersion)) { if (!upToDate(currentVersion, recentReleaseVersion)) {
return recentReleaseVersion return recentReleaseVersion;
} }
return null; return null;
} }

View File

@ -168,6 +168,8 @@ export const DEFAULT_VARIANT_STATE: VideoVariant = {
audioPassthrough: true, // if false, then CAN set audiobitrate audioPassthrough: true, // if false, then CAN set audiobitrate
audioBitrate: 0, audioBitrate: 0,
cpuUsageLevel: 3, cpuUsageLevel: 3,
scaledHeight: null,
scaledWidth: null,
}; };
export const DEFAULT_SOCIAL_HANDLE: SocialHandle = { export const DEFAULT_SOCIAL_HANDLE: SocialHandle = {

View File

@ -1,4 +1,4 @@
export function isValidUrl(url: string): boolean { export function isValidUrl(url: string): boolean {
const pattern = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/; const pattern = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/i;
return !!pattern.test(url); return !!pattern.test(url);
} }