@@ -107,6 +107,10 @@ function html(value: unknown) {
107107 . replaceAll ( '"' , """ ) ;
108108}
109109
110+ function linkHeaderValue ( value : string ) {
111+ return value . replaceAll ( "\\" , "\\\\" ) . replaceAll ( '"' , '\\"' ) ;
112+ }
113+
110114function jsString ( value : string ) {
111115 return JSON . stringify ( value ) . replaceAll ( "<" , "\\u003c" ) ;
112116}
@@ -223,6 +227,34 @@ function playbackOrigin(bootstrap: WatchPlaybackBootstrapReady | null) {
223227 }
224228}
225229
230+ function preloadablePlaybackSelection (
231+ selection : ReturnType < typeof playbackSelection > ,
232+ ) {
233+ if ( ! selection ?. url || selection . contentType !== "video/mp4" ) return null ;
234+ return selection ;
235+ }
236+
237+ function appendStartupLinkHeaders (
238+ headers : Headers ,
239+ edge : ReturnType < typeof playbackOrigin > ,
240+ selection : ReturnType < typeof playbackSelection > ,
241+ ) {
242+ if ( edge ) {
243+ headers . append (
244+ "link" ,
245+ `<${ linkHeaderValue ( edge . origin ) } >; rel=preconnect; crossorigin` ,
246+ ) ;
247+ }
248+
249+ const preload = preloadablePlaybackSelection ( selection ) ;
250+ if ( ! preload ) return ;
251+ const preloadContentType = preload . contentType ?? "video/mp4" ;
252+ headers . append (
253+ "link" ,
254+ `<${ linkHeaderValue ( preload . url ) } >; rel=preload; as=video; type="${ linkHeaderValue ( preloadContentType ) } "; crossorigin="use-credentials"; fetchpriority=high` ,
255+ ) ;
256+ }
257+
226258function playerMessage (
227259 bootstrap : WatchPlaybackBootstrapResponse | null ,
228260 selection : ReturnType < typeof playbackSelection > ,
@@ -246,6 +278,8 @@ export function renderFastEmbedHtml(options: FastEmbedRenderOptions) {
246278 const contentType = selection ?. contentType
247279 ? ` type="${ html ( selection . contentType ) } "`
248280 : "" ;
281+ const preload = preloadablePlaybackSelection ( selection ) ;
282+ const preloadContentType = preload ?. contentType ?? "video/mp4" ;
249283 const bootstrapMs =
250284 typeof options . bootstrapMs === "number"
251285 ? ` data-rend-bootstrap-ms="${ Math . max ( 0 , Math . round ( options . bootstrapMs ) ) } "`
@@ -261,6 +295,7 @@ export function renderFastEmbedHtml(options: FastEmbedRenderOptions) {
261295<title>Rend player</title>
262296${ edge ? `<link rel="dns-prefetch" href="${ html ( edge . dnsPrefetch ) } ">` : "" }
263297${ edge ? `<link rel="preconnect" href="${ html ( edge . origin ) } " crossorigin>` : "" }
298+ ${ preload ? `<link rel="preload" as="video" href="${ html ( preload . url ) } " type="${ html ( preloadContentType ) } " crossorigin="use-credentials" fetchpriority="high">` : "" }
264299<style>
265300html,body{margin:0;width:100%;height:100%;background:#050505;color:#f7f7f7}
266301body{overflow:hidden}
@@ -328,6 +363,9 @@ export async function GET(
328363 request ,
329364 normalizedAssetId ,
330365 ) ;
366+ const ready = bootstrap ?. status === "ready" ? bootstrap : null ;
367+ const selection = playbackSelection ( bootstrap , startup ) ;
368+ const edge = playbackOrigin ( ready ) ;
331369 const headers = new Headers ( {
332370 "cache-control" : "no-store" ,
333371 "content-type" : "text/html; charset=utf-8" ,
@@ -340,6 +378,7 @@ export async function GET(
340378 headers . append ( "set-cookie" , setCookie ) ;
341379 }
342380 }
381+ appendStartupLinkHeaders ( headers , edge , selection ) ;
343382
344383 return new Response (
345384 renderFastEmbedHtml ( {
0 commit comments