Add audio loop source
This commit is contained in:
parent
c1f9b95a08
commit
4854c63fb1
@ -508,6 +508,7 @@ class Restreamer {
|
||||
virtualaudio: [],
|
||||
virtualvideo: [],
|
||||
videoloop: [],
|
||||
audioloop: [],
|
||||
},
|
||||
sinks: {},
|
||||
};
|
||||
|
||||
203
src/views/Edit/Sources/AudioLoop.js
Normal file
203
src/views/Edit/Sources/AudioLoop.js
Normal file
@ -0,0 +1,203 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import Backdrop from '@mui/material/Backdrop';
|
||||
import Button from '@mui/material/Button';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Icon from '@mui/icons-material/Cached';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
import Dialog from '../../../misc/modals/Dialog';
|
||||
import Filesize from '../../../misc/Filesize';
|
||||
import FormInlineButton from '../../../misc/FormInlineButton';
|
||||
import UploadButton from '../../../misc/UploadButton';
|
||||
|
||||
const imageTypes = [{ mimetype: 'audio/*', extension: 'audio', maxSize: 25 * 1024 * 1024 }];
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
gridContainer: {
|
||||
marginTop: '0.5em',
|
||||
},
|
||||
}));
|
||||
|
||||
const initSettings = (initialSettings) => {
|
||||
if (!initialSettings) {
|
||||
initialSettings = {};
|
||||
}
|
||||
|
||||
const settings = {
|
||||
address: '',
|
||||
mimetype: '',
|
||||
...initialSettings,
|
||||
};
|
||||
|
||||
return settings;
|
||||
};
|
||||
|
||||
const createInputs = (settings) => {
|
||||
const address = '{diskfs}' + settings.address;
|
||||
const input = {
|
||||
address: address,
|
||||
options: [],
|
||||
};
|
||||
|
||||
input.options.push('-stream_loop', '-1');
|
||||
input.options.push('-re');
|
||||
|
||||
return [input];
|
||||
};
|
||||
|
||||
function Source(props) {
|
||||
const classes = useStyles();
|
||||
const settings = initSettings(props.settings);
|
||||
const [$saving, setSaving] = React.useState(false);
|
||||
const [$error, setError] = React.useState({
|
||||
open: false,
|
||||
title: '',
|
||||
message: '',
|
||||
});
|
||||
|
||||
const handleFileUpload = async (data, extension, mimetype) => {
|
||||
const path = await props.onStore('audioloop.source', data);
|
||||
|
||||
props.onChange({
|
||||
...settings,
|
||||
address: path,
|
||||
mimetype: mimetype,
|
||||
});
|
||||
|
||||
setSaving(false);
|
||||
};
|
||||
|
||||
const handleUploadStart = () => {
|
||||
setSaving(true);
|
||||
};
|
||||
|
||||
const handleUploadError = (title) => (err) => {
|
||||
let message = null;
|
||||
|
||||
switch (err.type) {
|
||||
case 'nofiles':
|
||||
message = <Trans>Please select a file to upload.</Trans>;
|
||||
break;
|
||||
case 'mimetype':
|
||||
message = (
|
||||
<Trans>
|
||||
The selected file type ({err.actual}) is not allowed. Allowed file types are {err.allowed.join(', ')}
|
||||
</Trans>
|
||||
);
|
||||
break;
|
||||
case 'size':
|
||||
message = (
|
||||
<Trans>
|
||||
The selected file is too big (<Filesize bytes={err.actual} />
|
||||
). Only <Filesize bytes={err.allowed} /> are allowed.
|
||||
</Trans>
|
||||
);
|
||||
break;
|
||||
case 'read':
|
||||
message = <Trans>There was an error during upload: {err.message}</Trans>;
|
||||
break;
|
||||
default:
|
||||
message = <Trans>Unknown upload error</Trans>;
|
||||
}
|
||||
|
||||
setSaving(false);
|
||||
|
||||
showUploadError(title, message);
|
||||
};
|
||||
|
||||
const showUploadError = (title, message) => {
|
||||
setError({
|
||||
...$error,
|
||||
open: true,
|
||||
title: title,
|
||||
message: message,
|
||||
});
|
||||
};
|
||||
|
||||
const hideUploadError = () => {
|
||||
setError({
|
||||
...$error,
|
||||
open: false,
|
||||
});
|
||||
};
|
||||
|
||||
const handleProbe = () => {
|
||||
props.onProbe(settings, createInputs(settings));
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid container alignItems="flex-start" spacing={2} className={classes.gridContainer}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="caption">
|
||||
<Trans>Upload an audio file ({imageTypes.map((t) => t.mimetype).join(', ')}) in order to loop it.</Trans>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={9}>
|
||||
<TextField variant="outlined" fullWidth label={<Trans>File path</Trans>} value={settings.address} readOnly />
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3}>
|
||||
<UploadButton
|
||||
label={<Trans>Upload</Trans>}
|
||||
acceptTypes={imageTypes}
|
||||
onStart={handleUploadStart}
|
||||
onError={handleUploadError(<Trans>Uploading the file failed</Trans>)}
|
||||
onUpload={handleFileUpload}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormInlineButton onClick={handleProbe} disabled={!settings.address.length}>
|
||||
<Trans>Probe</Trans>
|
||||
</FormInlineButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Backdrop open={$saving}>
|
||||
<CircularProgress color="inherit" />
|
||||
</Backdrop>
|
||||
<Dialog
|
||||
open={$error.open}
|
||||
title={$error.title}
|
||||
onClose={hideUploadError}
|
||||
buttonsRight={
|
||||
<Button variant="outlined" color="primary" onClick={hideUploadError}>
|
||||
<Trans>OK</Trans>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Typography variant="body1">{$error.message}</Typography>
|
||||
</Dialog>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
Source.defaultProps = {
|
||||
knownDevices: [],
|
||||
settings: {},
|
||||
onChange: function (settings) {},
|
||||
onProbe: function (settings, inputs) {},
|
||||
onRefresh: function () {},
|
||||
onStore: function (name, data) {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
function SourceIcon(props) {
|
||||
return <Icon style={{ color: '#FFF' }} {...props} />;
|
||||
}
|
||||
|
||||
const id = 'audioloop';
|
||||
const name = <Trans>Loop</Trans>;
|
||||
const capabilities = ['audio'];
|
||||
const ffversion = '^4.1.0 || ^5.0.0';
|
||||
|
||||
const func = {
|
||||
initSettings,
|
||||
createInputs,
|
||||
};
|
||||
|
||||
export { id, name, capabilities, ffversion, SourceIcon as icon, Source as component, func };
|
||||
@ -69,7 +69,7 @@ function Source(props) {
|
||||
});
|
||||
|
||||
const handleFileUpload = async (data, extension, mimetype) => {
|
||||
const path = await props.onStore('loop.source', data);
|
||||
const path = await props.onStore('videoloop.source', data);
|
||||
|
||||
props.onChange({
|
||||
...settings,
|
||||
|
||||
@ -6,6 +6,7 @@ import * as Raspicam from './Raspicam';
|
||||
import * as Video4Linux from './V4L';
|
||||
import * as VideoAudio from './VideoAudio';
|
||||
import * as VideoLoop from './VideoLoop';
|
||||
import * as AudioLoop from './AudioLoop';
|
||||
import * as VirtualAudio from './VirtualAudio';
|
||||
import * as VirtualVideo from './VirtualVideo';
|
||||
|
||||
@ -48,5 +49,6 @@ registry.Register(VirtualVideo);
|
||||
registry.Register(NoAudio);
|
||||
registry.Register(VideoAudio);
|
||||
registry.Register(VideoLoop);
|
||||
registry.Register(AudioLoop);
|
||||
|
||||
export default registry;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user