WIP new process details view
This commit is contained in:
parent
f4c9fbe61a
commit
159d00f000
@ -1,120 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Modal from '@mui/material/Modal';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import ModalContent from '../ModalContent';
|
||||
import Progress from '../Progress';
|
||||
import Textarea from '../Textarea';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
title: {
|
||||
marginBottom: '-.3em',
|
||||
marginTop: '0em',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
textarea: {
|
||||
marginBottom: '-1em',
|
||||
},
|
||||
box: {
|
||||
backgroundColor: theme.palette.background.modalbox,
|
||||
borderRadius: 4,
|
||||
padding: '1em',
|
||||
},
|
||||
banner: {
|
||||
marginBottom: '-1em',
|
||||
},
|
||||
logging: {
|
||||
marginTop: '.15em',
|
||||
},
|
||||
}));
|
||||
|
||||
const initLogdata = (logdata) => {
|
||||
if (!logdata) {
|
||||
logdata = {};
|
||||
}
|
||||
|
||||
return {
|
||||
prelude: [],
|
||||
log: [],
|
||||
...logdata,
|
||||
};
|
||||
};
|
||||
|
||||
const formatLogline = (entry) => {
|
||||
let line = '@' + entry[0] + ' ';
|
||||
|
||||
const matches = entry[1].match(/^\[([0-9A-Za-z]+) @ 0x[0-9a-f]+\]/i);
|
||||
if (matches !== null) {
|
||||
let t = '[' + matches[1];
|
||||
for (let i = 0; i < 10 - matches[1].length; i++) {
|
||||
t += ' ';
|
||||
}
|
||||
t += ']';
|
||||
line += entry[1].replace(matches[0], t);
|
||||
} else {
|
||||
line += entry[1];
|
||||
}
|
||||
|
||||
return line;
|
||||
};
|
||||
|
||||
const Component = function (props) {
|
||||
const classes = useStyles();
|
||||
const logdata = initLogdata(props.logdata);
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose} className="modal">
|
||||
<ModalContent title={props.title} onClose={props.onClose} onHelp={props.onHelp}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} md={8} lg={10}>
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.box}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} className={classes.banner}>
|
||||
<Typography variant="body1" className={classes.title}>
|
||||
<Trans>Banner</Trans>
|
||||
</Typography>
|
||||
<Textarea rows={9} value={logdata.prelude.join('\n')} scrollTo="bottom" readOnly allowCopy />
|
||||
</Grid>
|
||||
<Grid item xs={12} marginTop={2}>
|
||||
<Divider />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.logging}>
|
||||
<Typography variant="body1" className={classes.title}>
|
||||
<Trans>Logging</Trans>
|
||||
</Typography>
|
||||
<Textarea rows={16} value={logdata.log.map(formatLogline).join('\n')} scrollTo="bottom" readOnly allowCopy />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4} lg={2}>
|
||||
{props.progress !== null && <Progress {...props.progress} />}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
open: false,
|
||||
title: '',
|
||||
progress: {},
|
||||
logdata: {
|
||||
prelude: [],
|
||||
log: [],
|
||||
},
|
||||
onClose: null,
|
||||
onHelp: null,
|
||||
};
|
||||
269
src/misc/modals/Process/Progress.js
Normal file
269
src/misc/modals/Process/Progress.js
Normal file
@ -0,0 +1,269 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import Duration from '../../Duration';
|
||||
import Number from '../../Number';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
box: {
|
||||
backgroundColor: theme.palette.background.modalbox,
|
||||
borderRadius: 4,
|
||||
padding: '1em',
|
||||
height: '100%',
|
||||
},
|
||||
}));
|
||||
|
||||
function init(props) {
|
||||
const initProps = {
|
||||
time: 0,
|
||||
fps: 0,
|
||||
bitrate: 0,
|
||||
q: -1,
|
||||
speed: 0,
|
||||
drop: 0,
|
||||
dup: 0,
|
||||
frames: 0,
|
||||
cpu: 0,
|
||||
memory: 0,
|
||||
...props,
|
||||
};
|
||||
|
||||
return initProps;
|
||||
}
|
||||
|
||||
function IO(props) {
|
||||
if (props.progress === null) {
|
||||
return (
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Not available</Trans>
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
const progress = props.progress;
|
||||
|
||||
return (
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
{progress.type}: {progress.codec}{' '}
|
||||
{progress.type === 'video' ? (
|
||||
<React.Fragment>
|
||||
{progress.width}x{progress.height} {progress.pix_fmt}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
{progress.layout} {progress.sampling_hz} Hz
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.bitrate_kbit} digits={2} minDigits={2} />
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>kbit/s</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.fps} digits={2} minDigits={2} />
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>FPS</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
IO.defaultProps = {
|
||||
progress: null,
|
||||
};
|
||||
|
||||
export default function Progress(props) {
|
||||
const classes = useStyles();
|
||||
|
||||
const progress = init(props);
|
||||
|
||||
console.log(progress);
|
||||
|
||||
let input_video = null;
|
||||
let input_audio = null;
|
||||
let output_video = null;
|
||||
let output_audio = null;
|
||||
|
||||
for (let i = 0; i < progress.inputs.length; i++) {
|
||||
if (progress.inputs[i].type === 'video') {
|
||||
input_video = progress.inputs[i];
|
||||
} else if (progress.inputs[i].type === 'audio') {
|
||||
input_audio = progress.inputs[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < progress.outputs.length; i++) {
|
||||
if (progress.outputs[i].type === 'video') {
|
||||
output_video = progress.outputs[i];
|
||||
} else if (progress.outputs[i].type === 'audio') {
|
||||
output_audio = progress.outputs[i];
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Duration seconds={progress.time} />
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Uptime</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.bitrate} digits={2} minDigits={2} />
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>kbit/s</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.q} digits={2} minDigits={2} />
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Quality</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.speed} digits={2} minDigits={2} />
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Speed</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={!isNaN((props.drop * 100) / props.frames) || 0} digits={2} minDigits={2} />%
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Frame drops</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.dup} digits={0} minDigits={0} />
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Dup. frames</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.cpu} digits={2} minDigits={2} />%
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>CPU</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={3}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={progress.memory / 1024 / 1024} digits={0} minDigits={0} /> MB
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Memory</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{input_video || input_audio ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Inputs</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{input_video && (
|
||||
<Grid item xs={12}>
|
||||
<IO progress={input_video}></IO>
|
||||
</Grid>
|
||||
)}
|
||||
{input_audio && (
|
||||
<Grid item xs={12}>
|
||||
<IO progress={input_audio}></IO>
|
||||
</Grid>
|
||||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>No inputs</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
{output_video || output_audio ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>Outputs</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{output_video && (
|
||||
<Grid item xs={12}>
|
||||
<IO progress={output_video}></IO>
|
||||
</Grid>
|
||||
)}
|
||||
{output_audio && (
|
||||
<Grid item xs={12}>
|
||||
<IO progress={output_audio}></IO>
|
||||
</Grid>
|
||||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
<Trans>No outputs</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Progress.defaultProps = {
|
||||
time: 0,
|
||||
fps: 0,
|
||||
bitrate: 0,
|
||||
q: -1,
|
||||
speed: 0,
|
||||
drop: 0,
|
||||
dup: 0,
|
||||
frame: 0,
|
||||
cpu: 0,
|
||||
memory: 0,
|
||||
};
|
||||
148
src/misc/modals/Process/index.js
Normal file
148
src/misc/modals/Process/index.js
Normal file
@ -0,0 +1,148 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Modal from '@mui/material/Modal';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
|
||||
import ModalContent from '../../ModalContent';
|
||||
import Progress from './Progress';
|
||||
import Textarea from '../../Textarea';
|
||||
import TabPanel from '../../TabPanel';
|
||||
import TabsVerticalGrid from '../../TabsVerticalGrid';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
title: {
|
||||
marginBottom: '-.3em',
|
||||
marginTop: '0em',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
textarea: {
|
||||
marginBottom: '-1em',
|
||||
},
|
||||
box: {
|
||||
backgroundColor: theme.palette.background.modalbox,
|
||||
borderRadius: 4,
|
||||
padding: '1em',
|
||||
},
|
||||
banner: {
|
||||
marginBottom: '-1em',
|
||||
},
|
||||
logging: {
|
||||
marginTop: '.15em',
|
||||
},
|
||||
}));
|
||||
|
||||
const initLogdata = (logdata) => {
|
||||
if (!logdata) {
|
||||
logdata = {};
|
||||
}
|
||||
|
||||
return {
|
||||
command: [],
|
||||
prelude: [],
|
||||
log: [],
|
||||
...logdata,
|
||||
};
|
||||
};
|
||||
|
||||
const formatLogline = (entry) => {
|
||||
let line = '@' + entry[0] + ' ';
|
||||
|
||||
const matches = entry[1].match(/^\[([0-9A-Za-z]+) @ 0x[0-9a-f]+\]/i);
|
||||
if (matches !== null) {
|
||||
let t = '[' + matches[1];
|
||||
for (let i = 0; i < 10 - matches[1].length; i++) {
|
||||
t += ' ';
|
||||
}
|
||||
t += ']';
|
||||
line += entry[1].replace(matches[0], t);
|
||||
} else {
|
||||
line += entry[1];
|
||||
}
|
||||
|
||||
return line;
|
||||
};
|
||||
|
||||
const Component = function (props) {
|
||||
const [$tab, setTab] = React.useState('vitals');
|
||||
|
||||
const classes = useStyles();
|
||||
const logdata = initLogdata(props.logdata);
|
||||
|
||||
logdata.command = props.progress.command;
|
||||
|
||||
const handleChangeTab = (event, value) => {
|
||||
setTab(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose} className="modal">
|
||||
<ModalContent title={props.title} onClose={props.onClose} onHelp={props.onHelp}>
|
||||
<Grid container spacing={1}>
|
||||
<TabsVerticalGrid>
|
||||
<Tabs orientation="vertical" variant="scrollable" value={$tab} onChange={handleChangeTab}>
|
||||
<Tab className="tab" label={<Trans>Vitals</Trans>} value="vitals" />
|
||||
<Tab className="tab" label={<Trans>Log</Trans>} value="log" />
|
||||
</Tabs>
|
||||
<TabPanel value={$tab} index="vitals" className="panel">
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
{props.progress !== null && <Progress {...props.progress} />}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
<TabPanel value={$tab} index="log" className="panel">
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.box}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} className={classes.banner}>
|
||||
<Typography variant="body1" className={classes.title}>
|
||||
<Trans>Command</Trans>
|
||||
</Typography>
|
||||
<Textarea rows={1} value={'ffmpeg ' + logdata.command.join(' ')} scrollTo="bottom" readOnly allowCopy />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.banner}>
|
||||
<Typography variant="body1" className={classes.title}>
|
||||
<Trans>Banner</Trans>
|
||||
</Typography>
|
||||
<Textarea rows={9} value={logdata.prelude.join('\n')} scrollTo="bottom" readOnly allowCopy />
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} className={classes.logging}>
|
||||
<Typography variant="body1" className={classes.title}>
|
||||
<Trans>Logging</Trans>
|
||||
</Typography>
|
||||
<Textarea rows={16} value={logdata.log.map(formatLogline).join('\n')} scrollTo="bottom" readOnly allowCopy />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
</TabsVerticalGrid>
|
||||
</Grid>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
open: false,
|
||||
title: '',
|
||||
progress: {},
|
||||
logdata: {
|
||||
command: [],
|
||||
prelude: [],
|
||||
log: [],
|
||||
},
|
||||
onClose: null,
|
||||
onHelp: null,
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
import { i18n } from '@lingui/core';
|
||||
import { t } from '@lingui/macro';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
import { jwtDecode } from 'jwt-decode';
|
||||
import Handlebars from 'handlebars/dist/cjs/handlebars';
|
||||
import SemverSatisfies from 'semver/functions/satisfies';
|
||||
import SemverGt from 'semver/functions/gt';
|
||||
@ -3328,6 +3328,11 @@ class Restreamer {
|
||||
frames: 0,
|
||||
drop: 0,
|
||||
dup: 0,
|
||||
command: [],
|
||||
cpu: 0.0,
|
||||
memory: 0,
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
};
|
||||
|
||||
if (state === null) {
|
||||
@ -3336,6 +3341,7 @@ class Restreamer {
|
||||
|
||||
progress.valid = true;
|
||||
progress.order = state.order;
|
||||
progress.command = state.command.slice();
|
||||
|
||||
const fps = state.progress.fps || 0;
|
||||
|
||||
@ -3367,6 +3373,10 @@ class Restreamer {
|
||||
progress.frames = state.progress.frames || 0;
|
||||
progress.drop = state.progress.drop || 0;
|
||||
progress.dup = state.progress.dup || 0;
|
||||
progress.cpu = state.cpu_usage || 0;
|
||||
progress.memory = state.memory_bytes || 0;
|
||||
progress.inputs = state.progress.inputs.slice();
|
||||
progress.outputs = state.progress.outputs.slice();
|
||||
}
|
||||
|
||||
return progress;
|
||||
|
||||
@ -206,6 +206,7 @@ export default function Main(props) {
|
||||
|
||||
const open = !$processDetails.open;
|
||||
let logdata = {
|
||||
command: [],
|
||||
prelude: [],
|
||||
log: [],
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user