Skip to content

Conversation

@RikRootsGuardian
Copy link

What does this change?

TODO: write up!

Why?

Screenshots

Before After
before after

@RikRootsGuardian RikRootsGuardian marked this pull request as draft November 14, 2025 14:55
@github-actions
Copy link

Hello 👋! When you're ready to run Chromatic, please apply the run_chromatic label to this PR.

You will need to reapply the label each time you want to run Chromatic.

Click here to see the Chromatic project.

@github-actions
Copy link

github-actions bot commented Nov 14, 2025

@RikRootsGuardian
Copy link
Author

RikRootsGuardian commented Nov 17, 2025

(AI suggestions sent me down the wrong path)

@RikRootsGuardian
Copy link
Author

This appears to be a CORS issue rather than a code issue. When developing locally we test on http://localhost:3030, which leads to CORS issues for loading the video from eg https://uploads.guim.co.uk/2025/10/07/Metaxas_tests_audience_with_first_jokes_of_Talk_show___video--a182c225-e45f-4d6a-82ed-05fa50e17fbb-2.0.mp4#t=0.001 ...:
Screenshot 2025-11-17 at 14 45 10

The white space appears when the poster image fails to upload for some reason? This is an intermittent issue (because: see screen shot above which loaded the poster image fine 2 mins after failing and 2 mins before failing again):

Screenshot 2025-11-17 at 14 49 59

I noticed this comment in the last PR to touch the LoopVideoPlayer file:
Screenshot 2025-11-17 at 15 00 54

@RikRootsGuardian
Copy link
Author

Can confirm that the video src/url strings are correct (pasting the url into the browser address bar loads the video)

@RikRootsGuardian
Copy link
Author

Viewing the article locally, using http://r.thegulocal.com:3030/Article/https://www.theguardian.com/media/2025/oct/08/conservative-late-night-talkshow URL. doesn't solve the issue:
Screenshot 2025-11-17 at 15 12 28

@RikRootsGuardian
Copy link
Author

tl;dr: Looping videos now need to be served with a crossOrigin="anonymous" header (so subtitle files can be picked up) - failure will lead to the CORS issue. This is a fixed issue, except for this PR branch where we're testing on articles with self-hosted videos which are not looping videos, thus have not had the header added and old copies have not been purged from Fastly.

Chat messages: AnnaB, MarjanK

Anna:
Hey WebEx - we're running into a CORS issue and I wondered if you might have some advice.

We introduced (and rolled back) a crossOrigin="anonymous" header to looping videos so that we can retrieve the subtitle vtt file from uploads.guim. This change meant that both the video and the subtitle files are now requested as cross-origin CORS fetches, requiring fastly to include Access-Control-Allow-Origin headers.

Unsuprisingly, uploads.guim already has https://www.theguardian.com as an allowed origin but some users were getting the following error

Access to video at 'https://uploads.guim.co.uk/2025/11/05/Front_loop__Mamdani_victory_speech--4477e94a-a4a8-4a85-96c2-4ee8cff259f9-2.0.mp4#t=0.001' from origin 'https://www.theguardian.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Does anyone have any ideas as to why some users would be hitting this error?

I'm wondering if some users could be getting a cached mp4 response that does not have the headers attached because we did not ask for them before this change was rolled out and if we therefore need to purge the cache for some videos. Does that sound plausible? Are we able to purge certain items in the uploads.guim cache?

The fastly VCL is available here if anyone is interested in looking through it https://manage.fastly.com/configure/services/2TmfkSoyUoNo8aFNe6Htjs/versions/29/vcl/snippets/CORS%20headers

Marjan
Hi Anna, that is what I was thinking too, that we purge the cache for those videos. I think for uploads.guim.co.uk we'd need to do the purge manually in fastly by putting the whole url in Purge URL (for content & images we have this feature in the admin tool but I can't see anything there for uploads.guim)

[...etc]

@github-actions
Copy link

github-actions bot commented Nov 19, 2025

"elementId",
"id"
]
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to introduce LoopVideoInArticleElement? I think we should just be able to extend the existing MediaAtomBlockElement with the new attribute. Same story in renderElement.tsx, block-schema.json, content.ts.

Let me know if I've missed something!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I honestly don't know. I don't understand the purpose of these json files, though I expect they may be something to do with testing?

I defined it in renderElement.tsx because loop videos have a radically different structure to legacy self-hosted videos and components that display on a page all seem to get defined here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed (alongside the same change in a different json file)

interface VideoAssets {
url: string;
export interface VideoAssets {
url?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is url optional?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My fault - will attempt removal

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 454 to 455
subtitleSize?: string;
subtitleSource?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These attributes (subtitleSize and subtitleSource) don't exist on the element so will always be missing.

subtitleSize can probably be hardcoded in renderElement.tsx?

subtitleSource will require some thought. Might need to add it to the frontend PR..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this comment about computing subtitleSource.

I believe subtitleSize and subtitleSource can safely be removed here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines 41 to 49
const convertSubtitleSize = (val?: string): SubtitleSize => {
if (val == null) {
return 'small' as SubtitleSize;
}
if (['small', 'medium', 'large'].includes(val)) {
return val as SubtitleSize;
}
return 'small' as SubtitleSize;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully this isn't needed, we can just hardcode to an appropriate size (medium?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really hope so too. But newer loop videos with subtitles includes this data. I introduced this horrid little function to kill off a bunch of TypeScript errors. There must be a better solution!

const [hasBeenPlayed, setHasBeenPlayed] = useState(false);
const [hasTrackedPlay, setHasTrackedPlay] = useState(false);

const [devicePixelRatio, setDevicePixelRatio] = useState(1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introducing state feels a bit unnecessary here, what was your thinking?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bug in the new LoopVideo player. The bug tries to access the browser window environment for the DPR value before the element has been mounted. So I decided to move the value to state and do the window check in a useUpdate hook. Thus once we get the correct DPR the component will rectify itself.

<LoopVideoInArticle
assets={element.assets}
atomId={element.id}
uniqueId={`${Math.random()}`}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could we use element.id or element.elementId here instead of using a random number?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Will update

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +523 to +524
height={400}
width={500}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these numbers come from somewhere?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic numbers: currently represents the 5:4 aspect ratio used by loop videos. Could double to 1000/800 maybe for people using very wide displays? I think CSS overrides this in any case - need to check.

caption={element.title}
isMainMedia={isMainMedia}
subtitleSize={element.subtitleSize}
subtitleSource={element.subtitleSource}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to grab the relevant asset with something like this:

const getSubtitleAsset = (assets: VideoAssets[]) => {
	// Get the first subtitle asset from assets with a mimetype of 'text/vtt'
	return assets.find((asset) => asset.mimeType === 'text/vtt');
}

Then:

Suggested change
subtitleSource={element.subtitleSource}
subtitleSource={getSubtitleAsset(assets)?.url}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants