Pulling down dev branch
This commit is contained in:
commit
343ce32561
18
CHANGELOG.md
18
CHANGELOG.md
@ -1,8 +1,24 @@
|
||||
# Restreamer-UI
|
||||
|
||||
## v1.14.0 > v1.15.0
|
||||
|
||||
- Add EarthCam publication service
|
||||
- Add other RTSP transport modes
|
||||
- Add X11grab
|
||||
- Add SDP support ([PR-#47](https://github.com/datarhei/restreamer-ui/pull/47)) (thx patcarter883)
|
||||
- Add support for AV1 decoding ([PR-#46](https://github.com/datarhei/restreamer-ui/pull/46)) (thx patcarter883)
|
||||
- Add upload progress for audio/video loop files
|
||||
- Add support for custom HTTP headers
|
||||
- Add support for FFmpeg 7
|
||||
- Fix wetter.com category
|
||||
- Fix chromecast ([PR-#73](https://github.com/datarhei/restreamer-ui/pull/73)) (thx badincite)
|
||||
- Fix [#819](https://github.com/datarhei/restreamer#819)
|
||||
- Fix [#819](https://github.com/datarhei/restreamer#825)
|
||||
- Remove file size limit for loop file uploads
|
||||
|
||||
## v1.13.0 > v1.14.0
|
||||
|
||||
- Add wettercom service
|
||||
- Add wetter.com service
|
||||
- Add option to select which channels will be displayed on the playersite ([#392](https://github.com/datarhei/restreamer/issues/392), [#800](https://github.com/datarhei/restreamer/issues/800))
|
||||
- Mod updates public videojs >v8
|
||||
- Fix erroneous filter setting
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"@auth0/auth0-spa-js": "^2.1.3",
|
||||
"@babel/plugin-syntax-flow": "^7.24.7",
|
||||
"@babel/plugin-transform-react-jsx": "^7.25.2",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/dosis": "^5.0.21",
|
||||
@ -47,7 +48,7 @@
|
||||
"util": "^0.12.5",
|
||||
"uuid": "^10.0.0",
|
||||
"video.js": "^8.17.3",
|
||||
"videojs-overlay": "^3.1.0"
|
||||
"videojs-overlay": "^4.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
758
public/_player/videojs/dist/videojs-overlay.js
vendored
758
public/_player/videojs/dist/videojs-overlay.js
vendored
@ -1,386 +1,420 @@
|
||||
/*! @name videojs-overlay @version 2.1.5 @license Apache-2.0 */
|
||||
'use strict';
|
||||
/*! @name videojs-overlay @version 4.0.0 @license Apache-2.0 */
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
|
||||
typeof define === 'function' && define.amd ? define(['video.js'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojsOverlay = factory(global.videojs));
|
||||
})(this, (function (videojs) { 'use strict';
|
||||
|
||||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
||||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
||||
|
||||
var videojs = _interopDefault(require('video.js'));
|
||||
var window = _interopDefault(require('global/window'));
|
||||
var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs);
|
||||
|
||||
function _inheritsLoose(subClass, superClass) {
|
||||
subClass.prototype = Object.create(superClass.prototype);
|
||||
subClass.prototype.constructor = subClass;
|
||||
subClass.__proto__ = superClass;
|
||||
}
|
||||
const initOverlayComponent = videojs => {
|
||||
const Component = videojs.getComponent('Component');
|
||||
const dom = videojs.dom || videojs;
|
||||
|
||||
function _assertThisInitialized(self) {
|
||||
if (self === void 0) {
|
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
||||
}
|
||||
/**
|
||||
* Whether the value is a `Number`.
|
||||
*
|
||||
* Both `Infinity` and `-Infinity` are accepted, but `NaN` is not.
|
||||
*
|
||||
* @param {Number} n
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
return self;
|
||||
}
|
||||
/* eslint-disable no-self-compare */
|
||||
const isNumber = n => typeof n === 'number' && n === n;
|
||||
/* eslint-enable no-self-compare */
|
||||
|
||||
var version = "2.1.5";
|
||||
/**
|
||||
* Whether a value is a string with no whitespace.
|
||||
*
|
||||
* @param {string} s
|
||||
* @return {boolean}
|
||||
*/
|
||||
const hasNoWhitespace = s => typeof s === 'string' && /^\S+$/.test(s);
|
||||
|
||||
var defaults = {
|
||||
align: 'top-left',
|
||||
class: '',
|
||||
content: 'This overlay will show up while the video is playing',
|
||||
debug: false,
|
||||
showBackground: true,
|
||||
attachToControlBar: false,
|
||||
overlays: [{
|
||||
start: 'playing',
|
||||
end: 'paused'
|
||||
}]
|
||||
};
|
||||
var Component = videojs.getComponent('Component');
|
||||
var dom = videojs.dom || videojs;
|
||||
var registerPlugin = videojs.registerPlugin || videojs.plugin;
|
||||
/**
|
||||
* Whether the value is a `Number`.
|
||||
*
|
||||
* Both `Infinity` and `-Infinity` are accepted, but `NaN` is not.
|
||||
*
|
||||
* @param {Number} n
|
||||
* @return {Boolean}
|
||||
*/
|
||||
/**
|
||||
* Overlay component.
|
||||
*
|
||||
* @class Overlay
|
||||
* @extends {videojs.Component}
|
||||
*/
|
||||
class Overlay extends Component {
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
['start', 'end'].forEach(key => {
|
||||
const value = this.options_[key];
|
||||
if (isNumber(value)) {
|
||||
this[key + 'Event_'] = 'timeupdate';
|
||||
} else if (hasNoWhitespace(value)) {
|
||||
this[key + 'Event_'] = value;
|
||||
|
||||
/* eslint-disable no-self-compare */
|
||||
// An overlay MUST have a start option. Otherwise, it's pointless.
|
||||
} else if (key === 'start') {
|
||||
throw new Error('invalid "start" option; expected number or string');
|
||||
}
|
||||
});
|
||||
|
||||
var isNumber = function isNumber(n) {
|
||||
return typeof n === 'number' && n === n;
|
||||
};
|
||||
/* eslint-enable no-self-compare */
|
||||
// video.js does not like components with multiple instances binding
|
||||
// events to the player because it tracks them at the player level,
|
||||
// not at the level of the object doing the binding. This could also be
|
||||
// solved with Function.prototype.bind (but not videojs.bind because of
|
||||
// its GUID magic), but the anonymous function approach avoids any issues
|
||||
// caused by crappy libraries clobbering Function.prototype.bind.
|
||||
// - https://github.com/videojs/video.js/issues/3097
|
||||
['endListener_', 'rewindListener_', 'startListener_'].forEach(name => {
|
||||
this[name] = e => Overlay.prototype[name].call(this, e);
|
||||
});
|
||||
|
||||
/**
|
||||
* Whether a value is a string with no whitespace.
|
||||
*
|
||||
* @param {String} s
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
|
||||
var hasNoWhitespace = function hasNoWhitespace(s) {
|
||||
return typeof s === 'string' && /^\S+$/.test(s);
|
||||
};
|
||||
/**
|
||||
* Overlay component.
|
||||
*
|
||||
* @class Overlay
|
||||
* @extends {videojs.Component}
|
||||
*/
|
||||
|
||||
|
||||
var Overlay =
|
||||
/*#__PURE__*/
|
||||
function (_Component) {
|
||||
_inheritsLoose(Overlay, _Component);
|
||||
|
||||
function Overlay(player, options) {
|
||||
var _this;
|
||||
|
||||
_this = _Component.call(this, player, options) || this;
|
||||
['start', 'end'].forEach(function (key) {
|
||||
var value = _this.options_[key];
|
||||
|
||||
if (isNumber(value)) {
|
||||
_this[key + 'Event_'] = 'timeupdate';
|
||||
} else if (hasNoWhitespace(value)) {
|
||||
_this[key + 'Event_'] = value; // An overlay MUST have a start option. Otherwise, it's pointless.
|
||||
} else if (key === 'start') {
|
||||
throw new Error('invalid "start" option; expected number or string');
|
||||
}
|
||||
}); // video.js does not like components with multiple instances binding
|
||||
// events to the player because it tracks them at the player level,
|
||||
// not at the level of the object doing the binding. This could also be
|
||||
// solved with Function.prototype.bind (but not videojs.bind because of
|
||||
// its GUID magic), but the anonymous function approach avoids any issues
|
||||
// caused by crappy libraries clobbering Function.prototype.bind.
|
||||
// - https://github.com/videojs/video.js/issues/3097
|
||||
|
||||
['endListener_', 'rewindListener_', 'startListener_'].forEach(function (name$$1) {
|
||||
_this[name$$1] = function (e) {
|
||||
return Overlay.prototype[name$$1].call(_assertThisInitialized(_assertThisInitialized(_this)), e);
|
||||
};
|
||||
}); // If the start event is a timeupdate, we need to watch for rewinds (i.e.,
|
||||
// when the user seeks backward).
|
||||
|
||||
if (_this.startEvent_ === 'timeupdate') {
|
||||
_this.on(player, 'timeupdate', _this.rewindListener_);
|
||||
}
|
||||
|
||||
_this.debug("created, listening to \"" + _this.startEvent_ + "\" for \"start\" and \"" + (_this.endEvent_ || 'nothing') + "\" for \"end\"");
|
||||
|
||||
_this.hide();
|
||||
|
||||
return _this;
|
||||
}
|
||||
|
||||
var _proto = Overlay.prototype;
|
||||
|
||||
_proto.createEl = function createEl() {
|
||||
var options = this.options_;
|
||||
var content = options.content;
|
||||
var background = options.showBackground ? 'vjs-overlay-background' : 'vjs-overlay-no-background';
|
||||
var el = dom.createEl('div', {
|
||||
className: "\n vjs-overlay\n vjs-overlay-" + options.align + "\n " + options.class + "\n " + background + "\n vjs-hidden\n "
|
||||
});
|
||||
|
||||
if (typeof content === 'string') {
|
||||
el.innerHTML = content;
|
||||
} else if (content instanceof window.DocumentFragment) {
|
||||
el.appendChild(content);
|
||||
} else {
|
||||
dom.appendContent(el, content);
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
/**
|
||||
* Logs debug errors
|
||||
* @param {...[type]} args [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
|
||||
|
||||
_proto.debug = function debug() {
|
||||
if (!this.options_.debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
var log = videojs.log;
|
||||
var fn = log; // Support `videojs.log.foo` calls.
|
||||
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
if (log.hasOwnProperty(args[0]) && typeof log[args[0]] === 'function') {
|
||||
fn = log[args.shift()];
|
||||
}
|
||||
|
||||
fn.apply(void 0, ["overlay#" + this.id() + ": "].concat(args));
|
||||
};
|
||||
/**
|
||||
* Overrides the inherited method to perform some event binding
|
||||
*
|
||||
* @return {Overlay}
|
||||
*/
|
||||
|
||||
|
||||
_proto.hide = function hide() {
|
||||
_Component.prototype.hide.call(this);
|
||||
|
||||
this.debug('hidden');
|
||||
this.debug("bound `startListener_` to \"" + this.startEvent_ + "\""); // Overlays without an "end" are valid.
|
||||
|
||||
if (this.endEvent_) {
|
||||
this.debug("unbound `endListener_` from \"" + this.endEvent_ + "\"");
|
||||
this.off(this.player(), this.endEvent_, this.endListener_);
|
||||
}
|
||||
|
||||
this.on(this.player(), this.startEvent_, this.startListener_);
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* Determine whether or not the overlay should hide.
|
||||
*
|
||||
* @param {Number} time
|
||||
* The current time reported by the player.
|
||||
* @param {String} type
|
||||
* An event type.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
|
||||
_proto.shouldHide_ = function shouldHide_(time, type) {
|
||||
var end = this.options_.end;
|
||||
return isNumber(end) ? time >= end : end === type;
|
||||
};
|
||||
/**
|
||||
* Overrides the inherited method to perform some event binding
|
||||
*
|
||||
* @return {Overlay}
|
||||
*/
|
||||
|
||||
|
||||
_proto.show = function show() {
|
||||
_Component.prototype.show.call(this);
|
||||
|
||||
this.off(this.player(), this.startEvent_, this.startListener_);
|
||||
this.debug('shown');
|
||||
this.debug("unbound `startListener_` from \"" + this.startEvent_ + "\""); // Overlays without an "end" are valid.
|
||||
|
||||
if (this.endEvent_) {
|
||||
this.debug("bound `endListener_` to \"" + this.endEvent_ + "\"");
|
||||
this.on(this.player(), this.endEvent_, this.endListener_);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
/**
|
||||
* Determine whether or not the overlay should show.
|
||||
*
|
||||
* @param {Number} time
|
||||
* The current time reported by the player.
|
||||
* @param {String} type
|
||||
* An event type.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
|
||||
_proto.shouldShow_ = function shouldShow_(time, type) {
|
||||
var start = this.options_.start;
|
||||
var end = this.options_.end;
|
||||
|
||||
if (isNumber(start)) {
|
||||
if (isNumber(end)) {
|
||||
return time >= start && time < end; // In this case, the start is a number and the end is a string. We need
|
||||
// to check whether or not the overlay has shown since the last seek.
|
||||
} else if (!this.hasShownSinceSeek_) {
|
||||
this.hasShownSinceSeek_ = true;
|
||||
return time >= start;
|
||||
} // In this case, the start is a number and the end is a string, but
|
||||
// the overlay has shown since the last seek. This means that we need
|
||||
// to be sure we aren't re-showing it at a later time than it is
|
||||
// scheduled to appear.
|
||||
|
||||
|
||||
return Math.floor(time) === start;
|
||||
}
|
||||
|
||||
return start === type;
|
||||
};
|
||||
/**
|
||||
* Event listener that can trigger the overlay to show.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
|
||||
|
||||
_proto.startListener_ = function startListener_(e) {
|
||||
var time = this.player().currentTime();
|
||||
|
||||
if (this.shouldShow_(time, e.type)) {
|
||||
this.show();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Event listener that can trigger the overlay to show.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
|
||||
|
||||
_proto.endListener_ = function endListener_(e) {
|
||||
var time = this.player().currentTime();
|
||||
|
||||
if (this.shouldHide_(time, e.type)) {
|
||||
this.hide();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Event listener that can looks for rewinds - that is, backward seeks
|
||||
* and may hide the overlay as needed.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
|
||||
|
||||
_proto.rewindListener_ = function rewindListener_(e) {
|
||||
var time = this.player().currentTime();
|
||||
var previous = this.previousTime_;
|
||||
var start = this.options_.start;
|
||||
var end = this.options_.end; // Did we seek backward?
|
||||
|
||||
if (time < previous) {
|
||||
this.debug('rewind detected'); // The overlay remains visible if two conditions are met: the end value
|
||||
// MUST be an integer and the the current time indicates that the
|
||||
// overlay should NOT be visible.
|
||||
|
||||
if (isNumber(end) && !this.shouldShow_(time)) {
|
||||
this.debug("hiding; " + end + " is an integer and overlay should not show at this time");
|
||||
this.hasShownSinceSeek_ = false;
|
||||
this.hide(); // If the end value is an event name, we cannot reliably decide if the
|
||||
// overlay should still be displayed based solely on time; so, we can
|
||||
// only queue it up for showing if the seek took us to a point before
|
||||
// the start time.
|
||||
} else if (hasNoWhitespace(end) && time < start) {
|
||||
this.debug("hiding; show point (" + start + ") is before now (" + time + ") and end point (" + end + ") is an event");
|
||||
this.hasShownSinceSeek_ = false;
|
||||
// If the start event is a timeupdate, we need to watch for rewinds (i.e.,
|
||||
// when the user seeks backward).
|
||||
if (this.startEvent_ === 'timeupdate') {
|
||||
this.on(player, 'timeupdate', this.rewindListener_);
|
||||
}
|
||||
this.debug(`created, listening to "${this.startEvent_}" for "start" and "${this.endEvent_ || 'nothing'}" for "end"`);
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
createEl() {
|
||||
const options = this.options_;
|
||||
const content = options.content;
|
||||
const background = options.showBackground ? 'vjs-overlay-background' : 'vjs-overlay-no-background';
|
||||
const el = dom.createEl('div', {
|
||||
className: `
|
||||
vjs-overlay
|
||||
vjs-overlay-${options.align}
|
||||
${options.class}
|
||||
${background}
|
||||
vjs-hidden
|
||||
`
|
||||
});
|
||||
if (typeof content === 'string') {
|
||||
el.innerHTML = content;
|
||||
} else if (content instanceof window.DocumentFragment) {
|
||||
el.appendChild(content);
|
||||
} else {
|
||||
dom.appendContent(el, content);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
this.previousTime_ = time;
|
||||
/**
|
||||
* Logs debug errors
|
||||
*
|
||||
* @param {...[type]} args [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
debug(...args) {
|
||||
if (!this.options_.debug) {
|
||||
return;
|
||||
}
|
||||
const log = videojs.log;
|
||||
let fn = log;
|
||||
|
||||
// Support `videojs.log.foo` calls.
|
||||
if (log.hasOwnProperty(args[0]) && typeof log[args[0]] === 'function') {
|
||||
fn = log[args.shift()];
|
||||
}
|
||||
fn(...[`overlay#${this.id()}: `, ...args]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the inherited method to perform some event binding
|
||||
*
|
||||
* @return {Overlay}
|
||||
*/
|
||||
hide() {
|
||||
super.hide();
|
||||
this.debug('hidden');
|
||||
this.debug(`bound \`startListener_\` to "${this.startEvent_}"`);
|
||||
|
||||
// Overlays without an "end" are valid.
|
||||
if (this.endEvent_) {
|
||||
this.debug(`unbound \`endListener_\` from "${this.endEvent_}"`);
|
||||
this.off(this.player(), this.endEvent_, this.endListener_);
|
||||
}
|
||||
this.on(this.player(), this.startEvent_, this.startListener_);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the overlay should hide.
|
||||
*
|
||||
* @param {number} time
|
||||
* The current time reported by the player.
|
||||
* @param {string} type
|
||||
* An event type.
|
||||
* @return {boolean}
|
||||
*/
|
||||
shouldHide_(time, type) {
|
||||
const end = this.options_.end;
|
||||
return isNumber(end) ? time >= end : end === type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the inherited method to perform some event binding
|
||||
*
|
||||
* @return {Overlay}
|
||||
*/
|
||||
show() {
|
||||
super.show();
|
||||
this.off(this.player(), this.startEvent_, this.startListener_);
|
||||
this.debug('shown');
|
||||
this.debug(`unbound \`startListener_\` from "${this.startEvent_}"`);
|
||||
|
||||
// Overlays without an "end" are valid.
|
||||
if (this.endEvent_) {
|
||||
this.debug(`bound \`endListener_\` to "${this.endEvent_}"`);
|
||||
this.on(this.player(), this.endEvent_, this.endListener_);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the overlay should show.
|
||||
*
|
||||
* @param {number} time
|
||||
* The current time reported by the player.
|
||||
* @param {string} type
|
||||
* An event type.
|
||||
* @return {boolean}
|
||||
*/
|
||||
shouldShow_(time, type) {
|
||||
const start = this.options_.start;
|
||||
const end = this.options_.end;
|
||||
if (isNumber(start)) {
|
||||
if (isNumber(end)) {
|
||||
return time >= start && time < end;
|
||||
|
||||
// In this case, the start is a number and the end is a string. We need
|
||||
// to check whether or not the overlay has shown since the last seek.
|
||||
} else if (!this.hasShownSinceSeek_) {
|
||||
this.hasShownSinceSeek_ = true;
|
||||
return time >= start;
|
||||
}
|
||||
|
||||
// In this case, the start is a number and the end is a string, but
|
||||
// the overlay has shown since the last seek. This means that we need
|
||||
// to be sure we aren't re-showing it at a later time than it is
|
||||
// scheduled to appear.
|
||||
return Math.floor(time) === start;
|
||||
}
|
||||
return start === type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener that can trigger the overlay to show.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
startListener_(e) {
|
||||
const time = this.player().currentTime();
|
||||
if (this.shouldShow_(time, e.type)) {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener that can trigger the overlay to show.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
endListener_(e) {
|
||||
const time = this.player().currentTime();
|
||||
if (this.shouldHide_(time, e.type)) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener that can looks for rewinds - that is, backward seeks
|
||||
* and may hide the overlay as needed.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
rewindListener_(e) {
|
||||
const time = this.player().currentTime();
|
||||
const previous = this.previousTime_;
|
||||
const start = this.options_.start;
|
||||
const end = this.options_.end;
|
||||
|
||||
// Did we seek backward?
|
||||
if (time < previous) {
|
||||
this.debug('rewind detected');
|
||||
|
||||
// The overlay remains visible if two conditions are met: the end value
|
||||
// MUST be an integer and the the current time indicates that the
|
||||
// overlay should NOT be visible.
|
||||
if (isNumber(end) && !this.shouldShow_(time)) {
|
||||
this.debug(`hiding; ${end} is an integer and overlay should not show at this time`);
|
||||
this.hasShownSinceSeek_ = false;
|
||||
this.hide();
|
||||
|
||||
// If the end value is an event name, we cannot reliably decide if the
|
||||
// overlay should still be displayed based solely on time; so, we can
|
||||
// only queue it up for showing if the seek took us to a point before
|
||||
// the start time.
|
||||
} else if (hasNoWhitespace(end) && time < start) {
|
||||
this.debug(`hiding; show point (${start}) is before now (${time}) and end point (${end}) is an event`);
|
||||
this.hasShownSinceSeek_ = false;
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
this.previousTime_ = time;
|
||||
}
|
||||
}
|
||||
videojs.registerComponent('Overlay', Overlay);
|
||||
return Overlay;
|
||||
};
|
||||
|
||||
return Overlay;
|
||||
}(Component);
|
||||
const Plugin = videojs__default["default"].getPlugin('plugin');
|
||||
const defaults = {
|
||||
align: 'top-left',
|
||||
class: '',
|
||||
content: 'This overlay will show up while the video is playing',
|
||||
debug: false,
|
||||
showBackground: true,
|
||||
attachToControlBar: false,
|
||||
overlays: [{
|
||||
start: 'playing',
|
||||
end: 'paused'
|
||||
}]
|
||||
};
|
||||
|
||||
videojs.registerComponent('Overlay', Overlay);
|
||||
/**
|
||||
* Initialize the plugin.
|
||||
*
|
||||
* @function plugin
|
||||
* @param {Object} [options={}]
|
||||
*/
|
||||
/**
|
||||
* A plugin for handling overlays in the Brightcove Player.
|
||||
*/
|
||||
class OverlayPlugin extends Plugin {
|
||||
/**
|
||||
* Create an Overlay Plugin instance.
|
||||
*
|
||||
* @param {Player} player
|
||||
* A Video.js Player instance.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An options object.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player);
|
||||
this.reset(options);
|
||||
}
|
||||
|
||||
var plugin = function plugin(options) {
|
||||
var _this2 = this;
|
||||
|
||||
var settings = videojs.mergeOptions(defaults, options); // De-initialize the plugin if it already has an array of overlays.
|
||||
|
||||
if (Array.isArray(this.overlays_)) {
|
||||
this.overlays_.forEach(function (overlay) {
|
||||
_this2.removeChild(overlay);
|
||||
|
||||
if (_this2.controlBar) {
|
||||
_this2.controlBar.removeChild(overlay);
|
||||
/**
|
||||
* Adds one or more items to the existing list of overlays.
|
||||
*
|
||||
* @param {Object|Array} item
|
||||
* An item (or an array of items) to be added as overlay/s
|
||||
*
|
||||
* @return {Array[Overlay]}
|
||||
* The array of overlay objects that were added
|
||||
*/
|
||||
add(item) {
|
||||
if (!Array.isArray(item)) {
|
||||
item = [item];
|
||||
}
|
||||
const addedOverlays = this.mapOverlays_(item);
|
||||
this.player.overlays_ = this.player.overlays_.concat(addedOverlays);
|
||||
return addedOverlays;
|
||||
}
|
||||
|
||||
overlay.dispose();
|
||||
});
|
||||
/**
|
||||
*
|
||||
* @param {Overlay} item
|
||||
* An item to be removed from the array of overlays
|
||||
*
|
||||
* @throws {Error}
|
||||
* Item to remove must be present in the array of overlays
|
||||
*
|
||||
*/
|
||||
remove(item) {
|
||||
const index = this.player.overlays_.indexOf(item);
|
||||
if (index !== -1) {
|
||||
item.el().parentNode.removeChild(item.el());
|
||||
this.player.overlays_.splice(index, 1);
|
||||
} else {
|
||||
this.player.log.warn('overlay does not exist and cannot be removed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of overlays used for the current video
|
||||
*
|
||||
* @return The array of overlay objects currently used by the plugin
|
||||
*/
|
||||
get() {
|
||||
return this.player.overlays_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the overlay options
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An options object.
|
||||
*/
|
||||
reset(options) {
|
||||
this.clearOverlays_();
|
||||
|
||||
// Use merge function based on video.js version.
|
||||
const merge = videojs__default["default"].obj && videojs__default["default"].obj.merge || videojs__default["default"].mergeOptions;
|
||||
this.options = merge(defaults, options);
|
||||
const overlays = this.options.overlays;
|
||||
|
||||
// We don't want to keep the original array of overlay options around
|
||||
// because it doesn't make sense to pass it to each Overlay component.
|
||||
delete this.options.overlays;
|
||||
this.player.overlays_ = this.mapOverlays_(overlays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the plugin
|
||||
*/
|
||||
dispose() {
|
||||
this.clearOverlays_();
|
||||
delete this.player.overlays_;
|
||||
super.dispose();
|
||||
}
|
||||
clearOverlays_() {
|
||||
// Remove child components
|
||||
if (Array.isArray(this.player.overlays_)) {
|
||||
this.player.overlays_.forEach(overlay => {
|
||||
this.player.removeChild(overlay);
|
||||
if (this.player.controlBar) {
|
||||
this.player.controlBar.removeChild(overlay);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
mapOverlays_(items) {
|
||||
return items.map(o => {
|
||||
const mergeOptions = videojs__default["default"].mergeOptions(this.options, o);
|
||||
const attachToControlBar = typeof mergeOptions.attachToControlBar === 'string' || mergeOptions.attachToControlBar === true;
|
||||
if (!this.player.controls() || !this.player.controlBar) {
|
||||
return this.player.addChild('overlay', mergeOptions);
|
||||
}
|
||||
if (attachToControlBar && mergeOptions.align.indexOf('bottom') !== -1) {
|
||||
let referenceChild = this.player.controlBar.children()[0];
|
||||
if (this.player.controlBar.getChild(mergeOptions.attachToControlBar) !== undefined) {
|
||||
referenceChild = this.player.controlBar.getChild(mergeOptions.attachToControlBar);
|
||||
}
|
||||
if (referenceChild) {
|
||||
const referenceChildIndex = this.player.controlBar.children().indexOf(referenceChild);
|
||||
const controlBarChild = this.player.controlBar.addChild('overlay', mergeOptions, referenceChildIndex);
|
||||
return controlBarChild;
|
||||
}
|
||||
}
|
||||
const playerChild = this.player.addChild('overlay', mergeOptions);
|
||||
this.player.el().insertBefore(playerChild.el(), this.player.controlBar.el());
|
||||
return playerChild;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var overlays = settings.overlays; // We don't want to keep the original array of overlay options around
|
||||
// because it doesn't make sense to pass it to each Overlay component.
|
||||
var version = "4.0.0";
|
||||
|
||||
delete settings.overlays;
|
||||
this.overlays_ = overlays.map(function (o) {
|
||||
var mergeOptions = videojs.mergeOptions(settings, o);
|
||||
var attachToControlBar = typeof mergeOptions.attachToControlBar === 'string' || mergeOptions.attachToControlBar === true;
|
||||
initOverlayComponent(videojs__default["default"]);
|
||||
OverlayPlugin.VERSION = version;
|
||||
videojs__default["default"].registerPlugin('overlay', OverlayPlugin);
|
||||
|
||||
if (!_this2.controls() || !_this2.controlBar) {
|
||||
return _this2.addChild('overlay', mergeOptions);
|
||||
}
|
||||
return OverlayPlugin;
|
||||
|
||||
if (attachToControlBar && mergeOptions.align.indexOf('bottom') !== -1) {
|
||||
var referenceChild = _this2.controlBar.children()[0];
|
||||
|
||||
if (_this2.controlBar.getChild(mergeOptions.attachToControlBar) !== undefined) {
|
||||
referenceChild = _this2.controlBar.getChild(mergeOptions.attachToControlBar);
|
||||
}
|
||||
|
||||
if (referenceChild) {
|
||||
var referenceChildIndex = _this2.controlBar.children().indexOf(referenceChild);
|
||||
|
||||
var controlBarChild = _this2.controlBar.addChild('overlay', mergeOptions, referenceChildIndex);
|
||||
|
||||
return controlBarChild;
|
||||
}
|
||||
}
|
||||
|
||||
var playerChild = _this2.addChild('overlay', mergeOptions);
|
||||
|
||||
_this2.el().insertBefore(playerChild.el(), _this2.controlBar.el());
|
||||
|
||||
return playerChild;
|
||||
});
|
||||
};
|
||||
|
||||
plugin.VERSION = version;
|
||||
registerPlugin('overlay', plugin);
|
||||
|
||||
module.exports = plugin;
|
||||
}));
|
||||
|
||||
@ -1 +1,7 @@
|
||||
.video-js .vjs-overlay{color:#fff;position:absolute;text-align:center}.video-js .vjs-overlay-no-background{max-width:33%}.video-js .vjs-overlay-background{background-color:#646464;background-color:rgba(255,255,255,0.4);border-radius:3px;padding:10px;width:33%}.video-js .vjs-overlay-top-left{top:5px;left:5px}.video-js .vjs-overlay-top{left:50%;margin-left:-16.5%;top:5px}.video-js .vjs-overlay-top-right{right:5px;top:5px}.video-js .vjs-overlay-right{right:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-bottom-right{bottom:3.5em;right:5px}.video-js .vjs-overlay-bottom{bottom:3.5em;left:50%;margin-left:-16.5%}.video-js .vjs-overlay-bottom-left{bottom:3.5em;left:5px}.video-js .vjs-overlay-left{left:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-center{left:50%;margin-left:-16.5%;top:50%;transform:translateY(-50%)}.video-js .vjs-no-flex .vjs-overlay-left,.video-js .vjs-no-flex .vjs-overlay-center,.video-js .vjs-no-flex .vjs-overlay-right{margin-top:-15px}
|
||||
/**
|
||||
* Skipped minification because the original files appears to be already minified.
|
||||
* Original file: /npm/videojs-overlay@4.0.0/dist/videojs-overlay.css
|
||||
*
|
||||
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||
*/
|
||||
.video-js .vjs-overlay{color:#fff;position:absolute;text-align:center}.video-js .vjs-overlay-no-background{max-width:33%}.video-js .vjs-overlay-background{background-color:#646464;background-color:hsla(0,0%,100%,.4);border-radius:3px;padding:10px;width:33%}.video-js .vjs-overlay-top-left{top:5px;left:5px}.video-js .vjs-overlay-top{left:50%;margin-left:-16.5%;top:5px}.video-js .vjs-overlay-top-right{right:5px;top:5px}.video-js .vjs-overlay-right{right:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-bottom-right{bottom:3.5em;right:5px}.video-js .vjs-overlay-bottom{bottom:3.5em;left:50%;margin-left:-16.5%}.video-js .vjs-overlay-bottom-left{bottom:3.5em;left:5px}.video-js .vjs-overlay-left{left:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-center{left:50%;margin-left:-16.5%;top:50%;transform:translateY(-50%)}.video-js .vjs-no-flex .vjs-overlay-left,.video-js .vjs-no-flex .vjs-overlay-center,.video-js .vjs-no-flex .vjs-overlay-right{margin-top:-15px}/*# sourceMappingURL=videojs-overlay.css.map */
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -6,6 +6,21 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="{{description}}">
|
||||
<meta name="author" content="datarhei restreamer">
|
||||
<style>
|
||||
html{
|
||||
min-height: 100vh;
|
||||
}
|
||||
body{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
#player{
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<title>{{name}}</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="alternate" type="application/json+oembed" href="channels/{{channelid}}/oembed.json" title="{{name}}">
|
||||
@ -23,7 +38,7 @@
|
||||
{{/if}}
|
||||
</head>
|
||||
<body>
|
||||
<div style="position:absolute;top:0;right:0;bottom:0;left:0">
|
||||
<div>
|
||||
<video id="player" class="vjs-public video-js" controls playsinline></video>
|
||||
</div>
|
||||
<script src="player/videojs/dist/video.min.js"></script>
|
||||
@ -81,8 +96,8 @@
|
||||
muted: mute,
|
||||
liveui: true,
|
||||
responsive: true,
|
||||
fluid: true,
|
||||
sources: [{ src: window.location.origin + '/' + playerConfig.source, type: 'application/x-mpegURL' }],
|
||||
plugins: {},
|
||||
};
|
||||
|
||||
if (playerConfig.chromecast) {
|
||||
@ -97,41 +112,29 @@
|
||||
}
|
||||
|
||||
var player = videojs('player', config);
|
||||
|
||||
if(playerConfig.logo.image.length != 0) {
|
||||
const imgTag = new Image();
|
||||
imgTag.src = playerConfig.logo.image;
|
||||
imgTag.onload = function () {
|
||||
const logoWidth = imgTag.width;
|
||||
const logoHeight = imgTag.height;
|
||||
const overlayPosition = playerConfig.logo.position || 'top-left';
|
||||
const overlayLink = playerConfig.logo.link || '#';
|
||||
player.overlay({
|
||||
overlays: [{
|
||||
content: `<a href="${overlayLink}" target="_blank"><img src="${playerConfig.logo.image + '?' + Math.random()}" alt="Logo" width="${logoWidth}" height="${logoHeight}"></a>`,
|
||||
start: 'play',
|
||||
end: 'pause',
|
||||
align: overlayPosition,
|
||||
showBackground: false
|
||||
}]
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
player.ready(function() {
|
||||
if(playerConfig.logo.image.length != 0) {
|
||||
var overlay = null;
|
||||
|
||||
var imgTag = new Image();
|
||||
imgTag.onLoad = function () {
|
||||
imgTag.setAttribute('width', this.width);
|
||||
imgTag.setAttribute('height'.this.height);
|
||||
};
|
||||
imgTag.src = playerConfig.logo.image + '?' + Math.random();
|
||||
|
||||
if (playerConfig.logo.link.length !== 0) {
|
||||
var aTag = document.createElement('a');
|
||||
aTag.setAttribute('href', playerConfig.logo.link);
|
||||
aTag.setAttribute('target', '_blank');
|
||||
aTag.appendChild(imgTag);
|
||||
overlay = aTag.outerHTML;
|
||||
} else {
|
||||
overlay = imgTag.outerHTML;
|
||||
}
|
||||
|
||||
player.overlay({
|
||||
align: playerConfig.logo.position,
|
||||
overlays: [
|
||||
{
|
||||
showBackground: false,
|
||||
content: overlay,
|
||||
start: 'playing',
|
||||
end: 'pause',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
player.license(playerConfig.license);
|
||||
}
|
||||
player.license(playerConfig.license);
|
||||
|
||||
if (autoplay === true) {
|
||||
// https://videojs.com/blog/autoplay-best-practices-with-video-js/
|
||||
|
||||
@ -13,8 +13,8 @@ var config = {
|
||||
|
||||
if (chromecast) {
|
||||
config.techOrder = ['chromecast', 'html5'];
|
||||
// Provide a default reciever application ID
|
||||
config.plugins.chromecast = {
|
||||
// Provide a default reciever application ID
|
||||
receiverApplicationId: 'CC1AD845',
|
||||
};
|
||||
}
|
||||
|
||||
@ -67,7 +67,11 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function Resources(props) {
|
||||
function Resources({
|
||||
getResources = () => {
|
||||
return null;
|
||||
},
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const [$popover, setPopover] = React.useState(null);
|
||||
const [$resources, setResources] = React.useState(null);
|
||||
@ -94,7 +98,7 @@ function Resources(props) {
|
||||
}, []);
|
||||
|
||||
const update = async () => {
|
||||
const resources = await props.resources();
|
||||
const resources = await getResources();
|
||||
if (resources === null) {
|
||||
return;
|
||||
}
|
||||
@ -347,12 +351,6 @@ function Resources(props) {
|
||||
);
|
||||
}
|
||||
|
||||
Resources.defaultProps = {
|
||||
resources: () => {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
const initVersion = (initialVersion) => {
|
||||
if (!initialVersion) {
|
||||
initialVersion = {};
|
||||
@ -367,11 +365,18 @@ const initVersion = (initialVersion) => {
|
||||
return version;
|
||||
};
|
||||
|
||||
export default function Footer(props) {
|
||||
export default function Footer({
|
||||
expand = false,
|
||||
app = '',
|
||||
name = '',
|
||||
version = initVersion(),
|
||||
getResources = () => {
|
||||
return null;
|
||||
},
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const version = initVersion(props.version);
|
||||
|
||||
if (props.expand === true) {
|
||||
if (expand === true) {
|
||||
return (
|
||||
<Grid container className={classes.footer} spacing={0} direction="row" alignItems="center">
|
||||
<Grid item xs={12}>
|
||||
@ -379,10 +384,10 @@ export default function Footer(props) {
|
||||
<Stack className="footerLeft" direction="row" alignItems="center" spacing={0}>
|
||||
<Logo className={classes.logo} />
|
||||
<Typography className="footerVersion">
|
||||
{props.app} v{version.number} ({version.arch}) {props.name ? '- ' + props.name : ''}
|
||||
{app} v{version.number} ({version.arch}) {name ? '- ' + name : ''}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Resources resources={props.resources} />
|
||||
<Resources getResources={getResources} />
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@ -401,13 +406,3 @@ export default function Footer(props) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Footer.defaultProps = {
|
||||
expand: false,
|
||||
app: '',
|
||||
name: '',
|
||||
version: initVersion(),
|
||||
resources: () => {
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
@ -160,12 +160,12 @@ const StyledMenu = styled((props) => (
|
||||
},
|
||||
}));
|
||||
|
||||
function AboutModal(props) {
|
||||
function AboutModal({ open = false, onClose = () => {} }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Modal open={props.open} onClose={props.onClose} className="modal">
|
||||
<ModalContent title="About datarhei Restreamer" onClose={props.onClose} className={classes.modalPaper}>
|
||||
<Modal open={open} onClose={onClose} className="modal">
|
||||
<ModalContent title="About datarhei Restreamer" onClose={onClose} className={classes.modalPaper}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} className={classes.aboutImage}>
|
||||
<PaperThumb image={welcomeImage} title="Welcome to Restreamer v2" height="200px" />
|
||||
@ -215,12 +215,17 @@ function AboutModal(props) {
|
||||
);
|
||||
}
|
||||
|
||||
AboutModal.defaultProps = {
|
||||
open: false,
|
||||
onClose: () => {},
|
||||
};
|
||||
|
||||
function HeaderMenu(props) {
|
||||
function HeaderMenu({
|
||||
onChannel = () => {},
|
||||
onPlayersite = () => {},
|
||||
onSettings = () => {},
|
||||
onLogout = () => {},
|
||||
expand = true,
|
||||
showPlayersite = false,
|
||||
showSettings = false,
|
||||
hasUpdates = false,
|
||||
hasService = false,
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [$anchorEl, setAnchorEl] = React.useState(null);
|
||||
@ -238,17 +243,17 @@ function HeaderMenu(props) {
|
||||
Storage.Set('language', language);
|
||||
};
|
||||
|
||||
if (props.expand === true) {
|
||||
if (expand === true) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Fab className="headerFab" color="primary" onClick={props.onChannel}>
|
||||
<Fab className="headerFab" color="primary" onClick={onChannel}>
|
||||
<VideocamIcon className="fabIcon" />
|
||||
</Fab>
|
||||
<Fab className={props.hasUpdates ? 'headerFabHighlight' : 'headerFab'} color="primary" onClick={handleMenuOpen}>
|
||||
<Fab className={hasUpdates ? 'headerFabHighlight' : 'headerFab'} color="primary" onClick={handleMenuOpen}>
|
||||
<MenuOpenIcon className="fabIcon" />
|
||||
</Fab>
|
||||
<StyledMenu anchorEl={$anchorEl} open={$anchorEl !== null} onClose={handleMenuClose} onClick={handleMenuClose} disableScrollLock>
|
||||
{props.hasService === true && (
|
||||
{hasService === true && (
|
||||
<React.Fragment>
|
||||
<MenuItem component="a" href="https://service.datarhei.com" target="blank">
|
||||
<ListItemIcon>
|
||||
@ -259,18 +264,18 @@ function HeaderMenu(props) {
|
||||
<Divider />
|
||||
</React.Fragment>
|
||||
)}
|
||||
{props.showPlayersite === true && (
|
||||
<MenuItem onClick={props.onPlayersite}>
|
||||
{showPlayersite === true && (
|
||||
<MenuItem onClick={onPlayersite}>
|
||||
<ListItemIcon size="large">
|
||||
<WebIcon fontSize="small" size="large" />
|
||||
</ListItemIcon>
|
||||
<Trans>Playersite</Trans>
|
||||
</MenuItem>
|
||||
)}
|
||||
{props.showSettings === true && (
|
||||
<MenuItem onClick={props.onSettings}>
|
||||
{showSettings === true && (
|
||||
<MenuItem onClick={onSettings}>
|
||||
<ListItemIcon>
|
||||
<Settings fontSize="small" className={props.hasUpdates ? classes.colorHighlight : ''} />
|
||||
<Settings fontSize="small" className={hasUpdates ? classes.colorHighlight : ''} />
|
||||
</ListItemIcon>
|
||||
<Trans>System</Trans>
|
||||
</MenuItem>
|
||||
@ -300,7 +305,7 @@ function HeaderMenu(props) {
|
||||
</ListItemIcon>
|
||||
<LanguageSelect onChange={handleLanguageChange} />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={props.onLogout}>
|
||||
<MenuItem onClick={onLogout}>
|
||||
<ListItemIcon>
|
||||
<Logout fontSize="small" />
|
||||
</ListItemIcon>
|
||||
@ -348,19 +353,17 @@ function HeaderMenu(props) {
|
||||
}
|
||||
}
|
||||
|
||||
HeaderMenu.defaultProps = {
|
||||
onChannel: () => {},
|
||||
onPlayersite: () => {},
|
||||
onSettings: () => {},
|
||||
onLogout: () => {},
|
||||
expand: false,
|
||||
showPlayersite: false,
|
||||
showSettings: false,
|
||||
hasUpdates: false,
|
||||
hasService: false,
|
||||
};
|
||||
|
||||
export default function Header(props) {
|
||||
export default function Header({
|
||||
onChannel = () => {},
|
||||
onPlayersite = () => {},
|
||||
onSettings = () => {},
|
||||
onLogout = () => {},
|
||||
expand = true,
|
||||
showPlayersite = false,
|
||||
showSettings = false,
|
||||
hasUpdates = false,
|
||||
hasService = false,
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
@ -372,14 +375,20 @@ export default function Header(props) {
|
||||
<Typography className="headerTitle">Restreamer</Typography>
|
||||
</Stack>
|
||||
<Stack className="headerRight" direction="row" alignItems="center" spacing={0}>
|
||||
<HeaderMenu {...props}></HeaderMenu>
|
||||
<HeaderMenu
|
||||
onChannel={onChannel}
|
||||
onPlayersite={onPlayersite}
|
||||
onSettings={onSettings}
|
||||
onLogout={onLogout}
|
||||
expand={expand}
|
||||
showPlayersite={showPlayersite}
|
||||
showSettings={showSettings}
|
||||
hasUpdates={hasUpdates}
|
||||
hasService={hasService}
|
||||
></HeaderMenu>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Header.defaultProps = {
|
||||
expand: false,
|
||||
};
|
||||
|
||||
@ -40,7 +40,7 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function RestreamerUI(props) {
|
||||
export default function RestreamerUI({ address = '' }) {
|
||||
const classes = useStyles();
|
||||
|
||||
const [$state, setState] = React.useState({
|
||||
@ -121,7 +121,7 @@ export default function RestreamerUI(props) {
|
||||
};
|
||||
|
||||
const handleMount = async () => {
|
||||
restreamer.current = new Restreamer(props.address);
|
||||
restreamer.current = new Restreamer(address);
|
||||
restreamer.current.AddListener((event) => {
|
||||
notify(event.severity, event.type, event.message);
|
||||
});
|
||||
@ -452,7 +452,7 @@ export default function RestreamerUI(props) {
|
||||
name = restreamer.current.Name();
|
||||
}
|
||||
|
||||
let resources = () => {
|
||||
let getResources = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
@ -490,7 +490,7 @@ export default function RestreamerUI(props) {
|
||||
);
|
||||
} else {
|
||||
view = <Router restreamer={restreamer.current} />;
|
||||
resources = handleResources;
|
||||
getResources = handleResources;
|
||||
}
|
||||
}
|
||||
|
||||
@ -523,7 +523,7 @@ export default function RestreamerUI(props) {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Footer expand={$state.connected} app={app} version={version} name={name} resources={resources} />
|
||||
<Footer expand={$state.connected} app={app} version={version} name={name} getResources={getResources} />
|
||||
<Snackbar
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
@ -540,7 +540,7 @@ export default function RestreamerUI(props) {
|
||||
{expand && (
|
||||
<ChannelList
|
||||
open={$channelList.open}
|
||||
channels={$channelList.channels}
|
||||
allChannels={$channelList.channels}
|
||||
channelid={$channelList.channelid}
|
||||
onClose={handleCloseChannelList}
|
||||
onClick={handleSelectChannel}
|
||||
@ -555,7 +555,3 @@ export default function RestreamerUI(props) {
|
||||
</I18n>
|
||||
);
|
||||
}
|
||||
|
||||
RestreamerUI.defaultProps = {
|
||||
address: '',
|
||||
};
|
||||
|
||||
@ -3,33 +3,29 @@ import { Route, Navigate, Routes, HashRouter as DOMRouter } from 'react-router-d
|
||||
|
||||
import Views from './views';
|
||||
|
||||
export default function Router(props) {
|
||||
if (props.restreamer === null) {
|
||||
export default function Router({ restreamer = null }) {
|
||||
if (restreamer === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const channelid = props.restreamer.GetCurrentChannelID();
|
||||
const channelid = restreamer.GetCurrentChannelID();
|
||||
|
||||
return (
|
||||
<DOMRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<Views.ChannelSelect channelid={channelid} />} />
|
||||
<Route path="/playersite" element={<Views.Playersite restreamer={props.restreamer} />} />
|
||||
<Route path="/settings" element={<Views.Settings restreamer={props.restreamer} />} />
|
||||
<Route path="/settings/:tab" element={<Views.Settings restreamer={props.restreamer} />} />
|
||||
<Route path="/:channelid" element={<Views.Main key={channelid} restreamer={props.restreamer} />} />
|
||||
<Route path="/:channelid/edit" element={<Views.Edit key={channelid} restreamer={props.restreamer} />} />
|
||||
<Route path="/:channelid/edit/wizard" element={<Views.Wizard key={channelid} restreamer={props.restreamer} />} />
|
||||
<Route path="/:channelid/edit/:tab" element={<Views.Edit key={channelid} restreamer={props.restreamer} />} />
|
||||
<Route path="/:channelid/publication" element={<Views.AddService key={channelid} restreamer={props.restreamer} />} />
|
||||
<Route path="/:channelid/publication/player" element={<Views.EditPlayer key={channelid} restreamer={props.restreamer} />} />
|
||||
<Route path="/:channelid/publication/:service/:index" element={<Views.EditService key={channelid} restreamer={props.restreamer} />} />
|
||||
<Route path="/playersite" element={<Views.Playersite restreamer={restreamer} />} />
|
||||
<Route path="/settings" element={<Views.Settings restreamer={restreamer} />} />
|
||||
<Route path="/settings/:tab" element={<Views.Settings restreamer={restreamer} />} />
|
||||
<Route path="/:channelid" element={<Views.Main key={channelid} restreamer={restreamer} />} />
|
||||
<Route path="/:channelid/edit" element={<Views.Edit key={channelid} restreamer={restreamer} />} />
|
||||
<Route path="/:channelid/edit/wizard" element={<Views.Wizard key={channelid} restreamer={restreamer} />} />
|
||||
<Route path="/:channelid/edit/:tab" element={<Views.Edit key={channelid} restreamer={restreamer} />} />
|
||||
<Route path="/:channelid/publication" element={<Views.AddService key={channelid} restreamer={restreamer} />} />
|
||||
<Route path="/:channelid/publication/player" element={<Views.EditPlayer key={channelid} restreamer={restreamer} />} />
|
||||
<Route path="/:channelid/publication/:service/:index" element={<Views.EditService key={channelid} restreamer={restreamer} />} />
|
||||
<Route path="*" render={() => <Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</DOMRouter>
|
||||
);
|
||||
}
|
||||
|
||||
Router.defaultProps = {
|
||||
restreamer: null,
|
||||
};
|
||||
|
||||
@ -25,5 +25,5 @@ createRoot(document.getElementById('root')).render(
|
||||
<CssBaseline />
|
||||
<RestreamerUI address={address} />
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
</StyledEngineProvider>,
|
||||
);
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -3,59 +3,67 @@ import React from 'react';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import Button from '@mui/material/Button';
|
||||
|
||||
export default function ActionButton(props) {
|
||||
export default function ActionButton({
|
||||
order = 'stop',
|
||||
state = 'disconnected',
|
||||
reconnect = -1,
|
||||
disabled = false,
|
||||
onDisconnect = function () {},
|
||||
onConnect = function () {},
|
||||
onReconnect = function () {},
|
||||
}) {
|
||||
let button = null;
|
||||
|
||||
if (props.state === 'connecting') {
|
||||
if (state === 'connecting') {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth disabled>
|
||||
<Trans>Connecting ...</Trans>
|
||||
</Button>
|
||||
);
|
||||
} else if (props.state === 'disconnecting') {
|
||||
} else if (state === 'disconnecting') {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth disabled>
|
||||
<Trans>Disconnecting ...</Trans>
|
||||
</Button>
|
||||
);
|
||||
} else if (props.state === 'connected') {
|
||||
} else if (state === 'connected') {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth color="secondary" disabled={props.disabled} onClick={props.onDisconnect}>
|
||||
<Button variant="outlined" fullWidth color="secondary" disabled={disabled} onClick={onDisconnect}>
|
||||
<Trans>Disconnect</Trans>
|
||||
</Button>
|
||||
);
|
||||
} else if (props.state === 'disconnected') {
|
||||
if (props.reconnect < 0) {
|
||||
if (props.order === 'start') {
|
||||
} else if (state === 'disconnected') {
|
||||
if (reconnect < 0) {
|
||||
if (order === 'start') {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={props.disabled} onClick={props.onReconnect}>
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={disabled} onClick={onReconnect}>
|
||||
<Trans>Reconnect</Trans>
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={props.disabled} onClick={props.onConnect}>
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={disabled} onClick={onConnect}>
|
||||
<Trans>Connect</Trans>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth color="secondary" disabled={props.disabled} onClick={props.onDisconnect}>
|
||||
<Button variant="outlined" fullWidth color="secondary" disabled={disabled} onClick={onDisconnect}>
|
||||
<Trans>Disconnect</Trans>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
} else if (props.state === 'error') {
|
||||
if (props.reconnect < 0) {
|
||||
} else if (state === 'error') {
|
||||
if (reconnect < 0) {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={props.disabled} onClick={props.onReconnect}>
|
||||
<Button variant="outlined" fullWidth color="primary" disabled={disabled} onClick={onReconnect}>
|
||||
<Trans>Reconnect</Trans>
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
button = (
|
||||
<Button variant="outlined" fullWidth color="secondary" disabled={props.disabled} onClick={props.onDisconnect}>
|
||||
<Button variant="outlined" fullWidth color="secondary" disabled={disabled} onClick={onDisconnect}>
|
||||
<Trans>Disconnect</Trans>
|
||||
</Button>
|
||||
);
|
||||
@ -64,13 +72,3 @@ export default function ActionButton(props) {
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
ActionButton.defaultProps = {
|
||||
order: 'stop',
|
||||
state: 'disconnected',
|
||||
reconnect: -1,
|
||||
disabled: false,
|
||||
onDisconnect: function () {},
|
||||
onConnect: function () {},
|
||||
onReconnect: function () {},
|
||||
};
|
||||
|
||||
@ -41,29 +41,20 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ color = 'light', textAlign = 'left', alignItems = 'center', justifyContent = 'center', style = null, children = null }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="column"
|
||||
justifyContent={props.justifyContent}
|
||||
alignItems={props.alignItems}
|
||||
textAlign={props.textAlign}
|
||||
justifyContent={justifyContent}
|
||||
alignItems={alignItems}
|
||||
textAlign={textAlign}
|
||||
spacing={1}
|
||||
className={
|
||||
props.color === 'dark' ? classes.dark : props.color === 'success' ? classes.success : props.color === 'danger' ? classes.danger : classes.light
|
||||
}
|
||||
{...props}
|
||||
className={color === 'dark' ? classes.dark : color === 'success' ? classes.success : color === 'danger' ? classes.danger : classes.light}
|
||||
style={style}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
color: 'light',
|
||||
textAlign: 'left',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
};
|
||||
|
||||
@ -12,14 +12,12 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ style = null, children = null }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Stack direction="column" justifyContent="center" alignItems="center" spacing={1} className={classes.box} {...props}>
|
||||
{props.children}
|
||||
<Stack direction="column" justifyContent="center" alignItems="center" spacing={1} className={classes.box} style={style}>
|
||||
{children}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {};
|
||||
|
||||
@ -49,7 +49,7 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Changelog(props) {
|
||||
export default function Changelog({ open = false, current = '', previous = '', onClose = () => {}, children = null }) {
|
||||
const [$data, setData] = React.useState('');
|
||||
const classes = useStyles();
|
||||
|
||||
@ -62,7 +62,7 @@ export default function Changelog(props) {
|
||||
|
||||
const onMount = async () => {
|
||||
let data = await loadData();
|
||||
data = filter(data, props.current, props.previous);
|
||||
data = filter(data, current, previous);
|
||||
|
||||
setData(data);
|
||||
};
|
||||
@ -141,39 +141,39 @@ export default function Changelog(props) {
|
||||
const renderers = {
|
||||
h1: (props) => (
|
||||
<h1 className={classes.h1} {...props}>
|
||||
{props.children}
|
||||
{children}
|
||||
</h1>
|
||||
),
|
||||
h2: (props) => (
|
||||
<h2 className={classes.h2} {...props}>
|
||||
{props.children}
|
||||
{children}
|
||||
</h2>
|
||||
),
|
||||
h3: (props) => (
|
||||
<h3 className={classes.h3} {...props}>
|
||||
{props.children}
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
h4: (props) => (
|
||||
<h4 className={classes.h4} {...props}>
|
||||
{props.children}
|
||||
{children}
|
||||
</h4>
|
||||
),
|
||||
a: (props) => (
|
||||
<a className={classes.a} target="_blank" {...props}>
|
||||
{props.children}
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={props.open}
|
||||
onClose={props.onClose}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
title={<Trans>Update details (Changelog)</Trans>}
|
||||
maxWidth={600}
|
||||
buttonsRight={
|
||||
<Button variant="outlined" color="primary" onClick={props.onClose}>
|
||||
<Button variant="outlined" color="primary" onClick={onClose}>
|
||||
<Trans>Close</Trans>
|
||||
</Button>
|
||||
}
|
||||
@ -188,10 +188,3 @@ export default function Changelog(props) {
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
Changelog.defaultProps = {
|
||||
open: false,
|
||||
current: '',
|
||||
previous: '',
|
||||
onClose: () => {},
|
||||
};
|
||||
|
||||
@ -121,12 +121,12 @@ const ImageBackdrop = styled('span')(({ theme }) => ({
|
||||
border: `2px solid ${theme.palette.primary.dark}`,
|
||||
}));
|
||||
|
||||
function ChannelButton(props, largeChannelList) {
|
||||
function ChannelButton({ url = '', width = 200, title = '', state = '', disabled = false, onClick = () => {}, largeChannelList = [] }) {
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
|
||||
let color = theme.palette.primary.main;
|
||||
switch (props.state) {
|
||||
switch (state) {
|
||||
case 'disconnected':
|
||||
color = theme.palette.primary.main;
|
||||
break;
|
||||
@ -146,7 +146,7 @@ function ChannelButton(props, largeChannelList) {
|
||||
}
|
||||
|
||||
let color_active = theme.palette.primary.main;
|
||||
switch (props.disabled) {
|
||||
switch (disabled) {
|
||||
case true:
|
||||
color_active = theme.palette.primary.light;
|
||||
break;
|
||||
@ -157,23 +157,23 @@ function ChannelButton(props, largeChannelList) {
|
||||
|
||||
return (
|
||||
<Grid item xs={12} sm={6} md={4} lg={3} style={{ paddingBottom: largeChannelList ? '10px' : 'auto' }}>
|
||||
<ImageButton focusRipple disabled={props.disabled} onClick={props.onClick} style={{ width: props.width }}>
|
||||
<ImageButton focusRipple disabled={disabled} onClick={onClick} style={{ width: width }}>
|
||||
<Stack direction="column" spacing={0.5}>
|
||||
<Image
|
||||
style={{
|
||||
width: props.width,
|
||||
height: parseInt((props.width / 16) * 9),
|
||||
width: width,
|
||||
height: parseInt((width / 16) * 9),
|
||||
}}
|
||||
>
|
||||
<ImageAlt>
|
||||
<DoNotDisturbAltIcon fontSize="large" />
|
||||
</ImageAlt>
|
||||
<ImageSrc style={{ backgroundImage: `url(${props.url})`, borderColor: color_active }} />
|
||||
<ImageSrc style={{ backgroundImage: `url(${url})`, borderColor: color_active }} />
|
||||
<ImageBackdrop className="MuiImageBackdrop-root" style={{ borderColor: color_active }} />
|
||||
</Image>
|
||||
<Stack direction="row" alignItems="flex-start" justifyContent="space-between" className={classes.imageTitle}>
|
||||
<Typography variant="body2" color="inherit">
|
||||
{props.title}
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="inherit">
|
||||
<LensIcon fontSize="small" style={{ color: color }} />
|
||||
@ -185,15 +185,6 @@ function ChannelButton(props, largeChannelList) {
|
||||
);
|
||||
}
|
||||
|
||||
ChannelButton.defaultProps = {
|
||||
url: '',
|
||||
width: 200,
|
||||
title: '',
|
||||
state: '',
|
||||
disabled: false,
|
||||
onClick: () => {},
|
||||
};
|
||||
|
||||
const calculateColumnsPerRow = (breakpointSmall, breakpointMedium, breakpointLarge) => {
|
||||
if (breakpointLarge) {
|
||||
return 4;
|
||||
@ -209,7 +200,21 @@ const calculateRowsToFit = (windowHeight, thumbnailHeight, otherUIHeight) => {
|
||||
return Math.floor((windowHeight - otherUIHeight) / thumbnailHeight);
|
||||
};
|
||||
|
||||
export default function ChannelList(props) {
|
||||
export default function ChannelList({
|
||||
open = false,
|
||||
channelid = '',
|
||||
allChannels = [],
|
||||
onClose = () => {},
|
||||
onClick = (channelid) => {},
|
||||
onAdd = (name) => {},
|
||||
onState = (channelids) => {
|
||||
const states = {};
|
||||
for (let channelid of channelids) {
|
||||
states[channelid] = '';
|
||||
}
|
||||
return states;
|
||||
},
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const breakpointSmall = useMediaQuery(theme.breakpoints.up('sm'));
|
||||
@ -225,8 +230,6 @@ export default function ChannelList(props) {
|
||||
const [windowWidth, setWindowWidth] = React.useState(window.innerWidth);
|
||||
const [windowHeight, setWindowHeight] = React.useState(window.innerHeight);
|
||||
|
||||
const { channels: allChannels, channelid, onClick, onClose, onState } = props;
|
||||
|
||||
const [$largeChannelList, setLargeChannelList] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -316,7 +319,7 @@ export default function ChannelList(props) {
|
||||
});
|
||||
};
|
||||
|
||||
if (props.open === false) {
|
||||
if (open === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -333,9 +336,9 @@ export default function ChannelList(props) {
|
||||
<React.Fragment>
|
||||
<SwipeableDrawer
|
||||
anchor="bottom"
|
||||
open={props.open}
|
||||
open={open}
|
||||
onOpen={() => {}}
|
||||
onClose={props.onClose}
|
||||
onClose={onClose}
|
||||
sx={{
|
||||
marginButtom: 60,
|
||||
'& .MuiDrawer-paper': {
|
||||
@ -363,7 +366,7 @@ export default function ChannelList(props) {
|
||||
<IconButton color="inherit" size="large" onClick={handleLargeChannelList}>
|
||||
{$largeChannelList ? <FullscreenExitIcon /> : <FullscreenIcon />}
|
||||
</IconButton>
|
||||
<IconButton color="inherit" size="large" onClick={props.onClose}>
|
||||
<IconButton color="inherit" size="large" onClick={onClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
@ -419,7 +422,7 @@ export default function ChannelList(props) {
|
||||
disabled={$addChannel.name.length === 0}
|
||||
onClick={() => {
|
||||
handleAddChannelDialog();
|
||||
props.onAdd($addChannel.name);
|
||||
onAdd($addChannel.name);
|
||||
}}
|
||||
>
|
||||
<Trans>Add</Trans>
|
||||
@ -440,20 +443,3 @@ export default function ChannelList(props) {
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
ChannelList.defaultProps = {
|
||||
open: false,
|
||||
channelid: '',
|
||||
channels: [],
|
||||
onClose: () => {},
|
||||
onClick: (channelid) => {},
|
||||
onAdd: (name) => {},
|
||||
onState: (channelids) => {
|
||||
const states = {};
|
||||
for (let channelid of channelids) {
|
||||
states[channelid] = '';
|
||||
}
|
||||
|
||||
return states;
|
||||
},
|
||||
};
|
||||
|
||||
@ -18,22 +18,15 @@ const useStyles = makeStyles((theme) => ({
|
||||
disabled: {},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ label = '', checked = false, disabled = false, onChange = function (event) {} }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
className={classes.root}
|
||||
control={<Checkbox className={classes.root} checked={props.checked} onChange={props.onChange} />}
|
||||
label={props.label}
|
||||
disabled={props.disabled}
|
||||
control={<Checkbox className={classes.root} checked={checked} onChange={onChange} />}
|
||||
label={label}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
label: '',
|
||||
checked: false,
|
||||
disabled: false,
|
||||
onChange: function (event) {},
|
||||
};
|
||||
|
||||
31
src/misc/CircularProgress.js
Normal file
31
src/misc/CircularProgress.js
Normal 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>
|
||||
);
|
||||
}
|
||||
@ -16,7 +16,7 @@ function hexToRGBA(value) {
|
||||
return result ? `rgba(${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt(result[3], 16)},1)` : value;
|
||||
}
|
||||
|
||||
export default function ColorPicker(props) {
|
||||
export default function ColorPicker({ variant = 'default', label = '', fullWidth = false, value = 'rgba(255, 255, 255, 1)', onChange = () => {} }) {
|
||||
const [$open, setOpen] = React.useState(false);
|
||||
|
||||
const handleOpen = () => {
|
||||
@ -28,18 +28,18 @@ export default function ColorPicker(props) {
|
||||
};
|
||||
|
||||
const handleChange = (color) => {
|
||||
props.onChange({
|
||||
onChange({
|
||||
target: {
|
||||
value: color,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const value = hexToRGBA(props.value);
|
||||
value = hexToRGBA(value);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<TextField variant={props.variant} fullWidth={props.fullWidth} label={props.label} value={value} onClick={handleOpen} onChange={props.onChange} />
|
||||
<TextField variant={variant} fullWidth={fullWidth} label={label} value={value} onClick={handleOpen} onChange={onChange} />
|
||||
{$open ? (
|
||||
<div style={{ position: 'absolute', zIndex: '2' }}>
|
||||
<div
|
||||
@ -58,11 +58,3 @@ export default function ColorPicker(props) {
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
ColorPicker.defaultProps = {
|
||||
variant: 'default',
|
||||
label: '',
|
||||
fullWidth: false,
|
||||
value: 'rgba(255, 255, 255, 1)',
|
||||
onChange: () => {},
|
||||
};
|
||||
|
||||
@ -8,10 +8,9 @@ import FileCopyIcon from '@mui/icons-material/FileCopy';
|
||||
import CopyToClipboard from '../utils/clipboard';
|
||||
import NotifyContext from '../contexts/Notify';
|
||||
|
||||
export default function CopyButton(props) {
|
||||
export default function CopyButton({ variant = 'outlined', color = 'default', size = 'small', value = '', children = null }) {
|
||||
const notify = useContext(NotifyContext);
|
||||
const { i18n } = useLingui();
|
||||
const { children, value, ...other } = props;
|
||||
|
||||
const handleCopy = async () => {
|
||||
const success = await CopyToClipboard(value);
|
||||
@ -24,12 +23,8 @@ export default function CopyButton(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<Button {...other} endIcon={<FileCopyIcon />} onClick={handleCopy}>
|
||||
<Button variant={variant} color={color} size={size} endIcon={<FileCopyIcon />} onClick={handleCopy}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
CopyButton.defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Duration(props) {
|
||||
const fullSeconds = parseInt(Math.floor(props.seconds));
|
||||
export default function Duration({ seconds = 0 }) {
|
||||
const fullSeconds = parseInt(Math.floor(seconds));
|
||||
const s = fullSeconds % 60;
|
||||
const m = parseInt(fullSeconds / 60) % 60;
|
||||
const h = parseInt(fullSeconds / (60 * 60)) % 24;
|
||||
const d = parseInt(fullSeconds / (60 * 60 * 24));
|
||||
|
||||
let duration = '.' + ((props.seconds - fullSeconds) * 100).toFixed(0);
|
||||
let duration = '.' + ((seconds - fullSeconds) * 100).toFixed(0);
|
||||
|
||||
if (s < 10) {
|
||||
duration = ':0' + s + duration;
|
||||
@ -33,7 +33,3 @@ export default function Duration(props) {
|
||||
|
||||
return <React.Fragment>{duration}</React.Fragment>;
|
||||
}
|
||||
|
||||
Duration.defaultProps = {
|
||||
seconds: 0,
|
||||
};
|
||||
|
||||
@ -12,40 +12,46 @@ import * as Decoders from './coders/Decoders';
|
||||
import Select from './Select';
|
||||
import H from '../utils/help';
|
||||
|
||||
export default function EncodingSelect(props) {
|
||||
export default function EncodingSelect({
|
||||
type = '',
|
||||
streams = [],
|
||||
profile = {},
|
||||
codecs = [],
|
||||
skills = {},
|
||||
onChange = function (encoder, decoder, automatic) {},
|
||||
}) {
|
||||
const { i18n } = useLingui();
|
||||
|
||||
const profile = props.profile;
|
||||
let availableEncoders = [];
|
||||
let availableDecoders = [];
|
||||
|
||||
if (props.type === 'video') {
|
||||
availableEncoders = props.skills.encoders.video;
|
||||
availableDecoders = props.skills.decoders.video;
|
||||
} else if (props.type === 'audio') {
|
||||
availableEncoders = props.skills.encoders.audio;
|
||||
if (type === 'video') {
|
||||
availableEncoders = skills.encoders.video;
|
||||
availableDecoders = skills.decoders.video;
|
||||
} else if (type === 'audio') {
|
||||
availableEncoders = skills.encoders.audio;
|
||||
}
|
||||
|
||||
const handleDecoderChange = (event) => {
|
||||
const decoder = profile.decoder;
|
||||
const stream = props.streams[profile.stream];
|
||||
const stream = streams[profile.stream];
|
||||
decoder.coder = event.target.value;
|
||||
|
||||
// If the coder changes, use the coder's default settings
|
||||
let c = null;
|
||||
if (props.type === 'audio') {
|
||||
if (type === 'audio') {
|
||||
c = Decoders.Audio.Get(decoder.coder);
|
||||
} else if (props.type === 'video') {
|
||||
} else if (type === 'video') {
|
||||
c = Decoders.Video.Get(decoder.coder);
|
||||
}
|
||||
|
||||
if (c !== null) {
|
||||
const defaults = c.defaults(stream, props.skills);
|
||||
const defaults = c.defaults(stream, skills);
|
||||
decoder.settings = defaults.settings;
|
||||
decoder.mapping = defaults.mapping;
|
||||
}
|
||||
|
||||
props.onChange(profile.encoder, decoder, false);
|
||||
onChange(profile.encoder, decoder, false);
|
||||
};
|
||||
|
||||
const handleDecoderSettingsChange = (settings, mapping, automatic) => {
|
||||
@ -54,29 +60,29 @@ export default function EncodingSelect(props) {
|
||||
decoder.settings = settings;
|
||||
decoder.mapping = mapping;
|
||||
|
||||
props.onChange(profile.encoder, decoder, automatic);
|
||||
onChange(profile.encoder, decoder, automatic);
|
||||
};
|
||||
|
||||
const handleEncoderChange = (event) => {
|
||||
const encoder = profile.encoder;
|
||||
const stream = props.streams[profile.stream];
|
||||
const stream = streams[profile.stream];
|
||||
encoder.coder = event.target.value;
|
||||
|
||||
// If the coder changes, use the coder's default settings
|
||||
let c = null;
|
||||
if (props.type === 'audio') {
|
||||
if (type === 'audio') {
|
||||
c = Encoders.Audio.Get(encoder.coder);
|
||||
} else if (props.type === 'video') {
|
||||
} else if (type === 'video') {
|
||||
c = Encoders.Video.Get(encoder.coder);
|
||||
}
|
||||
|
||||
if (c !== null) {
|
||||
const defaults = c.defaults(stream, props.skills);
|
||||
const defaults = c.defaults(stream, skills);
|
||||
encoder.settings = defaults.settings;
|
||||
encoder.mapping = defaults.mapping;
|
||||
}
|
||||
|
||||
props.onChange(encoder, profile.decoder, false);
|
||||
onChange(encoder, profile.decoder, false);
|
||||
};
|
||||
|
||||
const handleEncoderSettingsChange = (settings, mapping, automatic) => {
|
||||
@ -85,7 +91,7 @@ export default function EncodingSelect(props) {
|
||||
encoder.settings = settings;
|
||||
encoder.mapping = mapping;
|
||||
|
||||
props.onChange(encoder, profile.decoder, automatic);
|
||||
onChange(encoder, profile.decoder, automatic);
|
||||
};
|
||||
|
||||
const handleEncoderHelp = (topic) => (event) => {
|
||||
@ -96,8 +102,8 @@ export default function EncodingSelect(props) {
|
||||
let stream = null;
|
||||
|
||||
if (profile.stream >= 0) {
|
||||
if (profile.stream < props.streams.length) {
|
||||
stream = props.streams[profile.stream];
|
||||
if (profile.stream < streams.length) {
|
||||
stream = streams[profile.stream];
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,18 +111,18 @@ export default function EncodingSelect(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stream.type !== props.type) {
|
||||
if (stream.type !== type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let allowCopy = props.codecs.includes(stream.codec);
|
||||
let allowCopy = codecs.includes(stream.codec);
|
||||
let encoderRegistry = null;
|
||||
let decoderRegistry = null;
|
||||
|
||||
if (props.type === 'video') {
|
||||
if (type === 'video') {
|
||||
encoderRegistry = Encoders.Video;
|
||||
decoderRegistry = Decoders.Video;
|
||||
} else if (props.type === 'audio') {
|
||||
} else if (type === 'audio') {
|
||||
encoderRegistry = Encoders.Audio;
|
||||
decoderRegistry = Decoders.Audio;
|
||||
} else {
|
||||
@ -130,9 +136,9 @@ export default function EncodingSelect(props) {
|
||||
if (coder !== null && availableEncoders.includes(coder.coder)) {
|
||||
const Settings = coder.component;
|
||||
|
||||
encoderSettings = <Settings stream={stream} settings={profile.encoder.settings} skills={props.skills} onChange={handleEncoderSettingsChange} />;
|
||||
encoderSettings = <Settings stream={stream} settings={profile.encoder.settings} skills={skills} onChange={handleEncoderSettingsChange} />;
|
||||
|
||||
if (props.type === 'video' && !['copy', 'none', 'rawvideo'].includes(coder.coder)) {
|
||||
if (type === 'video' && !['copy', 'none', 'rawvideo'].includes(coder.coder)) {
|
||||
encoderSettingsHelp = handleEncoderHelp(coder.coder);
|
||||
}
|
||||
}
|
||||
@ -146,7 +152,7 @@ export default function EncodingSelect(props) {
|
||||
}
|
||||
|
||||
// Is the encoder in the list of codec we allow as target?
|
||||
if (!props.codecs.includes(c.codec)) {
|
||||
if (!codecs.includes(c.codec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -187,7 +193,7 @@ export default function EncodingSelect(props) {
|
||||
if (c !== null && availableDecoders.includes(c.coder)) {
|
||||
const Settings = c.component;
|
||||
|
||||
decoderSettings = <Settings stream={stream} settings={profile.decoder.settings} skills={props.skills} onChange={handleDecoderSettingsChange} />;
|
||||
decoderSettings = <Settings stream={stream} settings={profile.decoder.settings} skills={skills} onChange={handleDecoderSettingsChange} />;
|
||||
}
|
||||
|
||||
// List all decoders for the codec of the stream
|
||||
@ -244,12 +250,3 @@ export default function EncodingSelect(props) {
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
EncodingSelect.defaultProps = {
|
||||
type: '',
|
||||
streams: [],
|
||||
profile: {},
|
||||
codecs: [],
|
||||
skills: {},
|
||||
onChange: function (encoder, decoder, automatic) {},
|
||||
};
|
||||
|
||||
@ -32,7 +32,7 @@ const HtmlTooltip = withStyles((theme) => ({
|
||||
},
|
||||
}))(Tooltip);
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ style = null }) {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<HtmlTooltip
|
||||
@ -44,9 +44,7 @@ export default function Component(props) {
|
||||
placement="right"
|
||||
arrow
|
||||
>
|
||||
<Chip size="small" label="ENV" className={classes.root} {...props} />
|
||||
<Chip size="small" label="ENV" className={classes.root} style={style} />
|
||||
</HtmlTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {};
|
||||
|
||||
@ -1,28 +1,21 @@
|
||||
import React from 'react';
|
||||
|
||||
// Adapted from https://stackoverflow.com/questions/10420352/converting-file-size-in-bytes-to-human-readable-string
|
||||
export default function Filesize(props) {
|
||||
let bytes = props.bytes;
|
||||
const thresh = props.si ? 1000 : 1024;
|
||||
export default function Filesize({ bytes = 0, si = false, digits = 1 }) {
|
||||
const thresh = si ? 1000 : 1024;
|
||||
|
||||
if (Math.abs(bytes) < thresh) {
|
||||
return bytes + ' B';
|
||||
}
|
||||
|
||||
const units = props.si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||
const units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||
let u = -1;
|
||||
const r = 10 ** props.digits;
|
||||
const r = 10 ** digits;
|
||||
|
||||
do {
|
||||
bytes /= thresh;
|
||||
++u;
|
||||
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
||||
|
||||
return <React.Fragment>{bytes.toFixed(props.digits) + ' ' + units[u]}</React.Fragment>;
|
||||
return <React.Fragment>{bytes.toFixed(digits) + ' ' + units[u]}</React.Fragment>;
|
||||
}
|
||||
|
||||
Filesize.defaultProps = {
|
||||
bytes: 0,
|
||||
si: false,
|
||||
digits: 1,
|
||||
};
|
||||
|
||||
@ -10,9 +10,7 @@ import * as Filters from './filters';
|
||||
// Import all encoders (audio/video)
|
||||
import * as Encoders from './coders/Encoders';
|
||||
|
||||
export default function FilterSelect(props) {
|
||||
const profile = props.profile;
|
||||
|
||||
export default function FilterSelect({ type = '', profile = {}, availableFilters = [], onChange = function (filter, automatic) {} }) {
|
||||
// handleFilterChange
|
||||
// what: Filter name
|
||||
// settings (component settings): {Key: Value}
|
||||
@ -28,7 +26,7 @@ export default function FilterSelect(props) {
|
||||
|
||||
// Get the order of the filters
|
||||
let filterOrder = [];
|
||||
if (props.type === 'video') {
|
||||
if (type === 'video') {
|
||||
filterOrder = Filters.Video.Filters();
|
||||
} else {
|
||||
filterOrder = Filters.Audio.Filters();
|
||||
@ -48,14 +46,14 @@ export default function FilterSelect(props) {
|
||||
|
||||
filter.graph = graphs.join(',');
|
||||
|
||||
props.onChange(filter, automatic);
|
||||
onChange(filter, automatic);
|
||||
};
|
||||
|
||||
// Set filterRegistry by type
|
||||
let filterRegistry = null;
|
||||
if (props.type === 'video') {
|
||||
if (type === 'video') {
|
||||
filterRegistry = Filters.Video;
|
||||
} else if (props.type === 'audio') {
|
||||
} else if (type === 'audio') {
|
||||
filterRegistry = Filters.Audio;
|
||||
} else {
|
||||
return null;
|
||||
@ -64,10 +62,10 @@ export default function FilterSelect(props) {
|
||||
// Checks the state of hwaccel (gpu encoding)
|
||||
let encoderRegistry = null;
|
||||
let hwaccel = false;
|
||||
if (props.type === 'video') {
|
||||
if (type === 'video') {
|
||||
encoderRegistry = Encoders.Video;
|
||||
for (let encoder of encoderRegistry.List()) {
|
||||
if (encoder.codec === props.profile.encoder.coder && encoder.hwaccel) {
|
||||
if (encoder.codec === profile.encoder.coder && encoder.hwaccel) {
|
||||
hwaccel = true;
|
||||
}
|
||||
}
|
||||
@ -78,7 +76,7 @@ export default function FilterSelect(props) {
|
||||
if (!hwaccel) {
|
||||
for (let c of filterRegistry.List()) {
|
||||
// Checks FFmpeg skills (filter is available)
|
||||
if (props.availableFilters.includes(c.filter)) {
|
||||
if (availableFilters.includes(c.filter)) {
|
||||
const Settings = c.component;
|
||||
|
||||
if (!(c.filter in profile.filter.settings)) {
|
||||
@ -125,10 +123,3 @@ export default function FilterSelect(props) {
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
FilterSelect.defaultProps = {
|
||||
type: '',
|
||||
profile: {},
|
||||
availableFilters: [],
|
||||
onChange: function (filter, automatic) {},
|
||||
};
|
||||
|
||||
@ -9,15 +9,37 @@ const useStyles = makeStyles((theme) => ({
|
||||
height: '56px!important',
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
// component="label" variant={variant} color={color} disabled={disabled}
|
||||
// onClick disabled
|
||||
// target="blank" href={stream_key_link} component="a"
|
||||
export default function Component({
|
||||
component = 'label',
|
||||
variant = 'outlined',
|
||||
size = 'large',
|
||||
color = 'primary',
|
||||
disabled = false,
|
||||
target = 'blank',
|
||||
href = '#',
|
||||
className = null,
|
||||
onClick = () => {},
|
||||
children = null,
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Button variant="outlined" size="large" fullWidth color="primary" className={classes.button} {...props}>
|
||||
{props.children}
|
||||
<Button
|
||||
component={component}
|
||||
variant={variant}
|
||||
size={size}
|
||||
disabled={disabled}
|
||||
fullWidth
|
||||
color={color}
|
||||
className={className ? className : classes.button}
|
||||
target={target}
|
||||
href={href}
|
||||
onClick={onClick}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {};
|
||||
|
||||
@ -28,7 +28,7 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function LanguageSelect(props) {
|
||||
export default function LanguageSelect({ onChange = function (lang) {} }) {
|
||||
const classes = useStyles();
|
||||
const { i18n } = useLingui();
|
||||
|
||||
@ -37,7 +37,7 @@ export default function LanguageSelect(props) {
|
||||
|
||||
i18n.activate(language);
|
||||
|
||||
props.onChange(language);
|
||||
onChange(language);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -60,7 +60,3 @@ export default function LanguageSelect(props) {
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
LanguageSelect.defaultProps = {
|
||||
onChange: function (lang) {},
|
||||
};
|
||||
|
||||
@ -44,25 +44,23 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const Component = React.forwardRef((props, ref) => {
|
||||
const Component = React.forwardRef(({ title = '', onClose = null, onHelp = null, className = null, style = null, children = null }, ref) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const { title, onClose, onHelp, ...other } = props;
|
||||
|
||||
return (
|
||||
<Paper className={classes.modalPaper} elevation={0} tabIndex={-1} ref={ref} {...other}>
|
||||
<Paper className={className ? className : classes.modalPaper} elevation={0} tabIndex={-1} ref={ref} style={style}>
|
||||
<Grid container spacing={0}>
|
||||
<Grid item xs={12} className={classes.modalHeader}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" spacing={2}>
|
||||
<Typography variant="button">{props.title}</Typography>
|
||||
<Typography variant="button">{title}</Typography>
|
||||
<Stack direction="row" justifyContent="flex-end" alignItems="center" spacing={2}>
|
||||
{typeof props.onHelp === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={props.onHelp}>
|
||||
{typeof onHelp === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={onHelp}>
|
||||
<HelpIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
{typeof props.onClose === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={props.onClose}>
|
||||
{typeof onClose === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={onClose}>
|
||||
<CloseIcon fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
@ -70,10 +68,10 @@ const Component = React.forwardRef((props, ref) => {
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{props.children}
|
||||
{children}
|
||||
<Grid container spacing={0}>
|
||||
<Grid item xs={12} className={classes.modalFooter}>
|
||||
<Button variant="outlined" color="default" onClick={props.onClose}>
|
||||
<Button variant="outlined" color="default" onClick={onClose}>
|
||||
<Trans>Close</Trans>
|
||||
</Button>
|
||||
</Grid>
|
||||
@ -83,9 +81,3 @@ const Component = React.forwardRef((props, ref) => {
|
||||
});
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
title: '',
|
||||
onClose: null,
|
||||
onHelp: null,
|
||||
};
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import OutlinedInput from '@mui/material/OutlinedInput';
|
||||
import Select from '@mui/material/Select';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
|
||||
const MenuProps = {
|
||||
PaperProps: {
|
||||
@ -13,22 +15,42 @@ const MenuProps = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Component(props) {
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
fontWeight: 'bold',
|
||||
backgroundColor: theme.palette.background.dark1,
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component({
|
||||
variant = 'outlined',
|
||||
label = '',
|
||||
value = [],
|
||||
disabled = false,
|
||||
renderValue = (selected) => selected.join(', '),
|
||||
onChange = function (event) {},
|
||||
items = [],
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<FormControl variant={props.variant} disabled={props.disabled} fullWidth>
|
||||
<InputLabel>{props.label}</InputLabel>
|
||||
<Select multiple value={props.value} onChange={props.onChange} input={<OutlinedInput />} renderValue={props.renderValue} MenuProps={MenuProps}>
|
||||
{props.children}
|
||||
<FormControl variant={variant} disabled={disabled} fullWidth>
|
||||
<InputLabel>{label}</InputLabel>
|
||||
<Select multiple value={value} onChange={onChange} input={<OutlinedInput label={label} />} renderValue={renderValue} MenuProps={MenuProps}>
|
||||
{items.map((item) => {
|
||||
if (!('key' in item)) {
|
||||
item.key = item.value;
|
||||
}
|
||||
if (!('name' in item)) {
|
||||
item.name = item.value;
|
||||
}
|
||||
return (
|
||||
<MenuItem key={item.key} value={item.value} className={item.selected ? classes.root : ''}>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
variant: 'outlined',
|
||||
label: '',
|
||||
value: [],
|
||||
disabled: false,
|
||||
renderValue: (selected) => selected.join(', '),
|
||||
onChange: function (event) {},
|
||||
};
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
fontWeight: 'bold',
|
||||
backgroundColor: theme.palette.background.dark1,
|
||||
},
|
||||
}));
|
||||
|
||||
const Component = React.forwardRef((props, ref) => {
|
||||
const classes = useStyles();
|
||||
|
||||
const { name, value, selected, ...other } = props;
|
||||
|
||||
return (
|
||||
<MenuItem value={props.value} className={props.selected ? classes.root : ''} ref={ref} {...other}>
|
||||
{props.name}
|
||||
</MenuItem>
|
||||
);
|
||||
});
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
name: '',
|
||||
value: '',
|
||||
selected: false,
|
||||
};
|
||||
@ -2,21 +2,15 @@ import React from 'react';
|
||||
|
||||
import { i18n } from '@lingui/core';
|
||||
|
||||
export default function Number(props) {
|
||||
export default function Number({ value = 0, digits = 0, minDigits = 0 }) {
|
||||
const options = {
|
||||
minimumFractionDigits: props.minDigits,
|
||||
maximumFractionDigits: props.digits,
|
||||
minimumFractionDigits: minDigits,
|
||||
maximumFractionDigits: digits,
|
||||
};
|
||||
|
||||
if (options.minimumFractionDigits > options.maximumFractionDigits) {
|
||||
options.maximumFractionDigits = options.minimumFractionDigits;
|
||||
}
|
||||
|
||||
return <React.Fragment>{i18n.number(props.value, options)}</React.Fragment>;
|
||||
return <React.Fragment>{i18n.number(value, options)}</React.Fragment>;
|
||||
}
|
||||
|
||||
Number.defaultProps = {
|
||||
value: 0,
|
||||
digits: 0,
|
||||
minDigits: 0,
|
||||
};
|
||||
|
||||
@ -18,31 +18,34 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const Component = React.forwardRef((props, ref) => {
|
||||
const classes = useStyles();
|
||||
let { marginBottom, xs, sm, md, ld, className, elevation, ...other } = props;
|
||||
const Component = React.forwardRef(
|
||||
(
|
||||
{
|
||||
xs = 12,
|
||||
sm = undefined,
|
||||
md = undefined,
|
||||
lg = undefined,
|
||||
elevation = 0,
|
||||
className = 'paper',
|
||||
style = null,
|
||||
marginBottom = '6em',
|
||||
tabIndex = 0,
|
||||
children = null,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const classes = useStyles();
|
||||
|
||||
elevation = 0;
|
||||
|
||||
return (
|
||||
<Grid container justifyContent="center" spacing={1} style={{ marginBottom: props.marginBottom }}>
|
||||
<Grid item xs={props.xs} sm={props.sm} md={props.md} lg={props.lg}>
|
||||
<Paper className={classes[props.className]} elevation={elevation} ref={ref} {...other}>
|
||||
{props.children}
|
||||
</Paper>
|
||||
return (
|
||||
<Grid container justifyContent="center" spacing={1} style={{ marginBottom: marginBottom }}>
|
||||
<Grid item xs={xs} sm={sm} md={md} lg={lg}>
|
||||
<Paper className={classes[className]} elevation={elevation} ref={ref} tabIndex={tabIndex} style={style}>
|
||||
{children}
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
marginBottom: '6em',
|
||||
xs: 12,
|
||||
sm: undefined,
|
||||
md: undefined,
|
||||
lg: undefined,
|
||||
elevation: 0,
|
||||
className: 'paper',
|
||||
};
|
||||
|
||||
@ -2,19 +2,14 @@ import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
|
||||
const Component = function (props) {
|
||||
const Component = function ({ spacing = 3, textAlign = 'left', children = null }) {
|
||||
return (
|
||||
<Grid container justifyContent="center" spacing={props.spacing} align={props.textAlign}>
|
||||
<Grid container justifyContent="center" spacing={spacing} align={textAlign}>
|
||||
<Grid item xs={12}>
|
||||
{props.children}
|
||||
{children}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
spacing: 3,
|
||||
textAlign: 'left',
|
||||
};
|
||||
|
||||
@ -19,22 +19,17 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const Component = function (props) {
|
||||
const Component = function ({ buttonsLeft = null, buttonsRight = null }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<Grid item xs={12} className={classes.root}>
|
||||
<div>{props.buttonsRight}</div>
|
||||
{props.buttonsLeft}
|
||||
<div>{buttonsRight}</div>
|
||||
{buttonsLeft}
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
buttonsLeft: null,
|
||||
buttonsRight: null,
|
||||
};
|
||||
|
||||
@ -19,47 +19,36 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const Component = function (props) {
|
||||
const Component = function ({ spacing = 0, padding = null, title = '', variant = 'pagetitle', onAbort = null, onHelp = null, onEdit = null, onAdd = null }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Grid container spacing={props.spacing} padding={props.padding}>
|
||||
<Grid container spacing={spacing} padding={padding}>
|
||||
<Grid item xs={12} className={classes.root}>
|
||||
{typeof props.onAbort === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={props.onAbort}>
|
||||
{typeof onAbort === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={onAbort}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
{typeof props.onEdit === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={props.onEdit}>
|
||||
{typeof onEdit === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={onEdit}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
{typeof props.onAdd === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={props.onAdd}>
|
||||
{typeof onAdd === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={onAdd}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
{typeof props.onHelp === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={props.onHelp}>
|
||||
{typeof onHelp === 'function' && (
|
||||
<IconButton color="inherit" size="small" onClick={onHelp}>
|
||||
<HelpIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
<Typography variant={props.variant}>{props.title}</Typography>
|
||||
<Typography variant={variant}>{title}</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Component;
|
||||
|
||||
Component.defaultProps = {
|
||||
spacing: 0,
|
||||
padding: null,
|
||||
title: '',
|
||||
variant: 'pagetitle',
|
||||
onAbort: null,
|
||||
onHelp: null,
|
||||
onEdit: null,
|
||||
onAdd: null,
|
||||
};
|
||||
|
||||
@ -10,14 +10,8 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ image = '', title = '', height = '0px' }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return <CardMedia className={classes.media} style={{ height: props.height }} image={props.image} title={props.title} />;
|
||||
return <CardMedia className={classes.media} style={{ height: height }} image={image} title={title} />;
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
image: '',
|
||||
title: '',
|
||||
height: '0px',
|
||||
};
|
||||
|
||||
@ -11,8 +11,20 @@ import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
|
||||
import Env from './Env';
|
||||
|
||||
export default function Password(props) {
|
||||
const [$visible, setVisible] = React.useState(props.show);
|
||||
export default function Password({
|
||||
id = 'password',
|
||||
label = '',
|
||||
value = '',
|
||||
disabled = false,
|
||||
autoComplete = 'current-password',
|
||||
env = false,
|
||||
show = false,
|
||||
helperText = false,
|
||||
inputProps = {},
|
||||
error = false,
|
||||
onChange = function (value) {},
|
||||
}) {
|
||||
const [$visible, setVisible] = React.useState(show);
|
||||
|
||||
const handleClickShowPassword = () => {
|
||||
setVisible(!$visible);
|
||||
@ -27,50 +39,36 @@ export default function Password(props) {
|
||||
<IconButton edge="end" size="large">
|
||||
<VisibilityOff />
|
||||
</IconButton>
|
||||
{props.env ? <Env /> : null}
|
||||
{env ? <Env /> : null}
|
||||
</InputAdornment>
|
||||
);
|
||||
|
||||
if (props.disabled === false) {
|
||||
if (disabled === false) {
|
||||
adornment = (
|
||||
<InputAdornment position="end">
|
||||
<IconButton onClick={handleClickShowPassword} onMouseDown={handleMouseDownPassword} edge="end" size="large">
|
||||
{$visible ? <Visibility /> : <VisibilityOff />}
|
||||
</IconButton>
|
||||
{props.env ? <Env /> : null}
|
||||
{env ? <Env /> : null}
|
||||
</InputAdornment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl variant="outlined" disabled={props.disabled} fullWidth>
|
||||
<InputLabel htmlFor={props.id}>{props.label}</InputLabel>
|
||||
<FormControl variant="outlined" disabled={disabled} fullWidth>
|
||||
<InputLabel htmlFor={id}>{label}</InputLabel>
|
||||
<OutlinedInput
|
||||
id={props.id}
|
||||
type={$visible && !props.disabled ? 'text' : 'password'}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
id={id}
|
||||
type={$visible && !disabled ? 'text' : 'password'}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
endAdornment={adornment}
|
||||
label={props.label}
|
||||
autoComplete={props.autoComplete}
|
||||
inputProps={props.inputProps}
|
||||
error={props.error}
|
||||
label={label}
|
||||
autoComplete={autoComplete}
|
||||
inputProps={inputProps}
|
||||
error={error}
|
||||
/>
|
||||
{props.helperText && <FormHelperText>{props.helperText}</FormHelperText>}
|
||||
{helperText && <FormHelperText>{helperText}</FormHelperText>}
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
Password.defaultProps = {
|
||||
id: 'password',
|
||||
label: '',
|
||||
value: '',
|
||||
disabled: false,
|
||||
autoComplete: 'current-password',
|
||||
env: false,
|
||||
show: false,
|
||||
helperText: false,
|
||||
inputProps: {},
|
||||
error: false,
|
||||
onChange: function (value) {},
|
||||
};
|
||||
|
||||
@ -2,22 +2,43 @@ import React from 'react';
|
||||
|
||||
import VideoJS from './videojs';
|
||||
|
||||
export default function Player(props) {
|
||||
const type = props.type ? props.type : 'videojs-internal';
|
||||
export default function Player({
|
||||
type = 'videojs-internal',
|
||||
source = '',
|
||||
poster = '',
|
||||
controls = false,
|
||||
autoplay = false,
|
||||
mute = false,
|
||||
logo = {
|
||||
image: '',
|
||||
position: 'top-right',
|
||||
link: '',
|
||||
},
|
||||
ga = {
|
||||
account: '',
|
||||
name: '',
|
||||
},
|
||||
colors = {
|
||||
seekbar: '#fff',
|
||||
buttons: '#fff',
|
||||
},
|
||||
statistics = false,
|
||||
}) {
|
||||
type = type ? type : 'videojs-internal';
|
||||
|
||||
if (type === 'videojs-internal' || type === 'videojs-public') {
|
||||
const config = {
|
||||
controls: props.controls,
|
||||
poster: props.poster,
|
||||
autoplay: type === 'videojs-internal' ? true : props.autoplay ? (props.mute === 'muted' ? true : false) : false,
|
||||
muted: type === 'videojs-internal' ? 'muted' : props.mute,
|
||||
controls: controls,
|
||||
poster: poster,
|
||||
autoplay: type === 'videojs-internal' ? true : autoplay ? (mute === 'muted' ? true : false) : false,
|
||||
muted: type === 'videojs-internal' ? 'muted' : mute,
|
||||
liveui: true,
|
||||
responsive: true,
|
||||
fluid: true,
|
||||
plugins: {
|
||||
reloadSourceOnError: {},
|
||||
},
|
||||
sources: [{ src: props.source, type: 'application/x-mpegURL' }],
|
||||
sources: [{ src: source, type: 'application/x-mpegURL' }],
|
||||
};
|
||||
|
||||
return (
|
||||
@ -25,7 +46,7 @@ export default function Player(props) {
|
||||
type={type}
|
||||
options={config}
|
||||
onReady={(player) => {
|
||||
if (props.logo.image.length !== 0) {
|
||||
if (logo.image.length !== 0) {
|
||||
var overlay = null;
|
||||
|
||||
var imgTag = new Image();
|
||||
@ -33,11 +54,11 @@ export default function Player(props) {
|
||||
imgTag.setAttribute('width', this.width);
|
||||
imgTag.setAttribute('height'.this.height);
|
||||
};
|
||||
imgTag.src = props.logo.image + '?' + Math.random();
|
||||
imgTag.src = logo.image + '?' + Math.random();
|
||||
|
||||
if (props.logo.link.length !== 0) {
|
||||
if (logo.link.length !== 0) {
|
||||
var aTag = document.createElement('a');
|
||||
aTag.setAttribute('href', props.logo.link);
|
||||
aTag.setAttribute('href', logo.link);
|
||||
aTag.setAttribute('target', '_blank');
|
||||
aTag.appendChild(imgTag);
|
||||
overlay = aTag.outerHTML;
|
||||
@ -47,12 +68,12 @@ export default function Player(props) {
|
||||
|
||||
if (player.overlay) {
|
||||
player.overlay({
|
||||
align: props.logo.position,
|
||||
align: logo.position,
|
||||
overlays: [
|
||||
{
|
||||
showBackground: false,
|
||||
content: overlay,
|
||||
start: 'playing',
|
||||
start: 'play',
|
||||
end: 'pause',
|
||||
},
|
||||
],
|
||||
@ -60,7 +81,7 @@ export default function Player(props) {
|
||||
}
|
||||
}
|
||||
|
||||
if (props.autoplay === true) {
|
||||
if (autoplay === true) {
|
||||
// https://videojs.com/blog/autoplay-best-practices-with-video-js/
|
||||
const p = player.play();
|
||||
|
||||
@ -73,7 +94,7 @@ export default function Player(props) {
|
||||
},
|
||||
() => {
|
||||
// autoplay did not work
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -82,26 +103,3 @@ export default function Player(props) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Player.defaultProps = {
|
||||
type: 'videojs-internal',
|
||||
source: '',
|
||||
poster: '',
|
||||
controls: false,
|
||||
autoplay: false,
|
||||
mute: false,
|
||||
logo: {
|
||||
image: '',
|
||||
position: 'top-right',
|
||||
link: '',
|
||||
},
|
||||
ga: {
|
||||
account: '',
|
||||
name: '',
|
||||
},
|
||||
colors: {
|
||||
seekbar: '#fff',
|
||||
buttons: '#fff',
|
||||
},
|
||||
statistics: false,
|
||||
};
|
||||
|
||||
1
src/misc/Player/videojs-overlay.css
Normal file
1
src/misc/Player/videojs-overlay.css
Normal file
@ -0,0 +1 @@
|
||||
.video-js .vjs-overlay{color:#fff;position:absolute;text-align:center}.video-js .vjs-overlay-no-background{max-width:33%}.video-js .vjs-overlay-background{background-color:#646464;background-color:hsla(0,0%,100%,.4);border-radius:3px;padding:10px;width:33%}.video-js .vjs-overlay-top-left{top:5px;left:5px}.video-js .vjs-overlay-top{left:50%;margin-left:-16.5%;top:5px}.video-js .vjs-overlay-top-right{right:5px;top:5px}.video-js .vjs-overlay-right{right:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-bottom-right{bottom:3.5em;right:5px}.video-js .vjs-overlay-bottom{bottom:3.5em;left:50%;margin-left:-16.5%}.video-js .vjs-overlay-bottom-left{bottom:3.5em;left:5px}.video-js .vjs-overlay-left{left:5px;top:50%;transform:translateY(-50%)}.video-js .vjs-overlay-center{left:50%;margin-left:-16.5%;top:50%;transform:translateY(-50%)}.video-js .vjs-no-flex .vjs-overlay-left,.video-js .vjs-no-flex .vjs-overlay-center,.video-js .vjs-no-flex .vjs-overlay-right{margin-top:-15px}
|
||||
413
src/misc/Player/videojs-overlay.es.js
Normal file
413
src/misc/Player/videojs-overlay.es.js
Normal file
@ -0,0 +1,413 @@
|
||||
/*! @name videojs-overlay @version 4.0.0 @license Apache-2.0 */
|
||||
import videojs from 'video.js';
|
||||
import window from 'global/window';
|
||||
|
||||
const initOverlayComponent = (videojs) => {
|
||||
const Component = videojs.getComponent('Component');
|
||||
const dom = videojs.dom || videojs;
|
||||
|
||||
/**
|
||||
* Whether the value is a `Number`.
|
||||
*
|
||||
* Both `Infinity` and `-Infinity` are accepted, but `NaN` is not.
|
||||
*
|
||||
* @param {Number} n
|
||||
* @return {Boolean}
|
||||
*/
|
||||
|
||||
/* eslint-disable no-self-compare */
|
||||
const isNumber = (n) => typeof n === 'number' && n === n;
|
||||
/* eslint-enable no-self-compare */
|
||||
|
||||
/**
|
||||
* Whether a value is a string with no whitespace.
|
||||
*
|
||||
* @param {string} s
|
||||
* @return {boolean}
|
||||
*/
|
||||
const hasNoWhitespace = (s) => typeof s === 'string' && /^\S+$/.test(s);
|
||||
|
||||
/**
|
||||
* Overlay component.
|
||||
*
|
||||
* @class Overlay
|
||||
* @extends {videojs.Component}
|
||||
*/
|
||||
class Overlay extends Component {
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
['start', 'end'].forEach((key) => {
|
||||
const value = this.options_[key];
|
||||
if (isNumber(value)) {
|
||||
this[key + 'Event_'] = 'timeupdate';
|
||||
} else if (hasNoWhitespace(value)) {
|
||||
this[key + 'Event_'] = value;
|
||||
|
||||
// An overlay MUST have a start option. Otherwise, it's pointless.
|
||||
} else if (key === 'start') {
|
||||
throw new Error('invalid "start" option; expected number or string');
|
||||
}
|
||||
});
|
||||
|
||||
// video.js does not like components with multiple instances binding
|
||||
// events to the player because it tracks them at the player level,
|
||||
// not at the level of the object doing the binding. This could also be
|
||||
// solved with Function.prototype.bind (but not videojs.bind because of
|
||||
// its GUID magic), but the anonymous function approach avoids any issues
|
||||
// caused by crappy libraries clobbering Function.prototype.bind.
|
||||
// - https://github.com/videojs/video.js/issues/3097
|
||||
['endListener_', 'rewindListener_', 'startListener_'].forEach((name) => {
|
||||
this[name] = (e) => Overlay.prototype[name].call(this, e);
|
||||
});
|
||||
|
||||
// If the start event is a timeupdate, we need to watch for rewinds (i.e.,
|
||||
// when the user seeks backward).
|
||||
if (this.startEvent_ === 'timeupdate') {
|
||||
this.on(player, 'timeupdate', this.rewindListener_);
|
||||
}
|
||||
this.debug(`created, listening to "${this.startEvent_}" for "start" and "${this.endEvent_ || 'nothing'}" for "end"`);
|
||||
this.hide();
|
||||
}
|
||||
createEl() {
|
||||
const options = this.options_;
|
||||
const content = options.content;
|
||||
const background = options.showBackground ? 'vjs-overlay-background' : 'vjs-overlay-no-background';
|
||||
const el = dom.createEl('div', {
|
||||
className: `
|
||||
vjs-overlay
|
||||
vjs-overlay-${options.align}
|
||||
${options.class}
|
||||
${background}
|
||||
vjs-hidden
|
||||
`,
|
||||
});
|
||||
if (typeof content === 'string') {
|
||||
el.innerHTML = content;
|
||||
} else if (content instanceof window.DocumentFragment) {
|
||||
el.appendChild(content);
|
||||
} else {
|
||||
dom.appendContent(el, content);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs debug errors
|
||||
*
|
||||
* @param {...[type]} args [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
debug(...args) {
|
||||
if (!this.options_.debug) {
|
||||
return;
|
||||
}
|
||||
const log = videojs.log;
|
||||
let fn = log;
|
||||
|
||||
// Support `videojs.log.foo` calls.
|
||||
if (log.hasOwnProperty(args[0]) && typeof log[args[0]] === 'function') {
|
||||
fn = log[args.shift()];
|
||||
}
|
||||
fn(...[`overlay#${this.id()}: `, ...args]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the inherited method to perform some event binding
|
||||
*
|
||||
* @return {Overlay}
|
||||
*/
|
||||
hide() {
|
||||
super.hide();
|
||||
this.debug('hidden');
|
||||
this.debug(`bound \`startListener_\` to "${this.startEvent_}"`);
|
||||
|
||||
// Overlays without an "end" are valid.
|
||||
if (this.endEvent_) {
|
||||
this.debug(`unbound \`endListener_\` from "${this.endEvent_}"`);
|
||||
this.off(this.player(), this.endEvent_, this.endListener_);
|
||||
}
|
||||
this.on(this.player(), this.startEvent_, this.startListener_);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the overlay should hide.
|
||||
*
|
||||
* @param {number} time
|
||||
* The current time reported by the player.
|
||||
* @param {string} type
|
||||
* An event type.
|
||||
* @return {boolean}
|
||||
*/
|
||||
shouldHide_(time, type) {
|
||||
const end = this.options_.end;
|
||||
return isNumber(end) ? time >= end : end === type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the inherited method to perform some event binding
|
||||
*
|
||||
* @return {Overlay}
|
||||
*/
|
||||
show() {
|
||||
super.show();
|
||||
this.off(this.player(), this.startEvent_, this.startListener_);
|
||||
this.debug('shown');
|
||||
this.debug(`unbound \`startListener_\` from "${this.startEvent_}"`);
|
||||
|
||||
// Overlays without an "end" are valid.
|
||||
if (this.endEvent_) {
|
||||
this.debug(`bound \`endListener_\` to "${this.endEvent_}"`);
|
||||
this.on(this.player(), this.endEvent_, this.endListener_);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether or not the overlay should show.
|
||||
*
|
||||
* @param {number} time
|
||||
* The current time reported by the player.
|
||||
* @param {string} type
|
||||
* An event type.
|
||||
* @return {boolean}
|
||||
*/
|
||||
shouldShow_(time, type) {
|
||||
const start = this.options_.start;
|
||||
const end = this.options_.end;
|
||||
if (isNumber(start)) {
|
||||
if (isNumber(end)) {
|
||||
return time >= start && time < end;
|
||||
|
||||
// In this case, the start is a number and the end is a string. We need
|
||||
// to check whether or not the overlay has shown since the last seek.
|
||||
} else if (!this.hasShownSinceSeek_) {
|
||||
this.hasShownSinceSeek_ = true;
|
||||
return time >= start;
|
||||
}
|
||||
|
||||
// In this case, the start is a number and the end is a string, but
|
||||
// the overlay has shown since the last seek. This means that we need
|
||||
// to be sure we aren't re-showing it at a later time than it is
|
||||
// scheduled to appear.
|
||||
return Math.floor(time) === start;
|
||||
}
|
||||
return start === type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener that can trigger the overlay to show.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
startListener_(e) {
|
||||
const time = this.player().currentTime();
|
||||
if (this.shouldShow_(time, e.type)) {
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener that can trigger the overlay to show.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
endListener_(e) {
|
||||
const time = this.player().currentTime();
|
||||
if (this.shouldHide_(time, e.type)) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener that can looks for rewinds - that is, backward seeks
|
||||
* and may hide the overlay as needed.
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
rewindListener_(e) {
|
||||
const time = this.player().currentTime();
|
||||
const previous = this.previousTime_;
|
||||
const start = this.options_.start;
|
||||
const end = this.options_.end;
|
||||
|
||||
// Did we seek backward?
|
||||
if (time < previous) {
|
||||
this.debug('rewind detected');
|
||||
|
||||
// The overlay remains visible if two conditions are met: the end value
|
||||
// MUST be an integer and the the current time indicates that the
|
||||
// overlay should NOT be visible.
|
||||
if (isNumber(end) && !this.shouldShow_(time)) {
|
||||
this.debug(`hiding; ${end} is an integer and overlay should not show at this time`);
|
||||
this.hasShownSinceSeek_ = false;
|
||||
this.hide();
|
||||
|
||||
// If the end value is an event name, we cannot reliably decide if the
|
||||
// overlay should still be displayed based solely on time; so, we can
|
||||
// only queue it up for showing if the seek took us to a point before
|
||||
// the start time.
|
||||
} else if (hasNoWhitespace(end) && time < start) {
|
||||
this.debug(`hiding; show point (${start}) is before now (${time}) and end point (${end}) is an event`);
|
||||
this.hasShownSinceSeek_ = false;
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
this.previousTime_ = time;
|
||||
}
|
||||
}
|
||||
videojs.registerComponent('Overlay', Overlay);
|
||||
return Overlay;
|
||||
};
|
||||
|
||||
const Plugin = videojs.getPlugin('plugin');
|
||||
const defaults = {
|
||||
align: 'top-left',
|
||||
class: '',
|
||||
content: 'This overlay will show up while the video is playing',
|
||||
debug: false,
|
||||
showBackground: true,
|
||||
attachToControlBar: false,
|
||||
overlays: [
|
||||
{
|
||||
start: 'playing',
|
||||
end: 'paused',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* A plugin for handling overlays in the Brightcove Player.
|
||||
*/
|
||||
class OverlayPlugin extends Plugin {
|
||||
/**
|
||||
* Create an Overlay Plugin instance.
|
||||
*
|
||||
* @param {Player} player
|
||||
* A Video.js Player instance.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An options object.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player);
|
||||
this.reset(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one or more items to the existing list of overlays.
|
||||
*
|
||||
* @param {Object|Array} item
|
||||
* An item (or an array of items) to be added as overlay/s
|
||||
*
|
||||
* @return {Array[Overlay]}
|
||||
* The array of overlay objects that were added
|
||||
*/
|
||||
add(item) {
|
||||
if (!Array.isArray(item)) {
|
||||
item = [item];
|
||||
}
|
||||
const addedOverlays = this.mapOverlays_(item);
|
||||
this.player.overlays_ = this.player.overlays_.concat(addedOverlays);
|
||||
return addedOverlays;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Overlay} item
|
||||
* An item to be removed from the array of overlays
|
||||
*
|
||||
* @throws {Error}
|
||||
* Item to remove must be present in the array of overlays
|
||||
*
|
||||
*/
|
||||
remove(item) {
|
||||
const index = this.player.overlays_.indexOf(item);
|
||||
if (index !== -1) {
|
||||
item.el().parentNode.removeChild(item.el());
|
||||
this.player.overlays_.splice(index, 1);
|
||||
} else {
|
||||
this.player.log.warn('overlay does not exist and cannot be removed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the array of overlays used for the current video
|
||||
*
|
||||
* @return The array of overlay objects currently used by the plugin
|
||||
*/
|
||||
get() {
|
||||
return this.player.overlays_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the overlay options
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An options object.
|
||||
*/
|
||||
reset(options) {
|
||||
this.clearOverlays_();
|
||||
|
||||
// Use merge function based on video.js version.
|
||||
const merge = (videojs.obj && videojs.obj.merge) || videojs.mergeOptions;
|
||||
this.options = merge(defaults, options);
|
||||
const overlays = this.options.overlays;
|
||||
|
||||
// We don't want to keep the original array of overlay options around
|
||||
// because it doesn't make sense to pass it to each Overlay component.
|
||||
delete this.options.overlays;
|
||||
this.player.overlays_ = this.mapOverlays_(overlays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the plugin
|
||||
*/
|
||||
dispose() {
|
||||
this.clearOverlays_();
|
||||
delete this.player.overlays_;
|
||||
super.dispose();
|
||||
}
|
||||
clearOverlays_() {
|
||||
// Remove child components
|
||||
if (Array.isArray(this.player.overlays_)) {
|
||||
this.player.overlays_.forEach((overlay) => {
|
||||
this.player.removeChild(overlay);
|
||||
if (this.player.controlBar) {
|
||||
this.player.controlBar.removeChild(overlay);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
mapOverlays_(items) {
|
||||
return items.map((o) => {
|
||||
const mergeOptions = videojs.mergeOptions(this.options, o);
|
||||
const attachToControlBar = typeof mergeOptions.attachToControlBar === 'string' || mergeOptions.attachToControlBar === true;
|
||||
if (!this.player.controls() || !this.player.controlBar) {
|
||||
return this.player.addChild('overlay', mergeOptions);
|
||||
}
|
||||
if (attachToControlBar && mergeOptions.align.indexOf('bottom') !== -1) {
|
||||
let referenceChild = this.player.controlBar.children()[0];
|
||||
if (this.player.controlBar.getChild(mergeOptions.attachToControlBar) !== undefined) {
|
||||
referenceChild = this.player.controlBar.getChild(mergeOptions.attachToControlBar);
|
||||
}
|
||||
if (referenceChild) {
|
||||
const referenceChildIndex = this.player.controlBar.children().indexOf(referenceChild);
|
||||
const controlBarChild = this.player.controlBar.addChild('overlay', mergeOptions, referenceChildIndex);
|
||||
return controlBarChild;
|
||||
}
|
||||
}
|
||||
const playerChild = this.player.addChild('overlay', mergeOptions);
|
||||
this.player.el().insertBefore(playerChild.el(), this.player.controlBar.el());
|
||||
return playerChild;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var version = '4.0.0';
|
||||
|
||||
initOverlayComponent(videojs);
|
||||
OverlayPlugin.VERSION = version;
|
||||
videojs.registerPlugin('overlay', OverlayPlugin);
|
||||
|
||||
export { OverlayPlugin as default };
|
||||
@ -3,16 +3,24 @@ import React from 'react';
|
||||
import Grid from '@mui/material/Grid';
|
||||
|
||||
import videojs from 'video.js';
|
||||
import 'videojs-overlay';
|
||||
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';
|
||||
import 'videojs-overlay/dist/videojs-overlay.css';
|
||||
import './videojs-overlay.css';
|
||||
|
||||
export default function VideoJS(props) {
|
||||
export default function VideoJS({ type = 'videojs-internal', options = {}, onReady = null }) {
|
||||
const videoRef = React.useRef(null);
|
||||
const playerRef = React.useRef(null);
|
||||
const { options, onReady } = props;
|
||||
|
||||
const retryVideo = () => {
|
||||
const player = playerRef.current;
|
||||
if (player) {
|
||||
player.error(null); // Clear the error
|
||||
player.src(options.sources); // Reload the source
|
||||
player.play(); // Attempt to play again
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
// make sure Video.js player is only initialized once
|
||||
@ -25,20 +33,29 @@ export default function VideoJS(props) {
|
||||
}));
|
||||
|
||||
// add internal/public skin style
|
||||
if (props.type === 'videojs-public') {
|
||||
if (type === 'videojs-public') {
|
||||
player.addClass('vjs-public');
|
||||
} else {
|
||||
player.addClass('vjs-internal');
|
||||
}
|
||||
player.addClass('video-js');
|
||||
player.addClass('vjs-16-9');
|
||||
|
||||
// retry on MEDIA_ERR_NETWORK = 2 || 4
|
||||
player.on('error', () => {
|
||||
const error = player.error();
|
||||
if (error && (error.code === 2 || error.code === 4)) {
|
||||
setTimeout(retryVideo, 2000);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// you can update player here [update player through props]
|
||||
// const player = playerRef.current;
|
||||
// player.autoplay(options.autoplay);
|
||||
// player.src(options.sources);
|
||||
}
|
||||
}, [options, videoRef, onReady, props.type]);
|
||||
// eslint-disable-next-line
|
||||
}, [options, videoRef, onReady, type]);
|
||||
|
||||
// Dispose the Video.js player when the functional component unmounts
|
||||
React.useEffect(() => {
|
||||
@ -67,7 +84,3 @@ export default function VideoJS(props) {
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
VideoJS.defaultProps = {
|
||||
type: 'videojs-internal',
|
||||
};
|
||||
|
||||
@ -18,28 +18,92 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function init(props) {
|
||||
function init({
|
||||
valid = false,
|
||||
order = 'stop',
|
||||
state = 'disconnected',
|
||||
error = '',
|
||||
reconnect = -1,
|
||||
bitrate = 0,
|
||||
fps = 0,
|
||||
time = 0,
|
||||
speed = 0,
|
||||
q = -1,
|
||||
frames = 0,
|
||||
drop = 0,
|
||||
dup = 0,
|
||||
command = [],
|
||||
cpu = 0,
|
||||
memory = 0,
|
||||
video_codec = '',
|
||||
audio_codec = '',
|
||||
}) {
|
||||
const initProps = {
|
||||
time: 0,
|
||||
fps: 0,
|
||||
bitrate: 0,
|
||||
q: -1,
|
||||
speed: 0,
|
||||
drop: 0,
|
||||
dup: 0,
|
||||
frames: 0,
|
||||
cpu: 0,
|
||||
memory: 0,
|
||||
...props,
|
||||
valid: valid,
|
||||
order: order,
|
||||
state: state,
|
||||
error: error,
|
||||
reconnect: reconnect,
|
||||
bitrate: bitrate,
|
||||
fps: fps,
|
||||
time: time,
|
||||
speed: speed,
|
||||
q: q,
|
||||
frames: frames,
|
||||
drop: drop,
|
||||
dup: dup,
|
||||
command: command,
|
||||
cpu: cpu,
|
||||
memory: memory,
|
||||
video_codec: video_codec,
|
||||
audio_codec: audio_codec,
|
||||
};
|
||||
|
||||
return initProps;
|
||||
}
|
||||
|
||||
export default function Progress(props) {
|
||||
export default function Progress({
|
||||
valid = false,
|
||||
order = 'stop',
|
||||
state = 'disconnected',
|
||||
error = '',
|
||||
reconnect = -1,
|
||||
bitrate = 0,
|
||||
fps = 0,
|
||||
time = 0,
|
||||
speed = 0,
|
||||
q = -1,
|
||||
frames = 0,
|
||||
drop = 0,
|
||||
dup = 0,
|
||||
command = [],
|
||||
cpu = 0,
|
||||
memory = 0,
|
||||
video_codec = '',
|
||||
audio_codec = '',
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
|
||||
const progress = init(props);
|
||||
const progress = init({
|
||||
valid: valid,
|
||||
order: order,
|
||||
state: state,
|
||||
error: error,
|
||||
reconnect: reconnect,
|
||||
bitrate: bitrate,
|
||||
fps: fps,
|
||||
time: time,
|
||||
speed: speed,
|
||||
q: q,
|
||||
frames: frames,
|
||||
drop: drop,
|
||||
dup: dup,
|
||||
command: command,
|
||||
cpu: cpu,
|
||||
memory: memory,
|
||||
video_codec: video_codec,
|
||||
audio_codec: audio_codec,
|
||||
});
|
||||
|
||||
return (
|
||||
<Grid container className={classes.box}>
|
||||
@ -137,7 +201,7 @@ export default function Progress(props) {
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h4">
|
||||
<strong>
|
||||
<Number value={!isNaN((props.drop * 100) / props.frames) || 0} digits={2} minDigits={2} />%
|
||||
<Number value={!isNaN((progress.drop * 100) / progress.frames) || 0} digits={2} minDigits={2} />%
|
||||
</strong>
|
||||
</Typography>
|
||||
<Typography variant="body2" gutterBottom>
|
||||
@ -160,14 +224,3 @@ export default function Progress(props) {
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Progress.defaultProps = {
|
||||
time: 0,
|
||||
fps: 0,
|
||||
bitrate: 0,
|
||||
q: -1,
|
||||
speed: 0,
|
||||
drop: 0,
|
||||
dup: 0,
|
||||
frame: 0,
|
||||
};
|
||||
|
||||
@ -4,21 +4,13 @@ import InputLabel from '@mui/material/InputLabel';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import Select from '@mui/material/Select';
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ variant = 'outlined', label = '', value = '', disabled = false, onChange = function (event) {}, children = null }) {
|
||||
return (
|
||||
<FormControl variant={props.variant} fullWidth>
|
||||
<InputLabel>{props.label}</InputLabel>
|
||||
<Select value={props.value} onChange={props.onChange} disabled={props.disabled} label={props.label} MenuProps={{ disableScrollLock: true }}>
|
||||
{props.children}
|
||||
<FormControl variant={variant} fullWidth>
|
||||
<InputLabel>{label}</InputLabel>
|
||||
<Select value={value} onChange={onChange} disabled={disabled} label={label} MenuProps={{ disableScrollLock: true }}>
|
||||
{children}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
variant: 'outlined',
|
||||
label: '',
|
||||
value: '',
|
||||
disabled: false,
|
||||
onChange: function (event) {},
|
||||
};
|
||||
|
||||
@ -17,11 +17,21 @@ function isCustomOption(value, options) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({
|
||||
variant = 'outlined',
|
||||
label = '',
|
||||
value = '',
|
||||
disabled = false,
|
||||
customKey = 'custom',
|
||||
customLabel = '',
|
||||
allowCustom = false,
|
||||
options = [],
|
||||
onChange = function (event) {},
|
||||
}) {
|
||||
const [$value, setValue] = React.useState({
|
||||
value: props.value,
|
||||
isCustom: isCustomOption(props.value, props.options),
|
||||
custom: isCustomOption(props.value, props.options) === true ? props.value : '',
|
||||
value: value,
|
||||
isCustom: isCustomOption(value, options),
|
||||
custom: isCustomOption(value, options) === true ? value : '',
|
||||
});
|
||||
|
||||
const handleChange = (event) => {
|
||||
@ -29,7 +39,7 @@ export default function Component(props) {
|
||||
|
||||
const value = $value;
|
||||
|
||||
value.isCustom = v === props.customKey ? true : false;
|
||||
value.isCustom = v === customKey ? true : false;
|
||||
if (value.isCustom === true) {
|
||||
value.custom = value.value;
|
||||
}
|
||||
@ -38,12 +48,12 @@ export default function Component(props) {
|
||||
setValue({
|
||||
...$value,
|
||||
value: v,
|
||||
isCustom: v === props.customKey ? true : false,
|
||||
isCustom: v === customKey ? true : false,
|
||||
});
|
||||
|
||||
props.onChange({
|
||||
onChange({
|
||||
target: {
|
||||
value: v === props.customKey ? value.custom : value.value,
|
||||
value: v === customKey ? value.custom : value.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -54,13 +64,13 @@ export default function Component(props) {
|
||||
custom: event.target.value,
|
||||
});
|
||||
|
||||
props.onChange(event);
|
||||
onChange(event);
|
||||
};
|
||||
|
||||
const options = [];
|
||||
const selectOptions = [];
|
||||
|
||||
for (let o of props.options) {
|
||||
options.push(
|
||||
for (let o of options) {
|
||||
selectOptions.push(
|
||||
<MenuItem key={o.value} value={o.value} disabled={o.disabled === true}>
|
||||
{o.label}
|
||||
</MenuItem>,
|
||||
@ -69,29 +79,29 @@ export default function Component(props) {
|
||||
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
{props.allowCustom === true ? (
|
||||
{allowCustom === true ? (
|
||||
<React.Fragment>
|
||||
{$value.isCustom === true ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={6}>
|
||||
<FormControl variant={props.variant} fullWidth>
|
||||
<InputLabel>{props.label}</InputLabel>
|
||||
<FormControl variant={variant} fullWidth>
|
||||
<InputLabel>{label}</InputLabel>
|
||||
<Select
|
||||
value={$value.isCustom === false ? $value.value : props.customKey}
|
||||
value={$value.isCustom === false ? $value.value : customKey}
|
||||
onChange={handleChange}
|
||||
disabled={props.disabled}
|
||||
label={props.label}
|
||||
disabled={disabled}
|
||||
label={label}
|
||||
>
|
||||
{options}
|
||||
{selectOptions}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
variant={props.variant}
|
||||
variant={variant}
|
||||
fullWidth
|
||||
disabled={props.disabled === true || $value.isCustom === false}
|
||||
label={props.customLabel ? props.customLabel : props.label}
|
||||
disabled={disabled === true || $value.isCustom === false}
|
||||
label={customLabel ? customLabel : label}
|
||||
value={$value.custom}
|
||||
onChange={handleCustomChange}
|
||||
/>
|
||||
@ -99,15 +109,10 @@ export default function Component(props) {
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Grid item xs={12}>
|
||||
<FormControl variant={props.variant} fullWidth>
|
||||
<InputLabel>{props.label}</InputLabel>
|
||||
<Select
|
||||
value={$value.isCustom === false ? $value.value : props.customKey}
|
||||
onChange={handleChange}
|
||||
disabled={props.disabled}
|
||||
label={props.label}
|
||||
>
|
||||
{options}
|
||||
<FormControl variant={variant} fullWidth>
|
||||
<InputLabel>{label}</InputLabel>
|
||||
<Select value={$value.isCustom === false ? $value.value : customKey} onChange={handleChange} disabled={disabled} label={label}>
|
||||
{selectOptions}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
@ -115,10 +120,10 @@ export default function Component(props) {
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Grid item xs={12}>
|
||||
<FormControl variant={props.variant} fullWidth>
|
||||
<InputLabel>{props.label}</InputLabel>
|
||||
<Select value={$value.value} onChange={handleChange} disabled={props.disabled} label={props.label}>
|
||||
{options}
|
||||
<FormControl variant={variant} fullWidth>
|
||||
<InputLabel>{label}</InputLabel>
|
||||
<Select value={$value.value} onChange={handleChange} disabled={disabled} label={label}>
|
||||
{selectOptions}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
@ -126,13 +131,3 @@ export default function Component(props) {
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
variant: 'outlined',
|
||||
label: '',
|
||||
value: '',
|
||||
disabled: false,
|
||||
customKey: 'custom',
|
||||
allowCustom: false,
|
||||
onChange: function (event) {},
|
||||
};
|
||||
|
||||
@ -13,12 +13,11 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function TabPanel(props) {
|
||||
export default function TabPanel({ children = null, value = '', index = '' }) {
|
||||
const classes = useStyles();
|
||||
const { children, value, index, ...other } = props;
|
||||
|
||||
return (
|
||||
<div className={classes.root} role="tabpanel" hidden={value !== index} id={`vertical-tabpanel-${index}`} {...other}>
|
||||
<div className={classes.root} role="tabpanel" hidden={value !== index} id={`vertical-tabpanel-${index}`}>
|
||||
{value === index && (
|
||||
<Box classes={{ root: classes }} p={0}>
|
||||
{children}
|
||||
|
||||
@ -26,20 +26,14 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ value = '', children = null, onChange = function (event) {} }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Box className={classes.box}>
|
||||
<Tabs className={classes.tabs} variant="scrollable" scrollButtons allowScrollButtonsMobile value={props.value} onChange={props.onChange}>
|
||||
{props.children}
|
||||
<Tabs className={classes.tabs} variant="scrollable" scrollButtons allowScrollButtonsMobile value={value} onChange={onChange}>
|
||||
{children}
|
||||
</Tabs>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
value: '',
|
||||
children: null,
|
||||
onChange: function (event) {},
|
||||
};
|
||||
|
||||
@ -11,16 +11,12 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({ children = null }) {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<Grid item xs={12} className={classes.grid}>
|
||||
{props.children}
|
||||
{children}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
children: null,
|
||||
};
|
||||
|
||||
@ -9,11 +9,24 @@ import OutlinedInput from '@mui/material/OutlinedInput';
|
||||
|
||||
import Env from './Env';
|
||||
|
||||
export default function Component(props) {
|
||||
const id = props.id === null ? uuidv4() : props.id;
|
||||
export default function Component({
|
||||
id = null,
|
||||
label = '',
|
||||
value = '',
|
||||
disabled = false,
|
||||
multiline = false,
|
||||
rows = 1,
|
||||
env = false,
|
||||
type = 'text',
|
||||
min = null,
|
||||
max = null,
|
||||
helperText = null,
|
||||
onChange = function (value) {},
|
||||
}) {
|
||||
id = id === null ? uuidv4() : id;
|
||||
let adornment = null;
|
||||
|
||||
if (props.env) {
|
||||
if (env) {
|
||||
adornment = (
|
||||
<InputAdornment position="end">
|
||||
<Env />
|
||||
@ -21,33 +34,31 @@ export default function Component(props) {
|
||||
);
|
||||
}
|
||||
|
||||
let inputProps = {};
|
||||
|
||||
if (min !== null) {
|
||||
inputProps.min = min;
|
||||
}
|
||||
|
||||
if (max !== null) {
|
||||
inputProps.max = max;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl variant="outlined" disabled={props.disabled} fullWidth>
|
||||
<InputLabel htmlFor={id}>{props.label}</InputLabel>
|
||||
<FormControl variant="outlined" disabled={disabled} fullWidth>
|
||||
<InputLabel htmlFor={id}>{label}</InputLabel>
|
||||
<OutlinedInput
|
||||
id={id}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
endAdornment={adornment}
|
||||
label={props.label}
|
||||
multiline={props.multiline}
|
||||
rows={props.rows}
|
||||
type={props.type}
|
||||
label={label}
|
||||
multiline={multiline}
|
||||
rows={rows}
|
||||
type={type}
|
||||
inputProps={inputProps}
|
||||
/>
|
||||
{props.helperText && <FormHelperText>{props.helperText}</FormHelperText>}
|
||||
{helperText && <FormHelperText>{helperText}</FormHelperText>}
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
id: null,
|
||||
label: '',
|
||||
value: '',
|
||||
disabled: false,
|
||||
multiline: false,
|
||||
rows: 1,
|
||||
env: false,
|
||||
type: 'text',
|
||||
helperText: null,
|
||||
onChange: function (value) {},
|
||||
};
|
||||
|
||||
@ -20,14 +20,26 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({
|
||||
id = null,
|
||||
label = '',
|
||||
value = '',
|
||||
disabled = false,
|
||||
multiline = false,
|
||||
rows = 1,
|
||||
type = 'text',
|
||||
readOnly = true,
|
||||
allowCopy = true,
|
||||
size = 'small',
|
||||
onChange = function (value) {},
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const { i18n } = useLingui();
|
||||
|
||||
const notify = useContext(NotifyContext);
|
||||
|
||||
const handleCopy = async () => {
|
||||
const success = await CopyToClipboard(props.value);
|
||||
const success = await CopyToClipboard(value);
|
||||
|
||||
if (success === true) {
|
||||
notify.Dispatch('success', 'clipboard', i18n._(t`Data copied to clipboard`));
|
||||
@ -37,18 +49,18 @@ export default function Component(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl variant="outlined" disabled={props.disabled} fullWidth>
|
||||
<InputLabel htmlFor={props.id}>{props.label}</InputLabel>
|
||||
<FormControl variant="outlined" disabled={disabled} fullWidth>
|
||||
<InputLabel htmlFor={id}>{label}</InputLabel>
|
||||
<OutlinedInput
|
||||
className={classes.root}
|
||||
id={props.id}
|
||||
value={props.value}
|
||||
label={props.label}
|
||||
type={props.type}
|
||||
id={id}
|
||||
value={value}
|
||||
label={label}
|
||||
type={type}
|
||||
inputprops={{
|
||||
readOnly: true,
|
||||
}}
|
||||
size={props.size}
|
||||
size={size}
|
||||
endAdornment={
|
||||
<IconButton size="small" onClick={handleCopy}>
|
||||
<FileCopyIcon fontSize="small" />
|
||||
@ -58,17 +70,3 @@ export default function Component(props) {
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
id: null,
|
||||
label: '',
|
||||
value: '',
|
||||
disabled: false,
|
||||
multiline: false,
|
||||
rows: 1,
|
||||
type: 'text',
|
||||
readOnly: true,
|
||||
allowCopy: true,
|
||||
size: 'small',
|
||||
onChange: function (value) {},
|
||||
};
|
||||
|
||||
@ -12,7 +12,21 @@ import NotifyContext from '../contexts/Notify';
|
||||
import Palette from '../theme/base/palette';
|
||||
import TextareaModal from './modals/Textarea';
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({
|
||||
title = '',
|
||||
rows = 20,
|
||||
value = '',
|
||||
readOnly = true,
|
||||
allowCopy = true,
|
||||
allowModal = false,
|
||||
allowDownload = false,
|
||||
downloadName = '',
|
||||
disabled = false,
|
||||
scrollTo = 'top',
|
||||
onChange = function (value) {},
|
||||
onHelp = null,
|
||||
content = null,
|
||||
}) {
|
||||
const { i18n } = useLingui();
|
||||
|
||||
const [$modal, setModal] = React.useState(false);
|
||||
@ -20,10 +34,8 @@ export default function Component(props) {
|
||||
const notify = useContext(NotifyContext);
|
||||
const textAreaRef = React.createRef();
|
||||
|
||||
const { content } = props;
|
||||
|
||||
React.useEffect(() => {
|
||||
scrollTo();
|
||||
scrollToAction();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [content]);
|
||||
|
||||
@ -62,8 +74,8 @@ export default function Component(props) {
|
||||
});
|
||||
};
|
||||
|
||||
const scrollTo = () => {
|
||||
if (props.scrollTo === 'bottom') {
|
||||
const scrollToAction = () => {
|
||||
if (scrollTo === 'bottom') {
|
||||
textAreaRef.current.scrollTop = textAreaRef.current.scrollHeight;
|
||||
}
|
||||
|
||||
@ -72,8 +84,8 @@ export default function Component(props) {
|
||||
|
||||
const handleDownload = () => {
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(props.value));
|
||||
element.setAttribute('download', props.downloadName);
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(value));
|
||||
element.setAttribute('download', downloadName);
|
||||
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
@ -83,18 +95,15 @@ export default function Component(props) {
|
||||
document.body.removeChild(element);
|
||||
};
|
||||
|
||||
let allowCopy = props.allowCopy;
|
||||
if (props.value.length === 0 || props.disabled === true) {
|
||||
if (value.length === 0 || disabled === true) {
|
||||
allowCopy = false;
|
||||
}
|
||||
|
||||
let allowModal = props.allowModal;
|
||||
if (props.value.length === 0 || props.disabled === true) {
|
||||
if (value.length === 0 || disabled === true) {
|
||||
allowModal = false;
|
||||
}
|
||||
|
||||
let allowDownload = props.allowDownload;
|
||||
if (props.value.length === 0 || props.disabled === true || props.downloadName.length === 0) {
|
||||
if (value.length === 0 || disabled === true || downloadName.length === 0) {
|
||||
allowDownload = false;
|
||||
}
|
||||
|
||||
@ -113,10 +122,10 @@ export default function Component(props) {
|
||||
backgroundColor: Palette.background.footer2,
|
||||
},
|
||||
};
|
||||
if (props.rows === 1) {
|
||||
if (rows === 1) {
|
||||
textAreaStyle = {
|
||||
...textAreaStyle,
|
||||
height: 18 * props.rows + 9.5 + 'px',
|
||||
height: 18 * rows + 9.5 + 'px',
|
||||
overflowY: 'hidden',
|
||||
marginBottom: '0em',
|
||||
marginTop: '0em',
|
||||
@ -134,7 +143,7 @@ export default function Component(props) {
|
||||
} else {
|
||||
textAreaStyle = {
|
||||
...textAreaStyle,
|
||||
height: 18 * props.rows + 8 + 'px',
|
||||
height: 18 * rows + 8 + 'px',
|
||||
};
|
||||
textAreaDivStyle = {
|
||||
...textAreaDivStyle,
|
||||
@ -167,45 +176,21 @@ export default function Component(props) {
|
||||
</IconButton>
|
||||
)}
|
||||
</Stack>
|
||||
<textarea
|
||||
style={textAreaStyle}
|
||||
ref={textAreaRef}
|
||||
rows={props.rows}
|
||||
value={props.value}
|
||||
readOnly={props.readOnly}
|
||||
disabled={props.disabled}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
<textarea style={textAreaStyle} ref={textAreaRef} rows={rows} value={value} readOnly={readOnly} disabled={disabled} onChange={onChange} />
|
||||
</Stack>
|
||||
<TextareaModal
|
||||
open={$modal}
|
||||
onClose={handleModal}
|
||||
title={props.title}
|
||||
onHelp={props.onHelp}
|
||||
rows={props.rows}
|
||||
value={props.value}
|
||||
readOnly={props.readOnly}
|
||||
disabled={props.disabled}
|
||||
onChange={props.onChange}
|
||||
scrollTo={props.scrollTo}
|
||||
{...props}
|
||||
title={title}
|
||||
onHelp={onHelp}
|
||||
rows={rows}
|
||||
value={value}
|
||||
readOnly={readOnly}
|
||||
disabled={disabled}
|
||||
onChange={onChange}
|
||||
scrollTo={scrollToAction}
|
||||
allowModal={false}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
title: '',
|
||||
rows: 20,
|
||||
value: '',
|
||||
readOnly: true,
|
||||
allowCopy: true,
|
||||
allowModal: false,
|
||||
allowDownload: false,
|
||||
downloadName: '',
|
||||
disabled: false,
|
||||
scrollTo: 'top',
|
||||
onChange: function (value) {},
|
||||
onHelp: null,
|
||||
};
|
||||
|
||||
@ -2,10 +2,17 @@ import React from 'react';
|
||||
|
||||
import FormInlineButton from './FormInlineButton';
|
||||
|
||||
export default function UploadButton(props) {
|
||||
const { acceptTypes, label, onError, onStart, onUpload, ...other } = props;
|
||||
|
||||
const accept = props.acceptTypes.map((t) => t.mimetype);
|
||||
export default function UploadButton({
|
||||
label = '',
|
||||
acceptTypes = [],
|
||||
onStart = function () {},
|
||||
onError = function () {},
|
||||
onUpload = function (data, extension) {},
|
||||
variant = 'outlined',
|
||||
color = 'primary',
|
||||
disabled = false,
|
||||
}) {
|
||||
const accept = acceptTypes.map((t) => t.mimetype);
|
||||
|
||||
const handleUpload = (event) => {
|
||||
const handler = (event) => {
|
||||
@ -13,7 +20,7 @@ export default function UploadButton(props) {
|
||||
|
||||
if (files.length === 0) {
|
||||
// no files selected
|
||||
props.onError({
|
||||
onError({
|
||||
type: 'nofiles',
|
||||
});
|
||||
return;
|
||||
@ -22,7 +29,7 @@ export default function UploadButton(props) {
|
||||
const file = files[0];
|
||||
|
||||
let type = null;
|
||||
for (let t of props.acceptTypes) {
|
||||
for (let t of acceptTypes) {
|
||||
const accept = t.mimetype.split('/');
|
||||
const actual = file.type.split('/');
|
||||
|
||||
@ -38,7 +45,7 @@ export default function UploadButton(props) {
|
||||
|
||||
if (type === null) {
|
||||
// not one of the allowed mimetypes
|
||||
props.onError({
|
||||
onError({
|
||||
type: 'mimetype',
|
||||
actual: file.type,
|
||||
allowed: accept.slice(),
|
||||
@ -46,9 +53,9 @@ export default function UploadButton(props) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > type.maxSize) {
|
||||
if (type.maxSize > 0 && file.size > type.maxSize) {
|
||||
// the file is too big
|
||||
props.onError({
|
||||
onError({
|
||||
type: 'size',
|
||||
actual: file.size,
|
||||
allowed: type.maxSize,
|
||||
@ -56,23 +63,29 @@ export default function UploadButton(props) {
|
||||
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) {
|
||||
// reading the file failed
|
||||
props.onError({
|
||||
onError({
|
||||
type: 'read',
|
||||
message: reader.error.message,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
props.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);
|
||||
//};
|
||||
};
|
||||
|
||||
props.onStart();
|
||||
onStart();
|
||||
|
||||
handler(event);
|
||||
|
||||
@ -82,16 +95,9 @@ export default function UploadButton(props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<FormInlineButton component="label" {...other}>
|
||||
{props.label}
|
||||
<FormInlineButton component="label" variant={variant} color={color} disabled={disabled}>
|
||||
{label}
|
||||
<input accept={accept.join(',')} type="file" hidden onChange={handleUpload} />
|
||||
</FormInlineButton>
|
||||
);
|
||||
}
|
||||
|
||||
UploadButton.defaultProps = {
|
||||
label: '',
|
||||
acceptTypes: [],
|
||||
onError: function () {},
|
||||
onUpload: function (data, extension) {},
|
||||
};
|
||||
|
||||
@ -23,10 +23,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'default';
|
||||
const name = 'Default';
|
||||
const codecs = [];
|
||||
|
||||
@ -4,6 +4,8 @@ import Helper from '../../helper';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -23,10 +25,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +37,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +48,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
// -hwaccel nvdec
|
||||
|
||||
const coder = 'av1_cuvid';
|
||||
|
||||
@ -23,10 +23,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'default';
|
||||
const name = 'Default';
|
||||
const codecs = [];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'h264_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'h264_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
// -c:v h264_cuvid -i ...
|
||||
|
||||
const coder = 'h264_cuvid';
|
||||
|
||||
@ -23,10 +23,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'h264_mmal';
|
||||
const name = 'H.264 (MMAL)';
|
||||
const codecs = ['h264'];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'hevc_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'hevc_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'hevc_cuvid';
|
||||
const name = 'HEVC (CUVID)';
|
||||
const codecs = ['hevc'];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'mjpeg_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mjpeg_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'mjpeg_cuvid';
|
||||
const name = 'MJPEG (CUVID)';
|
||||
const codecs = ['mjpeg'];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'mpeg1_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg1_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'mpeg1_cuvid';
|
||||
const name = 'MPEG1 (CUVID)';
|
||||
const codecs = ['mpeg1'];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'mpeg2_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg2_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'mpeg2_cuvid';
|
||||
const name = 'MPEG2 (CUVID)';
|
||||
const codecs = ['mpeg2'];
|
||||
|
||||
@ -23,10 +23,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'mpeg2_mmal';
|
||||
const name = 'MPEG2 (MMAL)';
|
||||
const codecs = ['mpeg2'];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'mpeg4_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'mpeg4_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'mpeg4_cuvid';
|
||||
const name = 'MPEG4 (CUVID)';
|
||||
const codecs = ['mpeg4'];
|
||||
|
||||
@ -23,10 +23,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'mpeg4_mmal';
|
||||
const name = 'MPEG4 (MMAL)';
|
||||
const codecs = ['mpeg4'];
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -16,17 +20,17 @@ function createMapping(settings, stream, skills) {
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-hwaccel', 'cuda', '-hwaccel_output_format', 'cuda'],
|
||||
local: ['-hwaccel', 'cuda', '-hwaccel_output_format', 'nv12', '-hwaccel_device', `${settings.gpu}`],
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +39,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,21 +56,21 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
// -hwaccel cuda -hwaccel_output_format cuda
|
||||
|
||||
const coder = 'cuda';
|
||||
const name = 'NVDEC (CUDA)';
|
||||
const codecs = ['h264', 'hevc', 'mpeg1', 'mpeg2', 'mpeg4', 'vp8', 'vp9', 'vc1'];
|
||||
const codecs = ['av1', 'h264', 'hevc', 'mpeg1', 'mpeg2', 'mpeg4', 'vp8', 'vp9', 'vc1'];
|
||||
const type = 'video';
|
||||
const hwaccel = true;
|
||||
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'vc1_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'vc1_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'vc1_cuvid';
|
||||
const name = 'VC1 (CUVID)';
|
||||
const codecs = ['vc1'];
|
||||
|
||||
@ -23,10 +23,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'vc1_mmal';
|
||||
const name = 'VC1 (MMAL)';
|
||||
const codecs = ['vc1'];
|
||||
|
||||
@ -23,10 +23,10 @@ function createMapping(settings, stream, skills) {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +35,7 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -46,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'videotoolbox';
|
||||
const name = 'VideoToolbox';
|
||||
const codecs = ['h264', 'hevc', 'vp9', 'mpeg1', 'mpeg2', 'mpeg4'];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'vp8_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'vp8_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'vp8_cuvid';
|
||||
const name = 'VP8 (CUVID)';
|
||||
const codecs = ['vp8'];
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import React from 'react';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import Helper from '../../helper';
|
||||
import Video from '../../settings/Video';
|
||||
|
||||
function init(initialState) {
|
||||
const state = {
|
||||
gpu: '0',
|
||||
resize: 'auto',
|
||||
...initialState,
|
||||
};
|
||||
|
||||
@ -14,19 +20,25 @@ function createMapping(settings, stream, skills) {
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
let local = ['-c:v', 'vp9_cuvid', '-gpu', `${settings.gpu}`];
|
||||
|
||||
if (settings.resize !== 'auto') {
|
||||
local.push('-resize', `${settings.resize}`);
|
||||
}
|
||||
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-c:v', 'vp9_cuvid'],
|
||||
local: local,
|
||||
filter: [],
|
||||
};
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function Coder(props) {
|
||||
const settings = init(props.settings);
|
||||
const stream = Helper.InitStream(props.stream);
|
||||
const skills = Helper.InitSkills(props.skills);
|
||||
function Coder({ stream = {}, settings = {}, skills = {}, onChange = function (settings, mapping) {} }) {
|
||||
settings = init(settings);
|
||||
stream = Helper.InitStream(stream);
|
||||
skills = Helper.InitSkills(skills);
|
||||
|
||||
const handleChange = (newSettings) => {
|
||||
let automatic = false;
|
||||
@ -35,7 +47,16 @@ function Coder(props) {
|
||||
automatic = true;
|
||||
}
|
||||
|
||||
props.onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
onChange(newSettings, createMapping(newSettings, stream, skills), automatic);
|
||||
};
|
||||
|
||||
const update = (what) => (event) => {
|
||||
const newSettings = {
|
||||
...settings,
|
||||
[what]: event.target.value,
|
||||
};
|
||||
|
||||
handleChange(newSettings);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -43,16 +64,26 @@ function Coder(props) {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
return (
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={6}>
|
||||
<Video.Size
|
||||
value={settings.resize}
|
||||
label={<Trans>Resize</Trans>}
|
||||
customLabel={<Trans>Custom size</Trans>}
|
||||
onChange={update('resize')}
|
||||
allowCustom={true}
|
||||
allowAuto={true}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Video.GPU value={settings.gpu} onChange={update('gpu')} />
|
||||
</Grid>
|
||||
<Grid item xs={12}></Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
const coder = 'vp9_cuvid';
|
||||
const name = 'VP9 (CUVID)';
|
||||
const codecs = ['vp9'];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user