Incremental livestream performance fixes (#307)

This commit is contained in:
infinite-persistence 2021-11-17 18:16:52 +08:00
commit 6382238834
No known key found for this signature in database
GPG key ID: B9C3252EDC3D0AA0
12 changed files with 52 additions and 47 deletions

View file

@ -1,6 +1,7 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { MAX_LIVESTREAM_COMMENTS } from 'constants/livestream';
import { selectShowMatureContent } from 'redux/selectors/settings'; import { selectShowMatureContent } from 'redux/selectors/settings';
import { selectSubscriptions } from 'redux/selectors/subscriptions'; import { selectSubscriptionUris } from 'redux/selectors/subscriptions';
import { withRouter } from 'react-router'; import { withRouter } from 'react-router';
import { makeSelectClaimForUri } from 'redux/selectors/claims'; import { makeSelectClaimForUri } from 'redux/selectors/claims';
import { doResolveUris } from 'redux/actions/claims'; import { doResolveUris } from 'redux/actions/claims';
@ -8,8 +9,12 @@ import { selectTopLevelCommentsForUri } from 'redux/selectors/comments';
import ChannelMentionSuggestions from './view'; import ChannelMentionSuggestions from './view';
const select = (state, props) => { const select = (state, props) => {
const subscriptionUris = selectSubscriptions(state).map(({ uri }) => uri); const subscriptionUris = selectSubscriptionUris(state);
const topLevelComments = selectTopLevelCommentsForUri(state, props.uri); const topLevelComments = selectTopLevelCommentsForUri(
state,
props.uri,
props.isLivestream ? MAX_LIVESTREAM_COMMENTS : -1
);
const commentorUris = []; const commentorUris = [];
// Avoid repeated commentors // Avoid repeated commentors

View file

@ -8,12 +8,11 @@ import ClaimPreviewReset from 'component/claimPreviewReset';
type Props = { type Props = {
uri: string, uri: string,
livestream?: boolean, livestream?: boolean,
activeViewers?: number,
isLive?: boolean, isLive?: boolean,
}; };
function FileSubtitle(props: Props) { function FileSubtitle(props: Props) {
const { uri, livestream = false, activeViewers, isLive = false } = props; const { uri, livestream = false, isLive = false } = props;
return ( return (
<> <>
@ -21,7 +20,7 @@ function FileSubtitle(props: Props) {
<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={livestream} activeViewers={activeViewers} isLive={isLive} /> <FileViewCount uri={uri} livestream={livestream} isLive={isLive} />
</div> </div>
<FileActions uri={uri} hideRepost={livestream} livestream={livestream} /> <FileActions uri={uri} hideRepost={livestream} livestream={livestream} />

View file

@ -1,19 +1,16 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { doFetchSubCount, selectSubCountForUri } from 'lbryinc'; import { doFetchSubCount, selectSubCountForUri } from 'lbryinc';
import { selectTitleForUri, makeSelectClaimForUri } from 'redux/selectors/claims'; import { selectTitleForUri, selectClaimForUri } from 'redux/selectors/claims';
import { makeSelectInsufficientCreditsForUri } from 'redux/selectors/content'; import { makeSelectInsufficientCreditsForUri } from 'redux/selectors/content';
import { makeSelectViewersForId } from 'redux/selectors/livestream';
import FileTitleSection from './view'; import FileTitleSection from './view';
const select = (state, props) => { const select = (state, props) => {
const claim = makeSelectClaimForUri(props.uri)(state); const claim = selectClaimForUri(state, props.uri);
const viewers = claim && makeSelectViewersForId(claim.claim_id)(state);
const channelClaimId = claim && claim.signing_channel ? claim.signing_channel.claim_id : undefined; const channelClaimId = claim && claim.signing_channel ? claim.signing_channel.claim_id : undefined;
const channelUri = claim && claim.signing_channel ? claim.signing_channel.canonical_url : undefined; const channelUri = claim && claim.signing_channel ? claim.signing_channel.canonical_url : undefined;
const subCount = channelUri && selectSubCountForUri(state, channelUri); const subCount = channelUri && selectSubCountForUri(state, channelUri);
return { return {
viewers,
isInsufficientCredits: makeSelectInsufficientCreditsForUri(props.uri)(state), isInsufficientCredits: makeSelectInsufficientCreditsForUri(props.uri)(state),
title: selectTitleForUri(state, props.uri), title: selectTitleForUri(state, props.uri),
channelClaimId, channelClaimId,

View file

@ -21,7 +21,6 @@ type Props = {
isNsfwBlocked: boolean, isNsfwBlocked: boolean,
livestream?: boolean, livestream?: boolean,
isLive?: boolean, isLive?: boolean,
viewers?: number,
subCount: number, subCount: number,
channelClaimId?: string, channelClaimId?: string,
fetchSubCount: (string) => void, fetchSubCount: (string) => void,
@ -35,7 +34,6 @@ function FileTitleSection(props: Props) {
isNsfwBlocked, isNsfwBlocked,
livestream = false, livestream = false,
isLive = false, isLive = false,
viewers,
subCount, subCount,
channelClaimId, channelClaimId,
fetchSubCount, fetchSubCount,
@ -66,7 +64,7 @@ function FileTitleSection(props: Props) {
body={ body={
<React.Fragment> <React.Fragment>
<ClaimInsufficientCredits uri={uri} /> <ClaimInsufficientCredits uri={uri} />
<FileSubtitle uri={uri} isLive={isLive} livestream={livestream} activeViewers={viewers} /> <FileSubtitle uri={uri} isLive={isLive} livestream={livestream} />
</React.Fragment> </React.Fragment>
} }
actions={ actions={

View file

@ -1,13 +1,18 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { makeSelectClaimForUri } from 'redux/selectors/claims'; import { selectClaimIdForUri } from 'redux/selectors/claims';
import { selectViewersForId } from 'redux/selectors/livestream';
import { doFetchViewCount, selectViewCountForUri } from 'lbryinc'; import { doFetchViewCount, selectViewCountForUri } from 'lbryinc';
import { doAnalyticsView } from 'redux/actions/app'; import { doAnalyticsView } from 'redux/actions/app';
import FileViewCount from './view'; import FileViewCount from './view';
const select = (state, props) => ({ const select = (state, props) => {
claim: makeSelectClaimForUri(props.uri)(state), const claimId = selectClaimIdForUri(state, props.uri);
return {
claimId,
viewCount: selectViewCountForUri(state, props.uri), viewCount: selectViewCountForUri(state, props.uri),
}); activeViewers: props.livestream && props.isLive && claimId ? selectViewersForId(state, claimId) : undefined,
};
};
const perform = (dispatch) => ({ const perform = (dispatch) => ({
fetchViewCount: (claimId) => dispatch(doFetchViewCount(claimId)), fetchViewCount: (claimId) => dispatch(doFetchViewCount(claimId)),

View file

@ -4,20 +4,19 @@ import React from 'react';
import HelpLink from 'component/common/help-link'; import HelpLink from 'component/common/help-link';
type Props = { type Props = {
claim: ?StreamClaim, livestream?: boolean,
isLive?: boolean,
// --- redux ---
claimId: ?string,
fetchViewCount: (string) => void, fetchViewCount: (string) => void,
fetchingViewCount: boolean,
uri: string, uri: string,
viewCount: string, viewCount: string,
livestream?: boolean,
activeViewers?: number, activeViewers?: number,
isLive?: boolean,
doAnalyticsView: (string) => void, doAnalyticsView: (string) => void,
}; };
function FileViewCount(props: Props) { function FileViewCount(props: Props) {
const { claim, uri, fetchViewCount, viewCount, livestream, activeViewers, isLive = false, doAnalyticsView } = props; const { claimId, uri, fetchViewCount, viewCount, livestream, activeViewers, isLive = false, doAnalyticsView } = props;
const claimId = claim && claim.claim_id;
React.useEffect(() => { React.useEffect(() => {
if (livestream) { if (livestream) {

View file

@ -1,4 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { MAX_LIVESTREAM_COMMENTS } from 'constants/livestream';
import { doResolveUris } from 'redux/actions/claims'; import { doResolveUris } from 'redux/actions/claims';
import { selectClaimForUri, selectMyClaimIdsRaw } from 'redux/selectors/claims'; import { selectClaimForUri, selectMyClaimIdsRaw } from 'redux/selectors/claims';
import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket'; import { doCommentSocketConnect, doCommentSocketDisconnect } from 'redux/actions/websocket';
@ -10,9 +11,8 @@ import {
selectSuperChatTotalAmountForUri, selectSuperChatTotalAmountForUri,
selectPinnedCommentsForUri, selectPinnedCommentsForUri,
} from 'redux/selectors/comments'; } from 'redux/selectors/comments';
import LivestreamComments from './view';
const MAX_LIVESTREAM_COMMENTS = 75; import LivestreamComments from './view';
const select = (state, props) => ({ const select = (state, props) => ({
claim: selectClaimForUri(state, props.uri), claim: selectClaimForUri(state, props.uri),

View file

@ -16,7 +16,6 @@ import { parseSticker } from 'util/comments';
type Props = { type Props = {
uri: string, uri: string,
claim: ?StreamClaim, claim: ?StreamClaim,
activeViewers: number,
embed?: boolean, embed?: boolean,
doCommentSocketConnect: (string, string) => void, doCommentSocketConnect: (string, string) => void,
doCommentSocketDisconnect: (string) => void, doCommentSocketDisconnect: (string) => void,

View file

@ -3,3 +3,5 @@ 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_REPLAY_API = 'https://api.live.odysee.com/v1/replays/odysee';
export const LIVESTREAM_RTMP_URL = 'rtmp://stream.odysee.com/live'; export const LIVESTREAM_RTMP_URL = 'rtmp://stream.odysee.com/live';
export const LIVESTREAM_KILL = 'https://api.stream.odysee.com/stream/kill'; export const LIVESTREAM_KILL = 'https://api.stream.odysee.com/stream/kill';
export const MAX_LIVESTREAM_COMMENTS = 75;

View file

@ -51,12 +51,11 @@ export const selectCommentsByUri = createSelector(selectState, (state) => {
export const selectPinnedCommentsById = (state: State) => selectState(state).pinnedCommentsById; export const selectPinnedCommentsById = (state: State) => selectState(state).pinnedCommentsById;
export const selectPinnedCommentsForUri = createCachedSelector( export const selectPinnedCommentsForUri = createCachedSelector(
selectCommentsByUri, selectClaimIdForUri,
selectCommentsById, selectCommentsById,
selectPinnedCommentsById, selectPinnedCommentsById,
(state, uri) => uri, (state, uri) => uri,
(byUri, byId, pinnedCommentsById, uri) => { (claimId, byId, pinnedCommentsById, uri) => {
const claimId = byUri[uri];
const pinnedCommentIds = pinnedCommentsById && pinnedCommentsById[claimId]; const pinnedCommentIds = pinnedCommentsById && pinnedCommentsById[claimId];
const pinnedComments = []; const pinnedComments = [];
@ -199,10 +198,9 @@ export const selectFetchingBlockedWords = (state: State) => selectState(state).f
export const selectCommentsForUri = createCachedSelector( export const selectCommentsForUri = createCachedSelector(
(state, uri) => uri, (state, uri) => uri,
selectCommentsByClaimId, selectCommentsByClaimId,
selectCommentsByUri, selectClaimIdForUri,
...Object.values(filterCommentsDepOnList), ...Object.values(filterCommentsDepOnList),
(uri, byClaimId, byUri, ...filterInputs) => { (uri, byClaimId, claimId, ...filterInputs) => {
const claimId = byUri[uri];
const comments = byClaimId && byClaimId[claimId]; const comments = byClaimId && byClaimId[claimId];
return filterComments(comments, claimId, filterInputs); return filterComments(comments, claimId, filterInputs);
} }
@ -212,22 +210,18 @@ export const selectTopLevelCommentsForUri = createCachedSelector(
(state, uri) => uri, (state, uri) => uri,
(state, uri, maxCount) => maxCount, (state, uri, maxCount) => maxCount,
selectTopLevelCommentsByClaimId, selectTopLevelCommentsByClaimId,
selectCommentsByUri, selectClaimIdForUri,
...Object.values(filterCommentsDepOnList), ...Object.values(filterCommentsDepOnList),
(uri, maxCount = -1, byClaimId, byUri, ...filterInputs) => { (uri, maxCount = -1, byClaimId, claimId, ...filterInputs) => {
const claimId = byUri[uri];
const comments = byClaimId && byClaimId[claimId]; const comments = byClaimId && byClaimId[claimId];
const filtered = filterComments(comments, claimId, filterInputs); if (comments) {
return maxCount > 0 ? filtered.slice(0, maxCount) : filtered; return filterComments(maxCount > 0 ? comments.slice(0, maxCount) : comments, claimId, filterInputs);
} else {
return [];
}
} }
)((state, uri, maxCount = -1) => `${String(uri)}:${maxCount}`); )((state, uri, maxCount = -1) => `${String(uri)}:${maxCount}`);
export const makeSelectTopLevelTotalCommentsForUri = (uri: string) =>
createSelector(selectState, selectCommentsByUri, (state, byUri) => {
const claimId = byUri[uri];
return state.topLevelTotalCommentsById[claimId] || 0;
});
export const makeSelectTopLevelTotalPagesForUri = (uri: string) => export const makeSelectTopLevelTotalPagesForUri = (uri: string) =>
createSelector(selectState, selectCommentsByUri, (state, byUri) => { createSelector(selectState, selectCommentsByUri, (state, byUri) => {
const claimId = byUri[uri]; const claimId = byUri[uri];

View file

@ -29,8 +29,10 @@ export const selectViewersById = (state: State) => selectState(state).viewersByI
export const makeSelectIsFetchingLivestreams = (channelId: string) => export const makeSelectIsFetchingLivestreams = (channelId: string) =>
createSelector(selectFetchingLivestreams, (fetchingLivestreams) => Boolean(fetchingLivestreams[channelId])); createSelector(selectFetchingLivestreams, (fetchingLivestreams) => Boolean(fetchingLivestreams[channelId]));
export const makeSelectViewersForId = (channelId: string) => export const selectViewersForId = (state: State, channelId: string) => {
createSelector(selectViewersById, (viewers) => viewers[channelId]); const viewers = selectViewersById(state);
return viewers[channelId];
};
export const makeSelectPendingLivestreamsForChannelId = (channelId: string) => export const makeSelectPendingLivestreamsForChannelId = (channelId: string) =>
createSelector(selectPendingClaims, (pendingClaims) => { createSelector(selectPendingClaims, (pendingClaims) => {

View file

@ -20,6 +20,11 @@ export const selectSubscriptions = createSelector(
(state) => state.subscriptions && state.subscriptions.sort((a, b) => a.channelName.localeCompare(b.channelName)) (state) => state.subscriptions && state.subscriptions.sort((a, b) => a.channelName.localeCompare(b.channelName))
); );
export const selectSubscriptionUris = createSelector(
selectSubscriptions,
(subscriptions) => subscriptions && subscriptions.map((sub) => sub.uri)
);
export const selectFollowing = createSelector(selectState, (state) => state.following && state.following); export const selectFollowing = createSelector(selectState, (state) => state.following && state.following);
// Fetching list of users subscriptions // Fetching list of users subscriptions