Skip to content

Commit 7a92e9f

Browse files
authored
Merge pull request #14962 from guardian/doml/enable-hls
Re-enable HLS for self-hosted video
2 parents 9ced453 + baf349b commit 7a92e9f

File tree

13 files changed

+136
-52
lines changed

13 files changed

+136
-52
lines changed

dotcom-rendering/src/components/Card/Card.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ export type Props = {
163163
headlinePosition?: 'inner' | 'outer';
164164
/** Feature flag for the labs redesign work */
165165
showLabsRedesign?: boolean;
166+
enableHls?: boolean;
166167
};
167168

168169
const starWrapper = (cardHasImage: boolean) => css`
@@ -420,6 +421,7 @@ export const Card = ({
420421
headlinePosition = 'inner',
421422
showLabsRedesign = false,
422423
subtitleSize = 'small',
424+
enableHls = false,
423425
}: Props) => {
424426
const hasSublinks = supportingContent && supportingContent.length > 0;
425427
const sublinkPosition = decideSublinkPosition(
@@ -978,6 +980,7 @@ export const Card = ({
978980
media.mainMedia.subtitleSource
979981
}
980982
subtitleSize={subtitleSize}
983+
enableHls={enableHls}
981984
/>
982985
</Island>
983986
)}

dotcom-rendering/src/components/DecideContainer.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type Props = {
4949
containerLevel?: DCRContainerLevel;
5050
/** Feature flag for the labs redesign work */
5151
showLabsRedesign?: boolean;
52+
enableHls?: boolean;
5253
};
5354

5455
export const DecideContainer = ({
@@ -65,6 +66,7 @@ export const DecideContainer = ({
6566
collectionId,
6667
containerLevel,
6768
showLabsRedesign = false,
69+
enableHls = false,
6870
}: Props) => {
6971
switch (containerType) {
7072
case 'dynamic/fast':
@@ -248,6 +250,7 @@ export const DecideContainer = ({
248250
aspectRatio={aspectRatio}
249251
collectionId={collectionId}
250252
showLabsRedesign={!!showLabsRedesign}
253+
enableHls={enableHls}
251254
/>
252255
);
253256
case 'flexible/general':
@@ -262,6 +265,7 @@ export const DecideContainer = ({
262265
containerLevel={containerLevel}
263266
collectionId={collectionId}
264267
showLabsRedesign={!!showLabsRedesign}
268+
enableHls={enableHls}
265269
/>
266270
);
267271
case 'scrollable/small':

dotcom-rendering/src/components/FlexibleGeneral.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Props = {
3535
collectionId: number;
3636
/** Feature flag for the labs redesign work */
3737
showLabsRedesign?: boolean;
38+
enableHls?: boolean;
3839
};
3940

4041
type RowLayout = 'oneCardHalfWidth' | 'oneCardFullWidth' | 'twoCard';
@@ -256,6 +257,7 @@ type SplashCardLayoutProps = {
256257
collectionId: number;
257258
/** Feature flag for the labs redesign work */
258259
showLabsRedesign?: boolean;
260+
enableHls?: boolean;
259261
};
260262

261263
const SplashCardLayout = ({
@@ -269,6 +271,7 @@ const SplashCardLayout = ({
269271
containerLevel,
270272
collectionId,
271273
showLabsRedesign,
274+
enableHls,
272275
}: SplashCardLayoutProps) => {
273276
const card = cards[0];
274277
if (!card) return null;
@@ -354,6 +357,7 @@ const SplashCardLayout = ({
354357
subtitleSize={subtitleSize}
355358
headlinePosition={card.showLivePlayable ? 'outer' : 'inner'}
356359
showLabsRedesign={showLabsRedesign}
360+
enableHls={enableHls}
357361
/>
358362
</LI>
359363
</UL>
@@ -421,6 +425,7 @@ type FullWidthCardLayoutProps = {
421425
collectionId: number;
422426
/** Feature flag for the labs redesign work */
423427
showLabsRedesign?: boolean;
428+
enableHls?: boolean;
424429
};
425430

426431
const FullWidthCardLayout = ({
@@ -435,6 +440,7 @@ const FullWidthCardLayout = ({
435440
containerLevel,
436441
collectionId,
437442
showLabsRedesign,
443+
enableHls,
438444
}: FullWidthCardLayoutProps) => {
439445
const card = cards[0];
440446
if (!card) return null;
@@ -511,6 +517,7 @@ const FullWidthCardLayout = ({
511517
showKickerImage={card.format.design === ArticleDesign.Audio}
512518
showLabsRedesign={showLabsRedesign}
513519
subtitleSize={subtitleSize}
520+
enableHls={enableHls}
514521
/>
515522
</LI>
516523
</UL>
@@ -530,6 +537,7 @@ type HalfWidthCardLayoutProps = {
530537
containerLevel: DCRContainerLevel;
531538
/** Feature flag for the labs redesign work */
532539
showLabsRedesign?: boolean;
540+
enableHls?: boolean;
533541
};
534542

535543
const HalfWidthCardLayout = ({
@@ -544,6 +552,7 @@ const HalfWidthCardLayout = ({
544552
isLastRow,
545553
containerLevel,
546554
showLabsRedesign,
555+
enableHls,
547556
}: HalfWidthCardLayoutProps) => {
548557
if (cards.length === 0) return null;
549558

@@ -599,6 +608,7 @@ const HalfWidthCardLayout = ({
599608
headlineSizes={undefined}
600609
canPlayInline={false}
601610
showLabsRedesign={showLabsRedesign}
611+
enableHls={enableHls}
602612
/>
603613
</LI>
604614
);
@@ -617,6 +627,7 @@ export const FlexibleGeneral = ({
617627
containerLevel = 'Primary',
618628
collectionId,
619629
showLabsRedesign,
630+
enableHls,
620631
}: Props) => {
621632
const splash = [...groupedTrails.splash].slice(0, 1).map((snap) => ({
622633
...snap,
@@ -646,6 +657,7 @@ export const FlexibleGeneral = ({
646657
containerLevel={containerLevel}
647658
collectionId={collectionId}
648659
showLabsRedesign={showLabsRedesign}
660+
enableHls={enableHls}
649661
/>
650662
)}
651663
{groupedCards.map((row, i) => {
@@ -665,6 +677,7 @@ export const FlexibleGeneral = ({
665677
containerLevel={containerLevel}
666678
collectionId={collectionId}
667679
showLabsRedesign={showLabsRedesign}
680+
enableHls={enableHls}
668681
/>
669682
);
670683

@@ -685,6 +698,7 @@ export const FlexibleGeneral = ({
685698
isLastRow={i === groupedCards.length - 1}
686699
containerLevel={containerLevel}
687700
showLabsRedesign={showLabsRedesign}
701+
enableHls={enableHls}
688702
/>
689703
);
690704
}

dotcom-rendering/src/components/FlexibleSpecial.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type Props = {
3232
containerLevel?: DCRContainerLevel;
3333
collectionId: number;
3434
showLabsRedesign?: boolean;
35+
enableHls?: boolean;
3536
};
3637

3738
type BoostProperties = {
@@ -134,6 +135,7 @@ type OneCardLayoutProps = {
134135
containerLevel: DCRContainerLevel;
135136
isSplashCard?: boolean;
136137
showLabsRedesign?: boolean;
138+
enableHls?: boolean;
137139
};
138140

139141
export const OneCardLayout = ({
@@ -148,6 +150,7 @@ export const OneCardLayout = ({
148150
containerLevel,
149151
isSplashCard,
150152
showLabsRedesign,
153+
enableHls,
151154
}: OneCardLayoutProps) => {
152155
const card = cards[0];
153156
if (!card) return null;
@@ -202,6 +205,7 @@ export const OneCardLayout = ({
202205
headlinePosition={isSplashCard ? 'outer' : 'inner'}
203206
showLabsRedesign={showLabsRedesign}
204207
subtitleSize={subtitleSize}
208+
enableHls={enableHls}
205209
/>
206210
</LI>
207211
</UL>
@@ -305,6 +309,7 @@ export const FlexibleSpecial = ({
305309
containerLevel = 'Primary',
306310
collectionId,
307311
showLabsRedesign,
312+
enableHls,
308313
}: Props) => {
309314
const snaps = [...groupedTrails.snap].slice(0, 1).map((snap) => ({
310315
...snap,
@@ -334,6 +339,7 @@ export const FlexibleSpecial = ({
334339
containerLevel={containerLevel}
335340
isSplashCard={false}
336341
showLabsRedesign={showLabsRedesign}
342+
enableHls={enableHls}
337343
/>
338344
)}
339345
{isNonEmptyArray(splash) && (
@@ -349,6 +355,7 @@ export const FlexibleSpecial = ({
349355
containerLevel={containerLevel}
350356
isSplashCard={true}
351357
showLabsRedesign={showLabsRedesign}
358+
enableHls={enableHls}
352359
/>
353360
)}
354361

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ type Props = {
142142
linkTo: string;
143143
subtitleSource?: string;
144144
subtitleSize: SubtitleSize;
145+
enableHls: boolean;
145146
};
146147

147148
export const SelfHostedVideo = ({
@@ -160,6 +161,7 @@ export const SelfHostedVideo = ({
160161
linkTo,
161162
subtitleSource,
162163
subtitleSize,
164+
enableHls,
163165
}: Props) => {
164166
const adapted = useShouldAdapt();
165167
const { renderingTarget } = useConfig();
@@ -709,6 +711,7 @@ export const SelfHostedVideo = ({
709711
subtitleSource={subtitleSource}
710712
subtitleSize={subtitleSize}
711713
activeCue={activeCue}
714+
enableHls={enableHls}
712715
/>
713716
</figure>
714717
</div>

dotcom-rendering/src/components/SelfHostedVideo.stories.tsx

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,26 +38,26 @@ export const Loop5to4: Story = {
3838
'https://media.guim.co.uk/9bdb802e6da5d3fd249b5060f367b3a817965f0c/0_0_1800_1080/master/1800.jpg',
3939
fallbackImage: '',
4040
},
41-
};
42-
43-
// export const WithM3U8File: Story = {
44-
// name: 'With M3U8 file',
45-
// args: {
46-
// ...Default.args,
47-
// sources: [
48-
// {
49-
// src: 'https://uploads.guimcode.co.uk/2025/09/01/Loop__Japan_fireball--ace3fcf6-1378-41db-9d21-f3fc07072ab2-1.10.m3u8',
50-
// mimeType: 'application/x-mpegURL',
51-
// },
52-
// {
53-
// src: 'https://uploads.guim.co.uk/2025%2F06%2F20%2Ftesting+only%2C+please+ignore--3cb22b60-2c3f-48d6-8bce-38c956907cce-3.mp4',
54-
// mimeType: 'video/mp4',
55-
// },
56-
// ],
57-
// },
58-
// };
59-
60-
export const Loop16to9: Story = {
41+
} satisfies Story;
42+
43+
export const WithM3U8File = {
44+
name: 'With M3U8 file',
45+
args: {
46+
...Loop5to4.args,
47+
sources: [
48+
{
49+
src: 'https://uploads.guimcode.co.uk/2025/09/01/Loop__Japan_fireball--ace3fcf6-1378-41db-9d21-f3fc07072ab2-1.10.m3u8',
50+
mimeType: 'application/x-mpegURL',
51+
},
52+
{
53+
src: 'https://uploads.guim.co.uk/2025%2F06%2F20%2Ftesting+only%2C+please+ignore--3cb22b60-2c3f-48d6-8bce-38c956907cce-3.mp4',
54+
mimeType: 'video/mp4',
55+
},
56+
],
57+
},
58+
} satisfies Story;
59+
60+
export const Loop16to9 = {
6161
name: 'Looping video in 16:9 aspect ratio',
6262
args: {
6363
...Loop5to4.args,
@@ -70,14 +70,14 @@ export const Loop16to9: Story = {
7070
height: 1080,
7171
width: 1920,
7272
},
73-
};
73+
} satisfies Story;
7474

75-
export const WithCinemagraph: Story = {
75+
export const WithCinemagraph = {
7676
args: {
7777
...Loop5to4.args,
7878
videoStyle: 'Cinemagraph',
7979
},
80-
};
80+
} satisfies Story;
8181

8282
export const PausePlay: Story = {
8383
...Loop5to4,
@@ -98,7 +98,7 @@ export const PausePlay: Story = {
9898
await userEvent.click(videoEl);
9999
await expect(canvas.queryByTestId('play-icon')).not.toBeInTheDocument();
100100
},
101-
};
101+
} satisfies Story;
102102

103103
export const UnmuteMute: Story = {
104104
...Loop5to4,
@@ -120,7 +120,7 @@ export const UnmuteMute: Story = {
120120
await userEvent.click(canvas.getByTestId('mute-icon'));
121121
await canvas.findByTestId('unmute-icon');
122122
},
123-
};
123+
} satisfies Story;
124124

125125
// Function to emulate pausing between interactions
126126
function sleep(ms: number) {
@@ -150,4 +150,4 @@ export const InteractionObserver: Story = {
150150
await expect(Number(progressBar.ariaValueNow)).toEqual(progress);
151151
await expect(canvas.queryByTestId('play-icon')).not.toBeInTheDocument();
152152
},
153-
};
153+
} satisfies Story;

dotcom-rendering/src/components/SelfHostedVideoPlayer.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import type {
1414
} from 'react';
1515
import { forwardRef } from 'react';
1616
import type { ActiveCue } from '../lib/useSubtitles';
17-
import type { Source } from '../lib/video';
17+
import { filterOutHlsSources, type Source } from '../lib/video';
1818
import { palette } from '../palette';
1919
import type { VideoPlayerFormat } from '../types/mainMedia';
2020
import { narrowPlayIconWidth, PlayIcon } from './Card/components/PlayIcon';
@@ -126,6 +126,7 @@ type Props = {
126126
subtitleSize?: SubtitleSize;
127127
/* used in custom subtitle overlays */
128128
activeCue?: ActiveCue | null;
129+
enableHls: boolean;
129130
};
130131

131132
/**
@@ -167,6 +168,7 @@ export const SelfHostedVideoPlayer = forwardRef(
167168
subtitleSource,
168169
subtitleSize,
169170
activeCue,
171+
enableHls,
170172
}: Props,
171173
ref: React.ForwardedRef<HTMLVideoElement>,
172174
) => {
@@ -185,6 +187,10 @@ export const SelfHostedVideoPlayer = forwardRef(
185187
showPlayIcon ? 'play' : 'pause'
186188
}-${atomId}`;
187189

190+
const filteredVideoSources = enableHls
191+
? sources
192+
: filterOutHlsSources(sources);
193+
188194
return (
189195
<>
190196
{/* eslint-disable-next-line jsx-a11y/media-has-caption -- Not all videos require captions. */}
@@ -226,7 +232,7 @@ export const SelfHostedVideoPlayer = forwardRef(
226232
onKeyDown={handleKeyDown}
227233
onError={onError}
228234
>
229-
{sources.map((source) => (
235+
{filteredVideoSources.map((source) => (
230236
<source
231237
key={source.mimeType}
232238
/* The start time is set to 1ms so that Safari will autoplay the video */

0 commit comments

Comments
 (0)