mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-17 10:17:50 +00:00
feat(recording): frontend logic can support live streaming and recording (#2952)
* feat(recording): frontend logic can support live streaming and recording Instead of either live streaming or recording, now both can live together. The changes to facilitate such include the following: - Killing the state storing in Recording.js. Instead state is stored in the lib and updated in redux for labels to display the necessary state updates. - Creating a new container, Labels, for recording labels. Previously labels were manually created and positioned. The container can create a reasonable number of labels and only the container itself needs to be positioned with CSS. The VideoQualityLabel has been shoved into the container as well because it moves along with the recording labels. - The action for updating recording state has been modified to enable updating an array of recording sessions to support having multiple sessions. - Confirmation dialogs for stopping and starting a file recording session have been created, as they previously were jquery modals opened by Recording.js. - Toolbox.web displays live streaming and recording buttons based on configuration instead of recording availability. - VideoQualityLabel and RecordingLabel have been simplified to remove any positioning logic, as the Labels container handles such. - Previous recording state update logic has been moved into the RecordingLabel component. Each RecordingLabel is in charge of displaying state for a recording session. The display UX has been left alone. - Sipgw availability is no longer broadcast so remove logic depending on its state. Some moving around of code was necessary to get around linting errors about the existing code being too deeply nested (even though I didn't touch it). * work around lib-jitsi-meet circular dependency issues * refactor labels to use html base * pass in translation keys to video quality label * add video quality classnames for torture tests * break up, rearrange recorder session update listener * add comment about disabling startup resize animation * rename session to sessionData * chore(deps): update to latest lib for recording changes
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
/* globals APP, interfaceConfig */
|
||||
// @flow
|
||||
|
||||
import Spinner from '@atlaskit/spinner';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import {
|
||||
createRecordingDialogEvent,
|
||||
sendAnalytics
|
||||
} from '../../../analytics';
|
||||
import { Dialog } from '../../../base/dialog';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
|
||||
import googleApi from '../../googleApi';
|
||||
|
||||
@@ -14,6 +18,8 @@ import BroadcastsDropdown from './BroadcastsDropdown';
|
||||
import GoogleSignInButton from './GoogleSignInButton';
|
||||
import StreamKeyForm from './StreamKeyForm';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* An enumeration of the different states the Google API can be in while
|
||||
* interacting with {@code StartLiveStreamDialog}.
|
||||
@@ -44,63 +50,73 @@ const GOOGLE_API_STATES = {
|
||||
ERROR: 3
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of
|
||||
* {@link StartLiveStreamDialog}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The {@code JitsiConference} for the current conference.
|
||||
*/
|
||||
_conference: Object,
|
||||
|
||||
/**
|
||||
* The ID for the Google web client application used for making stream key
|
||||
* related requests.
|
||||
*/
|
||||
_googleApiApplicationClientID: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of
|
||||
* {@link StartLiveStreamDialog}.
|
||||
*/
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Details about the broadcasts available for use for the logged in Google
|
||||
* user's YouTube account.
|
||||
*/
|
||||
broadcasts: ?Array<Object>,
|
||||
|
||||
/**
|
||||
* The current state of interactions with the Google API. Determines what
|
||||
* Google related UI should display.
|
||||
*/
|
||||
googleAPIState: number,
|
||||
|
||||
/**
|
||||
* The email of the user currently logged in to the Google web client
|
||||
* application.
|
||||
*/
|
||||
googleProfileEmail: string,
|
||||
|
||||
/**
|
||||
* The boundStreamID of the broadcast currently selected in the broadcast
|
||||
* dropdown.
|
||||
*/
|
||||
selectedBoundStreamID: ?string,
|
||||
|
||||
/**
|
||||
* The selected or entered stream key to use for YouTube live streaming.
|
||||
*/
|
||||
streamKey: string
|
||||
};
|
||||
|
||||
/**
|
||||
* A React Component for requesting a YouTube stream key to use for live
|
||||
* streaming of the current conference.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class StartLiveStreamDialog extends Component {
|
||||
/**
|
||||
* {@code StartLiveStreamDialog} component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The ID for the Google web client application used for making stream
|
||||
* key related requests.
|
||||
*/
|
||||
_googleApiApplicationClientID: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Callback to invoke when the dialog is dismissed without submitting a
|
||||
* stream key.
|
||||
*/
|
||||
onCancel: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Callback to invoke when a stream key is submitted for use.
|
||||
*/
|
||||
onSubmit: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: PropTypes.func
|
||||
};
|
||||
|
||||
/**
|
||||
* {@code StartLiveStreamDialog} component's local state.
|
||||
*
|
||||
* @property {boolean} googleAPIState - The current state of interactions
|
||||
* with the Google API. Determines what Google related UI should display.
|
||||
* @property {Object[]|undefined} broadcasts - Details about the broadcasts
|
||||
* available for use for the logged in Google user's YouTube account.
|
||||
* @property {string} googleProfileEmail - The email of the user currently
|
||||
* logged in to the Google web client application.
|
||||
* @property {string} selectedBoundStreamID - The boundStreamID of the
|
||||
* broadcast currently selected in the broadcast dropdown.
|
||||
* @property {string} streamKey - The selected or entered stream key to use
|
||||
* for YouTube live streaming.
|
||||
*/
|
||||
state = {
|
||||
broadcasts: undefined,
|
||||
googleAPIState: GOOGLE_API_STATES.NEEDS_LOADING,
|
||||
googleProfileEmail: '',
|
||||
selectedBoundStreamID: undefined,
|
||||
streamKey: ''
|
||||
};
|
||||
class StartLiveStreamDialog extends Component<Props, State> {
|
||||
_isMounted: boolean;
|
||||
|
||||
/**
|
||||
* Initializes a new {@code StartLiveStreamDialog} instance.
|
||||
@@ -108,9 +124,17 @@ class StartLiveStreamDialog extends Component {
|
||||
* @param {Props} props - The React {@code Component} props to initialize
|
||||
* the new {@code StartLiveStreamDialog} instance with.
|
||||
*/
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
broadcasts: undefined,
|
||||
googleAPIState: GOOGLE_API_STATES.NEEDS_LOADING,
|
||||
googleProfileEmail: '',
|
||||
selectedBoundStreamID: undefined,
|
||||
streamKey: ''
|
||||
};
|
||||
|
||||
/**
|
||||
* Instance variable used to flag whether the component is or is not
|
||||
* mounted. Used as a hack to avoid setting state on an unmounted
|
||||
@@ -186,6 +210,8 @@ class StartLiveStreamDialog extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
_onInitializeGoogleApi: () => Object;
|
||||
|
||||
/**
|
||||
* Loads the Google web client application used for fetching stream keys.
|
||||
* If the user is already logged in, then a request for available YouTube
|
||||
@@ -214,6 +240,8 @@ class StartLiveStreamDialog extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
_onCancel: () => boolean;
|
||||
|
||||
/**
|
||||
* Invokes the passed in {@link onCancel} callback and closes
|
||||
* {@code StartLiveStreamDialog}.
|
||||
@@ -222,11 +250,13 @@ class StartLiveStreamDialog extends Component {
|
||||
* @returns {boolean} True is returned to close the modal.
|
||||
*/
|
||||
_onCancel() {
|
||||
this.props.onCancel(APP.UI.messageHandler.CANCEL);
|
||||
sendAnalytics(createRecordingDialogEvent('start', 'cancel.button'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_onGetYouTubeBroadcasts: () => Object;
|
||||
|
||||
/**
|
||||
* Asks the user to sign in, if not already signed in, and then requests a
|
||||
* list of the user's YouTube broadcasts.
|
||||
@@ -269,6 +299,8 @@ class StartLiveStreamDialog extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
_onRequestGoogleSignIn: () => Object;
|
||||
|
||||
/**
|
||||
* Forces the Google web client application to prompt for a sign in, such as
|
||||
* when changing account, and will then fetch available YouTube broadcasts.
|
||||
@@ -282,6 +314,8 @@ class StartLiveStreamDialog extends Component {
|
||||
.then(() => this._onGetYouTubeBroadcasts());
|
||||
}
|
||||
|
||||
_onStreamKeyChange: () => void;
|
||||
|
||||
/**
|
||||
* Callback invoked to update the {@code StartLiveStreamDialog} component's
|
||||
* display of the entered YouTube stream key.
|
||||
@@ -297,6 +331,8 @@ class StartLiveStreamDialog extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
_onSubmit: () => boolean;
|
||||
|
||||
/**
|
||||
* Invokes the passed in {@link onSubmit} callback with the entered stream
|
||||
* key, and then closes {@code StartLiveStreamDialog}.
|
||||
@@ -306,7 +342,7 @@ class StartLiveStreamDialog extends Component {
|
||||
* closing, true to close the modal.
|
||||
*/
|
||||
_onSubmit() {
|
||||
const { streamKey, selectedBoundStreamID } = this.state;
|
||||
const { broadcasts, streamKey, selectedBoundStreamID } = this.state;
|
||||
|
||||
if (!streamKey) {
|
||||
return false;
|
||||
@@ -315,17 +351,25 @@ class StartLiveStreamDialog extends Component {
|
||||
let selectedBroadcastID = null;
|
||||
|
||||
if (selectedBoundStreamID) {
|
||||
const selectedBroadcast = this.state.broadcasts.find(
|
||||
const selectedBroadcast = broadcasts && broadcasts.find(
|
||||
broadcast => broadcast.boundStreamID === selectedBoundStreamID);
|
||||
|
||||
selectedBroadcastID = selectedBroadcast && selectedBroadcast.id;
|
||||
}
|
||||
|
||||
this.props.onSubmit(streamKey, selectedBroadcastID);
|
||||
sendAnalytics(createRecordingDialogEvent('start', 'confirm.button'));
|
||||
|
||||
this.props._conference.startRecording({
|
||||
broadcastId: selectedBroadcastID,
|
||||
mode: JitsiRecordingConstants.mode.STREAM,
|
||||
streamId: streamKey
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_onYouTubeBroadcastIDSelected: (string) => Object;
|
||||
|
||||
/**
|
||||
* Fetches the stream key for a YouTube broadcast and updates the internal
|
||||
* state to display the associated stream key as being entered.
|
||||
@@ -351,6 +395,8 @@ class StartLiveStreamDialog extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
_parseBroadcasts: (Array<Object>) => Array<Object>;
|
||||
|
||||
/**
|
||||
* Takes in a list of broadcasts from the YouTube API, removes dupes,
|
||||
* removes broadcasts that cannot get a stream key, and parses the
|
||||
@@ -487,13 +533,15 @@ class StartLiveStreamDialog extends Component {
|
||||
* {@code StartLiveStreamDialog}.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @private
|
||||
* @returns {{
|
||||
* _conference: Object,
|
||||
* _googleApiApplicationClientID: string
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_conference: state['features/base/conference'].conference,
|
||||
_googleApiApplicationClientID:
|
||||
state['features/base/config'].googleApiApplicationClientID
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user