WIP: live stream kill switch (#209)
* WIP: live stream kill switch * Update hint layout / style * update livestream API endpoint * use the no-cors option
This commit is contained in:
parent
db12a4b991
commit
11d3f88654
6 changed files with 149 additions and 5 deletions
|
@ -24,6 +24,7 @@ import FileDownloadLink from 'component/fileDownloadLink';
|
|||
import FileWatchLaterLink from 'component/fileWatchLaterLink';
|
||||
import PublishPending from 'component/publishPending';
|
||||
import ClaimMenuList from 'component/claimMenuList';
|
||||
import ClaimPreviewReset from 'component/claimPreviewReset';
|
||||
import ClaimPreviewLoading from './claim-preview-loading';
|
||||
import ClaimPreviewHidden from './claim-preview-no-mature';
|
||||
import ClaimPreviewNoContent from './claim-preview-no-content';
|
||||
|
@ -480,6 +481,13 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{claimIsMine && isLivestream && (
|
||||
<div className={'claim-preview__hints'}>
|
||||
<ClaimPreviewReset />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!hideMenu && <ClaimMenuList uri={uri} collectionId={listId} />}
|
||||
</>
|
||||
</WrapperElement>
|
||||
|
|
18
ui/component/claimPreviewReset/index.js
Normal file
18
ui/component/claimPreviewReset/index.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
||||
import { doToast } from 'redux/actions/notifications';
|
||||
import ClaimPreviewReset from './view';
|
||||
|
||||
const select = (state) => {
|
||||
const { claim_id: channelId, name: channelName } = selectActiveChannelClaim(state) || {};
|
||||
return {
|
||||
channelName,
|
||||
channelId,
|
||||
};
|
||||
};
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
doToast: (props) => dispatch(doToast(props)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(ClaimPreviewReset);
|
87
ui/component/claimPreviewReset/view.jsx
Normal file
87
ui/component/claimPreviewReset/view.jsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import Lbry from 'lbry';
|
||||
import { LIVESTREAM_KILL } from 'constants/livestream';
|
||||
import { SITE_HELP_EMAIL } from 'config';
|
||||
import { toHex } from 'util/hex';
|
||||
import Button from 'component/button';
|
||||
import 'scss/component/claim-preview-reset.scss';
|
||||
|
||||
// @Todo: move out of component.
|
||||
const getStreamData = async (channelId: string, channelName: string) => {
|
||||
if (!channelId || !channelName) throw new Error('Invalid channel data provided.');
|
||||
|
||||
const channelNameHex = toHex(channelName);
|
||||
let channelSignature;
|
||||
|
||||
try {
|
||||
channelSignature = await Lbry.channel_sign({ channel_id: channelId, hexdata: channelNameHex });
|
||||
if (!channelSignature || !channelSignature.signature || !channelSignature.signing_ts) {
|
||||
throw new Error('Error getting channel signature.');
|
||||
}
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
return {
|
||||
d: channelNameHex,
|
||||
s: channelSignature.signature,
|
||||
t: channelSignature.signing_ts,
|
||||
};
|
||||
};
|
||||
|
||||
// @Todo: move out of component.
|
||||
const killStream = async (channelId: string, payload: any) => {
|
||||
fetch(`${LIVESTREAM_KILL}/${channelId}`, {
|
||||
method: 'POST',
|
||||
mode: 'no-cors',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
body: new URLSearchParams(payload),
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.status === 200) throw new Error('Kill stream API failed.');
|
||||
})
|
||||
.catch((e) => {
|
||||
throw e;
|
||||
});
|
||||
};
|
||||
|
||||
type Props = {
|
||||
channelId: string,
|
||||
channelName: string,
|
||||
doToast: ({ message: string, isError?: boolean }) => void,
|
||||
};
|
||||
|
||||
const ClaimPreviewReset = (props: Props) => {
|
||||
const { channelId, channelName, doToast } = props;
|
||||
|
||||
const handleClick = async () => {
|
||||
try {
|
||||
const streamData = await getStreamData(channelId, channelName);
|
||||
await killStream(channelId, streamData);
|
||||
doToast({ message: __('Live stream successfully reset.'), isError: false });
|
||||
} catch {
|
||||
doToast({ message: __('There was an error resetting the live stream.'), isError: true });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<p className={'claimPreviewReset'}>
|
||||
<span className={'claimPreviewReset__hint'}>
|
||||
{__(
|
||||
"If you're having trouble starting a stream or if your stream shows that you're live but aren't, try a reset. If the problem persists, please reach out at %SITE_HELP_EMAIL%.",
|
||||
{ SITE_HELP_EMAIL }
|
||||
)}
|
||||
</span>
|
||||
<Button
|
||||
button="primary"
|
||||
label={__('Reset stream')}
|
||||
className={'claimPreviewReset__button'}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export default ClaimPreviewReset;
|
|
@ -2,3 +2,4 @@ export const LIVESTREAM_EMBED_URL = 'https://player.live.odysee.com/odysee';
|
|||
export const LIVESTREAM_LIVE_API = 'https://api.live.odysee.com/v1/odysee/live';
|
||||
export const LIVESTREAM_REPLAY_API = 'https://api.live.odysee.com/v1/replays/odysee';
|
||||
export const LIVESTREAM_RTMP_URL = 'rtmp://stream.odysee.com/live';
|
||||
export const LIVESTREAM_KILL = 'https://api.stream.odysee.com/stream/kill';
|
||||
|
|
|
@ -235,7 +235,6 @@
|
|||
.claim-preview__actions {
|
||||
align-self: flex-end;
|
||||
margin-bottom: auto;
|
||||
justify-content: flex-end;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
|
@ -245,10 +244,6 @@
|
|||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
.claim-preview__actions {
|
||||
margin-left: var(--spacing-m);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $breakpoint-xsmall) {
|
||||
|
|
35
ui/scss/component/claim-preview-reset.scss
Normal file
35
ui/scss/component/claim-preview-reset.scss
Normal file
|
@ -0,0 +1,35 @@
|
|||
@import '../init/vars';
|
||||
|
||||
.claimPreviewReset {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: var(--spacing-xs);
|
||||
color: var(--color-text-subtitle);
|
||||
font-size: var(--font-small);
|
||||
flex-direction: column;
|
||||
@media (min-width: $breakpoint-xsmall) {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
.claimPreviewReset__hint {
|
||||
display: inline-block;
|
||||
margin-top: var(--spacing-s);
|
||||
@media (min-width: $breakpoint-xsmall) {
|
||||
margin-right: var(--spacing-xl);
|
||||
}
|
||||
}
|
||||
|
||||
.claimPreviewReset__button {
|
||||
margin-top: var(--spacing-s);
|
||||
width: 100%;
|
||||
.button__content {
|
||||
justify-content: center;
|
||||
}
|
||||
@media (min-width: $breakpoint-xsmall) {
|
||||
margin-top: 0;
|
||||
width: auto;
|
||||
.button__content {
|
||||
justify-content: start;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue