pre-roll ads
This commit is contained in:
parent
ae980bc142
commit
e08b71774c
11 changed files with 328 additions and 99 deletions
|
@ -31,6 +31,7 @@ ENABLE_COMMENT_REACTIONS=true
|
||||||
ENABLE_FILE_REACTIONS=false
|
ENABLE_FILE_REACTIONS=false
|
||||||
ENABLE_CREATOR_REACTIONS=false
|
ENABLE_CREATOR_REACTIONS=false
|
||||||
ENABLE_NO_SOURCE_CLAIMS=false
|
ENABLE_NO_SOURCE_CLAIMS=false
|
||||||
|
ENABLE_PREROLL_ADS=false
|
||||||
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS=4
|
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS=4
|
||||||
CHANNEL_STAKED_LEVEL_LIVESTREAM=5
|
CHANNEL_STAKED_LEVEL_LIVESTREAM=5
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ const config = {
|
||||||
ENABLE_FILE_REACTIONS: process.env.ENABLE_FILE_REACTIONS === 'true',
|
ENABLE_FILE_REACTIONS: process.env.ENABLE_FILE_REACTIONS === 'true',
|
||||||
ENABLE_CREATOR_REACTIONS: process.env.ENABLE_CREATOR_REACTIONS === 'true',
|
ENABLE_CREATOR_REACTIONS: process.env.ENABLE_CREATOR_REACTIONS === 'true',
|
||||||
ENABLE_NO_SOURCE_CLAIMS: process.env.ENABLE_NO_SOURCE_CLAIMS === 'true',
|
ENABLE_NO_SOURCE_CLAIMS: process.env.ENABLE_NO_SOURCE_CLAIMS === 'true',
|
||||||
|
ENABLE_PREROLL_ADS: process.env.ENABLE_PREROLL_ADS === 'true',
|
||||||
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS: process.env.CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS,
|
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS: process.env.CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS,
|
||||||
CHANNEL_STAKED_LEVEL_LIVESTREAM: process.env.CHANNEL_STAKED_LEVEL_LIVESTREAM,
|
CHANNEL_STAKED_LEVEL_LIVESTREAM: process.env.CHANNEL_STAKED_LEVEL_LIVESTREAM,
|
||||||
SIMPLE_SITE: process.env.SIMPLE_SITE === 'true',
|
SIMPLE_SITE: process.env.SIMPLE_SITE === 'true',
|
||||||
|
|
|
@ -203,6 +203,7 @@
|
||||||
"tree-kill": "^1.1.0",
|
"tree-kill": "^1.1.0",
|
||||||
"unist-util-visit": "^2.0.3",
|
"unist-util-visit": "^2.0.3",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
|
"vast-client": "^3.1.1",
|
||||||
"video.js": "^7.10.1",
|
"video.js": "^7.10.1",
|
||||||
"videojs-contrib-quality-levels": "^2.0.9",
|
"videojs-contrib-quality-levels": "^2.0.9",
|
||||||
"videojs-event-tracking": "^1.0.1",
|
"videojs-event-tracking": "^1.0.1",
|
||||||
|
|
|
@ -51,6 +51,9 @@ type Analytics = {
|
||||||
readyState: number,
|
readyState: number,
|
||||||
}
|
}
|
||||||
) => void,
|
) => void,
|
||||||
|
adsFetchedEvent: () => void,
|
||||||
|
adsReceivedEvent: (any) => void,
|
||||||
|
adsErrorEvent: (any) => void,
|
||||||
emailProvidedEvent: () => void,
|
emailProvidedEvent: () => void,
|
||||||
emailVerifiedEvent: () => void,
|
emailVerifiedEvent: () => void,
|
||||||
rewardEligibleEvent: () => void,
|
rewardEligibleEvent: () => void,
|
||||||
|
@ -231,6 +234,15 @@ const analytics: Analytics = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
adsFetchedEvent: () => {
|
||||||
|
sendMatomoEvent('Media', 'AdsFetched');
|
||||||
|
},
|
||||||
|
adsReceivedEvent: (response) => {
|
||||||
|
sendMatomoEvent('Media', 'AdsReceived', JSON.stringify(response));
|
||||||
|
},
|
||||||
|
adsErrorEvent: (response) => {
|
||||||
|
sendMatomoEvent('Media', 'AdsError', JSON.stringify(response));
|
||||||
|
},
|
||||||
playerLoadedEvent: (embedded) => {
|
playerLoadedEvent: (embedded) => {
|
||||||
sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite');
|
sendMatomoEvent('Player', 'Loaded', embedded ? 'embedded' : 'onsite');
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,8 +7,9 @@ import { makeSelectContentPositionForUri } from 'redux/selectors/content';
|
||||||
import VideoViewer from './view';
|
import VideoViewer from './view';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
|
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectHomepageData } from 'redux/selectors/settings';
|
||||||
import { toggleVideoTheaterMode, doSetClientSetting } from 'redux/actions/settings';
|
import { toggleVideoTheaterMode, doSetClientSetting } from 'redux/actions/settings';
|
||||||
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
|
|
||||||
const select = (state, props) => {
|
const select = (state, props) => {
|
||||||
const { search } = props.location;
|
const { search } = props.location;
|
||||||
|
@ -26,19 +27,21 @@ const select = (state, props) => {
|
||||||
hasFileInfo: Boolean(makeSelectFileInfoForUri(props.uri)(state)),
|
hasFileInfo: Boolean(makeSelectFileInfoForUri(props.uri)(state)),
|
||||||
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
|
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
homepageData: selectHomepageData(state),
|
||||||
|
authenticated: selectUserVerifiedEmail(state),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = (dispatch) => ({
|
||||||
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
changeVolume: (volume) => dispatch(doChangeVolume(volume)),
|
||||||
savePosition: (uri, position) => dispatch(savePosition(uri, position)),
|
savePosition: (uri, position) => dispatch(savePosition(uri, position)),
|
||||||
clearPosition: uri => dispatch(clearPosition(uri)),
|
clearPosition: (uri) => dispatch(clearPosition(uri)),
|
||||||
changeMute: muted => dispatch(doChangeMute(muted)),
|
changeMute: (muted) => dispatch(doChangeMute(muted)),
|
||||||
doAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)),
|
doAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)),
|
||||||
doAnalyticsBuffer: (uri, bufferData) => dispatch(doAnalyticsBuffer(uri, bufferData)),
|
doAnalyticsBuffer: (uri, bufferData) => dispatch(doAnalyticsBuffer(uri, bufferData)),
|
||||||
claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()),
|
claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()),
|
||||||
toggleVideoTheaterMode: () => dispatch(toggleVideoTheaterMode()),
|
toggleVideoTheaterMode: () => dispatch(toggleVideoTheaterMode()),
|
||||||
setVideoPlaybackRate: rate => dispatch(doSetClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE, rate)),
|
setVideoPlaybackRate: (rate) => dispatch(doSetClientSetting(SETTINGS.VIDEO_PLAYBACK_RATE, rate)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default withRouter(connect(select, perform)(VideoViewer));
|
export default withRouter(connect(select, perform)(VideoViewer));
|
||||||
|
|
|
@ -49,6 +49,7 @@ type Props = {
|
||||||
startMuted: boolean,
|
startMuted: boolean,
|
||||||
autoplay: boolean,
|
autoplay: boolean,
|
||||||
toggleVideoTheaterMode: () => void,
|
toggleVideoTheaterMode: () => void,
|
||||||
|
adUrl: ?string,
|
||||||
};
|
};
|
||||||
|
|
||||||
type VideoJSOptions = {
|
type VideoJSOptions = {
|
||||||
|
@ -171,7 +172,17 @@ class LbryVolumeBarClass extends videojs.getComponent(VIDEOJS_VOLUME_BAR_CLASS)
|
||||||
properties for this component should be kept to ONLY those that if changed should REQUIRE an entirely new videojs element
|
properties for this component should be kept to ONLY those that if changed should REQUIRE an entirely new videojs element
|
||||||
*/
|
*/
|
||||||
export default React.memo<Props>(function VideoJs(props: Props) {
|
export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
const { autoplay, startMuted, source, sourceType, poster, isAudio, onPlayerReady, toggleVideoTheaterMode } = props;
|
const {
|
||||||
|
autoplay,
|
||||||
|
startMuted,
|
||||||
|
source,
|
||||||
|
sourceType,
|
||||||
|
poster,
|
||||||
|
isAudio,
|
||||||
|
onPlayerReady,
|
||||||
|
toggleVideoTheaterMode,
|
||||||
|
adUrl,
|
||||||
|
} = props;
|
||||||
|
|
||||||
const [reload, setReload] = useState('initial');
|
const [reload, setReload] = useState('initial');
|
||||||
|
|
||||||
|
@ -333,9 +344,11 @@ export default React.memo<Props>(function VideoJs(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnded() {
|
const onEnded = React.useCallback(() => {
|
||||||
|
if (!adUrl) {
|
||||||
showTapButton(TAP.NONE);
|
showTapButton(TAP.NONE);
|
||||||
}
|
}
|
||||||
|
}, [adUrl]);
|
||||||
|
|
||||||
function handleKeyDown(e: KeyboardEvent) {
|
function handleKeyDown(e: KeyboardEvent) {
|
||||||
const player = playerRef.current;
|
const player = playerRef.current;
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
import { ENABLE_PREROLL_ADS } from 'config';
|
||||||
|
import * as PAGES from 'constants/pages';
|
||||||
|
import * as ICONS from 'constants/icons';
|
||||||
import React, { useEffect, useState, useContext, useCallback } from 'react';
|
import React, { useEffect, useState, useContext, useCallback } from 'react';
|
||||||
import { stopContextMenu } from 'util/context-menu';
|
import { stopContextMenu } from 'util/context-menu';
|
||||||
import type { Player } from './internal/videojs';
|
import type { Player } from './internal/videojs';
|
||||||
|
@ -13,6 +16,10 @@ import FileViewerEmbeddedEnded from 'web/component/fileViewerEmbeddedEnded';
|
||||||
import FileViewerEmbeddedTitle from 'component/fileViewerEmbeddedTitle';
|
import FileViewerEmbeddedTitle from 'component/fileViewerEmbeddedTitle';
|
||||||
import LoadingScreen from 'component/common/loading-screen';
|
import LoadingScreen from 'component/common/loading-screen';
|
||||||
import { addTheaterModeButton } from './internal/theater-mode';
|
import { addTheaterModeButton } from './internal/theater-mode';
|
||||||
|
import { useGetAds } from 'effects/use-get-ads';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import I18nMessage from 'component/i18nMessage';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
|
|
||||||
const PLAY_TIMEOUT_ERROR = 'play_timeout_error';
|
const PLAY_TIMEOUT_ERROR = 'play_timeout_error';
|
||||||
const PLAY_TIMEOUT_LIMIT = 2000;
|
const PLAY_TIMEOUT_LIMIT = 2000;
|
||||||
|
@ -39,6 +46,16 @@ type Props = {
|
||||||
clearPosition: (string) => void,
|
clearPosition: (string) => void,
|
||||||
toggleVideoTheaterMode: () => void,
|
toggleVideoTheaterMode: () => void,
|
||||||
setVideoPlaybackRate: (number) => void,
|
setVideoPlaybackRate: (number) => void,
|
||||||
|
authenticated: boolean,
|
||||||
|
homepageData: {
|
||||||
|
PRIMARY_CONTENT_CHANNEL_IDS?: Array<string>,
|
||||||
|
ENLIGHTENMENT_CHANNEL_IDS?: Array<string>,
|
||||||
|
GAMING_CHANNEL_IDS?: Array<string>,
|
||||||
|
SCIENCE_CHANNEL_IDS?: Array<string>,
|
||||||
|
TECHNOLOGY_CHANNEL_IDS?: Array<string>,
|
||||||
|
COMMUNITY_CHANNEL_IDS?: Array<string>,
|
||||||
|
FINCANCE_CHANNEL_IDS?: Array<string>,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -69,22 +86,47 @@ function VideoViewer(props: Props) {
|
||||||
desktopPlayStartTime,
|
desktopPlayStartTime,
|
||||||
toggleVideoTheaterMode,
|
toggleVideoTheaterMode,
|
||||||
setVideoPlaybackRate,
|
setVideoPlaybackRate,
|
||||||
|
homepageData,
|
||||||
|
authenticated,
|
||||||
} = props;
|
} = props;
|
||||||
|
const {
|
||||||
|
PRIMARY_CONTENT_CHANNEL_IDS = [],
|
||||||
|
ENLIGHTENMENT_CHANNEL_IDS = [],
|
||||||
|
GAMING_CHANNEL_IDS = [],
|
||||||
|
SCIENCE_CHANNEL_IDS = [],
|
||||||
|
TECHNOLOGY_CHANNEL_IDS = [],
|
||||||
|
COMMUNITY_CHANNEL_IDS = [],
|
||||||
|
FINCANCE_CHANNEL_IDS = [],
|
||||||
|
} = homepageData;
|
||||||
|
const adApprovedChannelIds = [
|
||||||
|
...PRIMARY_CONTENT_CHANNEL_IDS,
|
||||||
|
...ENLIGHTENMENT_CHANNEL_IDS,
|
||||||
|
...GAMING_CHANNEL_IDS,
|
||||||
|
...SCIENCE_CHANNEL_IDS,
|
||||||
|
...TECHNOLOGY_CHANNEL_IDS,
|
||||||
|
...COMMUNITY_CHANNEL_IDS,
|
||||||
|
...FINCANCE_CHANNEL_IDS,
|
||||||
|
];
|
||||||
const claimId = claim && claim.claim_id;
|
const claimId = claim && claim.claim_id;
|
||||||
|
const channelClaimId = claim && claim.signing_channel && claim.signing_channel.claim_id;
|
||||||
const isAudio = contentType.includes('audio');
|
const isAudio = contentType.includes('audio');
|
||||||
const forcePlayer = FORCE_CONTENT_TYPE_PLAYER.includes(contentType);
|
const forcePlayer = FORCE_CONTENT_TYPE_PLAYER.includes(contentType);
|
||||||
|
const {
|
||||||
|
location: { pathname },
|
||||||
|
} = useHistory();
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false);
|
const [showAutoplayCountdown, setShowAutoplayCountdown] = useState(false);
|
||||||
const [isEndededEmbed, setIsEndededEmbed] = useState(false);
|
const [isEndededEmbed, setIsEndededEmbed] = useState(false);
|
||||||
const vjsCallbackDataRef: any = React.useRef();
|
const vjsCallbackDataRef: any = React.useRef();
|
||||||
|
const previousUri = usePrevious(uri);
|
||||||
|
const embedded = useContext(EmbedContext);
|
||||||
|
const approvedVideo = Boolean(channelClaimId) && adApprovedChannelIds.includes(channelClaimId);
|
||||||
|
const adsEnabled = ENABLE_PREROLL_ADS && !authenticated && !embedded && approvedVideo;
|
||||||
|
const [adUrl, setAdUrl, isFetchingAd] = useGetAds(adsEnabled);
|
||||||
/* isLoading was designed to show loading screen on first play press, rather than completely black screen, but
|
/* isLoading was designed to show loading screen on first play press, rather than completely black screen, but
|
||||||
breaks because some browsers (e.g. Firefox) block autoplay but leave the player.play Promise pending */
|
breaks because some browsers (e.g. Firefox) block autoplay but leave the player.play Promise pending */
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const previousUri = usePrevious(uri);
|
|
||||||
const embedded = useContext(EmbedContext);
|
|
||||||
|
|
||||||
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
// force everything to recent when URI changes, can cause weird corner cases otherwise (e.g. navigate while autoplay is true)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (uri && previousUri && uri !== previousUri) {
|
if (uri && previousUri && uri !== previousUri) {
|
||||||
|
@ -123,13 +165,18 @@ function VideoViewer(props: Props) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEnded() {
|
const onEnded = React.useCallback(() => {
|
||||||
|
if (adUrl) {
|
||||||
|
setAdUrl(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (embedded) {
|
if (embedded) {
|
||||||
setIsEndededEmbed(true);
|
setIsEndededEmbed(true);
|
||||||
} else if (autoplaySetting) {
|
} else if (autoplaySetting) {
|
||||||
setShowAutoplayCountdown(true);
|
setShowAutoplayCountdown(true);
|
||||||
}
|
}
|
||||||
}
|
}, [embedded, setIsEndededEmbed, autoplaySetting, setShowAutoplayCountdown, adUrl, setAdUrl]);
|
||||||
|
|
||||||
function onPlay() {
|
function onPlay() {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
@ -152,8 +199,12 @@ function VideoViewer(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPlayerReady = useCallback(
|
const playerReadyDependencyList = [uri, adUrl, embedded, autoplayIfEmbedded];
|
||||||
(player: Player) => {
|
if (!IS_WEB) {
|
||||||
|
playerReadyDependencyList.push(desktopPlayStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPlayerReady = useCallback((player: Player) => {
|
||||||
if (!embedded) {
|
if (!embedded) {
|
||||||
player.muted(muted);
|
player.muted(muted);
|
||||||
player.volume(volume);
|
player.volume(volume);
|
||||||
|
@ -228,9 +279,7 @@ function VideoViewer(props: Props) {
|
||||||
player.on('dispose', () => {
|
player.on('dispose', () => {
|
||||||
handlePosition(player);
|
handlePosition(player);
|
||||||
});
|
});
|
||||||
},
|
}, playerReadyDependencyList);
|
||||||
IS_WEB ? [uri] : [uri, desktopPlayStartTime]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -245,16 +294,50 @@ function VideoViewer(props: Props) {
|
||||||
{embedded && !isEndededEmbed && <FileViewerEmbeddedTitle uri={uri} />}
|
{embedded && !isEndededEmbed && <FileViewerEmbeddedTitle uri={uri} />}
|
||||||
{/* disable this loading behavior because it breaks when player.play() promise hangs */}
|
{/* disable this loading behavior because it breaks when player.play() promise hangs */}
|
||||||
{isLoading && <LoadingScreen status={__('Loading')} />}
|
{isLoading && <LoadingScreen status={__('Loading')} />}
|
||||||
|
|
||||||
|
{!isFetchingAd && adUrl && (
|
||||||
|
<>
|
||||||
|
<span className="ads__video-notify">
|
||||||
|
{__('Advertisement')}{' '}
|
||||||
|
<Button
|
||||||
|
className="ads__video-close"
|
||||||
|
icon={ICONS.REMOVE}
|
||||||
|
title={__('Close')}
|
||||||
|
onClick={() => setAdUrl(null)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span className="ads__video-nudge">
|
||||||
|
<I18nMessage
|
||||||
|
tokens={{
|
||||||
|
sign_up: (
|
||||||
|
<Button
|
||||||
|
button="secondary"
|
||||||
|
className="ads__video-link"
|
||||||
|
label={__('Sign Up')}
|
||||||
|
navigate={`/$/${PAGES.AUTH}?redirect=${pathname}&src=video-ad`}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
%sign_up% to turn ads off.
|
||||||
|
</I18nMessage>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isFetchingAd && (
|
||||||
<VideoJs
|
<VideoJs
|
||||||
source={source}
|
adUrl={adUrl}
|
||||||
|
source={adUrl || source}
|
||||||
|
sourceType={forcePlayer || adUrl ? 'video/mp4' : contentType}
|
||||||
isAudio={isAudio}
|
isAudio={isAudio}
|
||||||
poster={isAudio || (embedded && !autoplayIfEmbedded) ? thumbnail : ''}
|
poster={isAudio || (embedded && !autoplayIfEmbedded) ? thumbnail : ''}
|
||||||
sourceType={forcePlayer ? 'video/mp4' : contentType}
|
|
||||||
onPlayerReady={onPlayerReady}
|
onPlayerReady={onPlayerReady}
|
||||||
startMuted={autoplayIfEmbedded}
|
startMuted={autoplayIfEmbedded}
|
||||||
toggleVideoTheaterMode={toggleVideoTheaterMode}
|
toggleVideoTheaterMode={toggleVideoTheaterMode}
|
||||||
autoplay={!embedded || autoplayIfEmbedded}
|
autoplay={!embedded || autoplayIfEmbedded}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
53
ui/effects/use-get-ads.js
Normal file
53
ui/effects/use-get-ads.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { VASTClient } from 'vast-client';
|
||||||
|
import analytics from 'analytics';
|
||||||
|
|
||||||
|
const PRE_ROLL_ADS_PROVIDER = '`https://tag.targeting.unrulymedia.com/rmp/216276/0/vast2?vastfw=vpaid&w=300&h=500&url=';
|
||||||
|
|
||||||
|
// Ignores any call made 1 minutes or less after the last successful ad
|
||||||
|
const ADS_CAP_LEVEL = 1 * 60 * 1000;
|
||||||
|
const vastClient = new VASTClient(0, ADS_CAP_LEVEL);
|
||||||
|
|
||||||
|
export function useGetAds(adsEnabled: boolean): [?string, (?string) => void, boolean] {
|
||||||
|
const [isFetching, setIsFetching] = React.useState(true);
|
||||||
|
const [adUrl, setAdUrl] = React.useState();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!adsEnabled) {
|
||||||
|
setIsFetching(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
analytics.adsFetchedEvent();
|
||||||
|
const encodedHref = encodeURI(window.location.href);
|
||||||
|
const url = `${PRE_ROLL_ADS_PROVIDER}${encodedHref}`;
|
||||||
|
// Used for testing on local dev
|
||||||
|
// const url = 'https://raw.githubusercontent.com/dailymotion/vast-client-js/master/test/vastfiles/sample.xml';
|
||||||
|
|
||||||
|
vastClient
|
||||||
|
.get(url)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.ads.length > 0) {
|
||||||
|
// Let this line error if res.ads is empty
|
||||||
|
// I took this from an example response from Dailymotion
|
||||||
|
// It will be caught below and sent to matomo to figure out if there if this needs to be something changed to deal with unrulys data
|
||||||
|
const adUrl = res.ads[0].creatives[0].mediaFiles[0].fileURL;
|
||||||
|
|
||||||
|
// Dummy video file
|
||||||
|
// const adUrl = 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4';
|
||||||
|
|
||||||
|
if (adUrl) {
|
||||||
|
setAdUrl(adUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsFetching(false);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
setIsFetching(false);
|
||||||
|
});
|
||||||
|
}, [adsEnabled]);
|
||||||
|
|
||||||
|
return [adUrl, setAdUrl, isFetching];
|
||||||
|
}
|
|
@ -83,3 +83,52 @@
|
||||||
.ads__claim-text--small {
|
.ads__claim-text--small {
|
||||||
font-size: var(--font-small);
|
font-size: var(--font-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-roll ads
|
||||||
|
.ads__video-nudge,
|
||||||
|
.ads__video-notify {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ads__video-nudge {
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
color: var(--color-white);
|
||||||
|
font-weight: bold;
|
||||||
|
padding: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ads__video-notify {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
background-color: black;
|
||||||
|
border-bottom-left-radius: var(--border-radius);
|
||||||
|
color: var(--color-white);
|
||||||
|
font-size: var(--font-small);
|
||||||
|
padding: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ads__video-link.button--secondary {
|
||||||
|
font-size: var(--font-small);
|
||||||
|
padding: var(--spacing-xs);
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ads__video-close {
|
||||||
|
margin-left: var(--spacing-s);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
stroke: var(--color-white);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
stroke: var(--color-black);
|
||||||
|
background-color: var(--color-white);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -137,6 +137,7 @@
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
margin-top: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
|
||||||
|
|
12
yarn.lock
12
yarn.lock
|
@ -11498,6 +11498,13 @@ vary@~1.1.2:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||||
|
|
||||||
|
vast-client@^3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/vast-client/-/vast-client-3.1.1.tgz#75f044ff1554e0f193302dfa1c20c7f7006fd1f8"
|
||||||
|
integrity sha512-ED32RnLthWgAjQiEPsbqqC4LkN8+EhFyevHVh2SsmlPr6auugjswdbv+VgaQ/d7KUH/vpZ675HzVkIqkB2ibiQ==
|
||||||
|
dependencies:
|
||||||
|
xmldom "^0.3.0"
|
||||||
|
|
||||||
vendors@^1.0.0:
|
vendors@^1.0.0:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e"
|
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e"
|
||||||
|
@ -11998,6 +12005,11 @@ xmldom@^0.1.27:
|
||||||
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
|
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
|
||||||
integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
|
integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
|
||||||
|
|
||||||
|
xmldom@^0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a"
|
||||||
|
integrity sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==
|
||||||
|
|
||||||
xpipe@*:
|
xpipe@*:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf"
|
resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf"
|
||||||
|
|
Loading…
Reference in a new issue