Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions dotcom-rendering/fixtures/manual/trails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,10 @@ export const selfHostedLoopVideo54Card = {
height: 400,
},
],
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '5:4',
},
duration: 30,
image: 'https://media.guim.co.uk/6537e163c9164d25ec6102641f6a04fa5ba76560/0_210_5472_3283/master/5472.jpg',
},
Expand All @@ -730,7 +733,10 @@ export const selfHostedLoopVideo45Card = {
height: 720,
},
],
aspectRatio: 4 / 5,
aspectRatio: {
numberRepresentation: 4 / 5,
stringRepresentation: '4:5',
},
},
} satisfies DCRFrontCard;

Expand All @@ -747,7 +753,10 @@ export const selfHostedLoopVideo53Card = {
height: 720,
},
],
aspectRatio: 5 / 3,
aspectRatio: {
numberRepresentation: 5 / 3,
stringRepresentation: '5:3',
},
},
} satisfies DCRFrontCard;

Expand All @@ -764,7 +773,10 @@ export const selfHostedLoopVideo916Card = {
height: 720,
},
],
aspectRatio: 9 / 16,
aspectRatio: {
numberRepresentation: 9 / 16,
stringRepresentation: '9:16',
},
},
} satisfies DCRFrontCard;

Expand All @@ -781,7 +793,10 @@ export const selfHostedLoopVideo169Card = {
height: 720,
},
],
aspectRatio: 16 / 9,
aspectRatio: {
numberRepresentation: 16 / 9,
stringRepresentation: '16:9',
},
},
} satisfies DCRFrontCard;

Expand Down
2 changes: 1 addition & 1 deletion dotcom-rendering/src/components/Card/Card.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const mainSelfHostedVideo: MainMedia = {
height: 1080,
},
],
aspectRatio: 16 / 9,
aspectRatio: { numberRepresentation: 16 / 9, stringRepresentation: '16:9' },
image: `https://i.guim.co.uk/img/media/2eb01d138eb8fba6e59ce7589a60e3ff984f6a7a/0_0_1920_1080/1920.jpg?width=1200&quality=45&dpr=2&s=none`,
duration: 100,
};
Expand Down
25 changes: 15 additions & 10 deletions dotcom-rendering/src/components/SelfHostedVideo.island.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
} from '../lib/video';
import { palette } from '../palette';
import type { RoleType } from '../types/content';
import type { VideoPlayerFormat } from '../types/mainMedia';
import type { AspectRatio, VideoPlayerFormat } from '../types/mainMedia';
import type { RenderingTarget } from '../types/renderingTarget';
import { Caption } from './Caption';
import { CardPicture, type Props as CardPictureProps } from './CardPicture';
Expand Down Expand Up @@ -185,15 +185,18 @@ const dispatchOphanAttentionEvent = (
document.dispatchEvent(event);
};

const getOptimisedPosterImage = (mainImage: string): string => {
const getOptimisedPosterImage = (
mainImage: string,
aspectRatio: string,
): string => {
// This only runs on the client
const resolution = window.devicePixelRatio >= 2 ? 'high' : 'low';

return generateImageURL({
mainImage,
imageWidth: 940, // The widest a video can be: flexible special container, giga-boosted slot
resolution,
aspectRatio: '5:4',
aspectRatio,
});
};

Expand Down Expand Up @@ -284,7 +287,7 @@ type Props = {
atomId: string;
uniqueId: string;
videoStyle: VideoPlayerFormat;
aspectRatio: number;
aspectRatio: AspectRatio;
posterImage: string;
fallbackImage: CardPictureProps['mainImage'];
fallbackImageSize: CardPictureProps['imageSize'];
Expand Down Expand Up @@ -880,13 +883,15 @@ export const SelfHostedVideo = ({

/** The aspect ratio of the video will be clamped within the specified range */
const aspectRatioOfVisibleVideo = getAspectRatioOfVisibleVideo(
aspectRatio,
aspectRatio.numberRepresentation,
minAspectRatio,
maxAspectRatio,
);

const isVideoCroppedAtTopBottom = aspectRatio < aspectRatioOfVisibleVideo;
const isVideoCroppedAtLeftRight = aspectRatio > aspectRatioOfVisibleVideo;
const isVideoCroppedAtTopBottom =
aspectRatio.numberRepresentation < aspectRatioOfVisibleVideo;
const isVideoCroppedAtLeftRight =
aspectRatio.numberRepresentation > aspectRatioOfVisibleVideo;

const isGreyBarsAtSidesOnDesktop =
containerAspectRatioDesktop !== undefined &&
Expand All @@ -899,7 +904,7 @@ export const SelfHostedVideo = ({
const AudioIcon = isMuted ? SvgAudioMute : SvgAudio;

const optimisedPosterImage = showPosterImage
? getOptimisedPosterImage(posterImage)
? getOptimisedPosterImage(posterImage, aspectRatio.stringRepresentation)
: undefined;

return (
Expand All @@ -922,7 +927,7 @@ export const SelfHostedVideo = ({
>
<div
css={figureStyles(
aspectRatio,
aspectRatio.numberRepresentation,
aspectRatioOfVisibleVideo,
isGreyBarsAtSidesOnDesktop,
isGreyBarsAtTopAndBottomOnDesktop,
Expand All @@ -935,7 +940,7 @@ export const SelfHostedVideo = ({
sources={optimisedSources}
atomId={atomId}
uniqueId={uniqueId}
aspectRatio={aspectRatio}
aspectRatio={aspectRatio.numberRepresentation}
width={width}
height={height}
videoStyle={videoStyle}
Expand Down
14 changes: 13 additions & 1 deletion dotcom-rendering/src/frontend/schemas/feArticle.json
Original file line number Diff line number Diff line change
Expand Up @@ -5727,7 +5727,19 @@
}
},
"aspectRatio": {
"type": "number"
"type": "object",
"properties": {
"numberRepresentation": {
"type": "number"
},
"stringRepresentation": {
"type": "string"
}
},
"required": [
"numberRepresentation",
"stringRepresentation"
]
},
"duration": {
"type": "number"
Expand Down
14 changes: 13 additions & 1 deletion dotcom-rendering/src/frontend/schemas/feFront.json
Original file line number Diff line number Diff line change
Expand Up @@ -3979,7 +3979,19 @@
}
},
"aspectRatio": {
"type": "number"
"type": "object",
"properties": {
"numberRepresentation": {
"type": "number"
},
"stringRepresentation": {
"type": "string"
}
},
"required": [
"numberRepresentation",
"stringRepresentation"
]
},
"duration": {
"type": "number"
Expand Down
14 changes: 13 additions & 1 deletion dotcom-rendering/src/frontend/schemas/feTagPage.json
Original file line number Diff line number Diff line change
Expand Up @@ -2279,7 +2279,19 @@
}
},
"aspectRatio": {
"type": "number"
"type": "object",
"properties": {
"numberRepresentation": {
"type": "number"
},
"stringRepresentation": {
"type": "string"
}
},
"required": [
"numberRepresentation",
"stringRepresentation"
]
},
"duration": {
"type": "number"
Expand Down
20 changes: 16 additions & 4 deletions dotcom-rendering/src/lib/video.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ describe('video', () => {
width: 480,
aspectRatio: '5:3',
};
expect(getAspectRatioFromSources([testSource])).toEqual(5 / 3);
expect(getAspectRatioFromSources([testSource])).toEqual({
numberRepresentation: 5 / 3,
stringRepresentation: '5:3',
});
});

it('should calculate the aspect ratio from the width and height if aspect ratio is missing', () => {
Expand All @@ -179,7 +182,10 @@ describe('video', () => {
width: 480,
aspectRatio: undefined,
};
expect(getAspectRatioFromSources([testSource])).toEqual(2 / 3);
expect(getAspectRatioFromSources([testSource])).toEqual({
numberRepresentation: 2 / 3,
stringRepresentation: '480:720',
});
});

it('should return the default aspect ratio if the aspect ratio is undefined and width is 0', () => {
Expand All @@ -189,7 +195,10 @@ describe('video', () => {
width: 0,
aspectRatio: undefined,
};
expect(getAspectRatioFromSources([testSource])).toEqual(5 / 4);
expect(getAspectRatioFromSources([testSource])).toEqual({
numberRepresentation: 5 / 4,
stringRepresentation: '5:4',
});
});

it('should return the default aspect ratio if the aspect ratio is undefined and height is 0', () => {
Expand All @@ -199,7 +208,10 @@ describe('video', () => {
width: 480,
aspectRatio: undefined,
};
expect(getAspectRatioFromSources([testSource])).toEqual(5 / 4);
expect(getAspectRatioFromSources([testSource])).toEqual({
numberRepresentation: 5 / 4,
stringRepresentation: '5:4',
});
});
});

Expand Down
22 changes: 17 additions & 5 deletions dotcom-rendering/src/lib/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import type { VideoAssets } from '../types/content';
export type CustomPlayEventDetail = { uniqueId: string };

/** We expect all videos to include dimensions since the field was added to FEMediaAsset */
export const DEFAULT_ASPECT_RATIO = 5 / 4;
const DEFAULT_ASPECT_RATIO_NUMBER = 5 / 4;
const DEFAULT_ASPECT_RATIO_STRING = '5:4';

export const customSelfHostedVideoPlayAudioEventName =
'self-hosted-video:play-with-audio';
Expand Down Expand Up @@ -77,7 +78,9 @@ export const convertFEMediaAssetsToVideoAssets = (
* We use the first source to calculate aspect ratio, but we could use any of the sources.
* We make an assumption that all sources will have the same aspect ratio.
*/
export const getAspectRatioFromSources = (sources: Source[]): number => {
export const getAspectRatioFromSources = (
sources: Source[],
): { numberRepresentation: number; stringRepresentation: string } => {
const firstSource = sources[0];

if (firstSource?.aspectRatio !== undefined) {
Expand All @@ -88,15 +91,24 @@ export const getAspectRatioFromSources = (sources: Source[]): number => {
width > 0 &&
height > 0
) {
return width / height;
return {
numberRepresentation: width / height,
stringRepresentation: `${width}:${height}`,
};
}
}

if (!firstSource || firstSource.width === 0 || firstSource.height === 0) {
return DEFAULT_ASPECT_RATIO;
return {
numberRepresentation: DEFAULT_ASPECT_RATIO_NUMBER,
stringRepresentation: DEFAULT_ASPECT_RATIO_STRING,
};
}

return firstSource.width / firstSource.height;
return {
numberRepresentation: firstSource.width / firstSource.height,
stringRepresentation: `${firstSource.width}:${firstSource.height}`,
};
};

export const getSubtitleAsset = (assets: VideoAssets[]): string | undefined =>
Expand Down
35 changes: 28 additions & 7 deletions dotcom-rendering/src/model/enhanceCards.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ describe('Enhance Cards', () => {
).toEqual({
atomId: 'atomID',
duration: 15,
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '500:400',
},
image: '',
type: 'SelfHostedVideo',
videoStyle: 'Loop',
Expand Down Expand Up @@ -178,7 +181,10 @@ describe('Enhance Cards', () => {
).toEqual({
atomId: 'atomID',
duration: 15,
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '500:400',
},
image: '',
type: 'SelfHostedVideo',
videoStyle: 'Loop',
Expand Down Expand Up @@ -225,7 +231,10 @@ describe('Enhance Cards', () => {
).toEqual({
atomId: 'atomID',
duration: 15,
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '500:400',
},
image: '',
type: 'SelfHostedVideo',
videoStyle: 'Loop',
Expand Down Expand Up @@ -276,7 +285,10 @@ describe('Enhance Cards', () => {
videoStyle: 'Loop',
atomId: 'atomID',
sources: [],
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '500:400',
},
duration: 151,
};

Expand Down Expand Up @@ -425,7 +437,10 @@ describe('Enhance Cards', () => {
type: 'SelfHostedVideo',
atomId: 'atomID',
duration: 15,
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '500:400',
},
image: 'https://guim-example.co.uk/video-image',
sources: [
{
Expand Down Expand Up @@ -465,7 +480,10 @@ describe('Enhance Cards', () => {
).toEqual({
atomId: 'atomID',
duration: 15,
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '500:400',
},
sources: [
{
mimeType: 'video/mp4',
Expand All @@ -490,7 +508,10 @@ describe('Enhance Cards', () => {
type: 'SelfHostedVideo',
atomId: 'atomID',
duration: 15,
aspectRatio: 5 / 4,
aspectRatio: {
numberRepresentation: 5 / 4,
stringRepresentation: '500:400',
},
image: undefined,
sources: [
{
Expand Down
Loading
Loading