This commit is contained in:
Jan Stabenow 2024-11-11 11:06:05 +01:00
commit 4cb38c0a1d
23 changed files with 206 additions and 47 deletions

View File

@ -0,0 +1,31 @@
import Box from '@mui/material/Box';
import CircularProgress from '@mui/material/CircularProgress';
import Typography from '@mui/material/Typography';
export default function Component({ color = 'inherit', value = -1 }) {
if (value < 0) {
return <CircularProgress color={color} />;
}
return (
<Box sx={{ position: 'relative', display: 'inline-flex' }}>
<CircularProgress variant="determinate" value={value} color={color} />
<Box
sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Typography component="div" sx={{ color: 'text.secondary' }}>
{`${Math.round(value)}%`}
</Typography>
</Box>
</Box>
);
}

View File

@ -3,7 +3,7 @@ import React from 'react';
import Grid from '@mui/material/Grid';
import videojs from 'video.js';
import overlay from './videojs-overlay.es.js';
import './videojs-overlay.es.js';
import 'video.js/dist/video-js.min.css';
import './video-js-skin-internal.min.css';
import './video-js-skin-public.min.css';
@ -28,8 +28,6 @@ export default function VideoJS({ type = 'videojs-internal', options = {}, onRea
const videoElement = videoRef.current;
if (!videoElement) return;
videojs.registerPlugin('overlay', overlay);
const player = (playerRef.current = videojs(videoElement, options, () => {
onReady && onReady(player);
}));

View File

@ -63,7 +63,11 @@ export default function UploadButton({
return;
}
/*
let streamer = file.stream();
let reader = new FileReader();
// read as blob: https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
reader.readAsArrayBuffer(file);
reader.onloadend = async () => {
if (reader.result === null) {
@ -74,9 +78,11 @@ export default function UploadButton({
});
return;
}
onUpload(reader.result, type.extension, type.mimetype);
};
*/
// transformStream in order to count transferred bytes: https://stackoverflow.com/questions/35711724/upload-progress-indicators-for-fetch
// .pipeThrough(progressTrackingStream)
onUpload(file, type.extension, type.mimetype);
//};
};
onStart();

View File

@ -1,3 +1,5 @@
import { fetch } from './fetch';
class API {
constructor(address) {
this.base = '/api';
@ -277,12 +279,13 @@ class API {
return await this._HEAD('/v3/fs/disk' + path);
}
async DataPutFile(path, data) {
async DataPutFile(path, data, onprogress = null) {
return await this._PUT('/v3/fs/disk' + path, {
headers: {
'Content-Type': 'application/data',
},
body: data,
onprogress: onprogress,
});
}

81
src/utils/fetch.js Normal file
View File

@ -0,0 +1,81 @@
const fetch = async (url, options = {}) => {
options = {
method: 'GET',
...options,
};
options.method = options.method.toUpperCase();
const xhr = new XMLHttpRequest();
return new Promise((resolve, reject) => {
xhr.responseType = 'text';
xhr.onload = () => {
const response = {
ok: false,
headers: {
get: function (key) {
return this.data.get(key.toLowerCase());
},
data: xhr
.getAllResponseHeaders()
.split('\r\n')
.reduce((result, current) => {
let [name, value] = current.split(': ');
result.set(name, value);
return result;
}, new Map()),
},
status: xhr.status,
statusText: xhr.statusText,
data: xhr.response,
json: function () {
return JSON.parse(this.data);
},
text: function () {
return this.data;
},
};
if (xhr.status < 200 || xhr.status >= 300) {
resolve(response);
} else {
response.ok = true;
resolve(response);
}
};
xhr.onerror = () => {
reject({
message: 'network error',
});
};
if ('onprogress' in options && typeof options.onprogress == 'function') {
const tracker = (event) => {
if (!event.lengthComputable) {
options.onprogress(false, 0, event.loaded);
return;
}
options.onprogress(true, event.loaded / event.total, event.total);
};
if (options.method === 'GET') {
xhr.onprogress = tracker;
} else if (options.method === 'PUT' || options.method === 'POST') {
xhr.upload.onprogress = tracker;
}
}
xhr.open(options.method, url, true);
if ('headers' in options) {
for (const header in options.headers) {
xhr.setRequestHeader(header, options.headers[header]);
}
}
if ('body' in options) {
xhr.send(options.body);
} else {
xhr.send();
}
});
};
export { fetch };

View File

@ -2290,7 +2290,7 @@ class Restreamer {
}
// Upload channel specific channel data
async UploadData(channelid, name, data) {
async UploadData(channelid, name, data, onprogress = null) {
if (channelid.length === 0) {
channelid = this.GetCurrentChannelID();
}
@ -2305,7 +2305,7 @@ class Restreamer {
const path = `/channels/${channel.channelid}/${name}`;
await this._uploadAssetData(path, data);
await this._uploadAssetData(path, data, onprogress);
return path;
}
@ -3358,8 +3358,8 @@ class Restreamer {
return true;
}
async _uploadAssetData(remotePath, data) {
await this._call(this.api.DataPutFile, remotePath, data);
async _uploadAssetData(remotePath, data, onprogress = null) {
await this._call(this.api.DataPutFile, remotePath, data, onprogress);
return true;
}

View File

@ -1,7 +1,7 @@
import pkg from '../package.json';
const Core = '^16.11.0';
const FFmpeg = '^5.1.0 || ^6.1.0';
const FFmpeg = '^5.1.0 || ^6.1.0 || ^7.0.0';
const UI = pkg.bundle ? pkg.bundle : pkg.name + ' v' + pkg.version;
const Version = pkg.version;

View File

@ -234,8 +234,8 @@ export default function Profile({
setSkillsRefresh(false);
};
const handleStore = async (name, data) => {
return await onStore(name, data);
const handleStore = async (name, data, onprogress) => {
return await onStore(name, data, onprogress);
};
const handleEncoding = (type) => (encoder, decoder) => {

View File

@ -78,8 +78,8 @@ export default function SourceSelect({
await onRefresh();
};
const handleStore = async (name, data) => {
return await onStore(name, data);
const handleStore = async (name, data, onprogress) => {
return await onStore(name, data, onprogress);
};
const handleProbe = async (settings, inputs) => {

View File

@ -156,7 +156,7 @@ function SourceIcon(props) {
const id = 'alsa';
const name = <Trans>ALSA</Trans>;
const capabilities = ['audio'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -221,7 +221,7 @@ function SourceIcon(props) {
const id = 'avfoundation';
const name = <Trans>AVFoundation</Trans>;
const capabilities = ['audio', 'video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -4,12 +4,12 @@ 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 CircularProgress from '../../../misc/CircularProgress';
import Dialog from '../../../misc/modals/Dialog';
import Filesize from '../../../misc/Filesize';
import FormInlineButton from '../../../misc/FormInlineButton';
@ -63,7 +63,10 @@ function Source({
const classes = useStyles();
settings = initSettings(settings);
const [$saving, setSaving] = React.useState(false);
const [$progress, setProgress] = React.useState({
enable: false,
value: -1,
});
const [$error, setError] = React.useState({
open: false,
title: '',
@ -71,7 +74,15 @@ function Source({
});
const handleFileUpload = async (data, extension, mimetype) => {
const path = await onStore('audioloop.source', data);
const path = await onStore('audioloop.source', data, (computable, progress, total) => {
setProgress((current) => {
return {
...current,
enable: true,
value: computable ? progress * 100 : -1,
};
});
});
onChange({
...settings,
@ -79,11 +90,17 @@ function Source({
mimetype: mimetype,
});
setSaving(false);
setProgress({
...$progress,
enable: false,
});
};
const handleUploadStart = () => {
setSaving(true);
setProgress({
...$progress,
enable: true,
});
};
const handleUploadError = (title) => (err) => {
@ -115,7 +132,10 @@ function Source({
message = <Trans>Unknown upload error</Trans>;
}
setSaving(false);
setProgress({
...$progress,
enable: false,
});
showUploadError(title, message);
};
@ -166,8 +186,8 @@ function Source({
</FormInlineButton>
</Grid>
</Grid>
<Backdrop open={$saving}>
<CircularProgress color="inherit" />
<Backdrop open={$progress.enable}>
<CircularProgress color="inherit" value={$progress.value} />
</Backdrop>
<Dialog
open={$error.open}
@ -192,7 +212,7 @@ function SourceIcon(props) {
const id = 'audioloop';
const name = <Trans>Loop</Trans>;
const capabilities = ['audio'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -130,7 +130,7 @@ function SourceIcon(props) {
const id = 'fbdev';
const name = <Trans>Framebuffer</Trans>;
const capabilities = ['video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -1229,7 +1229,7 @@ function SourceIcon(props) {
const id = 'network';
const name = <Trans>Network source</Trans>;
const capabilities = ['audio', 'video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -37,7 +37,7 @@ function SourceIcon(props) {
const id = 'noaudio';
const name = <Trans>No audio</Trans>;
const capabilities = ['audio'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -136,7 +136,7 @@ function SourceIcon(props) {
const id = 'raspicam';
const name = <Trans>Raspberry Pi camera</Trans>;
const capabilities = ['video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -193,7 +193,7 @@ function SourceIcon(props) {
const id = 'sdp';
const name = <Trans>SDP</Trans>;
const capabilities = ['video', 'audio'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -145,7 +145,7 @@ function SourceIcon(props) {
const id = 'video4linux2';
const name = <Trans>Hardware device</Trans>;
const capabilities = ['video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -37,7 +37,7 @@ function SourceIcon(props) {
const id = 'videoaudio';
const name = <Trans>Video source</Trans>;
const capabilities = ['audio'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -4,12 +4,12 @@ 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 CircularProgress from '../../../misc/CircularProgress';
import Dialog from '../../../misc/modals/Dialog';
import Filesize from '../../../misc/Filesize';
import FormInlineButton from '../../../misc/FormInlineButton';
@ -71,7 +71,10 @@ function Source({
const classes = useStyles();
settings = initSettings(settings);
const [$saving, setSaving] = React.useState(false);
const [$progress, setProgress] = React.useState({
enable: false,
value: -1,
});
const [$error, setError] = React.useState({
open: false,
title: '',
@ -79,7 +82,15 @@ function Source({
});
const handleFileUpload = async (data, extension, mimetype) => {
const path = await onStore('videoloop.source', data);
const path = await onStore('videoloop.source', data, (computable, progress, total) => {
setProgress((current) => {
return {
...current,
enable: true,
value: computable ? progress * 100 : -1,
};
});
});
onChange({
...settings,
@ -87,11 +98,17 @@ function Source({
mimetype: mimetype,
});
setSaving(false);
setProgress({
...$progress,
enable: false,
});
};
const handleUploadStart = () => {
setSaving(true);
setProgress({
...$progress,
enable: true,
});
};
const handleUploadError = (title) => (err) => {
@ -123,7 +140,10 @@ function Source({
message = <Trans>Unknown upload error</Trans>;
}
setSaving(false);
setProgress({
...$progress,
enable: false,
});
showUploadError(title, message);
};
@ -174,8 +194,8 @@ function Source({
</FormInlineButton>
</Grid>
</Grid>
<Backdrop open={$saving}>
<CircularProgress color="inherit" />
<Backdrop open={$progress.enable}>
<CircularProgress color="inherit" value={$progress.value} />
</Backdrop>
<Dialog
open={$error.open}
@ -200,7 +220,7 @@ function SourceIcon(props) {
const id = 'videoloop';
const name = <Trans>Loop</Trans>;
const capabilities = ['video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -180,7 +180,7 @@ function SourceIcon(props) {
const id = 'virtualaudio';
const name = <Trans>Virtual source</Trans>;
const capabilities = ['audio'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -199,7 +199,7 @@ function SourceIcon(props) {
const id = 'virtualvideo';
const name = <Trans>Virtual source</Trans>;
const capabilities = ['video'];
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0';
const ffversion = '^4.1.0 || ^5.0.0 || ^6.1.0 || ^7.0.0';
const func = {
initSettings,

View File

@ -211,8 +211,8 @@ export default function Edit({ restreamer = null }) {
setSkills(skills);
};
const handleSourceStore = async (name, data) => {
return await restreamer.UploadData('', name, data);
const handleSourceStore = async (name, data, onprogress) => {
return await restreamer.UploadData('', name, data, onprogress);
};
const handleSourceProbe = async (inputs) => {