Cesar Mendivil bc97ee0a68 feat(nginx): add Docker setup for Nginx with templated configuration
- 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.
2026-03-18 10:35:28 -07:00

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 };