Skip to content

Commit da2b552

Browse files
committed
Don't display audio icon if video doesn't have audio
1 parent f023ef2 commit da2b552

File tree

2 files changed

+70
-25
lines changed

2 files changed

+70
-25
lines changed

dotcom-rendering/src/components/LoopVideo.importable.tsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,38 @@ const getOptimisedPosterImage = (mainImage: string): string => {
7272
});
7373
};
7474

75+
/**
76+
* Runs a series of browser-specific checks to determine if the video has audio.
77+
*/
78+
const doesVideoHaveAudio = (video: HTMLVideoElement): boolean => {
79+
// If there exists a browser that does not support any of these properties, we are
80+
// unable to detect whether the video has audio. Therefore, we assume it has audio,
81+
// so that the unmute/mute icon is displayed.
82+
if (
83+
!('mozHasAudio' in video) &&
84+
!('webkitAudioDecodedByteCount' in video) &&
85+
!('audioTracks' in video)
86+
) {
87+
// Gather data on what browsers do not support these properties.
88+
window.guardian.modules.sentry.reportError(
89+
new Error(
90+
'Could not determine if video has audio. This is likely due to the browser not supporting the necessary properties.',
91+
),
92+
'loop-video',
93+
);
94+
95+
return true;
96+
}
97+
98+
return (
99+
('mozHasAudio' in video && Boolean(video.mozHasAudio)) ||
100+
('webkitAudioDecodedByteCount' in video &&
101+
Boolean(video.webkitAudioDecodedByteCount)) ||
102+
('audioTracks' in video &&
103+
Boolean((video.audioTracks as { length: number }).length))
104+
);
105+
};
106+
75107
type Props = {
76108
src: string;
77109
atomId: string;
@@ -104,6 +136,7 @@ export const LoopVideo = ({
104136
const vidRef = useRef<HTMLVideoElement>(null);
105137
const [isPlayable, setIsPlayable] = useState(false);
106138
const [isMuted, setIsMuted] = useState(true);
139+
const [hasAudio, setHasAudio] = useState(true);
107140
const [showPlayIcon, setShowPlayIcon] = useState(false);
108141
const [preloadPartialData, setPreloadPartialData] = useState(false);
109142
const [showPosterImage, setShowPosterImage] = useState<boolean>(false);
@@ -277,7 +310,7 @@ export const LoopVideo = ({
277310
true,
278311
);
279312
} catch (error) {
280-
console.error('Failed to track video attention:', error);
313+
log('dotcom', 'Failed to track video attention:', error);
281314
}
282315
};
283316

@@ -410,6 +443,12 @@ export const LoopVideo = ({
410443
return FallbackImageComponent;
411444
}
412445

446+
const handleLoadedData = () => {
447+
if (vidRef.current) {
448+
setHasAudio(doesVideoHaveAudio(vidRef.current));
449+
}
450+
};
451+
413452
const handleCanPlay = () => {
414453
if (!isPlayable) {
415454
setIsPlayable(true);
@@ -541,13 +580,14 @@ export const LoopVideo = ({
541580
isPlayable={isPlayable}
542581
playerState={playerState}
543582
isMuted={isMuted}
583+
handleLoadedData={handleLoadedData}
544584
handleCanPlay={handleCanPlay}
545585
handlePlayPauseClick={handlePlayPauseClick}
546586
handleAudioClick={handleAudioClick}
547587
handleKeyDown={handleKeyDown}
548588
handlePause={handlePause}
549589
onError={onError}
550-
AudioIcon={AudioIcon}
590+
AudioIcon={hasAudio ? AudioIcon : null}
551591
preloadPartialData={preloadPartialData}
552592
showPlayIcon={showPlayIcon}
553593
/>

dotcom-rendering/src/components/LoopVideoPlayer.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,14 @@ type Props = {
8585
currentTime: number;
8686
setCurrentTime: Dispatch<SetStateAction<number>>;
8787
isMuted: boolean;
88+
handleLoadedData: (event: SyntheticEvent) => void;
8889
handleCanPlay: (event: SyntheticEvent) => void;
8990
handlePlayPauseClick: (event: SyntheticEvent) => void;
9091
handleAudioClick: (event: SyntheticEvent) => void;
9192
handleKeyDown: (event: React.KeyboardEvent<HTMLVideoElement>) => void;
9293
handlePause: (event: SyntheticEvent) => void;
9394
onError: (event: SyntheticEvent<HTMLVideoElement>) => void;
94-
AudioIcon: (iconProps: IconProps) => JSX.Element;
95+
AudioIcon: ((iconProps: IconProps) => JSX.Element) | null;
9596
posterImage?: string;
9697
preloadPartialData: boolean;
9798
showPlayIcon: boolean;
@@ -116,6 +117,7 @@ export const LoopVideoPlayer = forwardRef(
116117
currentTime,
117118
setCurrentTime,
118119
isMuted,
120+
handleLoadedData,
119121
handleCanPlay,
120122
handlePlayPauseClick,
121123
handleAudioClick,
@@ -150,6 +152,7 @@ export const LoopVideoPlayer = forwardRef(
150152
muted={isMuted}
151153
playsInline={true}
152154
poster={posterImage}
155+
onLoadedData={handleLoadedData}
153156
onCanPlay={handleCanPlay}
154157
onCanPlayThrough={handleCanPlay}
155158
onTimeUpdate={() => {
@@ -193,30 +196,32 @@ export const LoopVideoPlayer = forwardRef(
193196
duration={ref.current.duration}
194197
/>
195198
{/* Audio icon */}
196-
<button
197-
type="button"
198-
onClick={handleAudioClick}
199-
css={audioButtonStyles}
200-
data-link-name={`gu-video-loop-${
201-
isMuted ? 'unmute' : 'mute'
202-
}-${atomId}`}
203-
>
204-
<div
205-
css={audioIconContainerStyles}
206-
data-testId={`${
199+
{AudioIcon && (
200+
<button
201+
type="button"
202+
onClick={handleAudioClick}
203+
css={audioButtonStyles}
204+
data-link-name={`gu-video-loop-${
207205
isMuted ? 'unmute' : 'mute'
208-
}-icon`}
206+
}-${atomId}`}
209207
>
210-
<AudioIcon
211-
size="xsmall"
212-
theme={{
213-
fill: palette(
214-
'--loop-video-audio-icon',
215-
),
216-
}}
217-
/>
218-
</div>
219-
</button>
208+
<div
209+
css={audioIconContainerStyles}
210+
data-testId={`${
211+
isMuted ? 'unmute' : 'mute'
212+
}-icon`}
213+
>
214+
<AudioIcon
215+
size="xsmall"
216+
theme={{
217+
fill: palette(
218+
'--loop-video-audio-icon',
219+
),
220+
}}
221+
/>
222+
</div>
223+
</button>
224+
)}
220225
</>
221226
)}
222227
</>

0 commit comments

Comments
 (0)