Skip to content

Commit

Permalink
Translate record to English. v5.9.21
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Sep 10, 2023
1 parent 2b33dda commit 0e59076
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 16 deletions.
4 changes: 2 additions & 2 deletions ui/src/components/PopoverConfirm.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import React from "react";
import {Button, OverlayTrigger, Popover} from "react-bootstrap";
import {useSrsLanguage} from "./LanguageSwitch";

export default function PopoverConfirm({onClick, trigger, children}) {
export default function PopoverConfirm({onClick, trigger, children, placement}) {
const [startUpgrade, setStartUpgrade] = React.useState();
const language = useSrsLanguage();

Expand Down Expand Up @@ -46,7 +46,7 @@ export default function PopoverConfirm({onClick, trigger, children}) {
);

return (
<OverlayTrigger trigger="click" placement="right" overlay={popover} show={startUpgrade}>
<OverlayTrigger trigger="click" placement={placement || 'right'} overlay={popover} show={startUpgrade}>
<span onClick={(e) => {
e.preventDefault();
setStartUpgrade(!startUpgrade);
Expand Down
1 change: 0 additions & 1 deletion ui/src/pages/Scenario.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {Clipboard, Token} from "../utils";
import axios from "axios";
import ScenarioLive from './ScenarioLive';
import useUrls from "../components/UrlGenerator";
import ScenarioRecordVod from './ScenarioRecordVod';
import ScenarioForward from './ScenarioForward';
import {useErrorHandler} from 'react-error-boundary';
import {SrsErrorBoundary} from "../components/SrsErrorBoundary";
Expand Down
202 changes: 189 additions & 13 deletions ui/src/pages/ScenarioRecord.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ import TutorialsText from "../components/TutorialsText";

export default function ScenarioRecord() {
const language = useSrsLanguage();
return language === 'zh' ? <ScenarioRecordCn /> : <ScenarioRecordEn />;
}

function ScenarioRecordCn() {
const recordStatus = useRecordStatus();
const [activeKey, setActiveKey] = React.useState();

Expand All @@ -37,14 +33,14 @@ function ScenarioRecordCn() {
}
}, [recordStatus]);

return (
<>
{ activeKey && <ScenarioRecordImpl activeKey={activeKey} defaultApplyAll={recordStatus.all} recordHome={recordStatus.home} /> }
</>
);
if (!activeKey) return <></>;
if (language === 'zh') {
return <ScenarioRecordImplCn activeKey={activeKey} defaultApplyAll={recordStatus.all} recordHome={recordStatus.home} />;
}
return <ScenarioRecordImplEn activeKey={activeKey} defaultApplyAll={recordStatus.all} recordHome={recordStatus.home} />;
}

function ScenarioRecordImpl({activeKey, defaultApplyAll, recordHome}) {
function ScenarioRecordImplCn({activeKey, defaultApplyAll, recordHome}) {
const [recordAll, setRecordAll] = React.useState(defaultApplyAll);
const [recordFiles, setRecordFiles] = React.useState();
const [refreshNow, setRefreshNow] = React.useState();
Expand Down Expand Up @@ -206,7 +202,7 @@ function ScenarioRecordImpl({activeKey, defaultApplyAll, recordHome}) {
<td><a href={file.location} onClick={(e) => copyToClipboard(e, file.location)} target='_blank' rel='noreferrer'>复制</a></td>
<td>
<a href={file.preview} target='_blank' rel='noreferrer'>预览</a> &nbsp;
<PopoverConfirm trigger={ <a href={`#${file.uuid}`} hidden={file.progress}>删除</a> } onClick={() => removeRecord(file)}>
<PopoverConfirm placement='top' trigger={ <a href={`#${file.uuid}`} hidden={file.progress}>删除</a> } onClick={() => removeRecord(file)}>
<p>
{t('scenario.rmFileTip1')}
<span className='text-danger'><strong>
Expand All @@ -230,9 +226,189 @@ function ScenarioRecordImpl({activeKey, defaultApplyAll, recordHome}) {
);
}

function ScenarioRecordEn() {
function ScenarioRecordImplEn({activeKey, defaultApplyAll, recordHome}) {
const [recordAll, setRecordAll] = React.useState(defaultApplyAll);
const [recordFiles, setRecordFiles] = React.useState();
const [refreshNow, setRefreshNow] = React.useState();
const handleError = useErrorHandler();
const {t} = useTranslation();

React.useEffect(() => {
const refreshRecordFiles = () => {
const token = Token.load();
axios.post('/terraform/v1/hooks/record/files', {
...token,
}).then(res => {
console.log(`Record: Files ok, ${JSON.stringify(res.data.data)}`);
setRecordFiles(res.data.data.map(file => {
const l = window.location;
const schema = l.protocol.replace(':', '');
const httpPort = l.port || (l.protocol === 'http:' ? 80 : 443);
if (file.progress) {
file.location = `${l.protocol}//${l.host}/terraform/v1/hooks/record/hls/${file.uuid}.m3u8`;
file.preview = `/players/srs_player.html?schema=${schema}&port=${httpPort}&autostart=true&app=terraform/v1/hooks/record/hls&stream=${file.uuid}.m3u8`;
} else {
file.location = `${l.protocol}//${l.host}/terraform/v1/hooks/record/hls/${file.uuid}/index.mp4`;
file.preview = `/terraform/v1/hooks/record/hls/${file.uuid}/index.mp4`;
}

return {
...file,
url: StreamURL.build(file.vhost, file.app, file.stream),
update: moment(file.update),
duration: Number(file.duration),
size: Number(file.size / 1024.0 / 1024),
};
}).sort((a, b) => {
return b.update - a.update;
}).map((file, i) => {
return {...file, i: i + 1};
}));
}).catch(handleError);
};

refreshRecordFiles();
const timer = setInterval(() => refreshRecordFiles(), 10 * 1000);
return () => clearInterval(timer);
}, [handleError, refreshNow]);

const setupRecordPattern = React.useCallback((e, recordAll) => {
e.preventDefault();

const token = Token.load();
axios.post('/terraform/v1/hooks/record/apply', {
...token, all: !!recordAll,
}).then(res => {
alert('Setup OK');
console.log(`Record: Apply patterns ok, all=${recordAll}`);
}).catch(handleError);
}, [handleError]);

const removeRecord = React.useCallback((file) => {
const token = Token.load();
axios.post('/terraform/v1/hooks/record/remove', {
...token, uuid: file.uuid,
}).then(res => {
setRefreshNow(!refreshNow);
console.log(`Record: Remove file ok, file=${JSON.stringify(file)}`);
}).catch(handleError);
}, [refreshNow, handleError]);

const copyToClipboard = React.useCallback((e, text) => {
e.preventDefault();

Clipboard.copy(text).then(() => {
alert(t('helper.copyOk'));
}).catch((err) => {
alert(`${t('helper.copyFail')} ${err}`);
});
}, [t]);

return (
<span>On the way...</span>
<Accordion defaultActiveKey={activeKey}>
<Accordion.Item eventKey="0">
<Accordion.Header>Introduction</Accordion.Header>
<Accordion.Body>
<div>
Local recording refers to recording video streams to the local disk of the SRS Stack, and any stream pushed to the server can be recorded.
<p></p>
</div>
<p>Specific application scenarios include:</p>
<ul>
<li>Live to VOD, recording live streams into an HLS file, stored on the SRS Stack local disk, and can be downloaded</li>
</ul>
<p>Special attention:</p>
<ul>
<li>If there are many streams, the disk will be very busy, especially when using shared storage, so it is necessary to monitor disk IO and load.</li>
<li>Although the local disk is large enough, cloud storage is truly unlimited, while local disk space is actually limited, so it is necessary to monitor disk space.</li>
<li>Temporary management of local files, such as deletion and cleanup, is not supported.</li>
</ul>
<p>Instructions for use:</p>
<ul>
<li>For specific usage steps, please follow the guide below</li>
</ul>
</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="1">
<Accordion.Header>Record Directory</Accordion.Header>
<Accordion.Body>
Work Directory: <code>{recordHome}</code> &nbsp;
<div role='button' style={{display: 'inline-block'}} title='Copy'>
<Icon.Clipboard size={20} onClick={(e) => copyToClipboard(e, recordHome)} />
</div> &nbsp;
<TutorialsText prefixLine={true} title='How to modify the directory?'>
You can use a symbolic link to link this directory to a directory on another disk, and then <font color='red'>restart the service</font> (required).
</TutorialsText>
</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="2">
<Accordion.Header>Setup Record Rules</Accordion.Header>
<Accordion.Body>
<Form>
<Button variant="primary" type="submit" onClick={(e) => {
setRecordAll(!recordAll);
setupRecordPattern(e, !recordAll);
}}>
{recordAll ? 'Stop Record' : 'Start Record'}
</Button> &nbsp;
<Form.Text> * Record all streams</Form.Text>
</Form>
</Accordion.Body>
</Accordion.Item>
<Accordion.Item eventKey="3">
<Accordion.Header>Record Tasks</Accordion.Header>
<Accordion.Body>
{
recordFiles?.length ? (
<Table striped bordered hover>
<thead>
<tr>
<th>#</th>
<th>Status</th>
<th>Update</th>
<th>Source Stream</th>
<th>Duration</th>
<th>Size</th>
<th>Slices</th>
<th>URL</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
{
recordFiles?.map(file => {
return <tr key={file.uuid} name={file.uuid}>
<td>{file.i}</td>
<td title='If there is no stream for 300 seconds, the recording will be automatically completed.'>{file.progress ? 'Recording' : 'Done'}</td>
<td>{`${file.update.format('YYYY-MM-DD HH:mm:ss')}`}</td>
<td>{file.url}</td>
<td>{`${file.duration.toFixed(1)}`}s</td>
<td>{`${file.size.toFixed(1)}`}MB</td>
<td>{file.nn}</td>
<td><a href={file.location} onClick={(e) => copyToClipboard(e, file.location)} target='_blank' rel='noreferrer'>Copy</a></td>
<td>
<a href={file.preview} target='_blank' rel='noreferrer'>Preview</a> &nbsp;
<PopoverConfirm placement='top' trigger={ <a href={`#${file.uuid}`} hidden={file.progress}>Delete</a> } onClick={() => removeRecord(file)}>
<p>
{t('scenario.rmFileTip1')}
<span className='text-danger'><strong>
{t('scenario.rmFileTip2')}
</strong></span>
{t('scenario.rmFileTip3')}
</p>
</PopoverConfirm>
</td>
</tr>;
})
}
</tbody>
</Table>
) : ''
}
{!recordFiles?.length ? 'There is no stream. Please start the recording and push the stream, then wait for about 60 seconds, and the recording list will be automatically updated.' : ''}
</Accordion.Body>
</Accordion.Item>
</Accordion>
);
}

0 comments on commit 0e59076

Please sign in to comment.