Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .changeset/cool-pets-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"react-native-youtube-bridge": patch
"@react-native-youtube-bridge/core": patch
---

fix: resolve 101/150 errors by aligning WebView baseUrl and IFrame origin in inline mode

- Set baseUrl to the default `https://localhost/` for inline WebView
- Default playerVars.origin to `https://localhost` in inline WebView
- Wrap certain dev logs with DEV so they only run in development
- Add TSDoc for playerVars.origin and webViewUrl props
6 changes: 3 additions & 3 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,10 @@ function App() {
</TouchableOpacity>

<TouchableOpacity
style={[styles.videoButton, videoId === 'fJ9rUzIMcZQ' && styles.activeVideoButton]}
onPress={() => setVideoId('fJ9rUzIMcZQ')}
style={[styles.videoButton, videoId === 'dvgZkm1xWPE' && styles.activeVideoButton]}
onPress={() => setVideoId('dvgZkm1xWPE')}
>
<Text style={styles.videoButtonText}>Bohemian Rhapsody</Text>
<Text style={styles.videoButtonText}>Viva la Vida</Text>
</TouchableOpacity>

<TouchableOpacity
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ export type YoutubePlayerVars = {
rel?: boolean;
/**
* This parameter provides an extra security measure for the IFrame API and is only supported for IFrame embeds.
*
* @remark
* - When `useInlineHtml` is `true` (iOS/Android inline WebView), if not provided, the library defaults this to `https://localhost` and sets the WebView `baseUrl` accordingly so that the document origin and this value match.
* - When `useInlineHtml` is `false` (remote WebView), if not provided, the external page URL defaults to `https://react-native-youtube-bridge.pages.dev` and this value follows that URL. If you pass a custom `webViewUrl` (base URL), `origin` should follow that same origin.
* - In all cases, this value MUST exactly match the document's origin that hosts the iframe for the YouTube IFrame API to function correctly.
*/
origin?: string;
Comment on lines 168 to 175
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Double-check that runtime defaults match these docs

Since this comment promises defaults, verify the implementation actually:

  • injects baseUrl: 'https://localhost/' for inline HTML when not provided, and
  • defaults playerVars.origin to https://localhost in inline mode.

Run:


🏁 Script executed:

#!/bin/bash
# Verify baseUrl defaulting for inline HTML
rg -n -C3 --type ts --type tsx "baseUrl.*https://localhost/?'|\"https://localhost/\"|baseUrl:"

# Verify origin default to https://localhost in inline path
rg -n -C3 --type ts --type tsx "origin.*https://localhost|safeOrigin.*localhost|playerVars\.origin"

# Spot where the WebView source is built to ensure baseUrl is always present
rg -n -C3 --type tsx "WebView|source:\s*\{[^\}]*html" packages/react-native-youtube-bridge/src

Length of output: 380


🏁 Script executed:

#!/bin/bash
set -e

# 1. Find all references to useInlineHtml to see how inline vs remote paths differ
rg -n -C3 "useInlineHtml" .

# 2. Locate where baseUrl is set or defaulted in the WebView source builder
rg -n -C3 "baseUrl" .

# 3. Look for playerVars.origin defaults in the code
rg -n -C3 "playerVars" .

# 4. Inspect all WebView component usages to see how source/html and baseUrl interact
rg -n -C3 "WebView" packages/react-native-youtube-bridge/src

Length of output: 68622


🏁 Script executed:

#!/bin/bash
set -e

# Inspect instantiation of YT.Player to verify default origin assignment
rg -n -C3 "new YT.Player" packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts

# Search for origin usage in HTML generation
rg -n -C3 "origin" packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts

Length of output: 1491


🏁 Script executed:

#!/bin/bash
set -e

# Inspect import of escapeHtml in local HTML hook
sed -n '1,30p' packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts

Length of output: 1099


Ensure the documented origin default is actually applied in inline‐HTML mode

  • The default of baseUrl: 'https://localhost/' when useInlineHtml is true is implemented correctly in YoutubeView.tsx (…src/YoutubeView.tsx line 44) .
  • However, in the HTML generator hook (useCreateLocalPlayerHtml), the code uses
    const safeOrigin = escapeHtml(origin) ?? 'https://localhost';
    
    ${safeOrigin ? `origin: '${safeOrigin}',` : ''}
    (…src/hooks/useCreateLocalPlayerHtml.ts lines 28, 108) .
    Because escapeHtml(undefined) yields '' (an empty string, not null/undefined), the ?? fallback never runs and safeOrigin is falsy—so no origin is injected.

Refactor required
Update the fallback to use || so that an empty string also triggers the default:

- const safeOrigin = escapeHtml(origin) ?? 'https://localhost';
+ const safeOrigin = escapeHtml(origin) || 'https://localhost';

This ensures that when no origin is provided, the hook injects origin: 'https://localhost' into the player configuration, matching the documentation.

};
Expand Down
16 changes: 10 additions & 6 deletions packages/react-native-youtube-bridge/src/YoutubeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ function YoutubeView({
// biome-ignore lint/correctness/useExhaustiveDependencies: webViewProps.source is intentionally excluded to prevent unnecessary re-renders
const webViewSource = useMemo(() => {
if (useInlineHtml) {
return { html: createPlayerHTML(), ...(webViewBaseUrl ? { baseUrl: webViewBaseUrl } : {}) };
return {
html: createPlayerHTML(),
baseUrl: webViewBaseUrl ?? 'https://localhost/',
};
}

if (webViewUrl) {
Expand Down Expand Up @@ -107,7 +110,9 @@ function YoutubeView({
return;
}
} catch (error) {
console.error('Error parsing WebView message:', error);
if (__DEV__) {
console.error('Error parsing WebView message:', error);
}
player.emit('error', { code: 1000, message: 'FAILED_TO_PARSE_WEBVIEW_MESSAGE' });
}
},
Expand Down Expand Up @@ -159,12 +164,9 @@ function YoutubeView({
mediaPlaybackRequiresUserAction={false}
originWhitelist={['*']}
style={[styles.webView, webViewStyle]}
// iOS specific props
allowsLinkPreview={false}
dataDetectorTypes={dataDetectorTypes}
// Android specific props
mixedContentMode="compatibility"
thirdPartyCookiesEnabled={false}
webviewDebuggingEnabled={__DEV__}
onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest}
{...webViewProps}
Expand All @@ -173,7 +175,9 @@ function YoutubeView({
source={webViewSource}
onMessage={handleMessage}
onError={(error) => {
console.error('WebView error:', error);
if (__DEV__) {
console.error('WebView error:', error);
}
player.emit('error', { code: 1001, message: 'WEBVIEW_LOADING_ERROR' });
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const useCreateLocalPlayerHtml = ({
return '<html><body><div style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; color: #fff;">Invalid YouTube ID</div></body></html>';
}

const safeOrigin = escapeHtml(origin);
const safeOrigin = escapeHtml(origin) ?? 'https://localhost';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix origin fallback: escapeHtml returns an empty string, so ?? 'https://localhost' never triggers

As implemented, escapeHtml(undefined) returns '', not undefined/null, so safeOrigin becomes '' and the origin playerVar is omitted. This defeats the PR’s goal to default the IFrame origin to https://localhost in inline mode and can still trigger 101/150 if baseUrl is set but origin is missing.

Apply this diff to ensure the default is actually applied and still sanitized:

-    const safeOrigin = escapeHtml(origin) ?? 'https://localhost';
+    const safeOrigin = escapeHtml(origin ?? 'https://localhost');

Optional hardening: strip a trailing slash if the caller passes one to keep the value a proper “origin”:

-    const safeOrigin = escapeHtml(origin ?? 'https://localhost');
+    const safeOrigin = escapeHtml((origin ?? 'https://localhost').replace(/\/$/, ''));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const safeOrigin = escapeHtml(origin) ?? 'https://localhost';
const safeOrigin = escapeHtml(origin ?? 'https://localhost');
🤖 Prompt for AI Agents
In packages/react-native-youtube-bridge/src/hooks/useCreateLocalPlayerHtml.ts
around line 28, escapeHtml(undefined) returns an empty string so the nullish
fallback (?? 'https://localhost') never runs; change the logic to treat an empty
string as missing by checking the raw origin first (use origin ??
'https://localhost' or origin ? escapeHtml(origin) : 'https://localhost') so the
default origin is applied and still sanitized, and optionally trim any trailing
'/' from the resolved origin to ensure it remains a proper origin.

const safeStartTime = safeNumber(startTime);
const safeEndTime = endTime ? safeNumber(endTime) : undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ const useYouTubePlayer = (source: YoutubeSource, config?: YoutubePlayerVars): Yo
const isFastRefresh = useRef(false);

const onError = useCallback((error: YoutubeError) => {
console.error('Invalid YouTube source: ', error);
if (__DEV__) {
console.error('Invalid YouTube source: ', error);
}
playerRef.current?.emit('error', error);
}, []);

Expand Down
3 changes: 3 additions & 0 deletions packages/react-native-youtube-bridge/src/types/youtube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ export type YoutubeViewProps = {
* The URL for the WebView source.
* @remark
* When `useInlineHtml` is `true`, this value is set as the `baseUrl` for HTML content.
* In this case, the origin of `webViewUrl` MUST match the YouTube IFrame API `origin`
* (e.g. baseUrl `https://localhost/` ⇄ origin `https://localhost`).
*
* When `useInlineHtml` is `false`, this value overrides the default URI for the WebView source (https://react-native-youtube-bridge.pages.dev).
* @platform ios, android
*/
Expand Down