[feat] Add LiveStreaming Support #5691
6 changed files with 74 additions and 99 deletions
|
@ -9,6 +9,7 @@ import Button from 'component/button';
|
||||||
import ClaimListDiscover from 'component/claimListDiscover';
|
import ClaimListDiscover from 'component/claimListDiscover';
|
||||||
import Ads from 'web/component/ads';
|
import Ads from 'web/component/ads';
|
||||||
import Icon from 'component/common/icon';
|
import Icon from 'component/common/icon';
|
||||||
|
import LivestreamLink from 'component/livestreamLink';
|
||||||
import { Form, FormField } from 'component/common/form';
|
import { Form, FormField } from 'component/common/form';
|
||||||
import { DEBOUNCE_WAIT_DURATION_MS } from 'constants/search';
|
import { DEBOUNCE_WAIT_DURATION_MS } from 'constants/search';
|
||||||
import { lighthouse } from 'redux/actions/search';
|
import { lighthouse } from 'redux/actions/search';
|
||||||
|
@ -106,6 +107,8 @@ function ChannelContent(props: Props) {
|
||||||
<HiddenNsfwClaims uri={uri} />
|
<HiddenNsfwClaims uri={uri} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<LivestreamLink uri={uri} />
|
||||||
|
|
||||||
{!fetching && channelIsBlackListed && (
|
{!fetching && channelIsBlackListed && (
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -7,16 +7,21 @@ import FileActions from 'component/fileActions';
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
livestream?: boolean,
|
livestream?: boolean,
|
||||||
|
activeViewers?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
function FileSubtitle(props: Props) {
|
function FileSubtitle(props: Props) {
|
||||||
const { uri, livestream = false } = props;
|
const { uri, livestream = false, activeViewers = 0 } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="media__subtitle--between">
|
<div className="media__subtitle--between">
|
||||||
<div className="file__viewdate">
|
<div className="file__viewdate">
|
||||||
{livestream ? <span>{__('Right now')}</span> : <DateTime uri={uri} show={DateTime.SHOW_DATE} />}
|
{livestream ? <span>{__('Right now')}</span> : <DateTime uri={uri} show={DateTime.SHOW_DATE} />}
|
||||||
<FileViewCount uri={uri} />
|
{livestream ? (
|
||||||
|
<span>{__('%viewer_count% currently watching', { viewer_count: activeViewers })}</span>
|
||||||
|
) : (
|
||||||
|
<FileViewCount uri={uri} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FileActions uri={uri} />
|
<FileActions uri={uri} />
|
||||||
|
|
|
@ -19,10 +19,11 @@ type Props = {
|
||||||
nsfw: boolean,
|
nsfw: boolean,
|
||||||
isNsfwBlocked: boolean,
|
isNsfwBlocked: boolean,
|
||||||
livestream?: boolean,
|
livestream?: boolean,
|
||||||
|
activeViewers?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
function FileTitleSection(props: Props) {
|
function FileTitleSection(props: Props) {
|
||||||
const { title, uri, nsfw, isNsfwBlocked, livestream = false } = props;
|
const { title, uri, nsfw, isNsfwBlocked, livestream = false, activeViewers } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
|
@ -42,7 +43,7 @@ function FileTitleSection(props: Props) {
|
||||||
body={
|
body={
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ClaimInsufficientCredits uri={uri} />
|
<ClaimInsufficientCredits uri={uri} />
|
||||||
<FileSubtitle uri={uri} livestream={livestream} />
|
<FileSubtitle uri={uri} livestream={livestream} activeViewers={activeViewers} />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { BITWAVE_EMBED_URL } from 'constants/livestream';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import FileTitleSection from 'component/fileTitleSection';
|
import FileTitleSection from 'component/fileTitleSection';
|
||||||
import LivestreamComments from 'component/livestreamComments';
|
import LivestreamComments from 'component/livestreamComments';
|
||||||
import FileThumbnail from 'component/fileThumbnail';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -15,26 +14,23 @@ type Props = {
|
||||||
export default function LivestreamLayout(props: Props) {
|
export default function LivestreamLayout(props: Props) {
|
||||||
const { claim, uri, isLive, activeViewers } = props;
|
const { claim, uri, isLive, activeViewers } = props;
|
||||||
|
|
||||||
if (!claim) {
|
if (!claim || !claim.signing_channel) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelName = claim.signing_channel && claim.signing_channel.name;
|
const channelName = claim.signing_channel.name;
|
||||||
|
const channelClaimId = claim.signing_channel.claim_id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="section card-stack">
|
<div className="section card-stack">
|
||||||
<div className="file-render file-render--video livestream">
|
<div className="file-render file-render--video livestream">
|
||||||
<div className="file-viewer">
|
<div className="file-viewer">
|
||||||
{isLive ? (
|
<iframe
|
||||||
<iframe
|
src={`${BITWAVE_EMBED_URL}/${channelClaimId}?skin=odysee&autoplay=1`}
|
||||||
src={`${BITWAVE_EMBED_URL}/${'doomtube'}?skin=odysee&autoplay=1`}
|
scrolling="no"
|
||||||
scrolling="no"
|
allowFullScreen
|
||||||
allowFullScreen
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<FileThumbnail uri={uri} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -45,7 +41,7 @@ export default function LivestreamLayout(props: Props) {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<FileTitleSection uri={uri} livestream activeViewers={activeViewers} />
|
<FileTitleSection uri={uri} livestream isLive={isLive} activeViewers={activeViewers} />
|
||||||
</div>
|
</div>
|
||||||
<LivestreamComments uri={uri} />
|
<LivestreamComments uri={uri} />
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import { BITWAVE_API } from 'constants/livestream';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Card from 'component/common/card';
|
import Card from 'component/common/card';
|
||||||
import ClaimPreview from 'component/claimPreview';
|
import ClaimPreview from 'component/claimPreview';
|
||||||
|
@ -30,27 +31,27 @@ export default function LivestreamLink(props: Props) {
|
||||||
}, [livestreamChannelId]);
|
}, [livestreamChannelId]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// function fetchIsStreaming() {
|
function fetchIsStreaming() {
|
||||||
// // fetch(``)
|
fetch(`${BITWAVE_API}/MarkPugner`)
|
||||||
// // .then((res) => res.json())
|
.then((res) => res.json())
|
||||||
// // .then((res) => {
|
.then((res) => {
|
||||||
// // if (res && res.data && res.data.live) {
|
if (res && res.data && res.data.live) {
|
||||||
// // setIsLivestreaming(true);
|
setIsLivestreaming(true);
|
||||||
// // } else {
|
} else {
|
||||||
// // setIsLivestreaming(false);
|
setIsLivestreaming(false);
|
||||||
// // }
|
}
|
||||||
// // })
|
})
|
||||||
// // .catch((e) => {});
|
.catch((e) => {});
|
||||||
// }
|
}
|
||||||
// let interval;
|
let interval;
|
||||||
// if (livestreamChannelId) {
|
if (livestreamChannelId) {
|
||||||
// interval = setInterval(fetchIsStreaming, 5000);
|
interval = setInterval(fetchIsStreaming, 5000);
|
||||||
// }
|
}
|
||||||
// return () => {
|
return () => {
|
||||||
// if (interval) {
|
if (interval) {
|
||||||
// clearInterval(interval);
|
clearInterval(interval);
|
||||||
// }
|
}
|
||||||
// };
|
};
|
||||||
}, [livestreamChannelId]);
|
}, [livestreamChannelId]);
|
||||||
|
|
||||||
if (!livestreamClaim || !isLivestreaming) {
|
if (!livestreamClaim || !isLivestreaming) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
// import { BITWAVE_API } from 'constants/livestream';
|
import { BITWAVE_API } from 'constants/livestream';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
import LivestreamLayout from 'component/livestreamLayout';
|
import LivestreamLayout from 'component/livestreamLayout';
|
||||||
|
@ -16,67 +16,36 @@ type Props = {
|
||||||
export default function LivestreamPage(props: Props) {
|
export default function LivestreamPage(props: Props) {
|
||||||
const { uri, claim, doSetPlayingUri, isAuthenticated, doUserSetReferrer } = props;
|
const { uri, claim, doSetPlayingUri, isAuthenticated, doUserSetReferrer } = props;
|
||||||
const [activeViewers, setActiveViewers] = React.useState(0);
|
const [activeViewers, setActiveViewers] = React.useState(0);
|
||||||
|
const [isLive, setIsLive] = React.useState(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// function checkIsLive() {
|
function checkIsLive() {
|
||||||
// fetch(`${BITWAVE_API}/`)
|
fetch(`${BITWAVE_API}/MarkPugner`)
|
||||||
// .then((res) => res.json())
|
.then((res) => res.json())
|
||||||
// .then((res) => {
|
.then((res) => {
|
||||||
// if (!res || !res.data) {
|
if (!res || !res.data) {
|
||||||
// setIsLive(false);
|
setIsLive(false);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
// setActiveViewers(res.data.viewCount);
|
|
||||||
// if (res.data.live) {
|
setActiveViewers(res.data.viewCount);
|
||||||
// setDisplayCountdown(false);
|
|
||||||
// setIsLive(true);
|
if (res.data.live) {
|
||||||
// setIsFetching(false);
|
setIsLive(true);
|
||||||
// return;
|
}
|
||||||
// }
|
});
|
||||||
// // Not live, but see if we can display the countdown;
|
}
|
||||||
// const scheduledTime = res.data.scheduled;
|
|
||||||
// if (scheduledTime) {
|
let interval;
|
||||||
// const scheduledDate = new Date(scheduledTime);
|
checkIsLive();
|
||||||
// const lastLiveTimestamp = res.data.timestamp;
|
if (uri) {
|
||||||
// let isLivestreamOver = false;
|
interval = setInterval(checkIsLive, 10000);
|
||||||
// if (lastLiveTimestamp) {
|
}
|
||||||
// const timestampDate = new Date(lastLiveTimestamp);
|
return () => {
|
||||||
// isLivestreamOver = timestampDate.getTime() > scheduledDate.getTime();
|
if (interval) {
|
||||||
// }
|
clearInterval(interval);
|
||||||
// if (isLivestreamOver) {
|
}
|
||||||
// setDisplayCountdown(false);
|
};
|
||||||
// setIsLive(false);
|
|
||||||
// } else {
|
|
||||||
// const datePlusTenMinuteBuffer = scheduledDate.setMinutes(10, 0, 0);
|
|
||||||
// const isInFuture = Date.now() < datePlusTenMinuteBuffer;
|
|
||||||
// if (isInFuture) {
|
|
||||||
// setDisplayCountdown(true);
|
|
||||||
// setIsLive(false);
|
|
||||||
// } else {
|
|
||||||
// setDisplayCountdown(false);
|
|
||||||
// setIsLive(false);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// setIsFetching(false);
|
|
||||||
// } else {
|
|
||||||
// // Offline and no countdown happening
|
|
||||||
// setIsLive(false);
|
|
||||||
// setDisplayCountdown(false);
|
|
||||||
// setActiveViewers(0);
|
|
||||||
// setIsFetching(false);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// let interval;
|
|
||||||
// checkIsLive();
|
|
||||||
// if (uri) {
|
|
||||||
// interval = setInterval(checkIsLive, 10000);
|
|
||||||
// }
|
|
||||||
// return () => {
|
|
||||||
// if (interval) {
|
|
||||||
// clearInterval(interval);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
}, [uri]);
|
}, [uri]);
|
||||||
|
|
||||||
const stringifiedClaim = JSON.stringify(claim);
|
const stringifiedClaim = JSON.stringify(claim);
|
||||||
|
@ -108,7 +77,7 @@ export default function LivestreamPage(props: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page className="file-page" filePage livestream>
|
<Page className="file-page" filePage livestream>
|
||||||
<LivestreamLayout uri={uri} activeViewers={activeViewers} />
|
<LivestreamLayout uri={uri} activeViewers={activeViewers} isLive={isLive} />
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue