Skip to content

Commit 98ccb62

Browse files
committed
Enable public Tigris playback aliases
1 parent 0bad110 commit 98ccb62

29 files changed

Lines changed: 1081 additions & 58 deletions

.env.docker.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ REND_AUTUMN_UNIT_COST_STORAGE_2K=
5050
REND_AUTUMN_UNIT_COST_STORAGE_4K=
5151
REND_PLAYBACK_MODE=tigris
5252
REND_TIGRIS_PLAYBACK_BASE_URL=http://127.0.0.1:4000
53+
REND_PUBLIC_PLAYBACK_ENABLED=false
54+
REND_PUBLIC_PLAYBACK_ALIAS_PREFIX=v
55+
REND_PUBLIC_PLAYBACK_ALIAS_ACL=public-read
5356
# Edge playback is dormant by default. Set REND_PLAYBACK_MODE=edge to use these.
5457
REND_PLAYBACK_BASE_URL=http://127.0.0.1:4100
5558
REND_PLAYER_PLAYBACK_BASE_URL=

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ REND_AUTUMN_UNIT_COST_STORAGE_2K=
5858
REND_AUTUMN_UNIT_COST_STORAGE_4K=
5959
REND_PLAYBACK_MODE=tigris
6060
REND_TIGRIS_PLAYBACK_BASE_URL=http://127.0.0.1:4000
61+
REND_PUBLIC_PLAYBACK_ENABLED=false
62+
REND_PUBLIC_PLAYBACK_ALIAS_PREFIX=v
63+
REND_PUBLIC_PLAYBACK_ALIAS_ACL=public-read
6164
# Edge playback is dormant by default. Set REND_PLAYBACK_MODE=edge to use this base.
6265
REND_PLAYBACK_BASE_URL=http://127.0.0.1:4100
6366
REND_MAX_UPLOAD_BYTES=536870912

.env.production.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ REND_PUBLIC_API_BASE_URL=https://api.rend.so
2828
REND_PUBLIC_SITE_BASE_URL=https://rend.so
2929
REND_PLAYBACK_MODE=tigris
3030
REND_TIGRIS_PLAYBACK_BASE_URL=https://api.rend.so
31+
# Public Tigris playback is opt-in. Enable only when the playback base is a
32+
# public-read media host and generated aliases are available under /v/{asset}/...
33+
REND_PUBLIC_PLAYBACK_ENABLED=false
34+
REND_PUBLIC_PLAYBACK_ALIAS_PREFIX=v
35+
REND_PUBLIC_PLAYBACK_ALIAS_ACL=public-read
3136
# Edge playback is dormant by default. Set REND_PLAYBACK_MODE=edge to use these.
3237
REND_PLAYER_PLAYBACK_BASE_URL=
3338
REND_PLAYER_EDGE_BASE_URLS=

apps/site/app/api/player/[assetId]/route.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,9 @@ function setPlaybackCookieHeader(
218218
playbackBaseUrl: string | null,
219219
directPlaybackEnabled: boolean,
220220
directCookieDomain: string | undefined,
221+
playbackCredentialMode: "include" | "omit",
221222
) {
223+
if (playbackCredentialMode === "omit") return;
222224
const playbackCookie =
223225
directPlaybackEnabled && playbackBaseUrl
224226
? playbackDirectCookieHeader(
@@ -360,15 +362,18 @@ export async function GET(
360362
}
361363
const playbackBaseUrl = playbackDecision.playbackBaseUrl;
362364
logPlaybackEdgeDecision(request, playbackDecision);
365+
const publicPlaybackEnabled = playbackDecision.credentialMode === "omit";
363366
const directCookieDomain = directPlaybackCookieDomain(
364367
request,
365368
playbackBaseUrl,
366369
);
367-
const directPlaybackEnabled = canUseDirectPlaybackCookie(
370+
const directCookieEnabled = canUseDirectPlaybackCookie(
368371
request,
369372
playbackBaseUrl,
370373
directCookieDomain,
371374
);
375+
const directPlaybackEnabled =
376+
Boolean(playbackBaseUrl) && (publicPlaybackEnabled || directCookieEnabled);
372377
const cacheKey = cacheKeyForPlaybackBootstrap(
373378
normalizedAssetId,
374379
playbackBaseUrl,
@@ -388,6 +393,7 @@ export async function GET(
388393
cached.playbackBaseUrl,
389394
cached.directPlaybackEnabled,
390395
cached.directCookieDomain,
396+
cached.safeResponse.playback_credential_mode ?? "include",
391397
);
392398
return jsonResponse(cached.safeResponse, { headers });
393399
}
@@ -454,6 +460,7 @@ export async function GET(
454460
data,
455461
responsePlaybackBaseUrl,
456462
organizationId,
463+
publicPlaybackEnabled ? "omit" : "include",
457464
)
458465
: null;
459466

@@ -482,6 +489,7 @@ export async function GET(
482489
playbackBaseUrl,
483490
directPlaybackEnabled,
484491
directCookieDomain,
492+
safeResponse.playback_credential_mode ?? "include",
485493
);
486494
if (typeof playbackToken === "string") {
487495
rememberBootstrapResponse(cacheKey, {

apps/site/app/api/player/artifact-route.test.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function cachedSafeResponse(
2323
playback_url:
2424
"/api/player/00000000-0000-0000-0000-000000000001/artifact/opener.mp4",
2525
playback_content_type: "video/mp4",
26+
playback_credential_mode: "include",
2627
playback_token_expires_at: expiresAt,
2728
ttl_seconds: ttlSeconds,
2829
opener_url:

apps/site/app/embed-fast/[assetId]/route.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ function playbackSelection(
141141
};
142142
}
143143

144-
if (mode === "progressive") {
144+
if (mode === "progressive" && bootstrap.playback_credential_mode !== "omit") {
145145
const progressiveUrl = progressivePlaybackUrl(bootstrap);
146146
if (progressiveUrl) {
147147
return {
@@ -187,6 +187,12 @@ function playbackSelection(
187187
return null;
188188
}
189189

190+
function playbackCrossOrigin(bootstrap: WatchPlaybackBootstrapResponse | null) {
191+
return bootstrap?.status === "ready" && bootstrap.playback_credential_mode === "omit"
192+
? "anonymous"
193+
: "use-credentials";
194+
}
195+
190196
function progressivePlaybackUrl(bootstrap: WatchPlaybackBootstrapReady) {
191197
if (bootstrap.playable_state !== "hls_ready" || !bootstrap.manifest_url) {
192198
return null;
@@ -253,6 +259,7 @@ function appendStartupLinkHeaders(
253259
headers: Headers,
254260
edge: ReturnType<typeof playbackOrigin>,
255261
selection: ReturnType<typeof playbackSelection>,
262+
crossOrigin: "anonymous" | "use-credentials",
256263
) {
257264
if (edge) {
258265
headers.append(
@@ -266,7 +273,7 @@ function appendStartupLinkHeaders(
266273
const preloadContentType = preload.contentType ?? "video/mp4";
267274
headers.append(
268275
"link",
269-
`<${linkHeaderValue(preload.url)}>; rel=preload; as=video; type="${linkHeaderValue(preloadContentType)}"; crossorigin="use-credentials"; fetchpriority=high`,
276+
`<${linkHeaderValue(preload.url)}>; rel=preload; as=video; type="${linkHeaderValue(preloadContentType)}"; crossorigin="${crossOrigin}"; fetchpriority=high`,
270277
);
271278
}
272279

@@ -295,6 +302,7 @@ export function renderFastEmbedHtml(options: FastEmbedRenderOptions) {
295302
: "";
296303
const preload = preloadablePlaybackSelection(selection);
297304
const preloadContentType = preload?.contentType ?? "video/mp4";
305+
const crossOrigin = playbackCrossOrigin(options.bootstrap);
298306
const bootstrapMs =
299307
typeof options.bootstrapMs === "number"
300308
? ` data-rend-bootstrap-ms="${Math.max(0, Math.round(options.bootstrapMs))}"`
@@ -310,7 +318,7 @@ export function renderFastEmbedHtml(options: FastEmbedRenderOptions) {
310318
<title>Rend player</title>
311319
${edge ? `<link rel="dns-prefetch" href="${html(edge.dnsPrefetch)}">` : ""}
312320
${edge ? `<link rel="preconnect" href="${html(edge.origin)}" crossorigin>` : ""}
313-
${preload ? `<link rel="preload" as="video" href="${html(preload.url)}" type="${html(preloadContentType)}" crossorigin="use-credentials" fetchpriority="high">` : ""}
321+
${preload ? `<link rel="preload" as="video" href="${html(preload.url)}" type="${html(preloadContentType)}" crossorigin="${html(crossOrigin)}" fetchpriority="high">` : ""}
314322
<style>
315323
html,body{margin:0;width:100%;height:100%;background:#050505;color:#f7f7f7}
316324
body{overflow:hidden}
@@ -322,11 +330,11 @@ body{overflow:hidden}
322330
</head>
323331
<body>
324332
<main class="rend-fast" aria-label="Video player" data-rend-player-state="${html(state)}" data-rend-player-selected="${html(selection?.label ?? "")}" data-rend-player-artifact="${html(selection?.artifactPath ?? "")}" data-rend-ready-status="${html(ready?.status ?? options.bootstrap?.status ?? state)}" data-rend-source-state="${html(ready?.source_state ?? "")}" data-rend-playable-state="${html(ready?.playable_state ?? "")}" data-rend-manifest-content-type="${html(ready?.manifest_content_type ?? "")}" data-rend-opener-content-type="${html(ready?.opener_content_type ?? "")}" data-rend-poster="${html(ready?.poster_url ?? "")}" data-rend-prefetch-hint-count="${html(ready?.prefetch_hints.length ?? 0)}" data-rend-playback-engine="native" data-rend-document-start-ms="0"${bootstrapMs} data-rend-asset-id="${html(options.assetId)}">
325-
<video class="rend-fast__video"${source}${contentType}${poster}${autoPlay}${controls}${muted} playsinline preload="auto" crossorigin="use-credentials"></video>
333+
<video class="rend-fast__video"${source}${contentType}${poster}${autoPlay}${controls}${muted} playsinline preload="auto" crossorigin="${html(crossOrigin)}"></video>
326334
<div class="rend-fast__message" role="status" aria-live="polite">${html(message)}</div>
327335
</main>
328336
<script>
329-
(()=>{const root=document.querySelector("[data-rend-player-state]");const video=document.querySelector("video");if(!root||!video)return;const assetId=${jsString(options.assetId)};const preferredStartup=${jsString(preferredStartup)};const bootstrapUrl=${jsString(options.bootstrapUrl)};const autoPlay=${options.autoPlay ? "true" : "false"};const bootstrapStarted=performance.now();const mark=(name)=>{if(!root.getAttribute(name))root.setAttribute(name,String(Math.round(performance.now())))};const dims=()=>{if(video.videoWidth)root.setAttribute("data-rend-selected-width",String(video.videoWidth));if(video.videoHeight)root.setAttribute("data-rend-selected-height",String(video.videoHeight))};const play=()=>{if(autoPlay)video.play().catch(()=>{})};const progressive=(data)=>{if(data.playable_state!=="hls_ready"||!data.manifest_url)return null;const byRendition=new Map();for(const hint of Array.isArray(data.prefetch_hints)?data.prefetch_hints:[]){const match=/^hls\\/([^/]+)\\/([^/]+)$/.exec(String(hint.artifact_path||""));if(!match)continue;const state=byRendition.get(match[1])||{init:false,segment:false};state.init=state.init||match[2]===\`init_\${match[1]}.mp4\`;state.segment=state.segment||match[2]==="segment_00000.m4s";byRendition.set(match[1],state)}let rendition="";for(const candidate of ["360p","480p","720p","1080p","2k","4k"]){const state=byRendition.get(candidate);if(state&&state.init&&state.segment){rendition=candidate;break}}if(!rendition)return null;try{const parsed=new URL(data.manifest_url);const prefix="/v/"+assetId+"/";if(!parsed.pathname.startsWith(prefix))return null;const artifactPath="hls/"+rendition+"/progressive.mp4";parsed.pathname=prefix+artifactPath;parsed.search="";parsed.hash="";return{artifactPath,contentType:"video/mp4",label:"progressive_mp4",url:parsed.toString()}}catch{return null}};const select=(data)=>{if(!data||data.status!=="ready")return null;if(preferredStartup==="opener"&&data.opener_url)return{artifactPath:"opener.mp4",contentType:data.opener_content_type||"video/mp4",label:"opener",url:data.opener_url};if(preferredStartup==="progressive"){const selected=progressive(data);if(selected)return selected}if(data.playable_state==="hls_ready"&&data.manifest_url)return{artifactPath:"hls/master.m3u8",contentType:data.manifest_content_type||"application/vnd.apple.mpegurl",label:"native_hls",url:data.manifest_url};if(data.opener_url)return{artifactPath:"opener.mp4",contentType:data.opener_content_type||"video/mp4",label:"opener",url:data.opener_url};if(data.playback_url)return{artifactPath:data.playable_state==="hls_ready"?"hls/master.m3u8":"opener.mp4",contentType:data.playback_content_type||"",label:"primary",url:data.playback_url};return null};video.addEventListener("loadedmetadata",()=>{dims();mark("data-rend-metadata-ms")},{once:true});video.addEventListener("canplay",()=>{dims();mark("data-rend-canplay-ms")},{once:true});video.addEventListener("playing",()=>{root.setAttribute("data-rend-player-state","playing");dims()});if("requestVideoFrameCallback"in video){video.requestVideoFrameCallback(()=>{dims();mark("data-rend-first-frame-ms")})}else{video.addEventListener("playing",()=>mark("data-rend-first-frame-ms"),{once:true})}if(!video.currentSrc&&!video.getAttribute("src")){fetch(bootstrapUrl,{credentials:"same-origin",headers:{accept:"application/json"}}).then(r=>r.ok?r.json():null).then(data=>{root.setAttribute("data-rend-bootstrap-ms",String(Math.round(performance.now()-bootstrapStarted)));const selected=select(data);if(!selected)return;root.setAttribute("data-rend-player-state","ready");root.setAttribute("data-rend-player-selected",selected.label);root.setAttribute("data-rend-player-artifact",selected.artifactPath);if(data.poster_url){root.setAttribute("data-rend-poster",data.poster_url);video.poster=data.poster_url}root.setAttribute("data-rend-ready-status",data.status||"ready");root.setAttribute("data-rend-source-state",data.source_state||"");root.setAttribute("data-rend-playable-state",data.playable_state||"");video.src=selected.url;video.load();play()}).catch(()=>{root.setAttribute("data-rend-player-state","playback_failure")})}else{play()}})();
337+
(()=>{const root=document.querySelector("[data-rend-player-state]");const video=document.querySelector("video");if(!root||!video)return;const assetId=${jsString(options.assetId)};const preferredStartup=${jsString(preferredStartup)};const bootstrapUrl=${jsString(options.bootstrapUrl)};const autoPlay=${options.autoPlay ? "true" : "false"};const bootstrapStarted=performance.now();const mark=(name)=>{if(!root.getAttribute(name))root.setAttribute(name,String(Math.round(performance.now())))};const dims=()=>{if(video.videoWidth)root.setAttribute("data-rend-selected-width",String(video.videoWidth));if(video.videoHeight)root.setAttribute("data-rend-selected-height",String(video.videoHeight))};const play=()=>{if(autoPlay)video.play().catch(()=>{})};const crossOrigin=(data)=>data&&data.playback_credential_mode==="omit"?"anonymous":"use-credentials";const progressive=(data)=>{if(data.playback_credential_mode==="omit"||data.playable_state!=="hls_ready"||!data.manifest_url)return null;const byRendition=new Map();for(const hint of Array.isArray(data.prefetch_hints)?data.prefetch_hints:[]){const match=/^hls\\/([^/]+)\\/([^/]+)$/.exec(String(hint.artifact_path||""));if(!match)continue;const state=byRendition.get(match[1])||{init:false,segment:false};state.init=state.init||match[2]===\`init_\${match[1]}.mp4\`;state.segment=state.segment||match[2]==="segment_00000.m4s";byRendition.set(match[1],state)}let rendition="";for(const candidate of ["360p","480p","720p","1080p","2k","4k"]){const state=byRendition.get(candidate);if(state&&state.init&&state.segment){rendition=candidate;break}}if(!rendition)return null;try{const parsed=new URL(data.manifest_url);const prefix="/v/"+assetId+"/";if(!parsed.pathname.startsWith(prefix))return null;const artifactPath="hls/"+rendition+"/progressive.mp4";parsed.pathname=prefix+artifactPath;parsed.search="";parsed.hash="";return{artifactPath,contentType:"video/mp4",label:"progressive_mp4",url:parsed.toString()}}catch{return null}};const select=(data)=>{if(!data||data.status!=="ready")return null;if(preferredStartup==="opener"&&data.opener_url)return{artifactPath:"opener.mp4",contentType:data.opener_content_type||"video/mp4",label:"opener",url:data.opener_url};if(preferredStartup==="progressive"){const selected=progressive(data);if(selected)return selected}if(data.playable_state==="hls_ready"&&data.manifest_url)return{artifactPath:"hls/master.m3u8",contentType:data.manifest_content_type||"application/vnd.apple.mpegurl",label:"native_hls",url:data.manifest_url};if(data.opener_url)return{artifactPath:"opener.mp4",contentType:data.opener_content_type||"video/mp4",label:"opener",url:data.opener_url};if(data.playback_url)return{artifactPath:data.playable_state==="hls_ready"?"hls/master.m3u8":"opener.mp4",contentType:data.playback_content_type||"",label:"primary",url:data.playback_url};return null};video.addEventListener("loadedmetadata",()=>{dims();mark("data-rend-metadata-ms")},{once:true});video.addEventListener("canplay",()=>{dims();mark("data-rend-canplay-ms")},{once:true});video.addEventListener("playing",()=>{root.setAttribute("data-rend-player-state","playing");dims()});if("requestVideoFrameCallback"in video){video.requestVideoFrameCallback(()=>{dims();mark("data-rend-first-frame-ms")})}else{video.addEventListener("playing",()=>mark("data-rend-first-frame-ms"),{once:true})}if(!video.currentSrc&&!video.getAttribute("src")){fetch(bootstrapUrl,{credentials:"same-origin",headers:{accept:"application/json"}}).then(r=>r.ok?r.json():null).then(data=>{root.setAttribute("data-rend-bootstrap-ms",String(Math.round(performance.now()-bootstrapStarted)));const selected=select(data);if(!selected)return;video.crossOrigin=crossOrigin(data);root.setAttribute("data-rend-player-state","ready");root.setAttribute("data-rend-player-selected",selected.label);root.setAttribute("data-rend-player-artifact",selected.artifactPath);if(data.poster_url){root.setAttribute("data-rend-poster",data.poster_url);video.poster=data.poster_url}root.setAttribute("data-rend-ready-status",data.status||"ready");root.setAttribute("data-rend-source-state",data.source_state||"");root.setAttribute("data-rend-playable-state",data.playable_state||"");video.src=selected.url;video.load();play()}).catch(()=>{root.setAttribute("data-rend-player-state","playback_failure")})}else{play()}})();
330338
</script>
331339
</body>
332340
</html>`;
@@ -383,6 +391,7 @@ export async function GET(
383391
const ready = bootstrap?.status === "ready" ? bootstrap : null;
384392
const selection = playbackSelection(bootstrap, startup);
385393
const edge = playbackOrigin(ready) ?? defaultPlaybackOriginHint();
394+
const crossOrigin = playbackCrossOrigin(bootstrap);
386395
const headers = new Headers({
387396
"cache-control": "no-store",
388397
"content-type": "text/html; charset=utf-8",
@@ -395,7 +404,7 @@ export async function GET(
395404
headers.append("set-cookie", setCookie);
396405
}
397406
}
398-
appendStartupLinkHeaders(headers, edge, selection);
407+
appendStartupLinkHeaders(headers, edge, selection, crossOrigin);
399408

400409
return new Response(
401410
renderFastEmbedHtml({

apps/site/app/embed-fast/route.test.mts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,48 @@ test("fast embed defaults to progressive fMP4 when startup hints support it", ()
8282
assert.doesNotMatch(html, /playback_token|set-cookie|authorization/i);
8383
});
8484

85+
test("fast embed uses anonymous HLS for public playback", () => {
86+
const base = readyBootstrap();
87+
if (base.status !== "ready") throw new Error("expected ready bootstrap");
88+
const bootstrap = {
89+
...base,
90+
playback_credential_mode: "omit" as const,
91+
playback_url: `https://media.rend.so/v/${ASSET_ID}/hls/master.m3u8`,
92+
opener_url: `https://media.rend.so/v/${ASSET_ID}/opener.mp4`,
93+
manifest_url: `https://media.rend.so/v/${ASSET_ID}/hls/master.m3u8`,
94+
poster_url: `https://media.rend.so/v/${ASSET_ID}/thumbnail.jpg`,
95+
prefetch_hints: [
96+
{
97+
artifact_path: "hls/360p/init_360p.mp4",
98+
content_type: "video/mp4",
99+
url: `https://media.rend.so/v/${ASSET_ID}/hls/360p/init_360p.mp4`,
100+
},
101+
{
102+
artifact_path: "hls/360p/segment_00000.m4s",
103+
content_type: "video/mp4",
104+
url: `https://media.rend.so/v/${ASSET_ID}/hls/360p/segment_00000.m4s`,
105+
},
106+
],
107+
} satisfies Extract<WatchPlaybackBootstrapResponse, { status: "ready" }>;
108+
const html = renderFastEmbedHtml({
109+
assetId: ASSET_ID,
110+
autoPlay: true,
111+
bootstrap,
112+
bootstrapUrl: `/api/player/${ASSET_ID}`,
113+
bootstrapMs: 42,
114+
controls: false,
115+
muted: true,
116+
playbackOriginHint: null,
117+
startupMode: "progressive",
118+
});
119+
120+
assert.match(html, /src="https:\/\/media\.rend\.so\/v\/00000000-0000-0000-0000-000000000001\/hls\/master\.m3u8"/);
121+
assert.match(html, /crossorigin="anonymous"/);
122+
assert.match(html, /data-rend-player-selected="native_hls"/);
123+
assert.doesNotMatch(html, /src="https:\/\/media\.rend\.so[^"]+progressive\.mp4"/);
124+
assert.doesNotMatch(html, /crossorigin="use-credentials"/);
125+
});
126+
85127
test("fast embed route supports client bootstrap for immediate document response", async () => {
86128
const originalFetch = globalThis.fetch;
87129
const fetches: string[] = [];

apps/site/app/embed/[assetId]/page.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
initialPlaybackState,
1313
initialSourceSelection,
1414
instantPlaybackScript,
15+
playbackCrossOrigin,
1516
playbackStateMessage,
1617
readyBootstrap,
1718
startupPreloadHints,
@@ -142,6 +143,7 @@ export default async function EmbedPage({ params, searchParams }: EmbedPageProps
142143
const message = playbackStateMessage(initialBootstrap, state);
143144
const edgeHint = playbackEdgeHint(initialBootstrap);
144145
const preloadHints = startupPreloadHints(initialBootstrap, startupMode);
146+
const crossOrigin = playbackCrossOrigin(initialBootstrap);
145147

146148
const playerId = `rend-embed-${assetId}`;
147149
const sectionStyle = accent ? ({ "--rend-accent": accent } as CSSProperties) : undefined;
@@ -166,7 +168,7 @@ export default async function EmbedPage({ params, searchParams }: EmbedPageProps
166168
as={hint.as}
167169
href={hint.url}
168170
type={hint.contentType}
169-
crossOrigin="use-credentials"
171+
crossOrigin={hint.crossOrigin}
170172
data-rend-startup-preload={hint.artifactPath}
171173
/>
172174
))}
@@ -202,7 +204,7 @@ export default async function EmbedPage({ params, searchParams }: EmbedPageProps
202204
playsInline
203205
preload={autoPlay ? "auto" : "metadata"}
204206
src={selection?.url}
205-
crossOrigin="use-credentials"
207+
crossOrigin={crossOrigin}
206208
suppressHydrationWarning
207209
/>
208210
{controls && <PlayerControls />}

0 commit comments

Comments
 (0)