Merge branch 'dev' into dev
This commit is contained in:
commit
f35fd4148b
@ -3,6 +3,8 @@ name: 'Build dev restreamer-ui'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
schedule:
|
||||
- cron: '37 4 * * *'
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
@ -13,6 +15,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: dev
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
|
||||
39
CHANGELOG.md
39
CHANGELOG.md
@ -1,5 +1,44 @@
|
||||
# Restreamer-UI
|
||||
|
||||
## v1.13.0 > v1.14.0
|
||||
|
||||
- Add wettercom 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
|
||||
- Fix encoded address
|
||||
- Fix double -filter parameter when encoder sets filter
|
||||
- Fix Docker build ([#64](https://github.com/datarhei/restreamer-ui/issues/64))
|
||||
|
||||
## v1.12.0 > v1.13.0
|
||||
|
||||
- Add to allow stream hints in case probing fails
|
||||
- Mod enables ff-loglevel and prepares the logging component
|
||||
- Mod uses official Instagram-RTMP target
|
||||
- Mod Remove unused imports
|
||||
- Mod Update translations
|
||||
- Mod updates dep.
|
||||
- Fix player position
|
||||
- Fix missing stream URL, summarize streams in probe log, don't lock type for first stream
|
||||
|
||||
## v1.11.0 > v1.12.0
|
||||
|
||||
- Add option to select different SRT stream in wizard
|
||||
- Add option to select different RTMP stream in wizard
|
||||
- Fix selecting other than first audio stream ([#710](https://github.com/datarhei/restreamer/issues/710))
|
||||
- Fix reset of previous audio settings when editing profile ([#730](https://github.com/datarhei/restreamer/issues/730))
|
||||
- Fix RTMP URL for receive mode
|
||||
|
||||
## v1.10.0 > v1.11.0
|
||||
|
||||
- Add allow to stream HEVC and AV1 to Youtube via RTMP
|
||||
- Add librav1e AV1 encoder
|
||||
- Add support for AV1 CUDA decoding ([PR 46](https://github.com/datarhei/restreamer-ui/pull/46))
|
||||
- Add FFmpeg 6 support
|
||||
- Add HEVC VideoToolbox encoder
|
||||
- Fix anonymize error message ([#688](https://github.com/datarhei/restreamer/issues/688))
|
||||
- Fix chromecast config ([#37](https://github.com/datarhei/restreamer-ui/issues/37))
|
||||
|
||||
## v1.9.0 > v1.10.0
|
||||
|
||||
- Add resource usage and ffmpeg command to process details
|
||||
|
||||
17
Dockerfile
17
Dockerfile
@ -1,22 +1,17 @@
|
||||
ARG NODE_IMAGE=node:21-alpine3.17
|
||||
ARG CADDY_IMAGE=caddy:2.7.5-alpine
|
||||
ARG NODE_IMAGE=node:21-alpine3.20
|
||||
ARG CADDY_IMAGE=caddy:2.8.4-alpine
|
||||
|
||||
FROM $NODE_IMAGE as builder
|
||||
FROM $NODE_IMAGE AS builder
|
||||
|
||||
ARG NODE_SPACE_SIZE=10240
|
||||
ENV NODE_OPTIONS="--openssl-legacy-provider --max-old-space-size=$NODE_SPACE_SIZE"
|
||||
|
||||
ENV PUBLIC_URL "./"
|
||||
ENV PUBLIC_URL="./"
|
||||
|
||||
COPY . /ui
|
||||
|
||||
WORKDIR /ui
|
||||
|
||||
RUN cd /ui && \
|
||||
yarn set version berry && \
|
||||
yarn config set httpTimeout 600000 && \
|
||||
yarn install && \
|
||||
yarn run build
|
||||
yarn install && \
|
||||
yarn build
|
||||
|
||||
FROM $CADDY_IMAGE
|
||||
|
||||
|
||||
72
package.json
72
package.json
@ -1,52 +1,52 @@
|
||||
{
|
||||
"name": "restreamer-ui",
|
||||
"version": "1.10.0",
|
||||
"bundle": "restreamer-v2.8.0",
|
||||
"version": "1.14.0",
|
||||
"bundle": "restreamer-v2.12.0",
|
||||
"private": false,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@auth0/auth0-spa-js": "^2.1.3",
|
||||
"@babel/plugin-syntax-flow": "^7.23.3",
|
||||
"@babel/plugin-transform-react-jsx": "^7.23.4",
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fontsource/dosis": "^5.0.18",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.5.1",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.5.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.5.1",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@lingui/core": "^4.7.0",
|
||||
"@lingui/macro": "^4.7.0",
|
||||
"@lingui/react": "^4.7.0",
|
||||
"@mui/icons-material": "^5.15.6",
|
||||
"@mui/lab": "^5.0.0-alpha.162",
|
||||
"@mui/material": "^5.15.6",
|
||||
"@mui/styles": "^5.15.6",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "^6.3.0",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@babel/plugin-syntax-flow": "^7.24.7",
|
||||
"@babel/plugin-transform-react-jsx": "^7.25.2",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/dosis": "^5.0.21",
|
||||
"@fontsource/roboto": "^5.0.14",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@lingui/core": "^4.11.4",
|
||||
"@lingui/macro": "^4.11.4",
|
||||
"@lingui/react": "^4.11.4",
|
||||
"@mui/icons-material": "^6.0.1",
|
||||
"@mui/lab": "^6.0.0-beta.8",
|
||||
"@mui/material": "^6.0.1",
|
||||
"@mui/styles": "^6.0.1",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.5.0",
|
||||
"@testing-library/react": "^16.0.1",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react": "^18.3.5",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint": "^9.9.1",
|
||||
"handlebars": "^4.7.8",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"make-plural": "^7.3.0",
|
||||
"react": "^18.2.0",
|
||||
"make-plural": "^7.4.0",
|
||||
"react": "^18.3.1",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-device-detect": "^2.2.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-router-dom": "^6.21.3",
|
||||
"react-router-dom": "^6.26.1",
|
||||
"react-scripts": "^5.0.1",
|
||||
"semver": "^7.5.4",
|
||||
"serve": "^14.2.1",
|
||||
"typescript": "^5.3.3",
|
||||
"semver": "^7.6.3",
|
||||
"serve": "^14.2.3",
|
||||
"typescript": "^5.5.4",
|
||||
"url-parse": "^1.5.10",
|
||||
"util": "^0.12.5",
|
||||
"uuid": "^9.0.1",
|
||||
"video.js": "^8.10.0",
|
||||
"uuid": "^10.0.0",
|
||||
"video.js": "^8.17.3",
|
||||
"videojs-overlay": "^3.1.0"
|
||||
},
|
||||
"scripts": {
|
||||
@ -86,12 +86,12 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.9",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@lingui/cli": "^4.7.0",
|
||||
"@lingui/cli": "^4.11.4",
|
||||
"babel-core": "^6.26.3",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier": "^3.3.3",
|
||||
"react-error-overlay": "^6.0.11"
|
||||
},
|
||||
"resolutions": {}
|
||||
|
||||
778
public/_player/videojs/dist/video-js.css
vendored
778
public/_player/videojs/dist/video-js.css
vendored
File diff suppressed because one or more lines are too long
2
public/_player/videojs/dist/video-js.min.css
vendored
2
public/_player/videojs/dist/video-js.min.css
vendored
File diff suppressed because one or more lines are too long
46801
public/_player/videojs/dist/video.js
vendored
46801
public/_player/videojs/dist/video.js
vendored
File diff suppressed because one or more lines are too long
56
public/_player/videojs/dist/video.min.js
vendored
56
public/_player/videojs/dist/video.min.js
vendored
File diff suppressed because one or more lines are too long
1
public/_player/videojs/dist/videojs-airplay.css
vendored
Normal file
1
public/_player/videojs/dist/videojs-airplay.css
vendored
Normal file
@ -0,0 +1 @@
|
||||
.vjs-airplay-button .vjs-icon-placeholder{background:url("ic_airplay_white_24px.svg") center center no-repeat;background-size:contain;display:inline-block;width:12px;height:12px}.vjs-airplay-button:hover{cursor:pointer}.vjs-airplay-button:hover .vjs-icon-placeholder{background-image:url("ic_airplay_white_24px.svg")}.vjs-airplay-button.vjs-airplay-button-lg:not(.vjs-hidden){display:flex;align-items:center;width:auto;padding:0 4px}.vjs-airplay-button.vjs-airplay-button-lg:not(.vjs-hidden) .vjs-airplay-button-label{flex-grow:1;margin-left:4px}.vjs-airplay-button.vjs-airplay-button-lg:not(.vjs-hidden) .vjs-icon-placeholder{flex-grow:1}
|
||||
308
public/_player/videojs/dist/videojs-airplay.js
vendored
Normal file
308
public/_player/videojs/dist/videojs-airplay.js
vendored
Normal file
@ -0,0 +1,308 @@
|
||||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
var hasAirPlayAPISupport = require('../lib/hasAirPlayAPISupport');
|
||||
|
||||
/**
|
||||
* Registers the AirPlayButton Component with Video.js. Calls
|
||||
* {@link http://docs.videojs.com/Component.html#.registerComponent}, which will add a
|
||||
* component called `airPlayButton` to the list of globally registered Video.js
|
||||
* components. The `airPlayButton` is added to the player's control bar UI automatically
|
||||
* once {@link module:enableAirPlay} has been called. If you would like to specify the
|
||||
* order of the buttons that appear in the control bar, including this button, you can do
|
||||
* so in the options that you pass to the `videojs` function when creating a player:
|
||||
*
|
||||
* ```
|
||||
* videojs('playerID', {
|
||||
* controlBar: {
|
||||
* children: [
|
||||
* 'playToggle',
|
||||
* 'progressControl',
|
||||
* 'volumePanel',
|
||||
* 'fullscreenToggle',
|
||||
* 'airPlayButton',
|
||||
* ],
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @param videojs {object} A reference to {@link http://docs.videojs.com/module-videojs.html|Video.js}
|
||||
* @see http://docs.videojs.com/module-videojs.html#~registerPlugin
|
||||
*/
|
||||
module.exports = function (videojs) {
|
||||
/**
|
||||
* The AirPlayButton module contains both the AirPlayButton class definition and the
|
||||
* function used to register the button as a Video.js Component.
|
||||
*
|
||||
* @module AirPlayButton
|
||||
*/
|
||||
|
||||
const ButtonComponent = videojs.getComponent('Button');
|
||||
|
||||
/**
|
||||
* The Video.js Button class is the base class for UI button components.
|
||||
*
|
||||
* @external Button
|
||||
* @see {@link http://docs.videojs.com/Button.html|Button}
|
||||
*/
|
||||
|
||||
/** @lends AirPlayButton.prototype */
|
||||
class AirPlayButton extends ButtonComponent {
|
||||
/**
|
||||
* This class is a button component designed to be displayed in the
|
||||
* player UI's control bar. It displays an Apple AirPlay selection
|
||||
* list when clicked.
|
||||
*
|
||||
* @constructs
|
||||
* @extends external:Button
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
if (!hasAirPlayAPISupport()) {
|
||||
this.hide();
|
||||
}
|
||||
this._reactToAirPlayAvailableEvents();
|
||||
if (options.addAirPlayLabelToButton) {
|
||||
this.el().classList.add('vjs-airplay-button-lg');
|
||||
this._labelEl = document.createElement('span');
|
||||
this._labelEl.classList.add('vjs-airplay-button-label');
|
||||
this._labelEl.textContent = this.localize('AirPlay');
|
||||
this.el().appendChild(this._labelEl);
|
||||
} else {
|
||||
this.controlText('Start AirPlay');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Button#buildCSSClass to return the classes used on the button element.
|
||||
*
|
||||
* @param {DOMElement} el
|
||||
* @see {@link http://docs.videojs.com/Button.html#buildCSSClass|Button#buildCSSClass}
|
||||
*/
|
||||
buildCSSClass() {
|
||||
return 'vjs-airplay-button ' + super.buildCSSClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Button#handleClick to handle button click events. AirPlay
|
||||
* functionality is handled outside of this class, which should be limited
|
||||
* to UI related logic. This function simply triggers an event on the player.
|
||||
*
|
||||
* @fires AirPlayButton#airPlayRequested
|
||||
* @param {DOMElement} el
|
||||
* @see {@link http://docs.videojs.com/Button.html#handleClick|Button#handleClick}
|
||||
*/
|
||||
handleClick() {
|
||||
this.player().trigger('airPlayRequested');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying DOMElement used by the player.
|
||||
*
|
||||
* @private
|
||||
* @returns {DOMElement} either an <audio> or <video> tag, depending on the type of
|
||||
* player
|
||||
*/
|
||||
_getMediaEl() {
|
||||
var playerEl = this.player().el();
|
||||
return playerEl.querySelector('video, audio');
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a listener to the `webkitplaybacktargetavailabilitychanged` event, if it is
|
||||
* supported, that will show or hide this button Component based on the availability
|
||||
* of the AirPlay function.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_reactToAirPlayAvailableEvents() {
|
||||
var mediaEl = this._getMediaEl(),
|
||||
self = this;
|
||||
if (!mediaEl || !hasAirPlayAPISupport()) {
|
||||
return;
|
||||
}
|
||||
function onTargetAvailabilityChanged(event) {
|
||||
if (event.availability === 'available') {
|
||||
self.show();
|
||||
} else {
|
||||
self.hide();
|
||||
}
|
||||
}
|
||||
mediaEl.addEventListener('webkitplaybacktargetavailabilitychanged', onTargetAvailabilityChanged);
|
||||
this.on('dispose', function () {
|
||||
mediaEl.removeEventListener('webkitplaybacktargetavailabilitychanged', onTargetAvailabilityChanged);
|
||||
});
|
||||
}
|
||||
}
|
||||
videojs.registerComponent('airPlayButton', AirPlayButton);
|
||||
};
|
||||
|
||||
},{"../lib/hasAirPlayAPISupport":4}],2:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @module enableAirPlay
|
||||
*/
|
||||
|
||||
var hasAirPlayAPISupport = require('./lib/hasAirPlayAPISupport');
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {object} the Video.js Player instance
|
||||
* @returns {AirPlayButton} or `undefined` if it does not exist
|
||||
*/
|
||||
function getExistingAirPlayButton(player) {
|
||||
return player.controlBar.getChild('airPlayButton');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the AirPlayButton Component to the player's ControlBar component, if the
|
||||
* AirPlayButton does not already exist in the ControlBar.
|
||||
* @private
|
||||
* @param player {object} the Video.js Player instance
|
||||
* @param options {object}
|
||||
*/
|
||||
function ensureAirPlayButtonExists(player, options) {
|
||||
var existingAirPlayButton = getExistingAirPlayButton(player),
|
||||
indexOpt;
|
||||
if (options.addButtonToControlBar && !existingAirPlayButton) {
|
||||
// Figure out AirPlay button's index
|
||||
indexOpt = player.controlBar.children().length;
|
||||
if (typeof options.buttonPositionIndex !== 'undefined') {
|
||||
indexOpt = options.buttonPositionIndex >= 0 ? options.buttonPositionIndex : player.controlBar.children().length + options.buttonPositionIndex;
|
||||
}
|
||||
player.controlBar.addChild('airPlayButton', options, indexOpt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles requests for AirPlay triggered by the AirPlayButton Component.
|
||||
*
|
||||
* @private
|
||||
* @param player {object} the Video.js Player instance
|
||||
*/
|
||||
function onAirPlayRequested(player) {
|
||||
var mediaEl = player.el().querySelector('video, audio');
|
||||
if (mediaEl && mediaEl.webkitShowPlaybackTargetPicker) {
|
||||
mediaEl.webkitShowPlaybackTargetPicker();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an event listener for the `airPlayRequested` event triggered by the AirPlayButton
|
||||
* Component.
|
||||
*
|
||||
* @private
|
||||
* @param player {object} the Video.js Player instance
|
||||
*/
|
||||
function listenForAirPlayEvents(player) {
|
||||
// Respond to requests for AirPlay. The AirPlayButton component triggers this event
|
||||
// when the user clicks the AirPlay button.
|
||||
player.on('airPlayRequested', onAirPlayRequested.bind(null, player));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the AirPlay plugin.
|
||||
*
|
||||
* @private
|
||||
* @param player {object} the Video.js player
|
||||
* @param options {object} the plugin options
|
||||
*/
|
||||
function enableAirPlay(player, options) {
|
||||
if (!player.controlBar) {
|
||||
return;
|
||||
}
|
||||
if (hasAirPlayAPISupport()) {
|
||||
listenForAirPlayEvents(player);
|
||||
ensureAirPlayButtonExists(player, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the AirPlay plugin with Video.js. Calls
|
||||
* {@link http://docs.videojs.com/module-videojs.html#~registerPlugin|videojs#registerPlugin},
|
||||
* which will add a plugin function called `airPlay` to any instance of a Video.js player
|
||||
* that is created after calling this function. Call `player.airPlay(options)`, passing in
|
||||
* configuration options, to enable the AirPlay plugin on your Player instance.
|
||||
*
|
||||
* Currently, the only configuration option is:
|
||||
*
|
||||
* * **buttonText** - the text to display inside of the button component. By default,
|
||||
* this text is hidden and is used for accessibility purposes.
|
||||
*
|
||||
* @param {object} videojs
|
||||
* @see http://docs.videojs.com/module-videojs.html#~registerPlugin
|
||||
*/
|
||||
module.exports = function (videojs) {
|
||||
videojs.registerPlugin('airPlay', function (options) {
|
||||
var pluginOptions = Object.assign({
|
||||
addButtonToControlBar: true
|
||||
}, options || {});
|
||||
|
||||
// `this` is an instance of a Video.js Player.
|
||||
// Wait until the player is "ready" so that the player's control bar component has
|
||||
// been created.
|
||||
this.ready(enableAirPlay.bind(this, this, pluginOptions));
|
||||
});
|
||||
};
|
||||
|
||||
},{"./lib/hasAirPlayAPISupport":4}],3:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
var createAirPlayButton = require('./components/AirPlayButton'),
|
||||
createAirPlayPlugin = require('./enableAirPlay');
|
||||
|
||||
/**
|
||||
* @module index
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the AirPlay plugin and AirPlayButton Component with Video.js. See
|
||||
* {@link module:AirPlayButton} and {@link module:enableAirPlay} for more details about
|
||||
* how the plugin and button are registered and configured.
|
||||
*
|
||||
* @param {object} videojs
|
||||
* @see module:enableAirPlay
|
||||
* @see module:AirPlayButton
|
||||
*/
|
||||
module.exports = function (videojs) {
|
||||
videojs = videojs || window.videojs;
|
||||
createAirPlayButton(videojs);
|
||||
createAirPlayPlugin(videojs);
|
||||
};
|
||||
|
||||
},{"./components/AirPlayButton":1,"./enableAirPlay":2}],4:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @module hasAirPlayAPISupport
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns whether or not the current browser environment supports AirPlay.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean} true if AirPlay support is available
|
||||
*/
|
||||
module.exports = function () {
|
||||
return !!window.WebKitPlaybackTargetAvailabilityEvent;
|
||||
};
|
||||
|
||||
},{}],5:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* This module is used as an entry point for the build system to bundle this plugin into a
|
||||
* single javascript file that can be loaded by a script tag on a web page. The javascript
|
||||
* file that is built assumes that `videojs` is available globally at `window.videojs`, so
|
||||
* Video.js must be loaded **before** this plugin is loaded.
|
||||
*
|
||||
* Run `npm install` and then `grunt build` to build the plugin's bundled javascript
|
||||
* file, as well as the CSS and image assets into the project's `./dist/` folder.
|
||||
*
|
||||
* @module standalone
|
||||
*/
|
||||
|
||||
require('./index')();
|
||||
|
||||
},{"./index":3}]},{},[5]);
|
||||
@ -1 +1 @@
|
||||
.vjs-airplay-button .vjs-icon-placeholder{background:url("ic_airplay_white_24px.svg") center center no-repeat;background-size:contain;display:inline-block;width:20px;height:20px}.vjs-airplay-button:hover{cursor:pointer}.vjs-airplay-button:hover .vjs-icon-placeholder{background-image:url("ic_airplay_white_24px.svg")}
|
||||
.vjs-airplay-button .vjs-icon-placeholder{background:url("ic_airplay_white_24px.svg") center center no-repeat;background-size:contain;display:inline-block;width:12px;height:12px}.vjs-airplay-button:hover{cursor:pointer}.vjs-airplay-button:hover .vjs-icon-placeholder{background-image:url("ic_airplay_white_24px.svg")}.vjs-airplay-button.vjs-airplay-button-lg:not(.vjs-hidden){display:flex;align-items:center;width:auto;padding:0 4px}.vjs-airplay-button.vjs-airplay-button-lg:not(.vjs-hidden) .vjs-airplay-button-label{flex-grow:1;margin-left:4px}.vjs-airplay-button.vjs-airplay-button-lg:not(.vjs-hidden) .vjs-icon-placeholder{flex-grow:1}
|
||||
1317
public/_player/videojs/dist/videojs-airplay.min.js
vendored
1317
public/_player/videojs/dist/videojs-airplay.min.js
vendored
File diff suppressed because it is too large
Load Diff
@ -1,65 +1 @@
|
||||
/** Silvermine Chromecast **/
|
||||
.vjs-chromecast-button .vjs-icon-placeholder {
|
||||
background: url('ic_cast_white_24dp.png') center center no-repeat;
|
||||
background-size: contain;
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.vjs-chromecast-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.vjs-chromecast-button:hover .vjs-icon-placeholder {
|
||||
background-image: url('ic_cast_white_24dp.png');
|
||||
}
|
||||
.vjs-chromecast-button.vjs-chromecast-casting-state .vjs-icon-placeholder {
|
||||
background-image: url('ic_cast_connected_white_24dp.png');
|
||||
}
|
||||
.vjs-chromecast-button.vjs-chromecast-casting-state:hover .vjs-icon-placeholder {
|
||||
background-image: url('ic_cast_connected_white_24dp.png');
|
||||
}
|
||||
.vjs-tech-chromecast {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-poster::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 2px;
|
||||
width: 100px;
|
||||
background-color: #cccccc;
|
||||
position: absolute;
|
||||
left: calc(50% - 50px);
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-poster-img {
|
||||
max-height: 180px;
|
||||
width: auto;
|
||||
border: 2px solid #cccccc;
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-poster-img.vjs-tech-chromecast-poster-img-empty {
|
||||
width: 160px;
|
||||
height: 90px;
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-title-container {
|
||||
position: absolute;
|
||||
bottom: 50%;
|
||||
margin-bottom: 100px;
|
||||
color: #cccccc;
|
||||
text-align: center;
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-title {
|
||||
font-size: 22px;
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-title.vjs-tech-chromecast-title-empty {
|
||||
display: none;
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-subtitle {
|
||||
font-size: 18px;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
.vjs-tech-chromecast .vjs-tech-chromecast-subtitle.vjs-tech-chromecast-subtitle-empty {
|
||||
display: none;
|
||||
}
|
||||
.vjs-chromecast-button .vjs-icon-placeholder{background:url("ic_cast_white_24dp.png") center center no-repeat;background-size:contain;display:inline-block;width:12px;height:12px}.vjs-chromecast-button:hover{cursor:pointer}.vjs-chromecast-button:hover .vjs-icon-placeholder{background-image:url("ic_cast_white_24dp.png")}.vjs-chromecast-button.vjs-chromecast-casting-state .vjs-icon-placeholder{background-image:url("ic_cast_connected_white_24dp.png")}.vjs-chromecast-button.vjs-chromecast-casting-state:hover .vjs-icon-placeholder{background-image:url("ic_cast_connected_white_24dp.png")}.vjs-chromecast-button.vjs-chromecast-button-lg:not(.vjs-hidden){display:flex;align-items:center;width:auto;padding:0 4px}.vjs-chromecast-button.vjs-chromecast-button-lg:not(.vjs-hidden) .vjs-chromecast-button-label{flex-grow:1;margin-left:4px}.vjs-chromecast-button.vjs-chromecast-button-lg:not(.vjs-hidden) .vjs-icon-placeholder{flex-grow:1}.vjs-tech-chromecast{display:flex;flex-direction:column;justify-content:center;align-items:center;overflow:hidden}.vjs-tech-chromecast .vjs-tech-chromecast-poster::after{content:" ";display:block;height:2px;width:100px;background-color:#ccc;position:absolute;left:calc(50% - 50px)}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img{max-height:180px;width:auto;border:2px solid #ccc}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img.vjs-tech-chromecast-poster-img-empty{width:160px;height:90px}.vjs-tech-chromecast .vjs-tech-chromecast-title-container{position:absolute;bottom:50%;margin-bottom:100px;color:#ccc;text-align:center}.vjs-tech-chromecast .vjs-tech-chromecast-title{font-size:22px}.vjs-tech-chromecast .vjs-tech-chromecast-title.vjs-tech-chromecast-title-empty{display:none}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle{font-size:18px;padding-top:.5em}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle.vjs-tech-chromecast-subtitle-empty{display:none}
|
||||
4100
public/_player/videojs/dist/videojs-chromecast.js
vendored
Normal file
4100
public/_player/videojs/dist/videojs-chromecast.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
.vjs-chromecast-button .vjs-icon-placeholder{background:url(ic_cast_white_24dp.png) center center no-repeat;background-size:contain;display:inline-block;width:20px;height:20px}.vjs-chromecast-button:hover{cursor:pointer}.vjs-chromecast-button:hover .vjs-icon-placeholder{background-image:url(ic_cast_white_24dp.png)}.vjs-chromecast-button.vjs-chromecast-casting-state .vjs-icon-placeholder{background-image:url(ic_cast_connected_white_24dp.png)}.vjs-chromecast-button.vjs-chromecast-casting-state:hover .vjs-icon-placeholder{background-image:url(ic_cast_connected_white_24dp.png)}.vjs-tech-chromecast{display:flex;flex-direction:column;justify-content:center;align-items:center;overflow:hidden}.vjs-tech-chromecast .vjs-tech-chromecast-poster::after{content:' ';display:block;height:2px;width:100px;background-color:#ccc;position:absolute;left:calc(50% - 50px)}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img{max-height:180px;width:auto;border:2px solid #ccc}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img.vjs-tech-chromecast-poster-img-empty{width:160px;height:90px}.vjs-tech-chromecast .vjs-tech-chromecast-title-container{position:absolute;bottom:50%;margin-bottom:100px;color:#ccc;text-align:center}.vjs-tech-chromecast .vjs-tech-chromecast-title{font-size:22px}.vjs-tech-chromecast .vjs-tech-chromecast-title.vjs-tech-chromecast-title-empty{display:none}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle{font-size:18px;padding-top:.5em}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle.vjs-tech-chromecast-subtitle-empty{display:none}
|
||||
.vjs-chromecast-button .vjs-icon-placeholder{background:url("ic_cast_white_24dp.png") center center no-repeat;background-size:contain;display:inline-block;width:12px;height:12px}.vjs-chromecast-button:hover{cursor:pointer}.vjs-chromecast-button:hover .vjs-icon-placeholder{background-image:url("ic_cast_white_24dp.png")}.vjs-chromecast-button.vjs-chromecast-casting-state .vjs-icon-placeholder{background-image:url("ic_cast_connected_white_24dp.png")}.vjs-chromecast-button.vjs-chromecast-casting-state:hover .vjs-icon-placeholder{background-image:url("ic_cast_connected_white_24dp.png")}.vjs-chromecast-button.vjs-chromecast-button-lg:not(.vjs-hidden){display:flex;align-items:center;width:auto;padding:0 4px}.vjs-chromecast-button.vjs-chromecast-button-lg:not(.vjs-hidden) .vjs-chromecast-button-label{flex-grow:1;margin-left:4px}.vjs-chromecast-button.vjs-chromecast-button-lg:not(.vjs-hidden) .vjs-icon-placeholder{flex-grow:1}.vjs-tech-chromecast{display:flex;flex-direction:column;justify-content:center;align-items:center;overflow:hidden}.vjs-tech-chromecast .vjs-tech-chromecast-poster::after{content:" ";display:block;height:2px;width:100px;background-color:#ccc;position:absolute;left:calc(50% - 50px)}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img{max-height:180px;width:auto;border:2px solid #ccc}.vjs-tech-chromecast .vjs-tech-chromecast-poster-img.vjs-tech-chromecast-poster-img-empty{width:160px;height:90px}.vjs-tech-chromecast .vjs-tech-chromecast-title-container{position:absolute;bottom:50%;margin-bottom:100px;color:#ccc;text-align:center}.vjs-tech-chromecast .vjs-tech-chromecast-title{font-size:22px}.vjs-tech-chromecast .vjs-tech-chromecast-title.vjs-tech-chromecast-title-empty{display:none}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle{font-size:18px;padding-top:.5em}.vjs-tech-chromecast .vjs-tech-chromecast-subtitle.vjs-tech-chromecast-subtitle-empty{display:none}
|
||||
3149
public/_player/videojs/dist/videojs-chromecast.min.js
vendored
3149
public/_player/videojs/dist/videojs-chromecast.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
795
public/_player/videojs/dist/videojs-license.js
vendored
795
public/_player/videojs/dist/videojs-license.js
vendored
@ -1,459 +1,340 @@
|
||||
/*! @name videojs-license @version 0.1.0 @license MIT */
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js'), require('global/document')) :
|
||||
typeof define === 'function' && define.amd ? define(['video.js', 'global/document'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.videojsLicense = factory(global.videojs, global.document));
|
||||
}(this, (function (videojs, document) { 'use strict';
|
||||
|
||||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
||||
|
||||
var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs);
|
||||
var document__default = /*#__PURE__*/_interopDefaultLegacy(document);
|
||||
|
||||
function createCommonjsModule(fn, basedir, module) {
|
||||
return module = {
|
||||
path: basedir,
|
||||
exports: {},
|
||||
require: function (path, base) {
|
||||
return commonjsRequire(path, (base === undefined || base === null) ? module.path : base);
|
||||
}
|
||||
}, fn(module, module.exports), module.exports;
|
||||
}
|
||||
|
||||
function commonjsRequire () {
|
||||
throw new Error('Dynamic requires are not currently supported by @rollup/plugin-commonjs');
|
||||
}
|
||||
|
||||
var assertThisInitialized = createCommonjsModule(function (module) {
|
||||
function _assertThisInitialized(self) {
|
||||
if (self === void 0) {
|
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
module.exports = _assertThisInitialized;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var setPrototypeOf = createCommonjsModule(function (module) {
|
||||
function _setPrototypeOf(o, p) {
|
||||
module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
|
||||
o.__proto__ = p;
|
||||
return o;
|
||||
};
|
||||
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
return _setPrototypeOf(o, p);
|
||||
}
|
||||
|
||||
module.exports = _setPrototypeOf;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var inheritsLoose = createCommonjsModule(function (module) {
|
||||
function _inheritsLoose(subClass, superClass) {
|
||||
subClass.prototype = Object.create(superClass.prototype);
|
||||
subClass.prototype.constructor = subClass;
|
||||
setPrototypeOf(subClass, superClass);
|
||||
}
|
||||
|
||||
module.exports = _inheritsLoose;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var version = "0.1.0";
|
||||
|
||||
var Plugin = videojs__default['default'].getPlugin('plugin');
|
||||
var Component = videojs__default['default'].getComponent('Component');
|
||||
var Button = videojs__default['default'].getComponent('MenuButton'); // Default options for the plugin.
|
||||
|
||||
var defaults = {
|
||||
license: 'none',
|
||||
title: '',
|
||||
author: '',
|
||||
languages: {
|
||||
license: 'License',
|
||||
loading: 'Loading'
|
||||
}
|
||||
};
|
||||
/**
|
||||
* An advanced Video.js plugin. For more information on the API
|
||||
*
|
||||
* See: https://blog.videojs.com/feature-spotlight-advanced-plugins/
|
||||
*/
|
||||
|
||||
var License = /*#__PURE__*/function (_Plugin) {
|
||||
inheritsLoose(License, _Plugin);
|
||||
|
||||
/**
|
||||
* Create a License plugin instance.
|
||||
*
|
||||
* @param {Player} player
|
||||
* A Video.js Player instance.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An optional options object.
|
||||
*
|
||||
* While not a core part of the Video.js plugin architecture, a
|
||||
* second argument of options is a convenient way to accept inputs
|
||||
* from your plugin's caller.
|
||||
*/
|
||||
function License(player, options) {
|
||||
var _this;
|
||||
|
||||
// the parent class will add player under this.player
|
||||
_this = _Plugin.call(this, player) || this;
|
||||
_this.playerId = _this.player.id();
|
||||
_this.options = videojs__default['default'].mergeOptions(defaults, options);
|
||||
|
||||
if (options.license === 'none') {
|
||||
return assertThisInitialized(_this);
|
||||
}
|
||||
|
||||
_this.player.ready(function () {
|
||||
_this.player.addClass('vjs-license');
|
||||
|
||||
_this.buildUI();
|
||||
|
||||
if (videojs__default['default'].browser.IS_IOS || videojs__default['default'].browser.IS_ANDROID) {
|
||||
_this.mobileBuildUI();
|
||||
}
|
||||
}); // close the menu if open on userinactive
|
||||
|
||||
|
||||
_this.player.on('userinactive', function () {
|
||||
document__default['default'].getElementById(_this.playerId).querySelectorAll('.vjs-menu').forEach(function (element) {
|
||||
element.classList.remove('vjs-lock-open');
|
||||
});
|
||||
}); // close the menu if anywhere in the player is clicked
|
||||
|
||||
|
||||
_this.player.on('click', function (evt) {
|
||||
if (evt.target.tagName === 'VIDEO') {
|
||||
document__default['default'].getElementById(_this.playerId).querySelectorAll('.vjs-menu').forEach(function (element) {
|
||||
element.classList.remove('vjs-lock-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_this.player.on('loadstart', function (_event) {
|
||||
_this.removeElementsByClass('vjs-license-clear');
|
||||
|
||||
if (videojs__default['default'].browser.IS_IOS || videojs__default['default'].browser.IS_ANDROID) {
|
||||
_this.mobileBuildTopLevelMenu();
|
||||
} else {
|
||||
_this.buildTopLevelMenu();
|
||||
}
|
||||
});
|
||||
|
||||
return _this;
|
||||
}
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*/
|
||||
|
||||
|
||||
var _proto = License.prototype;
|
||||
|
||||
_proto.buildUI = function buildUI() {
|
||||
var playerId = this.playerId;
|
||||
var that = this;
|
||||
/**
|
||||
* LicenseMenuButton
|
||||
*/
|
||||
|
||||
var LicenseMenuButton = /*#__PURE__*/function (_Button) {
|
||||
inheritsLoose(LicenseMenuButton, _Button);
|
||||
|
||||
/**
|
||||
* Contructor
|
||||
*
|
||||
* @param {*} player videojs player instance
|
||||
* @param {*} options videojs player options
|
||||
*/
|
||||
function LicenseMenuButton(player, options) {
|
||||
var _this2;
|
||||
|
||||
_this2 = _Button.call(this, player, options) || this;
|
||||
|
||||
_this2.addClass('vjs-license');
|
||||
|
||||
_this2.controlText(that.options.languages.loading);
|
||||
|
||||
player.one('canplaythrough', function (_event) {
|
||||
_this2.controlText(that.options.languages.settings);
|
||||
});
|
||||
_this2.menu.contentEl_.id = playerId + '-vjs-license-default';
|
||||
return _this2;
|
||||
}
|
||||
/**
|
||||
* Handle click
|
||||
*/
|
||||
|
||||
|
||||
var _proto2 = LicenseMenuButton.prototype;
|
||||
|
||||
_proto2.handleClick = function handleClick() {
|
||||
if (videojs__default['default'].browser.IS_IOS || videojs__default['default'].browser.IS_ANDROID) {
|
||||
this.player.getChild('licenseMenuMobileModal').el().style.display = 'block';
|
||||
} else {
|
||||
this.el().classList.toggle('vjs-toogle-btn');
|
||||
this.menu.el().classList.toggle('vjs-lock-open');
|
||||
}
|
||||
};
|
||||
|
||||
return LicenseMenuButton;
|
||||
}(Button);
|
||||
|
||||
videojs__default['default'].registerComponent('licenseMenuButton', LicenseMenuButton);
|
||||
this.player.getChild('controlBar').addChild('licenseMenuButton');
|
||||
|
||||
if (this.player.getChild('controlBar').getChild('fullscreenToggle')) {
|
||||
this.player.getChild('controlBar').el().insertBefore(this.player.getChild('controlBar').getChild('licenseMenuButton').el(), this.player.getChild('controlBar').getChild('fullscreenToggle').el());
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* This is just build the top level menu no sub menus
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.buildTopLevelMenu = function buildTopLevelMenu() {
|
||||
var settingsButton = this.player.getChild('controlBar').getChild('licenseMenuButton'); // settingsButton.addClass('vjs-license-is-loaded');
|
||||
|
||||
var main = settingsButton.menu.contentEl_; // Empty the main menu div to repopulate
|
||||
|
||||
main.innerHTML = '';
|
||||
main.classList.add('vjs-license-top-level'); // Start building new list items
|
||||
|
||||
var menuTitle = document__default['default'].createElement('li');
|
||||
menuTitle.className = 'vjs-license-top-level-header';
|
||||
var menuTitleInner = document__default['default'].createElement('span');
|
||||
menuTitleInner.innerHTML = 'About';
|
||||
menuTitleInner.className = 'vjs-license-top-level-header-titel';
|
||||
menuTitle.appendChild(menuTitleInner);
|
||||
main.appendChild(menuTitle);
|
||||
var itemTitel = document__default['default'].createElement('li');
|
||||
itemTitel.innerHTML = this.buildItemTitel();
|
||||
itemTitel.className = 'vjs-license-top-level-item';
|
||||
main.appendChild(itemTitel);
|
||||
|
||||
if (this.options.author) {
|
||||
var itemAuthor = document__default['default'].createElement('li');
|
||||
itemAuthor.innerHTML = this.buildItemAuthor();
|
||||
itemAuthor.className = 'vjs-license-top-level-item';
|
||||
main.appendChild(itemAuthor);
|
||||
}
|
||||
|
||||
var itemLicense = document__default['default'].createElement('li');
|
||||
itemLicense.innerHTML = this.buildItemLicense();
|
||||
itemLicense.className = 'vjs-license-top-level-item';
|
||||
main.appendChild(itemLicense);
|
||||
}
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.mobileBuildUI = function mobileBuildUI() {
|
||||
/**
|
||||
* bla
|
||||
*/
|
||||
var LicenseMenuMobileModal = /*#__PURE__*/function (_Component) {
|
||||
inheritsLoose(LicenseMenuMobileModal, _Component);
|
||||
|
||||
/**
|
||||
* Contructor
|
||||
*
|
||||
* @param {*} player videojs player instance
|
||||
* @param {*} options videojs player options
|
||||
*/
|
||||
function LicenseMenuMobileModal(player, options) {
|
||||
return _Component.call(this, player, options) || this;
|
||||
}
|
||||
/**
|
||||
* Creates an HTML element
|
||||
*
|
||||
* @return {Object} HTML element
|
||||
*/
|
||||
|
||||
|
||||
var _proto3 = LicenseMenuMobileModal.prototype;
|
||||
|
||||
_proto3.createEl = function createEl() {
|
||||
return videojs__default['default'].createEl('div', {
|
||||
className: 'vjs-license-mobile'
|
||||
});
|
||||
};
|
||||
|
||||
return LicenseMenuMobileModal;
|
||||
}(Component);
|
||||
|
||||
videojs__default['default'].registerComponent('licenseMenuMobileModal', LicenseMenuMobileModal);
|
||||
videojs__default['default'].dom.prependTo(this.player.addChild('licenseMenuMobileModal').el(), document__default['default'].body);
|
||||
}
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.mobileBuildTopLevelMenu = function mobileBuildTopLevelMenu() {
|
||||
var _this3 = this;
|
||||
|
||||
var settingsButton = this.player.getChild('licenseMenuMobileModal');
|
||||
var menuTopLevel = document__default['default'].createElement('ul');
|
||||
menuTopLevel.className = 'vjs-license-mob-top-level vjs-setting-menu-clear';
|
||||
settingsButton.el().appendChild(menuTopLevel); // Empty the main menu div to repopulate
|
||||
|
||||
var menuTitle = document__default['default'].createElement('li');
|
||||
menuTitle.innerHTML = 'About';
|
||||
menuTitle.className = 'vjs-setting-menu-mobile-top-header';
|
||||
menuTopLevel.appendChild(menuTitle);
|
||||
var itemTitel = document__default['default'].createElement('li');
|
||||
itemTitel.innerHTML = this.buildItemTitel();
|
||||
itemTitel.className = 'vjs-license-top-level-item';
|
||||
|
||||
if (this.options.author) {
|
||||
var itemAuthor = document__default['default'].createElement('li');
|
||||
itemAuthor.innerHTML = this.buildItemAuthor();
|
||||
itemAuthor.className = 'vjs-license-top-level-item';
|
||||
}
|
||||
|
||||
var itemLicense = document__default['default'].createElement('li');
|
||||
itemLicense.innerHTML = this.buildItemLicense();
|
||||
itemLicense.className = 'vjs-license-top-level-item';
|
||||
var menuClose = document__default['default'].createElement('li');
|
||||
menuClose.innerHTML = 'Close';
|
||||
menuClose.className = 'setting-menu-footer-default';
|
||||
|
||||
menuClose.onclick = function (e) {
|
||||
_this3.player.getChild('settingsMenuMobileModal').el().style.display = 'none';
|
||||
};
|
||||
|
||||
menuTopLevel.appendChild(menuClose);
|
||||
}
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*
|
||||
* @return {string} Returns license text
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.buildItemTitel = function buildItemTitel() {
|
||||
var titel = '';
|
||||
|
||||
if (this.options.title) {
|
||||
titel = "" + this.options.title;
|
||||
}
|
||||
|
||||
return 'Title: ' + titel;
|
||||
}
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*
|
||||
* @return {string} Returns license text
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.buildItemAuthor = function buildItemAuthor() {
|
||||
var author = '';
|
||||
|
||||
if (this.options.author) {
|
||||
author = " by " + this.options.author;
|
||||
}
|
||||
|
||||
return 'Author: ' + author;
|
||||
}
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*
|
||||
* @return {string} Returns license text
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.buildItemLicense = function buildItemLicense() {
|
||||
var license = '';
|
||||
var reVersion = new RegExp('[0-9]+.[0-9]+$');
|
||||
var version = '4.0';
|
||||
var matches = this.options.license.match(reVersion);
|
||||
|
||||
if (matches !== null) {
|
||||
version = matches[0];
|
||||
}
|
||||
|
||||
var which = this.options.license.replace(reVersion, '').trim();
|
||||
var deed = null;
|
||||
|
||||
switch (which) {
|
||||
case 'CC0':
|
||||
deed = 'https://creativecommons.org/licenses/zero/1.0/';
|
||||
break;
|
||||
|
||||
case 'CC BY':
|
||||
deed = "https://creativecommons.org/licenses/by/" + version + "/";
|
||||
break;
|
||||
|
||||
case 'CC BY-SA':
|
||||
deed = "https://creativecommons.org/licenses/by-sa/" + version + "/";
|
||||
break;
|
||||
|
||||
case 'CC BY-NC':
|
||||
deed = "https://creativecommons.org/licenses/by-nc/" + version + "/";
|
||||
break;
|
||||
|
||||
case 'CC BY-NC-SA':
|
||||
deed = "https://creativecommons.org/licenses/by-nc-sa/" + version + "/";
|
||||
break;
|
||||
|
||||
case 'CC BY-ND':
|
||||
deed = "https://creativecommons.org/licenses/by-nd/" + version + "/";
|
||||
break;
|
||||
|
||||
case 'CC BY-NC-ND':
|
||||
deed = "https://creativecommons.org/licenses/by-nc-nd/" + version + "/";
|
||||
break;
|
||||
}
|
||||
|
||||
if (deed) {
|
||||
license = "<a href=\"" + deed + "\" onclick=\"window.open('" + deed + "')\" target=\"_blank\" rel=\"noopener\">" + this.options.license + "</a>";
|
||||
} else {
|
||||
license = this.options.license;
|
||||
}
|
||||
|
||||
return 'License: ' + license;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Helper class to clear menu items before rebuild
|
||||
*
|
||||
* @param {*} className Name of a class
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.removeElementsByClass = function removeElementsByClass(className) {
|
||||
// Need to prevent the menu from not showing sometimes
|
||||
document__default['default'].querySelectorAll('.vjs-sm-top-level').forEach(function (element) {
|
||||
element.classList.remove('vjs-hidden');
|
||||
});
|
||||
var elements = document__default['default'].getElementsByClassName(className);
|
||||
|
||||
while (elements.length > 0) {
|
||||
elements[0].parentNode.removeChild(elements[0]);
|
||||
}
|
||||
};
|
||||
|
||||
return License;
|
||||
}(Plugin); // Define default values for the plugin's `state` object here.
|
||||
|
||||
|
||||
License.defaultState = {}; // Include the version number.
|
||||
|
||||
License.VERSION = version; // Register the plugin with video.js.
|
||||
|
||||
videojs__default['default'].registerPlugin('license', License);
|
||||
|
||||
return License;
|
||||
|
||||
})));
|
||||
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.videojsLicense = factory(global.videojs));
|
||||
})(this, (function (videojs) { 'use strict';
|
||||
|
||||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
||||
|
||||
var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs);
|
||||
|
||||
var version = "0.1.0";
|
||||
|
||||
const Plugin = videojs__default["default"].getPlugin('plugin');
|
||||
const Component = videojs__default["default"].getComponent('Component');
|
||||
const Button = videojs__default["default"].getComponent('MenuButton');
|
||||
|
||||
// Default options for the plugin.
|
||||
const defaults = {
|
||||
license: 'none',
|
||||
title: '',
|
||||
author: '',
|
||||
languages: {
|
||||
license: 'License',
|
||||
loading: 'Loading'
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An advanced Video.js plugin. For more information on the API
|
||||
*
|
||||
* See: https://blog.videojs.com/feature-spotlight-advanced-plugins/
|
||||
*/
|
||||
class License extends Plugin {
|
||||
/**
|
||||
* Create a License plugin instance.
|
||||
*
|
||||
* @param {Player} player
|
||||
* A Video.js Player instance.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* An optional options object.
|
||||
*
|
||||
* While not a core part of the Video.js plugin architecture, a
|
||||
* second argument of options is a convenient way to accept inputs
|
||||
* from your plugin's caller.
|
||||
*/
|
||||
constructor(player, options) {
|
||||
// the parent class will add player under this.player
|
||||
super(player);
|
||||
this.playerId = this.player.id();
|
||||
this.options = videojs__default["default"].mergeOptions(defaults, options);
|
||||
if (options.license === 'none') {
|
||||
return;
|
||||
}
|
||||
player.addClass('vjs-license');
|
||||
this.buildUI();
|
||||
if (videojs__default["default"].browser.IS_IOS || videojs__default["default"].browser.IS_ANDROID) {
|
||||
this.mobileBuildUI();
|
||||
}
|
||||
|
||||
// close the menu if open on userinactive
|
||||
this.player.on('userinactive', () => {
|
||||
document.getElementById(this.playerId).querySelectorAll('.vjs-menu').forEach(element => {
|
||||
element.classList.remove('vjs-lock-open');
|
||||
});
|
||||
});
|
||||
|
||||
// close the menu if anywhere in the player is clicked
|
||||
this.player.on('click', evt => {
|
||||
if (evt.target.tagName === 'VIDEO') {
|
||||
document.getElementById(this.playerId).querySelectorAll('.vjs-menu').forEach(element => {
|
||||
element.classList.remove('vjs-lock-open');
|
||||
});
|
||||
}
|
||||
});
|
||||
this.player.on('loadstart', _event => {
|
||||
this.removeElementsByClass('vjs-license-clear');
|
||||
if (videojs__default["default"].browser.IS_IOS || videojs__default["default"].browser.IS_ANDROID) {
|
||||
this.mobileBuildTopLevelMenu();
|
||||
} else {
|
||||
this.buildTopLevelMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*/
|
||||
buildUI() {
|
||||
const playerId = this.playerId;
|
||||
const that = this;
|
||||
|
||||
/**
|
||||
* LicenseMenuButton
|
||||
*/
|
||||
class LicenseMenuButton extends Button {
|
||||
/**
|
||||
* Contructor
|
||||
*
|
||||
* @param {*} player videojs player instance
|
||||
* @param {*} options videojs player options
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
this.addClass('vjs-license');
|
||||
this.controlText(that.options.languages.loading);
|
||||
player.one('canplaythrough', _event => {
|
||||
this.controlText(that.options.languages.settings);
|
||||
});
|
||||
this.menu.contentEl_.id = playerId + '-vjs-license-default';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle click
|
||||
*/
|
||||
handleClick() {
|
||||
if (videojs__default["default"].browser.IS_IOS || videojs__default["default"].browser.IS_ANDROID) {
|
||||
this.player.getChild('licenseMenuMobileModal').el().style.display = 'block';
|
||||
} else {
|
||||
this.el().classList.toggle('vjs-toogle-btn');
|
||||
this.menu.el().classList.toggle('vjs-lock-open');
|
||||
}
|
||||
}
|
||||
}
|
||||
videojs__default["default"].registerComponent('licenseMenuButton', LicenseMenuButton);
|
||||
this.player.getChild('controlBar').addChild('licenseMenuButton');
|
||||
if (this.player.getChild('controlBar').getChild('fullscreenToggle')) {
|
||||
this.player.getChild('controlBar').el().insertBefore(this.player.getChild('controlBar').getChild('licenseMenuButton').el(), this.player.getChild('controlBar').getChild('fullscreenToggle').el());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This is just build the top level menu no sub menus
|
||||
*/
|
||||
buildTopLevelMenu() {
|
||||
const settingsButton = this.player.getChild('controlBar').getChild('licenseMenuButton');
|
||||
|
||||
// settingsButton.addClass('vjs-license-is-loaded');
|
||||
|
||||
const main = settingsButton.menu.contentEl_;
|
||||
|
||||
// Empty the main menu div to repopulate
|
||||
main.innerHTML = '';
|
||||
main.classList.add('vjs-license-top-level');
|
||||
|
||||
// Start building new list items
|
||||
const menuTitle = document.createElement('li');
|
||||
menuTitle.className = 'vjs-license-top-level-header';
|
||||
const menuTitleInner = document.createElement('span');
|
||||
menuTitleInner.innerHTML = 'About';
|
||||
menuTitleInner.className = 'vjs-license-top-level-header-titel';
|
||||
menuTitle.appendChild(menuTitleInner);
|
||||
main.appendChild(menuTitle);
|
||||
const itemTitel = document.createElement('li');
|
||||
itemTitel.innerHTML = this.buildItemTitel();
|
||||
itemTitel.className = 'vjs-license-top-level-item';
|
||||
main.appendChild(itemTitel);
|
||||
if (this.options.author) {
|
||||
const itemAuthor = document.createElement('li');
|
||||
itemAuthor.innerHTML = this.buildItemAuthor();
|
||||
itemAuthor.className = 'vjs-license-top-level-item';
|
||||
main.appendChild(itemAuthor);
|
||||
}
|
||||
const itemLicense = document.createElement('li');
|
||||
itemLicense.innerHTML = this.buildItemLicense();
|
||||
itemLicense.className = 'vjs-license-top-level-item';
|
||||
main.appendChild(itemLicense);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*/
|
||||
mobileBuildUI() {
|
||||
/**
|
||||
* bla
|
||||
*/
|
||||
class LicenseMenuMobileModal extends Component {
|
||||
/**
|
||||
* Contructor
|
||||
*
|
||||
* @param {*} player videojs player instance
|
||||
* @param {*} options videojs player options
|
||||
*/
|
||||
constructor(player, options) {
|
||||
super(player, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an HTML element
|
||||
*
|
||||
* @return {Object} HTML element
|
||||
*/
|
||||
createEl() {
|
||||
return videojs__default["default"].createEl('div', {
|
||||
className: 'vjs-license-mobile'
|
||||
});
|
||||
}
|
||||
}
|
||||
videojs__default["default"].registerComponent('licenseMenuMobileModal', LicenseMenuMobileModal);
|
||||
videojs__default["default"].dom.prependTo(this.player.addChild('licenseMenuMobileModal').el(), document.body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*/
|
||||
mobileBuildTopLevelMenu() {
|
||||
const settingsButton = this.player.getChild('licenseMenuMobileModal');
|
||||
const menuTopLevel = document.createElement('ul');
|
||||
menuTopLevel.className = 'vjs-license-mob-top-level vjs-setting-menu-clear';
|
||||
settingsButton.el().appendChild(menuTopLevel);
|
||||
|
||||
// Empty the main menu div to repopulate
|
||||
const menuTitle = document.createElement('li');
|
||||
menuTitle.innerHTML = 'About';
|
||||
menuTitle.className = 'vjs-setting-menu-mobile-top-header';
|
||||
menuTopLevel.appendChild(menuTitle);
|
||||
const itemTitel = document.createElement('li');
|
||||
itemTitel.innerHTML = this.buildItemTitel();
|
||||
itemTitel.className = 'vjs-license-top-level-item';
|
||||
if (this.options.author) {
|
||||
const itemAuthor = document.createElement('li');
|
||||
itemAuthor.innerHTML = this.buildItemAuthor();
|
||||
itemAuthor.className = 'vjs-license-top-level-item';
|
||||
}
|
||||
const itemLicense = document.createElement('li');
|
||||
itemLicense.innerHTML = this.buildItemLicense();
|
||||
itemLicense.className = 'vjs-license-top-level-item';
|
||||
const menuClose = document.createElement('li');
|
||||
menuClose.innerHTML = 'Close';
|
||||
menuClose.className = 'setting-menu-footer-default';
|
||||
menuClose.onclick = e => {
|
||||
this.player.getChild('settingsMenuMobileModal').el().style.display = 'none';
|
||||
};
|
||||
menuTopLevel.appendChild(menuClose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*
|
||||
* @return {string} Returns license text
|
||||
*/
|
||||
buildItemTitel() {
|
||||
let titel = '';
|
||||
if (this.options.title) {
|
||||
titel = `${this.options.title}`;
|
||||
}
|
||||
return 'Title: ' + titel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*
|
||||
* @return {string} Returns license text
|
||||
*/
|
||||
buildItemAuthor() {
|
||||
let author = '';
|
||||
if (this.options.author) {
|
||||
author = ` by ${this.options.author}`;
|
||||
}
|
||||
return 'Author: ' + author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the menu ui button to the controlbar
|
||||
*
|
||||
* @return {string} Returns license text
|
||||
*/
|
||||
buildItemLicense() {
|
||||
let license = '';
|
||||
const reVersion = new RegExp('[0-9]+.[0-9]+$');
|
||||
let version = '4.0';
|
||||
const matches = this.options.license.match(reVersion);
|
||||
if (matches !== null) {
|
||||
version = matches[0];
|
||||
}
|
||||
const which = this.options.license.replace(reVersion, '').trim();
|
||||
let deed = null;
|
||||
switch (which) {
|
||||
case 'CC0':
|
||||
deed = 'https://creativecommons.org/licenses/zero/1.0/';
|
||||
break;
|
||||
case 'CC BY':
|
||||
deed = `https://creativecommons.org/licenses/by/${version}/`;
|
||||
break;
|
||||
case 'CC BY-SA':
|
||||
deed = `https://creativecommons.org/licenses/by-sa/${version}/`;
|
||||
break;
|
||||
case 'CC BY-NC':
|
||||
deed = `https://creativecommons.org/licenses/by-nc/${version}/`;
|
||||
break;
|
||||
case 'CC BY-NC-SA':
|
||||
deed = `https://creativecommons.org/licenses/by-nc-sa/${version}/`;
|
||||
break;
|
||||
case 'CC BY-ND':
|
||||
deed = `https://creativecommons.org/licenses/by-nd/${version}/`;
|
||||
break;
|
||||
case 'CC BY-NC-ND':
|
||||
deed = `https://creativecommons.org/licenses/by-nc-nd/${version}/`;
|
||||
break;
|
||||
}
|
||||
if (deed) {
|
||||
license = `<a href='${deed}' onclick='window.open('${deed}')' target='_blank' rel='noopener'>${this.options.license}</a>`;
|
||||
} else {
|
||||
license = this.options.license;
|
||||
}
|
||||
return 'License: ' + license;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Helper class to clear menu items before rebuild
|
||||
*
|
||||
* @param {*} className Name of a class
|
||||
*/
|
||||
removeElementsByClass(className) {
|
||||
// Need to prevent the menu from not showing sometimes
|
||||
document.querySelectorAll('.vjs-sm-top-level').forEach(element => {
|
||||
element.classList.remove('vjs-hidden');
|
||||
});
|
||||
const elements = document.getElementsByClassName(className);
|
||||
while (elements.length > 0) {
|
||||
elements[0].parentNode.removeChild(elements[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Define default values for the plugin's `state` object here.
|
||||
License.defaultState = {};
|
||||
|
||||
// Include the version number.
|
||||
License.VERSION = version;
|
||||
|
||||
// Register the plugin with video.js.
|
||||
videojs__default["default"].registerPlugin('license', License);
|
||||
|
||||
return License;
|
||||
|
||||
}));
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -83,9 +83,6 @@
|
||||
responsive: true,
|
||||
fluid: true,
|
||||
sources: [{ src: window.location.origin + '/' + playerConfig.source, type: 'application/x-mpegURL' }],
|
||||
plugins: {
|
||||
license: playerConfig.license
|
||||
}
|
||||
};
|
||||
|
||||
if (playerConfig.chromecast) {
|
||||
@ -132,6 +129,8 @@
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
player.license(playerConfig.license);
|
||||
}
|
||||
|
||||
if (autoplay === true) {
|
||||
|
||||
@ -8,23 +8,26 @@ var config = {
|
||||
fluid: true,
|
||||
// Needed to append the url orgin in order for the source to properly pass to the cast device
|
||||
sources: [{ src: playerConfig.source, type: 'application/x-mpegURL' }],
|
||||
plugins: {
|
||||
license: playerConfig.license,
|
||||
},
|
||||
};
|
||||
|
||||
if (chromecast) {
|
||||
config.techOrder = ['chromecast', 'html5'];
|
||||
config.plugins.chromecast = {};
|
||||
}
|
||||
|
||||
if (airplay) {
|
||||
config.plugins.airPlay = {};
|
||||
}
|
||||
|
||||
var player = videojs('player', config);
|
||||
|
||||
player.ready(function () {
|
||||
|
||||
if (chromecast) {
|
||||
player.chromecast();
|
||||
}
|
||||
|
||||
if (airplay) {
|
||||
player.airPlay();
|
||||
}
|
||||
|
||||
player.license(playerConfig.license);
|
||||
|
||||
if (playerConfig.logo.image.length != 0) {
|
||||
var overlay = null;
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
|
||||
24
src/I18n.js
24
src/I18n.js
@ -2,7 +2,6 @@ import React from 'react';
|
||||
|
||||
import { I18nProvider } from '@lingui/react';
|
||||
import { i18n } from '@lingui/core';
|
||||
import * as plurals from 'make-plural/plurals';
|
||||
|
||||
import { messages as EN } from './locales/en/messages.js';
|
||||
import { messages as DA } from './locales/da/messages.js';
|
||||
@ -13,7 +12,7 @@ import { messages as FR } from './locales/fr/messages.js';
|
||||
import { messages as IT } from './locales/it/messages.js';
|
||||
import { messages as KO } from './locales/ko/messages.js';
|
||||
import { messages as PL } from './locales/pl/messages.js';
|
||||
import { messages as PT } from './locales/pt/messages.js';
|
||||
import { messages as PT } from './locales/pt-br/messages.js';
|
||||
import { messages as RU } from './locales/ru/messages.js';
|
||||
import { messages as SL } from './locales/sl/messages.js';
|
||||
import { messages as TR } from './locales/tr/messages.js';
|
||||
@ -21,21 +20,6 @@ import { messages as UK } from './locales/uk/messages.js';
|
||||
import { messages as ZH } from './locales/zh-hans/messages.js';
|
||||
import * as Storage from './utils/storage';
|
||||
|
||||
i18n.loadLocaleData('en', { plurals: plurals.en });
|
||||
i18n.loadLocaleData('da', { plurals: plurals.da });
|
||||
i18n.loadLocaleData('de', { plurals: plurals.de });
|
||||
i18n.loadLocaleData('el', { plurals: plurals.el });
|
||||
i18n.loadLocaleData('es', { plurals: plurals.es });
|
||||
i18n.loadLocaleData('fr', { plurals: plurals.fr });
|
||||
i18n.loadLocaleData('it', { plurals: plurals.it });
|
||||
i18n.loadLocaleData('ko', { plurals: plurals.ko });
|
||||
i18n.loadLocaleData('pl', { plurals: plurals.pl });
|
||||
i18n.loadLocaleData('pt', { plurals: plurals.pt });
|
||||
i18n.loadLocaleData('ru', { plurals: plurals.ru });
|
||||
i18n.loadLocaleData('sl', { plurals: plurals.sl });
|
||||
i18n.loadLocaleData('tr', { plurals: plurals.tr });
|
||||
i18n.loadLocaleData('uk', { plurals: plurals.tr });
|
||||
i18n.loadLocaleData('zh-hans', { plurals: plurals.zh });
|
||||
i18n.load({
|
||||
en: EN,
|
||||
da: DA,
|
||||
@ -46,7 +30,7 @@ i18n.load({
|
||||
it: IT,
|
||||
ko: KO,
|
||||
pl: PL,
|
||||
pt: PT,
|
||||
'pt-br': PT,
|
||||
ru: RU,
|
||||
sl: SL,
|
||||
tr: TR,
|
||||
@ -55,7 +39,7 @@ i18n.load({
|
||||
});
|
||||
|
||||
const aliases = {
|
||||
'pt-br': 'pt',
|
||||
pt: 'pt-br',
|
||||
'zh-cn': 'zh-hans',
|
||||
};
|
||||
|
||||
@ -93,7 +77,7 @@ const getBrowserLanguage = (defaultLanguage) => {
|
||||
return match[0].toLowerCase();
|
||||
};
|
||||
|
||||
i18n.activate(getLanguage('en', ['en', 'da', 'de', 'el', 'es', 'fr', 'it', 'ko', 'pl', 'pt', 'ru', 'sl', 'tr', 'uk', 'zh-hans']));
|
||||
i18n.activate(getLanguage('en', ['en', 'da', 'de', 'el', 'es', 'fr', 'it', 'ko', 'pl', 'pt-br', 'ru', 'sl', 'tr', 'uk', 'zh-hans']));
|
||||
|
||||
export default function Provider(props) {
|
||||
return <I18nProvider i18n={i18n}>{props.children}</I18nProvider>;
|
||||
|
||||
@ -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
1
src/locales/pt-br/messages.js
Normal file
1
src/locales/pt-br/messages.js
Normal file
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 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) {},
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -155,14 +161,14 @@ export default function EncodingSelect(props) {
|
||||
encoderList.push(
|
||||
<MenuItem value={c.coder} key={c.coder}>
|
||||
{c.name}
|
||||
</MenuItem>
|
||||
</MenuItem>,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
encoderList.push(
|
||||
<MenuItem value={c.coder} key={c.coder}>
|
||||
{c.name}
|
||||
</MenuItem>
|
||||
</MenuItem>,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -195,7 +201,7 @@ export default function EncodingSelect(props) {
|
||||
decoderList.push(
|
||||
<MenuItem value={c.coder} key={c.coder}>
|
||||
{c.name}
|
||||
</MenuItem>
|
||||
</MenuItem>,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -13,26 +13,21 @@ const MenuProps = {
|
||||
},
|
||||
};
|
||||
|
||||
export default function Component(props) {
|
||||
export default function Component({
|
||||
variant = 'outlined',
|
||||
label = '',
|
||||
value = [],
|
||||
disabled = false,
|
||||
renderValue = (selected) => selected.join(', '),
|
||||
onChange = function (event) {},
|
||||
children = null,
|
||||
}) {
|
||||
return (
|
||||
<FormControl variant="outlined" fullWidth>
|
||||
<InputLabel>{props.label}</InputLabel>
|
||||
<Select
|
||||
multiple
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
input={<OutlinedInput />}
|
||||
renderValue={(selected) => selected.join(', ')}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{props.children}
|
||||
<FormControl variant={variant} disabled={disabled} fullWidth>
|
||||
<InputLabel>{label}</InputLabel>
|
||||
<Select multiple value={value} onChange={onChange} input={<OutlinedInput />} renderValue={renderValue} MenuProps={MenuProps}>
|
||||
{children}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
|
||||
Component.defaultProps = {
|
||||
label: '',
|
||||
value: [],
|
||||
onChange: function (event) {},
|
||||
};
|
||||
|
||||
@ -10,22 +10,14 @@ const useStyles = makeStyles((theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const Component = React.forwardRef((props, ref) => {
|
||||
const Component = React.forwardRef(({ key = '', name = '', value = '', selected = false }, 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 key={key} value={value} className={selected ? classes.root : ''} ref={ref}>
|
||||
{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,7 +68,7 @@ export default function Player(props) {
|
||||
|
||||
if (player.overlay) {
|
||||
player.overlay({
|
||||
align: props.logo.position,
|
||||
align: logo.position,
|
||||
overlays: [
|
||||
{
|
||||
showBackground: false,
|
||||
@ -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,
|
||||
};
|
||||
|
||||
@ -9,10 +9,18 @@ import './video-js-skin-internal.min.css';
|
||||
import './video-js-skin-public.min.css';
|
||||
import 'videojs-overlay/dist/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,10 +48,14 @@ export default function Component(props) {
|
||||
setValue({
|
||||
...$value,
|
||||
value: v,
|
||||
isCustom: v === props.customKey ? true : false,
|
||||
isCustom: v === customKey ? true : false,
|
||||
});
|
||||
|
||||
props.onChange(event);
|
||||
onChange({
|
||||
target: {
|
||||
value: v === customKey ? value.custom : value.value,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleCustomChange = (event) => {
|
||||
@ -50,44 +64,44 @@ 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>
|
||||
</MenuItem>,
|
||||
);
|
||||
}
|
||||
|
||||
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}
|
||||
/>
|
||||
@ -95,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>
|
||||
@ -111,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>
|
||||
@ -122,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,22 @@ 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',
|
||||
helperText = null,
|
||||
onChange = function (value) {},
|
||||
}) {
|
||||
id = id === null ? uuidv4() : id;
|
||||
let adornment = null;
|
||||
|
||||
if (props.env) {
|
||||
if (env) {
|
||||
adornment = (
|
||||
<InputAdornment position="end">
|
||||
<Env />
|
||||
@ -22,32 +33,10 @@ export default function Component(props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl variant="outlined" disabled={props.disabled} fullWidth>
|
||||
<InputLabel htmlFor={id}>{props.label}</InputLabel>
|
||||
<OutlinedInput
|
||||
id={id}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
endAdornment={adornment}
|
||||
label={props.label}
|
||||
multiline={props.multiline}
|
||||
rows={props.rows}
|
||||
type={props.type}
|
||||
/>
|
||||
{props.helperText && <FormHelperText>{props.helperText}</FormHelperText>}
|
||||
<FormControl variant="outlined" disabled={disabled} fullWidth>
|
||||
<InputLabel htmlFor={id}>{label}</InputLabel>
|
||||
<OutlinedInput id={id} value={value} onChange={onChange} endAdornment={adornment} label={label} multiline={multiline} rows={rows} type={type} />
|
||||
{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(),
|
||||
@ -48,7 +55,7 @@ export default function UploadButton(props) {
|
||||
|
||||
if (file.size > type.maxSize) {
|
||||
// the file is too big
|
||||
props.onError({
|
||||
onError({
|
||||
type: 'size',
|
||||
actual: file.size,
|
||||
allowed: type.maxSize,
|
||||
@ -61,18 +68,18 @@ export default function UploadButton(props) {
|
||||
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);
|
||||
onUpload(reader.result, type.extension, type.mimetype);
|
||||
};
|
||||
};
|
||||
|
||||
props.onStart();
|
||||
onStart();
|
||||
|
||||
handler(event);
|
||||
|
||||
@ -82,16 +89,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) {},
|
||||
};
|
||||
|
||||
@ -17,15 +17,16 @@ function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
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;
|
||||
@ -34,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(() => {
|
||||
@ -45,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,22 +1,22 @@
|
||||
import * as AudioDefault from './audio/Default';
|
||||
import * as AudioDefault from './audio/default';
|
||||
|
||||
import * as NVDEC from './video/NVDEC';
|
||||
import * as H264MMAL from './video/H264MMAL';
|
||||
import * as H264CUVID from './video/H264CUVID';
|
||||
import * as HEVCCUVID from './video/HEVCCUVID';
|
||||
import * as MJPEGCUVID from './video/MJPEGCUVID';
|
||||
import * as MPEG1CUVID from './video/MPEG1CUVID';
|
||||
import * as MPEG2CUVID from './video/MPEG2CUVID';
|
||||
import * as MPEG2MMAL from './video/MPEG2MMAL';
|
||||
import * as MPEG4CUVID from './video/MPEG4CUVID';
|
||||
import * as MPEG4MMAL from './video/MPEG4MMAL';
|
||||
import * as VC1CUVID from './video/VC1CUVID';
|
||||
import * as VC1MMAL from './video/VC1MMAL';
|
||||
import * as VideoDefault from './video/Default';
|
||||
import * as VideoToolbox from './video/VideoToolbox';
|
||||
import * as VP8CUVID from './video/VP8CUVID';
|
||||
import * as VP9CUVID from './video/VP9CUVID';
|
||||
import * as AV1CUVID from './video/AV1CUVID';
|
||||
import * as NVDEC from './video/nvdec';
|
||||
import * as H264MMAL from './video/h264_mmal';
|
||||
import * as H264CUVID from './video/h264_cuvid';
|
||||
import * as HEVCCUVID from './video/hevc_cuvid';
|
||||
import * as MJPEGCUVID from './video/mjpeg_cuvid';
|
||||
import * as MPEG1CUVID from './video/mpeg1_cuvid';
|
||||
import * as MPEG2CUVID from './video/mpeg2_cuvid';
|
||||
import * as MPEG2MMAL from './video/mpeg2_mmal';
|
||||
import * as MPEG4CUVID from './video/mpeg4_cuvid';
|
||||
import * as MPEG4MMAL from './video/mpeg4_mmal';
|
||||
import * as VC1CUVID from './video/vc1_cuvid';
|
||||
import * as VC1MMAL from './video/vc1_mmal';
|
||||
import * as VideoDefault from './video/default';
|
||||
import * as VideoToolbox from './video/videotoolbox';
|
||||
import * as VP8CUVID from './video/vp8_cuvid';
|
||||
import * as VP9CUVID from './video/vp9_cuvid';
|
||||
import * as AV1CUVID from './video/av1_cuvid';
|
||||
|
||||
class Registry {
|
||||
constructor(type) {
|
||||
|
||||
@ -17,15 +17,16 @@ function createMapping(settings, stream, skills) {
|
||||
const mapping = {
|
||||
global: [],
|
||||
local: ['-hwaccel', 'cuda', '-c:v', 'av1_cuvid'],
|
||||
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;
|
||||
@ -34,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(() => {
|
||||
@ -45,13 +46,6 @@ function Coder(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Coder.defaultProps = {
|
||||
stream: {},
|
||||
settings: {},
|
||||
skills: {},
|
||||
onChange: function (settings, mapping) {},
|
||||
};
|
||||
|
||||
// -hwaccel nvdec
|
||||
|
||||
const coder = 'av1_cuvid';
|
||||
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