Fix autoplay infinite loop

## Issue
Closes 3661: Autoplay + Related go into loops ( infinite ) sometimes

## GUI
Push the actual "next" item into the top of the list.

## History search
1. Skip if the next item is itself.
2. The URL stored in the history comes in various forms, so a direct comparison won't work.
  - There's also a weird case where the URL differs by just a little (p.09 vs p-09), but with the same claim ID:

lbry://vacuum-tube-computer-p.09-–-building#5212bc8bc63c373e2bf1ebc5b765595ed7b6514d
lbry://vacuum-tube-computer-p-09-–-building#5212bc8bc63c373e2bf1ebc5b765595ed7b6514d

Check the claim_id as well to cover cases like these.
This commit is contained in:
infinite-persistence 2021-03-19 11:16:05 +08:00 committed by Sean Yesmunt
parent 59b7975085
commit ede83f358d
3 changed files with 39 additions and 4 deletions

View file

@ -3,17 +3,19 @@ import { makeSelectClaimForUri, makeSelectClaimIsNsfw } from 'lbry-redux';
import { doSearch } from 'redux/actions/search';
import { makeSelectRecommendedContentForUri, selectIsSearching } from 'redux/selectors/search';
import { selectUserVerifiedEmail } from 'redux/selectors/user';
import { makeSelectNextUnplayedRecommended } from 'redux/selectors/content';
import RecommendedVideos from './view';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
mature: makeSelectClaimIsNsfw(props.uri)(state),
recommendedContent: makeSelectRecommendedContentForUri(props.uri)(state),
nextRecommendedUri: makeSelectNextUnplayedRecommended(props.uri)(state),
isSearching: selectIsSearching(state),
isAuthenticated: selectUserVerifiedEmail(state),
});
const perform = dispatch => ({
const perform = (dispatch) => ({
search: (query, options) => dispatch(doSearch(query, options)),
});

View file

@ -15,6 +15,7 @@ type Props = {
uri: string,
claim: ?StreamClaim,
recommendedContent: Array<string>,
nextRecommendedUri: string,
isSearching: boolean,
search: (string, Options) => void,
mature: boolean,
@ -22,7 +23,7 @@ type Props = {
};
export default function RecommendedContent(props: Props) {
const { uri, claim, search, mature, recommendedContent, isSearching, isAuthenticated } = props;
const { uri, claim, search, mature, recommendedContent, nextRecommendedUri, isSearching, isAuthenticated } = props;
const isMobile = useIsMobile();
const isMedium = useIsMediumScreen();
@ -43,6 +44,22 @@ export default function RecommendedContent(props: Props) {
}
}, [stringifiedClaim, mature, search]);
function reorderList(recommendedContent) {
let newList = recommendedContent;
if (newList) {
const index = newList.indexOf(nextRecommendedUri);
if (index === -1) {
// This would be weird. Shouldn't happen since it is derived from the same list.
} else if (index !== 0) {
// Swap the "next" item to the top of the list
const a = newList[0];
newList[0] = nextRecommendedUri;
newList[index] = a;
}
}
return newList;
}
React.useEffect(() => {
getRecommendedContent();
}, [uri, getRecommendedContent]);
@ -57,7 +74,7 @@ export default function RecommendedContent(props: Props) {
<ClaimList
type="small"
loading={isSearching}
uris={recommendedContent}
uris={reorderList(recommendedContent)}
hideMenu={isMobile}
injectedItem={
SHOW_ADS && IS_WEB ? (

View file

@ -123,7 +123,23 @@ export const makeSelectNextUnplayedRecommended = (uri: string) =>
continue;
}
if (!history.some((item) => item.uri === recommendedForUri[i])) {
const recommendedUriInfo = parseURI(recommendedUri);
const recommendedUriShort = recommendedUriInfo.claimName + '#' + recommendedUriInfo.claimId.substring(0, 1);
if (claimsByUri[uri] && claimsByUri[uri].claim_id === recommendedUriInfo.claimId) {
// Skip myself (same claim ID)
continue;
}
if (
!history.some((h) => {
const directMatch = h.uri === recommendedForUri[i];
const shortUriMatch = h.uri.includes(recommendedUriShort);
const idMatch = claimsByUri[h.uri] && claimsByUri[h.uri].claim_id === recommendedUriInfo.claimId;
return directMatch || shortUriMatch || idMatch;
})
) {
return recommendedForUri[i];
}
}