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:
Dan Peterson 2021-11-03 16:52:18 -05:00 committed by GitHub
parent db12a4b991
commit 11d3f88654
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 149 additions and 5 deletions

View file

@ -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>

View 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);

View 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;

View file

@ -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';

View file

@ -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) {

View 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;
}
}
}