packages/app/app/containers/StreamVerificationContainer/index.tsx
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { v4 } from 'uuid';
import logger from 'electron-timber';
import { getTrackArtist, StreamVerification } from '@nuclear/ui';
import { StreamVerificationProps } from '@nuclear/ui/lib/components/StreamVerification';
import { rest } from '@nuclear/core';
import { queue as queueSelector } from '../../selectors/queue';
import { QueueItem } from '../../reducers/queue';
import { head } from 'lodash';
import { pluginsSelectors } from '../../selectors/plugins';
import { settingsSelector } from '../../selectors/settings';
import { setStringOption } from '../../actions/settings';
import { isResponseBody } from '@nuclear/core/src/rest/Nuclear/NuclearService';
const WEAK_VERIFICATION_THRESHOLD = 3;
export const StreamVerificationContainer: React.FC = () => {
const { t } = useTranslation('queue');
const dispatch = useDispatch();
const queue = useSelector(queueSelector);
const settings = useSelector(settingsSelector);
const selectedStreamProvider = useSelector(pluginsSelectors.selected)?.streamProviders;
const currentTrack: QueueItem = queue.queueItems[queue.currentSong];
const [isLoading, setLoading] = useState(false);
const [verificationStatus, setVerificationStatus] = useState<StreamVerificationProps['status']>('unknown');
const StreamMappingsService = new rest.NuclearStreamMappingsService(process.env.NUCLEAR_VERIFICATION_SERVICE_URL);
useEffect(() => {
setVerificationStatus('unknown');
if (currentTrack) {
StreamMappingsService.getTopStream(
getTrackArtist(currentTrack),
currentTrack.name,
selectedStreamProvider,
settings?.userId
).then(res => {
if (isResponseBody(res) && res.body.stream_id === head(currentTrack.streams)?.id) {
if (res.body.score === undefined) {
logger.error(`Failed to verify stream: ${currentTrack.name} by ${getTrackArtist(currentTrack)}`);
setVerificationStatus('unverified');
} else if (res.body.self_verified) {
setVerificationStatus('verified_by_user');
} else if (res.body.score < WEAK_VERIFICATION_THRESHOLD) {
setVerificationStatus('weakly_verified');
} else {
setVerificationStatus('verified');
}
} else {
setVerificationStatus('unverified');
}
})
.catch((e) => {
setVerificationStatus('unverified');
})
.finally(() => {
setLoading(false);
});
}
}, [currentTrack?.streams?.[0], settings?.userId, selectedStreamProvider]);
useEffect(() => {
if (settings.isReady && !settings?.userId) {
dispatch(setStringOption('userId', v4()));
}
}, [settings.isLoading, settings.isReady]);
const onVerify = () => {
if (currentTrack && verificationStatus !== 'verified_by_user') {
setLoading(true);
StreamMappingsService.postStreamMapping({
artist: getTrackArtist(currentTrack),
title: currentTrack.name,
source: selectedStreamProvider,
stream_id: head(currentTrack.streams).id,
author_id: settings?.userId
}).then(() => {
setVerificationStatus('verified_by_user');
}).catch((e) => {
logger.error(`Failed to verify stream: ${currentTrack.name} by ${getTrackArtist(currentTrack)}`);
logger.error(e);
setVerificationStatus('unknown');
}).finally(() => {
setLoading(false);
});
}
};
const onUnverify = () => {
if (currentTrack && verificationStatus === 'verified_by_user') {
setLoading(true);
StreamMappingsService.deleteStreamMapping({
artist: getTrackArtist(currentTrack),
title: currentTrack.name,
source: selectedStreamProvider,
stream_id: head(currentTrack.streams).id,
author_id: settings?.userId
}).then(() => {
setVerificationStatus('unverified');
}).catch(() => {
logger.error(`Failed to unverify stream: ${currentTrack.name} by ${getTrackArtist(currentTrack)}`);
setVerificationStatus('verified_by_user');
}).finally(() => {
setLoading(false);
});
}
};
if (settings.compactQueueBar || !currentTrack) {
return null;
}
return settings?.isReady &&
settings?.useStreamVerification &&
<StreamVerification
status={verificationStatus}
isLoading={isLoading}
isDisabled={!currentTrack?.streams?.[0]?.stream}
onVerify={onVerify}
onUnverify={onUnverify}
tooltipStrings={{
unknown: t('stream-verification.tooltip.unknown'),
unverified: t('stream-verification.tooltip.unverified'),
weakly_verified: t('stream-verification.tooltip.weakly-verified'),
verified: t('stream-verification.tooltip.verified'),
verified_by_user: t('stream-verification.tooltip.verified-by-user')
}}
streamStatusStrings={{
unknown: t('stream-verification.stream-status.unknown'),
unverified: t('stream-verification.stream-status.unverified'),
weakly_verified: t('stream-verification.stream-status.weakly-verified'),
verified: t('stream-verification.stream-status.verified'),
verified_by_user: t('stream-verification.stream-status.verified-by-user')
}}
textVerify={t('stream-verification.verify')}
textUnverify={t('stream-verification.unverify')}
/>;
};