- Create Dockerfile for Nginx with envsubst for dynamic configuration. - Add djmaster.conf.template for Nginx configuration with upstream services. - Implement docker-entrypoint.sh to substitute environment variables in the Nginx config. - Add README.md in nginx-examples for guidance on using the Nginx template. - Include djmaster.conf.template in nginx-examples for local setup. - Introduce utility functions for fetching YouTube video snippets and titles.
209 lines
6.4 KiB
JavaScript
209 lines
6.4 KiB
JavaScript
import React from 'react';
|
|
|
|
import { Trans } from '@lingui/macro';
|
|
import Grid from '@mui/material/Grid';
|
|
import Link from '@mui/material/Link';
|
|
import TextField from '@mui/material/TextField';
|
|
|
|
import Logo from './logos/rumble.svg';
|
|
import FormInlineButton from '../../../misc/FormInlineButton';
|
|
import InputAdornment from '@mui/material/InputAdornment';
|
|
import IconButton from '@mui/material/IconButton';
|
|
import Tooltip from '@mui/material/Tooltip';
|
|
import RestoreIcon from '@mui/icons-material/RestoreOutlined';
|
|
|
|
const id = 'rumble';
|
|
const name = 'Rumble';
|
|
const version = '1.0';
|
|
const stream_key_link = 'https://rumble.com/account/live-streaming';
|
|
const description = (
|
|
<Trans>
|
|
Transmit your Livestream to an Rumble RTMP service.{' '}
|
|
<Link color="secondary" target="_blank" href="https://rumblefaq.groovehq.com/help/search?keyword=rtmp">
|
|
Here{' '}
|
|
</Link>
|
|
you can find more details about the settings.
|
|
</Trans>
|
|
);
|
|
const image_copyright = <Trans>Please get in touch with the operator of the service and check what happens.</Trans>;
|
|
const author = {
|
|
creator: {
|
|
name: 'datarhei',
|
|
link: 'https://github.com/datarhei',
|
|
},
|
|
maintainer: {
|
|
name: 'datarhei',
|
|
link: 'https://github.com/datarhei',
|
|
},
|
|
};
|
|
const category = 'platform';
|
|
const requires = {
|
|
protocols: ['rtmp', 'rtmps'],
|
|
formats: ['flv'],
|
|
codecs: {
|
|
audio: ['aac', 'mp3'],
|
|
video: ['h264'],
|
|
},
|
|
};
|
|
|
|
function ServiceIcon(props) {
|
|
return <img src={Logo} alt="Rumble Logo" {...props} />;
|
|
}
|
|
|
|
function init(settings) {
|
|
const initSettings = {
|
|
server_url: '',
|
|
stream_key: '',
|
|
title: '',
|
|
description: '',
|
|
...settings,
|
|
};
|
|
|
|
return initSettings;
|
|
}
|
|
|
|
function Service(props) {
|
|
const settings = init(props.settings);
|
|
|
|
// Pre-fill title/description from channel metadata if not already set
|
|
if (!settings.title && props.metadata && props.metadata.name) {
|
|
settings.title = props.metadata.name;
|
|
}
|
|
if (!settings.description && props.metadata && props.metadata.description) {
|
|
settings.description = props.metadata.description;
|
|
}
|
|
|
|
const handleChange = (what) => (event) => {
|
|
const value = event.target.value;
|
|
settings[what] = value;
|
|
const output = createOutput(settings);
|
|
props.onChange([output], settings);
|
|
};
|
|
|
|
const pushSettings = () => {
|
|
props.onChange([createOutput(settings)], settings);
|
|
};
|
|
|
|
const createOutput = (settings) => {
|
|
const output = {
|
|
address: settings.server_url + '/' + settings.stream_key,
|
|
options: ['-f', 'flv'],
|
|
};
|
|
|
|
return output;
|
|
};
|
|
|
|
return (
|
|
<Grid container spacing={2}>
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
variant="outlined"
|
|
fullWidth
|
|
placeholder="rtmp://abc.live.rmbl.ws/slot-1234"
|
|
label={<Trans>Server URL</Trans>}
|
|
value={settings.server_url}
|
|
onChange={handleChange('server_url')}
|
|
error={settings.server_url.includes('rtmp://') ? false : true}
|
|
helperText={settings.server_url.includes('rtmp://') ? false : 'Please enter a valid RTMP URL.'}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12} md={9}>
|
|
<TextField
|
|
variant="outlined"
|
|
fullWidth
|
|
placeholder="abc123"
|
|
label={<Trans>Stream key</Trans>}
|
|
value={settings.stream_key}
|
|
onChange={handleChange('stream_key')}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12} md={3}>
|
|
<FormInlineButton target="blank" href={stream_key_link} component="a">
|
|
<Trans>GET</Trans>
|
|
</FormInlineButton>
|
|
</Grid>
|
|
{/* YouTube URL input removed — Reset will restore from stored metadata */}
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
variant="outlined"
|
|
fullWidth
|
|
label={<Trans>Stream title</Trans>}
|
|
placeholder={props.metadata && props.metadata.name ? props.metadata.name : ''}
|
|
value={settings.title}
|
|
onChange={handleChange('title')}
|
|
InputProps={props.metadata && props.metadata.name ? {
|
|
endAdornment: (
|
|
<InputAdornment position="end">
|
|
<Tooltip title={<Trans>Reset to stream title</Trans>}>
|
|
<IconButton size="small" onClick={async () => {
|
|
const meta = props.metadata || {};
|
|
const t = meta.name || meta.title || '';
|
|
const d = meta.description || '';
|
|
if (!t && !d) return;
|
|
if (t) settings.title = t;
|
|
if (d) settings.description = d;
|
|
pushSettings();
|
|
try {
|
|
if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') {
|
|
await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' });
|
|
}
|
|
} catch (err) { console.warn('[reset] failed to save metadata', err); }
|
|
}}>
|
|
<RestoreIcon fontSize="small" />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</InputAdornment>
|
|
),
|
|
} : undefined}
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12}>
|
|
<TextField
|
|
variant="outlined"
|
|
fullWidth
|
|
multiline
|
|
rows={3}
|
|
label={<Trans>Stream description</Trans>}
|
|
placeholder={props.metadata && props.metadata.description ? props.metadata.description : ''}
|
|
value={settings.description}
|
|
onChange={handleChange('description')}
|
|
InputProps={props.metadata && props.metadata.description ? {
|
|
endAdornment: (
|
|
<InputAdornment position="end" style={{ alignSelf: 'flex-start', marginTop: 10 }}>
|
|
<Tooltip title={<Trans>Reset to stream description</Trans>}>
|
|
<IconButton size="small" onClick={async () => {
|
|
const meta = props.metadata || {};
|
|
const t = meta.name || meta.title || '';
|
|
const d = meta.description || '';
|
|
if (!t && !d) return;
|
|
if (t) settings.title = t;
|
|
if (d) settings.description = d;
|
|
pushSettings();
|
|
try {
|
|
if (props.restreamer && typeof props.restreamer.SetEgressMetadata === 'function') {
|
|
await props.restreamer.SetEgressMetadata(props.channelId, props.publicationId, { name: settings.title || '', description: settings.description || '' });
|
|
}
|
|
} catch (err) { console.warn('[reset] failed to save metadata', err); }
|
|
}}>
|
|
<RestoreIcon fontSize="small" />
|
|
</IconButton>
|
|
</Tooltip>
|
|
</InputAdornment>
|
|
),
|
|
} : undefined}
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
}
|
|
|
|
Service.defaultProps = {
|
|
settings: {},
|
|
skills: {},
|
|
metadata: {},
|
|
streams: [],
|
|
onChange: function (output, settings) {},
|
|
};
|
|
|
|
export { id, name, version, stream_key_link, description, image_copyright, author, category, requires, ServiceIcon as icon, Service as component };
|