Skip to content

Commit e4066a5

Browse files
committed
Use subtitles file and use in looping video
1 parent b60a526 commit e4066a5

File tree

11 files changed

+96
-5
lines changed

11 files changed

+96
-5
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { CardPicture } from '../CardPicture';
4242
import { Island } from '../Island';
4343
import { LatestLinks } from '../LatestLinks.importable';
4444
import { LoopVideo } from '../LoopVideo.importable';
45+
import type { SubtitleSize } from '../LoopVideoPlayer';
4546
import { Pill } from '../Pill';
4647
import { SlideshowCarousel } from '../SlideshowCarousel.importable';
4748
import { Snap } from '../Snap';
@@ -157,6 +158,7 @@ export type Props = {
157158
showKickerImage?: boolean;
158159
isInAllBoostsTest?: boolean;
159160
fixImageWidth?: boolean;
161+
subtitleSize?: SubtitleSize;
160162
/** Determines if the headline should be positioned within the content or outside the content */
161163
headlinePosition?: 'inner' | 'outer';
162164
/** Feature flag for the labs redesign work */
@@ -401,6 +403,7 @@ export const Card = ({
401403
isInAllBoostsTest = false,
402404
headlinePosition = 'inner',
403405
showLabsRedesign = false,
406+
subtitleSize = 'small',
404407
}: Props) => {
405408
const hasSublinks = supportingContent && supportingContent.length > 0;
406409
const sublinkPosition = decideSublinkPosition(
@@ -956,6 +959,10 @@ export const Card = ({
956959
fallbackImageAlt={media.imageAltText}
957960
fallbackImageAspectRatio="5:4"
958961
linkTo={linkTo}
962+
subtitleSource={
963+
media.mainMedia.subtitleSource
964+
}
965+
subtitleSize={subtitleSize}
959966
/>
960967
</Island>
961968
)}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ export const SplashBoostLevels: Story = {
324324

325325
return (
326326
<>
327+
<Section title="Default" boostLevel="default" />
327328
<Section title="Boosted" boostLevel="boost" />
328329
<Section title="Mega boosted" boostLevel="megaboost" />
329330
<Section title="Giga boosted" boostLevel="gigaboost" />

dotcom-rendering/src/components/FlexibleGeneral.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import type { ResponsiveFontSize } from './CardHeadline';
2121
import type { Loading } from './CardPicture';
2222
import { FeatureCard } from './FeatureCard';
2323
import { FrontCard } from './FrontCard';
24+
import type { SubtitleSize } from './LoopVideoPlayer';
2425
import type { Alignment } from './SupportingContent';
2526

2627
type Props = {
@@ -153,6 +154,7 @@ type BoostedSplashProperties = {
153154
supportingContentAlignment: Alignment;
154155
liveUpdatesAlignment: Alignment;
155156
trailTextSize: TrailTextSize;
157+
subtitleSize: SubtitleSize;
156158
avatarUrl?: string;
157159
};
158160

@@ -183,6 +185,7 @@ const decideSplashCardProperties = (
183185
supportingContentLength >= 4 ? 'horizontal' : 'vertical',
184186
liveUpdatesAlignment: 'vertical',
185187
trailTextSize: 'regular',
188+
subtitleSize: 'medium',
186189
};
187190
case 'boost':
188191
return {
@@ -198,6 +201,7 @@ const decideSplashCardProperties = (
198201
supportingContentLength >= 4 ? 'horizontal' : 'vertical',
199202
liveUpdatesAlignment: 'vertical',
200203
trailTextSize: 'regular',
204+
subtitleSize: 'medium',
201205
};
202206
case 'megaboost':
203207
return {
@@ -214,6 +218,7 @@ const decideSplashCardProperties = (
214218
supportingContentAlignment: 'horizontal',
215219
liveUpdatesAlignment: 'horizontal',
216220
trailTextSize: 'large',
221+
subtitleSize: 'large',
217222
};
218223
case 'gigaboost':
219224
return {
@@ -230,6 +235,7 @@ const decideSplashCardProperties = (
230235
supportingContentAlignment: 'horizontal',
231236
liveUpdatesAlignment: 'horizontal',
232237
trailTextSize: 'large',
238+
subtitleSize: 'large',
233239
};
234240
}
235241
};
@@ -290,6 +296,7 @@ const SplashCardLayout = ({
290296
supportingContentAlignment,
291297
liveUpdatesAlignment,
292298
trailTextSize,
299+
subtitleSize,
293300
} = decideSplashCardProperties(
294301
card.boostLevel ?? 'default',
295302
card.supportingContent?.length ?? 0,
@@ -339,6 +346,7 @@ const SplashCardLayout = ({
339346
trailTextSize={trailTextSize}
340347
canPlayInline={true}
341348
showKickerImage={card.format.design === ArticleDesign.Audio}
349+
subtitleSize={subtitleSize}
342350
headlinePosition={card.showLivePlayable ? 'outer' : 'inner'}
343351
showLabsRedesign={showLabsRedesign}
344352
/>
@@ -352,6 +360,7 @@ type BoostedCardProperties = {
352360
mediaSize: MediaSizeType;
353361
liveUpdatesPosition: Position;
354362
supportingContentAlignment: Alignment;
363+
subtitleSize: SubtitleSize;
355364
};
356365

357366
/**
@@ -375,6 +384,7 @@ const decideCardProperties = (
375384
liveUpdatesPosition: 'outer',
376385
supportingContentAlignment:
377386
supportingContentLength >= 2 ? 'horizontal' : 'vertical',
387+
subtitleSize: 'medium',
378388
};
379389
case 'boost':
380390
default:
@@ -388,6 +398,7 @@ const decideCardProperties = (
388398
liveUpdatesPosition: 'inner',
389399
supportingContentAlignment:
390400
supportingContentLength >= 2 ? 'horizontal' : 'vertical',
401+
subtitleSize: 'small',
391402
};
392403
}
393404
};
@@ -428,6 +439,7 @@ const FullWidthCardLayout = ({
428439
mediaSize,
429440
supportingContentAlignment,
430441
liveUpdatesPosition,
442+
subtitleSize,
431443
} = decideCardProperties(
432444
card.supportingContent?.length ?? 0,
433445
card.boostLevel,
@@ -492,6 +504,7 @@ const FullWidthCardLayout = ({
492504
canPlayInline={true}
493505
showKickerImage={card.format.design === ArticleDesign.Audio}
494506
showLabsRedesign={showLabsRedesign}
507+
subtitleSize={subtitleSize}
495508
/>
496509
</LI>
497510
</UL>

dotcom-rendering/src/components/FlexibleSpecial.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { UL } from './Card/components/UL';
1919
import type { ResponsiveFontSize } from './CardHeadline';
2020
import type { Loading } from './CardPicture';
2121
import { FrontCard } from './FrontCard';
22+
import type { SubtitleSize } from './LoopVideoPlayer';
2223
import type { Alignment } from './SupportingContent';
2324

2425
type Props = {
@@ -42,6 +43,7 @@ type BoostProperties = {
4243
supportingContentAlignment: Alignment;
4344
liveUpdatesAlignment: Alignment;
4445
trailTextSize: TrailTextSize;
46+
subtitleSize: SubtitleSize;
4547
};
4648

4749
/**
@@ -70,6 +72,7 @@ const determineCardProperties = (
7072
supportingContentLength >= 3 ? 'horizontal' : 'vertical',
7173
liveUpdatesAlignment: 'vertical',
7274
trailTextSize: 'regular',
75+
subtitleSize: 'medium',
7376
};
7477
case 'boost':
7578
return {
@@ -85,6 +88,7 @@ const determineCardProperties = (
8588
supportingContentLength >= 3 ? 'horizontal' : 'vertical',
8689
liveUpdatesAlignment: 'vertical',
8790
trailTextSize: 'regular',
91+
subtitleSize: 'medium',
8892
};
8993
case 'megaboost':
9094
return {
@@ -99,6 +103,7 @@ const determineCardProperties = (
99103
supportingContentAlignment: 'horizontal',
100104
liveUpdatesAlignment: 'horizontal',
101105
trailTextSize: 'large',
106+
subtitleSize: 'large',
102107
};
103108
case 'gigaboost':
104109
return {
@@ -113,6 +118,7 @@ const determineCardProperties = (
113118
supportingContentAlignment: 'horizontal',
114119
liveUpdatesAlignment: 'horizontal',
115120
trailTextSize: 'large',
121+
subtitleSize: 'large',
116122
};
117123
}
118124
};
@@ -155,6 +161,7 @@ export const OneCardLayout = ({
155161
supportingContentAlignment,
156162
liveUpdatesAlignment,
157163
trailTextSize,
164+
subtitleSize,
158165
} = determineCardProperties(
159166
card.boostLevel ?? 'default',
160167
card.supportingContent?.length ?? 0,
@@ -195,6 +202,7 @@ export const OneCardLayout = ({
195202
showKickerImage={card.format.design === ArticleDesign.Audio}
196203
headlinePosition={isSplashCard ? 'outer' : 'inner'}
197204
showLabsRedesign={showLabsRedesign}
205+
subtitleSize={subtitleSize}
198206
/>
199207
</LI>
200208
</UL>

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ import {
1818
} from '../lib/video';
1919
import { CardPicture, type Props as CardPictureProps } from './CardPicture';
2020
import { useConfig } from './ConfigContext';
21+
import type {
22+
PLAYER_STATES,
23+
PlayerStates,
24+
SubtitleSize,
25+
} from './LoopVideoPlayer';
2126
import { LoopVideoPlayer } from './LoopVideoPlayer';
22-
import type { PLAYER_STATES, PlayerStates } from './LoopVideoPlayer';
2327
import { ophanTrackerWeb } from './YoutubeAtom/eventEmitters';
2428

2529
const videoContainerStyles = css`
@@ -117,6 +121,8 @@ type Props = {
117121
fallbackImageAlt: CardPictureProps['alt'];
118122
fallbackImageAspectRatio: CardPictureProps['aspectRatio'];
119123
linkTo: string;
124+
subtitleSource?: string;
125+
subtitleSize: SubtitleSize;
120126
};
121127

122128
export const LoopVideo = ({
@@ -132,6 +138,8 @@ export const LoopVideo = ({
132138
fallbackImageAlt,
133139
fallbackImageAspectRatio,
134140
linkTo,
141+
subtitleSource,
142+
subtitleSize,
135143
}: Props) => {
136144
const adapted = useShouldAdapt();
137145
const { renderingTarget } = useConfig();
@@ -627,6 +635,8 @@ export const LoopVideo = ({
627635
AudioIcon={hasAudio ? AudioIcon : null}
628636
preloadPartialData={preloadPartialData}
629637
showPlayIcon={showPlayIcon}
638+
subtitleSource={subtitleSource}
639+
subtitleSize={subtitleSize}
630640
/>
631641
</figure>
632642
);

dotcom-rendering/src/components/LoopVideoPlayer.tsx

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { css } from '@emotion/react';
2-
import { space } from '@guardian/source/foundations';
2+
import {
3+
space,
4+
textSans15,
5+
textSans17,
6+
textSans20,
7+
} from '@guardian/source/foundations';
38
import type { IconProps } from '@guardian/source/react-components';
49
import type {
510
Dispatch,
@@ -13,7 +18,13 @@ import { palette } from '../palette';
1318
import { narrowPlayIconWidth, PlayIcon } from './Card/components/PlayIcon';
1419
import { LoopVideoProgressBar } from './LoopVideoProgressBar';
1520

16-
const videoStyles = (width: number, height: number) => css`
21+
export type SubtitleSize = 'small' | 'medium' | 'large';
22+
23+
const videoStyles = (
24+
width: number,
25+
height: number,
26+
subtitleSize: SubtitleSize,
27+
) => css`
1728
position: relative;
1829
display: block;
1930
height: auto;
@@ -22,6 +33,15 @@ const videoStyles = (width: number, height: number) => css`
2233
/* Prevents CLS by letting the browser know the space the video will take up. */
2334
aspect-ratio: ${width} / ${height};
2435
object-fit: cover;
36+
37+
::cue {
38+
background-color: ${palette('--loop-video-subtitle-background')};
39+
color: ${palette('--loop-video-subtitle-text')};
40+
41+
${subtitleSize === 'small' && textSans15};
42+
${subtitleSize === 'medium' && textSans17};
43+
${subtitleSize === 'large' && textSans20};
44+
}
2545
`;
2646

2747
const playIconStyles = css`
@@ -97,6 +117,8 @@ type Props = {
97117
posterImage?: string;
98118
preloadPartialData: boolean;
99119
showPlayIcon: boolean;
120+
subtitleSource?: string;
121+
subtitleSize: SubtitleSize;
100122
};
101123

102124
/**
@@ -128,6 +150,8 @@ export const LoopVideoPlayer = forwardRef(
128150
AudioIcon,
129151
preloadPartialData,
130152
showPlayIcon,
153+
subtitleSource,
154+
subtitleSize,
131155
}: Props,
132156
ref: React.ForwardedRef<HTMLVideoElement>,
133157
) => {
@@ -138,7 +162,7 @@ export const LoopVideoPlayer = forwardRef(
138162
{/* eslint-disable-next-line jsx-a11y/media-has-caption -- Captions will be considered later. */}
139163
<video
140164
id={loopVideoId}
141-
css={videoStyles(width, height)}
165+
css={videoStyles(width, height, subtitleSize)}
142166
ref={ref}
143167
tabIndex={0}
144168
data-testid="loop-video"
@@ -179,6 +203,13 @@ export const LoopVideoPlayer = forwardRef(
179203
type={source.mimeType}
180204
/>
181205
))}
206+
{subtitleSource !== undefined && (
207+
<track
208+
default={true}
209+
kind="subtitles"
210+
src={subtitleSource}
211+
/>
212+
)}
182213
{FallbackImageComponent}
183214
</video>
184215
{ref && 'current' in ref && ref.current && isPlayable && (

dotcom-rendering/src/frontend/feFront.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export interface FEMediaAsset {
111111
version: number;
112112
platform: string;
113113
mimeType?: string;
114+
assetType: string;
114115
}
115116

116117
/** @see https://github.com/guardian/frontend/blob/0bf69f55a/common/app/model/content/Atom.scala#L158-L169 */

dotcom-rendering/src/model/enhanceCards.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ describe('Enhance Cards', () => {
1010
version: 1,
1111
platform: 'Url',
1212
mimeType: 'video/mp4',
13+
assetType: 'Video',
1314
},
1415
{
1516
id: 'https://guim-example.co.uk/atomID-1.m3u8',
1617
version: 1,
1718
platform: 'Url',
1819
mimeType: 'application/x-mpegURL',
20+
assetType: 'Video',
1921
},
2022
];
2123
const mediaAtom: FEMediaAtom = {

dotcom-rendering/src/model/enhanceCards.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,11 @@ export const getActiveMediaAtom = (
203203
const assets = mediaAtom.assets.filter(
204204
({ version }) => version === mediaAtom.activeVersion,
205205
);
206-
if (!assets.length) return undefined;
206+
207+
const videoAssets = assets.filter(
208+
({ assetType }) => assetType === 'Video',
209+
);
210+
if (!videoAssets.length) return undefined;
207211

208212
const image = decideMediaAtomImage(
209213
videoReplace,
@@ -231,13 +235,18 @@ export const getActiveMediaAtom = (
231235
);
232236
if (!sources.length) return undefined;
233237

238+
const subtitleAsset = assets.find(
239+
({ assetType }) => assetType === 'Subtitles',
240+
);
241+
234242
return {
235243
type: 'LoopVideo',
236244
atomId: mediaAtom.id,
237245
sources: sources.map((source) => ({
238246
src: source.id,
239247
mimeType: source.mimeType as SupportedVideoFileType,
240248
})),
249+
subtitleSource: subtitleAsset?.id,
241250
duration: mediaAtom.duration ?? 0,
242251
// Size fixed to a 5:4 ratio
243252
width: 500,

0 commit comments

Comments
 (0)